Compare commits

...

49 Commits

Author SHA1 Message Date
github-actions
7170931464 Auto commit for release 'develop' on 2024-05-20 2024-05-20 05:09:44 +02:00
github-actions
c1b5a1a13e Auto commit for release 'develop' on 2024-05-19 2024-05-19 10:37:08 +02:00
James Cole
a6265ce8ab Fix line in job. 2024-05-19 10:28:01 +02:00
github-actions
90109917df Auto commit for release 'v6.1.16' on 2024-05-19 2024-05-19 10:26:25 +02:00
James Cole
0acd54c2b7 Update changelog. 2024-05-19 10:21:03 +02:00
James Cole
c96226b9b4 Add final API changes 2024-05-19 10:17:21 +02:00
James Cole
6d143f1624 Clean up API 2024-05-19 06:36:31 +02:00
James Cole
93324d1154 Remove unused variables 2024-05-18 06:57:38 +02:00
github-actions
a39f0e1891 Auto commit for release 'develop' on 2024-05-18 2024-05-18 06:49:29 +02:00
James Cole
822f609a22 Fix ibans 2024-05-18 06:44:18 +02:00
James Cole
cd7ddd1c61 Add more IBAN filters 2024-05-18 06:42:09 +02:00
James Cole
0b63ba26bb Fix autocomplete entries. 2024-05-18 05:51:02 +02:00
James Cole
94d70cdb62 Fix missing parameters 2024-05-17 20:45:16 +02:00
James Cole
acb3831c8b Add parallel thing 2024-05-16 07:30:07 +02:00
github-actions
c9d9ecede4 Auto commit for release 'develop' on 2024-05-16 2024-05-16 07:22:12 +02:00
James Cole
4eb5873353 Merge branch 'main' into develop 2024-05-16 07:17:18 +02:00
James Cole
7ca39fdb21 Fix commands 2024-05-16 07:16:47 +02:00
James Cole
b8d1d7a8c0 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
# Conflicts:
#	app/Support/Models/AccountBalanceCalculator.php
2024-05-16 07:15:36 +02:00
James Cole
1af79eab30 Clean up commands 2024-05-16 07:14:44 +02:00
github-actions
03be2704ce Auto commit for release 'develop' on 2024-05-16 2024-05-16 05:10:41 +02:00
James Cole
34baea66a7 Merge pull request #8880 from firefly-iii/dependabot/github_actions/github/command-1.2.0 2024-05-14 05:43:54 +02:00
James Cole
0638d109d0 Clean up code. 2024-05-13 20:31:52 +02:00
dependabot[bot]
561e228a2d Bump github/command from 1.1.1 to 1.2.0
Bumps [github/command](https://github.com/github/command) from 1.1.1 to 1.2.0.
- [Release notes](https://github.com/github/command/releases)
- [Commits](https://github.com/github/command/compare/v1.1.1...v1.2.0)

---
updated-dependencies:
- dependency-name: github/command
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-13 03:12:36 +00:00
github-actions
cb5d856769 Auto commit for release 'develop' on 2024-05-13 2024-05-13 05:10:16 +02:00
James Cole
04fe5d1fc4 Correct account balances better. 2024-05-12 18:24:38 +02:00
James Cole
45e9d4f8de Fix auto complete 2024-05-12 17:50:54 +02:00
James Cole
73fdbb6202 Update API endpoints and account autocomplete. 2024-05-12 13:31:33 +02:00
James Cole
e49dbefddd Make the combined combination unique. 2024-05-12 08:10:12 +02:00
James Cole
fc5143337a Add a title to the table, so multiple balances per account are possible. 2024-05-12 08:09:50 +02:00
James Cole
4b3eb6dace Rename command. 2024-05-12 06:26:50 +02:00
James Cole
c741b2a819 Add related models. 2024-05-12 06:25:13 +02:00
James Cole
cebfaa32bf Add routine that caches account balances. Add it to the /flush routine as well. 2024-05-12 06:24:11 +02:00
James Cole
d356d39d43 add account balances with some random data 2024-05-11 20:32:25 +02:00
James Cole
7d9f22d3f4 Merge branch 'api' into develop
# Conflicts:
#	app/JsonApi/V3/Accounts/AccountRepository.php
#	app/JsonApi/V3/Accounts/AccountResource.php
#	app/JsonApi/V3/Accounts/Capabilities/AccountQuery.php
#	app/JsonApi/V3/Users/UserSchema.php
2024-05-10 12:52:44 +02:00
James Cole
c6c8f282e2 Restore previous stuff 2024-05-10 12:51:02 +02:00
James Cole
6a64420721 Refactor resources 2024-05-10 09:42:09 +02:00
James Cole
fcde4e2488 no message 2024-05-10 09:41:53 +02:00
James Cole
aa5c4c20e9 Various messy code. 2024-05-10 09:17:09 +02:00
James Cole
794e31e487 Add some experimental account endpoints using a package 2024-05-10 06:43:18 +02:00
James Cole
16bf186312 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
# Conflicts:
#	composer.lock
2024-05-10 06:42:47 +02:00
James Cole
45c722e786 Add config, merge later. 2024-05-10 06:36:57 +02:00
github-actions
36d9e5c3fe Auto commit for release 'develop' on 2024-05-09 2024-05-09 05:10:13 +02:00
James Cole
8d614de67f Possible fix for https://github.com/firefly-iii/firefly-iii/issues/8867 2024-05-08 21:55:14 +02:00
James Cole
d17da670ab Fix connect exception, add some debug logs. 2024-05-06 06:33:12 +02:00
github-actions
5bf4df9ad8 Auto commit for release 'develop' on 2024-05-06 2024-05-06 05:10:30 +02:00
James Cole
07db6b59ce Fix webhooks overview. 2024-05-02 06:15:41 +02:00
github-actions
e16f1cf4ee Auto commit for release 'develop' on 2024-05-02 2024-05-02 05:10:23 +02:00
James Cole
4c80d929ca Possible division by zero? 2024-05-01 06:29:28 +02:00
James Cole
5bd72f6428 Add command 2024-04-26 05:32:21 +02:00
150 changed files with 4419 additions and 750 deletions

View File

@@ -19,6 +19,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;
$current = __DIR__;
$paths = [
@@ -35,6 +37,7 @@ $finder = PhpCsFixer\Finder::create()
$config = new PhpCsFixer\Config();
$config->setParallelConfig(ParallelConfigFactory::detect());
return $config->setRules(
[
// rule sets

View File

@@ -6,6 +6,70 @@
],
"content-hash": "f1e0b38af4ded66da271a99d2bff5be8",
"packages": [
{
"name": "clue/ndjson-react",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/clue/reactphp-ndjson.git",
"reference": "392dc165fce93b5bb5c637b67e59619223c931b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0",
"reference": "392dc165fce93b5bb5c637b67e59619223c931b0",
"shasum": ""
},
"require": {
"php": ">=5.3",
"react/stream": "^1.2"
},
"require-dev": {
"phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35",
"react/event-loop": "^1.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Clue\\React\\NDJson\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering"
}
],
"description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.",
"homepage": "https://github.com/clue/reactphp-ndjson",
"keywords": [
"NDJSON",
"json",
"jsonlines",
"newline",
"reactphp",
"streaming"
],
"support": {
"issues": "https://github.com/clue/reactphp-ndjson/issues",
"source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0"
},
"funding": [
{
"url": "https://clue.engineering/support",
"type": "custom"
},
{
"url": "https://github.com/clue",
"type": "github"
}
],
"time": "2022-12-23T10:58:28+00:00"
},
{
"name": "composer/pcre",
"version": "3.1.3",
@@ -160,16 +224,16 @@
},
{
"name": "composer/xdebug-handler",
"version": "3.0.4",
"version": "3.0.5",
"source": {
"type": "git",
"url": "https://github.com/composer/xdebug-handler.git",
"reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255"
"reference": "6c1925561632e83d60a44492e0b344cf48ab85ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
"reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef",
"reference": "6c1925561632e83d60a44492e0b344cf48ab85ef",
"shasum": ""
},
"require": {
@@ -206,7 +270,7 @@
"support": {
"irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/xdebug-handler/issues",
"source": "https://github.com/composer/xdebug-handler/tree/3.0.4"
"source": "https://github.com/composer/xdebug-handler/tree/3.0.5"
},
"funding": [
{
@@ -222,29 +286,144 @@
"type": "tidelift"
}
],
"time": "2024-03-26T18:29:49+00:00"
"time": "2024-05-06T16:37:16+00:00"
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.54.0",
"name": "evenement/evenement",
"version": "v3.0.2",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "2aecbc8640d7906c38777b3dcab6f4ca79004d08"
"url": "https://github.com/igorw/evenement.git",
"reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2aecbc8640d7906c38777b3dcab6f4ca79004d08",
"reference": "2aecbc8640d7906c38777b3dcab6f4ca79004d08",
"url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc",
"reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc",
"shasum": ""
},
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^9 || ^6"
},
"type": "library",
"autoload": {
"psr-4": {
"Evenement\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch"
}
],
"description": "Événement is a very simple event dispatching library for PHP",
"keywords": [
"event-dispatcher",
"event-emitter"
],
"support": {
"issues": "https://github.com/igorw/evenement/issues",
"source": "https://github.com/igorw/evenement/tree/v3.0.2"
},
"time": "2023-08-08T05:53:35+00:00"
},
{
"name": "fidry/cpu-core-counter",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/theofidry/cpu-core-counter.git",
"reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42",
"reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"fidry/makefile": "^0.2.0",
"fidry/php-cs-fixer-config": "^1.1.2",
"phpstan/extension-installer": "^1.2.0",
"phpstan/phpstan": "^1.9.2",
"phpstan/phpstan-deprecation-rules": "^1.0.0",
"phpstan/phpstan-phpunit": "^1.2.2",
"phpstan/phpstan-strict-rules": "^1.4.4",
"phpunit/phpunit": "^8.5.31 || ^9.5.26",
"webmozarts/strict-phpunit": "^7.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Fidry\\CpuCoreCounter\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Théo FIDRY",
"email": "theo.fidry@gmail.com"
}
],
"description": "Tiny utility to get the number of CPU cores.",
"keywords": [
"CPU",
"core"
],
"support": {
"issues": "https://github.com/theofidry/cpu-core-counter/issues",
"source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0"
},
"funding": [
{
"url": "https://github.com/theofidry",
"type": "github"
}
],
"time": "2024-02-07T09:43:46+00:00"
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.57.1",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "3f7efe667a8c9818aacceee478add7c0fc24cb21"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/3f7efe667a8c9818aacceee478add7c0fc24cb21",
"reference": "3f7efe667a8c9818aacceee478add7c0fc24cb21",
"shasum": ""
},
"require": {
"clue/ndjson-react": "^1.0",
"composer/semver": "^3.4",
"composer/xdebug-handler": "^3.0.3",
"ext-filter": "*",
"ext-json": "*",
"ext-tokenizer": "*",
"fidry/cpu-core-counter": "^1.0",
"php": "^7.4 || ^8.0",
"react/child-process": "^0.6.5",
"react/event-loop": "^1.0",
"react/promise": "^2.0 || ^3.0",
"react/socket": "^1.0",
"react/stream": "^1.0",
"sebastian/diff": "^4.0 || ^5.0 || ^6.0",
"symfony/console": "^5.4 || ^6.0 || ^7.0",
"symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0",
@@ -307,7 +486,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.54.0"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.57.1"
},
"funding": [
{
@@ -315,7 +494,7 @@
"type": "github"
}
],
"time": "2024-04-17T08:12:13+00:00"
"time": "2024-05-15T22:01:07+00:00"
},
{
"name": "psr/container",
@@ -470,6 +649,536 @@
},
"time": "2021-07-14T16:46:02+00:00"
},
{
"name": "react/cache",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/cache.git",
"reference": "d47c472b64aa5608225f47965a484b75c7817d5b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b",
"reference": "d47c472b64aa5608225f47965a484b75c7817d5b",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"react/promise": "^3.0 || ^2.0 || ^1.1"
},
"require-dev": {
"phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "Async, Promise-based cache interface for ReactPHP",
"keywords": [
"cache",
"caching",
"promise",
"reactphp"
],
"support": {
"issues": "https://github.com/reactphp/cache/issues",
"source": "https://github.com/reactphp/cache/tree/v1.2.0"
},
"funding": [
{
"url": "https://opencollective.com/reactphp",
"type": "open_collective"
}
],
"time": "2022-11-30T15:59:55+00:00"
},
{
"name": "react/child-process",
"version": "v0.6.5",
"source": {
"type": "git",
"url": "https://github.com/reactphp/child-process.git",
"reference": "e71eb1aa55f057c7a4a0d08d06b0b0a484bead43"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/child-process/zipball/e71eb1aa55f057c7a4a0d08d06b0b0a484bead43",
"reference": "e71eb1aa55f057c7a4a0d08d06b0b0a484bead43",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.0",
"react/event-loop": "^1.2",
"react/stream": "^1.2"
},
"require-dev": {
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
"react/socket": "^1.8",
"sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\ChildProcess\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "Event-driven library for executing child processes with ReactPHP.",
"keywords": [
"event-driven",
"process",
"reactphp"
],
"support": {
"issues": "https://github.com/reactphp/child-process/issues",
"source": "https://github.com/reactphp/child-process/tree/v0.6.5"
},
"funding": [
{
"url": "https://github.com/WyriHaximus",
"type": "github"
},
{
"url": "https://github.com/clue",
"type": "github"
}
],
"time": "2022-09-16T13:41:56+00:00"
},
{
"name": "react/dns",
"version": "v1.12.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/dns.git",
"reference": "c134600642fa615b46b41237ef243daa65bb64ec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/dns/zipball/c134600642fa615b46b41237ef243daa65bb64ec",
"reference": "c134600642fa615b46b41237ef243daa65bb64ec",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"react/cache": "^1.0 || ^0.6 || ^0.5",
"react/event-loop": "^1.2",
"react/promise": "^3.0 || ^2.7 || ^1.2.1"
},
"require-dev": {
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"react/async": "^4 || ^3 || ^2",
"react/promise-timer": "^1.9"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Dns\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "Async DNS resolver for ReactPHP",
"keywords": [
"async",
"dns",
"dns-resolver",
"reactphp"
],
"support": {
"issues": "https://github.com/reactphp/dns/issues",
"source": "https://github.com/reactphp/dns/tree/v1.12.0"
},
"funding": [
{
"url": "https://opencollective.com/reactphp",
"type": "open_collective"
}
],
"time": "2023-11-29T12:41:06+00:00"
},
{
"name": "react/event-loop",
"version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/event-loop.git",
"reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
"reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
},
"suggest": {
"ext-pcntl": "For signal handling support when using the StreamSelectLoop"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\EventLoop\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
"keywords": [
"asynchronous",
"event-loop"
],
"support": {
"issues": "https://github.com/reactphp/event-loop/issues",
"source": "https://github.com/reactphp/event-loop/tree/v1.5.0"
},
"funding": [
{
"url": "https://opencollective.com/reactphp",
"type": "open_collective"
}
],
"time": "2023-11-13T13:48:05+00:00"
},
{
"name": "react/promise",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/promise.git",
"reference": "e563d55d1641de1dea9f5e84f3cccc66d2bfe02c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/promise/zipball/e563d55d1641de1dea9f5e84f3cccc66d2bfe02c",
"reference": "e563d55d1641de1dea9f5e84f3cccc66d2bfe02c",
"shasum": ""
},
"require": {
"php": ">=7.1.0"
},
"require-dev": {
"phpstan/phpstan": "1.10.39 || 1.4.10",
"phpunit/phpunit": "^9.6 || ^7.5"
},
"type": "library",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"React\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "A lightweight implementation of CommonJS Promises/A for PHP",
"keywords": [
"promise",
"promises"
],
"support": {
"issues": "https://github.com/reactphp/promise/issues",
"source": "https://github.com/reactphp/promise/tree/v3.1.0"
},
"funding": [
{
"url": "https://opencollective.com/reactphp",
"type": "open_collective"
}
],
"time": "2023-11-16T16:21:57+00:00"
},
{
"name": "react/socket",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/socket.git",
"reference": "216d3aec0b87f04a40ca04f481e6af01bdd1d038"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/socket/zipball/216d3aec0b87f04a40ca04f481e6af01bdd1d038",
"reference": "216d3aec0b87f04a40ca04f481e6af01bdd1d038",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.0",
"react/dns": "^1.11",
"react/event-loop": "^1.2",
"react/promise": "^3 || ^2.6 || ^1.2.1",
"react/stream": "^1.2"
},
"require-dev": {
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"react/async": "^4 || ^3 || ^2",
"react/promise-stream": "^1.4",
"react/promise-timer": "^1.10"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Socket\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
"keywords": [
"Connection",
"Socket",
"async",
"reactphp",
"stream"
],
"support": {
"issues": "https://github.com/reactphp/socket/issues",
"source": "https://github.com/reactphp/socket/tree/v1.15.0"
},
"funding": [
{
"url": "https://opencollective.com/reactphp",
"type": "open_collective"
}
],
"time": "2023-12-15T11:02:10+00:00"
},
{
"name": "react/stream",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/stream.git",
"reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/stream/zipball/6fbc9672905c7d5a885f2da2fc696f65840f4a66",
"reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"php": ">=5.3.8",
"react/event-loop": "^1.2"
},
"require-dev": {
"clue/stream-filter": "~1.2",
"phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
},
"type": "library",
"autoload": {
"psr-4": {
"React\\Stream\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christian Lück",
"email": "christian@clue.engineering",
"homepage": "https://clue.engineering/"
},
{
"name": "Cees-Jan Kiewiet",
"email": "reactphp@ceesjankiewiet.nl",
"homepage": "https://wyrihaximus.net/"
},
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com",
"homepage": "https://sorgalla.com/"
},
{
"name": "Chris Boden",
"email": "cboden@gmail.com",
"homepage": "https://cboden.dev/"
}
],
"description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
"keywords": [
"event-driven",
"io",
"non-blocking",
"pipe",
"reactphp",
"readable",
"stream",
"writable"
],
"support": {
"issues": "https://github.com/reactphp/stream/issues",
"source": "https://github.com/reactphp/stream/tree/v1.3.0"
},
"funding": [
{
"url": "https://opencollective.com/reactphp",
"type": "open_collective"
}
],
"time": "2023-06-16T10:52:11+00:00"
},
{
"name": "sebastian/diff",
"version": "6.0.1",
@@ -632,16 +1341,16 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.4.0",
"version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
@@ -650,7 +1359,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.4-dev"
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -679,7 +1388,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
},
"funding": [
{
@@ -695,7 +1404,7 @@
"type": "tidelift"
}
],
"time": "2023-05-23T14:45:45+00:00"
"time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/event-dispatcher",
@@ -779,16 +1488,16 @@
},
{
"name": "symfony/event-dispatcher-contracts",
"version": "v3.4.2",
"version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
"reference": "4e64b49bf370ade88e567de29465762e316e4224"
"reference": "8f93aec25d41b72493c6ddff14e916177c9efc50"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/4e64b49bf370ade88e567de29465762e316e4224",
"reference": "4e64b49bf370ade88e567de29465762e316e4224",
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50",
"reference": "8f93aec25d41b72493c6ddff14e916177c9efc50",
"shasum": ""
},
"require": {
@@ -798,7 +1507,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.4-dev"
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -835,7 +1544,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.2"
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0"
},
"funding": [
{
@@ -851,7 +1560,7 @@
"type": "tidelift"
}
],
"time": "2024-01-23T14:51:35+00:00"
"time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/filesystem",
@@ -1585,21 +2294,22 @@
},
{
"name": "symfony/service-contracts",
"version": "v3.4.2",
"version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "11bbf19a0fb7b36345861e85c5768844c552906e"
"reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/11bbf19a0fb7b36345861e85c5768844c552906e",
"reference": "11bbf19a0fb7b36345861e85c5768844c552906e",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
"reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
"shasum": ""
},
"require": {
"php": ">=8.1",
"psr/container": "^1.1|^2.0"
"psr/container": "^1.1|^2.0",
"symfony/deprecation-contracts": "^2.5|^3"
},
"conflict": {
"ext-psr": "<1.1|>=2"
@@ -1607,7 +2317,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.4-dev"
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -1647,7 +2357,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.4.2"
"source": "https://github.com/symfony/service-contracts/tree/v3.5.0"
},
"funding": [
{
@@ -1663,7 +2373,7 @@
"type": "tidelift"
}
],
"time": "2023-12-19T21:51:00+00:00"
"time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/stopwatch",

View File

@@ -299,27 +299,6 @@ DKR_BUILD_LOCALE=false
# Won't significantly speed up things.
DKR_CHECK_SQLITE=true
# Run database creation and migration commands. Disable this only if you're 100% sure the DB exists
# and is up to date.
DKR_RUN_MIGRATION=true
# Run database upgrade commands. Disable this only when you're 100% sure your DB is up-to-date
# with the latest fixes (outside of migrations!)
DKR_RUN_UPGRADE=true
# Verify database integrity. Includes all data checks and verifications.
# Disabling this makes Firefly III assume your DB is intact.
DKR_RUN_VERIFY=true
# Run database reporting commands. When disabled, Firefly III won't go over your data to report current state.
# Disabling this should have no impact on data integrity or safety but it won't warn you of possible issues.
DKR_RUN_REPORT=true
# Generate OAuth2 keys.
# When disabled, Firefly III won't attempt to generate OAuth2 Passport keys. This won't be an issue, IFF (if and only if)
# you had previously generated keys already and they're stored in your database for restoration.
DKR_RUN_PASSPORT_INSTALL=true
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII

View File

@@ -13,7 +13,7 @@ jobs:
close_duplicates:
runs-on: ubuntu-latest
steps:
- uses: github/command@v1.1.1
- uses: github/command@v1.2.0
id: command
with:
allowed_contexts: "issue"

View File

@@ -67,6 +67,15 @@ jobs:
env:
FIREFLY_III_ROOT: /github/workspace
GH_TOKEN: ${{ secrets.CHANGELOG_TOKEN }}
- name: "Create THANKS.md"
id: thank-you
uses: JC5/firefly-iii-dev@main
with:
action: 'ff3:thank-you'
output: ''
env:
FIREFLY_III_ROOT: /github/workspace
GH_TOKEN: ''
- name: Extract changelog
id: extract-changelog
uses: JC5/firefly-iii-dev@main
@@ -245,7 +254,7 @@ jobs:
echo '' >> output.txt
echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
echo "* The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)."
echo "* The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
echo "Create default release."
git tag -a $releaseName -m "Here be changelog"

View File

@@ -63,7 +63,7 @@ class StoreRequest extends FormRequest
'order' => $this->convertInteger('order'),
'currency_code' => $this->convertString('currency_code'),
'virtual_balance' => $this->convertString('virtual_balance'),
'iban' => $this->convertString('iban'),
'iban' => $this->convertIban('iban'),
'BIC' => $this->convertString('bic'),
'account_number' => $this->convertString('account_number'),
'account_role' => $this->convertString('account_role'),

View File

@@ -51,7 +51,7 @@ class UpdateRequest extends FormRequest
'include_net_worth' => ['include_net_worth', 'boolean'],
'account_type_name' => ['type', 'convertString'],
'virtual_balance' => ['virtual_balance', 'convertString'],
'iban' => ['iban', 'convertString'],
'iban' => ['iban', 'convertIban'],
'BIC' => ['bic', 'convertString'],
'account_number' => ['account_number', 'convertString'],
'account_role' => ['account_role', 'convertString'],

View File

@@ -103,14 +103,14 @@ class StoreRequest extends FormRequest
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string)$object['source_id']),
'source_name' => $this->clearString((string)$object['source_name']),
'source_iban' => $this->clearString((string)$object['source_iban']),
'source_iban' => $this->clearIban((string)$object['source_iban']),
'source_number' => $this->clearString((string)$object['source_number']),
'source_bic' => $this->clearString((string)$object['source_bic']),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
'destination_name' => $this->clearString((string)$object['destination_name']),
'destination_iban' => $this->clearString((string)$object['destination_iban']),
'destination_iban' => $this->clearIban((string)$object['destination_iban']),
'destination_number' => $this->clearString((string)$object['destination_number']),
'destination_bic' => $this->clearString((string)$object['destination_bic']),

View File

@@ -28,22 +28,21 @@ use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Autocomplete\AutocompleteRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Models\AccountBalance;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/**
* Class AccountController
*/
class AccountController extends Controller
{
use AccountFilter;
private AdminAccountRepositoryInterface $adminRepository;
private array $balanceTypes;
private AccountRepositoryInterface $repository;
private AccountRepositoryInterface $repository;
private TransactionCurrency $default;
private ExchangeRateConverter $converter;
/**
* AccountController constructor.
@@ -53,79 +52,88 @@ class AccountController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(AccountRepositoryInterface::class);
$this->adminRepository = app(AdminAccountRepositoryInterface::class);
$this->adminRepository->setUserGroup($this->validateUserGroup($request));
$userGroup = $this->validateUserGroup($request);
$this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUserGroup($userGroup);
$this->default = app('amount')->getDefaultCurrency();
$this->converter = app(ExchangeRateConverter::class);
return $next($request);
}
);
$this->balanceTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
}
/**
* Documentation for this endpoint:
* TODO list of checks
* 1. use dates from ParameterBag
* 2. Request validates dates
* 3. Request includes user_group_id
* 4. Endpoint is documented.
* 5. Collector uses user_group_id
*
* @throws FireflyException
* @throws FireflyException
* Documentation: https://api-docs.firefly-iii.org/?urls.primaryName=2.1.0%20(v2)#/autocomplete/getAccountsAC
*/
public function accounts(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$types = $data['types'];
$query = $data['query'];
$date = $this->parameters->get('date') ?? today(config('app.timezone'));
$result = $this->adminRepository->searchAccount((string) $query, $types, $data['limit']);
$defaultCurrency = app('amount')->getDefaultCurrency();
$groupedResult = [];
$allItems = [];
$queryParameters = $request->getParameters();
$result = $this->repository->searchAccount($queryParameters['query'], $queryParameters['account_types'], $queryParameters['size']);
$return = [];
/** @var Account $account */
foreach ($result as $account) {
$nameWithBalance = $account->name;
$currency = $this->repository->getAccountCurrency($account) ?? $defaultCurrency;
if (in_array($account->accountType->type, $this->balanceTypes, true)) {
$balance = app('steam')->balance($account, $date);
$nameWithBalance = sprintf('%s (%s)', $account->name, app('amount')->formatAnything($currency, $balance, false));
}
$type = (string) trans(sprintf('firefly.%s', $account->accountType->type));
$groupedResult[$type] ??= [
'group ' => $type,
'items' => [],
];
$allItems[] = [
'id' => (string) $account->id,
'value' => (string) $account->id,
'name' => $account->name,
'name_with_balance' => $nameWithBalance,
'label' => $nameWithBalance,
'type' => $account->accountType->type,
'currency_id' => (string) $currency->id,
'currency_name' => $currency->name,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
];
$return[] = $this->parseAccount($account);
}
usort(
$allItems,
static function (array $left, array $right): int {
$order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE];
$posLeft = (int) array_search($left['type'], $order, true);
$posRight = (int) array_search($right['type'], $order, true);
return response()->json($return);
}
return $posLeft - $posRight;
private function parseAccount(Account $account): array
{
$currency = $this->repository->getAccountCurrency($account);
return [
'id' => (string) $account->id,
'title' => $account->name,
'meta' => [
'type' => $account->accountType->type,
'currency_id' => null === $currency ? null : (string) $currency->id,
'currency_code' => $currency?->code,
'currency_symbol' => $currency?->symbol,
'currency_decimal_places' => $currency?->decimal_places,
'account_balances' => $this->getAccountBalances($account),
],
];
}
private function getAccountBalances(Account $account): array
{
$return = [];
$balances = $this->repository->getAccountBalances($account);
/** @var AccountBalance $balance */
foreach ($balances as $balance) {
try {
$return[] = $this->parseAccountBalance($balance);
} catch (FireflyException $e) {
Log::error(sprintf('Could not parse convert account balance: %s', $e->getMessage()));
}
);
}
return response()->json($allItems);
return $return;
}
/**
* @throws FireflyException
*/
private function parseAccountBalance(AccountBalance $balance): array
{
$currency = $balance->transactionCurrency;
return [
'title' => $balance->title,
'native_amount' => $this->converter->convert($currency, $this->default, today(), $balance->balance),
'amount' => app('steam')->bcround($balance->balance, $currency->decimal_places),
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'native_currency_id' => (string) $this->default->id,
'native_currency_code' => $this->default->code,
'native_currency_symbol' => $this->default->symbol,
'native_currency_decimal_places' => $this->default->decimal_places,
];
}
}

View File

@@ -53,23 +53,18 @@ class CategoryController extends Controller
}
/**
* Documentation for this endpoint:
* TODO list of checks
* 1. use dates from ParameterBag
* 2. Request validates dates
* 3. Request includes user_group_id
* 4. Endpoint is documented.
* 5. Collector uses user_group_id
* Documentation: https://api-docs.firefly-iii.org/?urls.primaryName=2.1.0%20(v2)#/autocomplete/getCategoriesAC
*/
public function categories(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$result = $this->repository->searchCategory($data['query'], $this->parameters->get('limit'));
$filtered = $result->map(
$queryParameters = $request->getParameters();
$result = $this->repository->searchCategory($queryParameters['query'], $queryParameters['size']);
$filtered = $result->map(
static function (Category $item) {
return [
'id' => (string)$item->id,
'name' => $item->name,
'id' => (string)$item->id,
'title' => $item->name,
'meta' => [],
];
}
);

View File

@@ -53,25 +53,20 @@ class TagController extends Controller
}
/**
* Documentation for this endpoint:
* TODO list of checks
* 1. use dates from ParameterBag
* 2. Request validates dates
* 3. Request includes user_group_id
* 4. Endpoint is documented.
* 5. Collector uses user_group_id
* Documentation: https://api-docs.firefly-iii.org/?urls.primaryName=2.1.0%20(v2)#/autocomplete/getTagsAC
*/
public function tags(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$result = $this->repository->searchTag($data['query'], $data['limit']);
$filtered = $result->map(
$queryParameters = $request->getParameters();
$result = $this->repository->searchTag($queryParameters['query'], $queryParameters['size']);
$filtered = $result->map(
static function (Tag $item) {
return [
'id' => (string)$item->id,
'name' => $item->tag,
'value' => (string)$item->id,
'id' => (string) $item->id,
'title' => $item->tag,
'value' => (string) $item->id,
'label' => $item->tag,
'meta' => [],
];
}
);

View File

@@ -53,30 +53,25 @@ class TransactionController extends Controller
}
/**
* Documentation for this endpoint:
* TODO list of checks
* 1. use dates from ParameterBag
* 2. Request validates dates
* 3. Request includes user_group_id
* 4. Endpoint is documented.
* 5. Collector uses user_group_id
* Documentation: https://api-docs.firefly-iii.org/?urls.primaryName=2.1.0%20(v2)#/autocomplete/getTransactionsAC
*/
public function transactionDescriptions(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$result = $this->repository->searchJournalDescriptions($data['query'], $data['limit']);
$queryParameters = $request->getParameters();
$result = $this->repository->searchJournalDescriptions($queryParameters['query'], $queryParameters['size']);
// limit and unique
$filtered = $result->unique('description');
$array = [];
$filtered = $result->unique('description');
$array = [];
/** @var TransactionJournal $journal */
foreach ($filtered as $journal) {
$array[] = [
'id' => (string)$journal->id,
'transaction_group_id' => (string)$journal->transaction_group_id,
'name' => $journal->description,
'description' => $journal->description,
'id' => (string) $journal->id,
'title' => $journal->description,
'meta' => [
'transaction_group_id' => (string) $journal->transaction_group_id,
],
];
}

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\Api\V2\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Chart\DashboardChartRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
@@ -47,7 +46,6 @@ class AccountController extends Controller
use ValidatesUserGroupTrait;
private AccountRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
public function __construct()
{
@@ -74,8 +72,6 @@ class AccountController extends Controller
* TODO validate and set user_group_id from request
*
* @throws FireflyException
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function dashboard(DashboardChartRequest $request): JsonResponse
{

View File

@@ -30,6 +30,7 @@ use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
use FireflyIII\Transformers\V2\AccountTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
class IndexController extends Controller
{
@@ -74,6 +75,7 @@ class IndexController extends Controller
// order is calculated in the account transformer and by that time it's too late.
$first = array_key_first($sorting);
$disablePagination = in_array($first, ['last_activity', 'balance', 'balance_difference'], true);
Log::debug(sprintf('Will disable pagination in account index v2? %s', var_export($disablePagination, true)));
if (!$disablePagination) {
$accounts = $accounts->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
}

View File

@@ -23,47 +23,68 @@ declare(strict_types=1);
namespace FireflyIII\Api\V2\Request\Autocomplete;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\AccountType;
use FireflyIII\JsonApi\Rules\IsValidFilter;
use FireflyIII\JsonApi\Rules\IsValidPage;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Http\Api\ParsesQueryFilters;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use LaravelJsonApi\Core\Query\QueryParameters;
use LaravelJsonApi\Validation\Rule as JsonApiRule;
/**
* Class AutocompleteRequest
*/
class AutocompleteRequest extends FormRequest
{
use AccountFilter;
use ChecksLogin;
use ConvertsDataTypes;
use ParsesQueryFilters;
protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS];
public function getData(): array
/**
* Loops over all possible query parameters (these are shared over ALL auto complete requests)
* and returns a validated array of parameters.
*
* The advantage is a single class. But you may also submit "account types" to an endpoint that doesn't use these.
*/
public function getParameters(): array
{
$types = $this->convertString('types');
$array = [];
if ('' !== $types) {
$array = explode(',', $types);
}
$limit = $this->convertInteger('limit');
$limit = 0 === $limit ? 10 : $limit;
// remove 'initial balance' and another from allowed types. its internal
$array = array_diff($array, [AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION]);
$queryParameters = QueryParameters::cast($this->all());
return [
'types' => $array,
'query' => $this->convertString('query'),
'date' => $this->getCarbonDate('date'),
'limit' => $limit,
'date' => $this->dateOrToday($queryParameters, 'date'),
'query' => $this->arrayOfStrings($queryParameters, 'query'),
'size' => $this->integerFromQueryParams($queryParameters, 'size', 50),
'account_types' => $this->getAccountTypeParameter($this->arrayOfStrings($queryParameters, 'account_types')),
];
}
public function rules(): array
{
return [
'limit' => 'min:0|max:1337',
'fields' => JsonApiRule::notSupported(),
'filter' => ['nullable', 'array', new IsValidFilter(['query', 'date', 'account_types'])],
'include' => JsonApiRule::notSupported(),
'page' => ['nullable', 'array', new IsValidPage(['size'])],
'sort' => JsonApiRule::notSupported(),
];
}
private function getAccountTypeParameter(mixed $types): array
{
if (is_string($types) && str_contains($types, ',')) {
$types = explode(',', $types);
}
if (!is_iterable($types)) {
$types = [$types];
}
$return = [];
foreach ($types as $type) {
$return = array_merge($return, $this->mapAccountTypes($type));
}
return array_unique($return);
}
}

View File

@@ -0,0 +1,112 @@
<?php
/*
* DashboardChartRequest.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V2\Request\Chart;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\JsonApi\Rules\IsValidFilter;
use FireflyIII\Support\Http\Api\ParsesQueryFilters;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
use LaravelJsonApi\Core\Query\QueryParameters;
use LaravelJsonApi\Validation\Rule as JsonApiRule;
/**
* Class ChartRequest
*/
class ChartRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
use ParsesQueryFilters;
use ValidatesUserGroupTrait;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
public function getParameters(): array
{
$queryParameters = QueryParameters::cast($this->all());
return [
'start' => $this->dateOrToday($queryParameters, 'start'),
'end' => $this->dateOrToday($queryParameters, 'end'),
// preselected heeft maar een paar toegestane waardes.
// 'query' => $this->arrayOfStrings($queryParameters, 'query'),
// 'size' => $this->integerFromQueryParams($queryParameters,'size', 50),
// 'account_types' => $this->getAccountTypeParameter($this->arrayOfStrings($queryParameters, 'account_types')),
];
}
// return [
// 'accounts' => $this->getAccountList(),
// 'preselected' => $this->convertString('preselected'),
// ];
// }
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'fields' => JsonApiRule::notSupported(),
'filter' => ['nullable', 'array', new IsValidFilter(['start', 'end', 'preselected', 'accounts'])],
'include' => JsonApiRule::notSupported(),
'page' => JsonApiRule::notSupported(),
'sort' => JsonApiRule::notSupported(),
];
// return [
// 'start' => 'required|date|after:1900-01-01|before:2099-12-31',
// 'end' => 'required|date|after_or_equal:start|before:2099-12-31|after:1900-01-01',
// 'preselected' => sprintf('in:%s', implode(',', config('firefly.preselected_accounts'))),
// 'accounts.*' => 'exists:accounts,id',
// ];
}
public function withValidator(Validator $validator): void
{
$validator->after(
static function (Validator $validator): void {
// validate transaction query data.
$data = $validator->getData();
if (!array_key_exists('accounts', $data)) {
// $validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts']));
return;
}
if (!is_array($data['accounts'])) {
$validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts']));
}
}
);
if ($validator->fails()) {
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Support\Models\AccountBalanceCalculator;
use Illuminate\Console\Command;
/**
* Class CorrectionSkeleton
*/
class CorrectAccountBalance extends Command
{
use ShowsFriendlyMessages;
protected $description = 'Recalculate all account balance amounts';
protected $signature = 'firefly-iii:correct-account-balance';
public function handle(): int
{
$this->correctBalanceAmounts();
return 0;
}
private function correctBalanceAmounts(): void
{
AccountBalanceCalculator::recalculateAll();
}
}

View File

@@ -74,6 +74,7 @@ class CorrectDatabase extends Command
'firefly-iii:unify-group-accounts',
'firefly-iii:trigger-credit-recalculation',
'firefly-iii:migrate-preferences',
'firefly-iii:correct-account-balance',
];
foreach ($commands as $command) {
$this->friendlyLine(sprintf('Now executing command "%s"', $command));

View File

@@ -60,15 +60,13 @@ class FixIbans extends Command
{
/** @var Account $account */
foreach ($accounts as $account) {
$iban = $account->iban;
if (str_contains($iban, ' ')) {
$iban = app('steam')->filterSpaces((string)$account->iban);
if ('' !== $iban) {
$account->iban = $iban;
$account->save();
$this->friendlyInfo(sprintf('Removed spaces from IBAN of account #%d', $account->id));
++$this->count;
}
$iban = (string) $account->iban;
$newIban = app('steam')->filterSpaces($iban);
if ('' !== $iban && $iban !== $newIban) {
$account->iban = $newIban;
$account->save();
$this->friendlyInfo(sprintf('Removed spaces from IBAN of account #%d', $account->id));
++$this->count;
}
}
}
@@ -81,7 +79,7 @@ class FixIbans extends Command
foreach ($accounts as $account) {
$userId = $account->user_id;
$set[$userId] ??= [];
$iban = (string)$account->iban;
$iban = (string) $account->iban;
if ('' === $iban) {
continue;
}

View File

@@ -0,0 +1,49 @@
<?php
/*
* AccountBalance.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Entities;
use FireflyIII\Models\Account;
class AccountBalance
{
public string $id;
public string $amount;
public string $currencyId;
public static function fromArray(): self
{
$balance = new self();
$balance->id = (string) random_int(1, 1000);
$balance->name = (string) random_int(1, 1000);
$balance->amount = (string) random_int(1, 1000);
$balance->currencyId = '1';
return $balance;
}
public function getAccount(): Account
{
return Account::inRandomOrder()->first();
}
}

View File

@@ -36,6 +36,7 @@ use Illuminate\Session\TokenMismatchException;
use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException as LaravelValidationException;
use Laravel\Passport\Exceptions\OAuthServerException as LaravelOAuthException;
use LaravelJsonApi\Core\Exceptions\JsonApiException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpFoundation\Response;
@@ -63,6 +64,7 @@ class Handler extends ExceptionHandler
HttpException::class,
SuspiciousOperationException::class,
BadHttpHeaderException::class,
JsonApiException::class,
];
/**

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\Transaction;
use FireflyIII\Support\Models\AccountBalanceCalculator;
/**
* Class TransactionObserver
@@ -35,4 +36,16 @@ class TransactionObserver
app('log')->debug('Observe "deleting" of a transaction.');
$transaction?->transactionJournal?->delete();
}
public function updated(Transaction $transaction): void
{
app('log')->debug('Observe "updated" of a transaction.');
AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal);
}
public function created(Transaction $transaction): void
{
app('log')->debug('Observe "created" of a transaction.');
AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal);
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Api\V3\Controllers;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\JsonApi\V3\AccountBalances\AccountBalanceSchema;
use FireflyIII\Models\Account;
use Illuminate\Contracts\Support\Responsable;
use LaravelJsonApi\Core\Facades\JsonApi;
use LaravelJsonApi\Core\Responses\DataResponse;
use LaravelJsonApi\Laravel\Http\Controllers\Actions;
use LaravelJsonApi\Laravel\Http\Requests\AnonymousQuery;
class AccountController extends Controller
{
use Actions\AttachRelationship;
use Actions\Destroy;
use Actions\DetachRelationship;
use Actions\FetchMany;
use Actions\FetchOne;
use Actions\FetchRelated;
use Actions\FetchRelationship;
use Actions\Store;
use Actions\Update;
use Actions\UpdateRelationship;
public function readAccountBalances(AnonymousQuery $query, AccountBalanceSchema $schema, Account $account): Responsable
{
$schema = JsonApi::server()->schemas()->schemaFor('account-balances');
$models = $schema
->repository()
->queryAll()
->withRequest($query)
->withAccount($account)
->get()
;
return DataResponse::make($models);
}
}

View File

@@ -86,14 +86,15 @@ class DebugController extends Controller
{
app('preferences')->mark();
$request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range', 'temp-mfa-secret', 'temp-mfa-codes']);
app('log')->debug('Call cache:clear...');
Artisan::call('cache:clear');
app('log')->debug('Call config:clear...');
Artisan::call('config:clear');
app('log')->debug('Call route:clear...');
Artisan::call('route:clear');
app('log')->debug('Call twig:clean...');
Artisan::call('view:clear');
// also do some recalculations.
Artisan::call('firefly-iii:correct-account-balance');
Artisan::call('firefly-iii:trigger-credit-recalculation');
try {
Artisan::call('twig:clean');
@@ -101,7 +102,6 @@ class DebugController extends Controller
throw new FireflyException($e->getMessage(), 0, $e);
}
app('log')->debug('Call view:clear...');
Artisan::call('view:clear');
return redirect(route('index'));

View File

@@ -57,7 +57,7 @@ class AccountFormRequest extends FormRequest
'account_type_name' => $this->convertString('objectType'),
'currency_id' => $this->convertInteger('currency_id'),
'virtual_balance' => $this->convertString('virtual_balance'),
'iban' => $this->convertString('iban'),
'iban' => $this->convertIban('iban'),
'BIC' => $this->convertString('BIC'),
'account_number' => $this->convertString('account_number'),
'account_role' => $this->convertString('account_role'),

View File

@@ -29,6 +29,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Bus\Queueable;
@@ -100,7 +101,7 @@ class DownloadExchangeRates implements ShouldQueue
try {
$res = $client->get($url);
} catch (RequestException $e) {
} catch (ConnectException|RequestException $e) {
app('log')->warning(sprintf('Trying to grab "%s" resulted in error "%d".', $url, $e->getMessage()));
return;

View File

@@ -0,0 +1,53 @@
<?php
/*
* IsValidFilter.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\JsonApi\Rules;
use Illuminate\Contracts\Validation\ValidationRule;
class IsValidFilter implements ValidationRule
{
private array $allowed;
public function __construct(array $keys)
{
$this->allowed = $keys;
$this->allowed[] = 'user_group_id';
}
#[\Override]
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
if ('filter' !== $attribute) {
$fail('validation.bad_api_filter')->translate();
}
if (!is_array($value)) {
$value = explode(',', $value);
}
foreach ($value as $key => $val) {
if (!in_array($key, $this->allowed, true)) {
$fail('validation.bad_api_filter')->translate(['filter' => $key]);
}
}
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* IsValidFilter.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\JsonApi\Rules;
use Illuminate\Contracts\Validation\ValidationRule;
class IsValidPage implements ValidationRule
{
private array $allowed;
public function __construct(array $keys)
{
$this->allowed = $keys;
}
#[\Override]
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
if ('page' !== $attribute) {
$fail('validation.bad_api_filter')->translate();
}
if (!is_array($value)) {
$value = explode(',', $value);
}
foreach ($value as $key => $val) {
if (!in_array($key, $this->allowed, true)) {
$fail('validation.bad_api_page')->translate();
}
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* AccountBalanceRepository.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\AccountBalances;
use FireflyIII\Entities\AccountBalance;
use LaravelJsonApi\Contracts\Store\QueriesAll;
use LaravelJsonApi\NonEloquent\AbstractRepository;
class AccountBalanceRepository extends AbstractRepository implements QueriesAll
{
#[\Override]
public function find(string $resourceId): ?object
{
return AccountBalance::fromArray();
}
public function queryAll(): Capabilities\AccountBalanceQuery
{
return Capabilities\AccountBalanceQuery::make()
->withServer($this->server)
->withSchema($this->schema)
;
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\AccountBalances;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;
class AccountBalanceResource extends JsonApiResource
{
/**
* Get the resource id.
*/
public function id(): string
{
return $this->resource->id;
}
/**
* Get the resource's attributes.
*
* @param null|Request $request
*/
public function attributes($request): iterable
{
return [
'name' => $this->resource->amount,
'amount' => $this->resource->amount,
];
}
/**
* Get the resource's relationships.
*
* @param null|Request $request
*/
public function relationships($request): iterable
{
return [
$this->relation('account')->withData($this->resource->getAccount()),
];
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\AccountBalances;
use FireflyIII\Entities\AccountBalance;
use LaravelJsonApi\Core\Schema\Schema;
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;
use LaravelJsonApi\NonEloquent\Fields\Attribute;
use LaravelJsonApi\NonEloquent\Fields\ID;
class AccountBalanceSchema extends Schema
{
/**
* The model the schema corresponds to.
*/
public static string $model = AccountBalance::class;
/**
* Get the resource fields.
*/
public function fields(): array
{
return [
ID::make(),
Attribute::make('name'),
Attribute::make('amount'),
HasOne::make('account'),
];
}
/**
* Get the resource filters.
*/
public function filters(): array
{
return [
// Filter::make('id'),
];
}
public function repository(): AccountBalanceRepository
{
return AccountBalanceRepository::make()
->withServer($this->server)
->withSchema($this)
;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* AccountBalanceQuery.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\AccountBalances\Capabilities;
use FireflyIII\Entities\AccountBalance;
use FireflyIII\Models\Account;
use LaravelJsonApi\NonEloquent\Capabilities\QueryAll;
class AccountBalanceQuery extends QueryAll
{
private Account $account;
/**
* QuerySites constructor.
*/
public function __construct()
{
parent::__construct();
}
public function get(): iterable
{
return [
AccountBalance::fromArray(),
AccountBalance::fromArray(),
AccountBalance::fromArray(),
AccountBalance::fromArray(),
];
}
public function withAccount(Account $account): self
{
$this->account = $account;
return $this;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* AccountRepository.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Accounts;
use FireflyIII\Models\Account;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use LaravelJsonApi\Contracts\Store\QueriesAll;
use LaravelJsonApi\NonEloquent\AbstractRepository;
class AccountRepository extends AbstractRepository implements QueriesAll
{
use UsergroupAware;
/**
* SiteRepository constructor.
*/
public function __construct() {}
public function find(string $resourceId): ?object
{
return Account::find((int) $resourceId);
}
public function queryAll(): Capabilities\AccountQuery
{
return Capabilities\AccountQuery::make()
->withUserGroup($this->userGroup)
->withServer($this->server)
->withSchema($this->schema)
;
}
}

View File

@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Accounts;
use FireflyIII\Models\Account;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;
/**
* @property Account $resource
*/
class AccountResource extends JsonApiResource
{
/**
* Get the resource's attributes.
*
* @param null|Request $request
*/
public function attributes($request): iterable
{
return [
'created_at' => $this->resource->created_at,
'updated_at' => $this->resource->updated_at,
'name' => $this->resource->name,
'iban' => '' === $this->resource->iban ? null : $this->resource->iban,
'active' => $this->resource->active,
'last_activity' => $this->resource->last_activity,
'type' => $this->resource->type,
'account_role' => $this->resource->account_role,
// 'virtual_balance' => $this->resource->virtual_balance,
// 'native_balance' => $this->resource->native_balance,
// 'user' => $this->resource->user_array,
// 'balances' => []
//
// currency
// 'currency_id' => $this->resource->currency_id,
// 'currency_code' => $this->resource->currency_code,
// 'currency_symbol' => $this->resource->currency_symbol,
// 'currency_decimal_places' => $this->resource->currency_decimal_places,
// balance (in currency, on date)
// 'current_balance' => $this->resource->current_balance,
// 'current_balance' => app('steam')->bcround(app('steam')->balance($account, $date), $decimalPlaces),
// 'current_balance_date' => $date->toAtomString(),
// 'notes' => $this->repository->getNoteText($account),
// 'monthly_payment_date' => $monthlyPaymentDate,
// 'credit_card_type' => $creditCardType,
// 'account_number' => $this->repository->getMetaValue($account, 'account_number'),
// 'bic' => $this->repository->getMetaValue($account, 'BIC'),
// 'opening_balance' => $openingBalance,
// 'opening_balance_date' => $openingBalanceDate,
// 'liability_type' => $liabilityType,
// 'liability_direction' => $liabilityDirection,
// 'interest' => $interest,
// 'interest_period' => $interestPeriod,
// 'current_debt' => $this->repository->getMetaValue($account, 'current_debt'),
// 'include_net_worth' => $includeNetWorth,
// 'longitude' => $longitude,
// 'latitude' => $latitude,
// 'zoom_level' => $zoomLevel,
// 'order' => $order,
// 'currency_id' => (string) $currency->id,
// 'currency_code' => $currency->code,
// 'currency_symbol' => $currency->symbol,
// 'currency_decimal_places' => $currency->decimal_places,
//
// 'native_currency_id' => (string) $this->default->id,
// 'native_currency_code' => $this->default->code,
// 'native_currency_symbol' => $this->default->symbol,
// 'native_currency_decimal_places' => $this->default->decimal_places,
//
// // balance:
// 'current_balance' => $balance,
// 'native_current_balance' => $nativeBalance,
// 'current_balance_date' => $this->getDate()->endOfDay()->toAtomString(),
//
// // balance difference
// 'balance_difference' => $balanceDiff,
// 'native_balance_difference' => $nativeBalanceDiff,
// 'balance_difference_start' => $diffStart,
// 'balance_difference_end' => $diffEnd,
//
// // more meta
// 'last_activity' => array_key_exists($id, $this->lastActivity) ? $this->lastActivity[$id]->toAtomString() : null,
//
// // liability stuff
// 'liability_type' => $liabilityType,
// 'liability_direction' => $liabilityDirection,
// 'interest' => $interest,
// 'interest_period' => $interestPeriod,
// 'current_debt' => $currentDebt,
//
// // object group
// 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null,
// 'object_group_order' => $objectGroupOrder,
// 'object_group_title' => $objectGroupTitle,
// 'notes' => $this->repository->getNoteText($account),
// 'monthly_payment_date' => $monthlyPaymentDate,
// 'credit_card_type' => $creditCardType,
// 'bic' => $this->repository->getMetaValue($account, 'BIC'),
// 'virtual_balance' => number_format((float) $account->virtual_balance, $decimalPlaces, '.', ''),
// 'opening_balance' => $openingBalance,
// 'opening_balance_date' => $openingBalanceDate,
// 'include_net_worth' => $includeNetWorth,
// 'longitude' => $longitude,
// 'latitude' => $latitude,
// 'zoom_level' => $zoomLevel,
];
}
/**
* Get the resource's relationships.
*
* @param null|Request $request
*/
public function relationships($request): iterable
{
return [
$this->relation('user')->withData($this->resource->user),
$this->relation('account_balances')->withData($this->resource->balances),
];
}
}

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Accounts;
use FireflyIII\Models\Account;
use LaravelJsonApi\Eloquent\Contracts\Paginator;
use LaravelJsonApi\Eloquent\Fields\Boolean;
use LaravelJsonApi\Eloquent\Fields\DateTime;
use LaravelJsonApi\Eloquent\Fields\ID;
use LaravelJsonApi\Eloquent\Fields\Number;
use LaravelJsonApi\Eloquent\Fields\Relations\HasMany;
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;
use LaravelJsonApi\Eloquent\Fields\Str;
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
use LaravelJsonApi\Eloquent\Schema;
class AccountSchema extends Schema
{
/**
* The model the schema corresponds to.
*/
public static string $model = Account::class;
/**
* Get the resource fields.
*/
public function fields(): array
{
return [
ID::make(),
DateTime::make('created_at')->sortable()->readOnly(),
DateTime::make('updated_at')->sortable()->readOnly(),
Str::make('name')->sortable(),
Str::make('account_type'),
Str::make('virtual_balance'),
Str::make('iban'),
Boolean::make('active'),
Number::make('order'),
HasOne::make('user'),
HasMany::make('account_balances'),
];
}
/**
* Get the resource filters.
*/
public function filters(): array
{
return [
WhereIdIn::make($this),
];
}
/**
* Get the resource paginator.
*/
public function pagination(): ?Paginator
{
return PagePagination::make();
}
}

View File

@@ -0,0 +1,73 @@
<?php
/*
* AccountQuery.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Accounts\Capabilities;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Support\JsonApi\ExpandsQuery;
use FireflyIII\Support\JsonApi\FiltersPagination;
use FireflyIII\Support\JsonApi\SortsCollection;
use FireflyIII\Support\JsonApi\ValidateSortParameters;
use LaravelJsonApi\Contracts\Store\HasPagination;
use LaravelJsonApi\NonEloquent\Capabilities\QueryAll;
use LaravelJsonApi\NonEloquent\Concerns\PaginatesEnumerables;
class AccountQuery extends QueryAll implements HasPagination
{
use ExpandsQuery;
use FiltersPagination;
use PaginatesEnumerables;
use SortsCollection;
use UsergroupAware;
use ValidateSortParameters;
#[\Override]
public function get(): iterable
{
$filters = $this->queryParameters->filter();
$sort = $this->queryParameters->sortFields();
$pagination = $this->filtersPagination($this->queryParameters->page());
$needsAll = $this->validateParams('account', $sort);
$query = $this->userGroup->accounts();
if (!$needsAll) {
$query = $this->addPagination($query, $pagination);
}
$query = $this->addSortParams($query, $sort);
$query = $this->addFilterParams('account', $query, $filters);
$collection = $query->get(['accounts.*']);
// enrich data
$enrichment = new AccountEnrichment();
$collection = $enrichment->enrich($collection);
// add filters after the query
// add sort after the query
return $this->sortCollection($collection, $sort);
// var_dump($filters->value('name'));
// exit;
}
}

38
app/JsonApi/V3/Server.php Normal file
View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3;
use FireflyIII\JsonApi\V3\Accounts\AccountSchema;
use FireflyIII\JsonApi\V3\AccountBalances\AccountBalanceSchema;
use FireflyIII\JsonApi\V3\Users\UserSchema;
use LaravelJsonApi\Core\Server\Server as BaseServer;
class Server extends BaseServer
{
/**
* The base URI namespace for this server.
*/
protected string $baseUri = '/api/v3';
/**
* Bootstrap the server when it is handling an HTTP request.
*/
public function serving(): void
{
// no-op
}
/**
* Get the server's list of schemas.
*/
protected function allSchemas(): array
{
return [
AccountSchema::class,
UserSchema::class,
AccountBalanceSchema::class,
];
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Users;
use FireflyIII\Models\User;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;
/**
* @property User $resource
*/
class UserResource extends JsonApiResource
{
/**
* Get the resource's attributes.
*
* @param null|Request $request
*/
public function attributes($request): iterable
{
return [
'created_at' => $this->resource->created_at,
'updated_at' => $this->resource->updated_at,
'email' => $this->resource->email,
];
}
/**
* Get the resource's relationships.
*
* @param null|Request $request
*/
public function relationships($request): iterable
{
return [
// @TODO
];
}
}

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V3\Users;
use FireflyIII\User;
use LaravelJsonApi\Eloquent\Contracts\Paginator;
use LaravelJsonApi\Eloquent\Fields\DateTime;
use LaravelJsonApi\Eloquent\Fields\ID;
use LaravelJsonApi\Eloquent\Fields\Relations\HasMany;
use LaravelJsonApi\Eloquent\Fields\Str;
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
use LaravelJsonApi\Eloquent\Schema;
class UserSchema extends Schema
{
/**
* The model the schema corresponds to.
*/
public static string $model = User::class;
/**
* Get the resource fields.
*/
public function fields(): array
{
return [
ID::make(),
DateTime::make('created_at')->sortable()->readOnly(),
DateTime::make('updated_at')->sortable()->readOnly(),
Str::make('email'),
HasMany::make('accounts'),
];
}
/**
* Get the resource filters.
*/
public function filters(): array
{
return [
WhereIdIn::make($this),
];
}
/**
* Get the resource paginator.
*/
public function pagination(): ?Paginator
{
return PagePagination::make();
}
}

View File

@@ -193,6 +193,11 @@ class Account extends Model
return $this->hasMany(AccountMeta::class);
}
public function accountBalances(): HasMany
{
return $this->hasMany(AccountBalance::class);
}
public function getEditNameAttribute(): string
{
$name = $this->name;

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AccountBalance extends Model
{
use HasFactory;
protected $fillable = ['account_id', 'title', 'transaction_currency_id', 'balance'];
public function account(): BelongsTo
{
return $this->belongsTo(Account::class);
}
public function transactionCurrency(): BelongsTo
{
return $this->belongsTo(TransactionCurrency::class);
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* AccountPolicy.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Policies;
use FireflyIII\Entities\AccountBalance;
use FireflyIII\User;
class AccountBalancePolicy
{
/**
* TODO needs better authentication.
*/
public function view(User $user, AccountBalance $accountBalance): bool
{
return true;
}
/**
* Everybody can do this, but selection should limit to user.
*
* @return true
*/
public function viewAny(): bool
{
return true;
}
}

View File

@@ -0,0 +1,67 @@
<?php
/*
* AccountPolicy.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Policies;
use FireflyIII\Models\Account;
use FireflyIII\User;
class AccountPolicy
{
/**
* TODO needs better authentication.
*/
public function view(User $user, Account $account): bool
{
return true;
return auth()->check() && $user->id === $account->user_id;
}
/**
* Everybody can do this, but selection should limit to user.
*
* @return true
*/
public function viewAny(): bool
{
return true;
return auth()->check();
}
/**
* Everybody can do this, but selection should limit to user.
*
* @return true
*/
public function viewUser(User $user, Account $account): bool
{
return $this->view($user, $account);
}
public function viewAccountBalances(User $user, Account $account): bool
{
return $this->view($user, $account);
}
}

View File

@@ -0,0 +1,48 @@
<?php
/*
* BalancePolicy.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Policies;
use FireflyIII\Models\Account;
use FireflyIII\User;
class BalancePolicy
{
/**
* TODO needs better authentication.
*/
public function view(User $user, Account $account): bool
{
return auth()->check() && $user->id === $account->user_id;
}
/**
* Everybody can do this, but selection should limit to user.
*
* @return true
*/
public function viewAny(): bool
{
return auth()->check();
}
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* UserPolicy.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Policies;
use FireflyIII\User;
class UserPolicy
{
/**
* TODO needs better authentication.
*/
public function view(User $user, User $user1): bool
{
return true;
return auth()->check() && $user->id === $account->user_id;
}
/**
* Everybody can do this, but selection should limit to user.
*
* @return true
*/
public function viewAny(): bool
{
return true;
return auth()->check();
}
public function viewAccounts(User $user): bool
{
return true;
return auth()->check();
}
}

View File

@@ -37,6 +37,7 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
use FireflyIII\Services\Internal\Update\AccountUpdateService;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
@@ -123,6 +124,7 @@ class AccountRepository implements AccountRepositoryInterface
public function findByIbanNull(string $iban, array $types): ?Account
{
$iban = Steam::filterSpaces($iban);
$query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban');
if (0 !== count($types)) {

View File

@@ -31,6 +31,7 @@ use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Services\Internal\Update\AccountUpdateService;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
@@ -80,6 +81,7 @@ class AccountRepository implements AccountRepositoryInterface
public function findByIbanNull(string $iban, array $types): ?Account
{
$iban = Steam::filterSpaces($iban);
$query = $this->userGroup->accounts()->where('iban', '!=', '')->whereNotNull('iban');
if (0 !== count($types)) {
@@ -290,7 +292,7 @@ class AccountRepository implements AccountRepositoryInterface
return $query->get(['accounts.*']);
}
public function searchAccount(string $query, array $types, int $limit): Collection
public function searchAccount(array $query, array $types, int $limit): Collection
{
// search by group, not by user
$dbQuery = $this->userGroup->accounts()
@@ -300,13 +302,17 @@ class AccountRepository implements AccountRepositoryInterface
->orderBy('accounts.name', 'ASC')
->with(['accountType'])
;
if ('' !== $query) {
if (count($query) > 0) {
// split query on spaces just in case:
$parts = explode(' ', $query);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$dbQuery->where('name', 'LIKE', $search);
}
$dbQuery->where(function (EloquentBuilder $q) use ($query): void {
foreach ($query as $line) {
$parts = explode(' ', $line);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhere('name', 'LIKE', $search);
}
}
});
}
if (0 !== count($types)) {
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
@@ -384,4 +390,10 @@ class AccountRepository implements AccountRepositoryInterface
return $return;
}
#[\Override]
public function getAccountBalances(Account $account): Collection
{
return $account->accountBalances;
}
}

View File

@@ -53,6 +53,8 @@ interface AccountRepositoryInterface
public function getAccountCurrency(Account $account): ?TransactionCurrency;
public function getAccountBalances(Account $account): Collection;
public function getAccountsById(array $accountIds): Collection;
public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection;
@@ -78,7 +80,7 @@ interface AccountRepositoryInterface
*/
public function resetAccountOrder(): void;
public function searchAccount(string $query, array $types, int $limit): Collection;
public function searchAccount(array $query, array $types, int $limit): Collection;
public function setUser(User $user): void;

View File

@@ -24,17 +24,27 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Category;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
class CategoryRepository implements CategoryRepositoryInterface
{
use UserGroupTrait;
public function searchCategory(string $query, int $limit): Collection
public function searchCategory(array $query, int $limit): Collection
{
$search = $this->userGroup->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
if (count($query) > 0) {
// split query on spaces just in case:
$search->where(function (EloquentBuilder $q) use ($query): void {
foreach ($query as $line) {
$parts = explode(' ', $line);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhere('name', 'LIKE', $search);
}
}
});
}
return $search->take($limit)->get();

View File

@@ -30,5 +30,5 @@ interface CategoryRepositoryInterface
/**
* Search for a category using wild cards. Uses the database, so case sensitive.
*/
public function searchCategory(string $query, int $limit): Collection;
public function searchCategory(array $query, int $limit): Collection;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Journal;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
/**
@@ -33,15 +34,24 @@ class JournalRepository implements JournalRepositoryInterface
{
use UserGroupTrait;
public function searchJournalDescriptions(string $search, int $limit): Collection
public function searchJournalDescriptions(array $query, int $limit): Collection
{
$query = $this->userGroup->transactionJournals()
$search = $this->userGroup->transactionJournals()
->orderBy('date', 'DESC')
;
if ('' !== $search) {
$query->where('description', 'LIKE', sprintf('%%%s%%', $search));
if (count($query) > 0) {
// split query on spaces just in case:
$search->where(function (EloquentBuilder $q) use ($query): void {
foreach ($query as $line) {
$parts = explode(' ', $line);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhere('description', 'LIKE', $search);
}
}
});
}
return $query->take($limit)->get();
return $search->take($limit)->get();
}
}

View File

@@ -34,7 +34,7 @@ interface JournalRepositoryInterface
/**
* Search in journal descriptions.
*/
public function searchJournalDescriptions(string $search, int $limit): Collection;
public function searchJournalDescriptions(array $query, int $limit): Collection;
public function setUser(User $user): void;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Tag;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
/**
@@ -33,11 +34,20 @@ class TagRepository implements TagRepositoryInterface
{
use UserGroupTrait;
public function searchTag(string $query, int $limit): Collection
public function searchTag(array $query, int $limit): Collection
{
$search = $this->user->tags();
if ('' !== $query) {
$search->where('tag', 'LIKE', sprintf('%%%s%%', $query));
$search = $this->userGroup->tags();
if (count($query) > 0) {
// split query on spaces just in case:
$search->where(function (EloquentBuilder $q) use ($query): void {
foreach ($query as $line) {
$parts = explode(' ', $line);
foreach ($parts as $part) {
$search = sprintf('%%%s%%', $part);
$q->orWhere('tag', 'LIKE', $search);
}
}
});
}
return $search->take($limit)->get(['tags.*']);

View File

@@ -30,5 +30,5 @@ interface TagRepositoryInterface
/**
* Find one or more tags based on the query.
*/
public function searchTag(string $query, int $limit): Collection;
public function searchTag(array $query, int $limit): Collection;
}

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Rules;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Contracts\Validation\ValidationRule;
/**
@@ -95,6 +96,9 @@ class UniqueIban implements ValidationRule
$maxCounts = $this->getMaxOccurrences();
foreach ($maxCounts as $type => $max) {
// make sure to trim the value of $value so all spaces are removed.
$value = Steam::filterSpaces($value);
$count = $this->countHits($type, $value);
app('log')->debug(sprintf('Count for "%s" and IBAN "%s" is %d', $type, $value, $count));
if ($count > $max) {

View File

@@ -0,0 +1,57 @@
<?php
/*
* ParsesQueryFilters.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\Http\Api;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Query\QueryParameters;
trait ParsesQueryFilters
{
private function dateOrToday(QueryParameters $parameters, string $field): Carbon
{
$date = today();
try {
$date = Carbon::createFromFormat('Y-m-d', $parameters->filter()?->value($field, date('Y-m-d')), config('app.timezone'));
} catch (InvalidFormatException $e) {
Log::debug(sprintf('Invalid date format in request. Using today: %s', $e->getMessage()));
}
return $date;
}
private function arrayOfStrings(QueryParameters $parameters, string $field): array
{
$array = $parameters->filter()?->value($field, []) ?? [];
return is_string($array) ? [$array] : $array;
}
private function integerFromQueryParams(QueryParameters $parameters, string $field, int $default): int
{
return (int) ($parameters->page()[$field] ?? $default);
}
}

View File

@@ -38,6 +38,10 @@ use Illuminate\Support\Facades\Log;
trait ValidatesUserGroupTrait
{
/**
* An "undocumented" filter
*
* TODO add this filter to the API docs.
*
* @throws AuthorizationException
* @throws AuthenticationException
*/

View File

@@ -0,0 +1,48 @@
<?php
/*
* UsergroupAware.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Concerns;
use FireflyIII\Models\UserGroup;
trait UsergroupAware
{
protected UserGroup $userGroup;
public function getUserGroup(): UserGroup
{
return $this->userGroup;
}
public function setUserGroup(UserGroup $userGroup): void
{
$this->userGroup = $userGroup;
}
public function withUserGroup(UserGroup $userGroup): self
{
$this->userGroup = $userGroup;
return $this;
}
}

View File

@@ -0,0 +1,149 @@
<?php
/*
* AccountEnricher.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class AccountEnrichment implements EnrichmentInterface
{
private Collection $collection;
private array $currencies;
#[\Override]
public function enrich(Collection $collection): Collection
{
$this->collection = $collection;
$this->currencies = [];
// do everything here:
$this->getLastActivity();
// $this->getMetaBalances();
$this->collectAccountTypes();
$this->collectMetaData();
$this->collection->transform(function (Account $account) {
$account->user_array = ['id' => 1, 'bla bla' => 'bla'];
$account->balances = collect([
['balance_id' => 1, 'balance' => 5],
['balance_id' => 2, 'balance' => 5],
['balance_id' => 3, 'balance' => 5],
]);
return $account;
});
return $this->collection;
}
/**
* TODO this method refers to a single-use method inside Steam that could be moved here.
*/
private function getLastActivity(): void
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$lastActivity = $accountRepository->getLastActivity($this->collection);
foreach ($lastActivity as $row) {
$this->collection->where('id', $row['account_id'])->first()->last_activity = Carbon::parse($row['date_max'], config('app.timezone'));
}
}
/**
* TODO this method refers to a single-use method inside Steam that could be moved here.
*/
private function getMetaBalances(): void
{
try {
$array = app('steam')->balancesByAccountsConverted($this->collection, today());
} catch (FireflyException $e) {
Log::error(sprintf('Could not load balances: %s', $e->getMessage()));
return;
}
foreach ($array as $accountId => $row) {
$this->collection->where('id', $accountId)->first()->balance = $row['balance'];
$this->collection->where('id', $accountId)->first()->native_balance = $row['native_balance'];
}
}
/**
* TODO this method refers to a single-use method inside Steam that could be moved here.
*/
private function collectAccountTypes(): void
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$accountTypes = $accountRepository->getAccountTypes($this->collection);
$types = [];
/** @var AccountType $row */
foreach ($accountTypes as $row) {
$types[$row->id] = $row->type;
}
$this->collection->transform(function (Account $account) use ($types) {
$account->type = $types[$account->id];
return $account;
});
}
private function collectMetaData(): void
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$metaFields = $accountRepository->getMetaValues($this->collection, ['currency_id', 'account_role', 'account_number', 'liability_direction', 'interest', 'interest_period', 'current_debt']);
$currencyIds = $metaFields->where('name', 'currency_id')->pluck('data')->toArray();
$currencies = [];
foreach ($repository->getByIds($currencyIds) as $currency) {
$id = $currency->id;
$currencies[$id] = $currency;
}
$this->collection->transform(function (Account $account) use ($metaFields, $currencies) {
$set = $metaFields->where('account_id', $account->id);
foreach ($set as $entry) {
$account->{$entry->name} = $entry->data;
if ('currency_id' === $entry->name) {
$id = (int) $entry->data;
$account->currency_code = $currencies[$id]?->code;
$account->currency_symbol = $currencies[$id]?->symbol;
$account->currency_decimal_places = $currencies[$id]?->decimal_places;
}
}
return $account;
});
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* EnricherInterface.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
use Illuminate\Support\Collection;
interface EnrichmentInterface
{
public function enrich(Collection $collection): Collection;
}

View File

@@ -0,0 +1,85 @@
<?php
/*
* ExpandsQuery.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi;
use FireflyIII\Support\Http\Api\AccountFilter;
use Illuminate\Contracts\Database\Eloquent\Builder;
use LaravelJsonApi\Core\Query\FilterParameters;
use LaravelJsonApi\Core\Query\SortFields;
trait ExpandsQuery
{
use AccountFilter;
final protected function addPagination(Builder $query, array $pagination): Builder
{
$skip = ($pagination['number'] - 1) * $pagination['size'];
return $query->skip($skip)->take($pagination['size']);
}
final protected function addSortParams(Builder $query, ?SortFields $sort): Builder
{
if (null === $sort) {
return $query;
}
foreach ($sort->all() as $sortField) {
$query->orderBy($sortField->name(), $sortField->isAscending() ? 'ASC' : 'DESC');
}
return $query;
}
final protected function addFilterParams(string $class, Builder $query, ?FilterParameters $filters): Builder
{
if (null === $filters) {
return $query;
}
$config = config(sprintf('firefly.valid_query_filters.%s', $class)) ?? [];
if (0 === count($filters->all())) {
return $query;
}
$query->where(function (Builder $q) use ($config, $filters): void {
foreach ($filters->all() as $filter) {
if (in_array($filter->key(), $config, true)) {
foreach ($filter->value() as $value) {
$q->where($filter->key(), 'LIKE', sprintf('%%%s%%', $value));
}
}
}
});
// some filters are special, i.e. the account type filter.
$typeFilters = $filters->value('type', false);
if (false !== $typeFilters && count($typeFilters) > 0) {
$query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
foreach ($typeFilters as $typeFilter) {
$types = $this->mapAccountTypes($typeFilter);
$query->whereIn('account_types.type', $types);
}
}
return $query;
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* FiltersPagination.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi;
trait FiltersPagination
{
protected function filtersPagination(?array $pagination): array
{
if (null === $pagination) {
return [
'number' => 1,
'size' => $this->getPageSize(),
];
}
// cleanup page number
$pagination['number'] = (int) ($pagination['number'] ?? 1);
$pagination['number'] = min(65536, max($pagination['number'], 1));
// clean up page size
$pagination['size'] = (int) ($pagination['size'] ?? $this->getPageSize());
$pagination['size'] = min(1337, max($pagination['size'], 1));
return $pagination;
}
private function getPageSize(): int
{
if (auth()->check()) {
return (int) app('preferences')->get('listPageSize', 50)->data;
}
return 50;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* SortsCollection.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi;
use Illuminate\Support\Collection;
use LaravelJsonApi\Core\Query\SortFields;
trait SortsCollection
{
protected function sortCollection(Collection $collection, ?SortFields $sortFields): Collection
{
if (null === $sortFields) {
return $collection;
}
foreach ($sortFields->all() as $sortField) {
$collection = $sortField->isAscending() ? $collection->sortBy($sortField->name()) : $collection->sortByDesc($sortField->name());
}
return $collection;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* ValidateSortParameters.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi;
use LaravelJsonApi\Core\Query\SortFields;
trait ValidateSortParameters
{
public function validateParams(string $class, ?SortFields $params): bool
{
if (null === $params) {
return false;
}
$config = config(sprintf('firefly.full_data_set.%s', $class)) ?? [];
foreach ($params->all() as $field) {
if (in_array($field->name(), $config, true)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,216 @@
<?php
/*
* AccountBalanceCalculator.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\Models;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountBalance;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Facades\Log;
class AccountBalanceCalculator
{
private function __construct()
{
// no-op
}
/**
* Recalculate all balances for a given account.
*
* Je moet toch altijd wel alles doen want je weet niet waar een transaction journal invloed op heeft.
* Dus dit aantikken per transaction journal is zinloos, beide accounts moeten gedaan worden.
*/
public static function recalculateAll(): void
{
$object = new self();
$object->recalculateLatest(null);
// $object->recalculateJournals(null, null);
}
public static function recalculateForJournal(TransactionJournal $transactionJournal): void
{
$object = new self();
foreach ($transactionJournal->transactions as $transaction) {
$object->recalculateLatest($transaction->account);
// $object->recalculateJournals($transaction->account, $transactionJournal);
}
}
private function getAccountBalanceByAccount(int $account, int $currency): AccountBalance
{
$query = AccountBalance::where('title', 'balance')->where('account_id', $account)->where('transaction_currency_id', $currency);
$entry = $query->first();
if (null !== $entry) {
// Log::debug(sprintf('Found account balance "balance" for account #%d and currency #%d: %s', $account, $currency, $entry->balance));
return $entry;
}
$entry = new AccountBalance();
$entry->title = 'balance';
$entry->account_id = $account;
$entry->transaction_currency_id = $currency;
$entry->balance = '0';
$entry->save();
// Log::debug(sprintf('Created new account balance for account #%d and currency #%d: %s', $account, $currency, $entry->balance));
return $entry;
}
private function getAccountBalanceByJournal(string $title, int $account, int $journal, int $currency): AccountBalance
{
$query = AccountBalance::where('title', $title)->where('account_id', $account)->where('transaction_journal_id', $journal)->where('transaction_currency_id', $currency);
$entry = $query->first();
if (null !== $entry) {
return $entry;
}
$entry = new AccountBalance();
$entry->title = $title;
$entry->account_id = $account;
$entry->transaction_journal_id = $journal;
$entry->transaction_currency_id = $currency;
$entry->balance = '0';
$entry->save();
return $entry;
}
private function recalculateLatest(?Account $account): void
{
$query = Transaction::groupBy(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
if (null !== $account) {
$query->where('transactions.account_id', $account->id);
}
$result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
// reset account balances:
$this->resetAccountBalancesByAccount('balance', $account);
/** @var \stdClass $row */
foreach ($result as $row) {
$account = (int) $row->account_id;
$transactionCurrency = (int) $row->transaction_currency_id;
$foreignCurrency = (int) $row->foreign_currency_id;
$sumAmount = $row->sum_amount;
$sumForeignAmount = $row->sum_foreign_amount;
// first create for normal currency:
$entry = $this->getAccountBalanceByAccount($account, $transactionCurrency);
$entry->balance = bcadd($entry->balance, $sumAmount);
$entry->save();
// then do foreign amount, if present:
if ($foreignCurrency > 0) {
$entry = $this->getAccountBalanceByAccount($account, $foreignCurrency);
$entry->balance = bcadd($entry->balance, $sumForeignAmount);
$entry->save();
}
}
Log::debug(sprintf('Recalculated %d account balance(s)', $result->count()));
}
private function resetAccountBalancesByAccount(string $title, ?Account $account): void
{
if (null === $account) {
$count = AccountBalance::whereNotNull('updated_at')->where('title', $title)->update(['balance' => '0']);
Log::debug(sprintf('Set %d account balance(s) to zero.', $count));
return;
}
$count = AccountBalance::where('account_id', $account->id)->where('title', $title)->update(['balance' => '0']);
Log::debug(sprintf('Set %d account balance(s) of account #%d to zero.', $count, $account->id));
}
/**
* Als je alles opnieuw doet, verzamel je alle transactions en het bedrag en zet je dat neer als "balance after journal".
* Dat betekent, netjes op volgorde van datum en doorrekenen.
*
* Zodra je een transaction journal verplaatst (datum) moet je dat journal en alle latere journals opnieuw doen.
* Maar dan moet je van de account wel een beginnetje hebben, namelijk de balance tot en met dat moment.
*
* 1. Dus dan search je eerst naar die SUM, som alle transactions eerder dan (niet inclusief) de journal.
* 2. En vanaf daar pak je alle journals op of na de journal (dus ook de journal zelf) en begin je door te tellen.
* 3. Elke voorbij gaande journal entry "balance_after_journal" geef je een update of voeg je toe.
*/
private function recalculateJournals(?Account $account, ?TransactionJournal $transactionJournal): void
{
$query = Transaction::groupBy(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
$query->orderBy('transaction_journals.date', 'asc');
$amounts = [];
if (null !== $account) {
$query->where('transactions.account_id', $account->id);
}
if (null !== $account && null !== $transactionJournal) {
$query->where('transaction_journals.date', '>=', $transactionJournal->date);
$amounts = $this->getStartAmounts($account, $transactionJournal);
}
$result = $query->get(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
/** @var \stdClass $row */
foreach ($result as $row) {
$account = (int) $row->account_id;
$transactionCurrency = (int) $row->transaction_currency_id;
$foreignCurrency = (int) $row->foreign_currency_id;
$sumAmount = $row->sum_amount;
$sumForeignAmount = $row->sum_foreign_amount;
$journalId = (int) $row->id;
// new amounts:
$amounts[$account][$transactionCurrency] = bcadd($amounts[$account][$transactionCurrency] ?? '0', $sumAmount ?? '0');
$amounts[$account][$foreignCurrency] = bcadd($amounts[$account][$foreignCurrency] ?? '0', $sumForeignAmount ?? '0');
// first create for normal currency:
$entry = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $transactionCurrency);
$entry->balance = $amounts[$account][$transactionCurrency];
$entry->save();
// then do foreign amount, if present:
if ($foreignCurrency > 0) {
$entry = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $foreignCurrency);
$entry->balance = $amounts[$account][$foreignCurrency];
$entry->save();
}
}
// select transactions.account_id, transaction_journals.id, transactions.transaction_currency_id, transactions.foreign_currency_id, sum(transactions.amount), sum(transactions.foreign_amount)
//
// from transactions
//
// left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
//
// group by account_id, transaction_journals.id, transaction_currency_id, foreign_currency_id
// order by transaction_journals.date desc
}
private function getStartAmounts(Account $account, TransactionJournal $journal): array
{
exit('here we are');
return [];
}
}

View File

@@ -27,6 +27,7 @@ use Carbon\Carbon;
use Carbon\Exceptions\InvalidDateException;
use Carbon\Exceptions\InvalidFormatException;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Collection;
/**
@@ -114,6 +115,11 @@ trait ConvertsDataTypes
return (string)$this->clearString((string)$entry);
}
public function convertIban(string $field): string
{
return Steam::filterSpaces($this->convertString($field));
}
public function clearString(?string $string): ?string
{
$string = $this->clearStringKeepNewlines($string);
@@ -131,6 +137,13 @@ trait ConvertsDataTypes
return trim($string);
}
public function clearIban(?string $string): ?string
{
$string = $this->clearString($string);
return Steam::filterSpaces($string);
}
public function clearStringKeepNewlines(?string $string): ?string
{
if (null === $string) {

View File

@@ -681,11 +681,13 @@ class Steam
"\u{202F}", // narrow no-break space
"\u{3000}", // ideographic space
"\u{FEFF}", // zero width no -break space
"\x20", // plain old normal space
"\x20", // plain old normal space,
' ',
];
// clear zalgo text
$string = preg_replace('/(\pM{2})\pM+/u', '\1', $string);
$string = preg_replace('/\s+/', '', $string);
return str_replace($search, '', $string);
}

View File

@@ -63,6 +63,8 @@ class AccountTransformer extends AbstractTransformer
$this->convertedBalances = [];
$this->balanceDifferences = [];
Log::debug(sprintf('collectMetaData on %d object(s)', $objects->count()));
// first collect all the "heavy" stuff that relies on ALL data to be present.
// get last activity:
$this->getLastActivity($objects);

View File

@@ -170,6 +170,7 @@ class FireflyValidator extends Validator
"\u{202F}", // narrow no-break space
"\u{3000}", // ideographic space
"\u{FEFF}", // zero width no -break space
' ',
'-',
'?',
];

View File

@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.1.16 - 2024-05-20
### Added
- Added [THANKS.md] to give credit to all developers who help with the development of Firefly III.
### Changed
- New data model for "account balance" makes it easier to calculate and use multi-currency accounts. Not yet in use.
### Fixed
- [Issue 8840](https://github.com/firefly-iii/firefly-iii/issues/8840) (Budget page crash) reported by @JcMinarro
- [Issue 8863](https://github.com/firefly-iii/firefly-iii/issues/8863) (Empty webhooks page) reported by @mrahmadt
- [Issue 8867](https://github.com/firefly-iii/firefly-iii/issues/8867) (SQL Integrity constraint violation when inserting into budget_limits) reported by @HedgehogRidingAnOwl
- [Issue 8858](https://github.com/firefly-iii/firefly-iii/issues/8858) (A single Account constantly loses its Account NUmber / IBAN ) reported by @ypsilonkah
### API
- New filters for the v2 autocomplete endpoints.
- Various attempts to make a better v2 accounts endpoint.
## 6.1.15 - 2024-04-24
### Fixed

View File

@@ -87,6 +87,8 @@
"guzzlehttp/guzzle": "^7.8",
"jc5/google2fa-laravel": "^2.0",
"jc5/recovery": "^2",
"laravel-json-api/laravel": "^4.0",
"laravel-json-api/non-eloquent": "^4.0",
"laravel/framework": "^11",
"laravel/passport": "^12",
"laravel/sanctum": "^4",
@@ -101,13 +103,13 @@
"psr/log": "<4",
"ramsey/uuid": "^4.7",
"rcrowe/twigbridge": "^0.14",
"twig/twig": "3.8.0",
"spatie/laravel-html": "^3.2",
"spatie/laravel-ignition": "^2",
"spatie/period": "^2.4",
"symfony/expression-language": "^7.0",
"symfony/http-client": "^7.0",
"symfony/mailgun-mailer": "^7.0"
"symfony/mailgun-mailer": "^7.0",
"twig/twig": "3.8.0"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.9",
@@ -116,6 +118,7 @@
"fakerphp/faker": "1.*",
"filp/whoops": "2.*",
"larastan/larastan": "^2",
"laravel-json-api/testing": "^3.0",
"mockery/mockery": "1.*",
"phpstan/extension-installer": "^1.3",
"phpstan/phpstan": "^1.10",

1010
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -117,8 +117,8 @@ return [
'expression_engine' => false,
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2024-04-30',
'api_version' => '2.0.14',
'version' => 'develop/2024-05-20',
'api_version' => '2.1.0',
'db_version' => 24,
// generic settings
@@ -941,4 +941,10 @@ return [
'accounts' => ['name', 'active', 'iban', 'balance', 'last_activity', 'balance_difference', 'current_debt'],
],
],
'full_data_set' => [
'account' => ['last_activity', 'balance_difference', 'current_balance', 'current_debt'],
],
'valid_query_filters' => [
'account' => ['name', 'iban', 'active'],
],
];

35
config/jsonapi.php Normal file
View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
use FireflyIII\JsonApi\V3\Server;
return [
/*
|--------------------------------------------------------------------------
| Root Namespace
|--------------------------------------------------------------------------
|
| The root JSON:API namespace, within your application's namespace.
| This is used when generating any class that does not sit *within*
| a server's namespace. For example, new servers and filters.
|
| By default this is set to `JsonApi` which means the root namespace
| will be `\App\JsonApi`, if your application's namespace is `App`.
*/
'namespace' => 'JsonApi',
/*
|--------------------------------------------------------------------------
| Servers
|--------------------------------------------------------------------------
|
| A list of the JSON:API compliant APIs in your application, referred to
| as "servers". They must be listed below, with the array key being the
| unique name for each server, and the value being the fully-qualified
| class name of the server class.
*/
'servers' => [
'v3' => Server::class,
],
];

View File

@@ -284,7 +284,7 @@ class CreateMainTables extends Migration
$table->integer('budget_id', false, true);
$table->date('startdate');
$table->decimal('amount', 32, 12);
$table->string('repeat_freq', 30);
$table->string('repeat_freq', 30)->nullable();
$table->boolean('repeats')->default(0);
$table->foreign('budget_id')->references('id')->on('budgets')->onDelete('cascade');
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
if (!Schema::hasTable('account_balances')) {
Schema::create('account_balances', function (Blueprint $table): void {
$table->id();
$table->timestamps();
$table->string('title', 100)->nullable();
$table->integer('account_id', false, true);
$table->integer('transaction_currency_id', false, true);
$table->date('date')->nullable();
$table->integer('transaction_journal_id', false, true)->nullable();
$table->decimal('balance', 32, 12);
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
$table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade');
$table->unique(['account_id', 'transaction_currency_id', 'transaction_journal_id', 'date', 'title'], 'unique_account_currency');
});
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('account_balances');
}
};

217
package-lock.json generated
View File

@@ -11,35 +11,35 @@
]
},
"node_modules/@ag-grid-community/client-side-row-model": {
"version": "31.3.1",
"resolved": "https://registry.npmjs.org/@ag-grid-community/client-side-row-model/-/client-side-row-model-31.3.1.tgz",
"integrity": "sha512-dSd6IDPnVdxGTxd7XWXgy+26d9LaIw7cNiLksytjtCUb67UfdH1Zyz681n9Kp753hJPCJHjJDxikNTUCQgw+JQ==",
"version": "31.3.2",
"resolved": "https://registry.npmjs.org/@ag-grid-community/client-side-row-model/-/client-side-row-model-31.3.2.tgz",
"integrity": "sha512-XFzq4BvPPEjf5HtC1VpruBESqvsJYfIcY1EC5CaY1hqV1NRp1JVImFrMdoxUuw9NsoucGneDT/DOj42WHweFIg==",
"dependencies": {
"@ag-grid-community/core": "31.3.1",
"@ag-grid-community/core": "31.3.2",
"tslib": "^2.3.0"
}
},
"node_modules/@ag-grid-community/core": {
"version": "31.3.1",
"resolved": "https://registry.npmjs.org/@ag-grid-community/core/-/core-31.3.1.tgz",
"integrity": "sha512-B7mzUWfU7ksojKjQyOuT4R6vlp3J2jbMjBlQ5xqLAxanM46b8tv0tVo29mES41dBhQwOEf7ElQpvvXqRsND6+g==",
"version": "31.3.2",
"resolved": "https://registry.npmjs.org/@ag-grid-community/core/-/core-31.3.2.tgz",
"integrity": "sha512-iab+xIPCvjzGkIBgo12IQNa9/ZGI4BmjsQz65+aipNXISbtGketkN5iO4OFQgafgf0f9JZVFOZ3EzCnMiVWLiQ==",
"dependencies": {
"tslib": "^2.3.0"
}
},
"node_modules/@ag-grid-community/infinite-row-model": {
"version": "31.3.1",
"resolved": "https://registry.npmjs.org/@ag-grid-community/infinite-row-model/-/infinite-row-model-31.3.1.tgz",
"integrity": "sha512-BwIyQm/fcHMSC53DaJMmuwWFocv/+QxG4DdBWPiBvKv9e8HqGbCEnN1ocieY6qxyqY5i3YhGb0ROji3fN7hNnA==",
"version": "31.3.2",
"resolved": "https://registry.npmjs.org/@ag-grid-community/infinite-row-model/-/infinite-row-model-31.3.2.tgz",
"integrity": "sha512-b0MVmuMLDwzKXFux7REfp1wkjRi+91xR/bdzeQVKfuL4laJL2mlNCWnphdZTtYAFxMubABBhWgI2uQl3U/Jpew==",
"dependencies": {
"@ag-grid-community/core": "31.3.1",
"@ag-grid-community/core": "31.3.2",
"tslib": "^2.3.0"
}
},
"node_modules/@ag-grid-community/styles": {
"version": "31.3.1",
"resolved": "https://registry.npmjs.org/@ag-grid-community/styles/-/styles-31.3.1.tgz",
"integrity": "sha512-0QFMoH0E7LF3/O/DDyLEAerkabyVEzKJN+chHeEUq9Vr66xSxyi70oh0t/H3AFhnXKg8MdWMuk3EuiAWQ6O3Hg=="
"version": "31.3.2",
"resolved": "https://registry.npmjs.org/@ag-grid-community/styles/-/styles-31.3.2.tgz",
"integrity": "sha512-FP1FxUGKHCUdu081eptYg1g5FdDHjluTt2w6Jm5/KmC2XISUzdhhxJATsNM4Nu0h6fwxhQICsJl0xsTvXG/rxw=="
},
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
@@ -2847,9 +2847,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "20.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
"version": "20.12.12",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
"integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -2943,39 +2943,39 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.4.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.26.tgz",
"integrity": "sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
"integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.24.4",
"@vue/shared": "3.4.26",
"@vue/shared": "3.4.27",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.4.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz",
"integrity": "sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
"integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
"dev": true,
"dependencies": {
"@vue/compiler-core": "3.4.26",
"@vue/shared": "3.4.26"
"@vue/compiler-core": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.4.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.26.tgz",
"integrity": "sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
"integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.24.4",
"@vue/compiler-core": "3.4.26",
"@vue/compiler-dom": "3.4.26",
"@vue/compiler-ssr": "3.4.26",
"@vue/shared": "3.4.26",
"@vue/compiler-core": "3.4.27",
"@vue/compiler-dom": "3.4.27",
"@vue/compiler-ssr": "3.4.27",
"@vue/shared": "3.4.27",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.10",
"postcss": "^8.4.38",
@@ -2983,13 +2983,13 @@
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.4.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.26.tgz",
"integrity": "sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
"integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
"dev": true,
"dependencies": {
"@vue/compiler-dom": "3.4.26",
"@vue/shared": "3.4.26"
"@vue/compiler-dom": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/component-compiler-utils": {
@@ -3064,9 +3064,9 @@
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
},
"node_modules/@vue/shared": {
"version": "3.4.26",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.26.tgz",
"integrity": "sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/@webassemblyjs/ast": {
@@ -3539,9 +3539,9 @@
}
},
"node_modules/axios": {
"version": "1.6.8",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
"integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.0.tgz",
"integrity": "sha512-IiB0wQeKyPRdsFVhBgIo31FbzOyf2M6wYl7/NVutFwFBRMiAbjNiydJIHKeLmPugF4kJLfA1uWZ82Is2QzqqFA==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -4020,9 +4020,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001614",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz",
"integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==",
"version": "1.0.30001620",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz",
"integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==",
"dev": true,
"funding": [
{
@@ -4065,9 +4065,9 @@
}
},
"node_modules/chart.js": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.2.tgz",
"integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==",
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz",
"integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
@@ -4163,9 +4163,9 @@
}
},
"node_modules/cli-table3": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.4.tgz",
"integrity": "sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==",
"version": "0.6.5",
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
"integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
"dev": true,
"dependencies": {
"string-width": "^4.2.0"
@@ -4429,9 +4429,9 @@
"dev": true
},
"node_modules/core-js-compat": {
"version": "3.37.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.0.tgz",
"integrity": "sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==",
"version": "3.37.1",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz",
"integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==",
"dev": true,
"dependencies": {
"browserslist": "^4.23.0"
@@ -5095,9 +5095,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
"version": "1.4.752",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.752.tgz",
"integrity": "sha512-P3QJreYI/AUTcfBVrC4zy9KvnZWekViThgQMX/VpJ+IsOBbcX5JFpORM4qWapwWQ+agb2nYAOyn/4PMXOk0m2Q==",
"version": "1.4.774",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz",
"integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==",
"dev": true
},
"node_modules/elliptic": {
@@ -5146,9 +5146,9 @@
}
},
"node_modules/enhanced-resolve": {
"version": "5.16.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
"integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==",
"version": "5.16.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz",
"integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.4",
@@ -5213,9 +5213,9 @@
}
},
"node_modules/es-module-lexer": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz",
"integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==",
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz",
"integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==",
"dev": true
},
"node_modules/esbuild": {
@@ -5754,9 +5754,9 @@
}
},
"node_modules/fs-monkey": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz",
"integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz",
"integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==",
"dev": true
},
"node_modules/fs.realpath": {
@@ -6275,9 +6275,9 @@
}
},
"node_modules/i18next": {
"version": "23.11.3",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.3.tgz",
"integrity": "sha512-Pq/aSKowir7JM0rj+Wa23Kb6KKDUGno/HjG+wRQu0PxoTbpQ4N89MAT0rFGvXmLkRLNMb1BbBOKGozl01dabzg==",
"version": "23.11.4",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.4.tgz",
"integrity": "sha512-CCUjtd5TfaCl+mLUzAA0uPSN+AVn4fP/kWCYt/hocPUwusTpMVczdrRyOBUwk6N05iH40qiKx6q1DoNJtBIwdg==",
"funding": [
{
"type": "individual",
@@ -6305,9 +6305,9 @@
}
},
"node_modules/i18next-http-backend": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.1.tgz",
"integrity": "sha512-+rNX1tghdVxdfjfPt0bI1sNg5ahGW9kA7OboG7b4t03Fp69NdDlRIze6yXhIbN8rbHxJ8IP4dzRm/okZ15lkQg==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.2.tgz",
"integrity": "sha512-+K8HbDfrvc1/2X8jpb7RLhI9ZxBDpx3xogYkQwGKlWAUXLSEGXzgdt3EcUjLlBCdMwdQY+K+EUF6oh8oB6rwHw==",
"dependencies": {
"cross-fetch": "4.0.0"
}
@@ -6433,9 +6433,9 @@
}
},
"node_modules/immutable": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
"integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz",
"integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==",
"dev": true
},
"node_modules/import-fresh": {
@@ -6945,9 +6945,9 @@
}
},
"node_modules/laravel-vite-plugin": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.2.tgz",
"integrity": "sha512-Mcclml10khYzBVxDwJro8wnVDwD4i7XOSEMACQNnarvTnHjrjXLLL+B/Snif2wYAyElsOqagJZ7VAinb/2vF5g==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.4.tgz",
"integrity": "sha512-dEj8Q/Fsn0kKbOQ55bl/NmyJL+dD6OxnVaM/nNByw5XV4b00ky6FzXKVuHLDr4BvSJKH1y6oaOcEG5wKpCZ5+A==",
"dev": true,
"dependencies": {
"picocolors": "^1.0.0",
@@ -7940,9 +7940,9 @@
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"dev": true
},
"node_modules/picomatch": {
@@ -9074,9 +9074,9 @@
"dev": true
},
"node_modules/sass": {
"version": "1.75.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
"integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
"version": "1.77.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz",
"integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==",
"dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
@@ -9128,13 +9128,10 @@
}
},
"node_modules/semver": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
@@ -9142,24 +9139,6 @@
"node": ">=10"
}
},
"node_modules/semver/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/semver/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
@@ -9985,9 +9964,9 @@
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
"integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==",
"dev": true,
"funding": [
{
@@ -10004,8 +9983,8 @@
}
],
"dependencies": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
"escalade": "^3.1.2",
"picocolors": "^1.0.1"
},
"bin": {
"update-browserslist-db": "cli.js"
@@ -10098,9 +10077,9 @@
}
},
"node_modules/vite": {
"version": "5.2.10",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz",
"integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
"integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==",
"dev": true,
"dependencies": {
"esbuild": "^0.20.1",
@@ -10943,7 +10922,7 @@
"alpinejs": "^3.13.7",
"bootstrap": "^5.3.0",
"bootstrap5-autocomplete": "^1.1.22",
"bootstrap5-tags": "^1.6.15",
"bootstrap5-tags": "^1.7",
"chart.js": "^4.4.0",
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.12.1",

View File

@@ -4,22 +4,22 @@
"date_time_fns": "do MMMM yyyy @ HH:mm:ss",
"month_and_day_fns": "d MMMM y",
"does_not_exist": "(config.does_not_exist)",
"date_time_fns_short": "MMMM do, yyyy @ HH:mm"
"date_time_fns_short": "D MMMM yyyy [o] HH:mm"
},
"form": {
"title": "Tytu\u0142"
},
"list": {
"drag_and_drop": "Drag and drop",
"drag_and_drop": "Przeci\u0105gnij i upu\u015b\u0107",
"active": "Jest aktywny?",
"name": "Nazwa",
"type": "Typ",
"number": "Account number",
"number": "Numer konta",
"liability_type": "Rodzaj zobowi\u0105zania",
"current_balance": "Current balance",
"last_activity": "Last activity",
"amount_due": "Amount due",
"balance_difference": "Balance difference",
"current_balance": "Bie\u017c\u0105ce saldo",
"last_activity": "Ostatnia aktywno\u015b\u0107",
"amount_due": "Do zap\u0142aty",
"balance_difference": "R\u00f3\u017cnica salda",
"menu": "Menu"
},
"validation": {
@@ -38,20 +38,20 @@
"interest_calc_quarterly": "Kwartalnie",
"spent": "Wydano",
"administration_owner": "Administration owner: {{email}}",
"administration_you": "Your role: {{role}}",
"administration_role_owner": "Owner",
"administration_role_ro": "Read-only",
"administration_role_mng_trx": "Manage transactions",
"administration_role_mng_meta": "Manage classification and meta-data",
"administration_role_mng_budgets": "Manage budgets",
"administration_role_mng_piggies": "Manage piggy banks",
"administration_role_mng_subscriptions": "Manage subscriptions",
"administration_role_mng_rules": "Manage rules",
"administration_role_mng_recurring": "Manage recurring transactions ",
"administration_you": "Twoja rola: {{role}}",
"administration_role_owner": "W\u0142a\u015bciciel",
"administration_role_ro": "Tylko odczyt",
"administration_role_mng_trx": "Zarz\u0105dzanie transakcjami",
"administration_role_mng_meta": "Zarz\u0105dzanie klasyfikacj\u0105 i meta-danymi",
"administration_role_mng_budgets": "Zarz\u0105dzanie bud\u017cetami",
"administration_role_mng_piggies": "Zarz\u0105dzanie skarbonkami",
"administration_role_mng_subscriptions": "Zarz\u0105dzanie subskrypcjami",
"administration_role_mng_rules": "Zarz\u0105dzanie regu\u0142ami",
"administration_role_mng_recurring": "Zarz\u0105dzanie transakcjami cyklicznymi ",
"administration_role_mng_webhooks": "Manage webhooks",
"administration_role_mng_currencies": "Manage currencies",
"administration_role_view_reports": "View reports",
"administration_role_full": "Full access",
"administration_role_mng_currencies": "Zarz\u0105dzanie walutami",
"administration_role_view_reports": "Przegl\u0105danie raport\u00f3w",
"administration_role_full": "Pe\u0142ny dost\u0119p",
"new_administration_created": "New financial administration \"{{title}}\" has been created",
"left": "Pozosta\u0142o",
"paid": "Zap\u0142acone",
@@ -70,10 +70,10 @@
"unknown_dest_plain": "Nieznane konto docelowe",
"unknown_any_plain": "Nieznane konto",
"unknown_budget_plain": "Brak bud\u017cetu",
"stored_journal_js": "Successfully created new transaction \"{{description}}\"",
"stored_journal_js": "Pomy\u015blnie utworzono now\u0105 transakcj\u0119 \"{{description}}\"",
"wait_loading_transaction": "Poczekaj na za\u0142adowanie formularza",
"nothing_found": "(nic nie znaleziono)",
"wait_loading_data": "Please wait for your information to load...",
"wait_loading_data": "Prosz\u0119 poczeka\u0107 na za\u0142adowanie informacji...",
"Transfer": "Transfer",
"Withdrawal": "Wyp\u0142ata",
"Deposit": "Wp\u0142ata",

View File

@@ -4,22 +4,22 @@
"date_time_fns": "do MMMM yyyy @ HH:mm:ss",
"month_and_day_fns": "d MMMM y",
"does_not_exist": "(config.does_not_exist)",
"date_time_fns_short": "MMMM do, yyyy @ HH:mm"
"date_time_fns_short": "D MMMM yyyy [o] HH:mm"
},
"form": {
"title": "Tytu\u0142"
},
"list": {
"drag_and_drop": "Drag and drop",
"drag_and_drop": "Przeci\u0105gnij i upu\u015b\u0107",
"active": "Jest aktywny?",
"name": "Nazwa",
"type": "Typ",
"number": "Account number",
"number": "Numer konta",
"liability_type": "Rodzaj zobowi\u0105zania",
"current_balance": "Current balance",
"last_activity": "Last activity",
"amount_due": "Amount due",
"balance_difference": "Balance difference",
"current_balance": "Bie\u017c\u0105ce saldo",
"last_activity": "Ostatnia aktywno\u015b\u0107",
"amount_due": "Do zap\u0142aty",
"balance_difference": "R\u00f3\u017cnica salda",
"menu": "Menu"
},
"validation": {
@@ -38,20 +38,20 @@
"interest_calc_quarterly": "Kwartalnie",
"spent": "Wydano",
"administration_owner": "Administration owner: {{email}}",
"administration_you": "Your role: {{role}}",
"administration_role_owner": "Owner",
"administration_role_ro": "Read-only",
"administration_role_mng_trx": "Manage transactions",
"administration_role_mng_meta": "Manage classification and meta-data",
"administration_role_mng_budgets": "Manage budgets",
"administration_role_mng_piggies": "Manage piggy banks",
"administration_role_mng_subscriptions": "Manage subscriptions",
"administration_role_mng_rules": "Manage rules",
"administration_role_mng_recurring": "Manage recurring transactions ",
"administration_you": "Twoja rola: {{role}}",
"administration_role_owner": "W\u0142a\u015bciciel",
"administration_role_ro": "Tylko odczyt",
"administration_role_mng_trx": "Zarz\u0105dzanie transakcjami",
"administration_role_mng_meta": "Zarz\u0105dzanie klasyfikacj\u0105 i meta-danymi",
"administration_role_mng_budgets": "Zarz\u0105dzanie bud\u017cetami",
"administration_role_mng_piggies": "Zarz\u0105dzanie skarbonkami",
"administration_role_mng_subscriptions": "Zarz\u0105dzanie subskrypcjami",
"administration_role_mng_rules": "Zarz\u0105dzanie regu\u0142ami",
"administration_role_mng_recurring": "Zarz\u0105dzanie transakcjami cyklicznymi ",
"administration_role_mng_webhooks": "Manage webhooks",
"administration_role_mng_currencies": "Manage currencies",
"administration_role_view_reports": "View reports",
"administration_role_full": "Full access",
"administration_role_mng_currencies": "Zarz\u0105dzanie walutami",
"administration_role_view_reports": "Przegl\u0105danie raport\u00f3w",
"administration_role_full": "Pe\u0142ny dost\u0119p",
"new_administration_created": "New financial administration \"{{title}}\" has been created",
"left": "Pozosta\u0142o",
"paid": "Zap\u0142acone",
@@ -70,10 +70,10 @@
"unknown_dest_plain": "Nieznane konto docelowe",
"unknown_any_plain": "Nieznane konto",
"unknown_budget_plain": "Brak bud\u017cetu",
"stored_journal_js": "Successfully created new transaction \"{{description}}\"",
"stored_journal_js": "Pomy\u015blnie utworzono now\u0105 transakcj\u0119 \"{{description}}\"",
"wait_loading_transaction": "Poczekaj na za\u0142adowanie formularza",
"nothing_found": "(nic nie znaleziono)",
"wait_loading_data": "Please wait for your information to load...",
"wait_loading_data": "Prosz\u0119 poczeka\u0107 na za\u0142adowanie informacji...",
"Transfer": "Transfer",
"Withdrawal": "Wyp\u0142ata",
"Deposit": "Wp\u0142ata",

View File

@@ -10,16 +10,16 @@
"title": "T\u00edtulo"
},
"list": {
"drag_and_drop": "Drag and drop",
"drag_and_drop": "Arrastar e soltar",
"active": "Esta ativo?",
"name": "Nome",
"type": "Tipo",
"number": "Account number",
"number": "N\u00famero de conta",
"liability_type": "Tipo de responsabilidade",
"current_balance": "Current balance",
"last_activity": "Last activity",
"amount_due": "Amount due",
"balance_difference": "Balance difference",
"current_balance": "Saldo atual",
"last_activity": "\u00daltima atividade",
"amount_due": "Valor a pagar",
"balance_difference": "Diferen\u00e7a de saldo",
"menu": "Menu"
},
"validation": {

View File

@@ -10,16 +10,16 @@
"title": "T\u00edtulo"
},
"list": {
"drag_and_drop": "Drag and drop",
"drag_and_drop": "Arrastar e soltar",
"active": "Esta ativo?",
"name": "Nome",
"type": "Tipo",
"number": "Account number",
"number": "N\u00famero de conta",
"liability_type": "Tipo de responsabilidade",
"current_balance": "Current balance",
"last_activity": "Last activity",
"amount_due": "Amount due",
"balance_difference": "Balance difference",
"current_balance": "Saldo atual",
"last_activity": "\u00daltima atividade",
"amount_due": "Valor a pagar",
"balance_difference": "Diferen\u00e7a de saldo",
"menu": "Menu"
},
"validation": {

View File

@@ -10,17 +10,17 @@
"title": "Naslov"
},
"list": {
"drag_and_drop": "Drag and drop",
"drag_and_drop": "Povleci in spusti",
"active": "Aktiviran?",
"name": "ime",
"type": "Vrsta",
"number": "Account number",
"number": "\u0160tevilka ra\u010duna",
"liability_type": "Vrsta obveznost",
"current_balance": "Current balance",
"last_activity": "Last activity",
"amount_due": "Amount due",
"balance_difference": "Balance difference",
"menu": "Menu"
"current_balance": "Trenutno stanje",
"last_activity": "Zadnja aktivnost",
"amount_due": "Dolgovani znesek",
"balance_difference": "Bilan\u010dna razlika",
"menu": "Meni"
},
"validation": {
"bad_type_source": "Na podlagi tega izvornega ra\u010duna Firefly III ne more dolo\u010diti vrste transakcije.",

View File

@@ -10,17 +10,17 @@
"title": "Naslov"
},
"list": {
"drag_and_drop": "Drag and drop",
"drag_and_drop": "Povleci in spusti",
"active": "Aktiviran?",
"name": "ime",
"type": "Vrsta",
"number": "Account number",
"number": "\u0160tevilka ra\u010duna",
"liability_type": "Vrsta obveznost",
"current_balance": "Current balance",
"last_activity": "Last activity",
"amount_due": "Amount due",
"balance_difference": "Balance difference",
"menu": "Menu"
"current_balance": "Trenutno stanje",
"last_activity": "Zadnja aktivnost",
"amount_due": "Dolgovani znesek",
"balance_difference": "Bilan\u010dna razlika",
"menu": "Meni"
},
"validation": {
"bad_type_source": "Na podlagi tega izvornega ra\u010duna Firefly III ne more dolo\u010diti vrste transakcije.",

View File

@@ -4,20 +4,16 @@
"/build/create_transaction.js": "/build/create_transaction.js",
"/build/edit_transaction.js": "/build/edit_transaction.js",
"/build/profile.js": "/build/profile.js",
"/build/index.js": "/build/index.js",
"/build/create.js": "/build/create.js",
"/build/edit.js": "/build/edit.js",
"/build/show.js": "/build/show.js",
"/build/webhooks/index.js": "/build/webhooks/index.js",
"/build/webhooks/create.js": "/build/webhooks/create.js",
"/build/webhooks/edit.js": "/build/webhooks/edit.js",
"/build/webhooks/show.js": "/build/webhooks/show.js",
"/public/v1/js/app.js": "/public/v1/js/app.js",
"/public/v1/js/app.js.LICENSE.txt": "/public/v1/js/app.js.LICENSE.txt",
"/public/v1/js/app_vue.js": "/public/v1/js/app_vue.js",
"/public/v1/js/app_vue.js.LICENSE.txt": "/public/v1/js/app_vue.js.LICENSE.txt",
"/public/v1/js/create.js": "/public/v1/js/create.js",
"/public/v1/js/create.js.LICENSE.txt": "/public/v1/js/create.js.LICENSE.txt",
"/public/v1/js/create_transaction.js": "/public/v1/js/create_transaction.js",
"/public/v1/js/create_transaction.js.LICENSE.txt": "/public/v1/js/create_transaction.js.LICENSE.txt",
"/public/v1/js/edit.js": "/public/v1/js/edit.js",
"/public/v1/js/edit.js.LICENSE.txt": "/public/v1/js/edit.js.LICENSE.txt",
"/public/v1/js/edit_transaction.js": "/public/v1/js/edit_transaction.js",
"/public/v1/js/edit_transaction.js.LICENSE.txt": "/public/v1/js/edit_transaction.js.LICENSE.txt",
"/public/v1/js/ff/accounts/create.js": "/public/v1/js/ff/accounts/create.js",
@@ -88,8 +84,6 @@
"/public/v1/js/ff/transactions/mass/edit-bulk.js": "/public/v1/js/ff/transactions/mass/edit-bulk.js",
"/public/v1/js/ff/transactions/mass/edit.js": "/public/v1/js/ff/transactions/mass/edit.js",
"/public/v1/js/ff/transactions/show.js": "/public/v1/js/ff/transactions/show.js",
"/public/v1/js/index.js": "/public/v1/js/index.js",
"/public/v1/js/index.js.LICENSE.txt": "/public/v1/js/index.js.LICENSE.txt",
"/public/v1/js/lib/Chart.bundle.min.js": "/public/v1/js/lib/Chart.bundle.min.js",
"/public/v1/js/lib/accounting.min.js": "/public/v1/js/lib/accounting.min.js",
"/public/v1/js/lib/bootstrap-multiselect.js": "/public/v1/js/lib/bootstrap-multiselect.js",
@@ -148,6 +142,12 @@
"/public/v1/js/lib/vue.js": "/public/v1/js/lib/vue.js",
"/public/v1/js/profile.js": "/public/v1/js/profile.js",
"/public/v1/js/profile.js.LICENSE.txt": "/public/v1/js/profile.js.LICENSE.txt",
"/public/v1/js/show.js": "/public/v1/js/show.js",
"/public/v1/js/show.js.LICENSE.txt": "/public/v1/js/show.js.LICENSE.txt"
"/public/v1/js/webhooks/create.js": "/public/v1/js/webhooks/create.js",
"/public/v1/js/webhooks/create.js.LICENSE.txt": "/public/v1/js/webhooks/create.js.LICENSE.txt",
"/public/v1/js/webhooks/edit.js": "/public/v1/js/webhooks/edit.js",
"/public/v1/js/webhooks/edit.js.LICENSE.txt": "/public/v1/js/webhooks/edit.js.LICENSE.txt",
"/public/v1/js/webhooks/index.js": "/public/v1/js/webhooks/index.js",
"/public/v1/js/webhooks/index.js.LICENSE.txt": "/public/v1/js/webhooks/index.js.LICENSE.txt",
"/public/v1/js/webhooks/show.js": "/public/v1/js/webhooks/show.js",
"/public/v1/js/webhooks/show.js.LICENSE.txt": "/public/v1/js/webhooks/show.js.LICENSE.txt"
}

View File

@@ -42,7 +42,7 @@ mix.js('src/edit_transaction.js', 'build').vue({version: 2});
mix.js('src/profile.js', 'build').vue({version: 2});
//
// // webhooks
mix.js('src/webhooks/index.js', 'build').vue({version: 2});
mix.js('src/webhooks/create.js', 'build').vue({version: 2});
mix.js('src/webhooks/edit.js', 'build').vue({version: 2});
mix.js('src/webhooks/show.js', 'build').vue({version: 2}).copy('build','../../../public/v1/js')
mix.js('src/webhooks/index.js', 'build/webhooks').vue({version: 2});
mix.js('src/webhooks/create.js', 'build/webhooks').vue({version: 2});
mix.js('src/webhooks/edit.js', 'build/webhooks').vue({version: 2});
mix.js('src/webhooks/show.js', 'build/webhooks').vue({version: 2}).copy('build','../../../public/v1/js')

View File

@@ -26,7 +26,7 @@
"alpinejs": "^3.13.7",
"bootstrap": "^5.3.0",
"bootstrap5-autocomplete": "^1.1.22",
"bootstrap5-tags": "^1.6.15",
"bootstrap5-tags": "^1.7",
"chart.js": "^4.4.0",
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.12.1",

View File

@@ -470,7 +470,11 @@ let transactions = function () {
server: urls.tag,
liveServer: true,
clearEnd: true,
labelField: 'title',
valueField: 'id',
queryParam: 'filter[query]',
allowNew: true,
serverDataKey: 'data',
notFoundMessage: i18next.t('firefly.nothing_found'),
noCache: true,
fetchOptions: {
@@ -486,13 +490,14 @@ let transactions = function () {
// addedSplit, is called from the HTML
// for source account
const renderAccount = function (item, b, c) {
return item.name_with_balance + '<br><small class="text-muted">' + i18next.t('firefly.account_type_' + item.type) + '</small>';
return item.title + '<br><small class="text-muted">' + i18next.t('firefly.account_type_' + item.meta.type) + '</small>';
};
addAutocomplete({
selector: 'input.ac-source',
serverUrl: urls.account,
// filters: this.filters.source,
// onRenderItem: renderAccount,
onRenderItem: renderAccount,
valueField: 'id',
labelField: 'title',
onChange: changeSourceAccount,
onSelectItem: selectSourceAccount,
hiddenValue: this.entries[count].source_account.alpine_name
@@ -500,7 +505,9 @@ let transactions = function () {
addAutocomplete({
selector: 'input.ac-dest',
serverUrl: urls.account,
filters: this.filters.destination,
account_types: this.filters.destination,
valueField: 'id',
labelField: 'title',
onRenderItem: renderAccount,
onChange: changeDestinationAccount,
onSelectItem: selectDestinationAccount
@@ -509,7 +516,7 @@ let transactions = function () {
selector: 'input.ac-category',
serverUrl: urls.category,
valueField: 'id',
labelField: 'name',
labelField: 'title',
onChange: changeCategory,
onSelectItem: changeCategory
});
@@ -517,7 +524,7 @@ let transactions = function () {
selector: 'input.ac-description',
serverUrl: urls.description,
valueField: 'id',
labelField: 'description',
labelField: 'title',
onChange: changeDescription,
onSelectItem: changeDescription,
});

View File

@@ -200,20 +200,25 @@ let transactions = function () {
// addedSplit, is called from the HTML
// for source account
const renderAccount = function (item, b, c) {
return item.name_with_balance + '<br><small class="text-muted">' + i18next.t('firefly.account_type_' + item.type) + '</small>';
console.log(item);
return item.title + '<br><small class="text-muted">' + i18next.t('firefly.account_type_' + item.meta.type) + '</small>';
};
addAutocomplete({
selector: 'input.ac-source',
serverUrl: urls.account,
filters: this.filters.source,
account_types: this.filters.source,
onRenderItem: renderAccount,
valueField: 'id',
labelField: 'title',
onChange: changeSourceAccount,
onSelectItem: selectSourceAccount
});
addAutocomplete({
selector: 'input.ac-dest',
serverUrl: urls.account,
filters: this.filters.destination,
valueField: 'id',
labelField: 'title',
account_types: this.filters.destination,
onRenderItem: renderAccount,
onChange: changeDestinationAccount,
onSelectItem: selectDestinationAccount
@@ -222,7 +227,7 @@ let transactions = function () {
selector: 'input.ac-category',
serverUrl: urls.category,
valueField: 'id',
labelField: 'name',
labelField: 'title',
onChange: changeCategory,
onSelectItem: changeCategory
});
@@ -230,7 +235,7 @@ let transactions = function () {
selector: 'input.ac-description',
serverUrl: urls.description,
valueField: 'id',
labelField: 'description',
labelField: 'title',
onChange: changeDescription,
onSelectItem: changeDescription,
});
@@ -301,6 +306,9 @@ let transactions = function () {
liveServer: true,
clearEnd: true,
allowNew: true,
labelField: 'title',
valueField: 'id',
queryParam: 'filter[query]',
notFoundMessage: i18next.t('firefly.nothing_found'),
noCache: true,
fetchOptions: {

View File

@@ -38,13 +38,14 @@ export function addAutocomplete(options) {
'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content
}
},
queryParam: 'filter[query]',
hiddenInput: true,
// preventBrowserAutocomplete: true,
highlightTyped: true,
liveServer: true,
};
if (typeof options.filters !== 'undefined' && options.filters.length > 0) {
params.serverParams.types = options.filters;
if (typeof options.account_types !== 'undefined' && options.account_types.length > 0) {
params.serverParams['filter[account_types]'] = options.account_types;
}
if (typeof options.onRenderItem !== 'undefined' && null !== options.onRenderItem) {
params.onRenderItem = options.onRenderItem;

View File

@@ -55,13 +55,12 @@ export function changeDestinationAccount(item, ac) {
export function selectDestinationAccount(item, ac) {
const index = parseInt(ac._searchInput.attributes['data-index'].value);
document.querySelector('#form')._x_dataStack[0].$data.entries[index].destination_account = {
id: item.id, name: item.name, alpine_name: item.name, type: item.type, currency_code: item.currency_code,
id: item.id, name: item.title, alpine_name: item.title, type: item.meta.type, currency_code: item.meta.currency_code,
};
document.querySelector('#form')._x_dataStack[0].changedDestinationAccount();
}
export function changeSourceAccount(item, ac) {
// console.log('changeSourceAccount');
if (typeof item === 'undefined') {
const index = parseInt(ac._searchInput.attributes['data-index'].value);
let source = document.querySelector('#form')._x_dataStack[0].$data.entries[index].source_account;
@@ -79,7 +78,7 @@ export function changeSourceAccount(item, ac) {
export function selectSourceAccount(item, ac) {
const index = parseInt(ac._searchInput.attributes['data-index'].value);
document.querySelector('#form')._x_dataStack[0].$data.entries[index].source_account = {
id: item.id, name: item.name, alpine_name: item.name, type: item.type, currency_code: item.currency_code,
id: item.id, name: item.title, alpine_name: item.title, type: item.meta.type, currency_code: item.meta.currency_code,
};
document.querySelector('#form')._x_dataStack[0].changedSourceAccount();
}

View File

@@ -34,6 +34,7 @@
declare(strict_types=1);
return [
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'bad_type_source' => 'Firefly III can\'t determine the transaction type based on this source account.',
'bad_type_destination' => 'Firefly III can\'t determine the transaction type based on this destination account.',
'missing_where' => 'Array is missing "where"-clause',

View File

@@ -138,7 +138,7 @@ return [
'error_ip' => 'L\'adreça IP d\'aquest error és: :ip',
'error_url' => 'L\'URL és: :url',
'error_user_agent' => 'Agent d\'usuari: :userAgent',
'error_stacktrace' => 'The full stacktrace is below. If you think this is a bug in Firefly III, you can forward this message to <a href="mailto:james@firefly-iii.org?subject=I%20found%20a%20bug!">james@firefly-iii.org</a>. This can help fix the bug you just encountered.',
'error_stacktrace' => 'La traça completa és a continuació. Si creus que és un error de Firefly III, pots reenviar aquest missatge a <a href="mailto:james@firefly-iii.org?subject=I%20found%20a%20bug!">james@firefly-iii.org</a>. Podria ajudar a solucionar l\'error que acabes de trobar.',
'error_github_html' => 'Si ho prefereixes, també pots obrir un nou issue a <a href="https://github.com/firefly-iii/firefly-iii/issues">GitHub</a>.',
'error_github_text' => 'Si ho prefereixes, també pots obrir un nou issue a https://github.com/firefly-iii/firefly-iii/issues.',
'error_stacktrace_below' => 'La traça completa és a continuació:',

View File

@@ -34,6 +34,7 @@
declare(strict_types=1);
return [
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'bad_type_source' => 'Firefly III no pot determinar el tipus de transacció a partir d\'aquest compte font.',
'bad_type_destination' => 'Firefly III no pot determinar el tipus de transacció a partir d\'aquest compte de destí.',
'missing_where' => 'A l\'array li falta la clàusula "where"',

View File

@@ -34,6 +34,7 @@
declare(strict_types=1);
return [
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'bad_type_source' => 'Firefly III can\'t determine the transaction type based on this source account.',
'bad_type_destination' => 'Firefly III can\'t determine the transaction type based on this destination account.',
'missing_where' => 'Array is missing "where"-clause',

View File

@@ -34,6 +34,7 @@
declare(strict_types=1);
return [
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'bad_type_source' => 'Firefly III kan ikke bestemme transaktionstypen baseret på denne kildekonto.',
'bad_type_destination' => 'Firefly III kan ikke bestemme transaktionstypen baseret på denne destinationskonto.',
'missing_where' => 'Array mangler “Where”-sektion',

View File

@@ -34,6 +34,7 @@
declare(strict_types=1);
return [
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'bad_type_source' => 'Firefly III kann die Buchungsart anhand dieses Quellkontos nicht ermitteln.',
'bad_type_destination' => 'Firefly III kann die Buchungsart anhand dieses Zielkontos nicht ermitteln.',
'missing_where' => 'Dem Array fehlt die „where”-Klausel',

View File

@@ -34,6 +34,7 @@
declare(strict_types=1);
return [
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'bad_type_source' => 'Το Firefly III δεν μπορεί να καθορίσει τον τύπο συναλλαγής με βάση αυτό το λογαριασμό προέλευσης.',
'bad_type_destination' => 'Το Firefly III δεν μπορεί να καθορίσει τον τύπο συναλλαγής με βάση αυτό το λογαριασμό προορισμού.',
'missing_where' => 'Από τον πίνακα λείπει η ρήτρα "where"',

View File

@@ -34,6 +34,7 @@
declare(strict_types=1);
return [
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'bad_type_source' => 'Firefly III can\'t determine the transaction type based on this source account.',
'bad_type_destination' => 'Firefly III can\'t determine the transaction type based on this destination account.',
'missing_where' => 'Array is missing "where"-clause',

View File

@@ -25,6 +25,7 @@
declare(strict_types=1);
return [
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'bad_type_source' => 'Firefly III can\'t determine the transaction type based on this source account.',
'bad_type_destination' => 'Firefly III can\'t determine the transaction type based on this destination account.',
'missing_where' => 'Array is missing "where"-clause',

View File

@@ -34,6 +34,7 @@
declare(strict_types=1);
return [
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',
'bad_type_source' => 'Firefly III no puede determinar el tipo de transacción basado en esta cuenta de origen.',
'bad_type_destination' => 'Firefly III no puede determinar el tipo de transacción basado en esta cuenta de destino.',
'missing_where' => 'El array esperaba la cláusula "where"',

Some files were not shown because too many files have changed in this diff Show More