diff --git a/.ci/php-cs-fixer/.php-cs-fixer.php b/.ci/php-cs-fixer/.php-cs-fixer.php index 1e5a1c2dff..7a869b1c2e 100644 --- a/.ci/php-cs-fixer/.php-cs-fixer.php +++ b/.ci/php-cs-fixer/.php-cs-fixer.php @@ -36,9 +36,15 @@ $finder = PhpCsFixer\Finder::create() $config = new PhpCsFixer\Config(); return $config->setRules([ - '@PSR12' => true, - 'declare_strict_types' => true, - 'strict_param' => true, - 'array_syntax' => ['syntax' => 'short'], + '@PHP83Migration' => true, + '@PhpCsFixer:risky' => true, + '@PSR12:risky' => true, + 'declare_strict_types' => true, + 'strict_param' => true, + 'comment_to_phpdoc' => false, // breaks phpstan lines in combination with PHPStorm. + 'array_syntax' => ['syntax' => 'short'], + 'native_function_invocation' => false, // annoying + 'php_unit_data_provider_name' => false, // bloody annoying long test names + 'static_lambda' => false, // breaks the Response macro for API's. ]) ->setFinder($finder); diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index e68e86b2d3..64679a91b8 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -226,50 +226,48 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.37.0", + "version": "v3.41.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "d5ccc3807fd496ac2b448e8e5e57aa0772f0d18b" + "reference": "7d8d18e19095a939b8a3b8046f57108feaad6134" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/d5ccc3807fd496ac2b448e8e5e57aa0772f0d18b", - "reference": "d5ccc3807fd496ac2b448e8e5e57aa0772f0d18b", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7d8d18e19095a939b8a3b8046f57108feaad6134", + "reference": "7d8d18e19095a939b8a3b8046f57108feaad6134", "shasum": "" }, "require": { - "composer/semver": "^3.3", + "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.3", "ext-json": "*", "ext-tokenizer": "*", "php": "^7.4 || ^8.0", "sebastian/diff": "^4.0 || ^5.0", - "symfony/console": "^5.4 || ^6.0", - "symfony/event-dispatcher": "^5.4 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0", - "symfony/finder": "^5.4 || ^6.0", - "symfony/options-resolver": "^5.4 || ^6.0", - "symfony/polyfill-mbstring": "^1.27", - "symfony/polyfill-php80": "^1.27", - "symfony/polyfill-php81": "^1.27", - "symfony/process": "^5.4 || ^6.0", - "symfony/stopwatch": "^5.4 || ^6.0" + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", + "symfony/finder": "^5.4 || ^6.0 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-mbstring": "^1.28", + "symfony/polyfill-php80": "^1.28", + "symfony/polyfill-php81": "^1.28", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { "facile-it/paraunit": "^1.3 || ^2.0", "justinrainbow/json-schema": "^5.2", - "keradus/cli-executor": "^2.0", + "keradus/cli-executor": "^2.1", "mikey179/vfsstream": "^1.6.11", - "php-coveralls/php-coveralls": "^2.5.3", + "php-coveralls/php-coveralls": "^2.7", "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", - "phpspec/prophecy": "^1.16", - "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "symfony/phpunit-bridge": "^6.2.3", - "symfony/yaml": "^5.4 || ^6.0" + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", + "phpunit/phpunit": "^9.6", + "symfony/phpunit-bridge": "^6.3.8 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -307,7 +305,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.37.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.41.0" }, "funding": [ { @@ -315,7 +313,7 @@ "type": "github" } ], - "time": "2023-10-28T14:49:50+00:00" + "time": "2023-12-08T22:54:33+00:00" }, { "name": "psr/container", @@ -539,43 +537,46 @@ }, { "name": "symfony/console", - "version": "v6.3.4", + "version": "v7.0.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" + "reference": "cdce5c684b2f920bb1343deecdfba356ffad83d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", + "url": "https://api.github.com/repos/symfony/console/zipball/cdce5c684b2f920bb1343deecdfba356ffad83d5", + "reference": "cdce5c684b2f920bb1343deecdfba356ffad83d5", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.2", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0" + "symfony/string": "^6.4|^7.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -609,7 +610,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.4" + "source": "https://github.com/symfony/console/tree/v7.0.1" }, "funding": [ { @@ -625,11 +626,11 @@ "type": "tidelift" } ], - "time": "2023-08-16T10:10:12+00:00" + "time": "2023-12-01T15:10:06+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -676,7 +677,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.3.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" }, "funding": [ { @@ -696,24 +697,24 @@ }, { "name": "symfony/event-dispatcher", - "version": "v6.3.2", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e" + "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e", - "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c459b40ffe67c49af6fd392aac374c9edf8a027e", + "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4", + "symfony/dependency-injection": "<6.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -722,13 +723,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/error-handler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0" + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -756,7 +757,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.0" }, "funding": [ { @@ -772,11 +773,11 @@ "type": "tidelift" } ], - "time": "2023-07-06T06:56:43+00:00" + "time": "2023-07-27T16:29:09+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", @@ -832,7 +833,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" }, "funding": [ { @@ -852,20 +853,20 @@ }, { "name": "symfony/filesystem", - "version": "v6.3.1", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", - "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/7da8ea2362a283771478c5f7729cfcb43a76b8b7", + "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, @@ -895,7 +896,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + "source": "https://github.com/symfony/filesystem/tree/v7.0.0" }, "funding": [ { @@ -911,27 +912,27 @@ "type": "tidelift" } ], - "time": "2023-06-01T08:30:39+00:00" + "time": "2023-07-27T06:33:22+00:00" }, { "name": "symfony/finder", - "version": "v6.3.5", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4" + "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a1b31d88c0e998168ca7792f222cbecee47428c4", - "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4", + "url": "https://api.github.com/repos/symfony/finder/zipball/6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", + "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.0" + "symfony/filesystem": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -959,7 +960,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.5" + "source": "https://github.com/symfony/finder/tree/v7.0.0" }, "funding": [ { @@ -975,24 +976,24 @@ "type": "tidelift" } ], - "time": "2023-09-26T12:56:25+00:00" + "time": "2023-10-31T17:59:56+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.3.0", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" + "reference": "700ff4096e346f54cb628ea650767c8130f1001f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", - "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/700ff4096e346f54cb628ea650767c8130f1001f", + "reference": "700ff4096e346f54cb628ea650767c8130f1001f", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -1026,7 +1027,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.0.0" }, "funding": [ { @@ -1042,7 +1043,7 @@ "type": "tidelift" } ], - "time": "2023-05-12T14:21:09+00:00" + "time": "2023-08-08T10:20:21+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1538,20 +1539,20 @@ }, { "name": "symfony/process", - "version": "v6.3.4", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" + "reference": "13bdb1670c7f510494e04fcb2bfa29af63db9c0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", - "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", + "url": "https://api.github.com/repos/symfony/process/zipball/13bdb1670c7f510494e04fcb2bfa29af63db9c0d", + "reference": "13bdb1670c7f510494e04fcb2bfa29af63db9c0d", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -1579,7 +1580,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.4" + "source": "https://github.com/symfony/process/tree/v7.0.0" }, "funding": [ { @@ -1595,20 +1596,20 @@ "type": "tidelift" } ], - "time": "2023-08-07T10:39:22+00:00" + "time": "2023-11-20T16:43:42+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", "shasum": "" }, "require": { @@ -1661,7 +1662,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" }, "funding": [ { @@ -1677,24 +1678,24 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-07-30T20:28:31+00:00" }, { "name": "symfony/stopwatch", - "version": "v6.3.0", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2" + "reference": "7bbfa3dd564a0ce12eb4acaaa46823c740f9cb7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", - "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/7bbfa3dd564a0ce12eb4acaaa46823c740f9cb7a", + "reference": "7bbfa3dd564a0ce12eb4acaaa46823c740f9cb7a", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -1723,7 +1724,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.3.0" + "source": "https://github.com/symfony/stopwatch/tree/v7.0.0" }, "funding": [ { @@ -1739,24 +1740,24 @@ "type": "tidelift" } ], - "time": "2023-02-16T10:14:28+00:00" + "time": "2023-07-05T13:06:06+00:00" }, { "name": "symfony/string", - "version": "v6.3.5", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339" + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339", - "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339", + "url": "https://api.github.com/repos/symfony/string/zipball/92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -1766,11 +1767,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -1809,7 +1810,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.5" + "source": "https://github.com/symfony/string/tree/v7.0.0" }, "funding": [ { @@ -1825,7 +1826,7 @@ "type": "tidelift" } ], - "time": "2023-09-18T10:38:32+00:00" + "time": "2023-11-29T08:40:23+00:00" } ], "packages-dev": [], @@ -1836,5 +1837,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/.ci/phpcs.sh b/.ci/phpcs.sh index 49c6420f04..c486849052 100755 --- a/.ci/phpcs.sh +++ b/.ci/phpcs.sh @@ -28,11 +28,29 @@ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) # enable test .env file. # cp .ci/.env.ci .env +OUTPUT_FORMAT=txt +EXTRA_PARAMS="-v" + +if [[ $GITHUB_ACTIONS = "true" ]] +then + OUTPUT_FORMAT=gitlab + EXTRA_PARAMS="--diff --dry-run" +fi + # clean up php code cd $SCRIPT_DIR/php-cs-fixer composer update --quiet rm -f .php-cs-fixer.cache -PHP_CS_FIXER_IGNORE_ENV=true ./vendor/bin/php-cs-fixer fix --config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php --allow-risky=yes +PHP_CS_FIXER_IGNORE_ENV=true +./vendor/bin/php-cs-fixer fix \ + --config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php \ + --format=$OUTPUT_FORMAT \ + --allow-risky=yes $EXTRA_PARAMS + +EXIT_CODE=$? + +echo "Exit code for CS fixer is $EXIT_CODE." + cd $SCRIPT_DIR/.. -exit 0 +exit $EXIT_CODE diff --git a/.ci/phpmd.sh b/.ci/phpmd.sh old mode 100644 new mode 100755 index 0cbadc94c6..f58b80e6d5 --- a/.ci/phpmd.sh +++ b/.ci/phpmd.sh @@ -22,16 +22,30 @@ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +OUTPUT_FORMAT=text + +if [[ $GITHUB_ACTIONS = "true" ]] +then + OUTPUT_FORMAT=github +fi + cd $SCRIPT_DIR/phpmd composer update --quiet ./vendor/bin/phpmd \ - $SCRIPT_DIR/../app text phpmd.xml \ + $SCRIPT_DIR/../app,$SCRIPT_DIR/../database,$SCRIPT_DIR/../routes,$SCRIPT_DIR/../config \ + $OUTPUT_FORMAT phpmd.xml \ --exclude $SCRIPT_DIR/../app/resources/** \ --exclude $SCRIPT_DIR/../app/frontend/** \ --exclude $SCRIPT_DIR/../app/public/** \ - --exclude $SCRIPT_DIR/../app/vendor/** \ + --exclude $SCRIPT_DIR/../app/vendor/** + +EXIT_CODE=$? cd $SCRIPT_DIR/.. +echo "Exit code is $EXIT_CODE, but we ignore this for the time being." + +# for the time being, exit 0 +#exit $EXIT_CODE exit 0 diff --git a/.ci/phpmd/composer.lock b/.ci/phpmd/composer.lock index 88f4ba6d7f..63e98f35e1 100644 --- a/.ci/phpmd/composer.lock +++ b/.ci/phpmd/composer.lock @@ -9,16 +9,16 @@ "packages-dev": [ { "name": "composer/pcre", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", "shasum": "" }, "require": { @@ -60,7 +60,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.0" + "source": "https://github.com/composer/pcre/tree/3.1.1" }, "funding": [ { @@ -76,7 +76,7 @@ "type": "tidelift" } ], - "time": "2022-11-17T09:50:14+00:00" + "time": "2023-10-11T07:11:09+00:00" }, { "name": "composer/xdebug-handler", @@ -146,23 +146,24 @@ }, { "name": "pdepend/pdepend", - "version": "2.14.0", + "version": "2.16.0", "source": { "type": "git", "url": "https://github.com/pdepend/pdepend.git", - "reference": "1121d4b04af06e33e9659bac3a6741b91cab1de1" + "reference": "8dfc0c46529e2073fa97986552f80646eedac562" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pdepend/pdepend/zipball/1121d4b04af06e33e9659bac3a6741b91cab1de1", - "reference": "1121d4b04af06e33e9659bac3a6741b91cab1de1", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/8dfc0c46529e2073fa97986552f80646eedac562", + "reference": "8dfc0c46529e2073fa97986552f80646eedac562", "shasum": "" }, "require": { "php": ">=5.3.7", - "symfony/config": "^2.3.0|^3|^4|^5|^6.0", - "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0", - "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0" + "symfony/config": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0|^7.0", + "symfony/polyfill-mbstring": "^1.19" }, "require-dev": { "easy-doc/easy-doc": "0.0.0|^1.2.3", @@ -197,7 +198,7 @@ ], "support": { "issues": "https://github.com/pdepend/pdepend/issues", - "source": "https://github.com/pdepend/pdepend/tree/2.14.0" + "source": "https://github.com/pdepend/pdepend/tree/2.16.0" }, "funding": [ { @@ -205,26 +206,26 @@ "type": "tidelift" } ], - "time": "2023-05-26T13:15:18+00:00" + "time": "2023-11-29T08:52:35+00:00" }, { "name": "phpmd/phpmd", - "version": "2.13.0", + "version": "2.14.1", "source": { "type": "git", "url": "https://github.com/phpmd/phpmd.git", - "reference": "dad0228156856b3ad959992f9748514fa943f3e3" + "reference": "442fc2c34edcd5198b442d8647c7f0aec3afabe8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/dad0228156856b3ad959992f9748514fa943f3e3", - "reference": "dad0228156856b3ad959992f9748514fa943f3e3", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/442fc2c34edcd5198b442d8647c7f0aec3afabe8", + "reference": "442fc2c34edcd5198b442d8647c7f0aec3afabe8", "shasum": "" }, "require": { "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0", "ext-xml": "*", - "pdepend/pdepend": "^2.12.1", + "pdepend/pdepend": "^2.15.1", "php": ">=5.3.9" }, "require-dev": { @@ -234,7 +235,7 @@ "gregwar/rst": "^1.0", "mikey179/vfsstream": "^1.6.8", "phpunit/phpunit": "^4.8.36 || ^5.7.27", - "squizlabs/php_codesniffer": "^2.0" + "squizlabs/php_codesniffer": "^2.9.2 || ^3.7.2" }, "bin": [ "src/bin/phpmd" @@ -271,6 +272,7 @@ "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", "homepage": "https://phpmd.org/", "keywords": [ + "dev", "mess detection", "mess detector", "pdepend", @@ -280,7 +282,7 @@ "support": { "irc": "irc://irc.freenode.org/phpmd", "issues": "https://github.com/phpmd/phpmd/issues", - "source": "https://github.com/phpmd/phpmd/tree/2.13.0" + "source": "https://github.com/phpmd/phpmd/tree/2.14.1" }, "funding": [ { @@ -288,7 +290,7 @@ "type": "tidelift" } ], - "time": "2022-09-10T08:44:15+00:00" + "time": "2023-09-28T13:07:44+00:00" }, { "name": "psr/container", @@ -395,34 +397,34 @@ }, { "name": "symfony/config", - "version": "v6.3.0", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "a5e00dec161b08c946a2c16eed02adbeedf827ae" + "reference": "8789646600f4e7e451dde9e1dc81cfa429f3857a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/a5e00dec161b08c946a2c16eed02adbeedf827ae", - "reference": "a5e00dec161b08c946a2c16eed02adbeedf827ae", + "url": "https://api.github.com/repos/symfony/config/zipball/8789646600f4e7e451dde9e1dc81cfa429f3857a", + "reference": "8789646600f4e7e451dde9e1dc81cfa429f3857a", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^5.4|^6.0", + "symfony/filesystem": "^6.4|^7.0", "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/finder": "<5.4", + "symfony/finder": "<6.4", "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/messenger": "^5.4|^6.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^5.4|^6.0" + "symfony/yaml": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -450,7 +452,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v6.3.0" + "source": "https://github.com/symfony/config/tree/v7.0.0" }, "funding": [ { @@ -466,44 +468,43 @@ "type": "tidelift" } ], - "time": "2023-04-25T10:46:17+00:00" + "time": "2023-11-09T08:30:23+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.3.1", + "version": "v7.0.1", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "7abf242af21f196b65f20ab00ff251fdf3889b8d" + "reference": "f6667642954bce638733f254c39e5b5700b47ba4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/7abf242af21f196b65f20ab00ff251fdf3889b8d", - "reference": "7abf242af21f196b65f20ab00ff251fdf3889b8d", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f6667642954bce638733f254c39e5b5700b47ba4", + "reference": "f6667642954bce638733f254c39e5b5700b47ba4", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/service-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.2.10" + "symfony/service-contracts": "^3.3", + "symfony/var-exporter": "^6.4|^7.0" }, "conflict": { "ext-psr": "<1.1|>=2", - "symfony/config": "<6.1", - "symfony/finder": "<5.4", - "symfony/proxy-manager-bridge": "<6.3", - "symfony/yaml": "<5.4" + "symfony/config": "<6.4", + "symfony/finder": "<6.4", + "symfony/yaml": "<6.4" }, "provide": { "psr/container-implementation": "1.1|2.0", "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^6.1", - "symfony/expression-language": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" + "symfony/config": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -531,7 +532,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.3.1" + "source": "https://github.com/symfony/dependency-injection/tree/v7.0.1" }, "funding": [ { @@ -547,11 +548,11 @@ "type": "tidelift" } ], - "time": "2023-06-24T11:51:27+00:00" + "time": "2023-12-01T15:10:06+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -598,7 +599,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.3.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" }, "funding": [ { @@ -618,20 +619,20 @@ }, { "name": "symfony/filesystem", - "version": "v6.3.1", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", - "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/7da8ea2362a283771478c5f7729cfcb43a76b8b7", + "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, @@ -661,7 +662,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + "source": "https://github.com/symfony/filesystem/tree/v7.0.0" }, "funding": [ { @@ -677,20 +678,20 @@ "type": "tidelift" } ], - "time": "2023-06-01T08:30:39+00:00" + "time": "2023-07-27T06:33:22+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -705,7 +706,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -743,7 +744,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -759,20 +760,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -787,7 +788,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -826,7 +827,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -842,20 +843,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", "shasum": "" }, "require": { @@ -908,7 +909,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" }, "funding": [ { @@ -924,27 +925,27 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-07-30T20:28:31+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.3.0", + "version": "v7.0.1", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "db5416d04269f2827d8c54331ba4cfa42620d350" + "reference": "a3d7c877414fcd59ab7075ecdc3b8f9c00f7bcc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/db5416d04269f2827d8c54331ba4cfa42620d350", - "reference": "db5416d04269f2827d8c54331ba4cfa42620d350", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/a3d7c877414fcd59ab7075ecdc3b8f9c00f7bcc3", + "reference": "a3d7c877414fcd59ab7075ecdc3b8f9c00f7bcc3", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "symfony/var-dumper": "^5.4|^6.0" + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -982,7 +983,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.3.0" + "source": "https://github.com/symfony/var-exporter/tree/v7.0.1" }, "funding": [ { @@ -998,7 +999,7 @@ "type": "tidelift" } ], - "time": "2023-04-21T08:48:44+00:00" + "time": "2023-11-30T11:38:21+00:00" } ], "aliases": [], @@ -1008,5 +1009,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/.ci/phpmd/phpmd.xml b/.ci/phpmd/phpmd.xml index 47c3f85687..ed62aab519 100644 --- a/.ci/phpmd/phpmd.xml +++ b/.ci/phpmd/phpmd.xml @@ -20,53 +20,74 @@ --> - Bla bla + xmlns="http://pmd.sf.net/ruleset/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" + xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> + Firefly III ruleset. + + + + - + + + - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.ci/phpstan.neon b/.ci/phpstan.neon index f846b62d11..7e804a9a43 100644 --- a/.ci/phpstan.neon +++ b/.ci/phpstan.neon @@ -1,42 +1,33 @@ -includes: - - ../vendor/nunomaduro/larastan/extension.neon - - ../vendor/ergebnis/phpstan-rules/rules.neon - - ../vendor/phpstan/phpstan-deprecation-rules/rules.neon - - ../vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon - parameters: + universalObjectCratesClasses: + - Illuminate\Database\Eloquent\Model + # TODO: slowly remove these parameters and fix the issues found. + reportUnmatchedIgnoredErrors: false + checkGenericClassInNonGenericObjectType: false # remove this rule when all other issues are solved. ignoreErrors: + # TODO: slowly remove these exceptions and fix the issues found. + - '#Dynamic call to static method#' # all the Laravel ORM things depend on this. + - '#Control structures using switch should not be used.#' # switch is fine in some cases. + - '#with no value type specified in iterable type array#' # remove this rule when all other issues are solved. + - '#has no value type specified in iterable type array#' # remove this rule when all other issues are solved. - '#is not allowed to extend#' + - '#switch is forbidden to use#' - '#is neither abstract nor final#' - - '#has a nullable return type declaration#' - - '#with a nullable type declaration#' + - '#on left side of \?\?\= always exists and is not nullable#' + - '#has a nullable return type declaration#' # perhaps throw errors instead? + - '#with a nullable type declaration#' # decide what action should be if param is null. - '#with null as default value#' - - '#is not covariant with PHPDoc type array#' + - + message: '#Constructor in [a-zA-Z0-9\\_]+ has parameter \$[a-zA-Z0-9\\_]+ with default value#' + paths: + - ../app/Exceptions/IntervalException.php + - ../app/Support/Navigation.php - message: '#but containers should not be injected#' paths: - ../app/Support/Authentication/RemoteUserGuard.php - - message: '#Control structures using switch should not be used.#' - paths: - - ../app/Api/V1/Controllers/Data/DestroyController.php - - ../app/Console/Commands/Correction/FixAccountTypes.php - - ../app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php - - ../app/Exceptions/GracefulNotFoundHandler.php - - ../app/Generator/Webhook/StandardMessageGenerator.php - - ../app/Support/Amount.php - - ../app/Support/Navigation.php - - ../app/Support/ParseDateString.php - - ../app/Support/Search/AccountSearch.php - - ../app/Support/Search/OperatorQuerySearch.php - - ../app/Support/Twig/General.php - - ../app/Transformers/RecurrenceTransformer.php - - ../app/Validation/AccountValidator.php - - ../app/Validation/RecurrenceValidation.php - - ../app/Validation/TransactionValidation.php - - - - message: '#Function compact\(\) should not be used#' + message: '#Function compact\(\) should not be used#' # too useful in template rendering. paths: - ../app/Generator/Report/Account/MonthReportGenerator.php - ../app/Generator/Report/Audit/MonthReportGenerator.php @@ -57,7 +48,6 @@ parameters: message: '#Either catch a more specific exception#' paths: - ../app/Support/Form/FormSupport.php - paths: - ../app - ../database @@ -66,5 +56,6 @@ parameters: - ../bootstrap/app.php # The level 8 is the highest level. original was 5 - level: 4 + # 7 is more than enough, higher just leaves NULL things. + level: 7 diff --git a/.ci/phpstan.sh b/.ci/phpstan.sh index 3b3beea910..84ff119226 100755 --- a/.ci/phpstan.sh +++ b/.ci/phpstan.sh @@ -29,7 +29,20 @@ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) # cp .ci/.env.ci .env # Do static code analysis. -# ./vendor/bin/phpstan analyse -c .ci/phpstan.neon --no-progress -./vendor/bin/phpstan analyse -c .ci/phpstan.neon --xdebug --error-format=table > phpstan-report.txt +if [[ $GITHUB_ACTIONS = "" ]] +then + ./vendor/bin/phpstan analyse -c .ci/phpstan.neon --error-format=table > phpstan-report.txt + EXIT_CODE=$? + echo "The PHPstan report can be found in phpstan-report.txt. Exit code is $EXIT_CODE." +fi -echo 'The PHPstan report can be found in phpstan-report.txt' +if [[ $GITHUB_ACTIONS = "true" ]] +then + ./vendor/bin/phpstan analyse -c .ci/phpstan.neon --no-progress --error-format=github + EXIT_CODE=$? + + # temporary exit code 0 + # EXIT_CODE=0 +fi + +exit $EXIT_CODE diff --git a/.ci/phpunit.sh b/.ci/phpunit.sh deleted file mode 100755 index 58b307c329..0000000000 --- a/.ci/phpunit.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash - -# -# phpunit.sh -# Copyright (c) 2021 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 . -# -SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -# enable test .env file. -cp $SCRIPT_DIR/../.env $SCRIPT_DIR/../.env.backup -cp $SCRIPT_DIR/.env.ci $SCRIPT_DIR/../.env - -COVERAGE=false -RESET=false -FILE=storage/database/database.sqlite - -while getopts "cr" o; do - case "${o}" in - c) COVERAGE=true;; - r) RESET=true;; - esac -done - -# reset if necessary. -if [ $RESET = "true" ] ; then - rm -f $FILE -fi - -# download test database -if [ -f "$FILE" ]; then - echo 'DB exists, will use it' -else - echo 'Download new DB' - wget --quiet https://github.com/firefly-iii/test-fixtures/raw/main/test-database.sqlite -O $FILE -fi - -# run phpunit -if [ $COVERAGE = "true" ] ; then - echo 'Run with coverage' - XDEBUG_MODE=coverage ./vendor/bin/phpunit --configuration phpunit.xml --coverage-html $SCRIPT_DIR/coverage -else - echo 'Run without coverage' - ./vendor/bin/phpunit --configuration phpunit.xml -fi - -# restore .env file -mv $SCRIPT_DIR/../.env.backup $SCRIPT_DIR/../.env - -cd $SCRIPT_DIR/.. diff --git a/.env.testing b/.env.testing new file mode 100644 index 0000000000..2ca0859f2a --- /dev/null +++ b/.env.testing @@ -0,0 +1,26 @@ +APP_ENV=testing +APP_DEBUG=true +SITE_OWNER=mail@example.com +APP_KEY=TestTestTestTestTestTestTestTest +DEFAULT_LANGUAGE=en_US +DEFAULT_LOCALE=equal +TZ=Europe/Amsterdam +LOG_CHANNEL=stdout +APP_LOG_LEVEL=debug +AUDIT_LOG_LEVEL=info +AUDIT_LOG_CHANNEL=audit_stdout +DB_CONNECTION=sqlite +CACHE_DRIVER=array +SESSION_DRIVER=array +MAIL_MAILER=log +SEND_ERROR_MESSAGE=true +ENABLE_EXTERNAL_MAP=false +ENABLE_EXTERNAL_RATES=true +AUTHENTICATION_GUARD=web +ALLOW_WEBHOOKS=true +APP_NAME=FireflyIII +BROADCAST_DRIVER=log +QUEUE_DRIVER=sync +CACHE_PREFIX=firefly +FIREFLY_III_LAYOUT=v1 +APP_URL=http://localhost diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index cf3b682c4f..ff9e1ebe6b 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -22,7 +22,7 @@ jobs: - name: Setup PHP with Xdebug uses: shivammathur/setup-php@v2 with: - php-version: '8.2' + php-version: '8.3' coverage: xdebug extensions: >- bcmath @@ -42,15 +42,24 @@ jobs: xml xmlwriter + - name: Copy standard configuration + run: cp .env.testing .env + - name: Install Composer dependencies run: composer install --prefer-dist --no-interaction --no-progress --no-scripts + - name: PHPStan + run: .ci/phpstan.sh + + - name: PHPMD + run: .ci/phpmd.sh + + - name: PHP CS Fixer + run: .ci/phpcs.sh + - name: "Create database file" run: touch storage/database/database.sqlite - - name: "Create the database" - run: php artisan firefly-iii:create-database - - name: "Upgrades the database to the latest version" run: php artisan firefly-iii:upgrade-database diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index ecbfba6e61..c89d5bf775 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -41,6 +41,7 @@ class AccountController extends Controller { use AccountFilter; + /** @var array */ private array $balanceTypes; private AccountRepositoryInterface $repository; @@ -117,10 +118,10 @@ class AccountController extends Controller // custom order. usort( $return, - function ($a, $b) { + static function (array $left, array $right) { $order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE]; - $posA = array_search($a['type'], $order, true); - $posB = array_search($b['type'], $order, true); + $posA = (int)array_search($left['type'], $order, true); + $posB = (int)array_search($right['type'], $order, true); return $posA - $posB; } diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index 24f5e5d777..25fe2d8f31 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -30,6 +30,7 @@ use FireflyIII\Api\V1\Requests\Data\DateRequest; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use FireflyIII\Models\Preference; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\ApiSupport; use FireflyIII\User; @@ -90,8 +91,9 @@ class AccountController extends Controller // user's preferences $defaultSet = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray(); - $frontPage = app('preferences')->get('frontPageAccounts', $defaultSet); - $default = app('amount')->getDefaultCurrency(); + /** @var Preference $frontPage */ + $frontPage = app('preferences')->get('frontPageAccounts', $defaultSet); + $default = app('amount')->getDefaultCurrency(); if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) { $frontPage->data = $defaultSet; diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index 1e2b2b45d7..7240fc7ef1 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -27,11 +27,12 @@ namespace FireflyIII\Api\V1\Controllers; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; use Carbon\Exceptions\InvalidFormatException; +use FireflyIII\Models\Preference; +use FireflyIII\User; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; -use Illuminate\Support\Facades\Log; use League\Fractal\Manager; use League\Fractal\Serializer\JsonApiSerializer; use Psr\Container\ContainerExceptionInterface; @@ -50,7 +51,8 @@ abstract class Controller extends BaseController use DispatchesJobs; use ValidatesRequests; - protected const CONTENT_TYPE = 'application/vnd.api+json'; + protected const string CONTENT_TYPE = 'application/vnd.api+json'; + /** @var array */ protected array $allowedSort; protected ParameterBag $parameters; @@ -88,8 +90,8 @@ abstract class Controller extends BaseController if ($page < 1) { $page = 1; } - if ($page > pow(2, 16)) { - $page = pow(2, 16); + if ($page > 2 ** 16) { + $page = 2 ** 16; } $bag->set('page', $page); @@ -100,21 +102,21 @@ abstract class Controller extends BaseController try { $date = request()->query->get($field); } catch (BadRequestException $e) { - Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field)); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field)); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $value = null; } $obj = null; if (null !== $date) { try { - $obj = Carbon::parse($date); + $obj = Carbon::parse((string)$date); } catch (InvalidDateException | InvalidFormatException $e) { // don't care app('log')->warning( sprintf( 'Ignored invalid date "%s" in API controller parameter check: %s', - substr($date, 0, 20), + substr((string)$date, 0, 20), $e->getMessage() ) ); @@ -129,17 +131,22 @@ abstract class Controller extends BaseController try { $value = request()->query->get($integer); } catch (BadRequestException $e) { - Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $integer)); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $integer)); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $value = null; } if (null !== $value) { $bag->set($integer, (int)$value); } - if (null === $value && 'limit' === $integer && auth()->check()) { + if (null === $value && + 'limit' === $integer && // @phpstan-ignore-line + auth()->check()) { // set default for user: - $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + /** @var User $user */ + $user = auth()->user(); + /** @var Preference $pageSize */ + $pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data; $bag->set($integer, $pageSize); } } @@ -159,9 +166,9 @@ abstract class Controller extends BaseController try { $param = (string)request()->query->get('sort'); } catch (BadRequestException $e) { - Log::error('Request field "sort" contains a non-scalar value. Value set to NULL.'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error('Request field "sort" contains a non-scalar value. Value set to NULL.'); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $param = ''; } if ('' === $param) { diff --git a/app/Api/V1/Controllers/Data/Bulk/TransactionController.php b/app/Api/V1/Controllers/Data/Bulk/TransactionController.php index 56afbde1cd..38ac56d84e 100644 --- a/app/Api/V1/Controllers/Data/Bulk/TransactionController.php +++ b/app/Api/V1/Controllers/Data/Bulk/TransactionController.php @@ -76,7 +76,7 @@ class TransactionController extends Controller // this deserves better code, but for now a loop of basic if-statements // to respond to what is in the $query. // this is OK because only one thing can be in the query at the moment. - if ($this->updatesTransactionAccount($params)) { + if ($this->isUpdateTransactionAccount($params)) { $original = $this->repository->find((int)$params['where']['account_id']); $destination = $this->repository->find((int)$params['update']['account_id']); @@ -89,11 +89,11 @@ class TransactionController extends Controller } /** - * @param array $params + * @param array $params >> * * @return bool */ - private function updatesTransactionAccount(array $params): bool + private function isUpdateTransactionAccount(array $params): bool { return array_key_exists('account_id', $params['where']) && array_key_exists('account_id', $params['update']); } diff --git a/app/Api/V1/Controllers/Data/DestroyController.php b/app/Api/V1/Controllers/Data/DestroyController.php index 8b22e7c673..f46a7f26ff 100644 --- a/app/Api/V1/Controllers/Data/DestroyController.php +++ b/app/Api/V1/Controllers/Data/DestroyController.php @@ -45,7 +45,6 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Services\Internal\Destroy\AccountDestroyService; use FireflyIII\Services\Internal\Destroy\JournalDestroyService; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Log; /** * Class DestroyController @@ -67,135 +66,34 @@ class DestroyController extends Controller { $objects = $request->getObjects(); $this->unused = $request->boolean('unused', false); - switch ($objects) { - default: - throw new FireflyException(sprintf('200033: This endpoint can\'t handle object "%s"', $objects)); - case 'budgets': - $this->destroyBudgets(); - break; - case 'bills': - $this->destroyBills(); - break; - case 'piggy_banks': - $this->destroyPiggyBanks(); - break; - case 'rules': - $this->destroyRules(); - break; - case 'recurring': - $this->destroyRecurringTransactions(); - break; - case 'categories': - $this->destroyCategories(); - break; - case 'tags': - $this->destroyTags(); - break; - case 'object_groups': - $this->destroyObjectGroups(); - break; - case 'not_assets_liabilities': - $this->destroyAccounts( - [ - AccountType::BENEFICIARY, - AccountType::CASH, - AccountType::CREDITCARD, - AccountType::DEFAULT, - AccountType::EXPENSE, - AccountType::IMPORT, - AccountType::INITIAL_BALANCE, - AccountType::LIABILITY_CREDIT, - AccountType::RECONCILIATION, - AccountType::REVENUE, - ] - ); - break; - case 'accounts': - $this->destroyAccounts( - [ - AccountType::ASSET, - AccountType::BENEFICIARY, - AccountType::CASH, - AccountType::CREDITCARD, - AccountType::DEBT, - AccountType::DEFAULT, - AccountType::EXPENSE, - AccountType::IMPORT, - AccountType::INITIAL_BALANCE, - AccountType::LIABILITY_CREDIT, - AccountType::LOAN, - AccountType::MORTGAGE, - AccountType::RECONCILIATION, - AccountType::REVENUE, - ] - ); - break; - case 'asset_accounts': - $this->destroyAccounts( - [ - AccountType::ASSET, - AccountType::DEFAULT, - ] - ); - break; - case 'expense_accounts': - $this->destroyAccounts( - [ - AccountType::BENEFICIARY, - AccountType::EXPENSE, - ] - ); - break; - case 'revenue_accounts': - $this->destroyAccounts( - [ - AccountType::REVENUE, - ] - ); - break; - case 'liabilities': - $this->destroyAccounts( - [ - AccountType::DEBT, - AccountType::LOAN, - AccountType::MORTGAGE, - AccountType::CREDITCARD, - ] - ); - break; - case 'transactions': - $this->destroyTransactions( - [ - TransactionType::WITHDRAWAL, - TransactionType::DEPOSIT, - TransactionType::TRANSFER, - TransactionType::RECONCILIATION, - TransactionType::OPENING_BALANCE, - ] - ); - break; - case 'withdrawals': - $this->destroyTransactions( - [ - TransactionType::WITHDRAWAL, - ] - ); - break; - case 'deposits': - $this->destroyTransactions( - [ - TransactionType::DEPOSIT, - ] - ); - break; - case 'transfers': - $this->destroyTransactions( - [ - TransactionType::TRANSFER, - ] - ); - break; - } + + $allExceptAssets = [AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::RECONCILIATION, AccountType::REVENUE,]; + $all = [AccountType::ASSET, AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEBT, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::RECONCILIATION,]; + $liabilities = [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD]; + $transactions = [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::RECONCILIATION, TransactionType::OPENING_BALANCE,]; + + match ($objects) { + 'budgets' => $this->destroyBudgets(), + 'bills' => $this->destroyBills(), + 'piggy_banks' => $this->destroyPiggyBanks(), + 'rules' => $this->destroyRules(), + 'recurring' => $this->destroyRecurringTransactions(), + 'categories' => $this->destroyCategories(), + 'tags' => $this->destroyTags(), + 'object_groups' => $this->destroyObjectGroups(), + 'not_assets_liabilities' => $this->destroyAccounts($allExceptAssets), + 'accounts' => $this->destroyAccounts($all), + 'asset_accounts' => $this->destroyAccounts([AccountType::ASSET, AccountType::DEFAULT]), + 'expense_accounts' => $this->destroyAccounts([AccountType::BENEFICIARY, AccountType::EXPENSE]), + 'revenue_accounts' => $this->destroyAccounts([AccountType::REVENUE]), + 'liabilities' => $this->destroyAccounts($liabilities), + 'transactions' => $this->destroyTransactions($transactions), + 'withdrawals' => $this->destroyTransactions([TransactionType::WITHDRAWAL]), + 'deposits' => $this->destroyTransactions([TransactionType::DEPOSIT]), + 'transfers' => $this->destroyTransactions([TransactionType::TRANSFER]), + default => throw new FireflyException(sprintf('200033: This endpoint can\'t handle object "%s"', $objects)), + }; + app('preferences')->mark(); return response()->json([], 204); @@ -290,7 +188,7 @@ class DestroyController extends Controller } /** - * @param array $types + * @param array $types */ private function destroyAccounts(array $types): void { @@ -303,19 +201,19 @@ class DestroyController extends Controller foreach ($collection as $account) { $count = $account->transactions()->count(); if (true === $this->unused && 0 === $count) { - Log::info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name)); + app('log')->info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name)); $service->destroy($account, null); continue; } if (false === $this->unused) { - Log::info(sprintf('Deleting account #%d "%s"', $account->id, $account->name)); + app('log')->info(sprintf('Deleting account #%d "%s"', $account->id, $account->name)); $service->destroy($account, null); } } } /** - * @param array $types + * @param array $types */ private function destroyTransactions(array $types): void { diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php index c722088a2e..ffb7b4b9a4 100644 --- a/app/Api/V1/Controllers/Data/Export/ExportController.php +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -62,7 +62,11 @@ class ExportController extends Controller * @param ExportRequest $request * * @return LaravelResponse + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function accounts(ExportRequest $request): LaravelResponse { @@ -108,7 +112,10 @@ class ExportController extends Controller * @param ExportRequest $request * * @return LaravelResponse + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function bills(ExportRequest $request): LaravelResponse { @@ -124,7 +131,10 @@ class ExportController extends Controller * @param ExportRequest $request * * @return LaravelResponse + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function budgets(ExportRequest $request): LaravelResponse { @@ -140,7 +150,10 @@ class ExportController extends Controller * @param ExportRequest $request * * @return LaravelResponse + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function categories(ExportRequest $request): LaravelResponse { @@ -156,7 +169,10 @@ class ExportController extends Controller * @param ExportRequest $request * * @return LaravelResponse + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function piggyBanks(ExportRequest $request): LaravelResponse { @@ -172,7 +188,10 @@ class ExportController extends Controller * @param ExportRequest $request * * @return LaravelResponse + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function recurring(ExportRequest $request): LaravelResponse { @@ -188,7 +207,10 @@ class ExportController extends Controller * @param ExportRequest $request * * @return LaravelResponse + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function rules(ExportRequest $request): LaravelResponse { @@ -204,7 +226,10 @@ class ExportController extends Controller * @param ExportRequest $request * * @return LaravelResponse + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function tags(ExportRequest $request): LaravelResponse { @@ -220,7 +245,9 @@ class ExportController extends Controller * @param ExportRequest $request * * @return LaravelResponse + * @throws ContainerExceptionInterface * @throws FireflyException + * @throws NotFoundExceptionInterface */ public function transactions(ExportRequest $request): LaravelResponse { diff --git a/app/Api/V1/Controllers/Data/PurgeController.php b/app/Api/V1/Controllers/Data/PurgeController.php index b3af771d6d..d97773763a 100644 --- a/app/Api/V1/Controllers/Data/PurgeController.php +++ b/app/Api/V1/Controllers/Data/PurgeController.php @@ -35,6 +35,7 @@ use FireflyIII\Models\RuleGroup; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; +use FireflyIII\User; use Illuminate\Http\JsonResponse; /** @@ -51,6 +52,7 @@ class PurgeController extends Controller */ public function purge(): JsonResponse { + /** @var User $user */ $user = auth()->user(); // some manual code, too lazy to call all repositories. diff --git a/app/Api/V1/Controllers/Insight/Expense/BillController.php b/app/Api/V1/Controllers/Insight/Expense/BillController.php index a584c55913..07e914a7b0 100644 --- a/app/Api/V1/Controllers/Insight/Expense/BillController.php +++ b/app/Api/V1/Controllers/Insight/Expense/BillController.php @@ -92,7 +92,7 @@ class BillController extends Controller $foreignKey = sprintf('%d-%d', $billId, $foreignCurrencyId); if (0 !== $currencyId) { - $response[$key] = $response[$key] ?? [ + $response[$key] ??= [ 'id' => (string)$billId, 'name' => $journal['bill_name'], 'difference' => '0', @@ -104,7 +104,7 @@ class BillController extends Controller $response[$key]['difference_float'] = (float)$response[$key]['difference']; // intentional float } if (0 !== $foreignCurrencyId) { - $response[$foreignKey] = $response[$foreignKey] ?? [ + $response[$foreignKey] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$foreignCurrencyId, @@ -147,7 +147,7 @@ class BillController extends Controller $foreignCurrencyId = (int)$journal['foreign_currency_id']; if (0 !== $currencyId) { - $response[$currencyId] = $response[$currencyId] ?? [ + $response[$currencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$currencyId, @@ -157,7 +157,7 @@ class BillController extends Controller $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; // intentional float } if (0 !== $foreignCurrencyId) { - $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + $response[$foreignCurrencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$foreignCurrencyId, diff --git a/app/Api/V1/Controllers/Insight/Expense/PeriodController.php b/app/Api/V1/Controllers/Insight/Expense/PeriodController.php index eb583d0693..fd10d7a7fe 100644 --- a/app/Api/V1/Controllers/Insight/Expense/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Expense/PeriodController.php @@ -58,7 +58,7 @@ class PeriodController extends Controller $foreignCurrencyId = (int)$journal['foreign_currency_id']; if (0 !== $currencyId) { - $response[$currencyId] = $response[$currencyId] ?? [ + $response[$currencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$currencyId, @@ -68,7 +68,7 @@ class PeriodController extends Controller $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; // intentional float } if (0 !== $foreignCurrencyId) { - $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + $response[$foreignCurrencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$foreignCurrencyId, diff --git a/app/Api/V1/Controllers/Insight/Expense/TagController.php b/app/Api/V1/Controllers/Insight/Expense/TagController.php index 9068f402f6..0de98065d9 100644 --- a/app/Api/V1/Controllers/Insight/Expense/TagController.php +++ b/app/Api/V1/Controllers/Insight/Expense/TagController.php @@ -83,7 +83,7 @@ class TagController extends Controller $foreignCurrencyId = (int)$journal['foreign_currency_id']; if (0 !== $currencyId) { - $response[$currencyId] = $response[$currencyId] ?? [ + $response[$currencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$currencyId, @@ -93,7 +93,7 @@ class TagController extends Controller $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; // float but on purpose. } if (0 !== $foreignCurrencyId) { - $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + $response[$foreignCurrencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$foreignCurrencyId, @@ -148,7 +148,7 @@ class TagController extends Controller // on currency ID if (0 !== $currencyId) { - $response[$key] = $response[$key] ?? [ + $response[$key] ??= [ 'id' => (string)$tagId, 'name' => $tag['name'], 'difference' => '0', diff --git a/app/Api/V1/Controllers/Insight/Income/PeriodController.php b/app/Api/V1/Controllers/Insight/Income/PeriodController.php index cff22e7cbb..aa2722c7d7 100644 --- a/app/Api/V1/Controllers/Insight/Income/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Income/PeriodController.php @@ -58,7 +58,7 @@ class PeriodController extends Controller $foreignCurrencyId = (int)$journal['foreign_currency_id']; if (0 !== $currencyId) { - $response[$currencyId] = $response[$currencyId] ?? [ + $response[$currencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$currencyId, @@ -68,7 +68,7 @@ class PeriodController extends Controller $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; // float but on purpose. } if (0 !== $foreignCurrencyId) { - $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + $response[$foreignCurrencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$foreignCurrencyId, diff --git a/app/Api/V1/Controllers/Insight/Income/TagController.php b/app/Api/V1/Controllers/Insight/Income/TagController.php index 87cb0bb4b3..520bf40814 100644 --- a/app/Api/V1/Controllers/Insight/Income/TagController.php +++ b/app/Api/V1/Controllers/Insight/Income/TagController.php @@ -84,7 +84,7 @@ class TagController extends Controller $foreignCurrencyId = (int)$journal['foreign_currency_id']; if (0 !== $currencyId) { - $response[$currencyId] = $response[$currencyId] ?? [ + $response[$currencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$currencyId, @@ -94,7 +94,7 @@ class TagController extends Controller $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; } if (0 !== $foreignCurrencyId) { - $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + $response[$foreignCurrencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$foreignCurrencyId, @@ -152,7 +152,7 @@ class TagController extends Controller // on currency ID if (0 !== $currencyId) { - $response[$key] = $response[$key] ?? [ + $response[$key] ??= [ 'id' => (string)$tagId, 'name' => $tag['name'], 'difference' => '0', diff --git a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php index fc4a35a196..75eb95613b 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php @@ -58,7 +58,7 @@ class PeriodController extends Controller $foreignCurrencyId = (int)$journal['foreign_currency_id']; if (0 !== $currencyId) { - $response[$currencyId] = $response[$currencyId] ?? [ + $response[$currencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$currencyId, @@ -68,7 +68,7 @@ class PeriodController extends Controller $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; } if (0 !== $foreignCurrencyId) { - $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + $response[$foreignCurrencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$foreignCurrencyId, diff --git a/app/Api/V1/Controllers/Insight/Transfer/TagController.php b/app/Api/V1/Controllers/Insight/Transfer/TagController.php index e880067197..a95f28ccea 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/TagController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/TagController.php @@ -81,7 +81,7 @@ class TagController extends Controller $foreignCurrencyId = (int)$journal['foreign_currency_id']; if (0 !== $currencyId) { - $response[$currencyId] = $response[$currencyId] ?? [ + $response[$currencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$currencyId, @@ -91,7 +91,7 @@ class TagController extends Controller $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; } if (0 !== $foreignCurrencyId) { - $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + $response[$foreignCurrencyId] ??= [ 'difference' => '0', 'difference_float' => 0, 'currency_id' => (string)$foreignCurrencyId, @@ -149,7 +149,7 @@ class TagController extends Controller // on currency ID if (0 !== $currencyId) { - $response[$key] = $response[$key] ?? [ + $response[$key] ??= [ 'id' => (string)$tagId, 'name' => $tag['name'], 'difference' => '0', diff --git a/app/Api/V1/Controllers/Models/Account/DestroyController.php b/app/Api/V1/Controllers/Models/Account/DestroyController.php index b157b011fd..bd46cad13a 100644 --- a/app/Api/V1/Controllers/Models/Account/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Account/DestroyController.php @@ -33,7 +33,7 @@ use Illuminate\Http\JsonResponse; */ class DestroyController extends Controller { - public const RESOURCE_KEY = 'accounts'; + public const string RESOURCE_KEY = 'accounts'; private AccountRepositoryInterface $repository; diff --git a/app/Api/V1/Controllers/Models/Account/ListController.php b/app/Api/V1/Controllers/Models/Account/ListController.php index 6e3660121c..2820397718 100644 --- a/app/Api/V1/Controllers/Models/Account/ListController.php +++ b/app/Api/V1/Controllers/Models/Account/ListController.php @@ -47,7 +47,7 @@ class ListController extends Controller { use TransactionFilter; - public const RESOURCE_KEY = 'accounts'; + public const string RESOURCE_KEY = 'accounts'; private AccountRepositoryInterface $repository; diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php index 8decc0a0dd..32101e0aff 100644 --- a/app/Api/V1/Controllers/Models/Account/ShowController.php +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -43,7 +43,7 @@ class ShowController extends Controller { use AccountFilter; - public const RESOURCE_KEY = 'accounts'; + public const string RESOURCE_KEY = 'accounts'; private AccountRepositoryInterface $repository; diff --git a/app/Api/V1/Controllers/Models/Account/StoreController.php b/app/Api/V1/Controllers/Models/Account/StoreController.php index cb49ece28f..4e80df8f56 100644 --- a/app/Api/V1/Controllers/Models/Account/StoreController.php +++ b/app/Api/V1/Controllers/Models/Account/StoreController.php @@ -35,7 +35,7 @@ use League\Fractal\Resource\Item; */ class StoreController extends Controller { - public const RESOURCE_KEY = 'accounts'; + public const string RESOURCE_KEY = 'accounts'; private AccountRepositoryInterface $repository; diff --git a/app/Api/V1/Controllers/Models/Account/UpdateController.php b/app/Api/V1/Controllers/Models/Account/UpdateController.php index 1be3f42970..c519421b84 100644 --- a/app/Api/V1/Controllers/Models/Account/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Account/UpdateController.php @@ -29,16 +29,14 @@ use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Transformers\AccountTransformer; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Log; use League\Fractal\Resource\Item; -use Preferences; /** * Class UpdateController */ class UpdateController extends Controller { - public const RESOURCE_KEY = 'accounts'; + public const string RESOURCE_KEY = 'accounts'; private AccountRepositoryInterface $repository; @@ -73,13 +71,13 @@ class UpdateController extends Controller */ public function update(UpdateRequest $request, Account $account): JsonResponse { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $data = $request->getUpdateData(); $data['type'] = config('firefly.shortNamesByFullName.' . $account->accountType->type); $account = $this->repository->update($account, $data); $manager = $this->getManager(); $account->refresh(); - Preferences::mark(); + app('preferences')->mark(); /** @var AccountTransformer $transformer */ $transformer = app(AccountTransformer::class); diff --git a/app/Api/V1/Controllers/Models/Attachment/StoreController.php b/app/Api/V1/Controllers/Models/Attachment/StoreController.php index 6eeb9160c6..c089b020e8 100644 --- a/app/Api/V1/Controllers/Models/Attachment/StoreController.php +++ b/app/Api/V1/Controllers/Models/Attachment/StoreController.php @@ -34,7 +34,6 @@ use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use League\Fractal\Resource\Item; /** @@ -78,7 +77,7 @@ class StoreController extends Controller */ public function store(StoreRequest $request): JsonResponse { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $data = $request->getAll(); $attachment = $this->repository->store($data); $manager = $this->getManager(); @@ -107,7 +106,7 @@ class StoreController extends Controller $helper = app(AttachmentHelperInterface::class); $body = $request->getContent(); if ('' === $body) { - Log::error('Body of attachment is empty.'); + app('log')->error('Body of attachment is empty.'); return response()->json([], 422); } diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php index adc69dc9a0..ab3f4f2a4f 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php @@ -28,7 +28,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; -use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; @@ -44,28 +43,6 @@ class ListController extends Controller { use TransactionFilter; - private BudgetLimitRepositoryInterface $blRepository; - - /** - * BudgetLimitController constructor. - * - - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - $this->blRepository = app(BudgetLimitRepositoryInterface::class); - $this->blRepository->setUser($user); - - return $next($request); - } - ); - } - /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/listTransactionByBudgetLimit diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php index 1a4c4e6302..972a8315d4 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php @@ -33,7 +33,6 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Transformers\BudgetLimitTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; @@ -75,13 +74,11 @@ class ShowController extends Controller * * Display a listing of the budget limits for this budget. * - * @param Request $request - * @param Budget $budget + * @param Budget $budget * * @return JsonResponse - * @throws FireflyException */ - public function index(Request $request, Budget $budget): JsonResponse + public function index(Budget $budget): JsonResponse { $manager = $this->getManager(); $manager->parseIncludes('budget'); @@ -111,7 +108,7 @@ class ShowController extends Controller * @param SameDateRequest $request * * @return JsonResponse - * @throws FireflyException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function indexAll(SameDateRequest $request): JsonResponse { @@ -138,16 +135,15 @@ class ShowController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/getBudgetLimit * - * @param Request $request * @param Budget $budget * @param BudgetLimit $budgetLimit * * @return JsonResponse * @throws FireflyException */ - public function show(Request $request, Budget $budget, BudgetLimit $budgetLimit): JsonResponse + public function show(Budget $budget, BudgetLimit $budgetLimit): JsonResponse { - if ((int)$budget->id !== (int)$budgetLimit->budget_id) { + if ($budget->id !== $budgetLimit->budget_id) { throw new FireflyException('20028: The budget limit does not belong to the budget.'); } // continue! diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php index 8cf724245d..100d6da9ce 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php @@ -78,7 +78,7 @@ class UpdateController extends Controller */ public function update(UpdateRequest $request, Budget $budget, BudgetLimit $budgetLimit): JsonResponse { - if ((int)$budget->id !== (int)$budgetLimit->budget_id) { + if ($budget->id !== $budgetLimit->budget_id) { throw new FireflyException('20028: The budget limit does not belong to the budget.'); } $data = $request->getAll(); diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php b/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php index ae8e202aa3..427403b5de 100644 --- a/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php +++ b/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php @@ -24,13 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\ObjectGroup; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ObjectGroup; use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; use FireflyIII\Transformers\ObjectGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; @@ -69,12 +67,9 @@ class ShowController extends Controller * * Display a listing of the resource. * - * @param Request $request - * * @return JsonResponse - * @throws FireflyException */ - public function index(Request $request): JsonResponse + public function index(): JsonResponse { $manager = $this->getManager(); diff --git a/app/Api/V1/Controllers/Models/Transaction/DestroyController.php b/app/Api/V1/Controllers/Models/Transaction/DestroyController.php index a15a24ff33..377441d20a 100644 --- a/app/Api/V1/Controllers/Models/Transaction/DestroyController.php +++ b/app/Api/V1/Controllers/Models/Transaction/DestroyController.php @@ -33,7 +33,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepository; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Log; /** * Class DestroyController @@ -79,7 +78,7 @@ class DestroyController extends Controller */ public function destroy(TransactionGroup $transactionGroup): JsonResponse { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); // grab asset account(s) from group: $accounts = []; /** @var TransactionJournal $journal */ @@ -100,7 +99,7 @@ class DestroyController extends Controller /** @var Account $account */ foreach ($accounts as $account) { - Log::debug(sprintf('Now going to trigger updated account event for account #%d', $account->id)); + app('log')->debug(sprintf('Now going to trigger updated account event for account #%d', $account->id)); event(new UpdatedAccount($account)); } diff --git a/app/Api/V1/Controllers/Models/Transaction/StoreController.php b/app/Api/V1/Controllers/Models/Transaction/StoreController.php index 29c4a262e8..f33b725f5b 100644 --- a/app/Api/V1/Controllers/Models/Transaction/StoreController.php +++ b/app/Api/V1/Controllers/Models/Transaction/StoreController.php @@ -83,7 +83,7 @@ class StoreController extends Controller */ public function store(StoreRequest $request): JsonResponse { - Log::debug('Now in API StoreController::store()'); + app('log')->debug('Now in API StoreController::store()'); $data = $request->getAll(); $data['user'] = auth()->user()->id; @@ -92,19 +92,19 @@ class StoreController extends Controller try { $transactionGroup = $this->groupRepository->store($data); - } catch (DuplicateTransactionException $e) { + } catch (DuplicateTransactionException $e) { // @phpstan-ignore-line app('log')->warning('Caught a duplicate transaction. Return error message.'); $validator = Validator::make( ['transactions' => [['description' => $e->getMessage()]]], ['transactions.0.description' => new IsDuplicateTransaction()] ); - throw new ValidationException($validator, 0, $e); - } catch (FireflyException $e) { + throw new ValidationException($validator); // @phpstan-ignore-line + } catch (FireflyException $e) { // @phpstan-ignore-line app('log')->warning('Caught an exception. Return error message.'); - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); $message = sprintf('Internal exception: %s', $e->getMessage()); $validator = Validator::make(['transactions' => [['description' => $message]]], ['transactions.0.description' => new IsDuplicateTransaction()]); - throw new ValidationException($validator, 0, $e); + throw new ValidationException($validator); // @phpstan-ignore-line } app('preferences')->mark(); $applyRules = $data['apply_rules'] ?? true; diff --git a/app/Api/V1/Controllers/Models/Transaction/UpdateController.php b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php index 8da2d15204..1cfe099ab0 100644 --- a/app/Api/V1/Controllers/Models/Transaction/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php @@ -32,7 +32,6 @@ use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Log; use League\Fractal\Resource\Item; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -77,7 +76,7 @@ class UpdateController extends Controller */ public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse { - Log::debug('Now in update routine for transaction group!'); + app('log')->debug('Now in update routine for transaction group!'); $data = $request->getAll(); $transactionGroup = $this->groupRepository->update($transactionGroup, $data); diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index 5360c6ead8..f4a2fa05b3 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -255,8 +255,8 @@ class ListController extends Controller $unfiltered = $recurringRepos->getAll(); // filter selection - $collection = $unfiltered->filter( - static function (Recurrence $recurrence) use ($currency) { + $collection = $unfiltered->filter( // @phpstan-ignore-line + static function (Recurrence $recurrence) use ($currency) { // @phpstan-ignore-line /** @var RecurrenceTransaction $transaction */ foreach ($recurrence->recurrenceTransactions as $transaction) { if ($transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id) { @@ -305,8 +305,8 @@ class ListController extends Controller $ruleRepos = app(RuleRepositoryInterface::class); $unfiltered = $ruleRepos->getAll(); - $collection = $unfiltered->filter( - static function (Rule $rule) use ($currency) { + $collection = $unfiltered->filter( // @phpstan-ignore-line + static function (Rule $rule) use ($currency) { // @phpstan-ignore-line /** @var RuleTrigger $trigger */ foreach ($rule->ruleTriggers as $trigger) { if ('currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value) { diff --git a/app/Api/V1/Controllers/Search/AccountController.php b/app/Api/V1/Controllers/Search/AccountController.php index 2da3d3a254..b64b7f7150 100644 --- a/app/Api/V1/Controllers/Search/AccountController.php +++ b/app/Api/V1/Controllers/Search/AccountController.php @@ -32,7 +32,6 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Facades\Log; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; @@ -67,7 +66,7 @@ class AccountController extends Controller */ public function search(Request $request): JsonResponse | Response { - Log::debug('Now in account search()'); + app('log')->debug('Now in account search()'); $manager = $this->getManager(); $query = trim((string)$request->get('query')); $field = trim((string)$request->get('field')); diff --git a/app/Api/V1/Controllers/Summary/BasicController.php b/app/Api/V1/Controllers/Summary/BasicController.php index 4958b2a72d..9d9078c343 100644 --- a/app/Api/V1/Controllers/Summary/BasicController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -32,7 +32,6 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Report\NetWorthInterface; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; @@ -107,8 +106,8 @@ class BasicController extends Controller $balanceData = $this->getBalanceInformation($start, $end); $billData = $this->getBillInformation($start, $end); $spentData = $this->getLeftToSpendInfo($start, $end); - $networthData = $this->getNetWorthInfo($start, $end); - $total = array_merge($balanceData, $billData, $spentData, $networthData); + $netWorthData = $this->getNetWorthInfo($start, $end); + $total = array_merge($balanceData, $billData, $spentData, $netWorthData); // give new keys $return = []; @@ -149,12 +148,12 @@ class BasicController extends Controller /** @var array $transactionJournal */ foreach ($set as $transactionJournal) { $currencyId = (int)$transactionJournal['currency_id']; - $incomes[$currencyId] = $incomes[$currencyId] ?? '0'; + $incomes[$currencyId] ??= '0'; $incomes[$currencyId] = bcadd( $incomes[$currencyId], bcmul($transactionJournal['amount'], '-1') ); - $sums[$currencyId] = $sums[$currencyId] ?? '0'; + $sums[$currencyId] ??= '0'; $sums[$currencyId] = bcadd($sums[$currencyId], bcmul($transactionJournal['amount'], '-1')); } @@ -172,9 +171,9 @@ class BasicController extends Controller /** @var array $transactionJournal */ foreach ($set as $transactionJournal) { $currencyId = (int)$transactionJournal['currency_id']; - $expenses[$currencyId] = $expenses[$currencyId] ?? '0'; + $expenses[$currencyId] ??= '0'; $expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']); - $sums[$currencyId] = $sums[$currencyId] ?? '0'; + $sums[$currencyId] ??= '0'; $sums[$currencyId] = bcadd($sums[$currencyId], $transactionJournal['amount']); } @@ -368,30 +367,30 @@ class BasicController extends Controller } ); - $netWorthSet = $netWorthHelper->getNetWorthByCurrency($filtered, $date); + $netWorthSet = $netWorthHelper->byAccounts($filtered, $date); $return = []; - foreach ($netWorthSet as $data) { - /** @var TransactionCurrency $currency */ - $currency = $data['currency']; - $amount = $data['balance']; + foreach ($netWorthSet as $key => $data) { + if ('native' === $key) { + continue; + } + $amount = $data['balance']; if (0 === bccomp($amount, '0')) { continue; } // return stuff $return[] = [ - 'key' => sprintf('net-worth-in-%s', $currency->code), - 'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $currency->symbol]), + 'key' => sprintf('net-worth-in-%s', $data['currency_code']), + 'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]), 'monetary_value' => $amount, - 'currency_id' => (string)$currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - 'value_parsed' => app('amount')->formatAnything($currency, $data['balance'], false), + 'currency_id' => (string)$data['currency_id'], + 'currency_code' => $data['currency_code'], + 'currency_symbol' => $data['currency_symbol'], + 'currency_decimal_places' => $data['currency_decimal_places'], + 'value_parsed' => app('amount')->formatFlat($data['currency_symbol'], $data['currency_decimal_places'], $data['balance'], false), 'local_icon' => 'line-chart', 'sub_title' => '', ]; } - return $return; } diff --git a/app/Api/V1/Controllers/System/ConfigurationController.php b/app/Api/V1/Controllers/System/ConfigurationController.php index b91efd9360..fd4da1c827 100644 --- a/app/Api/V1/Controllers/System/ConfigurationController.php +++ b/app/Api/V1/Controllers/System/ConfigurationController.php @@ -29,7 +29,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Support\Binder\EitherConfigKey; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Validator; @@ -70,8 +69,8 @@ class ConfigurationController extends Controller try { $dynamicData = $this->getDynamicConfiguration(); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException('200030: Could not load config variables.', 0, $e); } $staticData = $this->getStaticConfiguration(); diff --git a/app/Api/V1/Controllers/System/CronController.php b/app/Api/V1/Controllers/System/CronController.php index da110e13b8..eec18ecf8c 100644 --- a/app/Api/V1/Controllers/System/CronController.php +++ b/app/Api/V1/Controllers/System/CronController.php @@ -27,7 +27,6 @@ use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\System\CronRequest; use FireflyIII\Support\Http\Controllers\CronRunner; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -43,18 +42,17 @@ class CronController extends Controller * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/about/getCron * * @param CronRequest $request - * @param string $token * * @return JsonResponse * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function cron(CronRequest $request, string $token): JsonResponse + public function cron(CronRequest $request): JsonResponse { $config = $request->getAll(); - Log::debug(sprintf('Now in %s', __METHOD__)); - Log::debug(sprintf('Date is %s', $config['date']->toIsoString())); + app('log')->debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Date is %s', $config['date']->toIsoString())); $return = []; $return['recurring_transactions'] = $this->runRecurring($config['force'], $config['date']); $return['auto_budgets'] = $this->runAutoBudget($config['force'], $config['date']); diff --git a/app/Api/V1/Controllers/System/UserController.php b/app/Api/V1/Controllers/System/UserController.php index b0c6e519bc..45233925dc 100644 --- a/app/Api/V1/Controllers/System/UserController.php +++ b/app/Api/V1/Controllers/System/UserController.php @@ -33,7 +33,6 @@ use FireflyIII\Transformers\UserTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Facades\Log; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; @@ -192,7 +191,7 @@ class UserController extends Controller // can only update 'blocked' when user is admin. if (!$this->repository->hasRole(auth()->user(), 'owner')) { - Log::debug('Quietly drop fields "blocked" and "blocked_code" from request.'); + app('log')->debug('Quietly drop fields "blocked" and "blocked_code" from request.'); unset($data['blocked'], $data['blocked_code']); } diff --git a/app/Api/V1/Controllers/User/PreferencesController.php b/app/Api/V1/Controllers/User/PreferencesController.php index 325e567814..fdb4eb7382 100644 --- a/app/Api/V1/Controllers/User/PreferencesController.php +++ b/app/Api/V1/Controllers/User/PreferencesController.php @@ -41,8 +41,8 @@ use League\Fractal\Resource\Item; */ class PreferencesController extends Controller { - public const DATE_FORMAT = 'Y-m-d'; - public const RESOURCE_KEY = 'preferences'; + public const string DATE_FORMAT = 'Y-m-d'; + public const string RESOURCE_KEY = 'preferences'; /** * This endpoint is documented at: diff --git a/app/Api/V1/Controllers/Webhook/AttemptController.php b/app/Api/V1/Controllers/Webhook/AttemptController.php index 901e22519e..8288e41856 100644 --- a/app/Api/V1/Controllers/Webhook/AttemptController.php +++ b/app/Api/V1/Controllers/Webhook/AttemptController.php @@ -41,7 +41,7 @@ use League\Fractal\Resource\Item; */ class AttemptController extends Controller { - public const RESOURCE_KEY = 'webhook_attempts'; + public const string RESOURCE_KEY = 'webhook_attempts'; private WebhookRepositoryInterface $repository; /** diff --git a/app/Api/V1/Controllers/Webhook/MessageController.php b/app/Api/V1/Controllers/Webhook/MessageController.php index 53b427af0e..08baafd97d 100644 --- a/app/Api/V1/Controllers/Webhook/MessageController.php +++ b/app/Api/V1/Controllers/Webhook/MessageController.php @@ -40,7 +40,7 @@ use League\Fractal\Resource\Item; */ class MessageController extends Controller { - public const RESOURCE_KEY = 'webhook_messages'; + public const string RESOURCE_KEY = 'webhook_messages'; private WebhookRepositoryInterface $repository; /** diff --git a/app/Api/V1/Controllers/Webhook/ShowController.php b/app/Api/V1/Controllers/Webhook/ShowController.php index 4ad46fdacc..fbfa4bf411 100644 --- a/app/Api/V1/Controllers/Webhook/ShowController.php +++ b/app/Api/V1/Controllers/Webhook/ShowController.php @@ -43,7 +43,7 @@ use League\Fractal\Resource\Item; */ class ShowController extends Controller { - public const RESOURCE_KEY = 'webhooks'; + public const string RESOURCE_KEY = 'webhooks'; private WebhookRepositoryInterface $repository; /** diff --git a/app/Api/V1/Controllers/Webhook/StoreController.php b/app/Api/V1/Controllers/Webhook/StoreController.php index 6c573a7929..5458060b03 100644 --- a/app/Api/V1/Controllers/Webhook/StoreController.php +++ b/app/Api/V1/Controllers/Webhook/StoreController.php @@ -35,7 +35,7 @@ use League\Fractal\Resource\Item; */ class StoreController extends Controller { - public const RESOURCE_KEY = 'webhooks'; + public const string RESOURCE_KEY = 'webhooks'; private WebhookRepositoryInterface $repository; /** diff --git a/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php b/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php index 875d1f7335..c1d6069f15 100644 --- a/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php +++ b/app/Api/V1/Requests/Autocomplete/AutocompleteRequest.php @@ -33,8 +33,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class AutocompleteRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * @return array diff --git a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php index 07b29a7a60..5ce3a5502a 100644 --- a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php @@ -30,7 +30,6 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\Api\Data\Bulk\ValidatesBulkTransactionQuery; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; use JsonException; @@ -55,7 +54,7 @@ class TransactionRequest extends FormRequest ]; } catch (JsonException $e) { // dont really care. the validation should catch invalid json. - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); } return $data; diff --git a/app/Api/V1/Requests/Data/DateRequest.php b/app/Api/V1/Requests/Data/DateRequest.php index a6e1cdf23b..882412a46b 100644 --- a/app/Api/V1/Requests/Data/DateRequest.php +++ b/app/Api/V1/Requests/Data/DateRequest.php @@ -35,8 +35,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class DateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Data/DestroyRequest.php b/app/Api/V1/Requests/Data/DestroyRequest.php index ea96e92c26..3a9ca8d1d9 100644 --- a/app/Api/V1/Requests/Data/DestroyRequest.php +++ b/app/Api/V1/Requests/Data/DestroyRequest.php @@ -32,8 +32,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class DestroyRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Data/SameDateRequest.php b/app/Api/V1/Requests/Data/SameDateRequest.php index 549bf58563..19fe7829a6 100644 --- a/app/Api/V1/Requests/Data/SameDateRequest.php +++ b/app/Api/V1/Requests/Data/SameDateRequest.php @@ -35,8 +35,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class SameDateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Insight/GenericRequest.php b/app/Api/V1/Requests/Insight/GenericRequest.php index 0eb22bc584..82624ba7df 100644 --- a/app/Api/V1/Requests/Insight/GenericRequest.php +++ b/app/Api/V1/Requests/Insight/GenericRequest.php @@ -42,8 +42,8 @@ use Illuminate\Support\Collection; */ class GenericRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; private Collection $accounts; private Collection $bills; diff --git a/app/Api/V1/Requests/Models/Account/StoreRequest.php b/app/Api/V1/Requests/Models/Account/StoreRequest.php index d46322bea9..3f42208f1b 100644 --- a/app/Api/V1/Requests/Models/Account/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Account/StoreRequest.php @@ -40,9 +40,9 @@ use Illuminate\Foundation\Http\FormRequest; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use AppendsLocationData; use ChecksLogin; + use ConvertsDataTypes; /** * @return array @@ -124,7 +124,7 @@ class StoreRequest extends FormRequest 'liability_start_date' => 'required_with:liability_amount|date', 'liability_direction' => 'nullable|required_if:type,liability|required_if:type,liabilities|in:credit,debit', 'interest' => 'between:0,100|numeric', - 'interest_period' => sprintf('nullable|in:%s', join(',', config('firefly.interest_periods'))), + 'interest_period' => sprintf('nullable|in:%s', implode(',', config('firefly.interest_periods'))), 'notes' => 'min:0|max:65536', ]; diff --git a/app/Api/V1/Requests/Models/Account/UpdateRequest.php b/app/Api/V1/Requests/Models/Account/UpdateRequest.php index d50f9d10eb..9428fa545a 100644 --- a/app/Api/V1/Requests/Models/Account/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Account/UpdateRequest.php @@ -41,9 +41,9 @@ use Illuminate\Foundation\Http\FormRequest; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use AppendsLocationData; use ChecksLogin; + use ConvertsDataTypes; /** * @return array diff --git a/app/Api/V1/Requests/Models/Attachment/StoreRequest.php b/app/Api/V1/Requests/Models/Attachment/StoreRequest.php index 565c8a4cd4..aca785df2f 100644 --- a/app/Api/V1/Requests/Models/Attachment/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Attachment/StoreRequest.php @@ -35,8 +35,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php b/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php index 854a156df4..98734b2713 100644 --- a/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php @@ -35,8 +35,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/AvailableBudget/Request.php b/app/Api/V1/Requests/Models/AvailableBudget/Request.php index f92de0da5a..637f1ca192 100644 --- a/app/Api/V1/Requests/Models/AvailableBudget/Request.php +++ b/app/Api/V1/Requests/Models/AvailableBudget/Request.php @@ -36,8 +36,8 @@ use Illuminate\Validation\Validator; */ class Request extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. @@ -84,7 +84,7 @@ class Request extends FormRequest public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + static function (Validator $validator) { // validate start before end only if both are there. $data = $validator->getData(); if (array_key_exists('start', $data) && array_key_exists('end', $data)) { diff --git a/app/Api/V1/Requests/Models/Bill/StoreRequest.php b/app/Api/V1/Requests/Models/Bill/StoreRequest.php index 416d73ac99..3dfbe6c012 100644 --- a/app/Api/V1/Requests/Models/Bill/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Bill/StoreRequest.php @@ -28,7 +28,6 @@ use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -38,8 +37,8 @@ use Illuminate\Validation\Validator; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. @@ -48,7 +47,7 @@ class StoreRequest extends FormRequest */ public function getAll(): array { - Log::debug('Raw fields in Bill StoreRequest', $this->all()); + app('log')->debug('Raw fields in Bill StoreRequest', $this->all()); $fields = [ 'name' => ['name', 'convertString'], 'amount_min' => ['amount_min', 'convertString'], diff --git a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php index 25adeb2705..dc912d1197 100644 --- a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php @@ -38,8 +38,8 @@ use Illuminate\Validation\Validator; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Budget/StoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php index 78714bb474..a4e762411b 100644 --- a/app/Api/V1/Requests/Models/Budget/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php @@ -37,9 +37,9 @@ use Illuminate\Validation\Validator; */ class StoreRequest extends FormRequest { + use ChecksLogin; use ConvertsDataTypes; use ValidatesAutoBudgetRequest; - use ChecksLogin; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php index 2fd5e43560..4365619962 100644 --- a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php @@ -38,9 +38,9 @@ use Illuminate\Validation\Validator; */ class UpdateRequest extends FormRequest { + use ChecksLogin; use ConvertsDataTypes; use ValidatesAutoBudgetRequest; - use ChecksLogin; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php index 1c003221ef..d3f9de820c 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php index 4df2b88936..81b4d3499a 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php @@ -36,8 +36,8 @@ use Illuminate\Validation\Validator; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. @@ -84,7 +84,7 @@ class UpdateRequest extends FormRequest public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + static function (Validator $validator) { // validate start before end only if both are there. $data = $validator->getData(); if (array_key_exists('start', $data) && array_key_exists('end', $data)) { diff --git a/app/Api/V1/Requests/Models/Category/StoreRequest.php b/app/Api/V1/Requests/Models/Category/StoreRequest.php index 5bff239013..4d06ada65b 100644 --- a/app/Api/V1/Requests/Models/Category/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Category/StoreRequest.php @@ -35,8 +35,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Category/UpdateRequest.php b/app/Api/V1/Requests/Models/Category/UpdateRequest.php index aaaf9bfde1..43e1f4cc5f 100644 --- a/app/Api/V1/Requests/Models/Category/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Category/UpdateRequest.php @@ -35,8 +35,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php b/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php index 9a9f96d8da..afd86d9ca9 100644 --- a/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php @@ -36,8 +36,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * @return array diff --git a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php index d737a7e00e..237134bd50 100644 --- a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php +++ b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php b/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php index 7eb4ae1dba..f133190265 100644 --- a/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php @@ -37,8 +37,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php index bfd273969d..5cd4ea8071 100644 --- a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php @@ -39,12 +39,12 @@ use Illuminate\Validation\Validator; */ class StoreRequest extends FormRequest { + use ChecksLogin; use ConvertsDataTypes; - use RecurrenceValidation; - use TransactionValidation; use CurrencyValidation; use GetRecurrenceData; - use ChecksLogin; + use RecurrenceValidation; + use TransactionValidation; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php index 8d5f790c4c..a8b8405d8b 100644 --- a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php @@ -40,12 +40,12 @@ use Illuminate\Validation\Validator; */ class UpdateRequest extends FormRequest { + use ChecksLogin; use ConvertsDataTypes; - use RecurrenceValidation; - use TransactionValidation; use CurrencyValidation; use GetRecurrenceData; - use ChecksLogin; + use RecurrenceValidation; + use TransactionValidation; /** * Get all data from the request. @@ -73,9 +73,7 @@ class UpdateRequest extends FormRequest if (null !== $reps) { $return['repetitions'] = $reps; } - if (null !== $transactions) { - $return['transactions'] = $transactions; - } + $return['transactions'] = $transactions; return $return; } @@ -125,16 +123,16 @@ class UpdateRequest extends FormRequest * Returns the transaction data as it is found in the submitted data. It's a complex method according to code * standards but it just has a lot of ??-statements because of the fields that may or may not exist. * - * @return array|null + * @return array */ - private function getTransactionData(): ?array + private function getTransactionData(): array { $return = []; // transaction data: /** @var array|null $transactions */ $transactions = $this->get('transactions'); if (null === $transactions) { - return null; + return []; } /** @var array $transaction */ foreach ($transactions as $transaction) { diff --git a/app/Api/V1/Requests/Models/Rule/StoreRequest.php b/app/Api/V1/Requests/Models/Rule/StoreRequest.php index 2b241f0e96..b9ae6ac673 100644 --- a/app/Api/V1/Requests/Models/Rule/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Rule/StoreRequest.php @@ -30,16 +30,14 @@ use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; -use function is_array; - /** * Class StoreRequest */ class StoreRequest extends FormRequest { + use ChecksLogin; use ConvertsDataTypes; use GetRuleConfiguration; - use ChecksLogin; /** * Get all data from the request. @@ -199,7 +197,8 @@ class StoreRequest extends FormRequest */ protected function atLeastOneActiveTrigger(Validator $validator): void { - $data = $validator->getData(); + $data = $validator->getData(); + /** @var string|int|array|null $triggers */ $triggers = $data['triggers'] ?? []; // need at least one trigger if (!is_countable($triggers) || 0 === count($triggers)) { @@ -228,7 +227,8 @@ class StoreRequest extends FormRequest */ protected function atLeastOneActiveAction(Validator $validator): void { - $data = $validator->getData(); + $data = $validator->getData(); + /** @var string|int|array|null $actions */ $actions = $data['actions'] ?? []; // need at least one trigger if (!is_countable($actions) || 0 === count($actions)) { diff --git a/app/Api/V1/Requests/Models/Rule/TestRequest.php b/app/Api/V1/Requests/Models/Rule/TestRequest.php index 331c4922c3..479941f43d 100644 --- a/app/Api/V1/Requests/Models/Rule/TestRequest.php +++ b/app/Api/V1/Requests/Models/Rule/TestRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class TestRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * @return array @@ -66,7 +66,16 @@ class TestRequest extends FormRequest */ private function getDate(string $field): ?Carbon { - return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field)); + $value = $this->query($field); + if (is_array($value)) { + return null; + } + $value = (string)$value; + $result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); + if (false === $result) { + return null; + } + return $result; } /** diff --git a/app/Api/V1/Requests/Models/Rule/TriggerRequest.php b/app/Api/V1/Requests/Models/Rule/TriggerRequest.php index 12785842c1..5786a4f9cc 100644 --- a/app/Api/V1/Requests/Models/Rule/TriggerRequest.php +++ b/app/Api/V1/Requests/Models/Rule/TriggerRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class TriggerRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * @return array @@ -56,7 +56,16 @@ class TriggerRequest extends FormRequest */ private function getDate(string $field): ?Carbon { - return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($this->query($field), 0, 10)); + $value = $this->query($field); + if (is_array($value)) { + return null; + } + $value = (string)$value; + $result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); + if (false === $result) { + return null; + } + return $result; } /** diff --git a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php index 363dc7eef6..e99b1ebca0 100644 --- a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php @@ -31,16 +31,14 @@ use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; -use function is_array; - /** * Class UpdateRequest */ class UpdateRequest extends FormRequest { + use ChecksLogin; use ConvertsDataTypes; use GetRuleConfiguration; - use ChecksLogin; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php b/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php index 00ffa6ec55..afbf3a1c83 100644 --- a/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php @@ -33,8 +33,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php b/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php index 81226973b5..65e1c4c352 100644 --- a/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class TestRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * @return array @@ -56,7 +56,16 @@ class TestRequest extends FormRequest */ private function getDate(string $field): ?Carbon { - return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field)); + $value = $this->query($field); + if (is_array($value)) { + return null; + } + $value = (string)$value; + $result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); + if (false === $result) { + return null; + } + return $result; } /** diff --git a/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php b/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php index 4d9514307a..671fc4f12c 100644 --- a/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class TriggerRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * @return array @@ -56,7 +56,16 @@ class TriggerRequest extends FormRequest */ private function getDate(string $field): ?Carbon { - return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field)); + $value = $this->query($field); + if (is_array($value)) { + return null; + } + $value = (string)$value; + $result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($value, 0, 10)); + if (false === $result) { + return null; + } + return $result; } /** diff --git a/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php b/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php index a417570bce..1989cdaaf5 100644 --- a/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Tag/StoreRequest.php b/app/Api/V1/Requests/Models/Tag/StoreRequest.php index 5f0ce7737b..a68ca04c61 100644 --- a/app/Api/V1/Requests/Models/Tag/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Tag/StoreRequest.php @@ -36,9 +36,9 @@ use Illuminate\Foundation\Http\FormRequest; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; - use ChecksLogin; use AppendsLocationData; + use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Tag/UpdateRequest.php b/app/Api/V1/Requests/Models/Tag/UpdateRequest.php index d364e8605f..52b64662ed 100644 --- a/app/Api/V1/Requests/Models/Tag/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Tag/UpdateRequest.php @@ -38,9 +38,9 @@ use Illuminate\Foundation\Http\FormRequest; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; - use ChecksLogin; use AppendsLocationData; + use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php index 3b26c259c3..b060432b5f 100644 --- a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php @@ -35,7 +35,6 @@ use FireflyIII\Validation\CurrencyValidation; use FireflyIII\Validation\GroupValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -43,12 +42,12 @@ use Illuminate\Validation\Validator; */ class StoreRequest extends FormRequest { - use TransactionValidation; - use GroupValidation; - use CurrencyValidation; - use ConvertsDataTypes; - use ChecksLogin; use AppendsLocationData; + use ChecksLogin; + use ConvertsDataTypes; + use CurrencyValidation; + use GroupValidation; + use TransactionValidation; /** * Get all data. Is pretty complex because of all the ??-statements. @@ -57,7 +56,7 @@ class StoreRequest extends FormRequest */ public function getAll(): array { - Log::debug('get all data in TransactionStoreRequest'); + app('log')->debug('get all data in TransactionStoreRequest'); return [ 'group_title' => $this->convertString('group_title'), @@ -83,75 +82,75 @@ class StoreRequest extends FormRequest foreach ($this->get('transactions') as $transaction) { $object = new NullArrayObject($transaction); $return[] = [ - 'type' => $this->clearString($object['type'], false), + 'type' => $this->clearString($object['type']), 'date' => $this->dateFromValue($object['date']), 'order' => $this->integerFromValue((string)$object['order']), 'currency_id' => $this->integerFromValue((string)$object['currency_id']), - 'currency_code' => $this->clearString((string)$object['currency_code'], false), + 'currency_code' => $this->clearString((string)$object['currency_code']), // foreign currency info: 'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']), - 'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code'], false), + 'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']), // amount and foreign amount. Cannot be 0. - 'amount' => $this->clearString((string)$object['amount'], false), - 'foreign_amount' => $this->clearString((string)$object['foreign_amount'], false), + 'amount' => $this->clearString((string)$object['amount']), + 'foreign_amount' => $this->clearString((string)$object['foreign_amount']), // description. - 'description' => $this->clearString($object['description'], false), + 'description' => $this->clearString($object['description']), // 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'], false), - 'source_iban' => $this->clearString((string)$object['source_iban'], false), - 'source_number' => $this->clearString((string)$object['source_number'], false), - 'source_bic' => $this->clearString((string)$object['source_bic'], false), + 'source_name' => $this->clearString((string)$object['source_name']), + 'source_iban' => $this->clearString((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'], false), - 'destination_iban' => $this->clearString((string)$object['destination_iban'], false), - 'destination_number' => $this->clearString((string)$object['destination_number'], false), - 'destination_bic' => $this->clearString((string)$object['destination_bic'], false), + 'destination_name' => $this->clearString((string)$object['destination_name']), + 'destination_iban' => $this->clearString((string)$object['destination_iban']), + 'destination_number' => $this->clearString((string)$object['destination_number']), + 'destination_bic' => $this->clearString((string)$object['destination_bic']), // budget info 'budget_id' => $this->integerFromValue((string)$object['budget_id']), - 'budget_name' => $this->clearString((string)$object['budget_name'], false), + 'budget_name' => $this->clearString((string)$object['budget_name']), // category info 'category_id' => $this->integerFromValue((string)$object['category_id']), - 'category_name' => $this->clearString((string)$object['category_name'], false), + 'category_name' => $this->clearString((string)$object['category_name']), // journal bill reference. Optional. Will only work for withdrawals 'bill_id' => $this->integerFromValue((string)$object['bill_id']), - 'bill_name' => $this->clearString((string)$object['bill_name'], false), + 'bill_name' => $this->clearString((string)$object['bill_name']), // piggy bank reference. Optional. Will only work for transfers 'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']), - 'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name'], false), + 'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']), // some other interesting properties 'reconciled' => $this->convertBoolean((string)$object['reconciled']), - 'notes' => $this->clearString((string)$object['notes']), + 'notes' => $this->clearStringKeepNewlines((string)$object['notes']), 'tags' => $this->arrayFromValue($object['tags']), // all custom fields: - 'internal_reference' => $this->clearString((string)$object['internal_reference'], false), - 'external_id' => $this->clearString((string)$object['external_id'], false), + 'internal_reference' => $this->clearString((string)$object['internal_reference']), + 'external_id' => $this->clearString((string)$object['external_id']), 'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')), 'recurrence_id' => $this->integerFromValue($object['recurrence_id']), - 'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id'], false), - 'external_url' => $this->clearString((string)$object['external_url'], false), + 'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']), + 'external_url' => $this->clearString((string)$object['external_url']), - 'sepa_cc' => $this->clearString((string)$object['sepa_cc'], false), - 'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op'], false), - 'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id'], false), - 'sepa_db' => $this->clearString((string)$object['sepa_db'], false), - 'sepa_country' => $this->clearString((string)$object['sepa_country'], false), - 'sepa_ep' => $this->clearString((string)$object['sepa_ep'], false), - 'sepa_ci' => $this->clearString((string)$object['sepa_ci'], false), - 'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id'], false), + 'sepa_cc' => $this->clearString((string)$object['sepa_cc']), + 'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']), + 'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']), + 'sepa_db' => $this->clearString((string)$object['sepa_db']), + 'sepa_country' => $this->clearString((string)$object['sepa_country']), + 'sepa_ep' => $this->clearString((string)$object['sepa_ep']), + 'sepa_ci' => $this->clearString((string)$object['sepa_ci']), + 'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']), // custom date fields. Must be Carbon objects. Presence is optional. 'interest_date' => $this->dateFromValue($object['interest_date']), 'book_date' => $this->dateFromValue($object['book_date']), @@ -173,7 +172,7 @@ class StoreRequest extends FormRequest */ public function rules(): array { - Log::debug('Collect rules of TransactionStoreRequest'); + app('log')->debug('Collect rules of TransactionStoreRequest'); $validProtocols = config('firefly.valid_url_protocols'); return [ // basic fields for group: @@ -270,9 +269,9 @@ class StoreRequest extends FormRequest $this->validateTransactionArray($validator); // must submit at least one transaction. - Log::debug('Now going to validateOneTransaction'); + app('log')->debug('Now going to validateOneTransaction'); $this->validateOneTransaction($validator); - Log::debug('Now done with validateOneTransaction'); + app('log')->debug('Now done with validateOneTransaction'); // all journals must have a description $this->validateDescriptions($validator); diff --git a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php index d8b19ab5d0..d03d8bef72 100644 --- a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php @@ -34,7 +34,6 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\GroupValidation; use FireflyIII\Validation\TransactionValidation; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -42,10 +41,10 @@ use Illuminate\Validation\Validator; */ class UpdateRequest extends FormRequest { - use TransactionValidation; - use GroupValidation; - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; + use GroupValidation; + use TransactionValidation; private array $arrayFields; private array $booleanFields; @@ -63,7 +62,7 @@ class UpdateRequest extends FormRequest */ public function getAll(): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $this->integerFields = [ 'order', 'currency_id', @@ -157,15 +156,18 @@ class UpdateRequest extends FormRequest */ private function getTransactionData(): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $return = []; - if (!is_countable($this->get('transactions'))) { + /** @var array|null $transactions */ + $transactions = $this->get('transactions'); + + if (!is_countable($transactions)) { return $return; } - /** @var array $transaction */ - foreach ($this->get('transactions') as $transaction) { + /** @var array|null $transaction */ + foreach ($transactions as $transaction) { if (!is_array($transaction)) { throw new FireflyException('Invalid data submitted: transaction is not array.'); } @@ -213,7 +215,7 @@ class UpdateRequest extends FormRequest { foreach ($this->stringFields as $fieldName) { if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->clearString((string)$transaction[$fieldName], false); + $current[$fieldName] = $this->clearString((string)$transaction[$fieldName]); } } @@ -230,7 +232,7 @@ class UpdateRequest extends FormRequest { foreach ($this->textareaFields as $fieldName) { if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->clearString((string)$transaction[$fieldName]); + $current[$fieldName] = $this->clearStringKeepNewlines((string)$transaction[$fieldName]); // keep newlines } } @@ -246,9 +248,9 @@ class UpdateRequest extends FormRequest private function getDateData(array $current, array $transaction): array { foreach ($this->dateFields as $fieldName) { - Log::debug(sprintf('Now at date field %s', $fieldName)); + app('log')->debug(sprintf('Now at date field %s', $fieldName)); if (array_key_exists($fieldName, $transaction)) { - Log::debug(sprintf('New value: "%s"', (string)$transaction[$fieldName])); + app('log')->debug(sprintf('New value: "%s"', (string)$transaction[$fieldName])); $current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]); } } @@ -320,7 +322,7 @@ class UpdateRequest extends FormRequest */ public function rules(): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $validProtocols = config('firefly.valid_url_protocols'); return [ // basic fields for group: @@ -406,7 +408,7 @@ class UpdateRequest extends FormRequest */ public function withValidator(Validator $validator): void { - Log::debug('Now in withValidator'); + app('log')->debug('Now in withValidator'); /** @var TransactionGroup $transactionGroup */ $transactionGroup = $this->route()->parameter('transactionGroup'); $validator->after( diff --git a/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php b/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php index 479348c85b..955f6a0aaa 100644 --- a/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php @@ -35,8 +35,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php index 7c15fb7357..8e2e8b99d8 100644 --- a/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php @@ -36,8 +36,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php index dd97a2a4c1..7857e5df0a 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php @@ -36,8 +36,8 @@ use Illuminate\Validation\Validator; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php index 92cc96c909..8fd2aba40c 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php @@ -36,8 +36,8 @@ use Illuminate\Validation\Validator; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php b/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php index cc9a4a0dcf..ecc201b9fb 100644 --- a/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class StoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php index ed3d1e7c95..f7e98e94c6 100644 --- a/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php @@ -36,8 +36,8 @@ use Illuminate\Validation\Rule; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/Models/Webhook/CreateRequest.php b/app/Api/V1/Requests/Models/Webhook/CreateRequest.php index eb6b1ad5bf..9aab513693 100644 --- a/app/Api/V1/Requests/Models/Webhook/CreateRequest.php +++ b/app/Api/V1/Requests/Models/Webhook/CreateRequest.php @@ -57,9 +57,9 @@ class CreateRequest extends FormRequest // this is the way. $return = $this->getAllData($fields); - $return['trigger'] = $triggers[$return['trigger']] ?? intval($return['trigger']); - $return['response'] = $responses[$return['response']] ?? intval($return['response']); - $return['delivery'] = $deliveries[$return['delivery']] ?? intval($return['delivery']); + $return['trigger'] = $triggers[$return['trigger']] ?? (int)($return['trigger']); + $return['response'] = $responses[$return['response']] ?? (int)($return['response']); + $return['delivery'] = $deliveries[$return['delivery']] ?? (int)($return['delivery']); return $return; } diff --git a/app/Api/V1/Requests/System/UpdateRequest.php b/app/Api/V1/Requests/System/UpdateRequest.php index 5c6c59fe07..173f49fa01 100644 --- a/app/Api/V1/Requests/System/UpdateRequest.php +++ b/app/Api/V1/Requests/System/UpdateRequest.php @@ -36,8 +36,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class UpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V1/Requests/System/UserStoreRequest.php b/app/Api/V1/Requests/System/UserStoreRequest.php index 28dae61728..b8e44ebc5d 100644 --- a/app/Api/V1/Requests/System/UserStoreRequest.php +++ b/app/Api/V1/Requests/System/UserStoreRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class UserStoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Logged in + owner diff --git a/app/Api/V1/Requests/System/UserUpdateRequest.php b/app/Api/V1/Requests/System/UserUpdateRequest.php index 87013e88fd..46280e7b2b 100644 --- a/app/Api/V1/Requests/System/UserUpdateRequest.php +++ b/app/Api/V1/Requests/System/UserUpdateRequest.php @@ -36,8 +36,8 @@ use Illuminate\Validation\Validator; */ class UserUpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Logged in + owner diff --git a/app/Api/V2/Controllers/Autocomplete/AccountController.php b/app/Api/V2/Controllers/Autocomplete/AccountController.php index a3dd036c4b..834dbb5cbc 100644 --- a/app/Api/V2/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V2/Controllers/Autocomplete/AccountController.php @@ -90,8 +90,8 @@ class AccountController extends Controller $date = $this->parameters->get('date') ?? today(config('app.timezone')); $result = $this->adminRepository->searchAccount((string)$query, $types, $data['limit']); $defaultCurrency = app('amount')->getDefaultCurrency(); - - $allItems = []; + $groupedResult = []; + $allItems = []; /** @var Account $account */ foreach ($result as $account) { $nameWithBalance = $account->name; @@ -102,7 +102,7 @@ class AccountController extends Controller $nameWithBalance = sprintf('%s (%s)', $account->name, app('amount')->formatAnything($currency, $balance, false)); } $type = (string)trans(sprintf('firefly.%s', $account->accountType->type)); - $groupedResult[$type] = $groupedResult[$type] ?? [ + $groupedResult[$type] ??= [ 'group ' => $type, 'items' => [], ]; @@ -123,12 +123,12 @@ class AccountController extends Controller usort( $allItems, - function (array $a, array $b): int { - $order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE]; - $pos_a = array_search($a['type'], $order, true); - $pos_b = array_search($b['type'], $order, true); + 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 $pos_a - $pos_b; + return $posLeft - $posRight; } ); return response()->json($allItems); diff --git a/app/Api/V2/Controllers/Chart/AccountController.php b/app/Api/V2/Controllers/Chart/AccountController.php index b45fa09e9f..0e8a132a46 100644 --- a/app/Api/V2/Controllers/Chart/AccountController.php +++ b/app/Api/V2/Controllers/Chart/AccountController.php @@ -26,7 +26,7 @@ namespace FireflyIII\Api\V2\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Api\V2\Controllers\Controller; -use FireflyIII\Api\V2\Request\Generic\DateRequest; +use FireflyIII\Api\V2\Request\Chart\DashboardChartRequest; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; @@ -35,6 +35,7 @@ use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use Illuminate\Http\JsonResponse; +use Illuminate\Support\Collection; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -78,14 +79,15 @@ class AccountController extends Controller * * TODO validate and set user_group_id from request * - * @param DateRequest $request + * @param DashboardChartRequest $request * * @return JsonResponse * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface * @throws FireflyException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function dashboard(DateRequest $request): JsonResponse + public function dashboard(DashboardChartRequest $request): JsonResponse { /** @var Carbon $start */ $start = $this->parameters->get('start'); @@ -93,18 +95,37 @@ class AccountController extends Controller $end = $this->parameters->get('end'); $end->endOfDay(); - // user's preferences - $defaultSet = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])->pluck('id')->toArray(); - $frontPage = app('preferences')->get('frontPageAccounts', $defaultSet); /** @var TransactionCurrency $default */ - $default = app('amount')->getDefaultCurrency(); - $accounts = $this->repository->getAccountsById($frontPage->data); + $default = app('amount')->getDefaultCurrency(); + $params = $request->getAll(); + /** @var Collection $accounts */ + $accounts = $params['accounts']; $chartData = []; - if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) { - $frontPage->data = $defaultSet; - $frontPage->save(); + // user's preferences + if (0 === $accounts->count()) { + $defaultSet = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])->pluck('id')->toArray(); + $frontPage = app('preferences')->get('frontPageAccounts', $defaultSet); + + if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) { + $frontPage->data = $defaultSet; + $frontPage->save(); + } + + $accounts = $this->repository->getAccountsById($frontPage->data); } + + // both options are overruled by "preselected" + if ('all' === $params['preselected']) { + $accounts = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); + } + if ('assets' === $params['preselected']) { + $accounts = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); + } + if ('liabilities' === $params['preselected']) { + $accounts = $this->repository->getAccountsByType([AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); + } + /** @var Account $account */ foreach ($accounts as $account) { $currency = $this->repository->getAccountCurrency($account); @@ -123,7 +144,7 @@ class AccountController extends Controller 'native_id' => (string)$default->id, 'native_code' => $default->code, 'native_symbol' => $default->symbol, - 'native_decimal_places' => (int)$default->decimal_places, + 'native_decimal_places' => $default->decimal_places, 'start' => $start->toAtomString(), 'end' => $end->toAtomString(), 'period' => '1D', diff --git a/app/Api/V2/Controllers/Chart/BalanceController.php b/app/Api/V2/Controllers/Chart/BalanceController.php index 8ba070f627..45caf1405c 100644 --- a/app/Api/V2/Controllers/Chart/BalanceController.php +++ b/app/Api/V2/Controllers/Chart/BalanceController.php @@ -32,7 +32,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; -use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use Illuminate\Http\JsonResponse; @@ -45,23 +44,6 @@ class BalanceController extends Controller { use CleansChartData; - private AccountRepositoryInterface $repository; - - /** - * - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - $this->repository = app(AccountRepositoryInterface::class); - - return $next($request); - } - ); - } - /** * The code is practically a duplicate of ReportController::operations. * @@ -99,7 +81,7 @@ class BalanceController extends Controller /** @var TransactionCurrency $default */ $default = app('amount')->getDefaultCurrency(); $converter = new ExchangeRateConverter(); - $currencies = [(int)$default->id => $default,]; // currency cache + $currencies = [$default->id => $default,]; // currency cache $data = []; $chartData = []; @@ -112,18 +94,18 @@ class BalanceController extends Controller $journals = $collector->getExtractedJournals(); // set array for default currency (even if unused later on) - $defaultCurrencyId = (int)$default->id; + $defaultCurrencyId = $default->id; $data[$defaultCurrencyId] = [ 'currency_id' => (string)$defaultCurrencyId, 'currency_symbol' => $default->symbol, 'currency_code' => $default->code, 'currency_name' => $default->name, - 'currency_decimal_places' => (int)$default->decimal_places, + 'currency_decimal_places' => $default->decimal_places, 'native_id' => (string)$defaultCurrencyId, 'native_symbol' => $default->symbol, 'native_code' => $default->code, 'native_name' => $default->name, - 'native_decimal_places' => (int)$default->decimal_places, + 'native_decimal_places' => $default->decimal_places, ]; @@ -139,7 +121,7 @@ class BalanceController extends Controller $currencies[$currencyId] = $currency; // may just re-assign itself, don't mind. // set the array with monetary info, if it does not exist. - $data[$currencyId] = $data[$currencyId] ?? [ + $data[$currencyId] ??= [ 'currency_id' => (string)$currencyId, 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], @@ -149,11 +131,11 @@ class BalanceController extends Controller 'native_id' => (string)$default->id, 'native_code' => $default->code, 'native_symbol' => $default->symbol, - 'native_decimal_places' => (int)$default->decimal_places, + 'native_decimal_places' => $default->decimal_places, ]; // set the array (in monetary info) with spent/earned in this $period, if it does not exist. - $data[$currencyId][$period] = $data[$currencyId][$period] ?? [ + $data[$currencyId][$period] ??= [ 'period' => $period, 'spent' => '0', 'earned' => '0', @@ -186,7 +168,7 @@ class BalanceController extends Controller $amountConverted = bcmul($amount, $rate); // perhaps transaction already has the foreign amount in the native currency. - if ((int)$journal['foreign_currency_id'] === (int)$default->id) { + if ((int)$journal['foreign_currency_id'] === $default->id) { $amountConverted = $journal['foreign_amount'] ?? '0'; $amountConverted = 'earned' === $key ? app('steam')->positive($amountConverted) : app('steam')->negative($amountConverted); } diff --git a/app/Api/V2/Controllers/Chart/BudgetController.php b/app/Api/V2/Controllers/Chart/BudgetController.php index eb3ebb8389..acd1f3c5c7 100644 --- a/app/Api/V2/Controllers/Chart/BudgetController.php +++ b/app/Api/V2/Controllers/Chart/BudgetController.php @@ -170,9 +170,8 @@ class BudgetController extends Controller */ private function noBudgetLimits(Budget $budget, Carbon $start, Carbon $end): array { - $budgetId = (int)$budget->id; - $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget])); - return $this->processExpenses($budgetId, $spent, $start, $end); + $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget])); + return $this->processExpenses($budget->id, $spent, $start, $end); } /** @@ -199,8 +198,8 @@ class BudgetController extends Controller * @var array $block */ foreach ($array as $currencyId => $block) { - $this->currencies[$currencyId] = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId); - $return[$currencyId] = $return[$currencyId] ?? [ + $this->currencies[$currencyId] ??= TransactionCurrency::find($currencyId); + $return[$currencyId] ??= [ 'currency_id' => (string)$currencyId, 'currency_code' => $block['currency_code'], 'currency_name' => $block['currency_name'], @@ -210,7 +209,7 @@ class BudgetController extends Controller 'native_code' => $this->currency->code, 'native_name' => $this->currency->name, 'native_symbol' => $this->currency->symbol, - 'native_decimal_places' => (int)$this->currency->decimal_places, + 'native_decimal_places' => $this->currency->decimal_places, 'start' => $start->toAtomString(), 'end' => $end->toAtomString(), 'spent' => '0', @@ -275,11 +274,10 @@ class BudgetController extends Controller */ private function processLimit(Budget $budget, BudgetLimit $limit): array { - $budgetId = (int)$budget->id; - $end = clone $limit->end_date; + $end = clone $limit->end_date; $end->endOfDay(); $spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection([$budget])); - $limitCurrencyId = (int)$limit->transaction_currency_id; + $limitCurrencyId = $limit->transaction_currency_id; $limitCurrency = $limit->transactionCurrency; $converter = new ExchangeRateConverter(); $filtered = []; @@ -295,9 +293,9 @@ class BudgetController extends Controller $filtered[$currencyId] = $entry; } } - $result = $this->processExpenses($budgetId, $filtered, $limit->start_date, $end); + $result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end); if (1 === count($result)) { - $compare = bccomp((string)$limit->amount, app('steam')->positive($result[$limitCurrencyId]['spent'])); + $compare = bccomp($limit->amount, app('steam')->positive($result[$limitCurrencyId]['spent'])); if (1 === $compare) { // convert this amount into the native currency: $result[$limitCurrencyId]['left'] = bcadd($limit->amount, $result[$limitCurrencyId]['spent']); diff --git a/app/Api/V2/Controllers/Chart/CategoryController.php b/app/Api/V2/Controllers/Chart/CategoryController.php index 6346c40f66..3171448c7a 100644 --- a/app/Api/V2/Controllers/Chart/CategoryController.php +++ b/app/Api/V2/Controllers/Chart/CategoryController.php @@ -74,6 +74,7 @@ class CategoryController extends Controller * * @return JsonResponse * @throws FireflyException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function dashboard(DateRequest $request): JsonResponse { @@ -104,22 +105,22 @@ class CategoryController extends Controller $amount = app('steam')->positive($journal['amount']); $nativeAmount = $converter->convert($default, $currency, $journal['date'], $amount); $key = sprintf('%s-%s', $categoryName, $currency->code); - if ((int)$journal['foreign_currency_id'] === (int)$default->id) { + if ((int)$journal['foreign_currency_id'] === $default->id) { $nativeAmount = app('steam')->positive($journal['foreign_amount']); } // create arrays - $return[$key] = $return[$key] ?? [ + $return[$key] ??= [ 'label' => $categoryName, 'currency_id' => (string)$currency->id, 'currency_code' => $currency->code, 'currency_name' => $currency->name, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, + 'currency_decimal_places' => $currency->decimal_places, 'native_id' => (string)$default->id, 'native_code' => $default->code, 'native_name' => $default->name, 'native_symbol' => $default->symbol, - 'native_decimal_places' => (int)$default->decimal_places, + 'native_decimal_places' => $default->decimal_places, 'period' => null, 'start' => $start->toAtomString(), 'end' => $end->toAtomString(), @@ -135,7 +136,7 @@ class CategoryController extends Controller $return = array_values($return); // order by native amount - usort($return, function (array $a, array $b) { + usort($return, static function (array $a, array $b) { return (float)$a['native_amount'] < (float)$b['native_amount'] ? 1 : -1; }); return response()->json($this->clean($return)); diff --git a/app/Api/V2/Controllers/Controller.php b/app/Api/V2/Controllers/Controller.php index 6783b2e812..2bf9c8b1e1 100644 --- a/app/Api/V2/Controllers/Controller.php +++ b/app/Api/V2/Controllers/Controller.php @@ -33,7 +33,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Routing\Controller as BaseController; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use League\Fractal\Manager; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; @@ -51,7 +50,7 @@ class Controller extends BaseController { use ValidatesUserGroupTrait; - protected const CONTENT_TYPE = 'application/vnd.api+json'; + protected const string CONTENT_TYPE = 'application/vnd.api+json'; protected ParameterBag $parameters; /** @@ -103,16 +102,16 @@ class Controller extends BaseController try { $date = request()->query->get($field); } catch (BadRequestException $e) { - Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field)); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field)); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } if (null !== $date) { try { - $obj = Carbon::parse($date, config('app.timezone')); + $obj = Carbon::parse((string)$date, config('app.timezone')); } catch (InvalidDateException | InvalidFormatException $e) { // don't care - app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr($date, 0, 20), $e->getMessage())); + app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr((string)$date, 0, 20), $e->getMessage())); } // out of range? set to null. if (null !== $obj && ($obj->year <= 1900 || $obj->year > 2099)) { @@ -128,8 +127,8 @@ class Controller extends BaseController try { $value = request()->query->get($integer); } catch (BadRequestException $e) { - Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $integer)); - Log::error($e->getMessage()); + app('log')->error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $integer)); + app('log')->error($e->getMessage()); $value = null; } if (null !== $value) { diff --git a/app/Api/V2/Controllers/Model/Account/ShowController.php b/app/Api/V2/Controllers/Model/Account/ShowController.php index 85a9845da2..0e90b1e3ab 100644 --- a/app/Api/V2/Controllers/Model/Account/ShowController.php +++ b/app/Api/V2/Controllers/Model/Account/ShowController.php @@ -28,7 +28,6 @@ use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Transformers\V2\AccountTransformer; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; /** * Show = show a single account. @@ -40,7 +39,7 @@ class ShowController extends Controller /** * TODO this endpoint is not yet reachable. */ - public function show(Request $request, Account $account): JsonResponse + public function show(Account $account): JsonResponse { $transformer = new AccountTransformer(); $transformer->setParameters($this->parameters); diff --git a/app/Api/V2/Controllers/Model/Bill/IndexController.php b/app/Api/V2/Controllers/Model/Bill/IndexController.php index 6ccb5e0da6..2d61611348 100644 --- a/app/Api/V2/Controllers/Model/Bill/IndexController.php +++ b/app/Api/V2/Controllers/Model/Bill/IndexController.php @@ -26,13 +26,10 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Controllers\Model\Bill; use FireflyIII\Api\V2\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Bill; use FireflyIII\Repositories\UserGroups\Bill\BillRepositoryInterface; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Transformers\V2\BillTransformer; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; /** @@ -63,13 +60,12 @@ class IndexController extends Controller } /** - * @param Request $request * * TODO see autocomplete/accountcontroller for list. * * @return JsonResponse */ - public function index(Request $request): JsonResponse + public function index(): JsonResponse { $this->repository->correctOrder(); $bills = $this->repository->getBills(); diff --git a/app/Api/V2/Controllers/Model/Bill/ShowController.php b/app/Api/V2/Controllers/Model/Bill/ShowController.php index e43107e412..9576886d7e 100644 --- a/app/Api/V2/Controllers/Model/Bill/ShowController.php +++ b/app/Api/V2/Controllers/Model/Bill/ShowController.php @@ -26,14 +26,11 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Controllers\Model\Bill; use FireflyIII\Api\V2\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Bill; use FireflyIII\Repositories\UserGroups\Bill\BillRepositoryInterface; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Transformers\V2\BillTransformer; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; -use Illuminate\Pagination\LengthAwarePaginator; /** * Class ShowController @@ -65,7 +62,7 @@ class ShowController extends Controller /** * TODO this endpoint is not documented */ - public function show(Request $request, Bill $bill): JsonResponse + public function show(Bill $bill): JsonResponse { $transformer = new BillTransformer(); $transformer->setParameters($this->parameters); diff --git a/app/Api/V2/Controllers/Model/Bill/SumController.php b/app/Api/V2/Controllers/Model/Bill/SumController.php index 161dfdd4ce..7498025f01 100644 --- a/app/Api/V2/Controllers/Model/Bill/SumController.php +++ b/app/Api/V2/Controllers/Model/Bill/SumController.php @@ -69,6 +69,7 @@ class SumController extends Controller * @param DateRequest $request * * @return JsonResponse + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function paid(DateRequest $request): JsonResponse { @@ -85,6 +86,7 @@ class SumController extends Controller * TODO see autocomplete/accountcontroller for list. * * @param DateRequest $request + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return JsonResponse */ diff --git a/app/Api/V2/Controllers/Model/Budget/ListController.php b/app/Api/V2/Controllers/Model/Budget/IndexController.php similarity index 80% rename from app/Api/V2/Controllers/Model/Budget/ListController.php rename to app/Api/V2/Controllers/Model/Budget/IndexController.php index 7b92c751ae..7b25687b55 100644 --- a/app/Api/V2/Controllers/Model/Budget/ListController.php +++ b/app/Api/V2/Controllers/Model/Budget/IndexController.php @@ -25,17 +25,15 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Controllers\Model\Budget; use FireflyIII\Api\V2\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Transformers\V2\BudgetTransformer; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; /** - * Class ListController + * Class IndexController */ -class ListController extends Controller +class IndexController extends Controller { private BudgetRepositoryInterface $repository; @@ -55,19 +53,17 @@ class ListController extends Controller * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/budgets/listBudgets * - * @param Request $request * * @return JsonResponse */ - public function index(Request $request): JsonResponse + public function index(): JsonResponse { - echo 'this needs move to Administration'; - throw new FireflyException('Needs migration to IndexController'); + $pageSize = $this->parameters->get('limit'); $collection = $this->repository->getActiveBudgets(); $total = $collection->count(); - $collection->slice($this->pageXSize * $this->parameters->get('page'), $this->pXageSize); + $collection->slice($pageSize * $this->parameters->get('page'), $pageSize); - $paginator = new LengthAwarePaginator($collection, $total, $this->pagXeSize, $this->parameters->get('page')); + $paginator = new LengthAwarePaginator($collection, $total, $pageSize, $this->parameters->get('page')); $transformer = new BudgetTransformer(); return response() diff --git a/app/Api/V2/Controllers/Model/Budget/ShowController.php b/app/Api/V2/Controllers/Model/Budget/ShowController.php index 561be1e45d..26c505a401 100644 --- a/app/Api/V2/Controllers/Model/Budget/ShowController.php +++ b/app/Api/V2/Controllers/Model/Budget/ShowController.php @@ -27,10 +27,8 @@ namespace FireflyIII\Api\V2\Controllers\Model\Budget; use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Request\Generic\DateRequest; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Support\Http\Api\ConvertsExchangeRates; use Illuminate\Http\JsonResponse; /** @@ -39,8 +37,6 @@ use Illuminate\Http\JsonResponse; */ class ShowController extends Controller { - use ConvertsExchangeRates; - private BudgetRepositoryInterface $repository; /** @@ -59,18 +55,19 @@ class ShowController extends Controller } /** + * 2023-10-29 removed the cerSum reference, not sure where this is used atm + * so removed from api.php. Also applies to "spent" method. + * * This endpoint is documented at: * TODO add URL * */ public function budgeted(DateRequest $request, Budget $budget): JsonResponse { - throw new FireflyException('Needs refactoring, uses deprecated method.'); - $data = $request->getAll(); - $result = $this->repository->budgetedInPeriodForBudget($budget, $data['start'], $data['end']); - $converted = $this->cerSum(array_values($result)); + $data = $request->getAll(); + $result = $this->repository->budgetedInPeriodForBudget($budget, $data['start'], $data['end']); - return response()->json($converted); + return response()->json($result); } /** @@ -80,11 +77,9 @@ class ShowController extends Controller */ public function spent(DateRequest $request, Budget $budget): JsonResponse { - throw new FireflyException('Needs refactoring, uses deprecated method.'); - $data = $request->getAll(); - $result = $this->repository->spentInPeriodForBudget($budget, $data['start'], $data['end']); - $converted = $this->cerSum(array_values($result)); + $data = $request->getAll(); + $result = $this->repository->spentInPeriodForBudget($budget, $data['start'], $data['end']); - return response()->json($converted); + return response()->json($result); } } diff --git a/app/Api/V2/Controllers/Model/Budget/SumController.php b/app/Api/V2/Controllers/Model/Budget/SumController.php index 9f5c8f1ff6..6f6c29446c 100644 --- a/app/Api/V2/Controllers/Model/Budget/SumController.php +++ b/app/Api/V2/Controllers/Model/Budget/SumController.php @@ -26,9 +26,7 @@ namespace FireflyIII\Api\V2\Controllers\Model\Budget; use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Request\Generic\DateRequest; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Support\Http\Api\ConvertsExchangeRates; use Illuminate\Http\JsonResponse; /** @@ -36,8 +34,6 @@ use Illuminate\Http\JsonResponse; */ class SumController extends Controller { - use ConvertsExchangeRates; - private BudgetRepositoryInterface $repository; /** @@ -65,12 +61,10 @@ class SumController extends Controller */ public function budgeted(DateRequest $request): JsonResponse { - throw new FireflyException('Needs refactoring, uses deprecated method.'); - $data = $request->getAll(); - $result = $this->repository->budgetedInPeriod($data['start'], $data['end']); - $converted = $this->cerSum(array_values($result)); + $data = $request->getAll(); + $result = $this->repository->budgetedInPeriod($data['start'], $data['end']); - return response()->json($converted); + return response()->json($result); } /** @@ -83,11 +77,9 @@ class SumController extends Controller */ public function spent(DateRequest $request): JsonResponse { - throw new FireflyException('Needs refactoring, uses deprecated method.'); - $data = $request->getAll(); - $result = $this->repository->spentInPeriod($data['start'], $data['end']); - $converted = $this->cerSum(array_values($result)); + $data = $request->getAll(); + $result = $this->repository->spentInPeriod($data['start'], $data['end']); - return response()->json($converted); + return response()->json($result); } } diff --git a/app/Api/V2/Controllers/Model/BudgetLimit/ListController.php b/app/Api/V2/Controllers/Model/BudgetLimit/ListController.php index 9b72294183..5272aa7a4d 100644 --- a/app/Api/V2/Controllers/Model/BudgetLimit/ListController.php +++ b/app/Api/V2/Controllers/Model/BudgetLimit/ListController.php @@ -25,51 +25,47 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Controllers\Model\BudgetLimit; use FireflyIII\Api\V2\Controllers\Controller; -use FireflyIII\Api\V2\Request\Generic\DateRequest; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Budget; -use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; -use FireflyIII\Transformers\V2\BudgetLimitTransformer; use Illuminate\Http\JsonResponse; -use Illuminate\Pagination\LengthAwarePaginator; /** * Class ListController */ class ListController extends Controller { - private BudgetLimitRepositoryInterface $repository; - - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - $this->repository = app(BudgetLimitRepositoryInterface::class); - - return $next($request); - } - ); - } + // private BudgetLimitRepositoryInterface $repository; + // + // public function __construct() + // { + // parent::__construct(); + // $this->middleware( + // function ($request, $next) { + // $this->repository = app(BudgetLimitRepositoryInterface::class); + // + // return $next($request); + // } + // ); + // } /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/budgets/listBudgetLimitByBudget + * // DateRequest $request, Budget $budget */ - public function index(DateRequest $request, Budget $budget): JsonResponse + public function index(): JsonResponse { - throw new FireflyException('Needs refactoring, move to IndexController.'); - $pageSize = $this->parameters->get('limit'); - $dates = $request->getAll(); - $collection = $this->repository->getBudgetLimits($budget, $dates['start'], $dates['end']); - $total = $collection->count(); - $collection->slice($pageSize * $this->parameters->get('page'), $pageSize); - - $paginator = new LengthAwarePaginator($collection, $total, $pageSize, $this->parameters->get('page')); - $transformer = new BudgetLimitTransformer(); - - return response() - ->api($this->jsonApiList('budget-limits', $paginator, $transformer)) - ->header('Content-Type', self::CONTENT_TYPE); + return response()->json([]); + // throw new FireflyException('Needs refactoring, move to IndexController.'); + // $pageSize = $this->parameters->get('limit'); + // $dates = $request->getAll(); + // $collection = $this->repository->getBudgetLimits($budget, $dates['start'], $dates['end']); + // $total = $collection->count(); + // $collection->slice($pageSize * $this->parameters->get('page'), $pageSize); + // + // $paginator = new LengthAwarePaginator($collection, $total, $pageSize, $this->parameters->get('page')); + // $transformer = new BudgetLimitTransformer(); + // + // return response() + // ->api($this->jsonApiList('budget-limits', $paginator, $transformer)) + // ->header('Content-Type', self::CONTENT_TYPE); } } diff --git a/app/Api/V2/Controllers/Model/PiggyBank/IndexController.php b/app/Api/V2/Controllers/Model/PiggyBank/IndexController.php index 55d73bb84a..b0fa896470 100644 --- a/app/Api/V2/Controllers/Model/PiggyBank/IndexController.php +++ b/app/Api/V2/Controllers/Model/PiggyBank/IndexController.php @@ -26,12 +26,10 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Controllers\Model\PiggyBank; use FireflyIII\Api\V2\Controllers\Controller; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\UserGroups\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Transformers\V2\PiggyBankTransformer; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; /** @@ -61,13 +59,11 @@ class IndexController extends Controller } /** - * @param Request $request - * * TODO see autocomplete/accountcontroller for list. * * @return JsonResponse */ - public function index(Request $request): JsonResponse + public function index(): JsonResponse { $piggies = $this->repository->getPiggyBanks(); $pageSize = $this->parameters->get('limit'); diff --git a/app/Api/V2/Controllers/Model/Transaction/StoreController.php b/app/Api/V2/Controllers/Model/Transaction/StoreController.php index 4dc062cb0e..eb60af6921 100644 --- a/app/Api/V2/Controllers/Model/Transaction/StoreController.php +++ b/app/Api/V2/Controllers/Model/Transaction/StoreController.php @@ -82,19 +82,19 @@ class StoreController extends Controller try { $transactionGroup = $this->groupRepository->store($data); - } catch (DuplicateTransactionException $e) { + } catch (DuplicateTransactionException $e) { // @phpstan-ignore-line app('log')->warning('Caught a duplicate transaction. Return error message.'); $validator = Validator::make( ['transactions' => [['description' => $e->getMessage()]]], ['transactions.0.description' => new IsDuplicateTransaction()] ); - throw new ValidationException($validator, 0, $e); - } catch (FireflyException $e) { + throw new ValidationException($validator); // @phpstan-ignore-line + } catch (FireflyException $e) { // @phpstan-ignore-line app('log')->warning('Caught an exception. Return error message.'); app('log')->error($e->getMessage()); $message = sprintf('Internal exception: %s', $e->getMessage()); $validator = Validator::make(['transactions' => [['description' => $message]]], ['transactions.0.description' => new IsDuplicateTransaction()]); - throw new ValidationException($validator, 0, $e); + throw new ValidationException($validator); // @phpstan-ignore-line } app('preferences')->mark(); $applyRules = $data['apply_rules'] ?? true; diff --git a/app/Api/V2/Controllers/Summary/BasicController.php b/app/Api/V2/Controllers/Summary/BasicController.php index bc88c3dd50..6166b21675 100644 --- a/app/Api/V2/Controllers/Summary/BasicController.php +++ b/app/Api/V2/Controllers/Summary/BasicController.php @@ -100,6 +100,7 @@ class BasicController extends Controller * * @return JsonResponse * @throws Exception + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function basic(DateRequest $request): JsonResponse { @@ -163,15 +164,15 @@ class BasicController extends Controller $currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId); $currencies[$currencyId] = $currency; $nativeAmount = $converter->convert($currency, $default, $transactionJournal['date'], $amount); - if ((int)$transactionJournal['foreign_currency_id'] === (int)$default->id) { + if ((int)$transactionJournal['foreign_currency_id'] === $default->id) { // use foreign amount instead $nativeAmount = $transactionJournal['foreign_amount']; } // prep the arrays - $incomes[$currencyId] = $incomes[$currencyId] ?? '0'; - $incomes['native'] = $incomes['native'] ?? '0'; - $sums[$currencyId] = $sums[$currencyId] ?? '0'; - $sums['native'] = $sums['native'] ?? '0'; + $incomes[$currencyId] ??= '0'; + $incomes['native'] ??= '0'; + $sums[$currencyId] ??= '0'; + $sums['native'] ??= '0'; // add values: $incomes[$currencyId] = bcadd($incomes[$currencyId], $amount); @@ -201,16 +202,16 @@ class BasicController extends Controller $currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId); $currencies[$currencyId] = $currency; $nativeAmount = $converter->convert($currency, $default, $transactionJournal['date'], $amount); - if ((int)$transactionJournal['foreign_currency_id'] === (int)$default->id) { + if ((int)$transactionJournal['foreign_currency_id'] === $default->id) { // use foreign amount instead $nativeAmount = $transactionJournal['foreign_amount']; } // prep arrays - $expenses[$currencyId] = $expenses[$currencyId] ?? '0'; - $expenses['native'] = $expenses['native'] ?? '0'; - $sums[$currencyId] = $sums[$currencyId] ?? '0'; - $sums['native'] = $sums['native'] ?? '0'; + $expenses[$currencyId] ??= '0'; + $expenses['native'] ??= '0'; + $sums[$currencyId] ??= '0'; + $sums['native'] ??= '0'; // add values $expenses[$currencyId] = bcadd($expenses[$currencyId], $amount); @@ -376,7 +377,7 @@ class BasicController extends Controller 'currency_id' => (string)$default->id, 'currency_code' => $default->code, 'currency_symbol' => $default->symbol, - 'currency_decimal_places' => (int)$default->decimal_places, + 'currency_decimal_places' => $default->decimal_places, ]; $nativePerDay = [ 'key' => 'left-per-day-to-spend-in-native', @@ -384,7 +385,7 @@ class BasicController extends Controller 'currency_id' => (string)$default->id, 'currency_code' => $default->code, 'currency_symbol' => $default->symbol, - 'currency_decimal_places' => (int)$default->decimal_places, + 'currency_decimal_places' => $default->decimal_places, ]; /** @@ -393,7 +394,7 @@ class BasicController extends Controller */ foreach ($spent as $currencyId => $row) { app('log')->debug(sprintf('Processing spent array in currency #%d', $currencyId)); - $currencyId = (int)$currencyId; + $currencyId = $currencyId; $spent = '0'; $spentNative = '0'; // get the sum from the array of transactions (double loop but who cares) @@ -407,7 +408,7 @@ class BasicController extends Controller $currencies[$currencyId] = $currency; $amount = app('steam')->negative($journal['amount']); $amountNative = $converter->convert($default, $currency, $start, $amount); - if ((int)$journal['foreign_currency_id'] === (int)$default->id) { + if ((int)$journal['foreign_currency_id'] === $default->id) { $amountNative = $journal['foreign_amount']; } $spent = bcadd($spent, $amount); diff --git a/app/Api/V2/Controllers/Summary/NetWorthController.php b/app/Api/V2/Controllers/Summary/NetWorthController.php index 553721d3f6..90cb1dac90 100644 --- a/app/Api/V2/Controllers/Summary/NetWorthController.php +++ b/app/Api/V2/Controllers/Summary/NetWorthController.php @@ -27,7 +27,9 @@ namespace FireflyIII\Api\V2\Controllers\Summary; use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Request\Generic\SingleDateRequest; use FireflyIII\Helpers\Report\NetWorthInterface; -use FireflyIII\Support\Http\Api\ConvertsExchangeRates; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use Illuminate\Http\JsonResponse; @@ -37,9 +39,9 @@ use Illuminate\Http\JsonResponse; class NetWorthController extends Controller { use ValidatesUserGroupTrait; - use ConvertsExchangeRates; - private NetWorthInterface $netWorth; + private NetWorthInterface $netWorth; + private AccountRepositoryInterface $repository; /** * @@ -49,12 +51,13 @@ class NetWorthController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - $this->netWorth = app(NetWorthInterface::class); - + $this->netWorth = app(NetWorthInterface::class); + $this->repository = app(AccountRepositoryInterface::class); // new way of user group validation $userGroup = $this->validateUserGroup($request); if (null !== $userGroup) { $this->netWorth->setUserGroup($userGroup); + $this->repository->setUserGroup($userGroup); } return $next($request); @@ -72,10 +75,21 @@ class NetWorthController extends Controller */ public function get(SingleDateRequest $request): JsonResponse { - $date = $request->getDate(); - $result = $this->netWorth->sumNetWorthByCurrency($date); - $converted = $this->cerSum($result); + $date = $request->getDate(); + $accounts = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]); - return response()->api($converted); + // filter list on preference of being included. + $filtered = $accounts->filter( + function (Account $account) { + $includeNetWorth = $this->repository->getMetaValue($account, 'include_net_worth'); + + return null === $includeNetWorth || '1' === $includeNetWorth; + } + ); + + // skip accounts that should not be in the net worth + $result = $this->netWorth->byAccounts($filtered, $date); + + return response()->api($result); } } diff --git a/app/Api/V2/Controllers/UserGroup/DestroyController.php b/app/Api/V2/Controllers/UserGroup/DestroyController.php index 123b9eded6..23e01c0f9a 100644 --- a/app/Api/V2/Controllers/UserGroup/DestroyController.php +++ b/app/Api/V2/Controllers/UserGroup/DestroyController.php @@ -31,7 +31,6 @@ use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -57,17 +56,17 @@ class DestroyController extends Controller } /** - * @param Request $request * @param UserGroup $userGroup * * @return JsonResponse */ - public function destroy(Request $request, UserGroup $userGroup): JsonResponse + public function destroy(UserGroup $userGroup): JsonResponse { /** @var User $user */ $user = auth()->user(); + // to access this function: must be group owner or sysadmin. // need owner role or system owner role to delete user group. - $access = $user->hasRoleInGroup($userGroup, UserRoleEnum::OWNER, false, true); + $access = $user->hasSpecificRoleInGroup($userGroup, UserRoleEnum::OWNER) || $user->hasRole('owner'); if (false === $access) { throw new NotFoundHttpException(); } diff --git a/app/Api/V2/Controllers/UserGroup/ShowController.php b/app/Api/V2/Controllers/UserGroup/ShowController.php index fbc441427e..4b5c492128 100644 --- a/app/Api/V2/Controllers/UserGroup/ShowController.php +++ b/app/Api/V2/Controllers/UserGroup/ShowController.php @@ -30,7 +30,6 @@ use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface; use FireflyIII\Transformers\V2\UserGroupTransformer; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -57,11 +56,9 @@ class ShowController extends Controller } /** - * @param Request $request - * * @return JsonResponse */ - public function index(Request $request): JsonResponse + public function index(): JsonResponse { $collection = new Collection(); $pageSize = $this->parameters->get('limit'); @@ -85,12 +82,11 @@ class ShowController extends Controller } /** - * @param Request $request * @param UserGroup $userGroup * * @return JsonResponse */ - public function show(Request $request, UserGroup $userGroup): JsonResponse + public function show(UserGroup $userGroup): JsonResponse { $transformer = new UserGroupTransformer(); $transformer->setParameters($this->parameters); diff --git a/app/Api/V2/Request/Autocomplete/AutocompleteRequest.php b/app/Api/V2/Request/Autocomplete/AutocompleteRequest.php index e406a9e8b3..b33161b6ff 100644 --- a/app/Api/V2/Request/Autocomplete/AutocompleteRequest.php +++ b/app/Api/V2/Request/Autocomplete/AutocompleteRequest.php @@ -27,7 +27,6 @@ use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\AccountType; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; -use FireflyIII\User; use Illuminate\Foundation\Http\FormRequest; /** @@ -35,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class AutocompleteRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS]; @@ -55,8 +54,6 @@ class AutocompleteRequest extends FormRequest // remove 'initial balance' and another from allowed types. its internal $array = array_diff($array, [AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION]); - /** @var User $user */ - $user = auth()->user(); return [ 'types' => $array, diff --git a/app/Api/V2/Request/Chart/BalanceChartRequest.php b/app/Api/V2/Request/Chart/BalanceChartRequest.php index b2e137fe35..c0023bb944 100644 --- a/app/Api/V2/Request/Chart/BalanceChartRequest.php +++ b/app/Api/V2/Request/Chart/BalanceChartRequest.php @@ -36,8 +36,8 @@ use Illuminate\Validation\Validator; */ class BalanceChartRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; use ValidatesUserGroupTrait; /** @@ -64,7 +64,7 @@ class BalanceChartRequest extends FormRequest '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', 'accounts.*' => 'required|exists:accounts,id', - 'period' => sprintf('required|in:%s', join(',', config('firefly.valid_view_ranges'))), + 'period' => sprintf('required|in:%s', implode(',', config('firefly.valid_view_ranges'))), ]; } @@ -76,7 +76,7 @@ class BalanceChartRequest extends FormRequest public function withValidator(Validator $validator): void { $validator->after( - function (Validator $validator) { + static function (Validator $validator) { // validate transaction query data. $data = $validator->getData(); if (!array_key_exists('accounts', $data)) { diff --git a/app/Api/V2/Request/Chart/DashboardChartRequest.php b/app/Api/V2/Request/Chart/DashboardChartRequest.php new file mode 100644 index 0000000000..c82596c799 --- /dev/null +++ b/app/Api/V2/Request/Chart/DashboardChartRequest.php @@ -0,0 +1,90 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V2\Request\Chart; + +use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; +use FireflyIII\Support\Request\ChecksLogin; +use FireflyIII\Support\Request\ConvertsDataTypes; +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; + +/** + * Class DashboardChartRequest + */ +class DashboardChartRequest extends FormRequest +{ + use ChecksLogin; + use ConvertsDataTypes; + use ValidatesUserGroupTrait; + + /** + * Get all data from the request. + * + * @return array + */ + public function getAll(): array + { + return [ + 'accounts' => $this->getAccountList(), + 'preselected' => $this->convertString('preselected'), + ]; + } + + /** + * The rules that the incoming request must be matched against. + * + * @return array + */ + public function rules(): array + { + 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', + ]; + } + + /** + * @param Validator $validator + * + * @return void + */ + public function withValidator(Validator $validator): void + { + $validator->after( + static function (Validator $validator) { + // 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'])); + } + } + ); + } +} diff --git a/app/Api/V2/Request/Generic/DateRequest.php b/app/Api/V2/Request/Generic/DateRequest.php index 4f8eff34d8..baa7b2c521 100644 --- a/app/Api/V2/Request/Generic/DateRequest.php +++ b/app/Api/V2/Request/Generic/DateRequest.php @@ -35,8 +35,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class DateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V2/Request/Generic/SingleDateRequest.php b/app/Api/V2/Request/Generic/SingleDateRequest.php index c76e6df39a..afd460cdcc 100644 --- a/app/Api/V2/Request/Generic/SingleDateRequest.php +++ b/app/Api/V2/Request/Generic/SingleDateRequest.php @@ -36,8 +36,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class SingleDateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data from the request. diff --git a/app/Api/V2/Request/Model/Transaction/StoreRequest.php b/app/Api/V2/Request/Model/Transaction/StoreRequest.php index 44ba9fffcc..0137fb22d2 100644 --- a/app/Api/V2/Request/Model/Transaction/StoreRequest.php +++ b/app/Api/V2/Request/Model/Transaction/StoreRequest.php @@ -50,13 +50,13 @@ use Illuminate\Validation\Validator; */ class StoreRequest extends FormRequest { - use ChecksLogin; - use ConvertsDataTypes; - - use TransactionValidation; - use GroupValidation; - use CurrencyValidation; use AppendsLocationData; + use ChecksLogin; + + use ConvertsDataTypes; + use CurrencyValidation; + use GroupValidation; + use TransactionValidation; protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS]; @@ -93,75 +93,75 @@ class StoreRequest extends FormRequest foreach ($this->get('transactions') as $transaction) { $object = new NullArrayObject($transaction); $return[] = [ - 'type' => $this->clearString($object['type'], false), + 'type' => $this->clearString($object['type']), 'date' => $this->dateFromValue($object['date']), 'order' => $this->integerFromValue((string)$object['order']), 'currency_id' => $this->integerFromValue((string)$object['currency_id']), - 'currency_code' => $this->clearString((string)$object['currency_code'], false), + 'currency_code' => $this->clearString((string)$object['currency_code']), // foreign currency info: 'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']), - 'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code'], false), + 'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']), // amount and foreign amount. Cannot be 0. - 'amount' => $this->clearString((string)$object['amount'], false), - 'foreign_amount' => $this->clearString((string)$object['foreign_amount'], false), + 'amount' => $this->clearString((string)$object['amount']), + 'foreign_amount' => $this->clearString((string)$object['foreign_amount']), // description. - 'description' => $this->clearString($object['description'], false), + 'description' => $this->clearString($object['description']), // 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'], false), - 'source_iban' => $this->clearString((string)$object['source_iban'], false), - 'source_number' => $this->clearString((string)$object['source_number'], false), - 'source_bic' => $this->clearString((string)$object['source_bic'], false), + 'source_name' => $this->clearString((string)$object['source_name']), + 'source_iban' => $this->clearString((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'], false), - 'destination_iban' => $this->clearString((string)$object['destination_iban'], false), - 'destination_number' => $this->clearString((string)$object['destination_number'], false), - 'destination_bic' => $this->clearString((string)$object['destination_bic'], false), + 'destination_name' => $this->clearString((string)$object['destination_name']), + 'destination_iban' => $this->clearString((string)$object['destination_iban']), + 'destination_number' => $this->clearString((string)$object['destination_number']), + 'destination_bic' => $this->clearString((string)$object['destination_bic']), // budget info 'budget_id' => $this->integerFromValue((string)$object['budget_id']), - 'budget_name' => $this->clearString((string)$object['budget_name'], false), + 'budget_name' => $this->clearString((string)$object['budget_name']), // category info 'category_id' => $this->integerFromValue((string)$object['category_id']), - 'category_name' => $this->clearString((string)$object['category_name'], false), + 'category_name' => $this->clearString((string)$object['category_name']), // journal bill reference. Optional. Will only work for withdrawals 'bill_id' => $this->integerFromValue((string)$object['bill_id']), - 'bill_name' => $this->clearString((string)$object['bill_name'], false), + 'bill_name' => $this->clearString((string)$object['bill_name']), // piggy bank reference. Optional. Will only work for transfers 'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']), - 'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name'], false), + 'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']), // some other interesting properties 'reconciled' => $this->convertBoolean((string)$object['reconciled']), - 'notes' => $this->clearString((string)$object['notes']), + 'notes' => $this->clearStringKeepNewlines((string)$object['notes']), 'tags' => $this->arrayFromValue($object['tags']), // all custom fields: - 'internal_reference' => $this->clearString((string)$object['internal_reference'], false), - 'external_id' => $this->clearString((string)$object['external_id'], false), + 'internal_reference' => $this->clearString((string)$object['internal_reference']), + 'external_id' => $this->clearString((string)$object['external_id']), 'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')), 'recurrence_id' => $this->integerFromValue($object['recurrence_id']), - 'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id'], false), - 'external_url' => $this->clearString((string)$object['external_url'], false), + 'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']), + 'external_url' => $this->clearString((string)$object['external_url']), - 'sepa_cc' => $this->clearString((string)$object['sepa_cc'], false), - 'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op'], false), - 'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id'], false), - 'sepa_db' => $this->clearString((string)$object['sepa_db'], false), - 'sepa_country' => $this->clearString((string)$object['sepa_country'], false), - 'sepa_ep' => $this->clearString((string)$object['sepa_ep'], false), - 'sepa_ci' => $this->clearString((string)$object['sepa_ci'], false), - 'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id'], false), + 'sepa_cc' => $this->clearString((string)$object['sepa_cc']), + 'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']), + 'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']), + 'sepa_db' => $this->clearString((string)$object['sepa_db']), + 'sepa_country' => $this->clearString((string)$object['sepa_country']), + 'sepa_ep' => $this->clearString((string)$object['sepa_ep']), + 'sepa_ci' => $this->clearString((string)$object['sepa_ci']), + 'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']), // custom date fields. Must be Carbon objects. Presence is optional. 'interest_date' => $this->dateFromValue($object['interest_date']), 'book_date' => $this->dateFromValue($object['book_date']), diff --git a/app/Api/V2/Request/UserGroup/UpdateMembershipRequest.php b/app/Api/V2/Request/UserGroup/UpdateMembershipRequest.php index 75b56e49ad..552ebee6de 100644 --- a/app/Api/V2/Request/UserGroup/UpdateMembershipRequest.php +++ b/app/Api/V2/Request/UserGroup/UpdateMembershipRequest.php @@ -64,7 +64,7 @@ class UpdateMembershipRequest extends FormRequest return [ 'id' => 'exists:users,id|required_without:email', 'email' => 'exists:users,email|required_without:id', - 'roles.*' => 'required|in:' . join(',', $validRoles), + 'roles.*' => 'required|in:' . implode(',', $validRoles), ]; } } diff --git a/app/Api/V2/Response/Sum/AutoSum.php b/app/Api/V2/Response/Sum/AutoSum.php index 5953c7ed71..bb1364839d 100644 --- a/app/Api/V2/Response/Sum/AutoSum.php +++ b/app/Api/V2/Response/Sum/AutoSum.php @@ -55,7 +55,7 @@ class AutoSum /** @var string $amount */ $amount = $getSum($object); - $return[$currency->id] = $return[$currency->id] ?? [ + $return[$currency->id] ??= [ 'id' => (string)$currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, diff --git a/app/Console/Commands/Correction/CorrectAmounts.php b/app/Console/Commands/Correction/CorrectAmounts.php index a35b941aaa..147fb0eafd 100644 --- a/app/Console/Commands/Correction/CorrectAmounts.php +++ b/app/Console/Commands/Correction/CorrectAmounts.php @@ -88,7 +88,7 @@ class CorrectAmounts extends Command } /** @var AutoBudget $item */ foreach ($set as $item) { - $item->amount = app('steam')->positive((string)$item->amount); + $item->amount = app('steam')->positive($item->amount); $item->save(); } $this->friendlyInfo(sprintf('Corrected %d auto budget amount(s).', $count)); @@ -108,7 +108,7 @@ class CorrectAmounts extends Command } /** @var AvailableBudget $item */ foreach ($set as $item) { - $item->amount = app('steam')->positive((string)$item->amount); + $item->amount = app('steam')->positive($item->amount); $item->save(); } $this->friendlyInfo(sprintf('Corrected %d available budget amount(s).', $count)); @@ -128,8 +128,8 @@ class CorrectAmounts extends Command } /** @var Bill $item */ foreach ($set as $item) { - $item->amount_min = app('steam')->positive((string)$item->amount_min); - $item->amount_max = app('steam')->positive((string)$item->amount_max); + $item->amount_min = app('steam')->positive($item->amount_min); + $item->amount_max = app('steam')->positive($item->amount_max); $item->save(); } $this->friendlyInfo(sprintf('Corrected %d bill amount(s).', $count)); @@ -149,7 +149,7 @@ class CorrectAmounts extends Command } /** @var BudgetLimit $item */ foreach ($set as $item) { - $item->amount = app('steam')->positive((string)$item->amount); + $item->amount = app('steam')->positive($item->amount); $item->save(); } $this->friendlyInfo(sprintf('Corrected %d budget limit amount(s).', $count)); @@ -167,9 +167,9 @@ class CorrectAmounts extends Command return; } - /** @var BudgetLimit $item */ + /** @var CurrencyExchangeRate $item */ foreach ($set as $item) { - $item->rate = app('steam')->positive((string)$item->rate); + $item->rate = app('steam')->positive($item->rate); $item->save(); } $this->friendlyInfo(sprintf('Corrected %d currency exchange rate(s).', $count)); @@ -189,7 +189,7 @@ class CorrectAmounts extends Command } /** @var PiggyBankRepetition $item */ foreach ($set as $item) { - $item->currentamount = app('steam')->positive((string)$item->currentamount); + $item->currentamount = app('steam')->positive($item->currentamount); $item->save(); } $this->friendlyInfo(sprintf('Corrected %d piggy bank repetition amount(s).', $count)); @@ -207,9 +207,9 @@ class CorrectAmounts extends Command return; } - /** @var PiggyBankRepetition $item */ + /** @var PiggyBank $item */ foreach ($set as $item) { - $item->targetamount = app('steam')->positive((string)$item->targetamount); + $item->targetamount = app('steam')->positive($item->targetamount); $item->save(); } $this->friendlyInfo(sprintf('Corrected %d piggy bank amount(s).', $count)); @@ -229,10 +229,10 @@ class CorrectAmounts extends Command return; } - /** @var PiggyBankRepetition $item */ + /** @var RecurrenceTransaction $item */ foreach ($set as $item) { - $item->amount = app('steam')->positive((string)$item->amount); - $item->foreign_amount = app('steam')->positive((string)$item->foreign_amount); + $item->amount = app('steam')->positive($item->amount); + $item->foreign_amount = app('steam')->positive($item->foreign_amount); $item->save(); } $this->friendlyInfo(sprintf('Corrected %d recurring transaction amount(s).', $count)); @@ -259,7 +259,7 @@ class CorrectAmounts extends Command } if (-1 === $check) { $fixed++; - $item->trigger_value = app('steam')->positive((string)$item->trigger_value); + $item->trigger_value = app('steam')->positive($item->trigger_value); $item->save(); } } diff --git a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php index 0ee4229960..5a6ac44727 100644 --- a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php +++ b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php @@ -113,7 +113,7 @@ class CorrectOpeningBalanceCurrencies extends Command $transactions = $journal->transactions()->get(); /** @var Transaction $transaction */ foreach ($transactions as $transaction) { - /** @var Account $account */ + /** @var Account|null $account */ $account = $transaction->account()->first(); if (null !== $account && AccountType::INITIAL_BALANCE !== $account->accountType()->first()->type) { return $account; @@ -133,7 +133,7 @@ class CorrectOpeningBalanceCurrencies extends Command { $currency = $this->getCurrency($account); $count = 0; - if ((int)$journal->transaction_currency_id !== (int)$currency->id) { + if ((int)$journal->transaction_currency_id !== $currency->id) { $journal->transaction_currency_id = $currency->id; $journal->save(); $count = 1; @@ -141,7 +141,7 @@ class CorrectOpeningBalanceCurrencies extends Command /** @var Transaction $transaction */ foreach ($journal->transactions as $transaction) { - if ((int)$transaction->transaction_currency_id !== (int)$currency->id) { + if ($transaction->transaction_currency_id !== $currency->id) { $transaction->transaction_currency_id = $currency->id; $transaction->save(); $count = 1; diff --git a/app/Console/Commands/Correction/CorrectionSkeleton.php.stub b/app/Console/Commands/Correction/CorrectionSkeleton.php.stub index 7d25d8ff5e..801dd0d845 100644 --- a/app/Console/Commands/Correction/CorrectionSkeleton.php.stub +++ b/app/Console/Commands/Correction/CorrectionSkeleton.php.stub @@ -10,17 +10,8 @@ use Illuminate\Console\Command; */ class CorrectionSkeleton extends Command { - /** - * The console command description. - * - * @var string - */ protected $description = 'DESCRIPTION HERE'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:CORR_COMMAND'; /** diff --git a/app/Console/Commands/Correction/CreateAccessTokens.php b/app/Console/Commands/Correction/CreateAccessTokens.php index ab53dc2bcf..ec97a99e17 100644 --- a/app/Console/Commands/Correction/CreateAccessTokens.php +++ b/app/Console/Commands/Correction/CreateAccessTokens.php @@ -36,17 +36,8 @@ class CreateAccessTokens extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ protected $description = 'Creates user access tokens which are used for command line access to personal data.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:create-access-tokens'; /** diff --git a/app/Console/Commands/Correction/CreateLinkTypes.php b/app/Console/Commands/Correction/CreateLinkTypes.php index 43620515eb..cc9700e0d6 100644 --- a/app/Console/Commands/Correction/CreateLinkTypes.php +++ b/app/Console/Commands/Correction/CreateLinkTypes.php @@ -34,17 +34,8 @@ class CreateLinkTypes extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ protected $description = 'Creates all link types.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:create-link-types'; /** diff --git a/app/Console/Commands/Correction/DeleteEmptyJournals.php b/app/Console/Commands/Correction/DeleteEmptyJournals.php index 3ed726205c..ea6dcd2d7d 100644 --- a/app/Console/Commands/Correction/DeleteEmptyJournals.php +++ b/app/Console/Commands/Correction/DeleteEmptyJournals.php @@ -29,7 +29,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use Illuminate\Console\Command; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\Log; /** * Class DeleteEmptyJournals @@ -38,17 +37,8 @@ class DeleteEmptyJournals extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ protected $description = 'Delete empty and uneven transaction journals.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:delete-empty-journals'; /** @@ -69,9 +59,9 @@ class DeleteEmptyJournals extends Command */ private function deleteUnevenJournals(): void { - $set = Transaction::whereNull('deleted_at') - ->groupBy('transactions.transaction_journal_id') - ->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']); + $set = Transaction::whereNull('deleted_at') + ->groupBy('transactions.transaction_journal_id') + ->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']); // @phpstan-ignore-line $total = 0; /** @var Transaction $row */ foreach ($set as $row) { @@ -79,14 +69,14 @@ class DeleteEmptyJournals extends Command if (1 === $count % 2) { // uneven number, delete journal and transactions: try { - TransactionJournal::find((int)$row->transaction_journal_id)->delete(); + TransactionJournal::find($row->transaction_journal_id)->delete(); } catch (QueryException $e) { - Log::info(sprintf('Could not delete journal: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->info(sprintf('Could not delete journal: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); } - Transaction::where('transaction_journal_id', (int)$row->transaction_journal_id)->delete(); + Transaction::where('transaction_journal_id', $row->transaction_journal_id)->delete(); $this->friendlyWarning( sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id) ); @@ -113,8 +103,8 @@ class DeleteEmptyJournals extends Command try { TransactionJournal::find($entry->id)->delete(); } catch (QueryException $e) { - Log::info(sprintf('Could not delete entry: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->info(sprintf('Could not delete entry: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); } diff --git a/app/Console/Commands/Correction/DeleteOrphanedTransactions.php b/app/Console/Commands/Correction/DeleteOrphanedTransactions.php index 802609decd..bc26535586 100644 --- a/app/Console/Commands/Correction/DeleteOrphanedTransactions.php +++ b/app/Console/Commands/Correction/DeleteOrphanedTransactions.php @@ -37,17 +37,8 @@ class DeleteOrphanedTransactions extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ protected $description = 'Deletes orphaned transactions.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:delete-orphaned-transactions'; /** @@ -81,7 +72,7 @@ class DeleteOrphanedTransactions extends Command } $this->friendlyInfo(sprintf('Found %d orphaned journal(s).', $count)); foreach ($set as $entry) { - $journal = TransactionJournal::withTrashed()->find((int)$entry->id); + $journal = TransactionJournal::withTrashed()->find($entry->id); if (null !== $journal) { $journal->delete(); $this->friendlyWarning( @@ -144,11 +135,11 @@ class DeleteOrphanedTransactions extends Command /** @var Transaction $transaction */ foreach ($set as $transaction) { // delete journals - $journal = TransactionJournal::find((int)$transaction->transaction_journal_id); - if ($journal) { + $journal = TransactionJournal::find($transaction->transaction_journal_id); + if (null !== $journal) { $journal->delete(); } - Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete(); + Transaction::where('transaction_journal_id', $transaction->transaction_journal_id)->delete(); $this->friendlyWarning( sprintf( 'Deleted transaction journal #%d because account #%d was already deleted.', diff --git a/app/Console/Commands/Correction/DeleteZeroAmount.php b/app/Console/Commands/Correction/DeleteZeroAmount.php index f0e52cdccf..82774b1dad 100644 --- a/app/Console/Commands/Correction/DeleteZeroAmount.php +++ b/app/Console/Commands/Correction/DeleteZeroAmount.php @@ -35,17 +35,8 @@ class DeleteZeroAmount extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ protected $description = 'Delete transactions with zero amount.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:delete-zero-amount'; /** diff --git a/app/Console/Commands/Correction/EnableCurrencies.php b/app/Console/Commands/Correction/EnableCurrencies.php index f2b2e094b5..e1844b49be 100644 --- a/app/Console/Commands/Correction/EnableCurrencies.php +++ b/app/Console/Commands/Correction/EnableCurrencies.php @@ -73,14 +73,14 @@ class EnableCurrencies extends Command // get all from budget limits $limits = BudgetLimit::groupBy('transaction_currency_id')->get(['transaction_currency_id']); foreach ($limits as $entry) { - $found[] = (int)$entry->transaction_currency_id; + $found[] = $entry->transaction_currency_id; } $found = array_values(array_unique($found)); $found = array_values( array_filter( $found, - function (int $currencyId) { + static function (int $currencyId) { return $currencyId !== 0; } ) diff --git a/app/Console/Commands/Correction/FixAccountTypes.php b/app/Console/Commands/Correction/FixAccountTypes.php index a9d74e3d11..b7bb806317 100644 --- a/app/Console/Commands/Correction/FixAccountTypes.php +++ b/app/Console/Commands/Correction/FixAccountTypes.php @@ -81,11 +81,11 @@ class FixAccountTypes extends Command ->leftJoin('account_types as destination_account_type', 'destination_account.account_type_id', '=', 'destination_account_type.id'); // list all valid combinations, those are allowed. So we select those which are broken. - $query->where(function (Builder $q) use ($expected) { + $query->where(static function (Builder $q) use ($expected) { foreach ($expected as $transactionType => $info) { foreach ($info as $source => $destinations) { foreach ($destinations as $destination) { - $q->whereNot(function (Builder $q1) use ($transactionType, $source, $destination) { + $q->whereNot(static function (Builder $q1) use ($transactionType, $source, $destination) { $q1->where('transaction_types.type', $transactionType); $q1->where('source_account_type.type', $source); $q1->where('destination_account_type.type', $destination); @@ -114,7 +114,7 @@ class FixAccountTypes extends Command $this->friendlyLine(sprintf('Found %d journals that need to be fixed.', $resultSet->count())); foreach ($resultSet as $entry) { app('log')->debug(sprintf('Now fixing journal #%d', $entry->id)); - $journal = TransactionJournal::find((int)$entry->id); + $journal = TransactionJournal::find($entry->id); if (null !== $journal) { $this->inspectJournal($journal); } diff --git a/app/Console/Commands/Correction/FixFrontpageAccounts.php b/app/Console/Commands/Correction/FixFrontpageAccounts.php index 884b4d43e1..e77524e720 100644 --- a/app/Console/Commands/Correction/FixFrontpageAccounts.php +++ b/app/Console/Commands/Correction/FixFrontpageAccounts.php @@ -28,7 +28,6 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\AccountType; use FireflyIII\Models\Preference; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Support\Facades\Preferences; use FireflyIII\User; use Illuminate\Console\Command; @@ -52,7 +51,7 @@ class FixFrontpageAccounts extends Command $users = User::get(); /** @var User $user */ foreach ($users as $user) { - $preference = Preferences::getForUser($user, 'frontPageAccounts'); + $preference = app('preferences')->getForUser($user, 'frontPageAccounts'); if (null !== $preference) { $this->fixPreference($preference); } @@ -87,6 +86,6 @@ class FixFrontpageAccounts extends Command } } } - Preferences::setForUser($preference->user, 'frontPageAccounts', $fixed); + app('preferences')->setForUser($preference->user, 'frontPageAccounts', $fixed); } } diff --git a/app/Console/Commands/Correction/FixGroupAccounts.php b/app/Console/Commands/Correction/FixGroupAccounts.php index 9ddb1cddf6..9575de2807 100644 --- a/app/Console/Commands/Correction/FixGroupAccounts.php +++ b/app/Console/Commands/Correction/FixGroupAccounts.php @@ -51,7 +51,7 @@ class FixGroupAccounts extends Command { $groups = []; $res = TransactionJournal::groupBy('transaction_group_id') - ->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]); + ->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);// @phpstan-ignore-line /** @var TransactionJournal $journal */ foreach ($res as $journal) { if ((int)$journal->the_count > 1) { diff --git a/app/Console/Commands/Correction/FixIbans.php b/app/Console/Commands/Correction/FixIbans.php index 7afbd3c2cf..0717441a5a 100644 --- a/app/Console/Commands/Correction/FixIbans.php +++ b/app/Console/Commands/Correction/FixIbans.php @@ -90,8 +90,8 @@ class FixIbans extends Command $set = []; /** @var Account $account */ foreach ($accounts as $account) { - $userId = (int)$account->user_id; - $set[$userId] = $set[$userId] ?? []; + $userId = $account->user_id; + $set[$userId] ??= []; $iban = (string)$account->iban; if ('' === $iban) { continue; diff --git a/app/Console/Commands/Correction/FixLongDescriptions.php b/app/Console/Commands/Correction/FixLongDescriptions.php index d9a2a04144..fe96827961 100644 --- a/app/Console/Commands/Correction/FixLongDescriptions.php +++ b/app/Console/Commands/Correction/FixLongDescriptions.php @@ -36,7 +36,7 @@ class FixLongDescriptions extends Command { use ShowsFriendlyMessages; - private const MAX_LENGTH = 1000; + private const int MAX_LENGTH = 1000; protected $description = 'Fixes long descriptions in journals and groups.'; protected $signature = 'firefly-iii:fix-long-descriptions'; diff --git a/app/Console/Commands/Correction/FixTransactionTypes.php b/app/Console/Commands/Correction/FixTransactionTypes.php index 9fbd8a42ce..c6d083f3a9 100644 --- a/app/Console/Commands/Correction/FixTransactionTypes.php +++ b/app/Console/Commands/Correction/FixTransactionTypes.php @@ -60,7 +60,7 @@ class FixTransactionTypes extends Command } } if ($count > 0) { - $this->friendlyInfo('Corrected transaction type of %d transaction journals.', $count); + $this->friendlyInfo(sprintf('Corrected transaction type of %d transaction journals.', $count)); return 0; } diff --git a/app/Console/Commands/Correction/FixUnevenAmount.php b/app/Console/Commands/Correction/FixUnevenAmount.php index 9b09734392..fb532f4ca3 100644 --- a/app/Console/Commands/Correction/FixUnevenAmount.php +++ b/app/Console/Commands/Correction/FixUnevenAmount.php @@ -28,7 +28,9 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Log; use stdClass; +use ValueError; /** * Class FixUnevenAmount @@ -55,7 +57,10 @@ class FixUnevenAmount extends Command /** @var stdClass $entry */ foreach ($journals as $entry) { $sum = (string)$entry->the_sum; - if (!is_numeric($sum) || '' === $sum || str_contains($sum, 'e') || str_contains($sum, ',')) { + if (!is_numeric($sum) || + '' === $sum || // @phpstan-ignore-line + str_contains($sum, 'e') || + str_contains($sum, ',')) { $message = sprintf( 'Journal #%d has an invalid sum ("%s"). No sure what to do.', $entry->transaction_journal_id, @@ -66,7 +71,15 @@ class FixUnevenAmount extends Command $count++; continue; } - if (0 !== bccomp((string)$entry->the_sum, '0')) { + $res = -1; + try { + $res = bccomp($sum, '0'); + } catch (ValueError $e) { + $this->friendlyError(sprintf('Could not bccomp("%s", "0").', $sum)); + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + } + if (0 !== $res) { $message = sprintf( 'Sum of journal #%d is %s instead of zero.', $entry->transaction_journal_id, @@ -74,7 +87,7 @@ class FixUnevenAmount extends Command ); $this->friendlyWarning($message); app('log')->warning($message); - $this->fixJournal((int)$entry->transaction_journal_id); + $this->fixJournal($entry->transaction_journal_id); $count++; } } @@ -92,7 +105,7 @@ class FixUnevenAmount extends Command { // one of the transactions is bad. $journal = TransactionJournal::find($param); - if (!$journal) { + if (null === $journal) { return; } /** @var Transaction|null $source */ @@ -107,12 +120,12 @@ class FixUnevenAmount extends Command ) ); Transaction::where('transaction_journal_id', $journal->id ?? 0)->forceDelete(); - TransactionJournal::where('id', $journal->description ?? 0)->forceDelete(); + TransactionJournal::where('id', $journal->id ?? 0)->forceDelete(); return; } - $amount = bcmul('-1', (string)$source->amount); + $amount = bcmul('-1', $source->amount); // fix amount of destination: /** @var Transaction|null $destination */ @@ -128,7 +141,7 @@ class FixUnevenAmount extends Command ); Transaction::where('transaction_journal_id', $journal->id ?? 0)->forceDelete(); - TransactionJournal::where('id', $journal->description ?? 0)->forceDelete(); + TransactionJournal::where('id', $journal->id ?? 0)->forceDelete(); return; } diff --git a/app/Console/Commands/Correction/RenameMetaFields.php b/app/Console/Commands/Correction/RenameMetaFields.php index 1005114e61..c2e1edd1bc 100644 --- a/app/Console/Commands/Correction/RenameMetaFields.php +++ b/app/Console/Commands/Correction/RenameMetaFields.php @@ -37,7 +37,7 @@ class RenameMetaFields extends Command protected $description = 'Rename changed meta fields.'; protected $signature = 'firefly-iii:rename-meta-fields'; - private int $count; + private int $count = 0; /** * Execute the console command. @@ -46,8 +46,6 @@ class RenameMetaFields extends Command */ public function handle(): int { - $this->count = 0; - $changes = [ 'original-source' => 'original_source', 'importHash' => 'import_hash', diff --git a/app/Console/Commands/Correction/TransferBudgets.php b/app/Console/Commands/Correction/TransferBudgets.php index 12372a57ef..caf07f9b1c 100644 --- a/app/Console/Commands/Correction/TransferBudgets.php +++ b/app/Console/Commands/Correction/TransferBudgets.php @@ -27,7 +27,6 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Log; /** * Class TransferBudgets @@ -56,7 +55,7 @@ class TransferBudgets extends Command foreach ($set as $entry) { $message = sprintf('Transaction journal #%d is a %s, so has no longer a budget.', $entry->id, $entry->transactionType->type); $this->friendlyInfo($message); - Log::debug($message); + app('log')->debug($message); $entry->budgets()->sync([]); $count++; } @@ -66,7 +65,7 @@ class TransferBudgets extends Command } if (0 !== $count) { $message = sprintf('Corrected %d invalid budget/journal entries (entry).', $count); - Log::debug($message); + app('log')->debug($message); $this->friendlyInfo($message); } return 0; diff --git a/app/Console/Commands/Export/ExportData.php b/app/Console/Commands/Export/ExportData.php index d3c03f1e8c..8589dcb336 100644 --- a/app/Console/Commands/Export/ExportData.php +++ b/app/Console/Commands/Export/ExportData.php @@ -36,7 +36,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Export\ExportDataGenerator; use Illuminate\Console\Command; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use InvalidArgumentException; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -49,17 +48,9 @@ class ExportData extends Command use ShowsFriendlyMessages; use VerifiesAccessToken; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Command to export data from Firefly III.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:export-data {--user=1 : The user ID that the export should run for.} {--token= : The user\'s access token.} @@ -199,17 +190,26 @@ class ExportData extends Command { $date = today(config('app.timezone'))->subYear(); $error = false; - if (null !== $this->option($field)) { + + if (!in_array($field, ['start', 'end'], true)) { + throw new FireflyException(sprintf('Invalid field "%s" given, can only be "start" or "end".', $field)); + } + + if (is_string($this->option($field))) { try { $date = Carbon::createFromFormat('!Y-m-d', $this->option($field)); } catch (InvalidArgumentException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); $this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start'))); $error = true; } + if (false === $date) { + $this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD.', $field, $this->option('start'))); + throw new FireflyException(sprintf('%s date "%s" must be formatted YYYY-MM-DD.', $field, $this->option('start'))); + } } if (null === $this->option($field)) { - Log::info(sprintf('No date given in field "%s"', $field)); + app('log')->info(sprintf('No date given in field "%s"', $field)); $error = true; } @@ -217,12 +217,15 @@ class ExportData extends Command $journal = $this->journalRepository->firstNull(); $date = null === $journal ? today(config('app.timezone'))->subYear() : $journal->date; $date->startOfDay(); + return $date; } - - if (true === $error && 'end' === $field) { + // field can only be 'end' at this point, so no need to include it in the check. + if (true === $error) { $date = today(config('app.timezone')); $date->endOfDay(); + return $date; } + if ('end' === $field) { $date->endOfDay(); } @@ -238,13 +241,13 @@ class ExportData extends Command { $final = new Collection(); $accounts = new Collection(); - $accountList = $this->option('accounts'); + $accountList = (string)$this->option('accounts'); $types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; - if (null !== $accountList && '' !== (string)$accountList) { + if ('' !== $accountList) { $accountIds = explode(',', $accountList); $accounts = $this->accountRepository->getAccountsById($accountIds); } - if (null === $accountList) { + if ('' === $accountList) { $accounts = $this->accountRepository->getAccountsByType($types); } // filter accounts, @@ -269,7 +272,7 @@ class ExportData extends Command private function getExportDirectory(): string { $directory = (string)$this->option('export_directory'); - if (null === $directory) { + if ('' === $directory) { $directory = './'; } if (!is_writable($directory)) { diff --git a/app/Console/Commands/Integrity/CreateGroupMemberships.php b/app/Console/Commands/Integrity/CreateGroupMemberships.php index 5351c496f9..5f9f3d0bc5 100644 --- a/app/Console/Commands/Integrity/CreateGroupMemberships.php +++ b/app/Console/Commands/Integrity/CreateGroupMemberships.php @@ -40,7 +40,7 @@ class CreateGroupMemberships extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '560_create_group_memberships'; + public const string CONFIG_NAME = '560_create_group_memberships'; protected $description = 'Update group memberships'; protected $signature = 'firefly-iii:create-group-memberships'; diff --git a/app/Console/Commands/Integrity/ReportEmptyObjects.php b/app/Console/Commands/Integrity/ReportEmptyObjects.php index 2152694ed8..b9a396cbe0 100644 --- a/app/Console/Commands/Integrity/ReportEmptyObjects.php +++ b/app/Console/Commands/Integrity/ReportEmptyObjects.php @@ -38,17 +38,9 @@ class ReportEmptyObjects extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Reports on empty database objects.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:report-empty-objects'; /** diff --git a/app/Console/Commands/Integrity/ReportIntegrity.php b/app/Console/Commands/Integrity/ReportIntegrity.php index c90224fd22..4ace551c88 100644 --- a/app/Console/Commands/Integrity/ReportIntegrity.php +++ b/app/Console/Commands/Integrity/ReportIntegrity.php @@ -36,17 +36,9 @@ class ReportIntegrity extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Will report on the integrity of your database.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:report-integrity'; /** diff --git a/app/Console/Commands/Integrity/ReportSkeleton.php.stub b/app/Console/Commands/Integrity/ReportSkeleton.php.stub index bf37df022f..fa771b24c9 100644 --- a/app/Console/Commands/Integrity/ReportSkeleton.php.stub +++ b/app/Console/Commands/Integrity/ReportSkeleton.php.stub @@ -10,17 +10,9 @@ use Illuminate\Console\Command; */ class ReportSkeleton extends Command { - /** - * The console command description. - * - * @var string - */ + protected $description = 'DESCRIPTION HERE'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:INT_COMMAND'; /** diff --git a/app/Console/Commands/System/CreateDatabase.php b/app/Console/Commands/System/CreateDatabase.php index 5960f59144..3f80f6637c 100644 --- a/app/Console/Commands/System/CreateDatabase.php +++ b/app/Console/Commands/System/CreateDatabase.php @@ -36,17 +36,9 @@ class CreateDatabase extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Tries to create the database if it doesn\'t exist yet.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:create-database'; /** @@ -62,10 +54,8 @@ class CreateDatabase extends Command return 0; } // try to set up a raw connection: - $pdo = false; - $exists = false; - $checked = false; // checked for existence of DB? - $dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST', 'localhost'), env('DB_PORT', '3306')); + $exists = false; + $dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST', 'localhost'), env('DB_PORT', '3306')); if ('' !== env('DB_SOCKET', '')) { $dsn = sprintf('mysql:unix_socket=%s;charset=utf8mb4', env('DB_SOCKET', '')); @@ -87,26 +77,24 @@ class CreateDatabase extends Command } // only continue when no error. - if (false !== $pdo) { - // with PDO, try to list DB's ( - $stmt = $pdo->query('SHOW DATABASES;'); - $checked = true; - // slightly more complex but less error prone. - foreach ($stmt as $row) { - $name = $row['Database'] ?? false; - if ($name === env('DB_DATABASE')) { - $exists = true; - } + // with PDO, try to list DB's ( + /** @var array $stmt */ + $stmt = $pdo->query('SHOW DATABASES;'); + // slightly more complex but less error-prone. + foreach ($stmt as $row) { + $name = $row['Database'] ?? false; + if ($name === env('DB_DATABASE')) { + $exists = true; } } - if (false === $exists && true === $checked) { + if (false === $exists) { $this->friendlyError(sprintf('Database "%s" does not exist.', env('DB_DATABASE'))); // try to create it. $pdo->exec(sprintf('CREATE DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;', env('DB_DATABASE'))); $this->friendlyInfo(sprintf('Created database "%s"', env('DB_DATABASE'))); } - if (true === $exists && true === $checked) { + if (true === $exists) { $this->friendlyInfo(sprintf('Database "%s" exists.', env('DB_DATABASE'))); } diff --git a/app/Console/Commands/System/CreateFirstUser.php b/app/Console/Commands/System/CreateFirstUser.php index a547a3e47a..0cdc5370e6 100644 --- a/app/Console/Commands/System/CreateFirstUser.php +++ b/app/Console/Commands/System/CreateFirstUser.php @@ -39,17 +39,9 @@ class CreateFirstUser extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Creates a new user and gives admin rights. Outputs the password on the command line. Strictly for testing.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:create-first-user {email}'; private UserRepositoryInterface $repository; diff --git a/app/Console/Commands/System/ForceDecimalSize.php b/app/Console/Commands/System/ForceDecimalSize.php index 70b2e7384d..08e86cc706 100644 --- a/app/Console/Commands/System/ForceDecimalSize.php +++ b/app/Console/Commands/System/ForceDecimalSize.php @@ -41,7 +41,6 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; /** * Class ForceDecimalSize @@ -95,7 +94,7 @@ class ForceDecimalSize extends Command */ public function handle(): int { - Log::debug('Now in ForceDecimalSize::handle()'); + app('log')->debug('Now in ForceDecimalSize::handle()'); $this->determineDatabaseType(); $this->friendlyError('Running this command is dangerous and can cause data loss.'); @@ -137,7 +136,7 @@ class ForceDecimalSize extends Command { // if sqlite, add function? if ('sqlite' === (string)config('database.default')) { - DB::connection()->getPdo()->sqliteCreateFunction('REGEXP', function ($pattern, $value) { + DB::connection()->getPdo()->sqliteCreateFunction('REGEXP', static function ($pattern, $value) { mb_regex_encoding('UTF-8'); $pattern = trim($pattern, '"'); @@ -184,7 +183,7 @@ class ForceDecimalSize extends Command * @var array $fields */ foreach ($this->tables as $name => $fields) { - switch ($name) { + switch ($name) { // @phpstan-ignore-line default: $message = sprintf('Cannot handle table "%s"', $name); $this->friendlyError($message); @@ -240,7 +239,7 @@ class ForceDecimalSize extends Command $query->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) { foreach ($fields as $field) { $q->orWhere( - DB::raw(sprintf('CAST(accounts.%s AS %s)', $field, $cast)), + DB::raw(sprintf('CAST(accounts.%s AS %s)', $field, $cast)), // @phpstan-ignore-line $operator, DB::raw(sprintf($regularExpression, $currency->decimal_places)) ); @@ -254,13 +253,14 @@ class ForceDecimalSize extends Command } /** @var Account $account */ foreach ($result as $account) { + /** @var string $field */ foreach ($fields as $field) { $value = $account->$field; if (null === $value) { continue; } // fix $field by rounding it down correctly. - $pow = pow(10, (int)$currency->decimal_places); + $pow = 10 ** $currency->decimal_places; $correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $this->friendlyInfo(sprintf('Account #%d has %s with value "%s", this has been corrected to "%s".', $account->id, $field, $value, $correct)); Account::find($account->id)->update([$field => $correct]); @@ -287,9 +287,10 @@ class ForceDecimalSize extends Command /** @var Builder $query */ $query = $class::where('transaction_currency_id', $currency->id)->where( static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) { + /** @var string $field */ foreach ($fields as $field) { $q->orWhere( - DB::raw(sprintf('CAST(%s AS %s)', $field, $cast)), + DB::raw(sprintf('CAST(%s AS %s)', $field, $cast)), // @phpstan-ignore-line $operator, DB::raw(sprintf($regularExpression, $currency->decimal_places)) ); @@ -305,13 +306,14 @@ class ForceDecimalSize extends Command } /** @var Model $item */ foreach ($result as $item) { + /** @var string $field */ foreach ($fields as $field) { $value = $item->$field; if (null === $value) { continue; } // fix $field by rounding it down correctly. - $pow = pow(10, (int)$currency->decimal_places); + $pow = 10 ** $currency->decimal_places; $correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $this->friendlyWarning(sprintf('%s #%d has %s with value "%s", this has been corrected to "%s".', $table, $item->id, $field, $value, $correct)); $class::find($item->id)->update([$field => $correct]); @@ -342,7 +344,7 @@ class ForceDecimalSize extends Command ->where(static function (Builder $q) use ($fields, $currency, $cast, $operator, $regularExpression) { foreach ($fields as $field) { $q->orWhere( - DB::raw(sprintf('CAST(piggy_bank_events.%s AS %s)', $field, $cast)), + DB::raw(sprintf('CAST(piggy_bank_events.%s AS %s)', $field, $cast)), // @phpstan-ignore-line $operator, DB::raw(sprintf($regularExpression, $currency->decimal_places)) ); @@ -357,13 +359,14 @@ class ForceDecimalSize extends Command } /** @var PiggyBankEvent $item */ foreach ($result as $item) { + /** @var string $field */ foreach ($fields as $field) { $value = $item->$field; if (null === $value) { continue; } // fix $field by rounding it down correctly. - $pow = pow(10, (int)$currency->decimal_places); + $pow = 10 ** $currency->decimal_places; $correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $this->friendlyWarning( sprintf('Piggy bank event #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct) @@ -396,7 +399,7 @@ class ForceDecimalSize extends Command ->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) { foreach ($fields as $field) { $q->orWhere( - DB::raw(sprintf('CAST(piggy_bank_repetitions.%s AS %s)', $field, $cast)), + DB::raw(sprintf('CAST(piggy_bank_repetitions.%s AS %s)', $field, $cast)), // @phpstan-ignore-line $operator, DB::raw(sprintf($regularExpression, $currency->decimal_places)) ); @@ -411,13 +414,14 @@ class ForceDecimalSize extends Command } /** @var PiggyBankRepetition $item */ foreach ($result as $item) { + /** @var string $field */ foreach ($fields as $field) { $value = $item->$field; if (null === $value) { continue; } // fix $field by rounding it down correctly. - $pow = pow(10, (int)$currency->decimal_places); + $pow = 10 ** $currency->decimal_places; $correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $this->friendlyWarning( sprintf('Piggy bank repetition #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct) @@ -449,7 +453,7 @@ class ForceDecimalSize extends Command ->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) { foreach ($fields as $field) { $q->orWhere( - DB::raw(sprintf('CAST(piggy_banks.%s AS %s)', $field, $cast)), + DB::raw(sprintf('CAST(piggy_banks.%s AS %s)', $field, $cast)), // @phpstan-ignore-line $operator, DB::raw(sprintf($regularExpression, $currency->decimal_places)) ); @@ -464,13 +468,14 @@ class ForceDecimalSize extends Command } /** @var PiggyBank $item */ foreach ($result as $item) { + /** @var string $field */ foreach ($fields as $field) { $value = $item->$field; if (null === $value) { continue; } // fix $field by rounding it down correctly. - $pow = pow(10, (int)$currency->decimal_places); + $pow = 10 ** $currency->decimal_places; $correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $this->friendlyWarning(sprintf('Piggy bank #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct)); PiggyBank::find($item->id)->update([$field => $correct]); @@ -490,7 +495,7 @@ class ForceDecimalSize extends Command // select all transactions with this currency and issue. /** @var Builder $query */ $query = Transaction::where('transaction_currency_id', $currency->id)->where( - DB::raw(sprintf('CAST(amount as %s)', $this->cast)), + DB::raw(sprintf('CAST(amount as %s)', $this->cast)), // @phpstan-ignore-line $this->operator, DB::raw(sprintf($this->regularExpression, $currency->decimal_places)) ); @@ -503,12 +508,12 @@ class ForceDecimalSize extends Command /** @var Transaction $item */ foreach ($result as $item) { $value = $item->amount; - if (null === $value) { + if ('' === $value) { continue; } // fix $field by rounding it down correctly. - $pow = pow(10, (int)$currency->decimal_places); - $correct = bcdiv((string)round($value * $pow), (string)$pow, 12); + $pow = (float)10 ** $currency->decimal_places; + $correct = bcdiv((string)round((float)$value * $pow), (string)$pow, 12); $this->friendlyWarning(sprintf('Transaction #%d has amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct)); Transaction::find($item->id)->update(['amount' => $correct]); } @@ -516,7 +521,7 @@ class ForceDecimalSize extends Command // select all transactions with this FOREIGN currency and issue. /** @var Builder $query */ $query = Transaction::where('foreign_currency_id', $currency->id)->where( - DB::raw(sprintf('CAST(foreign_amount as %s)', $this->cast)), + DB::raw(sprintf('CAST(foreign_amount as %s)', $this->cast)), // @phpstan-ignore-line $this->operator, DB::raw(sprintf($this->regularExpression, $currency->decimal_places)) ); @@ -534,8 +539,8 @@ class ForceDecimalSize extends Command continue; } // fix $field by rounding it down correctly. - $pow = pow(10, (int)$currency->decimal_places); - $correct = bcdiv((string)round($value * $pow), (string)$pow, 12); + $pow = (float)10 ** $currency->decimal_places; + $correct = bcdiv((string)round((float)$value * $pow), (string)$pow, 12); $this->friendlyWarning( sprintf('Transaction #%d has foreign amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct) ); @@ -559,22 +564,18 @@ class ForceDecimalSize extends Command /** @var string $field */ foreach ($fields as $field) { $this->friendlyLine(sprintf('Updating table "%s", field "%s"...', $name, $field)); - - switch ($type) { - default: - $this->friendlyError(sprintf('Cannot handle database type "%s".', $type)); - - return; - case 'pgsql': - $query = sprintf('ALTER TABLE %s ALTER COLUMN %s TYPE DECIMAL(32,12);', $name, $field); - break; - case 'mysql': - $query = sprintf('ALTER TABLE %s CHANGE COLUMN %s %s DECIMAL(32, 12);', $name, $field, $field); - break; + if ('pgsql' === $type) { + DB::select(sprintf('ALTER TABLE %s ALTER COLUMN %s TYPE DECIMAL(32,12);', $name, $field)); + sleep(1); + return; } + if ('mysql' === $type) { + DB::select(sprintf('ALTER TABLE %s CHANGE COLUMN %s %s DECIMAL(32, 12);', $name, $field, $field)); + sleep(1); + return; + } + $this->friendlyError(sprintf('Cannot handle database type "%s".', $type)); - DB::select($query); - sleep(1); } } } diff --git a/app/Console/Commands/System/ForceMigration.php b/app/Console/Commands/System/ForceMigration.php index f437fa9482..bd2df4e03a 100644 --- a/app/Console/Commands/System/ForceMigration.php +++ b/app/Console/Commands/System/ForceMigration.php @@ -40,17 +40,9 @@ class ForceMigration extends Command use ShowsFriendlyMessages; use VerifiesAccessToken; - /** - * The console command description. - * - * @var string - */ + protected $description = 'This command will force-run all database migrations.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:force-migrations {--user=1 : The user ID.} {--token= : The user\'s access token.}'; diff --git a/app/Console/Commands/System/OutputVersion.php b/app/Console/Commands/System/OutputVersion.php index eea501b031..e11923746c 100644 --- a/app/Console/Commands/System/OutputVersion.php +++ b/app/Console/Commands/System/OutputVersion.php @@ -32,17 +32,9 @@ use Illuminate\Console\Command; */ class OutputVersion extends Command { - /** - * The console command description. - * - * @var string - */ + protected $description = 'Outputs the Firefly III version'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:output-version'; /** diff --git a/app/Console/Commands/System/ScanAttachments.php b/app/Console/Commands/System/ScanAttachments.php index 7e2ab68231..a823c08d48 100644 --- a/app/Console/Commands/System/ScanAttachments.php +++ b/app/Console/Commands/System/ScanAttachments.php @@ -28,7 +28,6 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Attachment; use Illuminate\Console\Command; use Illuminate\Contracts\Encryption\DecryptException; -use Illuminate\Support\Facades\Log; use Storage; /** @@ -40,18 +39,10 @@ class ScanAttachments extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Rescan all attachments and re-set the correct MD5 hash and mime.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:scan-attachments'; /** @@ -66,21 +57,23 @@ class ScanAttachments extends Command $fileName = $attachment->fileName(); $encryptedContent = $disk->get($fileName); if (null === $encryptedContent) { - Log::error(sprintf('No content for attachment #%d under filename "%s"', $attachment->id, $fileName)); + app('log')->error(sprintf('No content for attachment #%d under filename "%s"', $attachment->id, $fileName)); continue; } try { $decryptedContent = Crypt::decrypt($encryptedContent); // verified } catch (DecryptException $e) { - Log::error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage())); + app('log')->error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage())); $decryptedContent = $encryptedContent; } $tempFileName = tempnam(sys_get_temp_dir(), 'FireflyIII'); + if (false === $tempFileName) { + app('log')->error(sprintf('Could not create temporary file for attachment #%d', $attachment->id)); + exit(1); + } file_put_contents($tempFileName, $decryptedContent); - $md5 = md5_file($tempFileName); - $mime = mime_content_type($tempFileName); - $attachment->md5 = $md5; - $attachment->mime = $mime; + $attachment->md5 = (string)md5_file($tempFileName); + $attachment->mime = (string)mime_content_type($tempFileName); $attachment->save(); $this->friendlyInfo(sprintf('Fixed attachment #%d', $attachment->id)); } diff --git a/app/Console/Commands/System/SetLatestVersion.php b/app/Console/Commands/System/SetLatestVersion.php index ab510eb394..7ef7d9d953 100644 --- a/app/Console/Commands/System/SetLatestVersion.php +++ b/app/Console/Commands/System/SetLatestVersion.php @@ -34,17 +34,9 @@ class SetLatestVersion extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Set latest version in DB.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:set-latest-version {--james-is-cool}'; /** diff --git a/app/Console/Commands/System/UpgradeFireflyInstructions.php b/app/Console/Commands/System/UpgradeFireflyInstructions.php index 3623a42956..e9fd95b354 100644 --- a/app/Console/Commands/System/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/System/UpgradeFireflyInstructions.php @@ -26,8 +26,6 @@ namespace FireflyIII\Console\Commands\System; use FireflyIII\Support\System\GeneratesInstallationId; use Illuminate\Console\Command; -use function FireflyIII\Console\Commands\str_starts_with; - /** * Class UpgradeFireflyInstructions. * @@ -37,17 +35,9 @@ class UpgradeFireflyInstructions extends Command { use GeneratesInstallationId; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Instructions in case of upgrade trouble.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly:instructions {task}'; /** @@ -56,10 +46,10 @@ class UpgradeFireflyInstructions extends Command public function handle(): int { $this->generateInstallationId(); - if ('update' === (string)$this->argument('task')) { + if ('update' === $this->argument('task')) { $this->updateInstructions(); } - if ('install' === (string)$this->argument('task')) { + if ('install' === $this->argument('task')) { $this->installInstructions(); } @@ -71,14 +61,15 @@ class UpgradeFireflyInstructions extends Command */ private function updateInstructions(): void { - /** @var string $version */ - $version = config('firefly.version'); - $config = config('upgrade.text.upgrade'); - $text = ''; + $version = (string)config('firefly.version'); + /** @var array $config */ + $config = config('upgrade.text.upgrade'); + $text = ''; + /** @var string $compare */ foreach (array_keys($config) as $compare) { // if string starts with: - if (\str_starts_with($version, $compare)) { - $text = $config[$compare]; + if (str_starts_with($version, $compare)) { + $text = (string)$config[$compare]; } } @@ -88,7 +79,7 @@ class UpgradeFireflyInstructions extends Command $this->showLine(); $this->boxed(''); - if (null === $text || '' === $text) { + if ('' === $text) { $this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version)); $this->boxedInfo('There are no extra upgrade instructions.'); $this->boxed('Firefly III should be ready for use.'); @@ -184,14 +175,15 @@ class UpgradeFireflyInstructions extends Command */ private function installInstructions(): void { - /** @var string $version */ - $version = config('firefly.version'); - $config = config('upgrade.text.install'); - $text = ''; + $version = (string)config('firefly.version'); + /** @var array $config */ + $config = config('upgrade.text.install'); + $text = ''; + /** @var string $compare */ foreach (array_keys($config) as $compare) { // if string starts with: - if (\str_starts_with($version, $compare)) { - $text = $config[$compare]; + if (str_starts_with($version, $compare)) { + $text = (string)$config[$compare]; } } $this->newLine(); @@ -199,7 +191,7 @@ class UpgradeFireflyInstructions extends Command $this->newLine(); $this->showLine(); $this->boxed(''); - if (null === $text || '' === $text) { + if ('' === $text) { $this->boxed(sprintf('Thank you for installing Firefly III, v%s!', $version)); $this->boxedInfo('There are no extra installation instructions.'); $this->boxed('Firefly III should be ready for use.'); diff --git a/app/Console/Commands/System/VerifySecurityAlerts.php b/app/Console/Commands/System/VerifySecurityAlerts.php index 3c82bb45bd..dd8c59feac 100644 --- a/app/Console/Commands/System/VerifySecurityAlerts.php +++ b/app/Console/Commands/System/VerifySecurityAlerts.php @@ -27,7 +27,6 @@ namespace FireflyIII\Console\Commands\System; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\Log; use League\Flysystem\FilesystemException; use Storage; @@ -38,17 +37,9 @@ class VerifySecurityAlerts extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Verify security alerts'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:verify-security-alerts'; /** @@ -66,7 +57,7 @@ class VerifySecurityAlerts extends Command $disk = Storage::disk('resources'); // Next line is ignored because it's a Laravel Facade. if (!$disk->has('alerts.json')) { // @phpstan-ignore-line - Log::debug('No alerts.json file present.'); + app('log')->debug('No alerts.json file present.'); return 0; } @@ -76,19 +67,19 @@ class VerifySecurityAlerts extends Command /** @var array $array */ foreach ($json as $array) { if ($version === $array['version'] && true === $array['advisory']) { - Log::debug(sprintf('Version %s has an alert!', $array['version'])); + app('log')->debug(sprintf('Version %s has an alert!', $array['version'])); // add advisory to configuration. $this->saveSecurityAdvisory($array); // depends on level if ('info' === $array['level']) { - Log::debug('INFO level alert'); + app('log')->debug('INFO level alert'); $this->friendlyInfo($array['message']); return 0; } if ('warning' === $array['level']) { - Log::debug('WARNING level alert'); + app('log')->debug('WARNING level alert'); $this->friendlyWarning('------------------------ :o'); $this->friendlyWarning($array['message']); $this->friendlyWarning('------------------------ :o'); @@ -96,7 +87,7 @@ class VerifySecurityAlerts extends Command return 0; } if ('danger' === $array['level']) { - Log::debug('DANGER level alert'); + app('log')->debug('DANGER level alert'); $this->friendlyError('------------------------ :-('); $this->friendlyError($array['message']); $this->friendlyError('------------------------ :-('); @@ -107,7 +98,7 @@ class VerifySecurityAlerts extends Command return 0; } } - Log::debug(sprintf('No security alerts for version %s', $version)); + app('log')->debug(sprintf('No security alerts for version %s', $version)); $this->friendlyPositive(sprintf('No security alerts for version %s', $version)); return 0; } @@ -121,7 +112,7 @@ class VerifySecurityAlerts extends Command app('fireflyconfig')->delete('upgrade_security_message'); app('fireflyconfig')->delete('upgrade_security_level'); } catch (QueryException $e) { - Log::debug(sprintf('Could not delete old security advisory, but thats OK: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not delete old security advisory, but thats OK: %s', $e->getMessage())); } } @@ -136,7 +127,7 @@ class VerifySecurityAlerts extends Command app('fireflyconfig')->set('upgrade_security_message', $array['message']); app('fireflyconfig')->set('upgrade_security_level', $array['level']); } catch (QueryException $e) { - Log::debug(sprintf('Could not save new security advisory, but thats OK: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not save new security advisory, but thats OK: %s', $e->getMessage())); } } } diff --git a/app/Console/Commands/Tools/ApplyRules.php b/app/Console/Commands/Tools/ApplyRules.php index 97b8b526b1..eaa50b94bd 100644 --- a/app/Console/Commands/Tools/ApplyRules.php +++ b/app/Console/Commands/Tools/ApplyRules.php @@ -47,17 +47,9 @@ class ApplyRules extends Command use ShowsFriendlyMessages; use VerifiesAccessToken; - /** - * The console command description. - * - * @var string - */ + protected $description = 'This command will apply your rules and rule groups on a selection of your transactions.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:apply-rules {--user=1 : The user ID.} @@ -305,6 +297,10 @@ class ApplyRules extends Command if (null !== $endString && '' !== $endString) { $inputEnd = Carbon::createFromFormat('Y-m-d', $endString); } + if (false === $inputEnd || false === $inputStart) { + Log::error('Could not parse start or end date in verifyInputDate().'); + return; + } if ($inputStart > $inputEnd) { [$inputEnd, $inputStart] = [$inputStart, $inputEnd]; @@ -335,7 +331,7 @@ class ApplyRules extends Command // if in rule selection, or group in selection or all rules, it's included. $test = $this->includeRule($rule, $group); if (true === $test) { - Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title)); + app('log')->debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title)); $rulesToApply->push($rule); } } diff --git a/app/Console/Commands/Tools/Cron.php b/app/Console/Commands/Tools/Cron.php index bf9d13ada0..a7b2a7920e 100644 --- a/app/Console/Commands/Tools/Cron.php +++ b/app/Console/Commands/Tools/Cron.php @@ -32,7 +32,6 @@ use FireflyIII\Support\Cronjobs\BillWarningCronjob; use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob; use FireflyIII\Support\Cronjobs\RecurringCronjob; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Log; use InvalidArgumentException; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -46,17 +45,9 @@ class Cron extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Runs all Firefly III cron-job related commands. Configure a cron job according to the official Firefly III documentation.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:cron {--F|force : Force the cron job(s) to execute.} {--date= : Set the date in YYYY-MM-DD to make Firefly III think that\'s the current date.} @@ -75,7 +66,7 @@ class Cron extends Command } catch (InvalidArgumentException $e) { $this->friendlyError(sprintf('"%s" is not a valid date', $this->option('date'))); } - $force = (bool)$this->option('force'); + $force = (bool)$this->option('force'); // @phpstan-ignore-line /* * Fire exchange rates cron job. @@ -84,8 +75,8 @@ class Cron extends Command try { $this->exchangeRatesCronJob($force, $date); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } } @@ -96,8 +87,8 @@ class Cron extends Command try { $this->recurringCronJob($force, $date); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } @@ -107,8 +98,8 @@ class Cron extends Command try { $this->autoBudgetCronJob($force, $date); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } @@ -118,8 +109,8 @@ class Cron extends Command try { $this->billWarningCronJob($force, $date); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $this->friendlyError($e->getMessage()); } diff --git a/app/Console/Commands/Upgrade/AccountCurrencies.php b/app/Console/Commands/Upgrade/AccountCurrencies.php index 0a05dbf1d5..37305ff4e8 100644 --- a/app/Console/Commands/Upgrade/AccountCurrencies.php +++ b/app/Console/Commands/Upgrade/AccountCurrencies.php @@ -44,7 +44,7 @@ class AccountCurrencies extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_account_currencies'; + public const string CONFIG_NAME = '480_account_currencies'; protected $description = 'Give all accounts proper currency info.'; protected $signature = 'firefly-iii:account-currencies {--F|force : Force the execution of this command.}'; @@ -109,20 +109,18 @@ class AccountCurrencies extends Command */ private function updateAccountCurrencies(): void { - $users = $this->userRepos->all(); - $defaultCurrencyCode = (string)config('firefly.default_currency', 'EUR'); + $users = $this->userRepos->all(); foreach ($users as $user) { - $this->updateCurrenciesForUser($user, $defaultCurrencyCode); + $this->updateCurrenciesForUser($user); } } /** - * @param User $user - * @param string $systemCurrencyCode + * @param User $user * * @throws FireflyException */ - private function updateCurrenciesForUser(User $user, string $systemCurrencyCode): void + private function updateCurrenciesForUser(User $user): void { $this->accountRepos->setUser($user); $accounts = $this->accountRepos->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); diff --git a/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php b/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php index 12961e4843..83125ac478 100644 --- a/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php +++ b/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php @@ -26,7 +26,6 @@ namespace FireflyIII\Console\Commands\Upgrade; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\BudgetLimit; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -37,18 +36,10 @@ class AppendBudgetLimitPeriods extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '550_budget_limit_periods'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '550_budget_limit_periods'; + protected $description = 'Append budget limits with their (estimated) timeframe.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:budget-limit-periods {--F|force : Force the execution of this command.}'; /** @@ -99,7 +90,7 @@ class AppendBudgetLimitPeriods extends Command /** * @param BudgetLimit $limit */ - private function fixLimit(BudgetLimit $limit) + private function fixLimit(BudgetLimit $limit): void { $period = $this->getLimitPeriod($limit); @@ -125,7 +116,7 @@ class AppendBudgetLimitPeriods extends Command $limit->end_date->format('Y-m-d'), $period ); - Log::debug($msg); + app('log')->debug($msg); } /** diff --git a/app/Console/Commands/Upgrade/BackToJournals.php b/app/Console/Commands/Upgrade/BackToJournals.php index 3f6e2c5ca1..a767b0f732 100644 --- a/app/Console/Commands/Upgrade/BackToJournals.php +++ b/app/Console/Commands/Upgrade/BackToJournals.php @@ -41,18 +41,10 @@ class BackToJournals extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_back_to_journals'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '480_back_to_journals'; + protected $description = 'Move meta data back to journals, not individual transactions.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:back-to-journals {--F|force : Force the execution of this command.}'; /** @@ -177,7 +169,7 @@ class BackToJournals extends Command // both have a budget, but they don't match. if (null !== $budget && null !== $journalBudget && $budget->id !== $journalBudget->id) { // sync to journal: - $journal->budgets()->sync([(int)$budget->id]); + $journal->budgets()->sync([$budget->id]); return; } @@ -185,7 +177,7 @@ class BackToJournals extends Command // transaction has a budget, but the journal doesn't. if (null !== $budget && null === $journalBudget) { // sync to journal: - $journal->budgets()->sync([(int)$budget->id]); + $journal->budgets()->sync([$budget->id]); } } @@ -249,12 +241,12 @@ class BackToJournals extends Command // both have a category, but they don't match. if (null !== $category && null !== $journalCategory && $category->id !== $journalCategory->id) { // sync to journal: - $journal->categories()->sync([(int)$category->id]); + $journal->categories()->sync([$category->id]); } // transaction has a category, but the journal doesn't. if (null !== $category && null === $journalCategory) { - $journal->categories()->sync([(int)$category->id]); + $journal->categories()->sync([$category->id]); } } diff --git a/app/Console/Commands/Upgrade/BudgetLimitCurrency.php b/app/Console/Commands/Upgrade/BudgetLimitCurrency.php index 476b5ff1ac..e0244bcce4 100644 --- a/app/Console/Commands/Upgrade/BudgetLimitCurrency.php +++ b/app/Console/Commands/Upgrade/BudgetLimitCurrency.php @@ -25,7 +25,9 @@ namespace FireflyIII\Console\Commands\Upgrade; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; +use FireflyIII\User; use Illuminate\Console\Command; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -37,18 +39,10 @@ class BudgetLimitCurrency extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_bl_currency'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '480_bl_currency'; + protected $description = 'Give budget limits a currency'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:bl-currency {--F|force : Force the execution of this command.}'; /** @@ -73,8 +67,10 @@ class BudgetLimitCurrency extends Command /** @var BudgetLimit $budgetLimit */ foreach ($budgetLimits as $budgetLimit) { if (null === $budgetLimit->transaction_currency_id) { + /** @var Budget|null $budget */ $budget = $budgetLimit->budget; if (null !== $budget) { + /** @var User|null $user */ $user = $budget->user; if (null !== $user) { $currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); diff --git a/app/Console/Commands/Upgrade/CCLiabilities.php b/app/Console/Commands/Upgrade/CCLiabilities.php index b41f41c9a5..6eb373c67a 100644 --- a/app/Console/Commands/Upgrade/CCLiabilities.php +++ b/app/Console/Commands/Upgrade/CCLiabilities.php @@ -39,7 +39,7 @@ class CCLiabilities extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_cc_liabilities'; + public const string CONFIG_NAME = '480_cc_liabilities'; protected $description = 'Convert old credit card liabilities.'; protected $signature = 'firefly-iii:cc-liabilities {--F|force : Force the execution of this command.}'; diff --git a/app/Console/Commands/Upgrade/DecryptDatabase.php b/app/Console/Commands/Upgrade/DecryptDatabase.php index 0f584f9296..6ce6473b84 100644 --- a/app/Console/Commands/Upgrade/DecryptDatabase.php +++ b/app/Console/Commands/Upgrade/DecryptDatabase.php @@ -31,7 +31,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Preference; use Illuminate\Console\Command; use Illuminate\Contracts\Encryption\DecryptException; -use Illuminate\Support\Facades\Log; use JsonException; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -116,7 +115,7 @@ class DecryptDatabase extends Command try { $configVar = app('fireflyconfig')->get($configName, false); } catch (FireflyException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); } if (null !== $configVar) { return (bool)$configVar->data; @@ -157,8 +156,8 @@ class DecryptDatabase extends Command } catch (FireflyException $e) { $message = sprintf('Could not decrypt field "%s" in row #%d of table "%s": %s', $field, $id, $table, $e->getMessage()); $this->friendlyError($message); - Log::error($message); - Log::error($e->getTraceAsString()); + app('log')->error($message); + app('log')->error($e->getTraceAsString()); } // A separate routine for preferences table: @@ -213,8 +212,8 @@ class DecryptDatabase extends Command return; } - /** @var Preference $object */ - $object = Preference::find((int)$id); + /** @var Preference|null $object */ + $object = Preference::find($id); if (null !== $object) { $object->data = $newValue; $object->save(); diff --git a/app/Console/Commands/Upgrade/FixPostgresSequences.php b/app/Console/Commands/Upgrade/FixPostgresSequences.php index 71399ebd29..520cc36963 100644 --- a/app/Console/Commands/Upgrade/FixPostgresSequences.php +++ b/app/Console/Commands/Upgrade/FixPostgresSequences.php @@ -35,17 +35,9 @@ class FixPostgresSequences extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var string - */ + protected $description = 'Fixes issues with PostgreSQL sequences.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:fix-pgsql-sequences'; /** @@ -128,11 +120,11 @@ class FixPostgresSequences extends Command continue; } - if ($nextId->nextval < $highestId->max) { + if ($nextId->nextval < $highestId->max) { // @phpstan-ignore-line DB::select(sprintf('SELECT setval(\'%s_id_seq\', %d)', $tableToCheck, $highestId->max)); $highestId = DB::table($tableToCheck)->select(DB::raw('MAX(id)'))->first(); $nextId = DB::table($tableToCheck)->select(DB::raw(sprintf('nextval(\'%s_id_seq\')', $tableToCheck)))->first(); - if ($nextId->nextval > $highestId->max) { + if ($nextId->nextval > $highestId->max) { // @phpstan-ignore-line $this->friendlyInfo(sprintf('Table "%s" autoincrement corrected.', $tableToCheck)); } if ($nextId->nextval <= $highestId->max) { diff --git a/app/Console/Commands/Upgrade/MigrateAttachments.php b/app/Console/Commands/Upgrade/MigrateAttachments.php index db0e4dedb0..3d2e638666 100644 --- a/app/Console/Commands/Upgrade/MigrateAttachments.php +++ b/app/Console/Commands/Upgrade/MigrateAttachments.php @@ -28,7 +28,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Attachment; use FireflyIII\Models\Note; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -39,18 +38,10 @@ class MigrateAttachments extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_migrate_attachments'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '480_migrate_attachments'; + protected $description = 'Migrates attachment meta-data.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:migrate-attachments {--F|force : Force the execution of this command.}'; /** @@ -92,7 +83,7 @@ class MigrateAttachments extends Command $att->description = ''; $att->save(); - Log::debug(sprintf('Migrated attachment #%s description to note #%d.', $att->id, $note->id)); + app('log')->debug(sprintf('Migrated attachment #%s description to note #%d.', $att->id, $note->id)); $count++; } } diff --git a/app/Console/Commands/Upgrade/MigrateJournalNotes.php b/app/Console/Commands/Upgrade/MigrateJournalNotes.php index 293ce3738c..1e4fb924ac 100644 --- a/app/Console/Commands/Upgrade/MigrateJournalNotes.php +++ b/app/Console/Commands/Upgrade/MigrateJournalNotes.php @@ -27,7 +27,6 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Note; use FireflyIII\Models\TransactionJournalMeta; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -38,18 +37,10 @@ class MigrateJournalNotes extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_migrate_notes'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '480_migrate_notes'; + protected $description = 'Migrate notes for transaction journals.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:migrate-notes {--F|force : Force the execution of this command.}'; /** @@ -82,7 +73,7 @@ class MigrateJournalNotes extends Command $note->text = $meta->data; $note->save(); - Log::debug(sprintf('Migrated meta note #%d to Note #%d', $meta->id, $note->id)); + app('log')->debug(sprintf('Migrated meta note #%d to Note #%d', $meta->id, $note->id)); $meta->delete(); $count++; diff --git a/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php b/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php index 10c1ebd0a5..fda6b8e70b 100644 --- a/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php +++ b/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php @@ -40,18 +40,10 @@ class MigrateRecurrenceMeta extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '481_migrate_recurrence_meta'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '481_migrate_recurrence_meta'; + protected $description = 'Migrate recurrence meta data'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:migrate-recurrence-meta {--F|force : Force the execution of this command.}'; /** diff --git a/app/Console/Commands/Upgrade/MigrateRecurrenceType.php b/app/Console/Commands/Upgrade/MigrateRecurrenceType.php index c6a509495b..c722547621 100644 --- a/app/Console/Commands/Upgrade/MigrateRecurrenceType.php +++ b/app/Console/Commands/Upgrade/MigrateRecurrenceType.php @@ -39,18 +39,10 @@ class MigrateRecurrenceType extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '550_migrate_recurrence_type'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '550_migrate_recurrence_type'; + protected $description = 'Migrate transaction type of recurring transaction.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:migrate-recurrence-type {--F|force : Force the execution of this command.}'; /** @@ -107,7 +99,7 @@ class MigrateRecurrenceType extends Command */ private function migrateRecurrence(Recurrence $recurrence): void { - $originalType = (int)$recurrence->transaction_type_id; + $originalType = $recurrence->transaction_type_id; $newType = $this->getInvalidType(); $recurrence->transaction_type_id = $newType->id; $recurrence->save(); diff --git a/app/Console/Commands/Upgrade/MigrateTagLocations.php b/app/Console/Commands/Upgrade/MigrateTagLocations.php index c2d53c178e..afcb1a60c8 100644 --- a/app/Console/Commands/Upgrade/MigrateTagLocations.php +++ b/app/Console/Commands/Upgrade/MigrateTagLocations.php @@ -38,18 +38,10 @@ class MigrateTagLocations extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '500_migrate_tag_locations'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '500_migrate_tag_locations'; + protected $description = 'Migrate tag locations.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:migrate-tag-locations {--F|force : Force the execution of this command.}'; /** diff --git a/app/Console/Commands/Upgrade/MigrateToGroups.php b/app/Console/Commands/Upgrade/MigrateToGroups.php index 78a364b4a9..8da1102421 100644 --- a/app/Console/Commands/Upgrade/MigrateToGroups.php +++ b/app/Console/Commands/Upgrade/MigrateToGroups.php @@ -36,7 +36,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Services\Internal\Destroy\JournalDestroyService; use Illuminate\Console\Command; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -51,7 +50,7 @@ class MigrateToGroups extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_migrated_to_groups'; + public const string CONFIG_NAME = '480_migrated_to_groups'; protected $description = 'Migrates a pre-4.7.8 transaction structure to the 4.7.8+ transaction structure.'; protected $signature = 'firefly-iii:migrate-to-groups {--F|force : Force the migration, even if it fired before.}'; private JournalCLIRepositoryInterface $cliRepository; @@ -151,11 +150,11 @@ class MigrateToGroups extends Command { // double check transaction count. if ($journal->transactions->count() <= 2) { - Log::debug(sprintf('Will not try to convert journal #%d because it has 2 or less transactions.', $journal->id)); + app('log')->debug(sprintf('Will not try to convert journal #%d because it has 2 or less transactions.', $journal->id)); return; } - Log::debug(sprintf('Will now try to convert journal #%d', $journal->id)); + app('log')->debug(sprintf('Will now try to convert journal #%d', $journal->id)); $this->journalRepository->setUser($journal->user); $this->groupFactory->setUser($journal->user); @@ -193,11 +192,11 @@ class MigrateToGroups extends Command $paymentDate = $this->cliRepository->getMetaDate($journal, 'payment_date'); $invoiceDate = $this->cliRepository->getMetaDate($journal, 'invoice_date'); - Log::debug(sprintf('Will use %d positive transactions to create a new group.', $destTransactions->count())); + app('log')->debug(sprintf('Will use %d positive transactions to create a new group.', $destTransactions->count())); /** @var Transaction $transaction */ foreach ($destTransactions as $transaction) { - Log::debug(sprintf('Now going to add transaction #%d to the array.', $transaction->id)); + app('log')->debug(sprintf('Now going to add transaction #%d to the array.', $transaction->id)); $opposingTr = $this->findOpposingTransaction($journal, $transaction); if (null === $opposingTr) { @@ -256,9 +255,9 @@ class MigrateToGroups extends Command $data['transactions'][] = $tArray; } - Log::debug(sprintf('Now calling transaction journal factory (%d transactions in array)', count($data['transactions']))); + app('log')->debug(sprintf('Now calling transaction journal factory (%d transactions in array)', count($data['transactions']))); $group = $this->groupFactory->create($data); - Log::debug('Done calling transaction journal factory'); + app('log')->debug('Done calling transaction journal factory'); // delete the old transaction journal. $this->service->destroy($journal); @@ -266,7 +265,7 @@ class MigrateToGroups extends Command $this->count++; // report on result: - Log::debug( + app('log')->debug( sprintf( 'Migrated journal #%d into group #%d with these journals: #%s', $journal->id, @@ -310,8 +309,8 @@ class MigrateToGroups extends Command static function (Transaction $subject) use ($transaction) { $amount = (float)$transaction->amount * -1 === (float)$subject->amount; // intentional float $identifier = $transaction->identifier === $subject->identifier; - Log::debug(sprintf('Amount the same? %s', var_export($amount, true))); - Log::debug(sprintf('ID the same? %s', var_export($identifier, true))); + app('log')->debug(sprintf('Amount the same? %s', var_export($amount, true))); + app('log')->debug(sprintf('ID the same? %s', var_export($identifier, true))); return $amount && $identifier; } @@ -328,26 +327,26 @@ class MigrateToGroups extends Command */ private function getTransactionBudget(Transaction $left, Transaction $right): ?int { - Log::debug('Now in getTransactionBudget()'); + app('log')->debug('Now in getTransactionBudget()'); // try to get a budget ID from the left transaction: /** @var Budget|null $budget */ $budget = $left->budgets()->first(); if (null !== $budget) { - Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); + app('log')->debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); - return (int)$budget->id; + return $budget->id; } // try to get a budget ID from the right transaction: /** @var Budget|null $budget */ $budget = $right->budgets()->first(); if (null !== $budget) { - Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); + app('log')->debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); - return (int)$budget->id; + return $budget->id; } - Log::debug('Neither left or right have a budget, return NULL'); + app('log')->debug('Neither left or right have a budget, return NULL'); // if all fails, return NULL. return null; @@ -361,26 +360,26 @@ class MigrateToGroups extends Command */ private function getTransactionCategory(Transaction $left, Transaction $right): ?int { - Log::debug('Now in getTransactionCategory()'); + app('log')->debug('Now in getTransactionCategory()'); // try to get a category ID from the left transaction: /** @var Category|null $category */ $category = $left->categories()->first(); if (null !== $category) { - Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); + app('log')->debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); - return (int)$category->id; + return $category->id; } // try to get a category ID from the left transaction: /** @var Category|null $category */ $category = $right->categories()->first(); if (null !== $category) { - Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); + app('log')->debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); - return (int)$category->id; + return $category->id; } - Log::debug('Neither left or right have a category, return NULL'); + app('log')->debug('Neither left or right have a category, return NULL'); // if all fails, return NULL. return null; @@ -394,7 +393,7 @@ class MigrateToGroups extends Command $orphanedJournals = $this->cliRepository->getJournalsWithoutGroup(); $total = count($orphanedJournals); if ($total > 0) { - Log::debug(sprintf('Going to convert %d transaction journals. Please hold..', $total)); + app('log')->debug(sprintf('Going to convert %d transaction journals. Please hold..', $total)); $this->friendlyInfo(sprintf('Going to convert %d transaction journals. Please hold..', $total)); /** @var array $array */ foreach ($orphanedJournals as $array) { diff --git a/app/Console/Commands/Upgrade/MigrateToRules.php b/app/Console/Commands/Upgrade/MigrateToRules.php index 6e8c804894..94ec5bd1e0 100644 --- a/app/Console/Commands/Upgrade/MigrateToRules.php +++ b/app/Console/Commands/Upgrade/MigrateToRules.php @@ -44,28 +44,16 @@ class MigrateToRules extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_bills_to_rules'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '480_bills_to_rules'; + protected $description = 'Migrate bills to rules.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:bills-to-rules {--F|force : Force the execution of this command.}'; - /** @var BillRepositoryInterface */ - private $billRepository; - private $count; - /** @var RuleGroupRepositoryInterface */ - private $ruleGroupRepository; - /** @var RuleRepositoryInterface */ - private $ruleRepository; - /** @var UserRepositoryInterface */ - private $userRepository; + private BillRepositoryInterface $billRepository; + private int $count; + private RuleGroupRepositoryInterface $ruleGroupRepository; + private RuleRepositoryInterface $ruleRepository; + private UserRepositoryInterface $userRepository; /** * Execute the console command. @@ -150,14 +138,15 @@ class MigrateToRules extends Command /** @var Preference $lang */ $lang = app('preferences')->getForUser($user, 'language', 'en_US'); - $groupTitle = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data); + $language = null !== $lang->data && !is_array($lang->data) ? (string)$lang->data : 'en_US'; + $groupTitle = (string)trans('firefly.rulegroup_for_bills_title', [], $language); $ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle); if (null === $ruleGroup) { $ruleGroup = $this->ruleGroupRepository->store( [ - 'title' => (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data), - 'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $lang->data), + 'title' => (string)trans('firefly.rulegroup_for_bills_title', [], $language), + 'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $language), 'active' => true, ] ); @@ -180,6 +169,7 @@ class MigrateToRules extends Command if ('MIGRATED_TO_RULES' === $bill->match) { return; } + $languageString = null !== $language->data && !is_array($language->data) ? (string)$language->data : 'en_US'; // get match thing: $match = implode(' ', explode(',', $bill->match)); @@ -188,8 +178,8 @@ class MigrateToRules extends Command 'active' => true, 'strict' => false, 'stop_processing' => false, // field is no longer used. - 'title' => (string)trans('firefly.rule_for_bill_title', ['name' => $bill->name], $language->data), - 'description' => (string)trans('firefly.rule_for_bill_description', ['name' => $bill->name], $language->data), + 'title' => (string)trans('firefly.rule_for_bill_title', ['name' => $bill->name], $languageString), + 'description' => (string)trans('firefly.rule_for_bill_description', ['name' => $bill->name], $languageString), 'trigger' => 'store-journal', 'triggers' => [ [ diff --git a/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php b/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php index 845dd0eda8..18e01b6119 100644 --- a/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php +++ b/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php @@ -44,7 +44,7 @@ class OtherCurrenciesCorrections extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_other_currencies'; + public const string CONFIG_NAME = '480_other_currencies'; protected $description = 'Update all journal currency information.'; protected $signature = 'firefly-iii:other-currencies {--F|force : Force the execution of this command.}'; private array $accountCurrencies; @@ -169,8 +169,8 @@ class OtherCurrenciesCorrections extends Command } // when mismatch in transaction: - if ((int)$transaction->transaction_currency_id !== (int)$currency->id) { - $transaction->foreign_currency_id = (int)$transaction->transaction_currency_id; + if ($transaction->transaction_currency_id !== $currency->id) { + $transaction->foreign_currency_id = $transaction->transaction_currency_id; $transaction->foreign_amount = $transaction->amount; $transaction->transaction_currency_id = $currency->id; $transaction->save(); diff --git a/app/Console/Commands/Upgrade/RenameAccountMeta.php b/app/Console/Commands/Upgrade/RenameAccountMeta.php index 8451c88f0c..58697b522d 100644 --- a/app/Console/Commands/Upgrade/RenameAccountMeta.php +++ b/app/Console/Commands/Upgrade/RenameAccountMeta.php @@ -37,18 +37,10 @@ class RenameAccountMeta extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_rename_account_meta'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '480_rename_account_meta'; + protected $description = 'Rename account meta-data to new format.'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:rename-account-meta {--F|force : Force the execution of this command.}'; /** diff --git a/app/Console/Commands/Upgrade/TransactionIdentifier.php b/app/Console/Commands/Upgrade/TransactionIdentifier.php index 4f27a372ca..eb21a358b5 100644 --- a/app/Console/Commands/Upgrade/TransactionIdentifier.php +++ b/app/Console/Commands/Upgrade/TransactionIdentifier.php @@ -30,7 +30,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalCLIRepositoryInterface; use Illuminate\Console\Command; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Schema; @@ -42,7 +41,7 @@ class TransactionIdentifier extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_transaction_identifier'; + public const string CONFIG_NAME = '480_transaction_identifier'; protected $description = 'Fixes transaction identifiers.'; protected $signature = 'firefly-iii:transaction-identifiers {--F|force : Force the execution of this command.}'; private JournalCLIRepositoryInterface $cliRepository; @@ -162,7 +161,7 @@ class TransactionIdentifier extends Command private function findOpposing(Transaction $transaction, array $exclude): ?Transaction { // find opposing: - $amount = bcmul((string)$transaction->amount, '-1'); + $amount = bcmul($transaction->amount, '-1'); try { /** @var Transaction $opposing */ @@ -171,7 +170,7 @@ class TransactionIdentifier extends Command ->whereNotIn('id', $exclude) ->first(); } catch (QueryException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); $this->friendlyError('Firefly III could not find the "identifier" field in the "transactions" table.'); $this->friendlyError(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); $this->friendlyError('Please run "php artisan migrate" to add this field to the table.'); diff --git a/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php b/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php index 556994f292..a87f658ff8 100644 --- a/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php +++ b/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php @@ -32,7 +32,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalCLIRepositoryInterface; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -43,7 +42,7 @@ class TransferCurrenciesCorrections extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_transfer_currencies'; + public const string CONFIG_NAME = '480_transfer_currencies'; protected $description = 'Updates transfer currency information.'; protected $signature = 'firefly-iii:transfer-currencies {--F|force : Force the execution of this command.}'; private array $accountCurrencies; @@ -319,7 +318,7 @@ class TransferCurrenciesCorrections extends Command // source account must have a currency preference. if (null === $this->sourceCurrency) { $message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name); - Log::error($message); + app('log')->error($message); $this->friendlyError($message); return true; @@ -332,7 +331,7 @@ class TransferCurrenciesCorrections extends Command $this->destinationAccount->id, $this->destinationAccount->name ); - Log::error($message); + app('log')->error($message); $this->friendlyError($message); return true; @@ -350,7 +349,7 @@ class TransferCurrenciesCorrections extends Command if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) { $this->sourceTransaction ->transaction_currency_id - = (int)$this->sourceCurrency->id; + = $this->sourceCurrency->id; $message = sprintf( 'Transaction #%d has no currency setting, now set to %s.', $this->sourceTransaction->id, @@ -370,7 +369,7 @@ class TransferCurrenciesCorrections extends Command { if (null !== $this->sourceCurrency && null === $this->sourceTransaction->foreign_amount - && (int)$this->sourceTransaction->transaction_currency_id !== (int)$this->sourceCurrency->id + && (int)$this->sourceTransaction->transaction_currency_id !== $this->sourceCurrency->id ) { $message = sprintf( 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', @@ -381,7 +380,7 @@ class TransferCurrenciesCorrections extends Command ); $this->friendlyWarning($message); $this->count++; - $this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id; + $this->sourceTransaction->transaction_currency_id = $this->sourceCurrency->id; $this->sourceTransaction->save(); } } @@ -395,7 +394,7 @@ class TransferCurrenciesCorrections extends Command if (null === $this->destinationTransaction->transaction_currency_id && null !== $this->destinationCurrency) { $this->destinationTransaction ->transaction_currency_id - = (int)$this->destinationCurrency->id; + = $this->destinationCurrency->id; $message = sprintf( 'Transaction #%d has no currency setting, now set to %s.', $this->destinationTransaction->id, @@ -415,7 +414,7 @@ class TransferCurrenciesCorrections extends Command { if (null !== $this->destinationCurrency && null === $this->destinationTransaction->foreign_amount - && (int)$this->destinationTransaction->transaction_currency_id !== (int)$this->destinationCurrency->id + && (int)$this->destinationTransaction->transaction_currency_id !== $this->destinationCurrency->id ) { $message = sprintf( 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', @@ -426,7 +425,7 @@ class TransferCurrenciesCorrections extends Command ); $this->friendlyWarning($message); $this->count++; - $this->destinationTransaction->transaction_currency_id = (int)$this->destinationCurrency->id; + $this->destinationTransaction->transaction_currency_id = $this->destinationCurrency->id; $this->destinationTransaction->save(); } } @@ -438,7 +437,7 @@ class TransferCurrenciesCorrections extends Command */ private function fixInvalidForeignCurrency(): void { - if ((int)$this->destinationCurrency->id === (int)$this->sourceCurrency->id) { + if ($this->destinationCurrency->id === $this->sourceCurrency->id) { // update both transactions to match: $this->sourceTransaction->foreign_amount = null; $this->sourceTransaction->foreign_currency_id = null; @@ -458,7 +457,7 @@ class TransferCurrenciesCorrections extends Command */ private function fixMismatchedForeignCurrency(): void { - if ((int)$this->sourceCurrency->id !== (int)$this->destinationCurrency->id) { + if ($this->sourceCurrency->id !== $this->destinationCurrency->id) { $this->sourceTransaction->transaction_currency_id = $this->sourceCurrency->id; $this->sourceTransaction->foreign_currency_id = $this->destinationCurrency->id; $this->destinationTransaction->transaction_currency_id = $this->sourceCurrency->id; @@ -480,7 +479,7 @@ class TransferCurrenciesCorrections extends Command private function fixSourceNullForeignAmount(): void { if (null === $this->sourceTransaction->foreign_amount && null !== $this->destinationTransaction->foreign_amount) { - $this->sourceTransaction->foreign_amount = bcmul((string)$this->destinationTransaction->foreign_amount, '-1'); + $this->sourceTransaction->foreign_amount = bcmul($this->destinationTransaction->foreign_amount, '-1'); $this->sourceTransaction->save(); $this->count++; $this->friendlyInfo( @@ -500,7 +499,7 @@ class TransferCurrenciesCorrections extends Command private function fixDestNullForeignAmount(): void { if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) { - $this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1'); + $this->destinationTransaction->foreign_amount = bcmul($this->sourceTransaction->foreign_amount, '-1'); $this->destinationTransaction->save(); $this->count++; $this->friendlyInfo( @@ -520,7 +519,7 @@ class TransferCurrenciesCorrections extends Command */ private function fixTransactionJournalCurrency(TransactionJournal $journal): void { - if ((int)$journal->transaction_currency_id !== (int)$this->sourceCurrency->id) { + if ((int)$journal->transaction_currency_id !== $this->sourceCurrency->id) { $oldCurrencyCode = $journal->transactionCurrency->code ?? '(nothing)'; $journal->transaction_currency_id = $this->sourceCurrency->id; $message = sprintf( diff --git a/app/Console/Commands/Upgrade/UpgradeCurrencyPreferences.php b/app/Console/Commands/Upgrade/UpgradeCurrencyPreferences.php index b78ff5c57b..c0f0b965fa 100644 --- a/app/Console/Commands/Upgrade/UpgradeCurrencyPreferences.php +++ b/app/Console/Commands/Upgrade/UpgradeCurrencyPreferences.php @@ -35,24 +35,15 @@ use Illuminate\Support\Collection; /** * Class UpgradeCurrencyPreferences - * TODO DONT FORGET TO ADD THIS TO THE DOCKER BUILD */ class UpgradeCurrencyPreferences extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '610_upgrade_currency_prefs'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '610_upgrade_currency_prefs'; + protected $description = 'Upgrade user currency preferences'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:upgrade-currency-preferences {--F|force : Force the execution of this command.}'; /** @@ -71,7 +62,7 @@ class UpgradeCurrencyPreferences extends Command $this->friendlyPositive('Currency preferences migrated.'); - //$this->markAsExecuted(); + $this->markAsExecuted(); return 0; } @@ -142,7 +133,7 @@ class UpgradeCurrencyPreferences extends Command // set the default currency for the user and for the group: $preference = $this->getPreference($user); $defaultCurrency = TransactionCurrency::where('code', $preference)->first(); - if (null === $currency) { + if (null === $defaultCurrency) { // get EUR $defaultCurrency = TransactionCurrency::where('code', 'EUR')->first(); } @@ -159,7 +150,11 @@ class UpgradeCurrencyPreferences extends Command { $preference = Preference::where('user_id', $user->id)->where('name', 'currencyPreference')->first(['id', 'user_id', 'name', 'data', 'updated_at', 'created_at']); - if (null !== $preference) { + if (null === $preference) { + return 'EUR'; + } + + if (null !== $preference->data && !is_array($preference->data)) { return (string)$preference->data; } return 'EUR'; diff --git a/app/Console/Commands/Upgrade/UpgradeLiabilities.php b/app/Console/Commands/Upgrade/UpgradeLiabilities.php index 3723b2858d..f7c84ad070 100644 --- a/app/Console/Commands/Upgrade/UpgradeLiabilities.php +++ b/app/Console/Commands/Upgrade/UpgradeLiabilities.php @@ -43,7 +43,7 @@ class UpgradeLiabilities extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '560_upgrade_liabilities'; + public const string CONFIG_NAME = '560_upgrade_liabilities'; protected $description = 'Upgrade liabilities to new 5.6.0 structure.'; protected $signature = 'firefly-iii:upgrade-liabilities {--F|force : Force the execution of this command.}'; @@ -149,9 +149,9 @@ class UpgradeLiabilities extends Command return; } // source MUST be the liability. - if ((int)$destination->account_id === (int)$account->id) { + if ($destination->account_id === $account->id) { // so if not, switch things around: - $sourceAccountId = (int)$source->account_id; + $sourceAccountId = $source->account_id; $source->account_id = $destination->account_id; $destination->account_id = $sourceAccountId; $source->save(); diff --git a/app/Console/Commands/Upgrade/UpgradeLiabilitiesEight.php b/app/Console/Commands/Upgrade/UpgradeLiabilitiesEight.php index ccd8e4721b..b6e8f8069d 100644 --- a/app/Console/Commands/Upgrade/UpgradeLiabilitiesEight.php +++ b/app/Console/Commands/Upgrade/UpgradeLiabilitiesEight.php @@ -26,7 +26,6 @@ namespace FireflyIII\Console\Commands\Upgrade; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Models\Account; -use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; @@ -35,7 +34,6 @@ use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService; use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\User; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -46,7 +44,7 @@ class UpgradeLiabilitiesEight extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '600_upgrade_liabilities'; + public const string CONFIG_NAME = '600_upgrade_liabilities'; protected $description = 'Upgrade liabilities to new 6.0.0 structure.'; protected $signature = 'firefly-iii:liabilities-600 {--F|force : Force the execution of this command.}'; @@ -206,7 +204,7 @@ class UpgradeLiabilitiesEight extends Command $source = $openingJournal->transactions()->where('amount', '<', 0)->first(); /** @var Transaction|null $dest */ $dest = $openingJournal->transactions()->where('amount', '>', 0)->first(); - if ($source && $dest) { + if (null !== $source && null !== $dest) { $sourceId = $source->account_id; $destId = $dest->account_id; $dest->account_id = $sourceId; @@ -216,45 +214,47 @@ class UpgradeLiabilitiesEight extends Command return; } - Log::warning('Did not find opening balance.'); + app('log')->warning('Did not find opening balance.'); } /** - * @param $account + * @param Account $account * * @return int */ - private function deleteTransactions($account): int + private function deleteTransactions(Account $account): int { $count = 0; $journals = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->where('transactions.account_id', $account->id)->get(['transaction_journals.*']); /** @var TransactionJournal $journal */ foreach ($journals as $journal) { - $delete = false; - /** @var Transaction $source */ - $source = $journal->transactions()->where('amount', '<', 0)->first(); - /** @var Transaction $dest */ - $dest = $journal->transactions()->where('amount', '>', 0)->first(); +// $delete = false; +// /** @var Transaction $source */ +// $source = $journal->transactions()->where('amount', '<', 0)->first(); +// /** @var Transaction $dest */ +// $dest = $journal->transactions()->where('amount', '>', 0)->first(); - // if source is this liability and destination is expense, remove transaction. - // if source is revenue and destination is liability, remove transaction. - if ((int)$source->account_id === (int)$account->id && $dest->account->accountType->type === AccountType::EXPENSE) { - $delete = true; - } - if ((int)$dest->account_id === (int)$account->id && $source->account->accountType->type === AccountType::REVENUE) { - $delete = true; - } + /** + * // if source is this liability and destination is expense, remove transaction. + * // if source is revenue and destination is liability, remove transaction. + * if ($source->account_id === $account->id && $dest->account->accountType->type === AccountType::EXPENSE) { + * $delete = true; + * } + * if ($dest->account_id === $account->id && $source->account->accountType->type === AccountType::REVENUE) { + * $delete = true; + * } + * + * // overruled. No transaction will be deleted, ever. + * // code is kept in place, so I can revisit my reasoning. + * $delete = false; + */ - // overruled. No transaction will be deleted, ever. - // code is kept in place so i can revisit my reasoning. - $delete = false; - - if ($delete) { - $service = app(TransactionGroupDestroyService::class); - $service->destroy($journal->transactionGroup); - $count++; - } + // if ($delete) { + $service = app(TransactionGroupDestroyService::class); + $service->destroy($journal->transactionGroup); + $count++; + // } } return $count; diff --git a/app/Console/Commands/Upgrade/UpgradeSkeleton.php.stub b/app/Console/Commands/Upgrade/UpgradeSkeleton.php.stub index a5fd3aab50..437c42e220 100644 --- a/app/Console/Commands/Upgrade/UpgradeSkeleton.php.stub +++ b/app/Console/Commands/Upgrade/UpgradeSkeleton.php.stub @@ -11,18 +11,10 @@ use Illuminate\Console\Command; class UpgradeSkeleton extends Command { use ShowsFriendlyMessages; - public const CONFIG_NAME = '480_some_name'; - /** - * The console command description. - * - * @var string - */ + public const string CONFIG_NAME = '480_some_name'; + protected $description = 'SOME DESCRIPTION'; - /** - * The name and signature of the console command. - * - * @var string - */ + protected $signature = 'firefly-iii:UPGRSKELETON {--F|force : Force the execution of this command.}'; /** diff --git a/app/Console/Commands/VerifiesAccessToken.php b/app/Console/Commands/VerifiesAccessToken.php index d6c45610c3..38c1809551 100644 --- a/app/Console/Commands/VerifiesAccessToken.php +++ b/app/Console/Commands/VerifiesAccessToken.php @@ -26,7 +26,6 @@ namespace FireflyIII\Console\Commands; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Trait VerifiesAccessToken. @@ -78,19 +77,19 @@ trait VerifiesAccessToken $user = $repository->find($userId); if (null === $user) { - Log::error(sprintf('verifyAccessToken(): no such user for input "%d"', $userId)); + app('log')->error(sprintf('verifyAccessToken(): no such user for input "%d"', $userId)); return false; } $accessToken = app('preferences')->getForUser($user, 'access_token'); if (null === $accessToken) { - Log::error(sprintf('User #%d has no access token, so cannot access command line options.', $userId)); + app('log')->error(sprintf('User #%d has no access token, so cannot access command line options.', $userId)); return false; } if ($accessToken->data !== $token) { - Log::error(sprintf('Invalid access token for user #%d.', $userId)); - Log::error(sprintf('Token given is "%s", expected something else.', $token)); + app('log')->error(sprintf('Invalid access token for user #%d.', $userId)); + app('log')->error(sprintf('Token given is "%s", expected something else.', $token)); return false; } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 3282712a95..7ca1d8903a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -26,7 +26,6 @@ namespace FireflyIII\Console; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; -use Illuminate\Support\Facades\Log; /** * File to make sure commands work. @@ -54,7 +53,7 @@ class Kernel extends ConsoleKernel { $schedule->call( static function () { - Log::error( + app('log')->error( 'Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions at https://docs.firefly-iii.org/' ); echo "\n"; diff --git a/app/Enums/ClauseType.php b/app/Enums/ClauseType.php index 6b78943a0e..47f50192f8 100644 --- a/app/Enums/ClauseType.php +++ b/app/Enums/ClauseType.php @@ -29,7 +29,7 @@ namespace FireflyIII\Enums; */ class ClauseType { - public const TRANSACTION = 'transaction'; - public const UPDATE = 'update'; - public const WHERE = 'where'; + public const string TRANSACTION = 'transaction'; + public const string UPDATE = 'update'; + public const string WHERE = 'where'; } diff --git a/app/Enums/SearchDirection.php b/app/Enums/SearchDirection.php new file mode 100644 index 0000000000..5cc5f17825 --- /dev/null +++ b/app/Enums/SearchDirection.php @@ -0,0 +1,32 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Enums; + +enum SearchDirection +{ + case SOURCE; + case DESTINATION; + case BOTH; + +} diff --git a/app/Enums/StringPosition.php b/app/Enums/StringPosition.php new file mode 100644 index 0000000000..4c10335f65 --- /dev/null +++ b/app/Enums/StringPosition.php @@ -0,0 +1,37 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Enums; + +/** + * Class StringPosition + * + * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is + */ +enum StringPosition +{ + case STARTS; + case ENDS; + case CONTAINS; + case IS; +} diff --git a/app/Events/ActuallyLoggedIn.php b/app/Events/ActuallyLoggedIn.php index 557dad3b38..598077fe0a 100644 --- a/app/Events/ActuallyLoggedIn.php +++ b/app/Events/ActuallyLoggedIn.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Events; use FireflyIII\User; +use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Queue\SerializesModels; /** @@ -37,10 +38,13 @@ class ActuallyLoggedIn extends Event public User $user; /** - * @param User $user + * @param User|Authenticatable|null $user */ - public function __construct(User $user) + public function __construct(User | Authenticatable | null $user) { - $this->user = $user; + if ($user instanceof User) { + $this->user = $user; + } + } } diff --git a/app/Events/AdminRequestedTestMessage.php b/app/Events/AdminRequestedTestMessage.php index 1c8d0fa1de..09334089bb 100644 --- a/app/Events/AdminRequestedTestMessage.php +++ b/app/Events/AdminRequestedTestMessage.php @@ -26,7 +26,6 @@ namespace FireflyIII\Events; use FireflyIII\User; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Log; /** * Class AdminRequestedTestMessage. @@ -46,7 +45,7 @@ class AdminRequestedTestMessage extends Event */ public function __construct(User $user) { - Log::debug(sprintf('Triggered AdminRequestedTestMessage for user #%d (%s)', $user->id, $user->email)); + app('log')->debug(sprintf('Triggered AdminRequestedTestMessage for user #%d (%s)', $user->id, $user->email)); $this->user = $user; } } diff --git a/app/Events/DestroyedTransactionGroup.php b/app/Events/DestroyedTransactionGroup.php index febad99260..fa4aea8b1e 100644 --- a/app/Events/DestroyedTransactionGroup.php +++ b/app/Events/DestroyedTransactionGroup.php @@ -26,7 +26,6 @@ namespace FireflyIII\Events; use FireflyIII\Models\TransactionGroup; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Log; /** * Class DestroyedTransactionGroup. @@ -46,7 +45,7 @@ class DestroyedTransactionGroup extends Event */ public function __construct(TransactionGroup $transactionGroup) { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $this->transactionGroup = $transactionGroup; } } diff --git a/app/Events/DestroyedTransactionLink.php b/app/Events/DestroyedTransactionLink.php index fee5c47c9e..9353e95883 100644 --- a/app/Events/DestroyedTransactionLink.php +++ b/app/Events/DestroyedTransactionLink.php @@ -33,7 +33,7 @@ class DestroyedTransactionLink extends Event { use SerializesModels; - private TransactionJournalLink $link; /// @phpstan-ignore-line PHPStan thinks this property is never read. + private TransactionJournalLink $link; // @phpstan-ignore-line /** * DestroyedTransactionLink constructor. diff --git a/app/Events/Model/PiggyBank/ChangedAmount.php b/app/Events/Model/PiggyBank/ChangedAmount.php index 79d2068c30..83fe12b350 100644 --- a/app/Events/Model/PiggyBank/ChangedAmount.php +++ b/app/Events/Model/PiggyBank/ChangedAmount.php @@ -29,7 +29,6 @@ use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Log; /** * Class ChangedAmount @@ -53,7 +52,7 @@ class ChangedAmount extends Event */ public function __construct(PiggyBank $piggyBank, string $amount, ?TransactionJournal $transactionJournal, ?TransactionGroup $transactionGroup) { - Log::debug(sprintf('Created piggy bank event for piggy bank #%d with amount %s', $piggyBank->id, $amount)); + app('log')->debug(sprintf('Created piggy bank event for piggy bank #%d with amount %s', $piggyBank->id, $amount)); $this->piggyBank = $piggyBank; $this->transactionJournal = $transactionJournal; $this->transactionGroup = $transactionGroup; diff --git a/app/Events/RequestedReportOnJournals.php b/app/Events/RequestedReportOnJournals.php index 8b9edbd7eb..5719c25a8f 100644 --- a/app/Events/RequestedReportOnJournals.php +++ b/app/Events/RequestedReportOnJournals.php @@ -29,7 +29,6 @@ use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class RequestedReportOnJournals @@ -53,7 +52,7 @@ class RequestedReportOnJournals */ public function __construct(int $userId, Collection $groups) { - Log::debug('In event RequestedReportOnJournals.'); + app('log')->debug('In event RequestedReportOnJournals.'); $this->userId = $userId; $this->groups = $groups; } diff --git a/app/Events/TriggeredAuditLog.php b/app/Events/TriggeredAuditLog.php index 95f7fe5b71..a09bf11b84 100644 --- a/app/Events/TriggeredAuditLog.php +++ b/app/Events/TriggeredAuditLog.php @@ -42,6 +42,7 @@ class TriggeredAuditLog extends Event /** * Create a new event instance. + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct(Model $changer, Model $auditable, string $field, mixed $before, mixed $after) { diff --git a/app/Exceptions/GracefulNotFoundHandler.php b/app/Exceptions/GracefulNotFoundHandler.php index 94ba81ec38..1b27f0d67b 100644 --- a/app/Exceptions/GracefulNotFoundHandler.php +++ b/app/Exceptions/GracefulNotFoundHandler.php @@ -30,13 +30,8 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; -use Illuminate\Contracts\Foundation\Application; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; -use Illuminate\Http\JsonResponse; -use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Symfony\Component\HttpFoundation\Response; use Throwable; @@ -51,10 +46,10 @@ class GracefulNotFoundHandler extends ExceptionHandler * @param Request $request * @param Throwable $e * - * @return Application|JsonResponse|\Illuminate\Http\Response|Redirector|RedirectResponse|Response + * @return Response * @throws Throwable */ - public function render($request, Throwable $e) + public function render($request, Throwable $e): Response { $route = $request->route(); if (null === $route) { @@ -141,23 +136,24 @@ class GracefulNotFoundHandler extends ExceptionHandler * @return Response * @throws Throwable */ - private function handleAccount(Request $request, Throwable $exception) + private function handleAccount(Request $request, Throwable $exception): Response { - Log::debug('404 page is probably a deleted account. Redirect to overview of account types.'); + app('log')->debug('404 page is probably a deleted account. Redirect to overview of account types.'); /** @var User $user */ - $user = auth()->user(); - $route = $request->route(); - $param = $route->parameter('account'); + $user = auth()->user(); + $route = $request->route(); + $param = $route->parameter('account'); + $accountId = 0; if ($param instanceof Account) { - $accountId = (int)$param->id; + $accountId = $param->id; } - if (!($param instanceof Account)) { + if (!($param instanceof Account) && !is_object($param)) { $accountId = (int)$param; } /** @var Account|null $account */ $account = $user->accounts()->with(['accountType'])->withTrashed()->find($accountId); if (null === $account) { - Log::error(sprintf('Could not find account %d, so give big fat error.', $accountId)); + app('log')->error(sprintf('Could not find account %d, so give big fat error.', $accountId)); return parent::render($request, $exception); } @@ -177,23 +173,24 @@ class GracefulNotFoundHandler extends ExceptionHandler */ private function handleGroup(Request $request, Throwable $exception) { - Log::debug('404 page is probably a deleted group. Redirect to overview of group types.'); + app('log')->debug('404 page is probably a deleted group. Redirect to overview of group types.'); /** @var User $user */ $user = auth()->user(); $route = $request->route(); - $groupId = (int)$route->parameter('transactionGroup'); + $param = $route->parameter('transactionGroup'); + $groupId = !is_object($param) ? (int)$param : 0; /** @var TransactionGroup|null $group */ $group = $user->transactionGroups()->withTrashed()->find($groupId); if (null === $group) { - Log::error(sprintf('Could not find group %d, so give big fat error.', $groupId)); + app('log')->error(sprintf('Could not find group %d, so give big fat error.', $groupId)); return parent::render($request, $exception); } /** @var TransactionJournal|null $journal */ $journal = $group->transactionJournals()->withTrashed()->first(); if (null === $journal) { - Log::error(sprintf('Could not find journal for group %d, so give big fat error.', $groupId)); + app('log')->error(sprintf('Could not find journal for group %d, so give big fat error.', $groupId)); return parent::render($request, $exception); } @@ -216,22 +213,23 @@ class GracefulNotFoundHandler extends ExceptionHandler */ private function handleAttachment(Request $request, Throwable $exception) { - Log::debug('404 page is probably a deleted attachment. Redirect to parent object.'); + app('log')->debug('404 page is probably a deleted attachment. Redirect to parent object.'); /** @var User $user */ $user = auth()->user(); $route = $request->route(); - $attachmentId = (int)$route->parameter('attachment'); + $param = $route->parameter('attachment'); + $attachmentId = is_object($param) ? 0 : (int)$param; /** @var Attachment|null $attachment */ $attachment = $user->attachments()->withTrashed()->find($attachmentId); if (null === $attachment) { - Log::error(sprintf('Could not find attachment %d, so give big fat error.', $attachmentId)); + app('log')->error(sprintf('Could not find attachment %d, so give big fat error.', $attachmentId)); return parent::render($request, $exception); } // get bindable. if (TransactionJournal::class === $attachment->attachable_type) { // is linked to journal, get group of journal (if not also deleted) - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $journal */ $journal = $user->transactionJournals()->withTrashed()->find($attachment->attachable_id); if (null !== $journal) { return redirect(route('transactions.show', [$journal->transaction_group_id])); @@ -239,14 +237,14 @@ class GracefulNotFoundHandler extends ExceptionHandler } if (Bill::class === $attachment->attachable_type) { // is linked to bill. - /** @var Bill $bill */ + /** @var Bill|null $bill */ $bill = $user->bills()->withTrashed()->find($attachment->attachable_id); if (null !== $bill) { return redirect(route('bills.show', [$bill->id])); } } - Log::error(sprintf('Could not redirect attachment %d, its linked to a %s.', $attachmentId, $attachment->attachable_type)); + app('log')->error(sprintf('Could not redirect attachment %d, its linked to a %s.', $attachmentId, $attachment->attachable_type)); return parent::render($request, $exception); } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index b9bf1e6a36..d62e81247f 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -27,19 +27,18 @@ namespace FireflyIII\Exceptions; use ErrorException; use FireflyIII\Jobs\MailError; use Illuminate\Auth\AuthenticationException; -use Illuminate\Contracts\Foundation\Application; use Illuminate\Database\QueryException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; +use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Routing\Redirector; use Illuminate\Session\TokenMismatchException; use Illuminate\Support\Arr; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException as LaravelValidationException; use Laravel\Passport\Exceptions\OAuthServerException as LaravelOAuthException; use League\OAuth2\Server\Exception\OAuthServerException; use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; @@ -75,49 +74,49 @@ class Handler extends ExceptionHandler * @param Request $request * @param Throwable $e * - * @return mixed + * @return Response * @throws Throwable */ - public function render($request, Throwable $e) + public function render($request, Throwable $e): Response { $expectsJson = $request->expectsJson(); // if the user requests anything /api/, assume the user wants to see JSON. if (str_starts_with($request->getRequestUri(), '/api/')) { - Log::debug('API endpoint, always assume user wants JSON.'); + app('log')->debug('API endpoint, always assume user wants JSON.'); $expectsJson = true; } - Log::debug('Now in Handler::render()'); + app('log')->debug('Now in Handler::render()'); if ($e instanceof LaravelValidationException && $expectsJson) { // ignore it: controller will handle it. - Log::debug(sprintf('Return to parent to handle LaravelValidationException(%d)', $e->status)); + app('log')->debug(sprintf('Return to parent to handle LaravelValidationException(%d)', $e->status)); return parent::render($request, $e); } if ($e instanceof NotFoundHttpException && $expectsJson) { // JSON error: - Log::debug('Return JSON not found error.'); + app('log')->debug('Return JSON not found error.'); return response()->json(['message' => 'Resource not found', 'exception' => 'NotFoundHttpException'], 404); } if ($e instanceof AuthenticationException && $expectsJson) { // somehow Laravel handler does not catch this: - Log::debug('Return JSON unauthenticated error.'); + app('log')->debug('Return JSON unauthenticated error.'); return response()->json(['message' => 'Unauthenticated', 'exception' => 'AuthenticationException'], 401); } if ($e instanceof OAuthServerException && $expectsJson) { - Log::debug('Return JSON OAuthServerException.'); + app('log')->debug('Return JSON OAuthServerException.'); // somehow Laravel handler does not catch this: return response()->json(['message' => $e->getMessage(), 'exception' => 'OAuthServerException'], 401); } if ($e instanceof BadRequestHttpException) { - Log::debug('Return JSON BadRequestHttpException.'); + app('log')->debug('Return JSON BadRequestHttpException.'); return response()->json(['message' => $e->getMessage(), 'exception' => 'BadRequestHttpException'], 400); } if ($e instanceof BadHttpHeaderException) { // is always API exception. - Log::debug('Return JSON BadHttpHeaderException.'); + app('log')->debug('Return JSON BadHttpHeaderException.'); return response()->json(['message' => $e->getMessage(), 'exception' => 'BadHttpHeaderException'], $e->statusCode); } @@ -125,9 +124,9 @@ class Handler extends ExceptionHandler $errorCode = 500; $errorCode = $e instanceof MethodNotAllowedHttpException ? 405 : $errorCode; - $isDebug = config('app.debug', false); + $isDebug = (bool)config('app.debug', false); if ($isDebug) { - Log::debug(sprintf('Return JSON %s with debug.', get_class($e))); + app('log')->debug(sprintf('Return JSON %s with debug.', get_class($e))); return response()->json( [ 'message' => $e->getMessage(), @@ -139,7 +138,7 @@ class Handler extends ExceptionHandler $errorCode ); } - Log::debug(sprintf('Return JSON %s.', get_class($e))); + app('log')->debug(sprintf('Return JSON %s.', get_class($e))); return response()->json( ['message' => sprintf('Internal Firefly III Exception: %s', $e->getMessage()), 'exception' => get_class($e)], $errorCode @@ -147,7 +146,7 @@ class Handler extends ExceptionHandler } if ($e instanceof NotFoundHttpException) { - Log::debug('Refer to GracefulNotFoundHandler'); + app('log')->debug('Refer to GracefulNotFoundHandler'); $handler = app(GracefulNotFoundHandler::class); return $handler->render($request, $e); @@ -155,20 +154,20 @@ class Handler extends ExceptionHandler // special view for database errors with extra instructions if ($e instanceof QueryException) { - Log::debug('Return Firefly III database exception view.'); + app('log')->debug('Return Firefly III database exception view.'); $isDebug = config('app.debug'); return response()->view('errors.DatabaseException', ['exception' => $e, 'debug' => $isDebug], 500); } if ($e instanceof FireflyException || $e instanceof ErrorException || $e instanceof OAuthServerException) { - Log::debug('Return Firefly III error view.'); + app('log')->debug('Return Firefly III error view.'); $isDebug = config('app.debug'); return response()->view('errors.FireflyException', ['exception' => $e, 'debug' => $isDebug], 500); } - Log::debug(sprintf('Error "%s" has no Firefly III treatment, parent will handle.', get_class($e))); + app('log')->debug(sprintf('Error "%s" has no Firefly III treatment, parent will handle.', get_class($e))); return parent::render($request, $e); } @@ -184,7 +183,7 @@ class Handler extends ExceptionHandler */ public function report(Throwable $e) { - $doMailError = config('firefly.send_error_message'); + $doMailError = (bool)config('firefly.send_error_message'); if ($this->shouldntReportLocal($e) || !$doMailError) { parent::report($e); @@ -232,13 +231,11 @@ class Handler extends ExceptionHandler */ private function shouldntReportLocal(Throwable $e): bool { - return !is_null( - Arr::first( - $this->dontReport, - function ($type) use ($e) { + return null !== Arr::first( + $this->dontReport, + static function ($type) use ($e) { return $e instanceof $type; } - ) ); } @@ -248,9 +245,9 @@ class Handler extends ExceptionHandler * @param Request $request * @param LaravelValidationException $exception * - * @return Application|RedirectResponse|Redirector + * @return JsonResponse| RedirectResponse |\Illuminate\Http\Response */ - protected function invalid($request, LaravelValidationException $exception): Application | RedirectResponse | Redirector + protected function invalid($request, LaravelValidationException $exception): JsonResponse | RedirectResponse | \Illuminate\Http\Response { // protect against open redirect when submitting invalid forms. $previous = app('steam')->getSafePreviousUrl(); diff --git a/app/Support/Calendar/Exceptions/IntervalException.php b/app/Exceptions/IntervalException.php similarity index 75% rename from app/Support/Calendar/Exceptions/IntervalException.php rename to app/Exceptions/IntervalException.php index 6192dcc51e..417fedd182 100644 --- a/app/Support/Calendar/Exceptions/IntervalException.php +++ b/app/Exceptions/IntervalException.php @@ -3,7 +3,7 @@ /* * IntervalException.php - * Copyright (c) 2023 Antonio Spinelli https://github.com/tonicospinelli + * Copyright (c) 2023 james@firefly-iii.org * * This file is part of Firefly III (https://github.com/firefly-iii). * @@ -23,7 +23,7 @@ declare(strict_types=1); -namespace FireflyIII\Support\Calendar\Exceptions; +namespace FireflyIII\Exceptions; use Exception; use FireflyIII\Support\Calendar\Periodicity; @@ -34,10 +34,18 @@ use Throwable; */ final class IntervalException extends Exception { - public readonly array $availableIntervals; - public readonly Periodicity $periodicity; + public array $availableIntervals; + public Periodicity $periodicity; + /** @var mixed */ protected $message = 'The periodicity %s is unknown. Choose one of available periodicity: %s'; + public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + $this->availableIntervals = []; + $this->periodicity = Periodicity::Monthly; + } + /** * @param Periodicity $periodicity * @param array $intervals @@ -51,14 +59,15 @@ final class IntervalException extends Exception array $intervals, int $code = 0, ?Throwable $previous = null - ): IntervalException { + ): self + { $message = sprintf( 'The periodicity %s is unknown. Choose one of available periodicity: %s', $periodicity->name, - join(', ', $intervals) + implode(', ', $intervals) ); - $exception = new IntervalException($message, $code, $previous); + $exception = new self($message, $code, $previous); $exception->periodicity = $periodicity; $exception->availableIntervals = $intervals; return $exception; diff --git a/app/Factory/AccountFactory.php b/app/Factory/AccountFactory.php index 080da2d31b..09165897fd 100644 --- a/app/Factory/AccountFactory.php +++ b/app/Factory/AccountFactory.php @@ -79,7 +79,7 @@ class AccountFactory */ public function findOrCreate(string $accountName, string $accountType): Account { - Log::debug(sprintf('findOrCreate("%s", "%s")', $accountName, $accountType)); + app('log')->debug(sprintf('findOrCreate("%s", "%s")', $accountName, $accountType)); $type = $this->accountRepository->getAccountTypeByType($accountType); if (null === $type) { @@ -88,7 +88,7 @@ class AccountFactory $return = $this->user->accounts->where('account_type_id', $type->id)->where('name', $accountName)->first(); if (null === $return) { - Log::debug('Found nothing. Will create a new one.'); + app('log')->debug('Found nothing. Will create a new one.'); $return = $this->create( [ 'user_id' => $this->user->id, @@ -115,7 +115,7 @@ class AccountFactory */ public function create(array $data): Account { - Log::debug('Now in AccountFactory::create()'); + app('log')->debug('Now in AccountFactory::create()'); $type = $this->getAccountType($data); $data['iban'] = $this->filterIban($data['iban'] ?? null); @@ -163,7 +163,7 @@ class AccountFactory app('log')->warning(sprintf('Found NO account type based on %d and "%s"', $accountTypeId, $accountTypeName)); throw new FireflyException(sprintf('AccountFactory::create() was unable to find account type #%d ("%s").', $accountTypeId, $accountTypeName)); } - Log::debug(sprintf('Found account type based on %d and "%s": "%s"', $accountTypeId, $accountTypeName, $result->type)); + app('log')->debug(sprintf('Found account type based on %d and "%s": "%s"', $accountTypeId, $accountTypeName, $result->type)); return $result; } @@ -176,7 +176,7 @@ class AccountFactory */ public function find(string $accountName, string $accountType): ?Account { - Log::debug(sprintf('Now in AccountFactory::find("%s", "%s")', $accountName, $accountType)); + app('log')->debug(sprintf('Now in AccountFactory::find("%s", "%s")', $accountName, $accountType)); $type = AccountType::whereType($accountType)->first(); /** @var Account|null */ @@ -228,16 +228,16 @@ class AccountFactory try { $this->storeOpeningBalance($account, $data); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } // create credit liability data (only liabilities) try { $this->storeCreditLiability($account, $data); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } // create notes @@ -358,22 +358,22 @@ class AccountFactory */ private function storeCreditLiability(Account $account, array $data): void { - Log::debug('storeCreditLiability'); + app('log')->debug('storeCreditLiability'); $account->refresh(); $accountType = $account->accountType->type; $direction = $this->accountRepository->getMetaValue($account, 'liability_direction'); $valid = config('firefly.valid_liabilities'); if (in_array($accountType, $valid, true)) { - Log::debug('Is a liability with credit ("i am owed") direction.'); + app('log')->debug('Is a liability with credit ("i am owed") direction.'); if ($this->validOBData($data)) { - Log::debug('Has valid CL data.'); + app('log')->debug('Has valid CL data.'); $openingBalance = $data['opening_balance']; $openingBalanceDate = $data['opening_balance_date']; // store credit transaction. $this->updateCreditTransaction($account, $direction, $openingBalance, $openingBalanceDate); } if (!$this->validOBData($data)) { - Log::debug('Does NOT have valid CL data, deletr any CL transaction.'); + app('log')->debug('Does NOT have valid CL data, deletr any CL transaction.'); $this->deleteCreditTransaction($account); } } diff --git a/app/Factory/BillFactory.php b/app/Factory/BillFactory.php index 3e456c3e2a..d4b81d6eab 100644 --- a/app/Factory/BillFactory.php +++ b/app/Factory/BillFactory.php @@ -30,7 +30,6 @@ use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Services\Internal\Support\BillServiceTrait; use FireflyIII\User; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -52,7 +51,7 @@ class BillFactory */ public function create(array $data): ?Bill { - Log::debug(sprintf('Now in %s', __METHOD__), $data); + app('log')->debug(sprintf('Now in %s', __METHOD__), $data); $factory = app(TransactionCurrencyFactory::class); $currency = $factory->find((int)($data['currency_id'] ?? null), (string)($data['currency_code'] ?? null)) ?? app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); @@ -80,8 +79,8 @@ class BillFactory ] ); } catch (QueryException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException('400000: Could not store bill.', 0, $e); } diff --git a/app/Factory/BudgetFactory.php b/app/Factory/BudgetFactory.php index 728f69c51f..9c284b91f6 100644 --- a/app/Factory/BudgetFactory.php +++ b/app/Factory/BudgetFactory.php @@ -50,7 +50,7 @@ class BudgetFactory // first by ID: if ($budgetId > 0) { - /** @var Budget $budget */ + /** @var Budget|null $budget */ $budget = $this->user->budgets()->find($budgetId); if (null !== $budget) { return $budget; diff --git a/app/Factory/CategoryFactory.php b/app/Factory/CategoryFactory.php index e94ad3f50e..799740b3a1 100644 --- a/app/Factory/CategoryFactory.php +++ b/app/Factory/CategoryFactory.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Category; use FireflyIII\User; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\Log; /** * Class CategoryFactory @@ -48,14 +47,14 @@ class CategoryFactory $categoryId = (int)$categoryId; $categoryName = (string)$categoryName; - Log::debug(sprintf('Going to find category with ID %d and name "%s"', $categoryId, $categoryName)); + app('log')->debug(sprintf('Going to find category with ID %d and name "%s"', $categoryId, $categoryName)); if ('' === $categoryName && 0 === $categoryId) { return null; } // first by ID: if ($categoryId > 0) { - /** @var Category $category */ + /** @var Category|null $category */ $category = $this->user->categories()->find($categoryId); if (null !== $category) { return $category; @@ -76,8 +75,8 @@ class CategoryFactory ] ); } catch (QueryException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException('400003: Could not store new category.', 0, $e); } } diff --git a/app/Factory/PiggyBankEventFactory.php b/app/Factory/PiggyBankEventFactory.php index 3489ef96ec..38c395c5e0 100644 --- a/app/Factory/PiggyBankEventFactory.php +++ b/app/Factory/PiggyBankEventFactory.php @@ -26,7 +26,6 @@ namespace FireflyIII\Factory; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; -use Illuminate\Support\Facades\Log; /** * Create piggy bank events. @@ -41,9 +40,9 @@ class PiggyBankEventFactory */ public function create(TransactionJournal $journal, ?PiggyBank $piggyBank): void { - Log::debug(sprintf('Now in PiggyBankEventCreate for a %s', $journal->transactionType->type)); + app('log')->debug(sprintf('Now in PiggyBankEventCreate for a %s', $journal->transactionType->type)); if (null === $piggyBank) { - Log::debug('Piggy bank is null'); + app('log')->debug('Piggy bank is null'); return; } @@ -54,14 +53,14 @@ class PiggyBankEventFactory $repetition = $piggyRepos->getRepetition($piggyBank); if (null === $repetition) { - Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d'))); + app('log')->error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d'))); return; } - Log::debug('Found repetition'); + app('log')->debug('Found repetition'); $amount = $piggyRepos->getExactAmount($piggyBank, $repetition, $journal); if (0 === bccomp($amount, '0')) { - Log::debug('Amount is zero, will not create event.'); + app('log')->debug('Amount is zero, will not create event.'); return; } diff --git a/app/Factory/PiggyBankFactory.php b/app/Factory/PiggyBankFactory.php index 7d6d85fe2d..ec53f757b5 100644 --- a/app/Factory/PiggyBankFactory.php +++ b/app/Factory/PiggyBankFactory.php @@ -48,7 +48,7 @@ class PiggyBankFactory } // first find by ID: if ($piggyBankId > 0) { - /** @var PiggyBank $piggyBank */ + /** @var PiggyBank|null $piggyBank */ $piggyBank = $this->user->piggyBanks()->find($piggyBankId); if (null !== $piggyBank) { return $piggyBank; @@ -57,7 +57,7 @@ class PiggyBankFactory // then find by name: if ('' !== $piggyBankName) { - /** @var PiggyBank $piggyBank */ + /** @var PiggyBank|null $piggyBank */ $piggyBank = $this->findByName($piggyBankName); if (null !== $piggyBank) { return $piggyBank; diff --git a/app/Factory/RecurrenceFactory.php b/app/Factory/RecurrenceFactory.php index a826b06a66..3eab740fbd 100644 --- a/app/Factory/RecurrenceFactory.php +++ b/app/Factory/RecurrenceFactory.php @@ -29,7 +29,6 @@ use FireflyIII\Models\Recurrence; use FireflyIII\Services\Internal\Support\RecurringTransactionTrait; use FireflyIII\Services\Internal\Support\TransactionTypeTrait; use FireflyIII\User; -use Illuminate\Support\Facades\Log; use Illuminate\Support\MessageBag; use JsonException; @@ -38,8 +37,8 @@ use JsonException; */ class RecurrenceFactory { - use TransactionTypeTrait; use RecurringTransactionTrait; + use TransactionTypeTrait; private MessageBag $errors; private User $user; @@ -67,8 +66,8 @@ class RecurrenceFactory $type = $this->findTransactionType(ucfirst($data['recurrence']['type'])); } catch (FireflyException $e) { $message = sprintf('Cannot make a recurring transaction of type "%s"', $data['recurrence']['type']); - Log::error($message); - Log::error($e->getTraceAsString()); + app('log')->error($message); + app('log')->error($e->getTraceAsString()); throw new FireflyException($message, 0, $e); } @@ -129,8 +128,8 @@ class RecurrenceFactory try { $this->createTransactions($recurrence, $data['transactions'] ?? []); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $recurrence->forceDelete(); $message = sprintf('Could not create recurring transaction: %s', $e->getMessage()); $this->errors->add('store', $message); diff --git a/app/Factory/TagFactory.php b/app/Factory/TagFactory.php index dcfd9b8741..0f396d30e0 100644 --- a/app/Factory/TagFactory.php +++ b/app/Factory/TagFactory.php @@ -26,7 +26,6 @@ namespace FireflyIII\Factory; use FireflyIII\Models\Location; use FireflyIII\Models\Tag; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Class TagFactory @@ -43,12 +42,12 @@ class TagFactory public function findOrCreate(string $tag): ?Tag { $tag = trim($tag); - Log::debug(sprintf('Now in TagFactory::findOrCreate("%s")', $tag)); + app('log')->debug(sprintf('Now in TagFactory::findOrCreate("%s")', $tag)); /** @var Tag|null $dbTag */ $dbTag = $this->user->tags()->where('tag', $tag)->first(); if (null !== $dbTag) { - Log::debug(sprintf('Tag exists (#%d), return it.', $dbTag->id)); + app('log')->debug(sprintf('Tag exists (#%d), return it.', $dbTag->id)); return $dbTag; } @@ -63,11 +62,11 @@ class TagFactory ] ); if (null === $newTag) { - Log::error(sprintf('TagFactory::findOrCreate("%s") but tag is unexpectedly NULL!', $tag)); + app('log')->error(sprintf('TagFactory::findOrCreate("%s") but tag is unexpectedly NULL!', $tag)); return null; } - Log::debug(sprintf('Created new tag #%d ("%s")', $newTag->id, $newTag->tag)); + app('log')->debug(sprintf('Created new tag #%d ("%s")', $newTag->id, $newTag->tag)); return $newTag; } @@ -93,7 +92,8 @@ class TagFactory 'longitude' => null, 'zoomLevel' => null, ]; - $tag = Tag::create($array); + /** @var Tag|null $tag */ + $tag = Tag::create($array); if (null !== $tag && null !== $latitude && null !== $longitude) { // create location object. $location = new Location(); diff --git a/app/Factory/TransactionCurrencyFactory.php b/app/Factory/TransactionCurrencyFactory.php index 32bccaa0a4..946f3f63b5 100644 --- a/app/Factory/TransactionCurrencyFactory.php +++ b/app/Factory/TransactionCurrencyFactory.php @@ -26,7 +26,6 @@ namespace FireflyIII\Factory; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\Log; /** * Class TransactionCurrencyFactory @@ -67,8 +66,8 @@ class TransactionCurrencyFactory ); } catch (QueryException $e) { $result = null; - Log::error(sprintf('Could not create new currency: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not create new currency: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); throw new FireflyException('400004: Could not store new currency.', 0, $e); } @@ -83,11 +82,11 @@ class TransactionCurrencyFactory */ public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency { - $currencyCode = (string)e($currencyCode); + $currencyCode = e($currencyCode); $currencyId = (int)$currencyId; if ('' === $currencyCode && 0 === $currencyId) { - Log::debug('Cannot find anything on empty currency code and empty currency ID!'); + app('log')->debug('Cannot find anything on empty currency code and empty currency ID!'); return null; } diff --git a/app/Factory/TransactionFactory.php b/app/Factory/TransactionFactory.php index cc63aa1fc0..a5f8835984 100644 --- a/app/Factory/TransactionFactory.php +++ b/app/Factory/TransactionFactory.php @@ -33,7 +33,6 @@ use FireflyIII\Rules\UniqueIban; use FireflyIII\Services\Internal\Update\AccountUpdateService; use FireflyIII\User; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\Log; use Validator; /** @@ -107,16 +106,16 @@ class TransactionFactory /** @var Transaction|null $result */ $result = Transaction::create($data); } catch (QueryException $e) { - Log::error(sprintf('Could not create transaction: %s', $e->getMessage()), $data); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not create transaction: %s', $e->getMessage()), $data); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException(sprintf('Query exception when creating transaction: %s', $e->getMessage()), 0, $e); } if (null === $result) { throw new FireflyException('Transaction is NULL.'); } - Log::debug( + app('log')->debug( sprintf( 'Created transaction #%d (%s %s, account %s), part of journal #%d', $result->id, @@ -128,7 +127,9 @@ class TransactionFactory ); // do foreign currency thing: add foreign currency info to $one and $two if necessary. - if (null !== $this->foreignCurrency && null !== $foreignAmount && $this->foreignCurrency->id !== $this->currency->id && '' !== $foreignAmount) { + if (null !== $this->foreignCurrency && + null !== $foreignAmount && + $this->foreignCurrency->id !== $this->currency->id) { $result->foreign_currency_id = $this->foreignCurrency->id; $result->foreign_amount = $foreignAmount; } @@ -147,15 +148,15 @@ class TransactionFactory private function updateAccountInformation(): void { if (!array_key_exists('iban', $this->accountInformation)) { - Log::debug('No IBAN information in array, will not update.'); + app('log')->debug('No IBAN information in array, will not update.'); return; } if ('' !== (string)$this->account->iban) { - Log::debug('Account already has IBAN information, will not update.'); + app('log')->debug('Account already has IBAN information, will not update.'); return; } if ($this->account->iban === $this->accountInformation['iban']) { - Log::debug('Account already has this IBAN, will not update.'); + app('log')->debug('Account already has this IBAN, will not update.'); return; } // validate info: @@ -163,11 +164,11 @@ class TransactionFactory 'iban' => ['required', new UniqueIban($this->account, $this->account->accountType->type)], ]); if ($validator->fails()) { - Log::debug('Invalid or non-unique IBAN, will not update.'); + app('log')->debug('Invalid or non-unique IBAN, will not update.'); return; } - Log::debug('Will update account with IBAN information.'); + app('log')->debug('Will update account with IBAN information.'); $service = app(AccountUpdateService::class); $service->update($this->account, ['iban' => $this->accountInformation['iban']]); } @@ -253,8 +254,7 @@ class TransactionFactory /** * @param User $user - * - + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function setUser(User $user): void { diff --git a/app/Factory/TransactionGroupFactory.php b/app/Factory/TransactionGroupFactory.php index 9304a24650..ccc1b275ae 100644 --- a/app/Factory/TransactionGroupFactory.php +++ b/app/Factory/TransactionGroupFactory.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionGroup; use FireflyIII\User; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -60,7 +59,7 @@ class TransactionGroupFactory */ public function create(array $data): TransactionGroup { - Log::debug('Now in TransactionGroupFactory::create()'); + app('log')->debug('Now in TransactionGroupFactory::create()'); $this->journalFactory->setUser($this->user); $this->journalFactory->setErrorOnHash($data['error_if_duplicate_hash'] ?? false); try { diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 2c0c5756ac..e839fcdc12 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -29,7 +29,6 @@ use Exception; use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; -use FireflyIII\Models\Preference; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; @@ -48,7 +47,6 @@ use FireflyIII\Support\NullArrayObject; use FireflyIII\User; use FireflyIII\Validation\AccountValidator; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -102,40 +100,40 @@ class TransactionJournalFactory */ public function create(array $data): Collection { - Log::debug('Now in TransactionJournalFactory::create()'); + app('log')->debug('Now in TransactionJournalFactory::create()'); // convert to special object. $dataObject = new NullArrayObject($data); - Log::debug('Start of TransactionJournalFactory::create()'); + app('log')->debug('Start of TransactionJournalFactory::create()'); $collection = new Collection(); $transactions = $dataObject['transactions'] ?? []; if (0 === count($transactions)) { - Log::error('There are no transactions in the array, the TransactionJournalFactory cannot continue.'); + app('log')->error('There are no transactions in the array, the TransactionJournalFactory cannot continue.'); return new Collection(); } try { /** @var array $row */ foreach ($transactions as $index => $row) { - Log::debug(sprintf('Now creating journal %d/%d', $index + 1, count($transactions))); + app('log')->debug(sprintf('Now creating journal %d/%d', $index + 1, count($transactions))); $journal = $this->createJournal(new NullArrayObject($row)); if (null !== $journal) { $collection->push($journal); } if (null === $journal) { - Log::error('The createJournal() method returned NULL. This may indicate an error.'); + app('log')->error('The createJournal() method returned NULL. This may indicate an error.'); } } } catch (DuplicateTransactionException $e) { app('log')->warning('TransactionJournalFactory::create() caught a duplicate journal in createJournal()'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $this->forceDeleteOnError($collection); throw new DuplicateTransactionException($e->getMessage(), 0, $e); } catch (FireflyException $e) { app('log')->warning('TransactionJournalFactory::create() caught an exception.'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $this->forceDeleteOnError($collection); throw new FireflyException($e->getMessage(), 0, $e); } @@ -174,8 +172,8 @@ class TransactionJournalFactory // validate source and destination using a new Validator. $this->validateAccounts($row); } catch (FireflyException $e) { - Log::error('Could not validate source or destination.'); - Log::error($e->getMessage()); + app('log')->error('Could not validate source or destination.'); + app('log')->error($e->getMessage()); return null; } @@ -201,13 +199,13 @@ class TransactionJournalFactory 'bic' => $row['destination_bic'], 'currency_id' => $currency->id, ]; - Log::debug('Source info:', $sourceInfo); - Log::debug('Destination info:', $destInfo); - Log::debug('Now calling getAccount for the source.'); + app('log')->debug('Source info:', $sourceInfo); + app('log')->debug('Destination info:', $destInfo); + app('log')->debug('Now calling getAccount for the source.'); $sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo); - Log::debug('Now calling getAccount for the destination.'); + app('log')->debug('Now calling getAccount for the destination.'); $destinationAccount = $this->getAccount($type->type, 'destination', $destInfo); - Log::debug('Done with getAccount(2x)'); + app('log')->debug('Done with getAccount(2x)'); // this is the moment for a reconciliation sanity check (again). if (TransactionType::RECONCILIATION === $type->type) { @@ -219,7 +217,7 @@ class TransactionJournalFactory $foreignCurrency = $this->getForeignByAccount($type->type, $foreignCurrency, $destinationAccount); $description = $this->getDescription($description); - Log::debug(sprintf('Date: %s (%s)', $carbon->toW3cString(), $carbon->getTimezone()->getName())); + app('log')->debug(sprintf('Date: %s (%s)', $carbon->toW3cString(), $carbon->getTimezone()->getName())); /** Create a basic journal. */ $journal = TransactionJournal::create( @@ -236,7 +234,7 @@ class TransactionJournalFactory 'completed' => 0, ] ); - Log::debug(sprintf('Created new journal #%d: "%s"', $journal->id, $journal->description)); + app('log')->debug(sprintf('Created new journal #%d: "%s"', $journal->id, $journal->description)); /** Create two transactions. */ $transactionFactory = app(TransactionFactory::class); @@ -250,9 +248,9 @@ class TransactionJournalFactory try { $negative = $transactionFactory->createNegative((string)$row['amount'], (string)$row['foreign_amount']); } catch (FireflyException $e) { - Log::error('Exception creating negative transaction.'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error('Exception creating negative transaction.'); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $this->forceDeleteOnError(new Collection([$journal])); throw new FireflyException($e->getMessage(), 0, $e); } @@ -270,9 +268,9 @@ class TransactionJournalFactory try { $transactionFactory->createPositive((string)$row['amount'], (string)$row['foreign_amount']); } catch (FireflyException $e) { - Log::error('Exception creating positive transaction.'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error('Exception creating positive transaction.'); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); app('log')->warning('Delete negative transaction.'); $this->forceTrDelete($negative); $this->forceDeleteOnError(new Collection([$journal])); @@ -318,7 +316,7 @@ class TransactionJournalFactory unset($dataRow['import_hash_v2'], $dataRow['original_source']); $json = json_encode($dataRow, JSON_THROW_ON_ERROR); $hash = hash('sha256', $json); - Log::debug(sprintf('The hash is: %s', $hash), $dataRow); + app('log')->debug(sprintf('The hash is: %s', $hash), $dataRow); return $hash; } @@ -333,11 +331,11 @@ class TransactionJournalFactory */ private function errorIfDuplicate(string $hash): void { - Log::debug(sprintf('In errorIfDuplicate(%s)', $hash)); + app('log')->debug(sprintf('In errorIfDuplicate(%s)', $hash)); if (false === $this->errorOnHash) { return; } - Log::debug('Will verify duplicate!'); + app('log')->debug('Will verify duplicate!'); /** @var TransactionJournalMeta|null $result */ $result = TransactionJournalMeta::withTrashed() ->where('data', json_encode($hash, JSON_THROW_ON_ERROR)) @@ -362,17 +360,17 @@ class TransactionJournalFactory */ private function validateAccounts(NullArrayObject $data): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $transactionType = $data['type'] ?? 'invalid'; $this->accountValidator->setUser($this->user); $this->accountValidator->setTransactionType($transactionType); // validate source account. $array = [ - 'id' => $data['source_id'] ? (int)$data['source_id'] : null, - 'name' => $data['source_name'] ? (string)$data['source_name'] : null, - 'iban' => $data['source_iban'] ? (string)$data['source_iban'] : null, - 'number' => $data['source_number'] ? (string)$data['source_number'] : null, + 'id' => null !== $data['source_id'] ? (int)$data['source_id'] : null, + 'name' => null !== $data['source_name'] ? (string)$data['source_name'] : null, + 'iban' => null !== $data['source_iban'] ? (string)$data['source_iban'] : null, + 'number' => null !== $data['source_number'] ? (string)$data['source_number'] : null, ]; $validSource = $this->accountValidator->validateSource($array); @@ -380,14 +378,14 @@ class TransactionJournalFactory if (false === $validSource) { throw new FireflyException(sprintf('Source: %s', $this->accountValidator->sourceError)); } - Log::debug('Source seems valid.'); + app('log')->debug('Source seems valid.'); // validate destination account $array = [ - 'id' => $data['destination_id'] ? (int)$data['destination_id'] : null, - 'name' => $data['destination_name'] ? (string)$data['destination_name'] : null, - 'iban' => $data['destination_iban'] ? (string)$data['destination_iban'] : null, - 'number' => $data['destination_number'] ? (string)$data['destination_number'] : null, + 'id' => null !== $data['destination_id'] ? (int)$data['destination_id'] : null, + 'name' => null !== $data['destination_name'] ? (string)$data['destination_name'] : null, + 'iban' => null !== $data['destination_iban'] ? (string)$data['destination_iban'] : null, + 'number' => null !== $data['destination_number'] ? (string)$data['destination_number'] : null, ]; $validDestination = $this->accountValidator->validateDestination($array); @@ -422,25 +420,25 @@ class TransactionJournalFactory */ private function reconciliationSanityCheck(?Account $sourceAccount, ?Account $destinationAccount): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); if (null !== $sourceAccount && null !== $destinationAccount) { - Log::debug('Both accounts exist, simply return them.'); + app('log')->debug('Both accounts exist, simply return them.'); return [$sourceAccount, $destinationAccount]; } - if (null !== $sourceAccount && null === $destinationAccount) { - Log::debug('Destination account is NULL, source account is not.'); + if (null === $destinationAccount) { // @phpstan-ignore-line + app('log')->debug('Destination account is NULL, source account is not.'); $account = $this->accountRepository->getReconciliation($sourceAccount); - Log::debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type)); + app('log')->debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type)); return [$sourceAccount, $account]; } - if (null === $sourceAccount && null !== $destinationAccount) { - Log::debug('Source account is NULL, destination account is not.'); + if (null === $sourceAccount) { // @phpstan-ignore-line + app('log')->debug('Source account is NULL, destination account is not.'); $account = $this->accountRepository->getReconciliation($destinationAccount); - Log::debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type)); + app('log')->debug(sprintf('Will return account #%d ("%s") of type "%s"', $account->id, $account->name, $account->accountType->type)); return [$account, $destinationAccount]; } - Log::debug('Unused fallback'); + app('log')->debug('Unused fallback'); // @phpstan-ignore-line return [$sourceAccount, $destinationAccount]; } @@ -456,7 +454,7 @@ class TransactionJournalFactory */ private function getCurrencyByAccount(string $type, ?TransactionCurrency $currency, Account $source, Account $destination): TransactionCurrency { - Log::debug('Now in getCurrencyByAccount()'); + app('log')->debug('Now in getCurrencyByAccount()'); return match ($type) { default => $this->getCurrency($currency, $source), @@ -474,15 +472,15 @@ class TransactionJournalFactory */ private function getCurrency(?TransactionCurrency $currency, Account $account): TransactionCurrency { - Log::debug('Now in getCurrency()'); - /** @var Preference|null $preference */ + app('log')->debug('Now in getCurrency()'); + /** @var TransactionCurrency|null $preference */ $preference = $this->accountRepository->getAccountCurrency($account); if (null === $preference && null === $currency) { // return user's default: return app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); } - $result = ($preference ?? $currency) ?? app('amount')->getSystemCurrency(); - Log::debug(sprintf('Currency is now #%d (%s) because of account #%d (%s)', $result->id, $result->code, $account->id, $account->name)); + $result = $preference ?? $currency; + app('log')->debug(sprintf('Currency is now #%d (%s) because of account #%d (%s)', $result->id, $result->code, $account->id, $account->name)); return $result; } @@ -545,11 +543,11 @@ class TransactionJournalFactory */ private function forceDeleteOnError(Collection $collection): void { - Log::debug(sprintf('forceDeleteOnError on collection size %d item(s)', $collection->count())); + app('log')->debug(sprintf('forceDeleteOnError on collection size %d item(s)', $collection->count())); $service = app(JournalDestroyService::class); /** @var TransactionJournal $journal */ foreach ($collection as $journal) { - Log::debug(sprintf('forceDeleteOnError on journal #%d', $journal->id)); + app('log')->debug(sprintf('forceDeleteOnError on journal #%d', $journal->id)); $service->destroy($journal); } } @@ -570,17 +568,17 @@ class TransactionJournalFactory */ private function storePiggyEvent(TransactionJournal $journal, NullArrayObject $data): void { - Log::debug('Will now store piggy event.'); + app('log')->debug('Will now store piggy event.'); $piggyBank = $this->piggyRepository->findPiggyBank((int)$data['piggy_bank_id'], $data['piggy_bank_name']); if (null !== $piggyBank) { $this->piggyEventFactory->create($journal, $piggyBank); - Log::debug('Create piggy event.'); + app('log')->debug('Create piggy event.'); return; } - Log::debug('Create no piggy event'); + app('log')->debug('Create no piggy event'); } /** @@ -608,11 +606,11 @@ class TransactionJournalFactory ]; if ($data[$field] instanceof Carbon) { $data[$field]->setTimezone(config('app.timezone')); - Log::debug(sprintf('%s Date: %s (%s)', $field, $data[$field], $data[$field]->timezone->getName())); + app('log')->debug(sprintf('%s Date: %s (%s)', $field, $data[$field], $data[$field]->timezone->getName())); $set['data'] = $data[$field]->format('Y-m-d H:i:s'); } - Log::debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data'])); + app('log')->debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data'])); /** @var TransactionJournalMetaFactory $factory */ $factory = app(TransactionJournalMetaFactory::class); @@ -626,7 +624,7 @@ class TransactionJournalFactory { $this->errorOnHash = $errorOnHash; if (true === $errorOnHash) { - Log::info('Will trigger duplication alert for this journal.'); + app('log')->info('Will trigger duplication alert for this journal.'); } } } diff --git a/app/Factory/TransactionJournalMetaFactory.php b/app/Factory/TransactionJournalMetaFactory.php index 41cfe13971..0a27e92b5f 100644 --- a/app/Factory/TransactionJournalMetaFactory.php +++ b/app/Factory/TransactionJournalMetaFactory.php @@ -25,7 +25,6 @@ namespace FireflyIII\Factory; use Carbon\Carbon; use FireflyIII\Models\TransactionJournalMeta; -use Illuminate\Support\Facades\Log; /** * Class TransactionJournalMetaFactory @@ -39,26 +38,26 @@ class TransactionJournalMetaFactory */ public function updateOrCreate(array $data): ?TransactionJournalMeta { - //Log::debug('In updateOrCreate()'); + //app('log')->debug('In updateOrCreate()'); $value = $data['data']; /** @var TransactionJournalMeta|null $entry */ $entry = $data['journal']->transactionJournalMeta()->where('name', $data['name'])->first(); if (null === $value && null !== $entry) { - //Log::debug('Value is empty, delete meta value.'); + //app('log')->debug('Value is empty, delete meta value.'); $entry->delete(); return null; } if ($data['data'] instanceof Carbon) { - Log::debug('Is a carbon object.'); + app('log')->debug('Is a carbon object.'); $value = $data['data']->toW3cString(); } if ('' === (string)$value) { - // Log::debug('Is an empty string.'); + // app('log')->debug('Is an empty string.'); // don't store blank strings. if (null !== $entry) { - Log::debug('Will not store empty strings, delete meta value'); + app('log')->debug('Will not store empty strings, delete meta value'); $entry->delete(); } @@ -66,13 +65,13 @@ class TransactionJournalMetaFactory } if (null === $entry) { - //Log::debug('Will create new object.'); - Log::debug(sprintf('Going to create new meta-data entry to store "%s".', $data['name'])); + //app('log')->debug('Will create new object.'); + app('log')->debug(sprintf('Going to create new meta-data entry to store "%s".', $data['name'])); $entry = new TransactionJournalMeta(); $entry->transactionJournal()->associate($data['journal']); $entry->name = $data['name']; } - Log::debug('Will update value and return.'); + app('log')->debug('Will update value and return.'); $entry->data = $value; $entry->save(); diff --git a/app/Generator/Report/Account/MonthReportGenerator.php b/app/Generator/Report/Account/MonthReportGenerator.php index 8275835d8c..8c6fc02a19 100644 --- a/app/Generator/Report/Account/MonthReportGenerator.php +++ b/app/Generator/Report/Account/MonthReportGenerator.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -60,8 +59,8 @@ class MonthReportGenerator implements ReportGeneratorInterface ->with('doubles', $this->expense) ->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.double.report: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render reports.double.report: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index 30f5c162c9..8cc4204b61 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -31,7 +31,6 @@ use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; use Throwable; @@ -98,8 +97,8 @@ class MonthReportGenerator implements ReportGeneratorInterface ->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts) ->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.audit.report: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render reports.audit.report: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -135,7 +134,7 @@ class MonthReportGenerator implements ReportGeneratorInterface $journals = array_reverse($journals, true); $dayBeforeBalance = app('steam')->balance($account, $date); $startBalance = $dayBeforeBalance; - $defaultCurrency = app('amount')->getDefaultCurrencyByUser($account->user); + $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); $currency = $accountRepository->getAccountCurrency($account) ?? $defaultCurrency; foreach ($journals as $index => $journal) { diff --git a/app/Generator/Report/Budget/MonthReportGenerator.php b/app/Generator/Report/Budget/MonthReportGenerator.php index 477c2e1f68..3f80611c1f 100644 --- a/app/Generator/Report/Budget/MonthReportGenerator.php +++ b/app/Generator/Report/Budget/MonthReportGenerator.php @@ -29,7 +29,6 @@ use FireflyIII\Generator\Report\ReportGeneratorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -74,8 +73,8 @@ class MonthReportGenerator implements ReportGeneratorInterface ->with('accounts', $this->accounts) ->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.account.report: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render reports.account.report: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -155,7 +154,7 @@ class MonthReportGenerator implements ReportGeneratorInterface protected function getExpenses(): array { if (0 !== count($this->expenses)) { - Log::debug('Return previous set of expenses.'); + app('log')->debug('Return previous set of expenses.'); return $this->expenses; } diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index b7ed3cd4e5..aeca99aa80 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -29,7 +29,6 @@ use FireflyIII\Generator\Report\ReportGeneratorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -76,8 +75,8 @@ class MonthReportGenerator implements ReportGeneratorInterface ->with('accounts', $this->accounts) ->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.category.month: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render reports.category.month: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -155,7 +154,7 @@ class MonthReportGenerator implements ReportGeneratorInterface protected function getExpenses(): array { if (0 !== count($this->expenses)) { - Log::debug('Return previous set of expenses.'); + app('log')->debug('Return previous set of expenses.'); return $this->expenses; } diff --git a/app/Generator/Report/ReportGeneratorInterface.php b/app/Generator/Report/ReportGeneratorInterface.php index c56c71afbd..1040e3c4c2 100644 --- a/app/Generator/Report/ReportGeneratorInterface.php +++ b/app/Generator/Report/ReportGeneratorInterface.php @@ -45,7 +45,7 @@ interface ReportGeneratorInterface * * @return ReportGeneratorInterface */ - public function setAccounts(Collection $accounts): ReportGeneratorInterface; + public function setAccounts(Collection $accounts): self; /** * Set the involved budgets. @@ -54,7 +54,7 @@ interface ReportGeneratorInterface * * @return ReportGeneratorInterface */ - public function setBudgets(Collection $budgets): ReportGeneratorInterface; + public function setBudgets(Collection $budgets): self; /** * Set the involved categories. @@ -63,7 +63,7 @@ interface ReportGeneratorInterface * * @return ReportGeneratorInterface */ - public function setCategories(Collection $categories): ReportGeneratorInterface; + public function setCategories(Collection $categories): self; /** * Set the end date. @@ -72,7 +72,7 @@ interface ReportGeneratorInterface * * @return ReportGeneratorInterface */ - public function setEndDate(Carbon $date): ReportGeneratorInterface; + public function setEndDate(Carbon $date): self; /** * Set the expense accounts. @@ -81,7 +81,7 @@ interface ReportGeneratorInterface * * @return ReportGeneratorInterface */ - public function setExpense(Collection $expense): ReportGeneratorInterface; + public function setExpense(Collection $expense): self; /** * Set the start date. @@ -90,7 +90,7 @@ interface ReportGeneratorInterface * * @return ReportGeneratorInterface */ - public function setStartDate(Carbon $date): ReportGeneratorInterface; + public function setStartDate(Carbon $date): self; /** * Set the tags. @@ -99,5 +99,5 @@ interface ReportGeneratorInterface * * @return ReportGeneratorInterface */ - public function setTags(Collection $tags): ReportGeneratorInterface; + public function setTags(Collection $tags): self; } diff --git a/app/Generator/Report/Standard/MonthReportGenerator.php b/app/Generator/Report/Standard/MonthReportGenerator.php index a79956d9b1..7e1296833a 100644 --- a/app/Generator/Report/Standard/MonthReportGenerator.php +++ b/app/Generator/Report/Standard/MonthReportGenerator.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -58,8 +57,8 @@ class MonthReportGenerator implements ReportGeneratorInterface try { return view('reports.default.month', compact('accountIds', 'reportType'))->with('start', $this->start)->with('end', $this->end)->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.default.month: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render reports.default.month: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render report view.'; throw new FireflyException($result, 0, $e); } diff --git a/app/Generator/Report/Standard/MultiYearReportGenerator.php b/app/Generator/Report/Standard/MultiYearReportGenerator.php index 9647dfd555..e358dc5a82 100644 --- a/app/Generator/Report/Standard/MultiYearReportGenerator.php +++ b/app/Generator/Report/Standard/MultiYearReportGenerator.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -62,8 +61,8 @@ class MultiYearReportGenerator implements ReportGeneratorInterface compact('accountIds', 'reportType') )->with('start', $this->start)->with('end', $this->end)->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.default.multi-year: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render reports.default.multi-year: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } diff --git a/app/Generator/Report/Standard/YearReportGenerator.php b/app/Generator/Report/Standard/YearReportGenerator.php index 8cd9c768eb..d2fcb1c80d 100644 --- a/app/Generator/Report/Standard/YearReportGenerator.php +++ b/app/Generator/Report/Standard/YearReportGenerator.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -62,8 +61,8 @@ class YearReportGenerator implements ReportGeneratorInterface compact('accountIds', 'reportType') )->with('start', $this->start)->with('end', $this->end)->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.account.report: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render reports.account.report: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render report view.'; throw new FireflyException($result, 0, $e); } diff --git a/app/Generator/Report/Tag/MonthReportGenerator.php b/app/Generator/Report/Tag/MonthReportGenerator.php index 29ffb4ce91..8398176550 100644 --- a/app/Generator/Report/Tag/MonthReportGenerator.php +++ b/app/Generator/Report/Tag/MonthReportGenerator.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -70,8 +69,8 @@ class MonthReportGenerator implements ReportGeneratorInterface compact('accountIds', 'reportType', 'tagIds') )->with('start', $this->start)->with('end', $this->end)->with('tags', $this->tags)->with('accounts', $this->accounts)->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.tag.month: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render reports.tag.month: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = sprintf('Could not render report view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } diff --git a/app/Generator/Webhook/StandardMessageGenerator.php b/app/Generator/Webhook/StandardMessageGenerator.php index f71c0a8ecb..c6bcd6c82f 100644 --- a/app/Generator/Webhook/StandardMessageGenerator.php +++ b/app/Generator/Webhook/StandardMessageGenerator.php @@ -37,7 +37,6 @@ use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; use Ramsey\Uuid\Uuid; use Symfony\Component\HttpFoundation\ParameterBag; @@ -67,14 +66,14 @@ class StandardMessageGenerator implements MessageGeneratorInterface */ public function generateMessages(): void { - Log::debug(__METHOD__); + app('log')->debug(__METHOD__); // get the webhooks: if (0 === $this->webhooks->count()) { $this->webhooks = $this->getWebhooks(); } // do some debugging - Log::debug( + app('log')->debug( sprintf('StandardMessageGenerator will generate messages for %d object(s) and %d webhook(s).', $this->objects->count(), $this->webhooks->count()) ); $this->run(); @@ -93,12 +92,12 @@ class StandardMessageGenerator implements MessageGeneratorInterface */ private function run(): void { - Log::debug('Now in StandardMessageGenerator::run'); + app('log')->debug('Now in StandardMessageGenerator::run'); /** @var Webhook $webhook */ foreach ($this->webhooks as $webhook) { $this->runWebhook($webhook); } - Log::debug('Done with StandardMessageGenerator::run'); + app('log')->debug('Done with StandardMessageGenerator::run'); } /** @@ -109,7 +108,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface */ private function runWebhook(Webhook $webhook): void { - Log::debug(sprintf('Now in runWebhook(#%d)', $webhook->id)); + app('log')->debug(sprintf('Now in runWebhook(#%d)', $webhook->id)); /** @var Model $object */ foreach ($this->objects as $object) { $this->generateMessage($webhook, $object); @@ -127,7 +126,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface { $class = get_class($model); // Line is ignored because all of Firefly III's Models have an id property. - Log::debug(sprintf('Now in generateMessage(#%d, %s#%d)', $webhook->id, $class, $model->id)); // @phpstan-ignore-line + app('log')->debug(sprintf('Now in generateMessage(#%d, %s#%d)', $webhook->id, $class, $model->id)); $uuid = Uuid::uuid4(); $basicMessage = [ @@ -144,9 +143,9 @@ class StandardMessageGenerator implements MessageGeneratorInterface switch ($class) { default: // Line is ignored because all of Firefly III's Models have an id property. - Log::error( + app('log')->error( sprintf('Webhook #%d was given %s#%d to deal with but can\'t extract user ID from it.', $webhook->id, $class, $model->id) - ); // @phpstan-ignore-line + ); return; case TransactionGroup::class: @@ -158,7 +157,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface // then depends on the response what to put in the message: switch ($webhook->response) { default: - Log::error( + app('log')->error( sprintf('The response code for webhook #%d is "%d" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response) ); @@ -171,10 +170,10 @@ class StandardMessageGenerator implements MessageGeneratorInterface try { $basicMessage['content'] = $transformer->transformObject($model); } catch (FireflyException $e) { - Log::error( + app('log')->error( sprintf('The transformer could not include the requested transaction group for webhook #%d: %s', $webhook->id, $e->getMessage()) ); - Log::error($e->getTraceAsString()); + app('log')->error($e->getTraceAsString()); return; } @@ -232,7 +231,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface $webhookMessage->uuid = $message['uuid']; $webhookMessage->message = $message; $webhookMessage->save(); - Log::debug(sprintf('Stored new webhook message #%d', $webhookMessage->id)); + app('log')->debug(sprintf('Stored new webhook message #%d', $webhookMessage->id)); } /** diff --git a/app/Handlers/Events/APIEventHandler.php b/app/Handlers/Events/APIEventHandler.php index c1d78be08e..510d90b764 100644 --- a/app/Handlers/Events/APIEventHandler.php +++ b/app/Handlers/Events/APIEventHandler.php @@ -26,7 +26,6 @@ namespace FireflyIII\Handlers\Events; use Exception; use FireflyIII\Notifications\User\NewAccessToken; use FireflyIII\Repositories\User\UserRepositoryInterface; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; use Laravel\Passport\Events\AccessTokenCreated; @@ -43,7 +42,7 @@ class APIEventHandler */ public function accessTokenCreated(AccessTokenCreated $event): void { - Log::debug(__METHOD__); + app('log')->debug(__METHOD__); /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); $user = $repository->find((int)$event->userId); @@ -51,18 +50,18 @@ class APIEventHandler if (null !== $user) { try { Notification::send($user, new NewAccessToken()); - } catch (Exception $e) { + } catch (Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } } diff --git a/app/Handlers/Events/AdminEventHandler.php b/app/Handlers/Events/AdminEventHandler.php index acf73ff5a4..e4f7c5e36b 100644 --- a/app/Handlers/Events/AdminEventHandler.php +++ b/app/Handlers/Events/AdminEventHandler.php @@ -31,8 +31,6 @@ use FireflyIII\Notifications\Admin\TestNotification; use FireflyIII\Notifications\Admin\UserInvitation; use FireflyIII\Notifications\Admin\VersionCheckResult; use FireflyIII\Repositories\User\UserRepositoryInterface; -use FireflyIII\Support\Facades\FireflyConfig; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; /** @@ -47,7 +45,7 @@ class AdminEventHandler */ public function sendInvitationNotification(InvitationCreated $event): void { - $sendMail = FireflyConfig::get('notification_invite_created', true)->data; + $sendMail = app('fireflyconfig')->get('notification_invite_created', true)->data; if (false === $sendMail) { return; } @@ -59,18 +57,18 @@ class AdminEventHandler if ($repository->hasRole($user, 'owner')) { try { Notification::send($user, new UserInvitation($event->invitee)); - } catch (Exception $e) { + } catch (Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } } @@ -85,7 +83,7 @@ class AdminEventHandler */ public function sendNewVersion(NewVersionAvailable $event): void { - $sendMail = FireflyConfig::get('notification_new_version', true)->data; + $sendMail = app('fireflyconfig')->get('notification_new_version', true)->data; if (false === $sendMail) { return; } @@ -97,18 +95,18 @@ class AdminEventHandler if ($repository->hasRole($user, 'owner')) { try { Notification::send($user, new VersionCheckResult($event->message)); - } catch (Exception $e) { + } catch (Exception $e) {// @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } } @@ -131,18 +129,18 @@ class AdminEventHandler } try { Notification::send($event->user, new TestNotification($event->user->email)); - } catch (Exception $e) { + } catch (Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } } diff --git a/app/Handlers/Events/AutomationHandler.php b/app/Handlers/Events/AutomationHandler.php index defb50f90f..e7e8dc68b8 100644 --- a/app/Handlers/Events/AutomationHandler.php +++ b/app/Handlers/Events/AutomationHandler.php @@ -30,7 +30,6 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Notifications\User\TransactionCreation; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Transformers\TransactionGroupTransformer; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; /** @@ -47,23 +46,24 @@ class AutomationHandler */ public function reportJournals(RequestedReportOnJournals $event): void { - Log::debug('In reportJournals.'); + app('log')->debug('In reportJournals.'); /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); $user = $repository->find($event->userId); + /** @var bool $sendReport */ $sendReport = app('preferences')->getForUser($user, 'notification_transaction_creation', false)->data; if (false === $sendReport) { - Log::debug('Not sending report, because config says so.'); + app('log')->debug('Not sending report, because config says so.'); return; } if (null === $user || 0 === $event->groups->count()) { - Log::debug('No transaction groups in event, nothing to email about.'); + app('log')->debug('No transaction groups in event, nothing to email about.'); return; } - Log::debug('Continue with message!'); + app('log')->debug('Continue with message!'); // transform groups into array: /** @var TransactionGroupTransformer $transformer */ @@ -75,19 +75,19 @@ class AutomationHandler } try { Notification::send($user, new TransactionCreation($groups)); - } catch (Exception $e) { + } catch (Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } - Log::debug('If there is no error above this line, message was sent.'); + app('log')->debug('If there is no error above this line, message was sent.'); } } diff --git a/app/Handlers/Events/BillEventHandler.php b/app/Handlers/Events/BillEventHandler.php index f360c1e4a6..d6864d095d 100644 --- a/app/Handlers/Events/BillEventHandler.php +++ b/app/Handlers/Events/BillEventHandler.php @@ -27,8 +27,6 @@ namespace FireflyIII\Handlers\Events; use Exception; use FireflyIII\Events\WarnUserAboutBill; use FireflyIII\Notifications\User\BillReminder; -use FireflyIII\Support\Facades\Preferences; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; /** @@ -43,32 +41,32 @@ class BillEventHandler */ public function warnAboutBill(WarnUserAboutBill $event): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $bill = $event->bill; /** @var bool $preference */ - $preference = Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data; + $preference = app('preferences')->getForUser($bill->user, 'notification_bill_reminder', true)->data; if (true === $preference) { - Log::debug('Bill reminder is true!'); + app('log')->debug('Bill reminder is true!'); try { Notification::send($bill->user, new BillReminder($bill, $event->field, $event->diff)); - } catch (Exception $e) { + } catch (Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } if (false === $preference) { - Log::debug('User has disabled bill reminders.'); + app('log')->debug('User has disabled bill reminders.'); } } } diff --git a/app/Handlers/Events/DestroyedGroupEventHandler.php b/app/Handlers/Events/DestroyedGroupEventHandler.php index a7948333ff..72baf3f8b1 100644 --- a/app/Handlers/Events/DestroyedGroupEventHandler.php +++ b/app/Handlers/Events/DestroyedGroupEventHandler.php @@ -28,7 +28,6 @@ use FireflyIII\Events\DestroyedTransactionGroup; use FireflyIII\Events\RequestedSendWebhookMessages; use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class DestroyedGroupEventHandler @@ -40,7 +39,7 @@ class DestroyedGroupEventHandler */ public function triggerWebhooks(DestroyedTransactionGroup $destroyedGroupEvent): void { - Log::debug('DestroyedTransactionGroup:triggerWebhooks'); + app('log')->debug('DestroyedTransactionGroup:triggerWebhooks'); $group = $destroyedGroupEvent->transactionGroup; $user = $group->user; /** @var MessageGeneratorInterface $engine */ diff --git a/app/Handlers/Events/Model/BudgetLimitHandler.php b/app/Handlers/Events/Model/BudgetLimitHandler.php index 8abe31998a..52d9a8f7d3 100644 --- a/app/Handlers/Events/Model/BudgetLimitHandler.php +++ b/app/Handlers/Events/Model/BudgetLimitHandler.php @@ -32,7 +32,7 @@ use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; -use Illuminate\Support\Facades\Log; +use FireflyIII\User; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Spatie\Period\Boundaries; @@ -51,7 +51,7 @@ class BudgetLimitHandler */ public function created(Created $event): void { - Log::debug(sprintf('BudgetLimitHandler::created(#%s)', $event->budgetLimit->id)); + app('log')->debug(sprintf('BudgetLimitHandler::created(#%s)', $event->budgetLimit->id)); $this->updateAvailableBudget($event->budgetLimit); } @@ -62,22 +62,23 @@ class BudgetLimitHandler */ private function updateAvailableBudget(BudgetLimit $budgetLimit): void { - Log::debug(sprintf('Now in updateAvailableBudget(#%d)', $budgetLimit->id)); + app('log')->debug(sprintf('Now in updateAvailableBudget(#%d)', $budgetLimit->id)); $budget = Budget::find($budgetLimit->budget_id); if (null === $budget) { - Log::warning('Budget is null, probably deleted, find deleted version.'); + app('log')->warning('Budget is null, probably deleted, find deleted version.'); $budget = Budget::withTrashed()->find($budgetLimit->budget_id); } if (null === $budget) { - Log::warning('Budget is still null, cannot continue, will delete budget limit.'); + app('log')->warning('Budget is still null, cannot continue, will delete budget limit.'); $budgetLimit->forceDelete(); return; } + /** @var User|null $user */ $user = $budget->user; // sanity check. It happens when the budget has been deleted so the original user is unknown. if (null === $user) { - Log::warning('User is null, cannot continue.'); + app('log')->warning('User is null, cannot continue.'); $budgetLimit->forceDelete(); return; } @@ -91,6 +92,11 @@ class BudgetLimitHandler app('log')->error($e->getMessage()); $viewRange = '1M'; } + // safety catch + if (null === $viewRange || is_array($viewRange)) { + $viewRange = '1M'; + } + $viewRange = (string)$viewRange; $start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange); $end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange); @@ -106,14 +112,14 @@ class BudgetLimitHandler $currentEnd = app('navigation')->endOfPeriod($current, $viewRange); // create or find AB for this particular period, and set the amount accordingly. - /** @var AvailableBudget $availableBudget */ + /** @var AvailableBudget|null $availableBudget */ $availableBudget = $user->availableBudgets()->where('start_date', $current->format('Y-m-d'))->where( 'end_date', $currentEnd->format('Y-m-d') )->where('transaction_currency_id', $budgetLimit->transaction_currency_id)->first(); if (null !== $availableBudget) { - Log::debug('Found 1 AB, will update.'); + app('log')->debug('Found 1 AB, will update.'); $this->calculateAmount($availableBudget); } if (null === $availableBudget) { @@ -124,13 +130,13 @@ class BudgetLimitHandler // no need to calculate if period is equal. if ($currentPeriod->equals($limitPeriod)) { - $amount = 0 === (int)$budgetLimit->id ? '0' : $budgetLimit->amount; + $amount = 0 === $budgetLimit->id ? '0' : $budgetLimit->amount; } if (0 === bccomp($amount, '0')) { - Log::debug('Amount is zero, will not create AB.'); + app('log')->debug('Amount is zero, will not create AB.'); } if (0 !== bccomp($amount, '0')) { - Log::debug(sprintf('Will create AB for period %s to %s', $current->format('Y-m-d'), $currentEnd->format('Y-m-d'))); + app('log')->debug(sprintf('Will create AB for period %s to %s', $current->format('Y-m-d'), $currentEnd->format('Y-m-d'))); $availableBudget = new AvailableBudget( [ 'user_id' => $budgetLimit->budget->user->id, @@ -161,7 +167,7 @@ class BudgetLimitHandler $repository->setUser($availableBudget->user); $newAmount = '0'; $abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY()); - Log::debug( + app('log')->debug( sprintf( 'Now at AB #%d, ("%s" to "%s")', $availableBudget->id, @@ -171,10 +177,10 @@ class BudgetLimitHandler ); // have to recalculate everything just in case. $set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date); - Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count())); + app('log')->debug(sprintf('Found %d interesting budget limit(s).', $set->count())); /** @var BudgetLimit $budgetLimit */ foreach ($set as $budgetLimit) { - Log::debug( + app('log')->debug( sprintf( 'Found interesting budget limit #%d ("%s" to "%s")', $budgetLimit->id, @@ -210,11 +216,11 @@ class BudgetLimitHandler } } if (0 === bccomp('0', $newAmount)) { - Log::debug('New amount is zero, deleting AB.'); + app('log')->debug('New amount is zero, deleting AB.'); $availableBudget->delete(); return; } - Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount)); + app('log')->debug(sprintf('Concluded new amount for this AB must be %s', $newAmount)); $availableBudget->amount = app('steam')->bcround($newAmount, $availableBudget->transactionCurrency->decimal_places); $availableBudget->save(); } @@ -226,7 +232,7 @@ class BudgetLimitHandler */ private function getDailyAmount(BudgetLimit $budgetLimit): string { - if (0 === (int)$budgetLimit->id) { + if (0 === $budgetLimit->id) { return '0'; } $limitPeriod = Period::make( @@ -236,8 +242,8 @@ class BudgetLimitHandler boundaries: Boundaries::EXCLUDE_NONE() ); $days = $limitPeriod->length(); - $amount = bcdiv((string)$budgetLimit->amount, (string)$days, 12); - Log::debug( + $amount = bcdiv($budgetLimit->amount, (string)$days, 12); + app('log')->debug( sprintf('Total amount for budget limit #%d is %s. Nr. of days is %d. Amount per day is %s', $budgetLimit->id, $budgetLimit->amount, $days, $amount) ); return $amount; @@ -250,9 +256,9 @@ class BudgetLimitHandler */ public function deleted(Deleted $event): void { - Log::debug(sprintf('BudgetLimitHandler::deleted(#%s)', $event->budgetLimit->id)); + app('log')->debug(sprintf('BudgetLimitHandler::deleted(#%s)', $event->budgetLimit->id)); $budgetLimit = $event->budgetLimit; - $budgetLimit->id = null; + $budgetLimit->id = 0; $this->updateAvailableBudget($event->budgetLimit); } @@ -263,7 +269,7 @@ class BudgetLimitHandler */ public function updated(Updated $event): void { - Log::debug(sprintf('BudgetLimitHandler::updated(#%s)', $event->budgetLimit->id)); + app('log')->debug(sprintf('BudgetLimitHandler::updated(#%s)', $event->budgetLimit->id)); $this->updateAvailableBudget($event->budgetLimit); } diff --git a/app/Handlers/Events/Model/PiggyBankEventHandler.php b/app/Handlers/Events/Model/PiggyBankEventHandler.php index 5eca00f652..b7ac122910 100644 --- a/app/Handlers/Events/Model/PiggyBankEventHandler.php +++ b/app/Handlers/Events/Model/PiggyBankEventHandler.php @@ -26,7 +26,6 @@ namespace FireflyIII\Handlers\Events\Model; use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Models\PiggyBankEvent; -use Illuminate\Support\Facades\Log; /** * Class PiggyBankEventHandler @@ -52,7 +51,7 @@ class PiggyBankEventHandler ->where('transaction_journal_id', $journal->id) ->exists(); if ($exists) { - Log::warning('Already have event for this journal and piggy, will not create another.'); + app('log')->warning('Already have event for this journal and piggy, will not create another.'); return; } } diff --git a/app/Handlers/Events/Model/RuleHandler.php b/app/Handlers/Events/Model/RuleHandler.php index 99f9aa208b..c9a9985bf2 100644 --- a/app/Handlers/Events/Model/RuleHandler.php +++ b/app/Handlers/Events/Model/RuleHandler.php @@ -28,7 +28,6 @@ namespace FireflyIII\Handlers\Events\Model; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject; use FireflyIII\Notifications\User\RuleActionFailed; -use FireflyIII\Support\Facades\Preferences; use Illuminate\Support\Facades\Notification; /** @@ -45,7 +44,8 @@ class RuleHandler { $ruleAction = $event->ruleAction; $rule = $ruleAction->rule; - $preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data; + /** @var bool $preference */ + $preference = app('preferences')->getForUser($rule->user, 'notification_rule_action_failures', true)->data; if (false === $preference) { return; } @@ -74,7 +74,8 @@ class RuleHandler { $ruleAction = $event->ruleAction; $rule = $ruleAction->rule; - $preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data; + /** @var bool $preference */ + $preference = app('preferences')->getForUser($rule->user, 'notification_rule_action_failures', true)->data; if (false === $preference) { return; } diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index 853f299fb2..71f3e6752a 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -32,7 +32,6 @@ use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\TransactionRules\Engine\RuleEngineInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class StoredGroupEventHandler @@ -47,11 +46,11 @@ class StoredGroupEventHandler public function processRules(StoredTransactionGroup $storedGroupEvent): void { if (false === $storedGroupEvent->applyRules) { - Log::info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id)); + app('log')->info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id)); return; } - Log::debug('Now in StoredGroupEventHandler::processRules()'); + app('log')->debug('Now in StoredGroupEventHandler::processRules()'); $journals = $storedGroupEvent->transactionGroup->transactionJournals; $array = []; @@ -60,7 +59,7 @@ class StoredGroupEventHandler $array[] = $journal->id; } $journalIds = implode(',', $array); - Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); + app('log')->debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: $ruleGroupRepository = app(RuleGroupRepositoryInterface::class); @@ -97,10 +96,10 @@ class StoredGroupEventHandler */ public function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void { - Log::debug(__METHOD__); + app('log')->debug(__METHOD__); $group = $storedGroupEvent->transactionGroup; if (false === $storedGroupEvent->fireWebhooks) { - Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); + app('log')->info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); return; } diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index 991edabd6d..a1312828d6 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -35,7 +35,6 @@ use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\TransactionRules\Engine\RuleEngineInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class UpdatedGroupEventHandler @@ -50,7 +49,7 @@ class UpdatedGroupEventHandler public function processRules(UpdatedTransactionGroup $updatedGroupEvent): void { if (false === $updatedGroupEvent->applyRules) { - Log::info(sprintf('Will not run rules on group #%d', $updatedGroupEvent->transactionGroup->id)); + app('log')->info(sprintf('Will not run rules on group #%d', $updatedGroupEvent->transactionGroup->id)); return; } @@ -62,7 +61,7 @@ class UpdatedGroupEventHandler $array[] = $journal->id; } $journalIds = implode(',', $array); - Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); + app('log')->debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: $ruleGroupRepository = app(RuleGroupRepositoryInterface::class); @@ -95,10 +94,10 @@ class UpdatedGroupEventHandler */ public function triggerWebhooks(UpdatedTransactionGroup $updatedGroupEvent): void { - Log::debug(__METHOD__); + app('log')->debug(__METHOD__); $group = $updatedGroupEvent->transactionGroup; if (false === $updatedGroupEvent->fireWebhooks) { - Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); + app('log')->info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); return; } diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index 6f534d04a1..9cf9b577fe 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -45,10 +45,8 @@ use FireflyIII\Notifications\User\UserLogin; use FireflyIII\Notifications\User\UserNewPassword; use FireflyIII\Notifications\User\UserRegistration as UserRegistrationNotification; use FireflyIII\Repositories\User\UserRepositoryInterface; -use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\User; use Illuminate\Auth\Events\Login; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; use Mail; @@ -73,7 +71,7 @@ class UserEventHandler // first user ever? if (1 === $repository->count()) { - Log::debug('User count is one, attach role.'); + app('log')->debug('User count is one, attach role.'); $repository->attachRole($event->user, 'owner'); } } @@ -99,10 +97,10 @@ class UserEventHandler if (null === $role) { // create role, does not exist. Very strange situation so let's raise a big fuss about it. $role = $repository->createRole('owner', 'Site Owner', 'User runs this instance of FF3'); - Log::error('Could not find role "owner". This is weird.'); + app('log')->error('Could not find role "owner". This is weird.'); } - Log::info(sprintf('Gave user #%d role #%d ("%s")', $user->id, $role->id, $role->name)); + app('log')->info(sprintf('Gave user #%d role #%d ("%s")', $user->id, $role->id, $role->name)); // give user the role $repository->attachRole($user, 'owner'); } @@ -110,6 +108,7 @@ class UserEventHandler /** * @param RegisteredUser $event + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function createExchangeRates(RegisteredUser $event): void { @@ -132,7 +131,7 @@ class UserEventHandler $group = null; // create a new group. - while (true === $groupExists) { + while (true === $groupExists) { // @phpstan-ignore-line $groupExists = UserGroup::where('title', $groupTitle)->count() > 0; if (false === $groupExists) { $group = UserGroup::create(['title' => $groupTitle]); @@ -190,7 +189,6 @@ class UserEventHandler public function notifyNewIPAddress(DetectedNewIPAddress $event): void { $user = $event->user; - $email = $user->email; $ipAddress = $event->ipAddress; if ($user->hasRole('demo')) { @@ -198,24 +196,27 @@ class UserEventHandler } $list = app('preferences')->getForUser($user, 'login_ip_history', [])->data; + if (!is_array($list)) { + $list = []; + } /** @var array $entry */ foreach ($list as $index => $entry) { if (false === $entry['notified']) { try { Notification::send($user, new UserLogin($ipAddress)); - } catch (Exception $e) { + } catch (Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } $list[$index]['notified'] = true; @@ -229,7 +230,7 @@ class UserEventHandler */ public function sendAdminRegistrationNotification(RegisteredUser $event): void { - $sendMail = FireflyConfig::get('notification_admin_new_reg', true)->data; + $sendMail = (bool)app('fireflyconfig')->get('notification_admin_new_reg', true)->data; if ($sendMail) { /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); @@ -238,18 +239,18 @@ class UserEventHandler if ($repository->hasRole($user, 'owner')) { try { Notification::send($user, new AdminRegistrationNotification($event->user)); - } catch (Exception $e) { + } catch (Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } } @@ -274,9 +275,9 @@ class UserEventHandler try { Mail::to($newEmail)->send(new ConfirmEmailChangeMail($newEmail, $oldEmail, $url)); - } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + } catch (Exception $e) { + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException($e->getMessage(), 0, $e); } } @@ -299,9 +300,9 @@ class UserEventHandler $url = route('profile.undo-email-change', [$token->data, $hashed]); try { Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $url)); - } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + } catch (Exception $e) { + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException($e->getMessage(), 0, $e); } } @@ -315,18 +316,18 @@ class UserEventHandler { try { Notification::send($event->user, new UserNewPassword(route('password.reset', [$event->token]))); - } catch (Exception $e) { + } catch (Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } @@ -343,9 +344,9 @@ class UserEventHandler $url = route('invite', [$event->invitee->invite_code]); try { Mail::to($invitee)->send(new InvitationMail($invitee, $admin, $url)); - } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + } catch (Exception $e) { + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException($e->getMessage(), 0, $e); } } @@ -359,22 +360,22 @@ class UserEventHandler */ public function sendRegistrationMail(RegisteredUser $event): void { - $sendMail = FireflyConfig::get('notification_user_new_reg', true)->data; + $sendMail = (bool)app('fireflyconfig')->get('notification_user_new_reg', true)->data; if ($sendMail) { try { Notification::send($event->user, new UserRegistrationNotification()); - } catch (Exception $e) { + } catch (Exception $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } } @@ -386,11 +387,11 @@ class UserEventHandler */ public function storeUserIPAddress(ActuallyLoggedIn $event): void { - Log::debug('Now in storeUserIPAddress'); + app('log')->debug('Now in storeUserIPAddress'); $user = $event->user; if ($user->hasRole('demo')) { - Log::debug('Do not log demo user logins'); + app('log')->debug('Do not log demo user logins'); return; } @@ -399,25 +400,25 @@ class UserEventHandler $preference = app('preferences')->getForUser($user, 'login_ip_history', [])->data; } catch (FireflyException $e) { // don't care. - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); return; } $inArray = false; $ip = request()->ip(); - Log::debug(sprintf('User logging in from IP address %s', $ip)); + app('log')->debug(sprintf('User logging in from IP address %s', $ip)); // update array if in array foreach ($preference as $index => $row) { if ($row['ip'] === $ip) { - Log::debug('Found IP in array, refresh time.'); + app('log')->debug('Found IP in array, refresh time.'); $preference[$index]['time'] = now(config('app.timezone'))->format('Y-m-d H:i:s'); $inArray = true; } // clean up old entries (6 months) $carbon = Carbon::createFromFormat('Y-m-d H:i:s', $preference[$index]['time']); - if ($carbon->diffInMonths(today()) > 6) { - Log::debug(sprintf('Entry for %s is very old, remove it.', $row['ip'])); + if (false !== $carbon && $carbon->diffInMonths(today()) > 6) { + app('log')->debug(sprintf('Entry for %s is very old, remove it.', $row['ip'])); unset($preference[$index]); } } diff --git a/app/Handlers/Events/VersionCheckEventHandler.php b/app/Handlers/Events/VersionCheckEventHandler.php index 5b39792b23..256e4f4f89 100644 --- a/app/Handlers/Events/VersionCheckEventHandler.php +++ b/app/Handlers/Events/VersionCheckEventHandler.php @@ -28,7 +28,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Update\UpdateTrait; use FireflyIII\Models\Configuration; use FireflyIII\Repositories\User\UserRepositoryInterface; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -51,13 +50,13 @@ class VersionCheckEventHandler */ public function checkForUpdates(RequestedVersionCheckStatus $event): void { - Log::debug('Now in checkForUpdates()'); + app('log')->debug('Now in checkForUpdates()'); // should not check for updates: $permission = app('fireflyconfig')->get('permission_update_check', -1); $value = (int)$permission->data; if (1 !== $value) { - Log::debug('Update check is not enabled.'); + app('log')->debug('Update check is not enabled.'); $this->warnToCheckForUpdates($event); return; @@ -67,7 +66,7 @@ class VersionCheckEventHandler $repository = app(UserRepositoryInterface::class); $user = $event->user; if (!$repository->hasRole($user, 'owner')) { - Log::debug('User is not admin, done.'); + app('log')->debug('User is not admin, done.'); return; } @@ -76,14 +75,14 @@ class VersionCheckEventHandler $lastCheckTime = app('fireflyconfig')->get('last_update_check', time()); $now = time(); $diff = $now - $lastCheckTime->data; - Log::debug(sprintf('Last check time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff)); + app('log')->debug(sprintf('Last check time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff)); if ($diff < 604800) { - Log::debug(sprintf('Checked for updates less than a week ago (on %s).', date('Y-m-d H:i:s', $lastCheckTime->data))); + app('log')->debug(sprintf('Checked for updates less than a week ago (on %s).', date('Y-m-d H:i:s', $lastCheckTime->data))); return; } // last check time was more than a week ago. - Log::debug('Have not checked for a new version in a week!'); + app('log')->debug('Have not checked for a new version in a week!'); $release = $this->getLatestRelease(); session()->flash($release['level'], $release['message']); @@ -103,7 +102,7 @@ class VersionCheckEventHandler $repository = app(UserRepositoryInterface::class); $user = $event->user; if (!$repository->hasRole($user, 'owner')) { - Log::debug('User is not admin, done.'); + app('log')->debug('User is not admin, done.'); return; } @@ -112,14 +111,14 @@ class VersionCheckEventHandler $lastCheckTime = app('fireflyconfig')->get('last_update_warning', time()); $now = time(); $diff = $now - $lastCheckTime->data; - Log::debug(sprintf('Last warning time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff)); + app('log')->debug(sprintf('Last warning time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff)); if ($diff < 604800 * 4) { - Log::debug(sprintf('Warned about updates less than four weeks ago (on %s).', date('Y-m-d H:i:s', $lastCheckTime->data))); + app('log')->debug(sprintf('Warned about updates less than four weeks ago (on %s).', date('Y-m-d H:i:s', $lastCheckTime->data))); return; } // last check time was more than a week ago. - Log::debug('Have warned about a new version in four weeks!'); + app('log')->debug('Have warned about a new version in four weeks!'); session()->flash('info', (string)trans('firefly.disabled_but_check')); app('fireflyconfig')->set('last_update_warning', time()); diff --git a/app/Handlers/Events/WebhookEventHandler.php b/app/Handlers/Events/WebhookEventHandler.php index c678510606..ea9236e191 100644 --- a/app/Handlers/Events/WebhookEventHandler.php +++ b/app/Handlers/Events/WebhookEventHandler.php @@ -25,7 +25,6 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Jobs\SendWebhookMessage; use FireflyIII\Models\WebhookMessage; -use Illuminate\Support\Facades\Log; /** * Class WebhookEventHandler @@ -37,23 +36,23 @@ class WebhookEventHandler */ public function sendWebhookMessages(): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); // kick off the job! $messages = WebhookMessage::where('webhook_messages.sent', false) ->get(['webhook_messages.*']) ->filter( - function (WebhookMessage $message) { + static function (WebhookMessage $message) { return $message->webhookAttempts()->count() <= 2; } )->splice(0, 5); - Log::debug(sprintf('Found %d webhook message(s) ready to be send.', $messages->count())); + app('log')->debug(sprintf('Found %d webhook message(s) ready to be send.', $messages->count())); foreach ($messages as $message) { if (false === $message->sent) { - Log::debug(sprintf('Send message #%d', $message->id)); + app('log')->debug(sprintf('Send message #%d', $message->id)); SendWebhookMessage::dispatch($message)->afterResponse(); } if (false !== $message->sent) { - Log::debug(sprintf('Skip message #%d', $message->id)); + app('log')->debug(sprintf('Skip message #%d', $message->id)); } } } diff --git a/app/Handlers/Observer/PiggyBankObserver.php b/app/Handlers/Observer/PiggyBankObserver.php index e448e8378b..999aa831f8 100644 --- a/app/Handlers/Observer/PiggyBankObserver.php +++ b/app/Handlers/Observer/PiggyBankObserver.php @@ -43,7 +43,7 @@ class PiggyBankObserver $repetition->piggyBank()->associate($piggyBank); $repetition->startdate = $piggyBank->startdate; $repetition->targetdate = $piggyBank->targetdate; - $repetition->currentamount = 0; + $repetition->currentamount = '0'; $repetition->save(); } diff --git a/app/Handlers/Observer/TransactionJournalObserver.php b/app/Handlers/Observer/TransactionJournalObserver.php index c24f1089da..bab42543d0 100644 --- a/app/Handlers/Observer/TransactionJournalObserver.php +++ b/app/Handlers/Observer/TransactionJournalObserver.php @@ -40,7 +40,7 @@ class TransactionJournalObserver app('log')->debug('Observe "deleting" of a transaction journal.'); // to make sure the listener doesn't get back to use and loop - TransactionJournal::withoutEvents(function () use ($transactionJournal) { + TransactionJournal::withoutEvents(static function () use ($transactionJournal) { foreach ($transactionJournal->transactions()->get() as $transaction) { $transaction->delete(); } diff --git a/app/Handlers/Observer/TransactionObserver.php b/app/Handlers/Observer/TransactionObserver.php index d1d117f1a0..8b20b95d65 100644 --- a/app/Handlers/Observer/TransactionObserver.php +++ b/app/Handlers/Observer/TransactionObserver.php @@ -30,7 +30,12 @@ use FireflyIII\Models\Transaction; */ class TransactionObserver { - public function deleting(Transaction $transaction): void + /** + * @param Transaction|null $transaction + * + * @return void + */ + public function deleting(?Transaction $transaction): void { app('log')->debug('Observe "deleting" of a transaction.'); $transaction?->transactionJournal?->delete(); diff --git a/app/Handlers/Observer/WebhookObserver.php b/app/Handlers/Observer/WebhookObserver.php index dd19ebacb5..d7dd0dbca9 100644 --- a/app/Handlers/Observer/WebhookObserver.php +++ b/app/Handlers/Observer/WebhookObserver.php @@ -38,7 +38,7 @@ class WebhookObserver public function deleting(Webhook $webhook): void { app('log')->debug('Observe "deleting" of a webhook.'); - foreach ($webhook->webhookMessages() as $message) { + foreach ($webhook->webhookMessages()->get() as $message) { $message->delete(); } } diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index 8c9c038992..380e437666 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -36,6 +36,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Illuminate\Support\MessageBag; use Symfony\Component\HttpFoundation\File\UploadedFile; +use const DIRECTORY_SEPARATOR; /** * Class AttachmentHelper. @@ -48,8 +49,7 @@ class AttachmentHelper implements AttachmentHelperInterface protected array $allowedMimes = []; protected int $maxUploadSize = 0; - /** @var Filesystem The disk where attachments are stored. */ - protected $uploadDisk; + protected Filesystem $uploadDisk; /** * AttachmentHelper constructor. @@ -96,7 +96,7 @@ class AttachmentHelper implements AttachmentHelperInterface */ public function getAttachmentLocation(Attachment $attachment): string { - return sprintf('%sat-%d.data', DIRECTORY_SEPARATOR, (int)$attachment->id); + return sprintf('%sat-%d.data', DIRECTORY_SEPARATOR, $attachment->id); } /** @@ -139,6 +139,7 @@ class AttachmentHelper implements AttachmentHelperInterface */ public function saveAttachmentFromApi(Attachment $attachment, string $content): bool { + Log::debug(sprintf('Now in %s', __METHOD__)); $resource = tmpfile(); if (false === $resource) { Log::error('Cannot create temp-file for file upload.'); @@ -153,9 +154,20 @@ class AttachmentHelper implements AttachmentHelperInterface } $path = stream_get_meta_data($resource)['uri']; - fwrite($resource, $content); - $finfo = finfo_open(FILEINFO_MIME_TYPE); - $mime = finfo_file($finfo, $path); + Log::debug(sprintf('Path is %s', $path)); + $result = fwrite($resource, $content); + if (false === $result) { + Log::error('Could not write temp file.'); + return false; + } + Log::debug(sprintf('Wrote %d bytes to temp file.', $result)); + $finfo = finfo_open(FILEINFO_MIME_TYPE); + if (false === $finfo) { + Log::error('Could not open finfo.'); + fclose($resource); + return false; + } + $mime = (string)finfo_file($finfo, $path); $allowedMime = config('firefly.allowedMimes'); if (!in_array($mime, $allowedMime, true)) { Log::error(sprintf('Mime type %s is not allowed for API file upload.', $mime)); @@ -163,18 +175,22 @@ class AttachmentHelper implements AttachmentHelperInterface return false; } + Log::debug(sprintf('Found mime "%s" in file "%s"', $mime, $path)); // is allowed? Save the file, without encryption. $parts = explode('/', $attachment->fileName()); $file = $parts[count($parts) - 1]; + Log::debug(sprintf('Write file to disk in file named "%s"', $file)); $this->uploadDisk->put($file, $content); // update attachment. - $attachment->md5 = md5_file($path); + $attachment->md5 = (string)md5_file($path); $attachment->mime = $mime; $attachment->size = strlen($content); $attachment->uploaded = true; $attachment->save(); + Log::debug('Done!'); + return true; } @@ -196,7 +212,7 @@ class AttachmentHelper implements AttachmentHelperInterface Log::debug(sprintf('Now in saveAttachmentsForModel for model %s', get_class($model))); if (is_array($files)) { Log::debug('$files is an array.'); - /** @var UploadedFile $entry */ + /** @var UploadedFile|null $entry */ foreach ($files as $entry) { if (null !== $entry) { $this->processFile($entry, $model); @@ -227,7 +243,7 @@ class AttachmentHelper implements AttachmentHelperInterface $validation = $this->validateUpload($file, $model); $attachment = null; if (false !== $validation) { - $user = $model->user; // @phpstan-ignore-line + $user = $model->user; // ignore lines about polymorphic calls. if ($model instanceof PiggyBank) { $user = $model->account->user; @@ -236,7 +252,7 @@ class AttachmentHelper implements AttachmentHelperInterface $attachment = new Attachment(); // create Attachment object. $attachment->user()->associate($user); $attachment->attachable()->associate($model); - $attachment->md5 = md5_file($file->getRealPath()); + $attachment->md5 = (string)md5_file($file->getRealPath()); $attachment->filename = $file->getClientOriginalName(); $attachment->mime = $file->getMimeType(); $attachment->size = $file->getSize(); @@ -251,7 +267,7 @@ class AttachmentHelper implements AttachmentHelperInterface throw new FireflyException('Cannot upload empty or non-existent file.'); } - $content = $fileObject->fread($file->getSize()); + $content = (string)$fileObject->fread($file->getSize()); Log::debug(sprintf('Full file length is %d and upload size is %d.', strlen($content), $file->getSize())); // store it without encryption. @@ -371,7 +387,7 @@ class AttachmentHelper implements AttachmentHelperInterface $count = $model->account->user->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count(); } if (!($model instanceof PiggyBank)) { - $count = $model->user->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count(); // @phpstan-ignore-line + $count = $model->user->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count(); } $result = false; if ($count > 0) { diff --git a/app/Helpers/Collector/Extensions/AccountCollection.php b/app/Helpers/Collector/Extensions/AccountCollection.php index afb2466ea4..2981ae44dc 100644 --- a/app/Helpers/Collector/Extensions/AccountCollection.php +++ b/app/Helpers/Collector/Extensions/AccountCollection.php @@ -103,7 +103,7 @@ trait AccountCollection if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->where( - static function (EloquentBuilder $query) use ($accountIds) { + static function (EloquentBuilder $query) use ($accountIds) { // @phpstan-ignore-line $query->whereIn('source.account_id', $accountIds); $query->orWhereIn('destination.account_id', $accountIds); } @@ -126,7 +126,7 @@ trait AccountCollection if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->where( - static function (EloquentBuilder $query) use ($accountIds) { + static function (EloquentBuilder $query) use ($accountIds) { // @phpstan-ignore-line $query->whereIn('source.account_id', $accountIds); $query->whereIn('destination.account_id', $accountIds); } @@ -168,7 +168,7 @@ trait AccountCollection if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->where( - static function (EloquentBuilder $query) use ($accountIds) { + static function (EloquentBuilder $query) use ($accountIds) { // @phpstan-ignore-line $query->whereNotIn('source.account_id', $accountIds); $query->whereNotIn('destination.account_id', $accountIds); } @@ -210,7 +210,7 @@ trait AccountCollection if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->where( - static function (EloquentBuilder $q1) use ($accountIds) { + static function (EloquentBuilder $q1) use ($accountIds) { // @phpstan-ignore-line // sourceAccount is in the set, and destination is NOT. $q1->where( diff --git a/app/Helpers/Collector/Extensions/AmountCollection.php b/app/Helpers/Collector/Extensions/AmountCollection.php index 7e71d6e422..42edbe55fc 100644 --- a/app/Helpers/Collector/Extensions/AmountCollection.php +++ b/app/Helpers/Collector/Extensions/AmountCollection.php @@ -42,7 +42,7 @@ trait AmountCollection public function amountIs(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { + static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line $q->where('source.amount', app('steam')->negative($amount)); } ); @@ -56,7 +56,7 @@ trait AmountCollection public function amountIsNot(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { + static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line $q->where('source.amount', '!=', app('steam')->negative($amount)); } ); @@ -74,7 +74,7 @@ trait AmountCollection public function amountLess(string $amount): GroupCollectorInterface { $this->query->where( - function (EloquentBuilder $q) use ($amount) { + static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line $q->where('destination.amount', '<=', app('steam')->positive($amount)); } ); @@ -92,7 +92,7 @@ trait AmountCollection public function amountMore(string $amount): GroupCollectorInterface { $this->query->where( - function (EloquentBuilder $q) use ($amount) { + static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line $q->where('destination.amount', '>=', app('steam')->positive($amount)); } ); @@ -110,7 +110,7 @@ trait AmountCollection public function foreignAmountIs(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { + static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line $q->whereNotNull('source.foreign_amount'); $q->where('source.foreign_amount', app('steam')->negative($amount)); } @@ -129,7 +129,7 @@ trait AmountCollection public function foreignAmountIsNot(string $amount): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($amount) { + static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line $q->whereNull('source.foreign_amount'); $q->orWhere('source.foreign_amount', '!=', app('steam')->negative($amount)); } @@ -148,7 +148,7 @@ trait AmountCollection public function foreignAmountLess(string $amount): GroupCollectorInterface { $this->query->where( - function (EloquentBuilder $q) use ($amount) { + static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line $q->whereNotNull('destination.foreign_amount'); $q->where('destination.foreign_amount', '<=', app('steam')->positive($amount)); } @@ -167,7 +167,7 @@ trait AmountCollection public function foreignAmountMore(string $amount): GroupCollectorInterface { $this->query->where( - function (EloquentBuilder $q) use ($amount) { + static function (EloquentBuilder $q) use ($amount) { // @phpstan-ignore-line $q->whereNotNull('destination.foreign_amount'); $q->where('destination.foreign_amount', '>=', app('steam')->positive($amount)); } diff --git a/app/Helpers/Collector/Extensions/AttachmentCollection.php b/app/Helpers/Collector/Extensions/AttachmentCollection.php index 161422d107..4d929fb4db 100644 --- a/app/Helpers/Collector/Extensions/AttachmentCollection.php +++ b/app/Helpers/Collector/Extensions/AttachmentCollection.php @@ -29,7 +29,6 @@ use FireflyIII\Models\Attachment; use FireflyIII\Models\TransactionJournal; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; -use Illuminate\Support\Facades\Log; /** * Trait AttachmentCollection @@ -45,7 +44,13 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($name): bool { + /** + * @param int $index + * @param array $object + * + * @return bool + */ + $filter = static function (array $object) use ($name): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -73,7 +78,7 @@ trait AttachmentCollection */ public function hasAttachments(): GroupCollectorInterface { - Log::debug('Add filter on attachment ID.'); + app('log')->debug('Add filter on attachment ID.'); $this->joinAttachmentTables(); $this->query->whereNotNull('attachments.attachable_id'); $this->query->whereNull('attachments.deleted_at'); @@ -91,7 +96,7 @@ trait AttachmentCollection $this->hasJoinedAttTables = true; $this->query->leftJoin('attachments', 'attachments.attachable_id', '=', 'transaction_journals.id') ->where( - static function (EloquentBuilder $q1) { + static function (EloquentBuilder $q1) { // @phpstan-ignore-line $q1->where('attachments.attachable_type', TransactionJournal::class); $q1->where('attachments.uploaded', true); $q1->whereNull('attachments.deleted_at'); @@ -124,7 +129,14 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($name): bool { + /** + * @param int $index + * @param array $object + * + * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + $filter = static function (array $object) use ($name): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -154,7 +166,14 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($name): bool { + /** + * @param int $index + * @param array $object + * + * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + $filter = static function (array $object) use ($name): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -184,7 +203,14 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($name): bool { + /** + * @param int $index + * @param array $object + * + * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + $filter = static function (array $object) use ($name): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -214,7 +240,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($name): bool { + $filter = static function (array $object) use ($name): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -244,7 +270,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($name): bool { + $filter = static function (array $object) use ($name): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -271,7 +297,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($name): bool { + $filter = static function (array $object) use ($name): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -298,7 +324,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($name): bool { + $filter = static function (array $object) use ($name): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -328,7 +354,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($value): bool { + $filter = static function (array $object) use ($value): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -355,7 +381,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($value): bool { + $filter = static function (array $object) use ($value): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -382,7 +408,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($value): bool { + $filter = static function (array $object) use ($value): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -409,7 +435,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($value): bool { + $filter = static function (array $object) use ($value): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -436,7 +462,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($value): bool { + $filter = static function (array $object) use ($value): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -463,7 +489,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($value): bool { + $filter = static function (array $object) use ($value): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -490,7 +516,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($value): bool { + $filter = static function (array $object) use ($value): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -517,7 +543,7 @@ trait AttachmentCollection { $this->hasAttachments(); $this->withAttachmentInformation(); - $filter = function (int $index, array $object) use ($value): bool { + $filter = static function (array $object) use ($value): bool { /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { /** @var array $attachment */ @@ -542,13 +568,13 @@ trait AttachmentCollection */ public function hasNoAttachments(): GroupCollectorInterface { - Log::debug('Add filter on no attachments.'); + app('log')->debug('Add filter on no attachments.'); $this->joinAttachmentTables(); - $this->query->where(function (Builder $q1) { + $this->query->where(static function (Builder $q1) { // @phpstan-ignore-line $q1 ->whereNull('attachments.attachable_id') - ->orWhere(function (Builder $q2) { + ->orWhere(static function (Builder $q2) { $q2 ->whereNotNull('attachments.attachable_id') ->whereNotNull('attachments.deleted_at'); diff --git a/app/Helpers/Collector/Extensions/CollectorProperties.php b/app/Helpers/Collector/Extensions/CollectorProperties.php index fcb67c5d5b..ee48b0a766 100644 --- a/app/Helpers/Collector/Extensions/CollectorProperties.php +++ b/app/Helpers/Collector/Extensions/CollectorProperties.php @@ -33,7 +33,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; */ trait CollectorProperties { - public const TEST = 'Test'; + public const string TEST = 'Test'; private bool $expandGroupSearch; private array $fields; private bool $hasAccountInfo; diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index a8217a4b0a..9f23d89abb 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -45,7 +45,7 @@ trait MetaCollection public function excludeBills(Collection $bills): GroupCollectorInterface { $this->withBillInformation(); - $this->query->where(static function (EloquentBuilder $q1) use ($bills) { + $this->query->where(static function (EloquentBuilder $q1) use ($bills) { // @phpstan-ignore-line $q1->whereNotIn('transaction_journals.bill_id', $bills->pluck('id')->toArray()); $q1->orWhereNull('transaction_journals.bill_id'); }); @@ -83,7 +83,7 @@ trait MetaCollection { $this->withBudgetInformation(); - $this->query->where(static function (EloquentBuilder $q2) use ($budget) { + $this->query->where(static function (EloquentBuilder $q2) use ($budget) { // @phpstan-ignore-line $q2->where('budgets.id', '!=', $budget->id); $q2->orWhereNull('budgets.id'); }); @@ -119,7 +119,7 @@ trait MetaCollection { if ($budgets->count() > 0) { $this->withBudgetInformation(); - $this->query->where(static function (EloquentBuilder $q1) use ($budgets) { + $this->query->where(static function (EloquentBuilder $q1) use ($budgets) { // @phpstan-ignore-line $q1->whereNotIn('budgets.id', $budgets->pluck('id')->toArray()); $q1->orWhereNull('budgets.id'); }); @@ -135,7 +135,7 @@ trait MetaCollection { if ($categories->count() > 0) { $this->withCategoryInformation(); - $this->query->where(static function (EloquentBuilder $q1) use ($categories) { + $this->query->where(static function (EloquentBuilder $q1) use ($categories) { // @phpstan-ignore-line $q1->whereNotIn('categories.id', $categories->pluck('id')->toArray()); $q1->orWhereNull('categories.id'); }); @@ -176,7 +176,7 @@ trait MetaCollection { $this->withCategoryInformation(); - $this->query->where(static function (EloquentBuilder $q2) use ($category) { + $this->query->where(static function (EloquentBuilder $q2) use ($category) { // @phpstan-ignore-line $q2->where('categories.id', '!=', $category->id); $q2->orWhereNull('categories.id'); }); @@ -226,7 +226,7 @@ trait MetaCollection */ public function excludeInternalReference(string $internalReference): GroupCollectorInterface { - $internalReference = json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -253,7 +253,7 @@ trait MetaCollection */ public function externalIdContains(string $externalId): GroupCollectorInterface { - $externalId = json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -268,7 +268,7 @@ trait MetaCollection */ public function externalIdDoesNotContain(string $externalId): GroupCollectorInterface { - $externalId = json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -283,7 +283,7 @@ trait MetaCollection */ public function externalIdDoesNotEnd(string $externalId): GroupCollectorInterface { - $externalId = json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -298,7 +298,7 @@ trait MetaCollection */ public function externalIdDoesNotStart(string $externalId): GroupCollectorInterface { - $externalId = json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -313,7 +313,7 @@ trait MetaCollection */ public function externalIdEnds(string $externalId): GroupCollectorInterface { - $externalId = json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -328,7 +328,7 @@ trait MetaCollection */ public function externalIdStarts(string $externalId): GroupCollectorInterface { - $externalId = json_encode($externalId); + $externalId = (string)json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -346,7 +346,7 @@ trait MetaCollection public function externalUrlContains(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', trim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $url)); @@ -362,7 +362,7 @@ trait MetaCollection public function externalUrlDoesNotContain(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', trim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $url)); @@ -378,7 +378,7 @@ trait MetaCollection public function externalUrlDoesNotEnd(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', ltrim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s', $url)); @@ -394,7 +394,7 @@ trait MetaCollection public function externalUrlDoesNotStart(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', rtrim($url, '"')); //var_dump($url); @@ -412,7 +412,7 @@ trait MetaCollection public function externalUrlEnds(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', ltrim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s', $url)); @@ -428,7 +428,7 @@ trait MetaCollection public function externalUrlStarts(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = json_encode($url); + $url = (string)json_encode($url); $url = str_replace('\\', '\\\\', rtrim($url, '"')); //var_dump($url); @@ -487,7 +487,7 @@ trait MetaCollection */ public function internalReferenceContains(string $internalReference): GroupCollectorInterface { - $internalReference = json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); //var_dump($internalReference); //exit; @@ -504,7 +504,7 @@ trait MetaCollection */ public function internalReferenceDoesNotContain(string $internalReference): GroupCollectorInterface { - $internalReference = json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -519,7 +519,7 @@ trait MetaCollection */ public function internalReferenceDoesNotEnd(string $internalReference): GroupCollectorInterface { - $internalReference = json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -534,7 +534,7 @@ trait MetaCollection */ public function internalReferenceDoesNotStart(string $internalReference): GroupCollectorInterface { - $internalReference = json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -549,7 +549,7 @@ trait MetaCollection */ public function internalReferenceEnds(string $internalReference): GroupCollectorInterface { - $internalReference = json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -564,7 +564,7 @@ trait MetaCollection */ public function internalReferenceStarts(string $internalReference): GroupCollectorInterface { - $internalReference = json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -618,7 +618,7 @@ trait MetaCollection public function notesDoNotContain(string $value): GroupCollectorInterface { $this->withNotes(); - $this->query->where(static function (Builder $q) use ($value) { + $this->query->where(static function (Builder $q) use ($value) { // @phpstan-ignore-line $q->whereNull('notes.text'); $q->orWhere('notes.text', 'NOT LIKE', sprintf('%%%s%%', $value)); }); @@ -634,7 +634,7 @@ trait MetaCollection public function notesDontEndWith(string $value): GroupCollectorInterface { $this->withNotes(); - $this->query->where(static function (Builder $q) use ($value) { + $this->query->where(static function (Builder $q) use ($value) { // @phpstan-ignore-line $q->whereNull('notes.text'); $q->orWhere('notes.text', 'NOT LIKE', sprintf('%%%s', $value)); }); @@ -650,7 +650,7 @@ trait MetaCollection public function notesDontStartWith(string $value): GroupCollectorInterface { $this->withNotes(); - $this->query->where(static function (Builder $q) use ($value) { + $this->query->where(static function (Builder $q) use ($value) { // @phpstan-ignore-line $q->whereNull('notes.text'); $q->orWhere('notes.text', 'NOT LIKE', sprintf('%s%%', $value)); }); @@ -692,7 +692,7 @@ trait MetaCollection public function notesExactlyNot(string $value): GroupCollectorInterface { $this->withNotes(); - $this->query->where(static function (Builder $q) use ($value) { + $this->query->where(static function (Builder $q) use ($value) { // @phpstan-ignore-line $q->whereNull('notes.text'); $q->orWhere('notes.text', '!=', sprintf('%s', $value)); }); @@ -836,7 +836,7 @@ trait MetaCollection */ public function setInternalReference(string $internalReference): GroupCollectorInterface { - $internalReference = json_encode($internalReference); + $internalReference = (string)json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -915,8 +915,9 @@ trait MetaCollection // this method adds a "postFilter" to the collector. $list = $tags->pluck('tag')->toArray(); - $filter = function (int $index, array $object) use ($list): bool { + $filter = static function (array $object) use ($list): bool { foreach ($object['transactions'] as $transaction) { + app('log')->debug(sprintf('Transaction has %d tag(s)', count($transaction['tags']))); foreach ($transaction['tags'] as $tag) { if (in_array($tag['name'], $list, true)) { return false; @@ -1050,13 +1051,13 @@ trait MetaCollection { $this->joinMetaDataTables(); // TODO not sure if this will work properly. - $this->query->where(function (Builder $q1) { - $q1->where(function (Builder $q2) { + $this->query->where(static function (Builder $q1) { // @phpstan-ignore-line + $q1->where(static function (Builder $q2) { $q2->where('journal_meta.name', '=', 'external_id'); $q2->whereNull('journal_meta.data'); - })->orWhere(function (Builder $q3) { + })->orWhere(static function (Builder $q3) { $q3->where('journal_meta.name', '!=', 'external_id'); - })->orWhere(function (Builder $q4) { + })->orWhere(static function (Builder $q4) { $q4->whereNull('journal_meta.name'); }); }); @@ -1071,13 +1072,13 @@ trait MetaCollection { $this->joinMetaDataTables(); // TODO not sure if this will work properly. - $this->query->where(function (Builder $q1) { - $q1->where(function (Builder $q2) { + $this->query->where(static function (Builder $q1) { // @phpstan-ignore-line + $q1->where(static function (Builder $q2) { $q2->where('journal_meta.name', '=', 'external_url'); $q2->whereNull('journal_meta.data'); - })->orWhere(function (Builder $q3) { + })->orWhere(static function (Builder $q3) { $q3->where('journal_meta.name', '!=', 'external_url'); - })->orWhere(function (Builder $q4) { + })->orWhere(static function (Builder $q4) { $q4->whereNull('journal_meta.name'); }); }); @@ -1091,7 +1092,7 @@ trait MetaCollection public function withoutNotes(): GroupCollectorInterface { $this->withNotes(); - $this->query->where(function (Builder $q) { + $this->query->where(static function (Builder $q) { // @phpstan-ignore-line $q->whereNull('notes.text'); $q->orWhere('notes.text', ''); }); diff --git a/app/Helpers/Collector/Extensions/TimeCollection.php b/app/Helpers/Collector/Extensions/TimeCollection.php index 3908686d69..90c88ada99 100644 --- a/app/Helpers/Collector/Extensions/TimeCollection.php +++ b/app/Helpers/Collector/Extensions/TimeCollection.php @@ -93,7 +93,7 @@ trait TimeCollection $start->startOfDay(); $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $start, $end): bool { + $filter = static function (array $object) use ($field, $start, $end): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) { return $transaction[$field]->lt($start) || $transaction[$field]->gt($end); @@ -166,7 +166,7 @@ trait TimeCollection public function metaDayAfter(string $day, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $day): bool { + $filter = static function (array $object) use ($field, $day): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -190,7 +190,7 @@ trait TimeCollection public function metaDayBefore(string $day, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $day): bool { + $filter = static function (array $object) use ($field, $day): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -214,7 +214,7 @@ trait TimeCollection public function metaDayIs(string $day, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $day): bool { + $filter = static function (array $object) use ($field, $day): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -237,7 +237,7 @@ trait TimeCollection public function metaDayIsNot(string $day, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $day): bool { + $filter = static function (array $object) use ($field, $day): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -260,7 +260,7 @@ trait TimeCollection public function metaMonthAfter(string $month, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $month): bool { + $filter = static function (array $object) use ($field, $month): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -284,7 +284,7 @@ trait TimeCollection public function metaMonthBefore(string $month, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $month): bool { + $filter = static function (array $object) use ($field, $month): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -308,7 +308,7 @@ trait TimeCollection public function metaMonthIs(string $month, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $month): bool { + $filter = static function (array $object) use ($field, $month): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -331,7 +331,7 @@ trait TimeCollection public function metaMonthIsNot(string $month, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $month): bool { + $filter = static function (array $object) use ($field, $month): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -354,7 +354,7 @@ trait TimeCollection public function metaYearAfter(string $year, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $year): bool { + $filter = static function (array $object) use ($field, $year): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -378,7 +378,7 @@ trait TimeCollection public function metaYearBefore(string $year, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $year): bool { + $filter = static function (array $object) use ($field, $year): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -402,7 +402,7 @@ trait TimeCollection public function metaYearIs(string $year, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $year): bool { + $filter = static function (array $object) use ($field, $year): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -426,7 +426,7 @@ trait TimeCollection public function metaYearIsNot(string $year, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $year): bool { + $filter = static function (array $object) use ($field, $year): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -702,7 +702,7 @@ trait TimeCollection { $this->withMetaDate($field); $date->startOfDay(); - $filter = function (int $index, array $object) use ($field, $date): bool { + $filter = static function (array $object) use ($field, $date): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -726,7 +726,7 @@ trait TimeCollection public function setMetaBefore(Carbon $date, string $field): GroupCollectorInterface { $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $date): bool { + $filter = static function (array $object) use ($field, $date): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { @@ -758,7 +758,7 @@ trait TimeCollection $start->startOfDay(); $this->withMetaDate($field); - $filter = function (int $index, array $object) use ($field, $start, $end): bool { + $filter = static function (array $object) use ($field, $start, $end): bool { foreach ($object['transactions'] as $transaction) { if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon ) { diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index eee489d612..60a3fe14a1 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -44,7 +44,6 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\JoinClause; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class GroupCollector @@ -53,12 +52,12 @@ use Illuminate\Support\Facades\Log; */ class GroupCollector implements GroupCollectorInterface { - use CollectorProperties; use AccountCollection; use AmountCollection; - use TimeCollection; - use MetaCollection; use AttachmentCollection; + use CollectorProperties; + use MetaCollection; + use TimeCollection; /** * Group collector constructor. @@ -152,7 +151,7 @@ class GroupCollector implements GroupCollectorInterface public function descriptionDoesNotEnd(array $array): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($array) { + static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line $q->where( static function (EloquentBuilder $q1) use ($array) { foreach ($array as $word) { @@ -182,7 +181,7 @@ class GroupCollector implements GroupCollectorInterface public function descriptionDoesNotStart(array $array): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($array) { + static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line $q->where( static function (EloquentBuilder $q1) use ($array) { foreach ($array as $word) { @@ -212,7 +211,7 @@ class GroupCollector implements GroupCollectorInterface public function descriptionEnds(array $array): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($array) { + static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line $q->where( static function (EloquentBuilder $q1) use ($array) { foreach ($array as $word) { @@ -241,7 +240,7 @@ class GroupCollector implements GroupCollectorInterface public function descriptionIs(string $value): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($value) { + static function (EloquentBuilder $q) use ($value) { // @phpstan-ignore-line $q->where('transaction_journals.description', '=', $value); $q->orWhere('transaction_groups.title', '=', $value); } @@ -256,7 +255,7 @@ class GroupCollector implements GroupCollectorInterface public function descriptionIsNot(string $value): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($value) { + static function (EloquentBuilder $q) use ($value) { // @phpstan-ignore-line $q->where('transaction_journals.description', '!=', $value); $q->where( static function (EloquentBuilder $q2) use ($value) { @@ -276,7 +275,7 @@ class GroupCollector implements GroupCollectorInterface public function descriptionStarts(array $array): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($array) { + static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line $q->where( static function (EloquentBuilder $q1) use ($array) { foreach ($array as $word) { @@ -328,8 +327,8 @@ class GroupCollector implements GroupCollectorInterface */ public function dumpQueryInLogs(): void { - Log::debug($this->query->select($this->fields)->toSql()); - Log::debug('Bindings', $this->query->getBindings()); + app('log')->debug($this->query->select($this->fields)->toSql()); + app('log')->debug('Bindings', $this->query->getBindings()); } /** @@ -342,7 +341,7 @@ class GroupCollector implements GroupCollectorInterface public function excludeCurrency(TransactionCurrency $currency): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($currency) { + static function (EloquentBuilder $q) use ($currency) { // @phpstan-ignore-line $q->where('source.transaction_currency_id', '!=', $currency->id); $q->where( static function (EloquentBuilder $q2) use ($currency) { @@ -361,7 +360,7 @@ class GroupCollector implements GroupCollectorInterface */ public function excludeForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface { - $this->query->where(static function (EloquentBuilder $q2) use ($currency) { + $this->query->where(static function (EloquentBuilder $q2) use ($currency) { // @phpstan-ignore-line $q2->where('source.foreign_currency_id', '!=', $currency->id); $q2->orWhereNull('source.foreign_currency_id'); }); @@ -416,7 +415,7 @@ class GroupCollector implements GroupCollectorInterface return $this; } $this->query->where( - static function (EloquentBuilder $q) use ($array) { + static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line $q->where( static function (EloquentBuilder $q1) use ($array) { foreach ($array as $word) { @@ -516,7 +515,6 @@ class GroupCollector implements GroupCollectorInterface // add to query: $this->query->orWhereIn('transaction_journals.transaction_group_id', $groupIds); } - $result = $this->query->get($this->fields); // now to parse this into an array. @@ -564,8 +562,8 @@ class GroupCollector implements GroupCollectorInterface $parsedGroup = $this->parseAugmentedJournal($augumentedJournal); $groupArray = [ 'id' => (int)$augumentedJournal->transaction_group_id, - 'user_id' => (int)$augumentedJournal->user_id, - 'user_group_id' => (int)$augumentedJournal->user_group_id, + 'user_id' => $augumentedJournal->user_id, + 'user_group_id' => $augumentedJournal->user_group_id, // Field transaction_group_title was added by the query. 'title' => $augumentedJournal->transaction_group_title, // @phpstan-ignore-line 'transaction_type' => $parsedGroup['transaction_type_type'], @@ -627,7 +625,7 @@ class GroupCollector implements GroupCollectorInterface $result['created_at']->setTimezone(config('app.timezone')); $result['updated_at']->setTimezone(config('app.timezone')); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException($e->getMessage(), 0, $e); } @@ -653,7 +651,7 @@ class GroupCollector implements GroupCollectorInterface try { $tagDate = Carbon::parse($augumentedJournal['tag_date']); } catch (InvalidFormatException $e) { - Log::debug(sprintf('Could not parse date: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not parse date: %s', $e->getMessage())); } $result['tags'][$tagId] = [ @@ -734,7 +732,7 @@ class GroupCollector implements GroupCollectorInterface try { $tagDate = Carbon::parse($newArray['tag_date']); } catch (InvalidFormatException $e) { - Log::debug(sprintf('Could not parse date: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not parse date: %s', $e->getMessage())); } $existingJournal['tags'][$tagId] = [ @@ -783,6 +781,9 @@ class GroupCollector implements GroupCollectorInterface /** @var array $transaction */ foreach ($group['transactions'] as $transaction) { $currencyId = (int)$transaction['currency_id']; + if (null === $transaction['amount']) { + throw new FireflyException(sprintf('Amount is NULL for a transaction in group #%d, please investigate.', $groudId)); + } // set default: if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) { @@ -821,20 +822,24 @@ class GroupCollector implements GroupCollectorInterface private function postFilterCollection(Collection $collection): Collection { $currentCollection = $collection; + + app('log')->debug(sprintf('GroupCollector: postFilterCollection has %d filter(s) and %d transaction(s).', count($this->postFilters), count($currentCollection))); + + /** * @var Closure $function */ foreach ($this->postFilters as $function) { + app('log')->debug('Applying filter...'); $nextCollection = new Collection(); // loop everything in the current collection // and save it (or not) in the new collection. // that new collection is the next current collection /** - * @var int $ii * @var array $item */ - foreach ($currentCollection as $ii => $item) { - $result = $function($ii, $item); + foreach ($currentCollection as $item) { + $result = $function($item); if (false === $result) { // skip other filters, continue to next item. continue; @@ -842,6 +847,7 @@ class GroupCollector implements GroupCollectorInterface $nextCollection->push($item); } $currentCollection = $nextCollection; + app('log')->debug(sprintf('GroupCollector: postFilterCollection has %d transaction(s) left.', count($currentCollection))); } return $currentCollection; } @@ -904,7 +910,7 @@ class GroupCollector implements GroupCollectorInterface public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface { $this->query->where( - static function (EloquentBuilder $q) use ($currency) { + static function (EloquentBuilder $q) use ($currency) { // @phpstan-ignore-line $q->where('source.transaction_currency_id', $currency->id); $q->orWhere('source.foreign_currency_id', $currency->id); } @@ -916,9 +922,10 @@ class GroupCollector implements GroupCollectorInterface /** * @param bool $expandGroupSearch */ - public function setExpandGroupSearch(bool $expandGroupSearch): void + public function setExpandGroupSearch(bool $expandGroupSearch): GroupCollectorInterface { $this->expandGroupSearch = $expandGroupSearch; + return $this; } /** @@ -994,7 +1001,7 @@ class GroupCollector implements GroupCollectorInterface return $this; } $this->query->where( - static function (EloquentBuilder $q) use ($array) { + static function (EloquentBuilder $q) use ($array) { // @phpstan-ignore-line $q->where( static function (EloquentBuilder $q1) use ($array) { foreach ($array as $word) { @@ -1077,7 +1084,7 @@ class GroupCollector implements GroupCollectorInterface // join source transaction. ->leftJoin( 'transactions as source', - function (JoinClause $join) { + static function (JoinClause $join) { $join->on('source.transaction_journal_id', '=', 'transaction_journals.id') ->where('source.amount', '<', 0); } @@ -1085,7 +1092,7 @@ class GroupCollector implements GroupCollectorInterface // join destination transaction ->leftJoin( 'transactions as destination', - function (JoinClause $join) { + static function (JoinClause $join) { $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id') ->where('destination.amount', '>', 0); } @@ -1108,7 +1115,7 @@ class GroupCollector implements GroupCollectorInterface /** * Set the user object and start the query. * - * @param User $user + * @param UserGroup $userGroup * * @return GroupCollectorInterface */ @@ -1135,7 +1142,7 @@ class GroupCollector implements GroupCollectorInterface // join source transaction. ->leftJoin( 'transactions as source', - function (JoinClause $join) { + static function (JoinClause $join) { $join->on('source.transaction_journal_id', '=', 'transaction_journals.id') ->where('source.amount', '<', 0); } @@ -1143,7 +1150,7 @@ class GroupCollector implements GroupCollectorInterface // join destination transaction ->leftJoin( 'transactions as destination', - function (JoinClause $join) { + static function (JoinClause $join) { $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id') ->where('destination.amount', '>', 0); } diff --git a/app/Helpers/Collector/GroupCollectorInterface.php b/app/Helpers/Collector/GroupCollectorInterface.php index 49880b7cf2..5ab713e689 100644 --- a/app/Helpers/Collector/GroupCollectorInterface.php +++ b/app/Helpers/Collector/GroupCollectorInterface.php @@ -47,14 +47,14 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function amountIs(string $amount): GroupCollectorInterface; + public function amountIs(string $amount): self; /** * @param string $amount * * @return GroupCollectorInterface */ - public function amountIsNot(string $amount): GroupCollectorInterface; + public function amountIsNot(string $amount): self; /** * Get transactions where the amount is less than. @@ -63,7 +63,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function amountLess(string $amount): GroupCollectorInterface; + public function amountLess(string $amount): self; /** * Get transactions where the foreign amount is more than. @@ -72,147 +72,147 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function amountMore(string $amount): GroupCollectorInterface; + public function amountMore(string $amount): self; /** * @param string $name * * @return GroupCollectorInterface */ - public function attachmentNameContains(string $name): GroupCollectorInterface; + public function attachmentNameContains(string $name): self; /** * @param string $name * * @return GroupCollectorInterface */ - public function attachmentNameDoesNotContain(string $name): GroupCollectorInterface; + public function attachmentNameDoesNotContain(string $name): self; /** * @param string $name * * @return GroupCollectorInterface */ - public function attachmentNameDoesNotEnd(string $name): GroupCollectorInterface; + public function attachmentNameDoesNotEnd(string $name): self; /** * @param string $name * * @return GroupCollectorInterface */ - public function attachmentNameDoesNotStart(string $name): GroupCollectorInterface; + public function attachmentNameDoesNotStart(string $name): self; /** * @param string $name * * @return GroupCollectorInterface */ - public function attachmentNameEnds(string $name): GroupCollectorInterface; + public function attachmentNameEnds(string $name): self; /** * @param string $name * * @return GroupCollectorInterface */ - public function attachmentNameIs(string $name): GroupCollectorInterface; + public function attachmentNameIs(string $name): self; /** * @param string $name * * @return GroupCollectorInterface */ - public function attachmentNameIsNot(string $name): GroupCollectorInterface; + public function attachmentNameIsNot(string $name): self; /** * @param string $name * * @return GroupCollectorInterface */ - public function attachmentNameStarts(string $name): GroupCollectorInterface; + public function attachmentNameStarts(string $name): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function attachmentNotesAre(string $value): GroupCollectorInterface; + public function attachmentNotesAre(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function attachmentNotesAreNot(string $value): GroupCollectorInterface; + public function attachmentNotesAreNot(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function attachmentNotesContains(string $value): GroupCollectorInterface; + public function attachmentNotesContains(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function attachmentNotesDoNotContain(string $value): GroupCollectorInterface; + public function attachmentNotesDoNotContain(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function attachmentNotesDoNotEnd(string $value): GroupCollectorInterface; + public function attachmentNotesDoNotEnd(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function attachmentNotesDoNotStart(string $value): GroupCollectorInterface; + public function attachmentNotesDoNotStart(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function attachmentNotesEnds(string $value): GroupCollectorInterface; + public function attachmentNotesEnds(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function attachmentNotesStarts(string $value): GroupCollectorInterface; + public function attachmentNotesStarts(string $value): self; /** * @param string $day * * @return GroupCollectorInterface */ - public function dayAfter(string $day): GroupCollectorInterface; + public function dayAfter(string $day): self; /** * @param string $day * * @return GroupCollectorInterface */ - public function dayBefore(string $day): GroupCollectorInterface; + public function dayBefore(string $day): self; /** * @param string $day * * @return GroupCollectorInterface */ - public function dayIs(string $day): GroupCollectorInterface; + public function dayIs(string $day): self; /** * @param string $day * * @return GroupCollectorInterface */ - public function dayIsNot(string $day): GroupCollectorInterface; + public function dayIsNot(string $day): self; /** * End of the description must not match: @@ -221,7 +221,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function descriptionDoesNotEnd(array $array): GroupCollectorInterface; + public function descriptionDoesNotEnd(array $array): self; /** * Beginning of the description must not start with: @@ -230,7 +230,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function descriptionDoesNotStart(array $array): GroupCollectorInterface; + public function descriptionDoesNotStart(array $array): self; /** * End of the description must match: @@ -239,7 +239,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function descriptionEnds(array $array): GroupCollectorInterface; + public function descriptionEnds(array $array): self; /** * Description must be: @@ -248,7 +248,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function descriptionIs(string $value): GroupCollectorInterface; + public function descriptionIs(string $value): self; /** * Description must not be: @@ -257,7 +257,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function descriptionIsNot(string $value): GroupCollectorInterface; + public function descriptionIsNot(string $value): self; /** * Beginning of the description must match: @@ -266,7 +266,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function descriptionStarts(array $array): GroupCollectorInterface; + public function descriptionStarts(array $array): self; /** * These accounts must not be accounts. @@ -275,7 +275,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeAccounts(Collection $accounts): GroupCollectorInterface; + public function excludeAccounts(Collection $accounts): self; /** * Exclude a specific set of bills @@ -284,7 +284,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeBills(Collection $bills): GroupCollectorInterface; + public function excludeBills(Collection $bills): self; /** * Exclude a budget @@ -293,7 +293,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeBudget(Budget $budget): GroupCollectorInterface; + public function excludeBudget(Budget $budget): self; /** * Exclude a budget. @@ -302,7 +302,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeBudgets(Collection $budgets): GroupCollectorInterface; + public function excludeBudgets(Collection $budgets): self; /** * Exclude a set of categories. @@ -311,7 +311,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeCategories(Collection $categories): GroupCollectorInterface; + public function excludeCategories(Collection $categories): self; /** * Exclude a specific category @@ -320,7 +320,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeCategory(Category $category): GroupCollectorInterface; + public function excludeCategory(Category $category): self; /** * Limit results to NOT a specific currency, either foreign or normal one. @@ -329,7 +329,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeCurrency(TransactionCurrency $currency): GroupCollectorInterface; + public function excludeCurrency(TransactionCurrency $currency): self; /** * Exclude destination accounts. @@ -338,7 +338,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeDestinationAccounts(Collection $accounts): GroupCollectorInterface; + public function excludeDestinationAccounts(Collection $accounts): self; /** * Look for specific external ID's. @@ -347,14 +347,14 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeExternalId(string $externalId): GroupCollectorInterface; + public function excludeExternalId(string $externalId): self; /** * @param string $url * * @return GroupCollectorInterface */ - public function excludeExternalUrl(string $url): GroupCollectorInterface; + public function excludeExternalUrl(string $url): self; /** * Limit results to exclude a specific foreign currency. @@ -363,7 +363,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface; + public function excludeForeignCurrency(TransactionCurrency $currency): self; /** * Limit the result to NOT a set of specific transaction groups. @@ -372,7 +372,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeIds(array $groupIds): GroupCollectorInterface; + public function excludeIds(array $groupIds): self; /** * Look for specific external ID's. @@ -381,7 +381,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeInternalReference(string $internalReference): GroupCollectorInterface; + public function excludeInternalReference(string $internalReference): self; /** * Limit the result to NOT a set of specific transaction journals. @@ -390,7 +390,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeJournalIds(array $journalIds): GroupCollectorInterface; + public function excludeJournalIds(array $journalIds): self; /** * @param Carbon $start @@ -399,7 +399,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeMetaDateRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface; + public function excludeMetaDateRange(Carbon $start, Carbon $end, string $field): self; /** * @param Carbon $start @@ -408,7 +408,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeObjectRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface; + public function excludeObjectRange(Carbon $start, Carbon $end, string $field): self; /** * @param Carbon $start @@ -416,14 +416,14 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeRange(Carbon $start, Carbon $end): GroupCollectorInterface; + public function excludeRange(Carbon $start, Carbon $end): self; /** * @param string $recurringId * * @return GroupCollectorInterface */ - public function excludeRecurrenceId(string $recurringId): GroupCollectorInterface; + public function excludeRecurrenceId(string $recurringId): self; /** * Exclude words in descriptions. @@ -432,7 +432,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeSearchWords(array $array): GroupCollectorInterface; + public function excludeSearchWords(array $array): self; /** * These accounts must not be source accounts. @@ -441,7 +441,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeSourceAccounts(Collection $accounts): GroupCollectorInterface; + public function excludeSourceAccounts(Collection $accounts): self; /** * Limit the included transaction types. @@ -450,103 +450,103 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function excludeTypes(array $types): GroupCollectorInterface; + public function excludeTypes(array $types): self; /** * @return GroupCollectorInterface */ - public function exists(): GroupCollectorInterface; + public function exists(): self; /** * @param string $externalId * * @return GroupCollectorInterface */ - public function externalIdContains(string $externalId): GroupCollectorInterface; + public function externalIdContains(string $externalId): self; /** * @param string $externalId * * @return GroupCollectorInterface */ - public function externalIdDoesNotContain(string $externalId): GroupCollectorInterface; + public function externalIdDoesNotContain(string $externalId): self; /** * @param string $externalId * * @return GroupCollectorInterface */ - public function externalIdDoesNotEnd(string $externalId): GroupCollectorInterface; + public function externalIdDoesNotEnd(string $externalId): self; /** * @param string $externalId * * @return GroupCollectorInterface */ - public function externalIdDoesNotStart(string $externalId): GroupCollectorInterface; + public function externalIdDoesNotStart(string $externalId): self; /** * @param string $externalId * * @return GroupCollectorInterface */ - public function externalIdEnds(string $externalId): GroupCollectorInterface; + public function externalIdEnds(string $externalId): self; /** * @param string $externalId * * @return GroupCollectorInterface */ - public function externalIdStarts(string $externalId): GroupCollectorInterface; + public function externalIdStarts(string $externalId): self; /** * @param string $url * * @return GroupCollectorInterface */ - public function externalUrlContains(string $url): GroupCollectorInterface; + public function externalUrlContains(string $url): self; /** * @param string $url * * @return GroupCollectorInterface */ - public function externalUrlDoesNotContain(string $url): GroupCollectorInterface; + public function externalUrlDoesNotContain(string $url): self; /** * @param string $url * * @return GroupCollectorInterface */ - public function externalUrlDoesNotEnd(string $url): GroupCollectorInterface; + public function externalUrlDoesNotEnd(string $url): self; /** * @param string $url * * @return GroupCollectorInterface */ - public function externalUrlDoesNotStart(string $url): GroupCollectorInterface; + public function externalUrlDoesNotStart(string $url): self; /** * @param string $url * * @return GroupCollectorInterface */ - public function externalUrlEnds(string $url): GroupCollectorInterface; + public function externalUrlEnds(string $url): self; /** * @param string $url * * @return GroupCollectorInterface */ - public function externalUrlStarts(string $url): GroupCollectorInterface; + public function externalUrlStarts(string $url): self; /** * Ensure the search will find nothing at all, zero results. * * @return GroupCollectorInterface */ - public function findNothing(): GroupCollectorInterface; + public function findNothing(): self; /** * Get transactions with a specific foreign amount. @@ -555,7 +555,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function foreignAmountIs(string $amount): GroupCollectorInterface; + public function foreignAmountIs(string $amount): self; /** * Get transactions with a specific foreign amount. @@ -564,7 +564,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function foreignAmountIsNot(string $amount): GroupCollectorInterface; + public function foreignAmountIsNot(string $amount): self; /** * Get transactions where the amount is less than. @@ -573,7 +573,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function foreignAmountLess(string $amount): GroupCollectorInterface; + public function foreignAmountLess(string $amount): self; /** * Get transactions where the foreign amount is more than. @@ -582,7 +582,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function foreignAmountMore(string $amount): GroupCollectorInterface; + public function foreignAmountMore(string $amount): self; /** * @return bool @@ -613,77 +613,77 @@ interface GroupCollectorInterface /** * @return GroupCollectorInterface */ - public function hasAnyTag(): GroupCollectorInterface; + public function hasAnyTag(): self; /** * Has attachments * * @return GroupCollectorInterface */ - public function hasAttachments(): GroupCollectorInterface; + public function hasAttachments(): self; /** * Has no attachments * * @return GroupCollectorInterface */ - public function hasNoAttachments(): GroupCollectorInterface; + public function hasNoAttachments(): self; /** * @param string $internalReference * * @return GroupCollectorInterface */ - public function internalReferenceContains(string $internalReference): GroupCollectorInterface; + public function internalReferenceContains(string $internalReference): self; /** * @param string $internalReference * * @return GroupCollectorInterface */ - public function internalReferenceDoesNotContain(string $internalReference): GroupCollectorInterface; + public function internalReferenceDoesNotContain(string $internalReference): self; /** * @param string $internalReference * * @return GroupCollectorInterface */ - public function internalReferenceDoesNotEnd(string $internalReference): GroupCollectorInterface; + public function internalReferenceDoesNotEnd(string $internalReference): self; /** * @param string $internalReference * * @return GroupCollectorInterface */ - public function internalReferenceDoesNotStart(string $internalReference): GroupCollectorInterface; + public function internalReferenceDoesNotStart(string $internalReference): self; /** * @param string $internalReference * * @return GroupCollectorInterface */ - public function internalReferenceEnds(string $internalReference): GroupCollectorInterface; + public function internalReferenceEnds(string $internalReference): self; /** * @param string $internalReference * * @return GroupCollectorInterface */ - public function internalReferenceStarts(string $internalReference): GroupCollectorInterface; + public function internalReferenceStarts(string $internalReference): self; /** * Only journals that are reconciled. * * @return GroupCollectorInterface */ - public function isNotReconciled(): GroupCollectorInterface; + public function isNotReconciled(): self; /** * Only journals that are reconciled. * * @return GroupCollectorInterface */ - public function isReconciled(): GroupCollectorInterface; + public function isReconciled(): self; /** * @param string $day @@ -691,7 +691,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaDayAfter(string $day, string $field): GroupCollectorInterface; + public function metaDayAfter(string $day, string $field): self; /** * @param string $day @@ -699,7 +699,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaDayBefore(string $day, string $field): GroupCollectorInterface; + public function metaDayBefore(string $day, string $field): self; /** * @param string $day @@ -707,7 +707,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaDayIs(string $day, string $field): GroupCollectorInterface; + public function metaDayIs(string $day, string $field): self; /** * @param string $day @@ -715,7 +715,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaDayIsNot(string $day, string $field): GroupCollectorInterface; + public function metaDayIsNot(string $day, string $field): self; /** * @param string $month @@ -723,7 +723,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaMonthAfter(string $month, string $field): GroupCollectorInterface; + public function metaMonthAfter(string $month, string $field): self; /** * @param string $month @@ -731,7 +731,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaMonthBefore(string $month, string $field): GroupCollectorInterface; + public function metaMonthBefore(string $month, string $field): self; /** * @param string $month @@ -739,7 +739,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaMonthIs(string $month, string $field): GroupCollectorInterface; + public function metaMonthIs(string $month, string $field): self; /** * @param string $month @@ -747,7 +747,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaMonthIsNot(string $month, string $field): GroupCollectorInterface; + public function metaMonthIsNot(string $month, string $field): self; /** * @param string $year @@ -755,7 +755,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaYearAfter(string $year, string $field): GroupCollectorInterface; + public function metaYearAfter(string $year, string $field): self; /** * @param string $year @@ -763,7 +763,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaYearBefore(string $year, string $field): GroupCollectorInterface; + public function metaYearBefore(string $year, string $field): self; /** * @param string $year @@ -771,7 +771,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaYearIs(string $year, string $field): GroupCollectorInterface; + public function metaYearIs(string $year, string $field): self; /** * @param string $year @@ -779,91 +779,91 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function metaYearIsNot(string $year, string $field): GroupCollectorInterface; + public function metaYearIsNot(string $year, string $field): self; /** * @param string $month * * @return GroupCollectorInterface */ - public function monthAfter(string $month): GroupCollectorInterface; + public function monthAfter(string $month): self; /** * @param string $month * * @return GroupCollectorInterface */ - public function monthBefore(string $month): GroupCollectorInterface; + public function monthBefore(string $month): self; /** * @param string $month * * @return GroupCollectorInterface */ - public function monthIs(string $month): GroupCollectorInterface; + public function monthIs(string $month): self; /** * @param string $month * * @return GroupCollectorInterface */ - public function monthIsNot(string $month): GroupCollectorInterface; + public function monthIsNot(string $month): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function notesContain(string $value): GroupCollectorInterface; + public function notesContain(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function notesDoNotContain(string $value): GroupCollectorInterface; + public function notesDoNotContain(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function notesDontEndWith(string $value): GroupCollectorInterface; + public function notesDontEndWith(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function notesDontStartWith(string $value): GroupCollectorInterface; + public function notesDontStartWith(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function notesEndWith(string $value): GroupCollectorInterface; + public function notesEndWith(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function notesExactly(string $value): GroupCollectorInterface; + public function notesExactly(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function notesExactlyNot(string $value): GroupCollectorInterface; + public function notesExactlyNot(string $value): self; /** * @param string $value * * @return GroupCollectorInterface */ - public function notesStartWith(string $value): GroupCollectorInterface; + public function notesStartWith(string $value): self; /** * @param string $day @@ -871,7 +871,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectDayAfter(string $day, string $field): GroupCollectorInterface; + public function objectDayAfter(string $day, string $field): self; /** * @param string $day @@ -879,7 +879,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectDayBefore(string $day, string $field): GroupCollectorInterface; + public function objectDayBefore(string $day, string $field): self; /** * @param string $day @@ -887,7 +887,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectDayIs(string $day, string $field): GroupCollectorInterface; + public function objectDayIs(string $day, string $field): self; /** * @param string $day @@ -895,7 +895,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectDayIsNot(string $day, string $field): GroupCollectorInterface; + public function objectDayIsNot(string $day, string $field): self; /** * @param string $month @@ -903,7 +903,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectMonthAfter(string $month, string $field): GroupCollectorInterface; + public function objectMonthAfter(string $month, string $field): self; /** * @param string $month @@ -911,7 +911,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectMonthBefore(string $month, string $field): GroupCollectorInterface; + public function objectMonthBefore(string $month, string $field): self; /** * @param string $month @@ -919,7 +919,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectMonthIs(string $month, string $field): GroupCollectorInterface; + public function objectMonthIs(string $month, string $field): self; /** * @param string $month @@ -927,7 +927,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectMonthIsNot(string $month, string $field): GroupCollectorInterface; + public function objectMonthIsNot(string $month, string $field): self; /** * @param string $year @@ -935,7 +935,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectYearAfter(string $year, string $field): GroupCollectorInterface; + public function objectYearAfter(string $year, string $field): self; /** * @param string $year @@ -943,7 +943,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectYearBefore(string $year, string $field): GroupCollectorInterface; + public function objectYearBefore(string $year, string $field): self; /** * @param string $year @@ -951,7 +951,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectYearIs(string $year, string $field): GroupCollectorInterface; + public function objectYearIs(string $year, string $field): self; /** * @param string $year @@ -959,7 +959,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function objectYearIsNot(string $year, string $field): GroupCollectorInterface; + public function objectYearIsNot(string $year, string $field): self; /** * Define which accounts can be part of the source and destination transactions. @@ -968,7 +968,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setAccounts(Collection $accounts): GroupCollectorInterface; + public function setAccounts(Collection $accounts): self; /** * Collect transactions after a specific date. @@ -977,7 +977,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setAfter(Carbon $date): GroupCollectorInterface; + public function setAfter(Carbon $date): self; /** * Collect transactions before a specific date. @@ -986,7 +986,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setBefore(Carbon $date): GroupCollectorInterface; + public function setBefore(Carbon $date): self; /** * Limit the search to a specific bill. @@ -995,7 +995,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setBill(Bill $bill): GroupCollectorInterface; + public function setBill(Bill $bill): self; /** * Limit the search to a specific set of bills. @@ -1004,7 +1004,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setBills(Collection $bills): GroupCollectorInterface; + public function setBills(Collection $bills): self; /** * Both source AND destination must be in this list of accounts. @@ -1013,7 +1013,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setBothAccounts(Collection $accounts): GroupCollectorInterface; + public function setBothAccounts(Collection $accounts): self; /** * Limit the search to a specific budget. @@ -1022,7 +1022,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setBudget(Budget $budget): GroupCollectorInterface; + public function setBudget(Budget $budget): self; /** * Limit the search to a specific set of budgets. @@ -1031,7 +1031,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setBudgets(Collection $budgets): GroupCollectorInterface; + public function setBudgets(Collection $budgets): self; /** * Limit the search to a specific bunch of categories. @@ -1040,7 +1040,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setCategories(Collection $categories): GroupCollectorInterface; + public function setCategories(Collection $categories): self; /** * Limit the search to a specific category. @@ -1049,7 +1049,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setCategory(Category $category): GroupCollectorInterface; + public function setCategory(Category $category): self; /** * Collect transactions created on a specific date. @@ -1058,7 +1058,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setCreatedAt(Carbon $date): GroupCollectorInterface; + public function setCreatedAt(Carbon $date): self; /** * Limit results to a specific currency, either foreign or normal one. @@ -1067,7 +1067,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface; + public function setCurrency(TransactionCurrency $currency): self; /** * Set destination accounts. @@ -1076,7 +1076,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setDestinationAccounts(Collection $accounts): GroupCollectorInterface; + public function setDestinationAccounts(Collection $accounts): self; /** * Set the end time of the results to return. @@ -1085,12 +1085,12 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setEnd(Carbon $end): GroupCollectorInterface; + public function setEnd(Carbon $end): self; /** * @param bool $expandGroupSearch */ - public function setExpandGroupSearch(bool $expandGroupSearch); + public function setExpandGroupSearch(bool $expandGroupSearch): self; /** * Look for specific external ID's. @@ -1099,14 +1099,14 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setExternalId(string $externalId): GroupCollectorInterface; + public function setExternalId(string $externalId): self; /** * @param string $url * * @return GroupCollectorInterface */ - public function setExternalUrl(string $url): GroupCollectorInterface; + public function setExternalUrl(string $url): self; /** * Limit results to a specific foreign currency. @@ -1115,7 +1115,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface; + public function setForeignCurrency(TransactionCurrency $currency): self; /** * Limit the result to a set of specific transaction groups. @@ -1124,7 +1124,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setIds(array $groupIds): GroupCollectorInterface; + public function setIds(array $groupIds): self; /** * Look for specific external ID's. @@ -1133,7 +1133,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setInternalReference(string $internalReference): GroupCollectorInterface; + public function setInternalReference(string $internalReference): self; /** * Limit the result to a set of specific transaction journals. @@ -1142,7 +1142,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setJournalIds(array $journalIds): GroupCollectorInterface; + public function setJournalIds(array $journalIds): self; /** * Limit the number of returned entries. @@ -1151,7 +1151,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setLimit(int $limit): GroupCollectorInterface; + public function setLimit(int $limit): self; /** * Collect transactions after a specific date. @@ -1161,7 +1161,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setMetaAfter(Carbon $date, string $field): GroupCollectorInterface; + public function setMetaAfter(Carbon $date, string $field): self; /** * Collect transactions before a specific date. @@ -1171,7 +1171,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setMetaBefore(Carbon $date, string $field): GroupCollectorInterface; + public function setMetaBefore(Carbon $date, string $field): self; /** * Set the start and end time of the results to return, based on meta data. @@ -1182,7 +1182,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setMetaDateRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface; + public function setMetaDateRange(Carbon $start, Carbon $end, string $field): self; /** * Define which accounts can NOT be part of the source and destination transactions. @@ -1191,7 +1191,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setNotAccounts(Collection $accounts): GroupCollectorInterface; + public function setNotAccounts(Collection $accounts): self; /** * @param Carbon $date @@ -1199,7 +1199,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setObjectAfter(Carbon $date, string $field): GroupCollectorInterface; + public function setObjectAfter(Carbon $date, string $field): self; /** * @param Carbon $date @@ -1207,7 +1207,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setObjectBefore(Carbon $date, string $field): GroupCollectorInterface; + public function setObjectBefore(Carbon $date, string $field): self; /** * @param Carbon $start @@ -1216,7 +1216,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setObjectRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface; + public function setObjectRange(Carbon $start, Carbon $end, string $field): self; /** * Set the page to get. @@ -1225,7 +1225,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setPage(int $page): GroupCollectorInterface; + public function setPage(int $page): self; /** * Set the start and end time of the results to return. @@ -1235,7 +1235,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setRange(Carbon $start, Carbon $end): GroupCollectorInterface; + public function setRange(Carbon $start, Carbon $end): self; /** * Look for specific recurring ID's. @@ -1244,7 +1244,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setRecurrenceId(string $recurringId): GroupCollectorInterface; + public function setRecurrenceId(string $recurringId): self; /** * Search for words in descriptions. @@ -1253,14 +1253,14 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setSearchWords(array $array): GroupCollectorInterface; + public function setSearchWords(array $array): self; /** * @param string $sepaCT * * @return GroupCollectorInterface */ - public function setSepaCT(string $sepaCT): GroupCollectorInterface; + public function setSepaCT(string $sepaCT): self; /** * Set source accounts. @@ -1269,7 +1269,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setSourceAccounts(Collection $accounts): GroupCollectorInterface; + public function setSourceAccounts(Collection $accounts): self; /** * Set the start time of the results to return. @@ -1278,7 +1278,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setStart(Carbon $start): GroupCollectorInterface; + public function setStart(Carbon $start): self; /** * Limit results to a specific tag. @@ -1287,7 +1287,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setTag(Tag $tag): GroupCollectorInterface; + public function setTag(Tag $tag): self; /** * Limit results to a specific set of tags. @@ -1296,7 +1296,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setTags(Collection $tags): GroupCollectorInterface; + public function setTags(Collection $tags): self; /** * Limit the search to one specific transaction group. @@ -1305,7 +1305,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setTransactionGroup(TransactionGroup $transactionGroup): GroupCollectorInterface; + public function setTransactionGroup(TransactionGroup $transactionGroup): self; /** * Limit the included transaction types. @@ -1314,7 +1314,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setTypes(array $types): GroupCollectorInterface; + public function setTypes(array $types): self; /** * Collect transactions updated on a specific date. @@ -1323,7 +1323,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setUpdatedAt(Carbon $date): GroupCollectorInterface; + public function setUpdatedAt(Carbon $date): self; /** * Set the user object and start the query. @@ -1332,7 +1332,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setUser(User $user): GroupCollectorInterface; + public function setUser(User $user): self; /** * Set the user group object and start the query. @@ -1341,7 +1341,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setUserGroup(UserGroup $userGroup): GroupCollectorInterface; + public function setUserGroup(UserGroup $userGroup): self; /** * Only when does not have these tags @@ -1350,7 +1350,7 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setWithoutSpecificTags(Collection $tags): GroupCollectorInterface; + public function setWithoutSpecificTags(Collection $tags): self; /** * Either account can be set, but NOT both. This effectively excludes internal transfers. @@ -1359,91 +1359,91 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function setXorAccounts(Collection $accounts): GroupCollectorInterface; + public function setXorAccounts(Collection $accounts): self; /** * Automatically include all stuff required to make API calls work. * * @return GroupCollectorInterface */ - public function withAPIInformation(): GroupCollectorInterface; + public function withAPIInformation(): self; /** * Will include the source and destination account names and types. * * @return GroupCollectorInterface */ - public function withAccountInformation(): GroupCollectorInterface; + public function withAccountInformation(): self; /** * Any notes, no matter what. * * @return GroupCollectorInterface */ - public function withAnyNotes(): GroupCollectorInterface; + public function withAnyNotes(): self; /** * Add basic info on attachments of transactions. * * @return GroupCollectorInterface */ - public function withAttachmentInformation(): GroupCollectorInterface; + public function withAttachmentInformation(): self; /** * Limit results to transactions without a bill.. * * @return GroupCollectorInterface */ - public function withBill(): GroupCollectorInterface; + public function withBill(): self; /** * Include bill name + ID. * * @return GroupCollectorInterface */ - public function withBillInformation(): GroupCollectorInterface; + public function withBillInformation(): self; /** * Limit results to a transactions with a budget. * * @return GroupCollectorInterface */ - public function withBudget(): GroupCollectorInterface; + public function withBudget(): self; /** * Will include budget ID + name, if any. * * @return GroupCollectorInterface */ - public function withBudgetInformation(): GroupCollectorInterface; + public function withBudgetInformation(): self; /** * Limit results to a transactions with a category. * * @return GroupCollectorInterface */ - public function withCategory(): GroupCollectorInterface; + public function withCategory(): self; /** * Will include category ID + name, if any. * * @return GroupCollectorInterface */ - public function withCategoryInformation(): GroupCollectorInterface; + public function withCategoryInformation(): self; /** * Transactions with any external ID * * @return GroupCollectorInterface */ - public function withExternalId(): GroupCollectorInterface; + public function withExternalId(): self; /** * Transactions with any external URL * * @return GroupCollectorInterface */ - public function withExternalUrl(): GroupCollectorInterface; + public function withExternalUrl(): self; /** * Transaction must have meta date field X. @@ -1452,94 +1452,94 @@ interface GroupCollectorInterface * * @return GroupCollectorInterface */ - public function withMetaDate(string $field): GroupCollectorInterface; + public function withMetaDate(string $field): self; /** * Will include notes. * * @return GroupCollectorInterface */ - public function withNotes(): GroupCollectorInterface; + public function withNotes(): self; /** * Add tag info. * * @return GroupCollectorInterface */ - public function withTagInformation(): GroupCollectorInterface; + public function withTagInformation(): self; /** * Limit results to a transactions without a bill. * * @return GroupCollectorInterface */ - public function withoutBill(): GroupCollectorInterface; + public function withoutBill(): self; /** * Limit results to a transactions without a budget. * * @return GroupCollectorInterface */ - public function withoutBudget(): GroupCollectorInterface; + public function withoutBudget(): self; /** * Limit results to a transactions without a category. * * @return GroupCollectorInterface */ - public function withoutCategory(): GroupCollectorInterface; + public function withoutCategory(): self; /** * Transactions without an external ID * * @return GroupCollectorInterface */ - public function withoutExternalId(): GroupCollectorInterface; + public function withoutExternalId(): self; /** * Transactions without an external URL * * @return GroupCollectorInterface */ - public function withoutExternalUrl(): GroupCollectorInterface; + public function withoutExternalUrl(): self; /** * @return GroupCollectorInterface */ - public function withoutNotes(): GroupCollectorInterface; + public function withoutNotes(): self; /** * @return GroupCollectorInterface */ - public function withoutTags(): GroupCollectorInterface; + public function withoutTags(): self; /** * @param string $year * * @return GroupCollectorInterface */ - public function yearAfter(string $year): GroupCollectorInterface; + public function yearAfter(string $year): self; /** * @param string $year * * @return GroupCollectorInterface */ - public function yearBefore(string $year): GroupCollectorInterface; + public function yearBefore(string $year): self; /** * @param string $year * * @return GroupCollectorInterface */ - public function yearIs(string $year): GroupCollectorInterface; + public function yearIs(string $year): self; /** * @param string $year * * @return GroupCollectorInterface */ - public function yearIsNot(string $year): GroupCollectorInterface; + public function yearIsNot(string $year): self; } diff --git a/app/Helpers/Fiscal/FiscalHelper.php b/app/Helpers/Fiscal/FiscalHelper.php index a1c86ce9eb..1b6b23dd06 100644 --- a/app/Helpers/Fiscal/FiscalHelper.php +++ b/app/Helpers/Fiscal/FiscalHelper.php @@ -52,7 +52,7 @@ class FiscalHelper implements FiscalHelperInterface */ public function endOfFiscalYear(Carbon $date): Carbon { - // Log::debug(sprintf('Now in endOfFiscalYear(%s).', $date->format('Y-m-d'))); + // app('log')->debug(sprintf('Now in endOfFiscalYear(%s).', $date->format('Y-m-d'))); $endDate = $this->startOfFiscalYear($date); if (true === $this->useCustomFiscalYear) { // add 1 year and sub 1 day @@ -62,7 +62,7 @@ class FiscalHelper implements FiscalHelperInterface if (false === $this->useCustomFiscalYear) { $endDate->endOfYear(); } - // Log::debug(sprintf('Result of endOfFiscalYear(%s) = %s', $date->format('Y-m-d'), $endDate->format('Y-m-d'))); + // app('log')->debug(sprintf('Result of endOfFiscalYear(%s) = %s', $date->format('Y-m-d'), $endDate->format('Y-m-d'))); return $endDate; } @@ -80,6 +80,10 @@ class FiscalHelper implements FiscalHelperInterface $startDate = clone $date; if (true === $this->useCustomFiscalYear) { $prefStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data; + if (is_array($prefStartStr)) { + $prefStartStr = '01-01'; + } + $prefStartStr = (string)$prefStartStr; [$mth, $day] = explode('-', $prefStartStr); $startDate->day((int)$day)->month((int)$mth); @@ -92,7 +96,7 @@ class FiscalHelper implements FiscalHelperInterface $startDate->startOfYear(); } - // Log::debug(sprintf('Result of startOfFiscalYear(%s) = %s', $date->format('Y-m-d'), $startDate->format('Y-m-d'))); + // app('log')->debug(sprintf('Result of startOfFiscalYear(%s) = %s', $date->format('Y-m-d'), $startDate->format('Y-m-d'))); return $startDate; } diff --git a/app/Helpers/Report/NetWorth.php b/app/Helpers/Report/NetWorth.php index de4a6d0d90..67e5238f03 100644 --- a/app/Helpers/Report/NetWorth.php +++ b/app/Helpers/Report/NetWorth.php @@ -36,7 +36,6 @@ use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use JsonException; /** * This class can handle both request with and without a user group and will return the appropriate repository when @@ -54,6 +53,11 @@ class NetWorth implements NetWorthInterface private null | UserGroup $userGroup; /** + * This method collects the user's net worth in ALL the user's currencies + * (1, 4 and 8) and also in the 'native' currency for ease of use. + * + * The set of accounts has to be fed to it. + * * @param Collection $accounts * @param Carbon $date * @@ -81,16 +85,16 @@ class NetWorth implements NetWorthInterface 'native' => [ 'balance' => '0', 'native_balance' => '0', - 'currency_id' => (int)$default->id, + 'currency_id' => $default->id, 'currency_code' => $default->code, 'currency_name' => $default->name, 'currency_symbol' => $default->symbol, - 'currency_decimal_places' => (int)$default->decimal_places, - 'native_id' => (int)$default->id, + 'currency_decimal_places' => $default->decimal_places, + 'native_id' => $default->id, 'native_code' => $default->code, 'native_name' => $default->name, 'native_symbol' => $default->symbol, - 'native_decimal_places' => (int)$default->decimal_places, + 'native_decimal_places' => $default->decimal_places, ], ]; $balances = app('steam')->balancesByAccountsConverted($accounts, $date); @@ -98,35 +102,38 @@ class NetWorth implements NetWorthInterface /** @var Account $account */ foreach ($accounts as $account) { app('log')->debug(sprintf('Now at account #%d ("%s")', $account->id, $account->name)); - $currency = $this->getRepository()->getAccountCurrency($account); - $currencyId = (int)$currency->id; + $currency = $this->getRepository()->getAccountCurrency($account); + if (null === $currency) { + $currency = app('amount')->getDefaultCurrency(); + } + $currencyId = $currency->id; $balance = '0'; $nativeBalance = '0'; - if (array_key_exists((int)$account->id, $balances)) { - $balance = $balances[(int)$account->id]['balance'] ?? '0'; - $nativeBalance = $balances[(int)$account->id]['native_balance'] ?? '0'; + if (array_key_exists($account->id, $balances)) { + $balance = $balances[$account->id]['balance'] ?? '0'; + $nativeBalance = $balances[$account->id]['native_balance'] ?? '0'; } app('log')->debug(sprintf('Balance is %s, native balance is %s', $balance, $nativeBalance)); // always subtract virtual balance - $virtualBalance = (string)$account->virtual_balance; + $virtualBalance = $account->virtual_balance; if ('' !== $virtualBalance) { $balance = bcsub($balance, $virtualBalance); $nativeVirtualBalance = $converter->convert($default, $currency, $account->created_at, $virtualBalance); $nativeBalance = bcsub($nativeBalance, $nativeVirtualBalance); } - $netWorth[$currencyId] = $netWorth[$currencyId] ?? [ + $netWorth[$currencyId] ??= [ 'balance' => '0', 'native_balance' => '0', 'currency_id' => $currencyId, 'currency_code' => $currency->code, 'currency_name' => $currency->name, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, - 'native_id' => (int)$default->id, + 'currency_decimal_places' => $currency->decimal_places, + 'native_id' => $default->id, 'native_code' => $default->code, 'native_name' => $default->name, 'native_symbol' => $default->symbol, - 'native_decimal_places' => (int)$default->decimal_places, + 'native_decimal_places' => $default->decimal_places, ]; $netWorth[$currencyId]['balance'] = bcadd($balance, $netWorth[$currencyId]['balance']); @@ -150,94 +157,12 @@ class NetWorth implements NetWorthInterface return $this->adminAccountRepository; } - /** - * Returns the user's net worth in an array with the following layout: - * - * - - * - currency: TransactionCurrency object - * - date: the current date - * - amount: the user's net worth in that currency. - * - * This repeats for each currency the user has transactions in. - * Result of this method is cached. - * - * @param Collection $accounts - * @param Carbon $date - * - * @return array - * @throws JsonException - * @throws FireflyException - * @deprecated - */ - public function getNetWorthByCurrency(Collection $accounts, Carbon $date): array - { - // start in the past, end in the future? use $date - $cache = new CacheProperties(); - $cache->addProperty($date); - $cache->addProperty('net-worth-by-currency'); - $cache->addProperty(implode(',', $accounts->pluck('id')->toArray())); - if ($cache->has()) { - return $cache->get(); - } - - $netWorth = []; - $result = []; - // Log::debug(sprintf('Now in getNetWorthByCurrency(%s)', $date->format('Y-m-d'))); - - // get default currency - $default = app('amount')->getDefaultCurrencyByUser($this->user); - - // get all balances: - $balances = app('steam')->balancesByAccounts($accounts, $date); - - // get the preferred currency for this account - /** @var Account $account */ - foreach ($accounts as $account) { - // Log::debug(sprintf('Now at account #%d: "%s"', $account->id, $account->name)); - $currencyId = (int)$this->getRepository()->getMetaValue($account, 'currency_id'); - $currencyId = 0 === $currencyId ? $default->id : $currencyId; - - // Log::debug(sprintf('Currency ID is #%d', $currencyId)); - - // balance in array: - $balance = $balances[$account->id] ?? '0'; - - //Log::debug(sprintf('Balance for %s is %s', $date->format('Y-m-d'), $balance)); - - // always subtract virtual balance. - $virtualBalance = (string)$account->virtual_balance; - if ('' !== $virtualBalance) { - $balance = bcsub($balance, $virtualBalance); - } - - // Log::debug(sprintf('Balance corrected to %s because of virtual balance (%s)', $balance, $virtualBalance)); - - if (!array_key_exists($currencyId, $netWorth)) { - $netWorth[$currencyId] = '0'; - } - $netWorth[$currencyId] = bcadd($balance, $netWorth[$currencyId]); - // Log::debug(sprintf('Total net worth for currency #%d is %s', $currencyId, $netWorth[$currencyId])); - } - ksort($netWorth); - - // loop results and add currency information: - foreach ($netWorth as $currencyId => $balance) { - $result[] = [ - 'currency' => $this->currencyRepos->find($currencyId), - 'balance' => $balance, - ]; - } - $cache->store($result); - - return $result; - } - /** * @param User|Authenticatable|null $user */ public function setUser(User | Authenticatable | null $user): void { - if (null === $user) { + if (!($user instanceof User)) { return; } $this->user = $user; @@ -263,6 +188,7 @@ class NetWorth implements NetWorthInterface /** * @inheritDoc + * @deprecated */ public function sumNetWorthByCurrency(Carbon $date): array { @@ -277,12 +203,12 @@ class NetWorth implements NetWorthInterface $balance = $balances[$account->id] ?? '0'; // always subtract virtual balance. - $virtualBalance = (string)$account->virtual_balance; + $virtualBalance = $account->virtual_balance; if ('' !== $virtualBalance) { $balance = bcsub($balance, $virtualBalance); } - $return[$currency->id] = $return[$currency->id] ?? [ + $return[$currency->id] ??= [ 'id' => (string)$currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, diff --git a/app/Helpers/Report/NetWorthInterface.php b/app/Helpers/Report/NetWorthInterface.php index 61f4c9455c..548401a735 100644 --- a/app/Helpers/Report/NetWorthInterface.php +++ b/app/Helpers/Report/NetWorthInterface.php @@ -50,27 +50,6 @@ interface NetWorthInterface */ public function byAccounts(Collection $accounts, Carbon $date): array; - /** - * TODO unsure why this is deprecated. - * - * Returns the user's net worth in an array with the following layout: - * - * - - * - currency: TransactionCurrency object - * - date: the current date - * - amount: the user's net worth in that currency. - * - * This repeats for each currency the user has transactions in. - * Result of this method is cached. - * - * @param Collection $accounts - * @param Carbon $date - * - * @return array - * @deprecated - */ - public function getNetWorthByCurrency(Collection $accounts, Carbon $date): array; - /** * @param User|Authenticatable|null $user */ @@ -89,6 +68,7 @@ interface NetWorthInterface * @param Carbon $date * * @return array + * @deprecated */ public function sumNetWorthByCurrency(Carbon $date): array; } diff --git a/app/Helpers/Update/UpdateTrait.php b/app/Helpers/Update/UpdateTrait.php index b5443da8de..832623daf9 100644 --- a/app/Helpers/Update/UpdateTrait.php +++ b/app/Helpers/Update/UpdateTrait.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Update; use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -45,11 +44,11 @@ trait UpdateTrait */ public function getLatestRelease(): array { - Log::debug('Now in getLatestRelease()'); + app('log')->debug('Now in getLatestRelease()'); /** @var UpdateRequestInterface $checker */ $checker = app(UpdateRequestInterface::class); $channelConfig = app('fireflyconfig')->get('update_channel', 'stable'); - $channel = $channelConfig ? $channelConfig->data : 'stable'; + $channel = (string)$channelConfig->data; return $checker->getUpdateInformation($channel); } diff --git a/app/Helpers/Webhook/Sha3SignatureGenerator.php b/app/Helpers/Webhook/Sha3SignatureGenerator.php index c51edf0a4f..2bc458d8b0 100644 --- a/app/Helpers/Webhook/Sha3SignatureGenerator.php +++ b/app/Helpers/Webhook/Sha3SignatureGenerator.php @@ -25,7 +25,6 @@ namespace FireflyIII\Helpers\Webhook; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\WebhookMessage; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -45,15 +44,15 @@ class Sha3SignatureGenerator implements SignatureGeneratorInterface if (null === $message->webhook) { throw new FireflyException('Part of a deleted webhook.'); } - + $json = ''; try { $json = json_encode($message->message, JSON_THROW_ON_ERROR); } catch (JsonException $e) { - Log::error('Could not generate hash.'); - Log::error(sprintf('JSON value: %s', $json)); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error('Could not generate hash.'); + app('log')->error(sprintf('JSON value: %s', $json)); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException('Could not generate JSON for SHA3 hash.', 0, $e); } diff --git a/app/Http/Controllers/Account/CreateController.php b/app/Http/Controllers/Account/CreateController.php index ff88e40f5b..d73a1ee05c 100644 --- a/app/Http/Controllers/Account/CreateController.php +++ b/app/Http/Controllers/Account/CreateController.php @@ -152,13 +152,16 @@ class CreateController extends Controller // update preferences if necessary: $frontPage = app('preferences')->get('frontPageAccounts', [])->data; + if (!is_array($frontPage)) { + $frontPage = []; + } if (AccountType::ASSET === $account->accountType->type) { $frontPage[] = $account->id; app('preferences')->set('frontPageAccounts', $frontPage); } // store attachment(s): - /** @var array $files */ + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($account, $files); diff --git a/app/Http/Controllers/Account/EditController.php b/app/Http/Controllers/Account/EditController.php index 292c00ec8c..f227deda97 100644 --- a/app/Http/Controllers/Account/EditController.php +++ b/app/Http/Controllers/Account/EditController.php @@ -88,15 +88,15 @@ class EditController extends Controller $roles = $this->getRoles(); $liabilityTypes = $this->getLiabilityTypes(); $location = $repository->getLocation($account); - $latitude = $location ? $location->latitude : config('firefly.default_location.latitude'); - $longitude = $location ? $location->longitude : config('firefly.default_location.longitude'); - $zoomLevel = $location ? $location->zoom_level : config('firefly.default_location.zoom_level'); + $latitude = null !== $location ? $location->latitude : config('firefly.default_location.latitude'); + $longitude = null !== $location ? $location->longitude : config('firefly.default_location.longitude'); + $zoomLevel = null !== $location ? $location->zoom_level : config('firefly.default_location.zoom_level'); $hasLocation = null !== $location; $locations = [ 'location' => [ - 'latitude' => old('location_latitude') ?? $latitude, - 'longitude' => old('location_longitude') ?? $longitude, - 'zoom_level' => old('location_zoom_level') ?? $zoomLevel, + 'latitude' => null !== old('location_latitude') ? old('location_latitude') : $latitude, + 'longitude' => null !== old('location_longitude') ? old('location_longitude') : $longitude, + 'zoom_level' => null !== old('location_zoom_level') ? old('location_zoom_level') : $zoomLevel, 'has_location' => $hasLocation || 'true' === old('location_has_location'), ], ]; @@ -195,7 +195,7 @@ class EditController extends Controller $request->session()->flash('success', (string)trans('firefly.updated_account', ['name' => $account->name])); // store new attachment(s): - + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($account, $files); diff --git a/app/Http/Controllers/Account/IndexController.php b/app/Http/Controllers/Account/IndexController.php index 3a6915cb8a..df602d506a 100644 --- a/app/Http/Controllers/Account/IndexController.php +++ b/app/Http/Controllers/Account/IndexController.php @@ -32,7 +32,6 @@ use FireflyIII\Support\Http\Controllers\BasicDataSupport; use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use JsonException; use Psr\Container\ContainerExceptionInterface; @@ -138,7 +137,7 @@ class IndexController extends Controller */ public function index(Request $request, string $objectType) { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); $subTitle = (string)trans(sprintf('firefly.%s_accounts', $objectType)); $subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType)); $types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType)); @@ -152,7 +151,7 @@ class IndexController extends Controller $accounts = $collection->slice(($page - 1) * $pageSize, $pageSize); $inactiveCount = $this->repository->getInactiveAccountsByType($types)->count(); - Log::debug(sprintf('Count of collection: %d, count of accounts: %d', $total, $accounts->count())); + app('log')->debug(sprintf('Count of collection: %d, count of accounts: %d', $total, $accounts->count())); unset($collection); /** @var Carbon $start */ @@ -189,13 +188,13 @@ class IndexController extends Controller } ); // make paginator: - Log::debug(sprintf('Count of accounts before LAP: %d', $accounts->count())); + app('log')->debug(sprintf('Count of accounts before LAP: %d', $accounts->count())); /** @var LengthAwarePaginator $accounts */ $accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page); $accounts->setPath(route('accounts.index', [$objectType])); - Log::debug(sprintf('Count of accounts after LAP (1): %d', $accounts->count())); - Log::debug(sprintf('Count of accounts after LAP (2): %d', $accounts->getCollection()->count())); + app('log')->debug(sprintf('Count of accounts after LAP (1): %d', $accounts->count())); + app('log')->debug(sprintf('Count of accounts after LAP (2): %d', $accounts->getCollection()->count())); return view('accounts.index', compact('objectType', 'inactiveCount', 'subTitleIcon', 'subTitle', 'page', 'accounts')); } diff --git a/app/Http/Controllers/Account/ReconcileController.php b/app/Http/Controllers/Account/ReconcileController.php index 414a14b7e7..9ebe1c0d09 100644 --- a/app/Http/Controllers/Account/ReconcileController.php +++ b/app/Http/Controllers/Account/ReconcileController.php @@ -38,7 +38,6 @@ use FireflyIII\User; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use JsonException; use Psr\Container\ContainerExceptionInterface; @@ -169,14 +168,14 @@ class ReconcileController extends Controller return $this->redirectAccountToAccount($account); } - Log::debug('In ReconcileController::submit()'); + app('log')->debug('In ReconcileController::submit()'); $data = $request->getAll(); /** @var string $journalId */ foreach ($data['journals'] as $journalId) { $this->repository->reconcileById((int)$journalId); } - Log::debug('Reconciled all transactions.'); + app('log')->debug('Reconciled all transactions.'); // switch dates if necessary if ($end->lt($start)) { @@ -188,7 +187,7 @@ class ReconcileController extends Controller if ('create' === $data['reconcile']) { $result = $this->createReconciliation($account, $start, $end, $data['difference']); } - Log::debug('End of routine.'); + app('log')->debug('End of routine.'); app('preferences')->mark(); if ('' === $result) { session()->flash('success', (string)trans('firefly.reconciliation_stored')); @@ -208,14 +207,14 @@ class ReconcileController extends Controller * @param Carbon $end * @param string $difference * - * @return RedirectResponse|Redirector|string + * @return string * @throws DuplicateTransactionException * @throws JsonException */ - private function createReconciliation(Account $account, Carbon $start, Carbon $end, string $difference) + private function createReconciliation(Account $account, Carbon $start, Carbon $end, string $difference): string { if (!$this->isEditableAccount($account)) { - return $this->redirectAccountToAccount($account); + return 'not-editable'; } $reconciliation = $this->accountRepos->getReconciliation($account); diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index 7144330d3a..e2e7792f11 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -97,9 +97,9 @@ class ShowController extends Controller } /** @var Carbon $start */ - $start = $start ?? session('start'); + $start ??= session('start'); /** @var Carbon $end */ - $end = $end ?? session('end'); + $end ??= session('end'); if ($end < $start) { [$start, $end] = [$end, $start]; diff --git a/app/Http/Controllers/Admin/HomeController.php b/app/Http/Controllers/Admin/HomeController.php index 38c5799cc6..d84d9a7eda 100644 --- a/app/Http/Controllers/Admin/HomeController.php +++ b/app/Http/Controllers/Admin/HomeController.php @@ -26,7 +26,6 @@ namespace FireflyIII\Http\Controllers\Admin; use FireflyIII\Events\AdminRequestedTestMessage; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Middleware\IsDemoUser; -use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Contracts\View\Factory; @@ -75,9 +74,9 @@ class HomeController extends Controller // admin notification settings: $notifications = []; foreach (config('firefly.admin_notifications') as $item) { - $notifications[$item] = FireflyConfig::get(sprintf('notification_%s', $item), true)->data; + $notifications[$item] = app('fireflyconfig')->get(sprintf('notification_%s', $item), true)->data; } - $slackUrl = FireflyConfig::get('slack_webhook_url', '')->data; + $slackUrl = app('fireflyconfig')->get('slack_webhook_url', '')->data; return view('admin.index', compact('title', 'mainTitleIcon', 'email', 'notifications', 'slackUrl')); } @@ -94,14 +93,14 @@ class HomeController extends Controller if ($request->has(sprintf('notification_%s', $item))) { $value = true; } - FireflyConfig::set(sprintf('notification_%s', $item), $value); + app('fireflyconfig')->set(sprintf('notification_%s', $item), $value); } $url = (string)$request->get('slackUrl'); if ('' === $url) { - FireflyConfig::delete('slack_webhook_url'); + app('fireflyconfig')->delete('slack_webhook_url'); } if (UrlValidator::isValidWebhookURL($url)) { - FireflyConfig::set('slack_webhook_url', $url); + app('fireflyconfig')->set('slack_webhook_url', $url); } session()->flash('success', (string)trans('firefly.notification_settings_saved')); @@ -111,16 +110,14 @@ class HomeController extends Controller /** * Send a test message to the admin. * - * @param Request $request - * * @return RedirectResponse|Redirector */ - public function testMessage(Request $request) + public function testMessage() { Log::channel('audit')->info('User sends test message.'); /** @var User $user */ $user = auth()->user(); - Log::debug('Now in testMessage() controller.'); + app('log')->debug('Now in testMessage() controller.'); event(new AdminRequestedTestMessage($user)); session()->flash('info', (string)trans('firefly.send_test_triggered')); diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 12808973f3..3c863ab747 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -36,7 +36,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -94,13 +93,13 @@ class UserController extends Controller */ public function deleteInvite(InvitedUser $invitedUser): JsonResponse { - Log::debug('Will now delete invitation'); + app('log')->debug('Will now delete invitation'); if ($invitedUser->redeemed) { - Log::debug('Is already redeemed.'); + app('log')->debug('Is already redeemed.'); session()->flash('error', trans('firefly.invite_is_already_redeemed', ['address' => $invitedUser->email])); return response()->json(['success' => false]); } - Log::debug('Delete!'); + app('log')->debug('Delete!'); session()->flash('success', trans('firefly.invite_is_deleted', ['address' => $invitedUser->email])); $this->repository->deleteInvite($invitedUser); return response()->json(['success' => true]); @@ -171,7 +170,7 @@ class UserController extends Controller $subTitle = (string)trans('firefly.user_administration'); $subTitleIcon = 'fa-users'; $users = $this->repository->all(); - $singleUserMode = app('fireflyconfig')->get('single_user_mode', config('firefly.configuration.single_user_mode'))->data; + $singleUserMode = (bool)app('fireflyconfig')->get('single_user_mode', config('firefly.configuration.single_user_mode'))->data; $allowInvites = false; if (!$this->externalIdentity && $singleUserMode) { // also registration enabled. @@ -246,7 +245,7 @@ class UserController extends Controller */ public function update(UserFormRequest $request, User $user) { - Log::debug('Actually here'); + app('log')->debug('Actually here'); $data = $request->getUserData(); //var_dump($data); diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 6f45458f30..e7a6f71a31 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -41,8 +41,7 @@ use Illuminate\View\View; */ class AttachmentController extends Controller { - /** @var AttachmentRepositoryInterface Attachment repository */ - private $repository; + private AttachmentRepositoryInterface $repository; /** * AttachmentController constructor. @@ -128,7 +127,7 @@ class AttachmentController extends Controller ->header('Expires', '0') ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') ->header('Pragma', 'public') - ->header('Content-Length', strlen($content)); + ->header('Content-Length', (string)strlen($content)); return $response; } @@ -210,14 +209,13 @@ class AttachmentController extends Controller /** * View attachment in browser. * - * @param Request $request * @param Attachment $attachment * * @return LaravelResponse * @throws FireflyException * @throws BindingResolutionException */ - public function view(Request $request, Attachment $attachment): LaravelResponse + public function view(Attachment $attachment): LaravelResponse { if ($this->repository->exists($attachment)) { $content = $this->repository->getContent($attachment); diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 2dd3028ef2..6149fd77ad 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -31,7 +31,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -68,10 +67,10 @@ class ForgotPasswordController extends Controller */ public function sendResetLinkEmail(Request $request, UserRepositoryInterface $repository) { - Log::info('Start of sendResetLinkEmail()'); + app('log')->info('Start of sendResetLinkEmail()'); if ('web' !== config('firefly.authentication_guard')) { $message = sprintf('Cannot reset password when authenticating over "%s".', config('firefly.authentication_guard')); - Log::error($message); + app('log')->error($message); return view('error', compact('message')); } @@ -80,7 +79,7 @@ class ForgotPasswordController extends Controller $this->validateEmail($request); // verify if the user is not a demo user. If so, we give him back an error. - /** @var User $user */ + /** @var User|null $user */ $user = User::where('email', $request->get('email'))->first(); if (null !== $user && $repository->hasRole($user, 'demo')) { @@ -92,7 +91,7 @@ class ForgotPasswordController extends Controller // need to show to the user. Finally, we'll send out a proper response. $result = $this->broker()->sendResetLink($request->only('email')); if ('passwords.throttled' === $result) { - Log::error(sprintf('Cowardly refuse to send a password reset message to user #%d because the reset button has been throttled.', $user->id)); + app('log')->error(sprintf('Cowardly refuse to send a password reset message to user #%d because the reset button has been throttled.', $user->id)); } // always send the same response to the user: diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 7a1892799d..bd8a490e9c 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -34,6 +34,7 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Foundation\Auth\ThrottlesLogins; +use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; @@ -81,24 +82,24 @@ class LoginController extends Controller /** * Handle a login request to the application. * - * + * @return JsonResponse|RedirectResponse * @throws ValidationException */ - public function login(Request $request) + public function login(Request $request): JsonResponse | RedirectResponse { Log::channel('audit')->info(sprintf('User is trying to login using "%s"', $request->get($this->username()))); - Log::info('User is trying to login.'); + app('log')->info('User is trying to login.'); $this->validateLogin($request); - Log::debug('Login data is present.'); + app('log')->debug('Login data is present.'); /** Copied directly from AuthenticatesUsers, but with logging added: */ // If the class is using the ThrottlesLogins trait, we can automatically throttle // the login attempts for this application. We'll key this by the username and // the IP address of the client making these requests into this application. - if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) { + if ($this->hasTooManyLoginAttempts($request)) { Log::channel('audit')->info(sprintf('Login for user "%s" was locked out.', $request->get($this->username()))); - Log::error(sprintf('Login for user "%s" was locked out.', $request->get($this->username()))); + app('log')->error(sprintf('Login for user "%s" was locked out.', $request->get($this->username()))); $this->fireLockoutEvent($request); $this->sendLockoutResponse($request); @@ -106,7 +107,7 @@ class LoginController extends Controller /** Copied directly from AuthenticatesUsers, but with logging added: */ if ($this->attemptLogin($request)) { Log::channel('audit')->info(sprintf('User "%s" has been logged in.', $request->get($this->username()))); - Log::debug(sprintf('Redirect after login is %s.', $this->redirectPath())); + app('log')->debug(sprintf('Redirect after login is %s.', $this->redirectPath())); // if you just logged in, it can't be that you have a valid 2FA cookie. @@ -126,6 +127,9 @@ class LoginController extends Controller Log::channel('audit')->info(sprintf('Login failed. Attempt for user "%s" failed.', $request->get($this->username()))); $this->sendFailedLoginResponse($request); + + /** @noinspection PhpUnreachableStatementInspection */ + return response()->json([]); } /** @@ -144,7 +148,7 @@ class LoginController extends Controller * @param Request $request * * @return void - * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @throws ValidationException */ protected function sendFailedLoginResponse(Request $request) @@ -187,9 +191,7 @@ class LoginController extends Controller $request->session()->regenerateToken(); - if ($response = $this->loggedOut($request)) { - return $response; - } + $this->loggedOut($request); return $request->wantsJson() ? new Response('', 204) diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 331ecc66bc..31a0b2cf3a 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -35,7 +35,6 @@ use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException; use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; @@ -52,8 +51,8 @@ use Psr\Container\NotFoundExceptionInterface; */ class RegisterController extends Controller { - use RegistersUsers; use CreateStuff; + use RegistersUsers; /** * Where to redirect users after registration. @@ -98,7 +97,7 @@ class RegisterController extends Controller $this->validator($request->all())->validate(); $user = $this->createUser($request->all()); - Log::info(sprintf('Registered new user %s', $user->email)); + app('log')->info(sprintf('Registered new user %s', $user->email)); event(new RegisteredUser($user)); $this->guard()->login($user); diff --git a/app/Http/Controllers/Auth/TwoFactorController.php b/app/Http/Controllers/Auth/TwoFactorController.php index 7bd4eabc5b..7098c7bf7d 100644 --- a/app/Http/Controllers/Auth/TwoFactorController.php +++ b/app/Http/Controllers/Auth/TwoFactorController.php @@ -61,7 +61,7 @@ class TwoFactorController extends Controller public function submitMFA(Request $request) { /** @var array $mfaHistory */ - $mfaHistory = Preferences::get('mfa_history', [])->data; + $mfaHistory = app('preferences')->get('mfa_history', [])->data; $mfaCode = (string)$request->get('one_time_password'); // is in history? then refuse to use it. @@ -127,7 +127,7 @@ class TwoFactorController extends Controller private function filterMFAHistory(): void { /** @var array $mfaHistory */ - $mfaHistory = Preferences::get('mfa_history', [])->data; + $mfaHistory = app('preferences')->get('mfa_history', [])->data; $newHistory = []; $now = time(); foreach ($mfaHistory as $entry) { @@ -140,7 +140,7 @@ class TwoFactorController extends Controller ]; } } - Preferences::set('mfa_history', $newHistory); + app('preferences')->set('mfa_history', $newHistory); } /** @@ -149,14 +149,14 @@ class TwoFactorController extends Controller private function addToMFAHistory(string $mfaCode): void { /** @var array $mfaHistory */ - $mfaHistory = Preferences::get('mfa_history', [])->data; + $mfaHistory = app('preferences')->get('mfa_history', [])->data; $entry = [ 'time' => time(), 'code' => $mfaCode, ]; $mfaHistory[] = $entry; - Preferences::set('mfa_history', $mfaHistory); + app('preferences')->set('mfa_history', $mfaHistory); $this->filterMFAHistory(); } @@ -169,7 +169,10 @@ class TwoFactorController extends Controller */ private function isBackupCode(string $mfaCode): bool { - $list = Preferences::get('mfa_recovery', [])->data; + $list = app('preferences')->get('mfa_recovery', [])->data; + if (!is_array($list)) { + $list = []; + } if (in_array($mfaCode, $list, true)) { return true; } @@ -184,8 +187,11 @@ class TwoFactorController extends Controller */ private function removeFromBackupCodes(string $mfaCode): void { - $list = Preferences::get('mfa_recovery', [])->data; + $list = app('preferences')->get('mfa_recovery', [])->data; + if (!is_array($list)) { + $list = []; + } $newList = array_values(array_diff($list, [$mfaCode])); - Preferences::set('mfa_recovery', $newList); + app('preferences')->set('mfa_recovery', $newList); } } diff --git a/app/Http/Controllers/Bill/CreateController.php b/app/Http/Controllers/Bill/CreateController.php index 8dfdde0f48..4976785400 100644 --- a/app/Http/Controllers/Bill/CreateController.php +++ b/app/Http/Controllers/Bill/CreateController.php @@ -33,7 +33,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; /** * Class CreateController @@ -107,7 +106,7 @@ class CreateController extends Controller try { $bill = $this->repository->store($billData); } catch (FireflyException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); $request->session()->flash('error', (string)trans('firefly.bill_store_error')); return redirect(route('bills.create'))->withInput(); @@ -115,7 +114,7 @@ class CreateController extends Controller $request->session()->flash('success', (string)trans('firefly.stored_new_bill', ['name' => $bill->name])); app('preferences')->mark(); - /** @var array $files */ + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($bill, $files); diff --git a/app/Http/Controllers/Bill/EditController.php b/app/Http/Controllers/Bill/EditController.php index f3f9e935cb..b6a8e89735 100644 --- a/app/Http/Controllers/Bill/EditController.php +++ b/app/Http/Controllers/Bill/EditController.php @@ -103,7 +103,7 @@ class EditController extends Controller 'notes' => $this->repository->getNoteText($bill), 'transaction_currency_id' => $bill->transaction_currency_id, 'active' => $hasOldInput ? (bool)$request->old('active') : $bill->active, - 'object_group' => $bill->objectGroups->first() ? $bill->objectGroups->first()->title : '', + 'object_group' => null !== $bill->objectGroups->first() ? $bill->objectGroups->first()->title : '', ]; $request->session()->flash('preFilled', $preFilled); @@ -128,7 +128,7 @@ class EditController extends Controller $request->session()->flash('success', (string)trans('firefly.updated_bill', ['name' => $bill->name])); app('preferences')->mark(); - /** @var array $files */ + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($bill, $files); diff --git a/app/Http/Controllers/Bill/IndexController.php b/app/Http/Controllers/Bill/IndexController.php index 9c82002fc0..60079d3eeb 100644 --- a/app/Http/Controllers/Bill/IndexController.php +++ b/app/Http/Controllers/Bill/IndexController.php @@ -30,9 +30,11 @@ use FireflyIII\Models\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\ObjectGroup\OrganisesObjectGroups; use FireflyIII\Transformers\BillTransformer; +use Illuminate\Contracts\View\Factory; +use Illuminate\Contracts\View\View; +use Illuminate\Foundation\Application; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\HttpFoundation\ParameterBag; @@ -69,7 +71,7 @@ class IndexController extends Controller /** * Show all bills. */ - public function index() + public function index(): View | Application | Factory | \Illuminate\Contracts\Foundation\Application { $this->cleanupObjectGroups(); $this->repository->correctOrder(); @@ -107,7 +109,7 @@ class IndexController extends Controller $array = $transformer->transform($bill); $groupOrder = (int)$array['object_group_order']; // make group array if necessary: - $bills[$groupOrder] = $bills[$groupOrder] ?? [ + $bills[$groupOrder] ??= [ 'object_group_id' => $array['object_group_id'], 'object_group_title' => $array['object_group_title'], 'bills' => [], @@ -157,7 +159,7 @@ class IndexController extends Controller } $currencyId = $bill['currency_id']; - $sums[$groupOrder][$currencyId] = $sums[$groupOrder][$currencyId] ?? [ + $sums[$groupOrder][$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_code' => $bill['currency_code'], 'currency_name' => $bill['currency_name'], @@ -190,8 +192,8 @@ class IndexController extends Controller { $avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2'); - Log::debug(sprintf('Amount per period for bill #%d "%s"', $bill['id'], $bill['name'])); - Log::debug(sprintf('Average is %s', $avg)); + app('log')->debug(sprintf('Amount per period for bill #%d "%s"', $bill['id'], $bill['name'])); + app('log')->debug(sprintf('Average is %s', $avg)); // calculate amount per year: $multiplies = [ 'yearly' => '1', @@ -202,7 +204,7 @@ class IndexController extends Controller 'daily' => '365.24', ]; $yearAmount = bcmul($avg, bcdiv($multiplies[$bill['repeat_freq']], (string)($bill['skip'] + 1))); - Log::debug(sprintf('Amount per year is %s (%s * %s / %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']], (string)($bill['skip'] + 1))); + app('log')->debug(sprintf('Amount per year is %s (%s * %s / %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']], (string)($bill['skip'] + 1))); // per period: $division = [ @@ -222,7 +224,7 @@ class IndexController extends Controller ]; $perPeriod = bcdiv($yearAmount, $division[$range]); - Log::debug(sprintf('Amount per %s is %s (%s / %s)', $range, $perPeriod, $yearAmount, $division[$range])); + app('log')->debug(sprintf('Amount per %s is %s (%s / %s)', $range, $perPeriod, $yearAmount, $division[$range])); return $perPeriod; } @@ -247,7 +249,7 @@ class IndexController extends Controller * @var array $entry */ foreach ($array as $currencyId => $entry) { - $totals[$currencyId] = $totals[$currencyId] ?? [ + $totals[$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_code' => $entry['currency_code'], 'currency_name' => $entry['currency_name'], diff --git a/app/Http/Controllers/Bill/ShowController.php b/app/Http/Controllers/Bill/ShowController.php index 327c84c5f9..c59850189c 100644 --- a/app/Http/Controllers/Bill/ShowController.php +++ b/app/Http/Controllers/Bill/ShowController.php @@ -90,11 +90,7 @@ class ShowController extends Controller return redirect(route('bills.show', [$bill->id])); } - $set = new Collection(); - if (true === $bill->active) { - $set = $this->repository->getRulesForBill($bill); - $total = 0; - } + $set = $this->repository->getRulesForBill($bill); if (0 === $set->count()) { $request->session()->flash('error', (string)trans('firefly.no_rules_for_bill')); @@ -112,7 +108,7 @@ class ShowController extends Controller // file the rule(s) $ruleEngine->fire(); - $request->session()->flash('success', (string)trans_choice('firefly.rescanned_bill', $total)); + $request->session()->flash('success', trans_choice('firefly.rescanned_bill', $total)); app('preferences')->mark(); return redirect(route('bills.show', [$bill->id])); @@ -146,6 +142,10 @@ class ShowController extends Controller $manager->setSerializer(new DataArraySerializer()); $manager->parseIncludes(['attachments', 'notes']); + // add another period to end, could fix 8163 + $range = app('navigation')->getViewRange(true); + $end = app('navigation')->addPeriod($end, $range); + // Make a resource out of the data and $parameters = new ParameterBag(); $parameters->set('start', $start); diff --git a/app/Http/Controllers/Budget/BudgetLimitController.php b/app/Http/Controllers/Budget/BudgetLimitController.php index 8cc83335cd..cc9d868ca4 100644 --- a/app/Http/Controllers/Budget/BudgetLimitController.php +++ b/app/Http/Controllers/Budget/BudgetLimitController.php @@ -40,7 +40,6 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; /** @@ -107,12 +106,11 @@ class BudgetLimitController extends Controller } /** - * @param Request $request * @param BudgetLimit $budgetLimit * * @return RedirectResponse|Redirector */ - public function delete(Request $request, BudgetLimit $budgetLimit) + public function delete(BudgetLimit $budgetLimit) { $this->blRepository->destroyBudgetLimit($budgetLimit); session()->flash('success', trans('firefly.deleted_bl')); @@ -130,15 +128,20 @@ class BudgetLimitController extends Controller */ public function store(Request $request): RedirectResponse | JsonResponse { - Log::debug('Going to store new budget-limit.', $request->all()); + app('log')->debug('Going to store new budget-limit.', $request->all()); // first search for existing one and update it if necessary. $currency = $this->currencyRepos->find((int)$request->get('transaction_currency_id')); $budget = $this->repository->find((int)$request->get('budget_id')); if (null === $currency || null === $budget) { throw new FireflyException('No valid currency or budget.'); } - $start = Carbon::createFromFormat('Y-m-d', $request->get('start')); - $end = Carbon::createFromFormat('Y-m-d', $request->get('end')); + $start = Carbon::createFromFormat('Y-m-d', $request->get('start')); + $end = Carbon::createFromFormat('Y-m-d', $request->get('end')); + + if (false === $start || false === $end) { + return response()->json([]); + } + $amount = (string)$request->get('amount'); $start->startOfDay(); $end->startOfDay(); @@ -147,7 +150,7 @@ class BudgetLimitController extends Controller return response()->json([]); } - Log::debug(sprintf('Start: %s, end: %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); + app('log')->debug(sprintf('Start: %s, end: %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); $limit = $this->blRepository->find($budget, $currency, $start, $end); diff --git a/app/Http/Controllers/Budget/CreateController.php b/app/Http/Controllers/Budget/CreateController.php index da18c84c23..451da6fe52 100644 --- a/app/Http/Controllers/Budget/CreateController.php +++ b/app/Http/Controllers/Budget/CreateController.php @@ -125,6 +125,7 @@ class CreateController extends Controller app('preferences')->mark(); // store attachment(s): + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($budget, $files); diff --git a/app/Http/Controllers/Budget/EditController.php b/app/Http/Controllers/Budget/EditController.php index 76a2bedbcd..1d0c009ddb 100644 --- a/app/Http/Controllers/Budget/EditController.php +++ b/app/Http/Controllers/Budget/EditController.php @@ -100,8 +100,12 @@ class EditController extends Controller 'active' => $hasOldInput ? (bool)$request->old('active') : $budget->active, 'auto_budget_currency_id' => $hasOldInput ? (int)$request->old('auto_budget_currency_id') : $currency->id, ]; - if ($autoBudget) { - $amount = $hasOldInput ? $request->old('auto_budget_amount') : $autoBudget->amount; + if (null !== $autoBudget) { + $amount = $hasOldInput ? $request->old('auto_budget_amount') : $autoBudget->amount; + if (is_array($amount)) { + $amount = '0'; + } + $amount = (string)$amount; $preFilled['auto_budget_amount'] = app('steam')->bcround($amount, $autoBudget->transactionCurrency->decimal_places); } @@ -135,6 +139,7 @@ class EditController extends Controller $redirect = redirect($this->getPreviousUrl('budgets.edit.url')); // store new attachment(s): + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($budget, $files); diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index 22eb85d586..4a8ce7c181 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -40,7 +40,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use JsonException; use Psr\Container\ContainerExceptionInterface; @@ -88,8 +87,6 @@ class IndexController extends Controller /** * Show all budgets. * - * @param Request $request - * * @param Carbon|null $start * @param Carbon|null $end * @@ -99,23 +96,23 @@ class IndexController extends Controller * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function index(Request $request, Carbon $start = null, Carbon $end = null) + public function index(Carbon $start = null, Carbon $end = null) { $this->abRepository->cleanup(); - Log::debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d'))); + app('log')->debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d'))); // collect some basic vars: $range = app('navigation')->getViewRange(true); $isCustomRange = session('is_custom_range', false); if (false === $isCustomRange) { - $start = $start ?? session('start', today(config('app.timezone'))->startOfMonth()); - $end = $end ?? app('navigation')->endOfPeriod($start, $range); + $start ??= session('start', today(config('app.timezone'))->startOfMonth()); + $end ??= app('navigation')->endOfPeriod($start, $range); } // overrule start and end if necessary: if (true === $isCustomRange) { - $start = $start ?? session('start', today(config('app.timezone'))->startOfMonth()); - $end = $end ?? session('end', today(config('app.timezone'))->endOfMonth()); + $start ??= session('start', today(config('app.timezone'))->startOfMonth()); + $end ??= session('end', today(config('app.timezone'))->endOfMonth()); } @@ -219,12 +216,12 @@ class IndexController extends Controller // get all budgets, and paginate them into $budgets. $collection = $this->repository->getActiveBudgets(); $budgets = []; - Log::debug(sprintf('7) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); + app('log')->debug(sprintf('7) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); // complement budget with budget limits in range, and expenses in currency X in range. /** @var Budget $current */ foreach ($collection as $current) { - Log::debug(sprintf('Working on budget #%d ("%s")', $current->id, $current->name)); + app('log')->debug(sprintf('Working on budget #%d ("%s")', $current->id, $current->name)); $array = $current->toArray(); $array['spent'] = []; $array['spent_total'] = []; @@ -234,7 +231,7 @@ class IndexController extends Controller $budgetLimits = $this->blRepository->getBudgetLimits($current, $start, $end); /** @var BudgetLimit $limit */ foreach ($budgetLimits as $limit) { - Log::debug(sprintf('Working on budget limit #%d', $limit->id)); + app('log')->debug(sprintf('Working on budget limit #%d', $limit->id)); $currency = $limit->transactionCurrency ?? $defaultCurrency; $amount = app('steam')->bcround($limit->amount, $currency->decimal_places); $array['budgeted'][] = [ @@ -248,7 +245,7 @@ class IndexController extends Controller 'currency_name' => $currency->name, 'currency_decimal_places' => $currency->decimal_places, ]; - Log::debug(sprintf('The amount budgeted for budget limit #%d is %s %s', $limit->id, $currency->code, $amount)); + app('log')->debug(sprintf('The amount budgeted for budget limit #%d is %s %s', $limit->id, $currency->code, $amount)); } /** @var TransactionCurrency $currency */ @@ -286,13 +283,12 @@ class IndexController extends Controller foreach ($budget['spent'] as $spent) { $currencyId = $spent['currency_id']; $sums['spent'][$currencyId] - = $sums['spent'][$currencyId] - ?? [ - 'amount' => '0', - 'currency_id' => $spent['currency_id'], - 'currency_symbol' => $spent['currency_symbol'], - 'currency_decimal_places' => $spent['currency_decimal_places'], - ]; + ??= [ + 'amount' => '0', + 'currency_id' => $spent['currency_id'], + 'currency_symbol' => $spent['currency_symbol'], + 'currency_decimal_places' => $spent['currency_decimal_places'], + ]; $sums['spent'][$currencyId]['amount'] = bcadd($sums['spent'][$currencyId]['amount'], $spent['spent']); } @@ -300,31 +296,29 @@ class IndexController extends Controller foreach ($budget['budgeted'] as $budgeted) { $currencyId = $budgeted['currency_id']; $sums['budgeted'][$currencyId] - = $sums['budgeted'][$currencyId] - ?? [ - 'amount' => '0', - 'currency_id' => $budgeted['currency_id'], - 'currency_symbol' => $budgeted['currency_symbol'], - 'currency_decimal_places' => $budgeted['currency_decimal_places'], - ]; + ??= [ + 'amount' => '0', + 'currency_id' => $budgeted['currency_id'], + 'currency_symbol' => $budgeted['currency_symbol'], + 'currency_decimal_places' => $budgeted['currency_decimal_places'], + ]; $sums['budgeted'][$currencyId]['amount'] = bcadd($sums['budgeted'][$currencyId]['amount'], $budgeted['amount']); // also calculate how much left from budgeted: - $sums['left'][$currencyId] = $sums['left'][$currencyId] - ?? [ - 'amount' => '0', - 'currency_id' => $budgeted['currency_id'], - 'currency_symbol' => $budgeted['currency_symbol'], - 'currency_decimal_places' => $budgeted['currency_decimal_places'], - ]; + $sums['left'][$currencyId] + ??= [ + 'amount' => '0', + 'currency_id' => $budgeted['currency_id'], + 'currency_symbol' => $budgeted['currency_symbol'], + 'currency_decimal_places' => $budgeted['currency_decimal_places'], + ]; } } // final calculation for 'left': /** - * @var int $currencyId - * @var array $info + * @var int $currencyId */ - foreach ($sums['budgeted'] as $currencyId => $info) { + foreach (array_keys($sums['budgeted']) as $currencyId) { $spent = $sums['spent'][$currencyId]['amount'] ?? '0'; $budgeted = $sums['budgeted'][$currencyId]['amount'] ?? '0'; $sums['left'][$currencyId]['amount'] = bcadd($spent, $budgeted); @@ -348,7 +342,7 @@ class IndexController extends Controller $budgetId = (int)$budgetId; $budget = $repository->find($budgetId); if (null !== $budget) { - Log::debug(sprintf('Set budget #%d ("%s") to position %d', $budget->id, $budget->name, $index + 1)); + app('log')->debug(sprintf('Set budget #%d ("%s") to position %d', $budget->id, $budget->name, $index + 1)); $repository->setBudgetOrder($budget, $index + 1); } } diff --git a/app/Http/Controllers/Budget/ShowController.php b/app/Http/Controllers/Budget/ShowController.php index c07af019e7..9188ddf0bf 100644 --- a/app/Http/Controllers/Budget/ShowController.php +++ b/app/Http/Controllers/Budget/ShowController.php @@ -46,8 +46,8 @@ use Psr\Container\NotFoundExceptionInterface; */ class ShowController extends Controller { - use PeriodOverview; use AugumentData; + use PeriodOverview; protected JournalRepositoryInterface $journalRepos; private BudgetRepositoryInterface $repository; @@ -88,9 +88,9 @@ class ShowController extends Controller public function noBudget(Request $request, Carbon $start = null, Carbon $end = null) { /** @var Carbon $start */ - $start = $start ?? session('start'); + $start ??= session('start'); /** @var Carbon $end */ - $end = $end ?? session('end'); + $end ??= session('end'); $subTitle = trans( 'firefly.without_budget_between', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)] diff --git a/app/Http/Controllers/Category/CreateController.php b/app/Http/Controllers/Category/CreateController.php index 77e2b7ea83..dbd2ed4322 100644 --- a/app/Http/Controllers/Category/CreateController.php +++ b/app/Http/Controllers/Category/CreateController.php @@ -98,6 +98,7 @@ class CreateController extends Controller app('preferences')->mark(); // store attachment(s): + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($category, $files); diff --git a/app/Http/Controllers/Category/EditController.php b/app/Http/Controllers/Category/EditController.php index ba3c591c9d..c5abab1bcc 100644 --- a/app/Http/Controllers/Category/EditController.php +++ b/app/Http/Controllers/Category/EditController.php @@ -105,6 +105,7 @@ class EditController extends Controller app('preferences')->mark(); // store new attachment(s): + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($category, $files); diff --git a/app/Http/Controllers/Category/NoCategoryController.php b/app/Http/Controllers/Category/NoCategoryController.php index 3b8d7bb4b3..00eaa19cc6 100644 --- a/app/Http/Controllers/Category/NoCategoryController.php +++ b/app/Http/Controllers/Category/NoCategoryController.php @@ -33,7 +33,6 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -83,11 +82,11 @@ class NoCategoryController extends Controller */ public function show(Request $request, Carbon $start = null, Carbon $end = null) { - Log::debug('Start of noCategory()'); + app('log')->debug('Start of noCategory()'); /** @var Carbon $start */ - $start = $start ?? session('start'); + $start ??= session('start'); /** @var Carbon $end */ - $end = $end ?? session('end'); + $end ??= session('end'); $page = (int)$request->get('page'); $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; $subTitle = trans( @@ -96,8 +95,8 @@ class NoCategoryController extends Controller ); $periods = $this->getNoCategoryPeriodOverview($start); - Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d'))); - Log::debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d'))); + app('log')->debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d'))); + app('log')->debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d'))); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); @@ -128,13 +127,13 @@ class NoCategoryController extends Controller $periods = new Collection(); $page = (int)$request->get('page'); $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; - Log::debug('Start of noCategory()'); + app('log')->debug('Start of noCategory()'); $subTitle = (string)trans('firefly.all_journals_without_category'); $first = $this->journalRepos->firstNull(); $start = null === $first ? new Carbon() : $first->date; $end = today(config('app.timezone')); - Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d'))); - Log::debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d'))); + app('log')->debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d'))); + app('log')->debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d'))); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); diff --git a/app/Http/Controllers/Category/ShowController.php b/app/Http/Controllers/Category/ShowController.php index e534a4d984..2e78674a41 100644 --- a/app/Http/Controllers/Category/ShowController.php +++ b/app/Http/Controllers/Category/ShowController.php @@ -86,9 +86,9 @@ class ShowController extends Controller public function show(Request $request, Category $category, Carbon $start = null, Carbon $end = null) { /** @var Carbon $start */ - $start = $start ?? session('start', today(config('app.timezone'))->startOfMonth()); + $start ??= session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $end */ - $end = $end ?? session('end', today(config('app.timezone'))->endOfMonth()); + $end ??= session('end', today(config('app.timezone'))->endOfMonth()); $subTitleIcon = 'fa-bookmark'; $page = (int)$request->get('page'); $attachments = $this->repository->getAttachments($category); diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index ab4cdf48e3..4225290114 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -40,7 +40,6 @@ use FireflyIII\Support\Http\Controllers\ChartGeneration; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -51,9 +50,9 @@ use Psr\Container\NotFoundExceptionInterface; */ class AccountController extends Controller { - use DateCalculation; use AugumentData; use ChartGeneration; + use DateCalculation; protected GeneratorInterface $generator; private AccountRepositoryInterface $accountRepository; @@ -126,7 +125,7 @@ class AccountController extends Controller // grab the difference and find the currency. $startAmount = (string)($startBalances[$accountId][$currencyId] ?? '0'); $diff = bcsub((string)$endAmount, $startAmount); - $currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->find($currencyId); + $currencies[$currencyId] ??= $this->currencyRepository->find($currencyId); if (0 !== bccomp($diff, '0')) { // store the values in a temporary array. $tempData[] = [ @@ -335,14 +334,15 @@ class AccountController extends Controller $start = clone session('start', today(config('app.timezone'))->startOfMonth()); $end = clone session('end', today(config('app.timezone'))->endOfMonth()); $defaultSet = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray(); - Log::debug('Default set is ', $defaultSet); - $frontPage = app('preferences')->get('frontPageAccounts', $defaultSet); - Log::debug('Frontpage preference set is ', $frontPage->data); - if (0 === count($frontPage->data)) { + app('log')->debug('Default set is ', $defaultSet); + $frontPage = app('preferences')->get('frontPageAccounts', $defaultSet); + $frontPageArray = !is_array($frontPage->data) ? [] : $frontPage->data; + app('log')->debug('Frontpage preference set is ', $frontPageArray); + if (0 === count($frontPageArray)) { app('preferences')->set('frontPageAccounts', $defaultSet); - Log::debug('frontpage set is empty!'); + app('log')->debug('frontpage set is empty!'); } - $accounts = $repository->getAccountsById($frontPage->data); + $accounts = $repository->getAccountsById($frontPageArray); return response()->json($this->accountBalanceChart($accounts, $start, $end)); } @@ -585,7 +585,7 @@ class AccountController extends Controller // grab the difference and find the currency. $startAmount = (string)($startBalances[$accountId][$currencyId] ?? '0'); $diff = bcsub((string)$endAmount, $startAmount); - $currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->find($currencyId); + $currencies[$currencyId] ??= $this->currencyRepository->find($currencyId); if (0 !== bccomp($diff, '0')) { // store the values in a temporary array. $tempData[] = [ diff --git a/app/Http/Controllers/Chart/BillController.php b/app/Http/Controllers/Chart/BillController.php index 6397554f0d..7ee1e0221b 100644 --- a/app/Http/Controllers/Chart/BillController.php +++ b/app/Http/Controllers/Chart/BillController.php @@ -164,7 +164,7 @@ class BillController extends Controller 'entries' => [], ], ]; - $currencyId = (int)$bill->transaction_currency_id; + $currencyId = $bill->transaction_currency_id; foreach ($journals as $journal) { $date = $journal['date']->isoFormat((string)trans('config.month_and_day_js', [], $locale)); $chartData[0]['entries'][$date] = $bill->amount_min; // minimum amount of bill diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index cc77852515..e58755eb82 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -49,8 +49,8 @@ use Illuminate\Support\Collection; */ class BudgetController extends Controller { - use DateCalculation; use AugumentData; + use DateCalculation; protected GeneratorInterface $generator; protected OperationsRepositoryInterface $opsRepository; @@ -117,7 +117,7 @@ class BudgetController extends Controller foreach ($spent as $row) { $currencyId = $row['currency_id']; - $currencies[$currencyId] = $currencies[$currencyId] ?? $row; // don't mind the field 'sum' + $currencies[$currencyId] ??= $row; // don't mind the field 'sum' // also store this day's sum: $currencies[$currencyId]['spent'][$label] = $row['sum']; } @@ -181,7 +181,7 @@ class BudgetController extends Controller while ($start <= $end) { $current = clone $start; $expenses = $this->opsRepository->sumExpenses($current, $current, null, $budgetCollection, $currency); - $spent = $expenses[(int)$currency->id]['sum'] ?? '0'; + $spent = $expenses[$currency->id]['sum'] ?? '0'; $amount = bcadd($amount, $spent); $format = $start->isoFormat((string)trans('config.month_and_day_js', [], $locale)); $entries[$format] = $amount; @@ -237,7 +237,7 @@ class BudgetController extends Controller // group by asset account ID: foreach ($journals as $journal) { $key = sprintf('%d-%d', (int)$journal['source_account_id'], $journal['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'amount' => '0', 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], @@ -302,7 +302,7 @@ class BudgetController extends Controller $chartData = []; foreach ($journals as $journal) { $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'amount' => '0', 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], @@ -367,7 +367,7 @@ class BudgetController extends Controller /** @var array $journal */ foreach ($journals as $journal) { $key = sprintf('%d-%d', $journal['destination_account_id'], $journal['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'amount' => '0', 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], @@ -429,6 +429,9 @@ class BudgetController extends Controller /** * Shows a budget overview chart (spent and budgeted). * + * Suppress warning because this method will be replaced by API calls. + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * * @param Budget $budget * @param TransactionCurrency $currency * @param Collection $accounts diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php index dcc9758c86..9be5621994 100644 --- a/app/Http/Controllers/Chart/BudgetReportController.php +++ b/app/Http/Controllers/Chart/BudgetReportController.php @@ -87,7 +87,7 @@ class BudgetReportController extends Controller foreach ($spent as $currency) { foreach ($currency['budgets'] as $budget) { $title = sprintf('%s (%s)', $budget['name'], $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -124,7 +124,7 @@ class BudgetReportController extends Controller foreach ($budget['transaction_journals'] as $journal) { $categoryName = $journal['category_name'] ?? trans('firefly.no_category'); $title = sprintf('%s (%s)', $categoryName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -161,7 +161,7 @@ class BudgetReportController extends Controller foreach ($currency['budgets'] as $budget) { foreach ($budget['transaction_journals'] as $journal) { $title = sprintf('%s (%s)', $journal['destination_account_name'], $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -198,7 +198,7 @@ class BudgetReportController extends Controller foreach ($spent as $currency) { // add things to chart Data for each currency: $spentKey = sprintf('%d-spent', $currency['currency_id']); - $chartData[$spentKey] = $chartData[$spentKey] ?? [ + $chartData[$spentKey] ??= [ 'label' => sprintf( '%s (%s)', (string)trans('firefly.spent_in_specific_budget', ['budget' => $budget->name]), @@ -215,7 +215,7 @@ class BudgetReportController extends Controller foreach ($currentBudget['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); $amount = app('steam')->positive($journal['amount']); - $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0'; + $chartData[$spentKey]['entries'][$key] ??= '0'; $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } @@ -269,7 +269,7 @@ class BudgetReportController extends Controller foreach ($currency['budgets'] as $budget) { foreach ($budget['transaction_journals'] as $journal) { $title = sprintf('%s (%s)', $journal['source_account_name'], $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index f600454d4d..087edaeb40 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -47,9 +47,9 @@ use Psr\Container\NotFoundExceptionInterface; */ class CategoryController extends Controller { - use DateCalculation; use AugumentData; use ChartGeneration; + use DateCalculation; /** @var GeneratorInterface Chart generation methods. */ protected $generator; @@ -193,7 +193,7 @@ class CategoryController extends Controller if (null !== $category) { /** @var OperationsRepositoryInterface $opsRepository */ $opsRepository = app(OperationsRepositoryInterface::class); - $categoryId = (int)$category->id; + $categoryId = $category->id; // this gives us all currencies $collection = new Collection([$category]); $expenses = $opsRepository->listExpenses($start, $end, null, $collection); @@ -235,7 +235,7 @@ class CategoryController extends Controller foreach ($outSet['transaction_journals'] as $journal) { $amount = app('steam')->positive($journal['amount']); $date = $journal['date']->isoFormat($format); - $chartData[$outKey]['entries'][$date] = $chartData[$outKey]['entries'][$date] ?? '0'; + $chartData[$outKey]['entries'][$date] ??= '0'; $chartData[$outKey]['entries'][$date] = bcadd($amount, $chartData[$outKey]['entries'][$date]); } @@ -244,7 +244,7 @@ class CategoryController extends Controller foreach ($inSet['transaction_journals'] as $journal) { $amount = app('steam')->positive($journal['amount']); $date = $journal['date']->isoFormat($format); - $chartData[$inKey]['entries'][$date] = $chartData[$inKey]['entries'][$date] ?? '0'; + $chartData[$inKey]['entries'][$date] ??= '0'; $chartData[$inKey]['entries'][$date] = bcadd($amount, $chartData[$inKey]['entries'][$date]); } } diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 9d23654432..25f87a46c3 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -86,7 +86,7 @@ class CategoryReportController extends Controller foreach ($category['transaction_journals'] as $journal) { $objectName = $journal['budget_name'] ?? trans('firefly.no_budget'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -120,7 +120,7 @@ class CategoryReportController extends Controller /** @var array $category */ foreach ($currency['categories'] as $category) { $title = sprintf('%s (%s)', $category['name'], $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -155,7 +155,7 @@ class CategoryReportController extends Controller /** @var array $category */ foreach ($currency['categories'] as $category) { $title = sprintf('%s (%s)', $category['name'], $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -192,7 +192,7 @@ class CategoryReportController extends Controller foreach ($category['transaction_journals'] as $journal) { $objectName = $journal['destination_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -228,7 +228,7 @@ class CategoryReportController extends Controller foreach ($category['transaction_journals'] as $journal) { $objectName = $journal['destination_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -264,7 +264,7 @@ class CategoryReportController extends Controller foreach ($spent as $currency) { // add things to chart Data for each currency: $spentKey = sprintf('%d-spent', $currency['currency_id']); - $chartData[$spentKey] = $chartData[$spentKey] ?? [ + $chartData[$spentKey] ??= [ 'label' => sprintf( '%s (%s)', (string)trans('firefly.spent_in_specific_category', ['category' => $category->name]), @@ -281,7 +281,7 @@ class CategoryReportController extends Controller foreach ($currentCategory['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); $amount = app('steam')->positive($journal['amount']); - $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0'; + $chartData[$spentKey]['entries'][$key] ??= '0'; $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } @@ -291,7 +291,7 @@ class CategoryReportController extends Controller foreach ($earned as $currency) { // add things to chart Data for each currency: $spentKey = sprintf('%d-earned', $currency['currency_id']); - $chartData[$spentKey] = $chartData[$spentKey] ?? [ + $chartData[$spentKey] ??= [ 'label' => sprintf( '%s (%s)', (string)trans('firefly.earned_in_specific_category', ['category' => $category->name]), @@ -308,7 +308,7 @@ class CategoryReportController extends Controller foreach ($currentCategory['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); $amount = app('steam')->positive($journal['amount']); - $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0'; + $chartData[$spentKey]['entries'][$key] ??= '0'; $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } @@ -364,7 +364,7 @@ class CategoryReportController extends Controller foreach ($category['transaction_journals'] as $journal) { $objectName = $journal['source_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -400,7 +400,7 @@ class CategoryReportController extends Controller foreach ($category['transaction_journals'] as $journal) { $objectName = $journal['source_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], diff --git a/app/Http/Controllers/Chart/DoubleReportController.php b/app/Http/Controllers/Chart/DoubleReportController.php index 1cda544991..7e006af206 100644 --- a/app/Http/Controllers/Chart/DoubleReportController.php +++ b/app/Http/Controllers/Chart/DoubleReportController.php @@ -85,7 +85,7 @@ class DoubleReportController extends Controller foreach ($currency['transaction_journals'] as $journal) { $categoryName = $journal['budget_name'] ?? trans('firefly.no_budget'); $title = sprintf('%s (%s)', $categoryName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -120,7 +120,7 @@ class DoubleReportController extends Controller foreach ($currency['transaction_journals'] as $journal) { $categoryName = $journal['category_name'] ?? trans('firefly.no_category'); $title = sprintf('%s (%s)', $categoryName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -155,7 +155,7 @@ class DoubleReportController extends Controller foreach ($currency['transaction_journals'] as $journal) { $categoryName = $journal['category_name'] ?? trans('firefly.no_category'); $title = sprintf('%s (%s)', $categoryName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -195,7 +195,7 @@ class DoubleReportController extends Controller $spentKey = sprintf('%d-spent', $currency['currency_id']); $name = $this->getCounterpartName($accounts, $account->id, $account->name, $account->iban); - $chartData[$spentKey] = $chartData[$spentKey] ?? [ + $chartData[$spentKey] ??= [ 'label' => sprintf( '%s (%s)', (string)trans('firefly.spent_in_specific_double', ['account' => $name]), @@ -211,7 +211,7 @@ class DoubleReportController extends Controller foreach ($currency['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); $amount = app('steam')->positive($journal['amount']); - $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0'; + $chartData[$spentKey]['entries'][$key] ??= '0'; $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } @@ -221,7 +221,7 @@ class DoubleReportController extends Controller $earnedKey = sprintf('%d-earned', $currency['currency_id']); $name = $this->getCounterpartName($accounts, $account->id, $account->name, $account->iban); - $chartData[$earnedKey] = $chartData[$earnedKey] ?? [ + $chartData[$earnedKey] ??= [ 'label' => sprintf( '%s (%s)', (string)trans('firefly.earned_in_specific_double', ['account' => $name]), @@ -237,7 +237,7 @@ class DoubleReportController extends Controller foreach ($currency['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); $amount = app('steam')->positive($journal['amount']); - $chartData[$earnedKey]['entries'][$key] = $chartData[$earnedKey]['entries'][$key] ?? '0'; + $chartData[$earnedKey]['entries'][$key] ??= '0'; $chartData[$earnedKey]['entries'][$key] = bcadd($chartData[$earnedKey]['entries'][$key], $amount); } } @@ -323,7 +323,7 @@ class DoubleReportController extends Controller // do something $tagName = trans('firefly.no_tags'); $title = sprintf('%s (%s)', $tagName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -341,7 +341,7 @@ class DoubleReportController extends Controller // do something $tagName = $tag['name']; $title = sprintf('%s (%s)', $tagName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -383,7 +383,7 @@ class DoubleReportController extends Controller // do something $tagName = trans('firefly.no_tags'); $title = sprintf('%s (%s)', $tagName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -401,7 +401,7 @@ class DoubleReportController extends Controller // do something $tagName = $tag['name']; $title = sprintf('%s (%s)', $tagName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], diff --git a/app/Http/Controllers/Chart/ExpenseReportController.php b/app/Http/Controllers/Chart/ExpenseReportController.php index 6757e856c4..56d2f4c2a0 100644 --- a/app/Http/Controllers/Chart/ExpenseReportController.php +++ b/app/Http/Controllers/Chart/ExpenseReportController.php @@ -147,7 +147,7 @@ class ExpenseReportController extends Controller while ($currentStart < $end) { $currentEnd = clone $currentStart; - $currentEnd = $currentEnd->$function(); + $currentEnd = $currentEnd->$function(); // @phpstan-ignore-line // get expenses grouped by opposing name: $expenses = $this->groupByName($this->getExpensesForOpposing($accounts, $all, $currentStart, $currentEnd)); @@ -166,8 +166,8 @@ class ExpenseReportController extends Controller $currentExpense = $expenses[$name] ?? '0'; // add to sum: - $sumOfIncome[$exp->id] = $sumOfIncome[$exp->id] ?? '0'; - $sumOfExpense[$exp->id] = $sumOfExpense[$exp->id] ?? '0'; + $sumOfIncome[$exp->id] ??= '0'; + $sumOfExpense[$exp->id] ??= '0'; $sumOfIncome[$exp->id] = bcadd($sumOfIncome[$exp->id], $currentIncome); $sumOfExpense[$exp->id] = bcadd($sumOfExpense[$exp->id], $currentExpense); @@ -186,7 +186,7 @@ class ExpenseReportController extends Controller $newSet = []; foreach ($chartData as $key => $entry) { // TODO not sure, this is a bad comparison. - if (0 === !array_sum($entry['entries'])) { + if (array_sum($entry['entries']) > 0) { $newSet[$key] = $entry; } } diff --git a/app/Http/Controllers/Chart/PiggyBankController.php b/app/Http/Controllers/Chart/PiggyBankController.php index 7057f84947..125b7e26d6 100644 --- a/app/Http/Controllers/Chart/PiggyBankController.php +++ b/app/Http/Controllers/Chart/PiggyBankController.php @@ -96,7 +96,7 @@ class PiggyBankController extends Controller $chartData = []; while ($oldest <= $today) { $filtered = $set->filter( - function (PiggyBankEvent $event) use ($oldest) { + static function (PiggyBankEvent $event) use ($oldest) { return $event->date->lte($oldest); } ); @@ -106,7 +106,7 @@ class PiggyBankController extends Controller $oldest = app('navigation')->addPeriod($oldest, $step, 0); } $finalFiltered = $set->filter( - function (PiggyBankEvent $event) use ($today) { + static function (PiggyBankEvent $event) use ($today) { return $event->date->lte($today); } ); diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 426e84c66b..737519c4f5 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -36,7 +36,6 @@ use FireflyIII\Support\Http\Controllers\BasicDataSupport; use FireflyIII\Support\Http\Controllers\ChartGeneration; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -98,7 +97,7 @@ class ReportController extends Controller $includeNetWorth = $accountRepository->getMetaValue($account, 'include_net_worth'); $result = null === $includeNetWorth ? true : '1' === $includeNetWorth; if (false === $result) { - Log::debug(sprintf('Will not include "%s" in net worth charts.', $account->name)); + app('log')->debug(sprintf('Will not include "%s" in net worth charts.', $account->name)); } return $result; @@ -109,19 +108,22 @@ class ReportController extends Controller while ($current < $end) { // get balances by date, grouped by currency. - $result = $helper->getNetWorthByCurrency($filtered, $current); + $result = $helper->byAccounts($filtered, $current); // loop result, add to array. /** @var array $netWorthItem */ - foreach ($result as $netWorthItem) { - $currencyId = $netWorthItem['currency']->id; + foreach ($result as $key => $netWorthItem) { + if ('native' === $key) { + continue; + } + $currencyId = $netWorthItem['currency_id']; $label = $current->isoFormat((string)trans('config.month_and_day_js', [], $locale)); if (!array_key_exists($currencyId, $chartData)) { $chartData[$currencyId] = [ - 'label' => 'Net worth in ' . $netWorthItem['currency']->name, + 'label' => 'Net worth in ' . $netWorthItem['currency_name'], 'type' => 'line', - 'currency_symbol' => $netWorthItem['currency']->symbol, - 'currency_code' => $netWorthItem['currency']->code, + 'currency_symbol' => $netWorthItem['currency_symbol'], + 'currency_code' => $netWorthItem['currency_code'], 'entries' => [], ]; } @@ -157,7 +159,7 @@ class ReportController extends Controller if ($cache->has()) { return response()->json($cache->get()); } - Log::debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray()); + app('log')->debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray()); $format = app('navigation')->preferredCarbonFormat($start, $end); $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); $preferredRange = app('navigation')->preferredRangeFormat($start, $end); @@ -180,14 +182,14 @@ class ReportController extends Controller foreach ($journals as $journal) { $period = $journal['date']->format($format); $currencyId = (int)$journal['currency_id']; - $data[$currencyId] = $data[$currencyId] ?? [ + $data[$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], 'currency_name' => $journal['currency_name'], 'currency_decimal_places' => (int)$journal['currency_decimal_places'], ]; - $data[$currencyId][$period] = $data[$currencyId][$period] ?? [ + $data[$currencyId][$period] ??= [ 'period' => $period, 'spent' => '0', 'earned' => '0', diff --git a/app/Http/Controllers/Chart/TagReportController.php b/app/Http/Controllers/Chart/TagReportController.php index 2f3b7db2dd..6eb24c40fd 100644 --- a/app/Http/Controllers/Chart/TagReportController.php +++ b/app/Http/Controllers/Chart/TagReportController.php @@ -87,7 +87,7 @@ class TagReportController extends Controller foreach ($tag['transaction_journals'] as $journal) { $objectName = $journal['budget_name'] ?? trans('firefly.no_budget'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -123,7 +123,7 @@ class TagReportController extends Controller foreach ($tag['transaction_journals'] as $journal) { $objectName = $journal['category_name'] ?? trans('firefly.no_category'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -159,7 +159,7 @@ class TagReportController extends Controller foreach ($tag['transaction_journals'] as $journal) { $objectName = $journal['category_name'] ?? trans('firefly.no_category'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -195,7 +195,7 @@ class TagReportController extends Controller foreach ($tag['transaction_journals'] as $journal) { $objectName = $journal['destination_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -231,7 +231,7 @@ class TagReportController extends Controller foreach ($tag['transaction_journals'] as $journal) { $objectName = $journal['destination_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -269,7 +269,7 @@ class TagReportController extends Controller foreach ($spent as $currency) { // add things to chart Data for each currency: $spentKey = sprintf('%d-spent', $currency['currency_id']); - $chartData[$spentKey] = $chartData[$spentKey] ?? [ + $chartData[$spentKey] ??= [ 'label' => sprintf( '%s (%s)', (string)trans('firefly.spent_in_specific_tag', ['tag' => $tag->tag]), @@ -286,7 +286,7 @@ class TagReportController extends Controller foreach ($currentTag['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); $amount = app('steam')->positive($journal['amount']); - $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0'; + $chartData[$spentKey]['entries'][$key] ??= '0'; $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } @@ -296,7 +296,7 @@ class TagReportController extends Controller foreach ($earned as $currency) { // add things to chart Data for each currency: $spentKey = sprintf('%d-earned', $currency['currency_id']); - $chartData[$spentKey] = $chartData[$spentKey] ?? [ + $chartData[$spentKey] ??= [ 'label' => sprintf( '%s (%s)', (string)trans('firefly.earned_in_specific_tag', ['tag' => $tag->tag]), @@ -313,7 +313,7 @@ class TagReportController extends Controller foreach ($currentTag['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); $amount = app('steam')->positive($journal['amount']); - $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0'; + $chartData[$spentKey]['entries'][$key] ??= '0'; $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } @@ -369,7 +369,7 @@ class TagReportController extends Controller foreach ($tag['transaction_journals'] as $journal) { $objectName = $journal['source_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -405,7 +405,7 @@ class TagReportController extends Controller foreach ($tag['transaction_journals'] as $journal) { $objectName = $journal['source_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -439,7 +439,7 @@ class TagReportController extends Controller /** @var array $tag */ foreach ($currency['tags'] as $tag) { $title = sprintf('%s (%s)', $tag['name'], $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], @@ -473,7 +473,7 @@ class TagReportController extends Controller /** @var array $tag */ foreach ($currency['tags'] as $tag) { $title = sprintf('%s (%s)', $tag['name'], $currency['currency_name']); - $result[$title] = $result[$title] ?? [ + $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], diff --git a/app/Http/Controllers/Chart/TransactionController.php b/app/Http/Controllers/Chart/TransactionController.php index ccb349fe75..5d73fcc2a0 100644 --- a/app/Http/Controllers/Chart/TransactionController.php +++ b/app/Http/Controllers/Chart/TransactionController.php @@ -78,7 +78,7 @@ class TransactionController extends Controller foreach ($result as $journal) { $budget = $journal['budget_name'] ?? (string)trans('firefly.no_budget'); $title = sprintf('%s (%s)', $budget, $journal['currency_symbol']); - $data[$title] = $data[$title] ?? [ + $data[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], @@ -131,7 +131,7 @@ class TransactionController extends Controller foreach ($result as $journal) { $category = $journal['category_name'] ?? (string)trans('firefly.no_category'); $title = sprintf('%s (%s)', $category, $journal['currency_symbol']); - $data[$title] = $data[$title] ?? [ + $data[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], @@ -184,7 +184,7 @@ class TransactionController extends Controller foreach ($result as $journal) { $name = $journal['destination_account_name']; $title = sprintf('%s (%s)', $name, $journal['currency_symbol']); - $data[$title] = $data[$title] ?? [ + $data[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], @@ -237,7 +237,7 @@ class TransactionController extends Controller foreach ($result as $journal) { $name = $journal['source_account_name']; $title = sprintf('%s (%s)', $name, $journal['currency_symbol']); - $data[$title] = $data[$title] ?? [ + $data[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $journal['currency_code'], diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index f77fc7ccef..876b4e1962 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -39,9 +39,9 @@ abstract class Controller extends BaseController { use AuthorizesRequests; use DispatchesJobs; - use ValidatesRequests; use RequestInformation; use UserNavigation; + use ValidatesRequests; protected string $dateTimeFormat; protected string $monthAndDayFormat; @@ -57,7 +57,7 @@ abstract class Controller extends BaseController { // is site a demo site? $isDemoSiteConfig = app('fireflyconfig')->get('is_demo_site', config('firefly.configuration.is_demo_site', false)); - $isDemoSite = $isDemoSiteConfig ? $isDemoSiteConfig->data : false; + $isDemoSite = (bool)$isDemoSiteConfig->data; app('view')->share('IS_DEMO_SITE', $isDemoSite); app('view')->share('DEMO_USERNAME', config('firefly.demo_username')); app('view')->share('DEMO_PASSWORD', config('firefly.demo_password')); @@ -74,8 +74,8 @@ abstract class Controller extends BaseController app('view')->share('logoutUrl', $logoutUrl); // upload size - $maxFileSize = app('steam')->phpBytes(ini_get('upload_max_filesize')); - $maxPostSize = app('steam')->phpBytes(ini_get('post_max_size')); + $maxFileSize = app('steam')->phpBytes((string)ini_get('upload_max_filesize')); + $maxPostSize = app('steam')->phpBytes((string)ini_get('post_max_size')); $uploadSize = min($maxFileSize, $maxPostSize); app('view')->share('uploadSize', $uploadSize); diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index c4fc690fe1..c45380af83 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -42,6 +42,7 @@ use Illuminate\View\View; use Monolog\Handler\RotatingFileHandler; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; +use const PHP_SAPI; /** * Class DebugController @@ -69,11 +70,11 @@ class DebugController extends Controller */ public function displayError(): void { - Log::debug('This is a test message at the DEBUG level.'); - Log::info('This is a test message at the INFO level.'); + app('log')->debug('This is a test message at the DEBUG level.'); + app('log')->info('This is a test message at the INFO level.'); Log::notice('This is a test message at the NOTICE level.'); app('log')->warning('This is a test message at the WARNING level.'); - Log::error('This is a test message at the ERROR level.'); + app('log')->error('This is a test message at the ERROR level.'); Log::critical('This is a test message at the CRITICAL level.'); Log::alert('This is a test message at the ALERT level.'); Log::emergency('This is a test message at the EMERGENCY level.'); @@ -92,21 +93,21 @@ class DebugController extends Controller { app('preferences')->mark(); $request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range', 'temp-mfa-secret', 'temp-mfa-codes']); - Log::debug('Call cache:clear...'); + app('log')->debug('Call cache:clear...'); Artisan::call('cache:clear'); - Log::debug('Call config:clear...'); + app('log')->debug('Call config:clear...'); Artisan::call('config:clear'); - Log::debug('Call route:clear...'); + app('log')->debug('Call route:clear...'); Artisan::call('route:clear'); - Log::debug('Call twig:clean...'); + app('log')->debug('Call twig:clean...'); try { Artisan::call('twig:clean'); } catch (Exception $e) { // intentional generic exception throw new FireflyException($e->getMessage(), 0, $e); } - Log::debug('Call view:clear...'); + app('log')->debug('Call view:clear...'); Artisan::call('view:clear'); return redirect(route('index')); @@ -115,12 +116,10 @@ class DebugController extends Controller /** * Show debug info. * - * @param Request $request - * * @return Factory|View * @throws FireflyException */ - public function index(Request $request) + public function index() { $table = $this->generateTable(); $table = str_replace(["\n", "\t", ' '], '', $table); @@ -129,7 +128,7 @@ class DebugController extends Controller // get latest log file: $logger = Log::driver(); // PHPstan doesn't recognize the method because of its polymorphic nature. - $handlers = $logger->getHandlers(); // @phpstan-ignore-line + $handlers = $logger->getHandlers(); // @phpstan-ignore-line $logContent = ''; foreach ($handlers as $handler) { if ($handler instanceof RotatingFileHandler) { @@ -141,7 +140,7 @@ class DebugController extends Controller } if ('' !== $logContent) { // last few lines - $logContent = 'Truncated from this point <----|' . substr($logContent, -16384); + $logContent = 'Truncated from this point <----|' . substr((string)$logContent, -16384); } return view('debug', compact('table', 'now', 'logContent')); @@ -166,8 +165,8 @@ class DebugController extends Controller */ private function getSystemInformation(): array { - $maxFileSize = app('steam')->phpBytes(ini_get('upload_max_filesize')); - $maxPostSize = app('steam')->phpBytes(ini_get('post_max_size')); + $maxFileSize = app('steam')->phpBytes((string)ini_get('upload_max_filesize')); + $maxPostSize = app('steam')->phpBytes((string)ini_get('post_max_size')); $drivers = DB::availableDrivers(); $currentDriver = DB::getDriverName(); return [ @@ -199,19 +198,19 @@ class DebugController extends Controller ]; try { if (file_exists('/var/www/counter-main.txt')) { - $return['build'] = trim(file_get_contents('/var/www/counter-main.txt')); + $return['build'] = trim((string)file_get_contents('/var/www/counter-main.txt')); } - } catch (Exception $e) { // generic catch for open basedir. - Log::debug('Could not check build counter, but thats ok.'); - Log::warning($e->getMessage()); + } catch (Exception $e) { // @phpstan-ignore-line + app('log')->debug('Could not check build counter, but thats ok.'); + app('log')->warning($e->getMessage()); } try { if (file_exists('/var/www/build-date-main.txt')) { - $return['build_date'] = trim(file_get_contents('/var/www/build-date-main.txt')); + $return['build_date'] = trim((string)file_get_contents('/var/www/build-date-main.txt')); } - } catch (Exception $e) { // generic catch for open basedir. - Log::debug('Could not check build date, but thats ok.'); - Log::warning($e->getMessage()); + } catch (Exception $e) { // @phpstan-ignore-line + app('log')->debug('Could not check build date, but thats ok.'); + app('log')->warning($e->getMessage()); } if ('' !== (string)env('BASE_IMAGE_BUILD')) { $return['base_build'] = env('BASE_IMAGE_BUILD'); @@ -236,7 +235,7 @@ class DebugController extends Controller if ($lastTime > 0) { $carbon = Carbon::createFromTimestamp($lastTime); $lastCronjob = $carbon->format('Y-m-d H:i:s'); - $lastCronjobAgo = $carbon->locale('en')->diffForHumans(); + $lastCronjobAgo = $carbon->locale('en')->diffForHumans(); // @phpstan-ignore-line } return [ @@ -246,7 +245,7 @@ class DebugController extends Controller 'default_locale' => (string)config('firefly.default_locale'), 'remote_header' => $userGuard === 'remote_user_guard' ? config('auth.guard_header') : 'N/A', 'remote_mail_header' => $userGuard === 'remote_user_guard' ? config('auth.guard_email') : 'N/A', - 'stateful_domains' => join(', ', config('sanctum.stateful')), + 'stateful_domains' => implode(', ', config('sanctum.stateful')), // the dates for the cron job are based on the recurring cron job's times. // any of the cron jobs will do, they always run at the same time. @@ -270,16 +269,16 @@ class DebugController extends Controller $userAgent = request()->header('user-agent'); // set languages, see what happens: - $original = setlocale(LC_ALL, 0); + $original = setlocale(LC_ALL, '0'); $localeAttempts = []; $parts = app('steam')->getLocaleArray(app('steam')->getLocale()); foreach ($parts as $code) { $code = trim($code); - Log::debug(sprintf('Trying to set %s', $code)); + app('log')->debug(sprintf('Trying to set %s', $code)); $result = setlocale(LC_ALL, $code); $localeAttempts[$code] = $result === $code; } - setlocale(LC_ALL, $original); + setlocale(LC_ALL, (string)$original); return [ 'user_id' => auth()->user()->id, @@ -314,7 +313,7 @@ class DebugController extends Controller // has stored reconciliations $type = TransactionType::whereType(TransactionType::RECONCILIATION)->first(); - if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count()) { + if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count() > 0) { $flags[] = ':ledger:'; } @@ -339,7 +338,7 @@ class DebugController extends Controller if ($user->bills()->count() > 0) { $flags[] = ':email:'; } - return join(' ', $flags); + return implode(' ', $flags); } /** diff --git a/app/Http/Controllers/Export/IndexController.php b/app/Http/Controllers/Export/IndexController.php index 0c1e84f4a3..bbcc5249dc 100644 --- a/app/Http/Controllers/Export/IndexController.php +++ b/app/Http/Controllers/Export/IndexController.php @@ -102,7 +102,7 @@ class IndexController extends Controller ->header('Expires', '0') ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') ->header('Pragma', 'public') - ->header('Content-Length', strlen($result['transactions'])); + ->header('Content-Length', (string)strlen($result['transactions'])); // return CSV file made from 'transactions' array. return $response; diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 69a2d2d2aa..38db72eb10 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -67,29 +67,38 @@ class HomeController extends Controller */ public function dateRange(Request $request): JsonResponse { + $stringStart = ''; + $stringEnd = ''; try { $stringStart = e((string)$request->get('start')); $start = Carbon::createFromFormat('Y-m-d', $stringStart); } catch (InvalidFormatException $e) { - Log::error(sprintf('Start: could not parse date string "%s" so ignore it.', $stringStart)); + app('log')->error(sprintf('Start: could not parse date string "%s" so ignore it.', $stringStart)); $start = Carbon::now()->startOfMonth(); } try { $stringEnd = e((string)$request->get('end')); $end = Carbon::createFromFormat('Y-m-d', $stringEnd); } catch (InvalidFormatException $e) { - Log::error(sprintf('End could not parse date string "%s" so ignore it.', $stringEnd)); + app('log')->error(sprintf('End could not parse date string "%s" so ignore it.', $stringEnd)); $end = Carbon::now()->endOfMonth(); } + if (false === $start) { + $start = Carbon::now()->startOfMonth(); + } + if (false === $end) { + $end = Carbon::now()->endOfMonth(); + } + $label = $request->get('label'); $isCustomRange = false; - Log::debug('Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]); + app('log')->debug('Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]); // check if the label is "everything" or "Custom range" which will betray // a possible problem with the budgets. if ($label === (string)trans('firefly.everything') || $label === (string)trans('firefly.customRange')) { $isCustomRange = true; - Log::debug('Range is now marked as "custom".'); + app('log')->debug('Range is now marked as "custom".'); } $diff = $start->diffInDays($end) + 1; @@ -99,11 +108,11 @@ class HomeController extends Controller } $request->session()->put('is_custom_range', $isCustomRange); - Log::debug(sprintf('Set is_custom_range to %s', var_export($isCustomRange, true))); + app('log')->debug(sprintf('Set is_custom_range to %s', var_export($isCustomRange, true))); $request->session()->put('start', $start); - Log::debug(sprintf('Set start to %s', $start->format('Y-m-d H:i:s'))); + app('log')->debug(sprintf('Set start to %s', $start->format('Y-m-d H:i:s'))); $request->session()->put('end', $end); - Log::debug(sprintf('Set end to %s', $end->format('Y-m-d H:i:s'))); + app('log')->debug(sprintf('Set end to %s', $end->format('Y-m-d H:i:s'))); return response()->json(['ok' => 'ok']); } @@ -125,20 +134,26 @@ class HomeController extends Controller if (0 === $count) { return redirect(route('new-user.index')); } - $subTitle = (string)trans('firefly.welcome_back'); - $transactions = []; - $frontPage = app('preferences')->getFresh('frontPageAccounts', $repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray()); + $subTitle = (string)trans('firefly.welcome_back'); + $transactions = []; + $frontPage = app('preferences')->getFresh('frontPageAccounts', $repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray()); + $frontPageArray = $frontPage->data; + if (!is_array($frontPageArray)) { + $frontPageArray = []; + } + + /** @var Carbon $start */ $start = session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $end */ $end = session('end', today(config('app.timezone'))->endOfMonth()); - $accounts = $repository->getAccountsById($frontPage->data); + $accounts = $repository->getAccountsById($frontPageArray); $today = today(config('app.timezone')); // sort frontpage accounts by order $accounts = $accounts->sortBy('order'); - Log::debug('Frontpage accounts are ', $frontPage->data); + app('log')->debug('Frontpage accounts are ', $frontPageArray); /** @var BillRepositoryInterface $billRepository */ $billRepository = app(BillRepositoryInterface::class); diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index 614f491830..aaf1d6f230 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -47,14 +47,13 @@ class JavascriptController extends Controller /** * Show info about accounts. * - * @param AccountRepositoryInterface $repository - * @param CurrencyRepositoryInterface $currencyRepository + * @param AccountRepositoryInterface $repository * * @return Response * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function accounts(AccountRepositoryInterface $repository, CurrencyRepositoryInterface $currencyRepository): Response + public function accounts(AccountRepositoryInterface $repository): Response { $accounts = $repository->getAccountsByType( [AccountType::DEFAULT, AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD] @@ -102,9 +101,8 @@ class JavascriptController extends Controller /** * Show some common variables to be used in scripts. * - * @param Request $request - * @param AccountRepositoryInterface $repository - * @param CurrencyRepositoryInterface $currencyRepository + * @param Request $request + * @param AccountRepositoryInterface $repository * * @return Response * @throws FireflyException @@ -112,7 +110,7 @@ class JavascriptController extends Controller * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ - public function variables(Request $request, AccountRepositoryInterface $repository, CurrencyRepositoryInterface $currencyRepository): Response + public function variables(Request $request, AccountRepositoryInterface $repository): Response { $account = $repository->find((int)$request->get('account')); $currency = app('amount')->getDefaultCurrency(); @@ -147,11 +145,9 @@ class JavascriptController extends Controller /** * Bit of a hack but OK. * - * @param Request $request - * * @return Response */ - public function variablesV2(Request $request): Response + public function variablesV2(): Response { /** @var Carbon $start */ $start = clone session('start', today(config('app.timezone'))->startOfMonth()); diff --git a/app/Http/Controllers/Json/BoxController.php b/app/Http/Controllers/Json/BoxController.php index 5e270878d3..aa29ecd438 100644 --- a/app/Http/Controllers/Json/BoxController.php +++ b/app/Http/Controllers/Json/BoxController.php @@ -30,7 +30,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\AvailableBudget; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; @@ -38,7 +37,6 @@ use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Log; /** * Class BoxController. @@ -75,7 +73,7 @@ class BoxController extends Controller $cache->addProperty($today); $cache->addProperty('box-available'); if ($cache->has()) { - //return response()->json($cache->get()); + return response()->json($cache->get()); } $leftPerDayAmount = '0'; $leftToSpendAmount = '0'; @@ -85,7 +83,7 @@ class BoxController extends Controller $availableBudgets = $abRepository->getAvailableBudgetsByExactDate($start, $end); app('log')->debug(sprintf('Found %d available budget(s)', $availableBudgets->count())); $availableBudgets = $availableBudgets->filter( - static function (AvailableBudget $availableBudget) use ($currency) { + static function (AvailableBudget $availableBudget) use ($currency) { // @phpstan-ignore-line if ($availableBudget->transaction_currency_id === $currency->id) { app('log')->debug(sprintf( 'Will include AB #%d: from %s-%s amount %s', @@ -104,7 +102,7 @@ class BoxController extends Controller // spent in this period, in budgets, for default currency. // also calculate spent per day. $spent = $opsRepository->sumExpenses($start, $end, null, null, $currency); - $spentAmount = $spent[(int)$currency->id]['sum'] ?? '0'; + $spentAmount = $spent[$currency->id]['sum'] ?? '0'; app('log')->debug(sprintf('Spent for default currency for all budgets in this period: %s', $spentAmount)); $days = $today->between($start, $end) ? $today->diffInDays($start) + 1 : $end->diffInDays($start) + 1; @@ -120,7 +118,7 @@ class BoxController extends Controller $leftToSpendAmount = bcadd($totalAvailableSum, $spentAmount); app('log')->debug(sprintf('So left to spend is %s', $leftToSpendAmount)); if (1 === bccomp($leftToSpendAmount, '0')) { - app('log')->debug(sprintf('Left to spend is positive!')); + app('log')->debug('Left to spend is positive!'); $boxTitle = (string)trans('firefly.left_to_spend'); $days = $today->diffInDays($end) + 1; $display = 1; // not overspent @@ -181,9 +179,9 @@ class BoxController extends Controller foreach ($set as $journal) { $currencyId = (int)$journal['currency_id']; $amount = $journal['amount'] ?? '0'; - $incomes[$currencyId] = $incomes[$currencyId] ?? '0'; + $incomes[$currencyId] ??= '0'; $incomes[$currencyId] = bcadd($incomes[$currencyId], app('steam')->positive($amount)); - $sums[$currencyId] = $sums[$currencyId] ?? '0'; + $sums[$currencyId] ??= '0'; $sums[$currencyId] = bcadd($sums[$currencyId], app('steam')->positive($amount)); } @@ -196,9 +194,9 @@ class BoxController extends Controller /** @var array $journal */ foreach ($set as $journal) { $currencyId = (int)$journal['currency_id']; - $expenses[$currencyId] = $expenses[$currencyId] ?? '0'; + $expenses[$currencyId] ??= '0'; $expenses[$currencyId] = bcadd($expenses[$currencyId], $journal['amount'] ?? '0'); - $sums[$currencyId] = $sums[$currencyId] ?? '0'; + $sums[$currencyId] ??= '0'; $sums[$currencyId] = bcadd($sums[$currencyId], $journal['amount']); } @@ -253,27 +251,28 @@ class BoxController extends Controller $allAccounts = $accountRepository->getActiveAccountsByType( [AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE] ); - Log::debug(sprintf('Found %d accounts.', $allAccounts->count())); + app('log')->debug(sprintf('Found %d accounts.', $allAccounts->count())); // filter list on preference of being included. $filtered = $allAccounts->filter( - function (Account $account) use ($accountRepository) { + static function (Account $account) use ($accountRepository) { $includeNetWorth = $accountRepository->getMetaValue($account, 'include_net_worth'); $result = null === $includeNetWorth ? true : '1' === $includeNetWorth; if (false === $result) { - Log::debug(sprintf('Will not include "%s" in net worth charts.', $account->name)); + app('log')->debug(sprintf('Will not include "%s" in net worth charts.', $account->name)); } return $result; } ); - $netWorthSet = $netWorthHelper->getNetWorthByCurrency($filtered, $date); + $netWorthSet = $netWorthHelper->byAccounts($filtered, $date); $return = []; - foreach ($netWorthSet as $data) { - /** @var TransactionCurrency $currency */ - $currency = $data['currency']; - $return[$currency->id] = app('amount')->formatAnything($currency, $data['balance'], false); + foreach ($netWorthSet as $key => $data) { + if ('native' === $key) { + continue; + } + $return[$data['currency_id']] = app('amount')->formatFlat($data['currency_symbol'], $data['currency_decimal_places'], $data['balance'], false); } $return = [ 'net_worths' => array_values($return), diff --git a/app/Http/Controllers/Json/FrontpageController.php b/app/Http/Controllers/Json/FrontpageController.php index 3ae35b987e..44d5611346 100644 --- a/app/Http/Controllers/Json/FrontpageController.php +++ b/app/Http/Controllers/Json/FrontpageController.php @@ -28,7 +28,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\PiggyBank; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -74,8 +73,8 @@ class FrontpageController extends Controller try { $html = view('json.piggy-banks', compact('info'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render json.piggy-banks: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render json.piggy-banks: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $html = 'Could not render view.'; throw new FireflyException($html, 0, $e); } diff --git a/app/Http/Controllers/Json/IntroController.php b/app/Http/Controllers/Json/IntroController.php index 7b98747d9c..1c02784a40 100644 --- a/app/Http/Controllers/Json/IntroController.php +++ b/app/Http/Controllers/Json/IntroController.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\Http\Controllers\GetConfigurationData; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Facades\Log; /** * Class IntroController. @@ -46,12 +45,12 @@ class IntroController extends Controller */ public function getIntroSteps(string $route, string $specificPage = null): JsonResponse { - Log::debug(sprintf('getIntroSteps for route "%s" and page "%s"', $route, $specificPage)); - $specificPage = $specificPage ?? ''; + app('log')->debug(sprintf('getIntroSteps for route "%s" and page "%s"', $route, $specificPage)); + $specificPage ??= ''; $steps = $this->getBasicSteps($route); $specificSteps = $this->getSpecificSteps($route, $specificPage); if (0 === count($specificSteps)) { - Log::debug(sprintf('No specific steps for route "%s" and page "%s"', $route, $specificPage)); + app('log')->debug(sprintf('No specific steps for route "%s" and page "%s"', $route, $specificPage)); return response()->json($steps); } @@ -81,7 +80,7 @@ class IntroController extends Controller public function hasOutroStep(string $route): bool { $routeKey = str_replace('.', '_', $route); - Log::debug(sprintf('Has outro step for route %s', $routeKey)); + app('log')->debug(sprintf('Has outro step for route %s', $routeKey)); $elements = config(sprintf('intro.%s', $routeKey)); if (!is_array($elements)) { return false; @@ -89,9 +88,9 @@ class IntroController extends Controller $hasStep = array_key_exists('outro', $elements); - Log::debug('Elements is array', $elements); - Log::debug('Keys is', array_keys($elements)); - Log::debug(sprintf('Keys has "outro": %s', var_export($hasStep, true))); + app('log')->debug('Elements is array', $elements); + app('log')->debug('Keys is', array_keys($elements)); + app('log')->debug(sprintf('Keys has "outro": %s', var_export($hasStep, true))); return $hasStep; } @@ -107,13 +106,13 @@ class IntroController extends Controller */ public function postEnable(string $route, string $specialPage = null): JsonResponse { - $specialPage = $specialPage ?? ''; + $specialPage ??= ''; $route = str_replace('.', '_', $route); $key = 'shown_demo_' . $route; if ('' !== $specialPage) { $key .= '_' . $specialPage; } - Log::debug(sprintf('Going to mark the following route as NOT done: %s with special "%s" (%s)', $route, $specialPage, $key)); + app('log')->debug(sprintf('Going to mark the following route as NOT done: %s with special "%s" (%s)', $route, $specialPage, $key)); app('preferences')->set($key, false); app('preferences')->mark(); @@ -131,12 +130,12 @@ class IntroController extends Controller */ public function postFinished(string $route, string $specialPage = null): JsonResponse { - $specialPage = $specialPage ?? ''; + $specialPage ??= ''; $key = 'shown_demo_' . $route; if ('' !== $specialPage) { $key .= '_' . $specialPage; } - Log::debug(sprintf('Going to mark the following route as done: %s with special "%s" (%s)', $route, $specialPage, $key)); + app('log')->debug(sprintf('Going to mark the following route as done: %s with special "%s" (%s)', $route, $specialPage, $key)); app('preferences')->set($key, true); return response()->json(['result' => sprintf('Reported demo watched for route "%s" (%s): %s.', $route, $specialPage, $key)]); diff --git a/app/Http/Controllers/Json/ReconcileController.php b/app/Http/Controllers/Json/ReconcileController.php index d9d2bc1136..47eac09ab3 100644 --- a/app/Http/Controllers/Json/ReconcileController.php +++ b/app/Http/Controllers/Json/ReconcileController.php @@ -34,7 +34,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; use Throwable; @@ -116,13 +115,13 @@ class ReconcileController extends Controller $clearedJournals = $collector->getExtractedJournals(); } - Log::debug('Start transaction loop'); + app('log')->debug('Start transaction loop'); /** @var array $journal */ foreach ($journals as $journal) { $amount = $this->processJournal($account, $accountCurrency, $journal, $amount); } - Log::debug(sprintf('Final amount is %s', $amount)); - Log::debug('End transaction loop'); + app('log')->debug(sprintf('Final amount is %s', $amount)); + app('log')->debug('End transaction loop'); /** @var array $journal */ foreach ($clearedJournals as $journal) { @@ -156,8 +155,8 @@ class ReconcileController extends Controller ) )->render(); } catch (Throwable $e) { - Log::debug(sprintf('View error: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->debug(sprintf('View error: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $view = sprintf('Could not render accounts.reconcile.overview: %s', $e->getMessage()); throw new FireflyException($view, 0, $e); } @@ -181,7 +180,7 @@ class ReconcileController extends Controller private function processJournal(Account $account, TransactionCurrency $currency, array $journal, string $amount): string { $toAdd = '0'; - Log::debug(sprintf('User submitted %s #%d: "%s"', $journal['transaction_type_type'], $journal['transaction_journal_id'], $journal['description'])); + app('log')->debug(sprintf('User submitted %s #%d: "%s"', $journal['transaction_type_type'], $journal['transaction_journal_id'], $journal['description'])); // not much magic below we need to cover using tests. @@ -203,9 +202,9 @@ class ReconcileController extends Controller } - Log::debug(sprintf('Going to add %s to %s', $toAdd, $amount)); + app('log')->debug(sprintf('Going to add %s to %s', $toAdd, $amount)); $amount = bcadd($amount, $toAdd); - Log::debug(sprintf('Result is %s', $amount)); + app('log')->debug(sprintf('Result is %s', $amount)); return $amount; } @@ -258,8 +257,8 @@ class ReconcileController extends Controller compact('account', 'journals', 'currency', 'start', 'end', 'selectionStart', 'selectionEnd') )->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->debug(sprintf('Could not render: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $html = sprintf('Could not render accounts.reconcile.transactions: %s', $e->getMessage()); throw new FireflyException($html, 0, $e); } diff --git a/app/Http/Controllers/Json/RecurrenceController.php b/app/Http/Controllers/Json/RecurrenceController.php index f7dbd0678d..0ac57139f8 100644 --- a/app/Http/Controllers/Json/RecurrenceController.php +++ b/app/Http/Controllers/Json/RecurrenceController.php @@ -31,7 +31,6 @@ use FireflyIII\Models\RecurrenceRepetition; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; /** * Class RecurrenceController @@ -80,6 +79,11 @@ class RecurrenceController extends Controller $repetitionType = explode(',', $request->get('type'))[0]; $repetitions = (int)$request->get('reps'); $repetitionMoment = ''; + + if (false === $start || false === $end || false === $firstDate || false === $endDate) { + return response()->json(); + } + $start->startOfDay(); // if $firstDate is beyond $end, simply return an empty array. @@ -149,19 +153,23 @@ class RecurrenceController extends Controller $string = '' === (string)$request->get('date') ? date('Y-m-d') : (string)$request->get('date'); $today = today(config('app.timezone'))->startOfDay(); try { - $date = Carbon::createFromFormat('Y-m-d', $string, config('app.timezone'))->startOfDay(); + $date = Carbon::createFromFormat('Y-m-d', $string, config('app.timezone')); } catch (InvalidFormatException $e) { $date = Carbon::today(config('app.timezone')); } + if (false === $date) { + return response()->json(); + } + $date->startOfDay(); $preSelected = (string)$request->get('pre_select'); $locale = app('steam')->getLocale(); - Log::debug(sprintf('date = %s, today = %s. date > today? %s', $date->toAtomString(), $today->toAtomString(), var_export($date > $today, true))); - Log::debug(sprintf('past = true? %s', var_export('true' === (string)$request->get('past'), true))); + app('log')->debug(sprintf('date = %s, today = %s. date > today? %s', $date->toAtomString(), $today->toAtomString(), var_export($date > $today, true))); + app('log')->debug(sprintf('past = true? %s', var_export('true' === (string)$request->get('past'), true))); $result = []; if ($date > $today || 'true' === (string)$request->get('past')) { - Log::debug('Will fill dropdown.'); + app('log')->debug('Will fill dropdown.'); $weekly = sprintf('weekly,%s', $date->dayOfWeekIso); $monthly = sprintf('monthly,%s', $date->day); $dayOfWeek = (string)trans(sprintf('config.dow_%s', $date->dayOfWeekIso)); @@ -188,7 +196,7 @@ class RecurrenceController extends Controller ], ]; } - Log::debug('Dropdown is', $result); + app('log')->debug('Dropdown is', $result); return response()->json($result); } diff --git a/app/Http/Controllers/Json/RuleController.php b/app/Http/Controllers/Json/RuleController.php index 9a2a9d6d1f..fcec63cedc 100644 --- a/app/Http/Controllers/Json/RuleController.php +++ b/app/Http/Controllers/Json/RuleController.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -54,8 +53,8 @@ class RuleController extends Controller try { $view = view('rules.partials.action', compact('actions', 'count'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render rules.partials.action: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render rules.partials.action: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $view = 'Could not render view.'; throw new FireflyException($view, 0, $e); } @@ -86,8 +85,8 @@ class RuleController extends Controller try { $view = view('rules.partials.trigger', compact('triggers', 'count'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render rules.partials.trigger: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render rules.partials.trigger: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $view = 'Could not render view.'; throw new FireflyException($view, 0, $e); } diff --git a/app/Http/Controllers/ObjectGroup/IndexController.php b/app/Http/Controllers/ObjectGroup/IndexController.php index 7f48d4240c..990c569b91 100644 --- a/app/Http/Controllers/ObjectGroup/IndexController.php +++ b/app/Http/Controllers/ObjectGroup/IndexController.php @@ -31,7 +31,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; /** * Class IndexController @@ -82,7 +81,7 @@ class IndexController extends Controller */ public function setOrder(Request $request, ObjectGroup $objectGroup) { - Log::debug(sprintf('Found object group #%d "%s"', $objectGroup->id, $objectGroup->title)); + app('log')->debug(sprintf('Found object group #%d "%s"', $objectGroup->id, $objectGroup->title)); $newOrder = (int)$request->get('order'); $this->repository->setOrder($objectGroup, $newOrder); diff --git a/app/Http/Controllers/PiggyBank/AmountController.php b/app/Http/Controllers/PiggyBank/AmountController.php index ec59780b2e..38a876cbea 100644 --- a/app/Http/Controllers/PiggyBank/AmountController.php +++ b/app/Http/Controllers/PiggyBank/AmountController.php @@ -32,7 +32,6 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; /** @@ -140,7 +139,7 @@ class AmountController extends Controller return redirect(route('piggy-banks.index')); } - Log::error('Cannot add ' . $amount . ' because canAddAmount returned false.'); + app('log')->error('Cannot add ' . $amount . ' because canAddAmount returned false.'); session()->flash( 'error', (string)trans( diff --git a/app/Http/Controllers/PiggyBank/CreateController.php b/app/Http/Controllers/PiggyBank/CreateController.php index 66c3a8d518..4a3ec07f11 100644 --- a/app/Http/Controllers/PiggyBank/CreateController.php +++ b/app/Http/Controllers/PiggyBank/CreateController.php @@ -103,7 +103,7 @@ class CreateController extends Controller app('preferences')->mark(); // store attachment(s): - /** @var array $files */ + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($piggyBank, $files); diff --git a/app/Http/Controllers/PiggyBank/EditController.php b/app/Http/Controllers/PiggyBank/EditController.php index 45b1db77fa..6824bb4ab2 100644 --- a/app/Http/Controllers/PiggyBank/EditController.php +++ b/app/Http/Controllers/PiggyBank/EditController.php @@ -84,7 +84,7 @@ class EditController extends Controller $startDate = $piggyBank->startdate?->format('Y-m-d'); $currency = $this->accountRepository->getAccountCurrency($piggyBank->account); if (null === $currency) { - $currency = Amount::getDefaultCurrency(); + $currency = app('amount')->getDefaultCurrency(); } $preFilled = [ @@ -93,7 +93,7 @@ class EditController extends Controller 'targetamount' => app('steam')->bcround($piggyBank->targetamount, $currency->decimal_places), 'targetdate' => $targetDate, 'startdate' => $startDate, - 'object_group' => $piggyBank->objectGroups->first() ? $piggyBank->objectGroups->first()->title : '', + 'object_group' => null !== $piggyBank->objectGroups->first() ? $piggyBank->objectGroups->first()->title : '', 'notes' => null === $note ? '' : $note->text, ]; if (0 === bccomp($piggyBank->targetamount, '0')) { @@ -127,7 +127,7 @@ class EditController extends Controller app('preferences')->mark(); // store new attachment(s): - /** @var array $files */ + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($piggyBank, $files); diff --git a/app/Http/Controllers/PiggyBank/IndexController.php b/app/Http/Controllers/PiggyBank/IndexController.php index fb0412e1cd..65db2fd08a 100644 --- a/app/Http/Controllers/PiggyBank/IndexController.php +++ b/app/Http/Controllers/PiggyBank/IndexController.php @@ -74,13 +74,11 @@ class IndexController extends Controller * * TODO very complicated function. * - * @param Request $request - * * @return Factory|View * @throws FireflyException * @throws JsonException */ - public function index(Request $request) + public function index() { $this->cleanupObjectGroups(); $this->piggyRepos->resetOrder(); @@ -108,7 +106,7 @@ class IndexController extends Controller $array = $transformer->transform($piggy); $groupOrder = (int)$array['object_group_order']; // make group array if necessary: - $piggyBanks[$groupOrder] = $piggyBanks[$groupOrder] ?? [ + $piggyBanks[$groupOrder] ??= [ 'object_group_id' => $array['object_group_id'] ?? 0, 'object_group_title' => $array['object_group_title'] ?? trans('firefly.default_group_title_name'), 'piggy_banks' => [], @@ -156,7 +154,7 @@ class IndexController extends Controller $groupId = $group['object_group_id']; foreach ($group['piggy_banks'] as $piggy) { $currencyId = $piggy['currency_id']; - $sums[$groupId][$currencyId] = $sums[$groupId][$currencyId] ?? [ + $sums[$groupId][$currencyId] ??= [ 'target' => '0', 'saved' => '0', 'left_to_save' => '0', diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index aec2828524..a526b3061b 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -33,7 +33,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use JsonException; use Psr\Container\ContainerExceptionInterface; @@ -54,7 +53,7 @@ class PreferencesController extends Controller parent::__construct(); $this->middleware( - function ($request, $next) { + static function ($request, $next) { app('view')->share('title', (string)trans('firefly.preferences')); app('view')->share('mainTitleIcon', 'fa-gear'); @@ -92,13 +91,18 @@ class PreferencesController extends Controller if ('opt_group_' === $role) { $role = 'opt_group_defaultAsset'; } - $groupedAccounts[trans(sprintf('firefly.%s', $role))][$account->id] = $account->name; + $groupedAccounts[(string)trans(sprintf('firefly.%s', $role))][$account->id] = $account->name; } ksort($groupedAccounts); - $accountIds = $accounts->pluck('id')->toArray(); - $viewRange = app('navigation')->getViewRange(false); - $frontPageAccounts = app('preferences')->get('frontPageAccounts', $accountIds); + /** @var array $accountIds */ + $accountIds = $accounts->pluck('id')->toArray(); + $viewRange = app('navigation')->getViewRange(false); + $frontPageAccountsPref = app('preferences')->get('frontPageAccounts', $accountIds); + $frontPageAccounts = $frontPageAccountsPref->data; + if (!is_array($frontPageAccounts)) { + $frontPageAccounts = $accountIds; + } $language = app('steam')->getLanguage(); $languages = config('firefly.languages'); $locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; @@ -107,7 +111,10 @@ class PreferencesController extends Controller $slackUrl = app('preferences')->get('slack_webhook_url', '')->data; $customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data; $fiscalYearStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data; - $fiscalYearStart = date('Y') . '-' . $fiscalYearStartStr; + if (is_array($fiscalYearStartStr)) { + $fiscalYearStartStr = '01-01'; + } + $fiscalYearStart = sprintf('%s-%s', date('Y'), (string)$fiscalYearStartStr); $tjOptionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data; $availableDarkModes = config('firefly.available_dark_modes'); @@ -122,20 +129,20 @@ class PreferencesController extends Controller // list of locales also has "equal" which makes it equal to whatever the language is. try { - $locales = json_decode(file_get_contents(resource_path(sprintf('lang/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR); + $locales = json_decode((string)file_get_contents(resource_path(sprintf('lang/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR); } catch (JsonException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); $locales = []; } $locales = ['equal' => (string)trans('firefly.equal_to_language')] + $locales; // an important fallback is that the frontPageAccount array gets refilled automatically // when it turns up empty. - if (0 === count($frontPageAccounts->data)) { + if (0 === count($frontPageAccounts)) { $frontPageAccounts = $accountIds; } // for the demo user, the slackUrl is automatically emptied. - // this isn't really secure but it means that the demo site has a semi-secret + // this isn't really secure, but it means that the demo site has a semi-secret // slackUrl. if (auth()->user()->hasRole('demo')) { $slackUrl = ''; diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 59b624fac6..5f724b5714 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -36,7 +36,6 @@ use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\Http\Requests\TokenFormRequest; use FireflyIII\Models\Preference; use FireflyIII\Repositories\User\UserRepositoryInterface; -use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\Http\Controllers\CreateStuff; use FireflyIII\User; use Google2FA; @@ -49,7 +48,6 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Laravel\Passport\ClientRepository; use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException; @@ -90,7 +88,7 @@ class ProfileController extends Controller ); $authGuard = config('firefly.authentication_guard'); $this->internalAuth = 'web' === $authGuard; - Log::debug(sprintf('ProfileController::__construct(). Authentication guard is "%s"', $authGuard)); + app('log')->debug(sprintf('ProfileController::__construct(). Authentication guard is "%s"', $authGuard)); $this->middleware(IsDemoUser::class)->except(['index']); } @@ -115,14 +113,14 @@ class ProfileController extends Controller return redirect(route('profile.index')); } $domain = $this->getDomain(); - $secretPreference = Preferences::get('temp-mfa-secret'); - $codesPreference = Preferences::get('temp-mfa-codes'); + $secretPreference = app('preferences')->get('temp-mfa-secret'); + $codesPreference = app('preferences')->get('temp-mfa-codes'); // generate secret if not in session if (null === $secretPreference) { // generate secret + store + flash $secret = Google2FA::generateSecretKey(); - Preferences::set('temp-mfa-secret', $secret); + app('preferences')->set('temp-mfa-secret', $secret); } // re-use secret if in session @@ -130,6 +128,9 @@ class ProfileController extends Controller // get secret from session and flash $secret = $secretPreference->data; } + if (is_array($secret)) { + $secret = ''; + } // generate recovery codes if not in session: $recoveryCodes = ''; @@ -138,17 +139,20 @@ class ProfileController extends Controller // generate codes + store + flash: $recovery = app(Recovery::class); $recoveryCodes = $recovery->lowercase()->setCount(8)->setBlocks(2)->setChars(6)->toArray(); - Preferences::set('temp-mfa-codes', $recoveryCodes); + app('preferences')->set('temp-mfa-codes', $recoveryCodes); } // get codes from session if present already: if (null !== $codesPreference) { $recoveryCodes = $codesPreference->data; } + if (!is_array($recoveryCodes)) { + $recoveryCodes = []; + } $codes = implode("\r\n", $recoveryCodes); - $image = Google2FA::getQRCodeInline($domain, auth()->user()->email, $secret); + $image = Google2FA::getQRCodeInline($domain, auth()->user()->email, (string)$secret); return view('profile.code', compact('image', 'secret', 'codes')); } @@ -228,8 +232,8 @@ class ProfileController extends Controller /** @var User $user */ $user = auth()->user(); - Preferences::delete('temp-mfa-secret'); - Preferences::delete('temp-mfa-codes'); + app('preferences')->delete('temp-mfa-secret'); + app('preferences')->delete('temp-mfa-codes'); $repository->setMFACode($user, null); app('preferences')->mark(); @@ -284,7 +288,11 @@ class ProfileController extends Controller $subTitle = $user->email; $userId = $user->id; $enabled2FA = null !== $user->mfa_secret; - $mfaBackupCount = count(app('preferences')->get('mfa_recovery', [])->data); + $recoveryData = app('preferences')->get('mfa_recovery', [])->data; + if (!is_array($recoveryData)) { + $recoveryData = []; + } + $mfaBackupCount = count($recoveryData); $this->createOAuthKeys(); if (0 === $count) { @@ -499,12 +507,18 @@ class ProfileController extends Controller $user = auth()->user(); /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); - $secret = Preferences::get('temp-mfa-secret')?->data; + $secret = app('preferences')->get('temp-mfa-secret')?->data; + if (is_array($secret)) { + $secret = null; + } + if (is_int($secret)) { + $secret = (string)$secret; + } $repository->setMFACode($user, $secret); - Preferences::delete('temp-mfa-secret'); - Preferences::delete('temp-mfa-codes'); + app('preferences')->delete('temp-mfa-secret'); + app('preferences')->delete('temp-mfa-codes'); session()->flash('success', (string)trans('firefly.saved_preferences')); app('preferences')->mark(); @@ -595,7 +609,7 @@ class ProfileController extends Controller } /** @var User $user */ $user = auth()->user(); - Log::info(sprintf('User #%d has opted to delete their account', auth()->user()->id)); + app('log')->info(sprintf('User #%d has opted to delete their account', auth()->user()->id)); // make repository delete user: auth()->logout(); session()->flush(); diff --git a/app/Http/Controllers/Recurring/CreateController.php b/app/Http/Controllers/Recurring/CreateController.php index ac51114be3..0fcd8bf211 100644 --- a/app/Http/Controllers/Recurring/CreateController.php +++ b/app/Http/Controllers/Recurring/CreateController.php @@ -163,9 +163,9 @@ class CreateController extends Controller $source = $journal->transactions()->where('amount', '<', 0)->first(); /** @var Transaction $dest */ $dest = $journal->transactions()->where('amount', '>', 0)->first(); - $category = $journal->categories()->first() ? $journal->categories()->first()->name : ''; - $budget = $journal->budgets()->first() ? $journal->budgets()->first()->id : 0; - $bill = $journal->bill ? $journal->bill->id : 0; + $category = null !== $journal->categories()->first() ? $journal->categories()->first()->name : ''; + $budget = null !== $journal->budgets()->first() ? $journal->budgets()->first()->id : 0; + $bill = null !== $journal->bill ? $journal->bill->id : 0; $hasOldInput = null !== $request->old('_token'); // flash some data $preFilled = []; if (true === $hasOldInput) { @@ -241,7 +241,7 @@ class CreateController extends Controller app('preferences')->mark(); // store attachment(s): - /** @var array $files */ + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($recurrence, $files); diff --git a/app/Http/Controllers/Recurring/EditController.php b/app/Http/Controllers/Recurring/EditController.php index f61ae3ac60..8b674ec82c 100644 --- a/app/Http/Controllers/Recurring/EditController.php +++ b/app/Http/Controllers/Recurring/EditController.php @@ -181,6 +181,7 @@ class EditController extends Controller $request->session()->flash('success', (string)trans('firefly.updated_recurrence', ['title' => $recurrence->title])); // store new attachment(s): + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachments->saveAttachmentsForModel($recurrence, $files); diff --git a/app/Http/Controllers/Recurring/TriggerController.php b/app/Http/Controllers/Recurring/TriggerController.php index 173f9e5375..d7e9ac678d 100644 --- a/app/Http/Controllers/Recurring/TriggerController.php +++ b/app/Http/Controllers/Recurring/TriggerController.php @@ -33,7 +33,6 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class TriggerController @@ -55,21 +54,21 @@ class TriggerController extends Controller $backupDate = $recurrence->latest_date; // fire the recurring cron job on the given date, then post-date the created transaction. - Log::info(sprintf('Trigger: will now fire recurring cron job task for date "%s".', $date->format('Y-m-d H:i:s'))); + app('log')->info(sprintf('Trigger: will now fire recurring cron job task for date "%s".', $date->format('Y-m-d H:i:s'))); /** @var CreateRecurringTransactions $job */ $job = app(CreateRecurringTransactions::class); $job->setRecurrences(new Collection([$recurrence])); $job->setDate($date); $job->setForce(false); $job->handle(); - Log::debug('Done with recurrence.'); + app('log')->debug('Done with recurrence.'); $groups = $job->getGroups(); /** @var TransactionGroup $group */ foreach ($groups as $group) { /** @var TransactionJournal $journal */ foreach ($group->transactionJournals as $journal) { - Log::debug(sprintf('Set date of journal #%d to today!', $journal->id)); + app('log')->debug(sprintf('Set date of journal #%d to today!', $journal->id)); $journal->date = today(config('app.timezone')); $journal->save(); } diff --git a/app/Http/Controllers/Report/AccountController.php b/app/Http/Controllers/Report/AccountController.php index 323f289fa7..b8d1149df8 100644 --- a/app/Http/Controllers/Report/AccountController.php +++ b/app/Http/Controllers/Report/AccountController.php @@ -29,7 +29,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -65,8 +64,8 @@ class AccountController extends Controller try { $result = view('reports.partials.accounts', compact('accountReport'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.accounts: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not render reports.partials.accounts: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/Report/BalanceController.php b/app/Http/Controllers/Report/BalanceController.php index b5f17b782c..617feffeab 100644 --- a/app/Http/Controllers/Report/BalanceController.php +++ b/app/Http/Controllers/Report/BalanceController.php @@ -32,7 +32,6 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -105,7 +104,7 @@ class BalanceController extends Controller foreach ($journals as $journal) { $sourceAccount = $journal['source_account_id']; $currencyId = $journal['currency_id']; - $spent[$sourceAccount] = $spent[$sourceAccount] ?? [ + $spent[$sourceAccount] ??= [ 'source_account_id' => $sourceAccount, 'currency_id' => $journal['currency_id'], 'currency_code' => $journal['currency_code'], @@ -117,7 +116,7 @@ class BalanceController extends Controller $spent[$sourceAccount]['spent'] = bcadd($spent[$sourceAccount]['spent'], $journal['amount']); // also fix sum: - $report['sums'][$budgetId][$currencyId] = $report['sums'][$budgetId][$currencyId] ?? [ + $report['sums'][$budgetId][$currencyId] ??= [ 'sum' => '0', 'currency_id' => $journal['currency_id'], 'currency_code' => $journal['currency_code'], @@ -141,8 +140,8 @@ class BalanceController extends Controller try { $result = view('reports.partials.balance', compact('report'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.balance: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not render reports.partials.balance: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/Report/BillController.php b/app/Http/Controllers/Report/BillController.php index bc143c05ff..dce8da7893 100644 --- a/app/Http/Controllers/Report/BillController.php +++ b/app/Http/Controllers/Report/BillController.php @@ -29,7 +29,6 @@ use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -61,8 +60,8 @@ class BillController extends Controller try { $result = view('reports.partials.bills', compact('report'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budgets: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not render reports.partials.budgets: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index e4d3dea46d..7e05b96eee 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -34,7 +34,6 @@ use FireflyIII\Support\Http\Controllers\BasicDataSupport; use FireflyIII\Support\Report\Budget\BudgetReportGenerator; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use JsonException; use Throwable; @@ -110,7 +109,7 @@ class BudgetController extends Controller /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; - $report[$accountId] = $report[$accountId] ?? [ + $report[$accountId] ??= [ 'name' => $account->name, 'id' => $account->id, 'iban' => $account->iban, @@ -121,7 +120,7 @@ class BudgetController extends Controller // loop expenses. foreach ($spent as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -131,7 +130,7 @@ class BudgetController extends Controller foreach ($currency['budgets'] as $budget) { foreach ($budget['transaction_journals'] as $journal) { $sourceAccountId = $journal['source_account_id']; - $report[$sourceAccountId]['currencies'][$currencyId] = $report[$sourceAccountId]['currencies'][$currencyId] ?? [ + $report[$sourceAccountId]['currencies'][$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -168,7 +167,7 @@ class BudgetController extends Controller foreach ($budget['transaction_journals'] as $journal) { $destinationId = $journal['destination_account_id']; $key = sprintf('%d-%d', $destinationId, $currency['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'transactions' => 0, 'sum' => '0', 'avg' => '0', @@ -195,9 +194,9 @@ class BudgetController extends Controller try { $result = view('reports.budget.partials.avg-expenses', compact('result'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getTraceAsString()); throw new FireflyException($result, 0, $e); } @@ -220,7 +219,7 @@ class BudgetController extends Controller /** @var Budget $budget */ foreach ($budgets as $budget) { $budgetId = $budget->id; - $report[$budgetId] = $report[$budgetId] ?? [ + $report[$budgetId] ??= [ 'name' => $budget->name, 'id' => $budget->id, 'currencies' => [], @@ -228,7 +227,7 @@ class BudgetController extends Controller } foreach ($spent as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -241,7 +240,7 @@ class BudgetController extends Controller foreach ($budget['transaction_journals'] as $journal) { // add currency info to report array: - $report[$budgetId]['currencies'][$currencyId] = $report[$budgetId]['currencies'][$currencyId] ?? [ + $report[$budgetId]['currencies'][$currencyId] ??= [ 'sum' => '0', 'sum_pct' => '0', 'currency_id' => $currency['currency_id'], @@ -259,7 +258,7 @@ class BudgetController extends Controller // loop again to get percentages. foreach ($report as $budgetId => $data) { foreach ($data['currencies'] as $currencyId => $dataX) { - $sum = $dataX['sum'] ?? '0'; + $sum = $dataX['sum']; $total = $sums[$currencyId]['sum'] ?? '0'; $pct = '0'; if (0 !== bccomp($sum, '0') && 0 !== bccomp($total, '9')) { @@ -333,7 +332,7 @@ class BudgetController extends Controller $count++; $key = sprintf('%d-%d', $budget['id'], $currency['currency_id']); $dateKey = $journal['date']->format($keyFormat); - $report[$key] = $report[$key] ?? [ + $report[$key] ??= [ 'id' => $budget['id'], 'name' => sprintf('%s (%s)', $budget['name'], $currency['currency_name']), 'sum' => '0', @@ -344,7 +343,7 @@ class BudgetController extends Controller 'currency_decimal_places' => $currency['currency_decimal_places'], 'entries' => [], ]; - $report[$key]['entries'][$dateKey] = $report[$key] ['entries'][$dateKey] ?? '0'; + $report[$key]['entries'][$dateKey] ??= '0'; $report[$key]['entries'][$dateKey] = bcadd($journal['amount'], $report[$key] ['entries'][$dateKey]); $report[$key]['sum'] = bcadd($report[$key] ['sum'], $journal['amount']); $report[$key]['avg'] = bcdiv($report[$key]['sum'], (string)count($periods)); @@ -354,8 +353,8 @@ class BudgetController extends Controller try { $result = view('reports.partials.budget-period', compact('report', 'periods'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } @@ -408,7 +407,7 @@ class BudgetController extends Controller try { $result = view('reports.budget.partials.top-expenses', compact('result'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index bced1f2231..fd1f86a862 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -35,7 +35,6 @@ use FireflyIII\Support\Http\Controllers\BasicDataSupport; use FireflyIII\Support\Report\Category\CategoryReportGenerator; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Throwable; @@ -83,7 +82,7 @@ class CategoryController extends Controller /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; - $report[$accountId] = $report[$accountId] ?? [ + $report[$accountId] ??= [ 'name' => $account->name, 'id' => $account->id, 'iban' => $account->iban, @@ -99,7 +98,7 @@ class CategoryController extends Controller foreach ($currency['categories'] as $category) { foreach ($category['transaction_journals'] as $journal) { $sourceAccountId = $journal['source_account_id']; - $report[$sourceAccountId]['currencies'][$currencyId] = $report[$sourceAccountId]['currencies'][$currencyId] ?? [ + $report[$sourceAccountId]['currencies'][$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -108,13 +107,11 @@ class CategoryController extends Controller ]; $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']] - = $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']] - ?? - [ - 'spent' => '0', - 'earned' => '0', - 'sum' => '0', - ]; + ??= [ + 'spent' => '0', + 'earned' => '0', + 'sum' => '0', + ]; $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]['spent'] = bcadd( $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]['spent'], $journal['amount'] @@ -135,22 +132,19 @@ class CategoryController extends Controller foreach ($category['transaction_journals'] as $journal) { $destinationId = $journal['destination_account_id']; $report[$destinationId]['currencies'][$currencyId] - = $report[$destinationId]['currencies'][$currencyId] - ?? [ - 'currency_id' => $currency['currency_id'], - 'currency_symbol' => $currency['currency_symbol'], - 'currency_name' => $currency['currency_name'], - 'currency_decimal_places' => $currency['currency_decimal_places'], - 'categories' => [], - ]; + ??= [ + 'currency_id' => $currency['currency_id'], + 'currency_symbol' => $currency['currency_symbol'], + 'currency_name' => $currency['currency_name'], + 'currency_decimal_places' => $currency['currency_decimal_places'], + 'categories' => [], + ]; $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']] - = $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']] - ?? - [ - 'spent' => '0', - 'earned' => '0', - 'sum' => '0', - ]; + ??= [ + 'spent' => '0', + 'earned' => '0', + 'sum' => '0', + ]; $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]['earned'] = bcadd( $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]['earned'], $journal['amount'] @@ -183,7 +177,7 @@ class CategoryController extends Controller /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; - $report[$accountId] = $report[$accountId] ?? [ + $report[$accountId] ??= [ 'name' => $account->name, 'id' => $account->id, 'iban' => $account->iban, @@ -194,7 +188,7 @@ class CategoryController extends Controller // loop expenses. foreach ($spent as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -206,7 +200,7 @@ class CategoryController extends Controller foreach ($currency['categories'] as $category) { foreach ($category['transaction_journals'] as $journal) { $sourceAccountId = $journal['source_account_id']; - $report[$sourceAccountId]['currencies'][$currencyId] = $report[$sourceAccountId]['currencies'][$currencyId] ?? [ + $report[$sourceAccountId]['currencies'][$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -232,7 +226,7 @@ class CategoryController extends Controller // loop income. foreach ($earned as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -244,7 +238,7 @@ class CategoryController extends Controller foreach ($currency['categories'] as $category) { foreach ($category['transaction_journals'] as $journal) { $destinationAccountId = $journal['destination_account_id']; - $report[$destinationAccountId]['currencies'][$currencyId] = $report[$destinationAccountId]['currencies'][$currencyId] ?? [ + $report[$destinationAccountId]['currencies'][$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -288,7 +282,7 @@ class CategoryController extends Controller foreach ($category['transaction_journals'] as $journal) { $destinationId = $journal['destination_account_id']; $key = sprintf('%d-%d', $destinationId, $currency['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'transactions' => 0, 'sum' => '0', 'avg' => '0', @@ -315,7 +309,7 @@ class CategoryController extends Controller try { $result = view('reports.category.partials.avg-expenses', compact('result'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -341,7 +335,7 @@ class CategoryController extends Controller foreach ($category['transaction_journals'] as $journal) { $sourceId = $journal['source_account_id']; $key = sprintf('%d-%d', $sourceId, $currency['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'transactions' => 0, 'sum' => '0', 'avg' => '0', @@ -368,7 +362,7 @@ class CategoryController extends Controller try { $result = view('reports.category.partials.avg-income', compact('result'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -393,7 +387,7 @@ class CategoryController extends Controller /** @var Category $category */ foreach ($categories as $category) { $categoryId = $category->id; - $report[$categoryId] = $report[$categoryId] ?? [ + $report[$categoryId] ??= [ 'name' => $category->name, 'id' => $category->id, 'currencies' => [], @@ -401,7 +395,7 @@ class CategoryController extends Controller } foreach ($spent as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -416,7 +410,7 @@ class CategoryController extends Controller foreach ($category['transaction_journals'] as $journal) { // add currency info to report array: - $report[$categoryId]['currencies'][$currencyId] = $report[$categoryId]['currencies'][$currencyId] ?? [ + $report[$categoryId]['currencies'][$currencyId] ??= [ 'spent' => '0', 'earned' => '0', 'sum' => '0', @@ -442,7 +436,7 @@ class CategoryController extends Controller foreach ($earned as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -457,7 +451,7 @@ class CategoryController extends Controller foreach ($category['transaction_journals'] as $journal) { // add currency info to report array: - $report[$categoryId]['currencies'][$currencyId] = $report[$categoryId]['currencies'][$currencyId] ?? [ + $report[$categoryId]['currencies'][$currencyId] ??= [ 'spent' => '0', 'earned' => '0', 'sum' => '0', @@ -524,7 +518,7 @@ class CategoryController extends Controller foreach ($set as $currencyId => $currencyRow) { foreach ($currencyRow['categories'] as $categoryId => $categoryRow) { $key = sprintf('%d-%d', $currencyId, $categoryId); - $data[$key] = $data[$key] ?? [ + $data[$key] ??= [ 'id' => $categoryRow['id'], 'title' => sprintf('%s (%s)', $categoryRow['name'], $currencyRow['currency_name']), 'currency_id' => $currencyRow['currency_id'], @@ -538,7 +532,7 @@ class CategoryController extends Controller ]; foreach ($categoryRow['transaction_journals'] as $journal) { $date = $journal['date']->format($format); - $data[$key]['entries'][$date] = $data[$key]['entries'][$date] ?? '0'; + $data[$key]['entries'][$date] ??= '0'; $data[$key]['entries'][$date] = bcadd($data[$key]['entries'][$date], $journal['amount']); $data[$key]['sum'] = bcadd($data[$key]['sum'], $journal['amount']); } @@ -553,7 +547,7 @@ class CategoryController extends Controller try { $result = view('reports.partials.category-period', compact('report', 'periods'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render category::expenses: %s', $e->getMessage())); $result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -605,7 +599,7 @@ class CategoryController extends Controller foreach ($set as $currencyId => $currencyRow) { foreach ($currencyRow['categories'] as $categoryId => $categoryRow) { $key = sprintf('%d-%d', $currencyId, $categoryId); - $data[$key] = $data[$key] ?? [ + $data[$key] ??= [ 'id' => $categoryRow['id'], 'title' => sprintf('%s (%s)', $categoryRow['name'], $currencyRow['currency_name']), 'currency_id' => $currencyRow['currency_id'], @@ -619,7 +613,7 @@ class CategoryController extends Controller ]; foreach ($categoryRow['transaction_journals'] as $journal) { $date = $journal['date']->format($format); - $data[$key]['entries'][$date] = $data[$key]['entries'][$date] ?? '0'; + $data[$key]['entries'][$date] ??= '0'; $data[$key]['entries'][$date] = bcadd($data[$key]['entries'][$date], $journal['amount']); $data[$key]['sum'] = bcadd($data[$key]['sum'], $journal['amount']); } @@ -632,7 +626,7 @@ class CategoryController extends Controller try { $result = view('reports.partials.category-period', compact('report', 'periods'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render category::expenses: %s', $e->getMessage())); $result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -675,10 +669,10 @@ class CategoryController extends Controller try { - $result = (string)view('reports.partials.categories', compact('report'))->render(); + $result = view('reports.partials.categories', compact('report'))->render(); $cache->store($result); } catch (Throwable $e) { - Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render category::expenses: %s', $e->getMessage())); $result = sprintf('An error prevented Firefly III from rendering: %s. Apologies.', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -729,7 +723,7 @@ class CategoryController extends Controller try { $result = view('reports.category.partials.top-expenses', compact('result'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($e->getMessage(), 0, $e); } @@ -780,7 +774,7 @@ class CategoryController extends Controller try { $result = view('reports.category.partials.top-income', compact('result'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($e->getMessage(), 0, $e); } diff --git a/app/Http/Controllers/Report/DoubleController.php b/app/Http/Controllers/Report/DoubleController.php index c72eb63732..ee0835e66c 100644 --- a/app/Http/Controllers/Report/DoubleController.php +++ b/app/Http/Controllers/Report/DoubleController.php @@ -32,7 +32,6 @@ use FireflyIII\Repositories\Account\OperationsRepositoryInterface; use FireflyIII\Support\Http\Controllers\AugumentData; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Throwable; @@ -89,7 +88,7 @@ class DoubleController extends Controller foreach ($currency['transaction_journals'] as $journal) { $sourceId = $journal['source_account_id']; $key = sprintf('%d-%d', $sourceId, $currency['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'transactions' => 0, 'sum' => '0', 'avg' => '0', @@ -115,7 +114,7 @@ class DoubleController extends Controller try { $result = view('reports.double.partials.avg-expenses', compact('result'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($e->getMessage(), 0, $e); } @@ -142,7 +141,7 @@ class DoubleController extends Controller foreach ($currency['transaction_journals'] as $journal) { $destinationId = $journal['destination_account_id']; $key = sprintf('%d-%d', $destinationId, $currency['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'transactions' => 0, 'sum' => '0', 'avg' => '0', @@ -168,7 +167,7 @@ class DoubleController extends Controller try { $result = view('reports.double.partials.avg-income', compact('result'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($e->getMessage(), 0, $e); } @@ -199,7 +198,7 @@ class DoubleController extends Controller foreach ($spent as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'spent' => '0', 'earned' => '0', 'sum' => '0', @@ -217,7 +216,7 @@ class DoubleController extends Controller $destIban = $journal['destination_account_iban']; $genericName = $this->getCounterpartName($withCounterpart, $destId, $destName, $destIban); $objectName = sprintf('%s (%s)', $genericName, $currency['currency_name']); - $report[$objectName] = $report[$objectName] ?? [ + $report[$objectName] ??= [ 'dest_name' => '', 'dest_iban' => '', 'source_name' => '', @@ -247,7 +246,7 @@ class DoubleController extends Controller foreach ($earned as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'spent' => '0', 'earned' => '0', 'sum' => '0', @@ -265,7 +264,7 @@ class DoubleController extends Controller $sourceIban = $journal['source_account_iban']; $genericName = $this->getCounterpartName($withCounterpart, $sourceId, $sourceName, $sourceIban); $objectName = sprintf('%s (%s)', $genericName, $currency['currency_name']); - $report[$objectName] = $report[$objectName] ?? [ + $report[$objectName] ??= [ 'dest_name' => '', 'dest_iban' => '', 'source_name' => '', @@ -343,7 +342,7 @@ class DoubleController extends Controller foreach ($spent as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'spent' => '0', 'earned' => '0', 'sum' => '0', @@ -357,7 +356,7 @@ class DoubleController extends Controller /** @var array $journal */ foreach ($currency['transaction_journals'] as $journal) { $objectName = sprintf('%s (%s)', $journal['source_account_name'], $currency['currency_name']); - $report[$objectName] = $report[$objectName] ?? [ + $report[$objectName] ??= [ 'account_id' => $journal['source_account_id'], 'account_name' => $objectName, 'currency_id' => $currency['currency_id'], @@ -382,7 +381,7 @@ class DoubleController extends Controller foreach ($earned as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'spent' => '0', 'earned' => '0', 'sum' => '0', @@ -396,7 +395,7 @@ class DoubleController extends Controller /** @var array $journal */ foreach ($currency['transaction_journals'] as $journal) { $objectName = sprintf('%s (%s)', $journal['destination_account_name'], $currency['currency_name']); - $report[$objectName] = $report[$objectName] ?? [ + $report[$objectName] ??= [ 'account_id' => $journal['destination_account_id'], 'account_name' => $objectName, 'currency_id' => $currency['currency_id'], @@ -463,7 +462,7 @@ class DoubleController extends Controller try { $result = view('reports.double.partials.top-expenses', compact('result'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($e->getMessage(), 0, $e); } @@ -514,7 +513,7 @@ class DoubleController extends Controller try { $result = view('reports.double.partials.top-income', compact('result'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($e->getMessage(), 0, $e); } diff --git a/app/Http/Controllers/Report/OperationsController.php b/app/Http/Controllers/Report/OperationsController.php index 9eb7d89b40..556c794679 100644 --- a/app/Http/Controllers/Report/OperationsController.php +++ b/app/Http/Controllers/Report/OperationsController.php @@ -29,7 +29,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -85,8 +84,8 @@ class OperationsController extends Controller try { $result = view('reports.partials.income-expenses', compact('report', 'type'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.income-expense: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not render reports.partials.income-expense: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } @@ -122,8 +121,8 @@ class OperationsController extends Controller try { $result = view('reports.partials.income-expenses', compact('report', 'type'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.income-expenses: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not render reports.partials.income-expenses: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } @@ -163,7 +162,7 @@ class OperationsController extends Controller /** @var int $currencyId */ foreach ($keys as $currencyId) { $currencyInfo = $incomes['sums'][$currencyId] ?? $expenses['sums'][$currencyId]; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_name' => $currencyInfo['currency_name'], 'currency_code' => $currencyInfo['currency_code'], @@ -179,8 +178,8 @@ class OperationsController extends Controller try { $result = view('reports.partials.operations', compact('sums'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render reports.partials.operations: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not render reports.partials.operations: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/Report/TagController.php b/app/Http/Controllers/Report/TagController.php index 8b43ead1db..72567c3674 100644 --- a/app/Http/Controllers/Report/TagController.php +++ b/app/Http/Controllers/Report/TagController.php @@ -31,7 +31,6 @@ use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\OperationsRepositoryInterface; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Throwable; @@ -76,7 +75,7 @@ class TagController extends Controller /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; - $report[$accountId] = $report[$accountId] ?? [ + $report[$accountId] ??= [ 'name' => $account->name, 'id' => $account->id, 'iban' => $account->iban, @@ -92,7 +91,7 @@ class TagController extends Controller foreach ($currency['tags'] as $tag) { foreach ($tag['transaction_journals'] as $journal) { $sourceAccountId = $journal['source_account_id']; - $report[$sourceAccountId]['currencies'][$currencyId] = $report[$sourceAccountId]['currencies'][$currencyId] ?? [ + $report[$sourceAccountId]['currencies'][$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -101,13 +100,11 @@ class TagController extends Controller ]; $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']] - = $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']] - ?? - [ - 'spent' => '0', - 'earned' => '0', - 'sum' => '0', - ]; + ??= [ + 'spent' => '0', + 'earned' => '0', + 'sum' => '0', + ]; $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['spent'] = bcadd( $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['spent'], $journal['amount'] @@ -128,22 +125,19 @@ class TagController extends Controller foreach ($tag['transaction_journals'] as $journal) { $destinationId = $journal['destination_account_id']; $report[$destinationId]['currencies'][$currencyId] - = $report[$destinationId]['currencies'][$currencyId] - ?? [ - 'currency_id' => $currency['currency_id'], - 'currency_symbol' => $currency['currency_symbol'], - 'currency_name' => $currency['currency_name'], - 'currency_decimal_places' => $currency['currency_decimal_places'], - 'tags' => [], - ]; + ??= [ + 'currency_id' => $currency['currency_id'], + 'currency_symbol' => $currency['currency_symbol'], + 'currency_name' => $currency['currency_name'], + 'currency_decimal_places' => $currency['currency_decimal_places'], + 'tags' => [], + ]; $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']] - = $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']] - ?? - [ - 'spent' => '0', - 'earned' => '0', - 'sum' => '0', - ]; + ??= [ + 'spent' => '0', + 'earned' => '0', + 'sum' => '0', + ]; $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['earned'] = bcadd( $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['earned'], $journal['amount'] @@ -176,7 +170,7 @@ class TagController extends Controller /** @var Account $account */ foreach ($accounts as $account) { $accountId = $account->id; - $report[$accountId] = $report[$accountId] ?? [ + $report[$accountId] ??= [ 'name' => $account->name, 'id' => $account->id, 'iban' => $account->iban, @@ -187,7 +181,7 @@ class TagController extends Controller // loop expenses. foreach ($spent as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -199,7 +193,7 @@ class TagController extends Controller foreach ($currency['tags'] as $tag) { foreach ($tag['transaction_journals'] as $journal) { $sourceAccountId = $journal['source_account_id']; - $report[$sourceAccountId]['currencies'][$currencyId] = $report[$sourceAccountId]['currencies'][$currencyId] ?? [ + $report[$sourceAccountId]['currencies'][$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -225,7 +219,7 @@ class TagController extends Controller // loop income. foreach ($earned as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -237,7 +231,7 @@ class TagController extends Controller foreach ($currency['tags'] as $tag) { foreach ($tag['transaction_journals'] as $journal) { $destinationAccountId = $journal['destination_account_id']; - $report[$destinationAccountId]['currencies'][$currencyId] = $report[$destinationAccountId]['currencies'][$currencyId] ?? [ + $report[$destinationAccountId]['currencies'][$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -281,7 +275,7 @@ class TagController extends Controller foreach ($tag['transaction_journals'] as $journal) { $destinationId = $journal['destination_account_id']; $key = sprintf('%d-%d', $destinationId, $currency['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'transactions' => 0, 'sum' => '0', 'avg' => '0', @@ -308,7 +302,7 @@ class TagController extends Controller try { $result = view('reports.tag.partials.avg-expenses', compact('result'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -334,7 +328,7 @@ class TagController extends Controller foreach ($tag['transaction_journals'] as $journal) { $sourceId = $journal['source_account_id']; $key = sprintf('%d-%d', $sourceId, $currency['currency_id']); - $result[$key] = $result[$key] ?? [ + $result[$key] ??= [ 'transactions' => 0, 'sum' => '0', 'avg' => '0', @@ -361,7 +355,7 @@ class TagController extends Controller try { $result = view('reports.tag.partials.avg-income', compact('result'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -386,7 +380,7 @@ class TagController extends Controller /** @var Tag $tag */ foreach ($tags as $tag) { $tagId = $tag->id; - $report[$tagId] = $report[$tagId] ?? [ + $report[$tagId] ??= [ 'name' => $tag->tag, 'id' => $tag->id, 'currencies' => [], @@ -394,7 +388,7 @@ class TagController extends Controller } foreach ($spent as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -409,7 +403,7 @@ class TagController extends Controller foreach ($tag['transaction_journals'] as $journal) { // add currency info to report array: - $report[$tagId]['currencies'][$currencyId] = $report[$tagId]['currencies'][$currencyId] ?? [ + $report[$tagId]['currencies'][$currencyId] ??= [ 'spent' => '0', 'earned' => '0', 'sum' => '0', @@ -435,7 +429,7 @@ class TagController extends Controller foreach ($earned as $currency) { $currencyId = $currency['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currency['currency_id'], 'currency_symbol' => $currency['currency_symbol'], 'currency_name' => $currency['currency_name'], @@ -450,7 +444,7 @@ class TagController extends Controller foreach ($tag['transaction_journals'] as $journal) { // add currency info to report array: - $report[$tagId]['currencies'][$currencyId] = $report[$tagId]['currencies'][$currencyId] ?? [ + $report[$tagId]['currencies'][$currencyId] ??= [ 'spent' => '0', 'earned' => '0', 'sum' => '0', @@ -520,7 +514,7 @@ class TagController extends Controller try { $result = view('reports.tag.partials.top-expenses', compact('result'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } @@ -571,7 +565,7 @@ class TagController extends Controller try { $result = view('reports.tag.partials.top-income', compact('result'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); $result = sprintf('Could not render view: %s', $e->getMessage()); throw new FireflyException($result, 0, $e); } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 029dce0424..fa129a000c 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -38,7 +38,6 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -51,11 +50,8 @@ class ReportController extends Controller { use RenderPartialViews; - /** @var ReportHelperInterface Helper interface. */ - protected $helper; - - /** @var BudgetRepositoryInterface The budget repository */ - private $repository; + protected ReportHelperInterface $helper; + private BudgetRepositoryInterface $repository; /** * ReportController constructor. @@ -292,7 +288,7 @@ class ReportController extends Controller if ('opt_group_' === $role) { $role = 'opt_group_defaultAsset'; } - $groupedAccounts[trans(sprintf('firefly.%s', $role))][$account->id] = $account; + $groupedAccounts[(string)trans(sprintf('firefly.%s', $role))][$account->id] = $account; } ksort($groupedAccounts); @@ -328,12 +324,12 @@ class ReportController extends Controller * * @param ReportFormRequest $request * - * @return RedirectResponse|Redirector + * @return RedirectResponse|Redirector|View * * @throws FireflyException * */ - public function postIndex(ReportFormRequest $request) + public function postIndex(ReportFormRequest $request): RedirectResponse | Redirector | View { // report type: $reportType = $request->get('report_type'); @@ -346,7 +342,7 @@ class ReportController extends Controller $double = implode(',', $request->getDoubleList()->pluck('id')->toArray()); if (0 === $request->getAccountList()->count()) { - Log::debug('Account count is zero'); + app('log')->debug('Account count is zero'); session()->flash('error', (string)trans('firefly.select_at_least_one_account')); return redirect(route('reports.index')); diff --git a/app/Http/Controllers/Rule/CreateController.php b/app/Http/Controllers/Rule/CreateController.php index eb42aa0b03..18bdb10815 100644 --- a/app/Http/Controllers/Rule/CreateController.php +++ b/app/Http/Controllers/Rule/CreateController.php @@ -45,8 +45,8 @@ use Illuminate\View\View; */ class CreateController extends Controller { - use RuleManagement; use ModelInformation; + use RuleManagement; private RuleRepositoryInterface $ruleRepos; @@ -100,13 +100,15 @@ class CreateController extends Controller session()->flash('warning', trans('firefly.rule_from_search_words', ['string' => $words])); $operators[] = [ 'type' => 'description_contains', - 'value' => $words]; + 'value' => $words, + ]; } $oldTriggers = $this->parseFromOperators($operators); } + //var_dump($oldTriggers);exit; // restore actions and triggers from old input: - if ($request->old()) { + if (is_array($request->old()) && count($request->old()) > 0) { $oldTriggers = $this->getPreviousTriggers($request); $oldActions = $this->getPreviousActions($request); } @@ -163,7 +165,7 @@ class CreateController extends Controller $oldActions = $this->getActionsForBill($bill); // restore actions and triggers from old input: - if ($request->old()) { + if (null !== $request->old()) { $oldTriggers = $this->getPreviousTriggers($request); $oldActions = $this->getPreviousActions($request); } @@ -172,7 +174,7 @@ class CreateController extends Controller $actionCount = count($oldActions); $subTitleIcon = 'fa-clone'; - // title depends on whether or not there is a rule group: + // title depends on whether there is a rule group: $subTitle = (string)trans('firefly.make_new_rule_no_group'); // flash old data @@ -218,7 +220,7 @@ class CreateController extends Controller ]; // restore actions and triggers from old input: - if ($request->old()) { + if (null !== $request->old() && is_array($request->old()) && count($request->old()) > 0) { $oldTriggers = $this->getPreviousTriggers($request); $oldActions = $this->getPreviousActions($request); } diff --git a/app/Http/Controllers/Rule/EditController.php b/app/Http/Controllers/Rule/EditController.php index de93bb2839..c159ca4685 100644 --- a/app/Http/Controllers/Rule/EditController.php +++ b/app/Http/Controllers/Rule/EditController.php @@ -36,7 +36,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Throwable; @@ -45,8 +44,8 @@ use Throwable; */ class EditController extends Controller { - use RuleManagement; use RenderPartialViews; + use RuleManagement; private RuleRepositoryInterface $ruleRepos; @@ -101,7 +100,7 @@ class EditController extends Controller $oldTriggers = $this->parseFromOperators($operators); } // has old input? - if (count($request->old()) > 0) { + if (null !== $request->old() && is_array($request->old()) && count($request->old()) > 0) { $oldTriggers = $this->getPreviousTriggers($request); $oldActions = $this->getPreviousActions($request); } @@ -173,8 +172,8 @@ class EditController extends Controller )->render(); } catch (Throwable $e) { $message = sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()); - Log::debug($message); - Log::error($e->getTraceAsString()); + app('log')->debug($message); + app('log')->error($e->getTraceAsString()); throw new FireflyException($message, 0, $e); } $index++; diff --git a/app/Http/Controllers/Rule/IndexController.php b/app/Http/Controllers/Rule/IndexController.php index f4dec56a96..d83e726877 100644 --- a/app/Http/Controllers/Rule/IndexController.php +++ b/app/Http/Controllers/Rule/IndexController.php @@ -89,7 +89,7 @@ class IndexController extends Controller public function moveRule(Request $request, Rule $rule, RuleGroup $ruleGroup): JsonResponse { $order = (int)$request->get('order'); - $this->ruleRepos->moveRule($rule, $ruleGroup, (int)$order); + $this->ruleRepos->moveRule($rule, $ruleGroup, $order); return response()->json([]); } diff --git a/app/Http/Controllers/Rule/SelectController.php b/app/Http/Controllers/Rule/SelectController.php index 89a3317702..2d77544620 100644 --- a/app/Http/Controllers/Rule/SelectController.php +++ b/app/Http/Controllers/Rule/SelectController.php @@ -38,7 +38,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Throwable; @@ -58,7 +57,7 @@ class SelectController extends Controller parent::__construct(); $this->middleware( - function ($request, $next) { + static function ($request, $next) { app('view')->share('title', (string)trans('firefly.rules')); app('view')->share('mainTitleIcon', 'fa-random'); @@ -98,7 +97,7 @@ class SelectController extends Controller $newRuleEngine->fire(); $resultCount = $newRuleEngine->getResults(); - session()->flash('success', (string)trans_choice('firefly.applied_rule_selection', $resultCount, ['title' => $rule->title])); + session()->flash('success', trans_choice('firefly.applied_rule_selection', $resultCount, ['title' => $rule->title])); return redirect()->route('rules.index'); } @@ -108,9 +107,9 @@ class SelectController extends Controller * * @param Rule $rule * - * @return Factory|View + * @return Factory|View|RedirectResponse */ - public function selectTransactions(Rule $rule) + public function selectTransactions(Rule $rule): Factory | View | RedirectResponse { if (false === $rule->active) { session()->flash('warning', trans('firefly.cannot_fire_inactive_rules')); @@ -137,7 +136,8 @@ class SelectController extends Controller public function testTriggers(TestRuleFormRequest $request): JsonResponse { // build fake rule - $rule = new Rule(); + $rule = new Rule(); + /** @var \Illuminate\Database\Eloquent\Collection $triggers */ $triggers = new Collection(); $rule->strict = '1' === $request->get('strict'); @@ -183,8 +183,8 @@ class SelectController extends Controller try { $view = view('list.journals-array-tiny', ['groups' => $collection])->render(); } catch (Throwable $exception) { - Log::error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage())); - Log::error($exception->getTraceAsString()); + app('log')->error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage())); + app('log')->error($exception->getTraceAsString()); $view = sprintf('Could not render list.journals-tiny: %s', $exception->getMessage()); throw new FireflyException($view, 0, $exception); } @@ -227,8 +227,8 @@ class SelectController extends Controller $view = view('list.journals-array-tiny', ['groups' => $collection])->render(); } catch (Throwable $exception) { $message = sprintf('Could not render view in testTriggersByRule(): %s', $exception->getMessage()); - Log::error($message); - Log::error($exception->getTraceAsString()); + app('log')->error($message); + app('log')->error($exception->getTraceAsString()); throw new FireflyException($message, 0, $exception); } diff --git a/app/Http/Controllers/RuleGroup/EditController.php b/app/Http/Controllers/RuleGroup/EditController.php index 828fd3d0c2..6f0ae47a71 100644 --- a/app/Http/Controllers/RuleGroup/EditController.php +++ b/app/Http/Controllers/RuleGroup/EditController.php @@ -103,14 +103,14 @@ class EditController extends Controller $direction = $request->get('direction'); if ('down' === $direction) { $maxOrder = $this->repository->maxOrder(); - $order = (int)$ruleGroup->order; + $order = $ruleGroup->order; if ($order < $maxOrder) { $newOrder = $order + 1; $this->repository->setOrder($ruleGroup, $newOrder); } } if ('up' === $direction) { - $order = (int)$ruleGroup->order; + $order = $ruleGroup->order; if ($order > 1) { $newOrder = $order - 1; $this->repository->setOrder($ruleGroup, $newOrder); diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 5963f8eb5e..86d4c13bad 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -29,7 +29,6 @@ use FireflyIII\Support\Search\SearchInterface; use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Throwable; @@ -127,8 +126,8 @@ class SearchController extends Controller try { $html = view('search.search', compact('groups', 'hasPages', 'searchTime'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render search.search: %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Cannot render search.search: %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $html = 'Could not render view.'; throw new FireflyException($html, 0, $e); } diff --git a/app/Http/Controllers/System/CronController.php b/app/Http/Controllers/System/CronController.php index 1e9c80f9dd..75c26d4206 100644 --- a/app/Http/Controllers/System/CronController.php +++ b/app/Http/Controllers/System/CronController.php @@ -26,7 +26,6 @@ namespace FireflyIII\Http\Controllers\System; use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Routing\ResponseFactory; use Illuminate\Http\Response; -use Illuminate\Support\Facades\Log; /** * Class CronController @@ -38,7 +37,7 @@ class CronController */ public function cron() { - Log::error('The cron endpoint has moved to GET /api/v1/cron/[token]'); + app('log')->error('The cron endpoint has moved to GET /api/v1/cron/[token]'); return response('The cron endpoint has moved to GET /api/v1/cron/[token]', 500); } diff --git a/app/Http/Controllers/System/InstallController.php b/app/Http/Controllers/System/InstallController.php index 42cd599107..e5eccf6b11 100644 --- a/app/Http/Controllers/System/InstallController.php +++ b/app/Http/Controllers/System/InstallController.php @@ -28,12 +28,10 @@ use Cache; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; -use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\Http\Controllers\GetConfigurationData; use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Laravel\Passport\Passport; use phpseclib3\Crypt\RSA; @@ -47,9 +45,9 @@ class InstallController extends Controller { use GetConfigurationData; - public const BASEDIR_ERROR = 'Firefly III cannot execute the upgrade commands. It is not allowed to because of an open_basedir restriction.'; - public const FORBIDDEN_ERROR = 'Internal PHP function "proc_close" is disabled for your installation. Auto-migration is not possible.'; - public const OTHER_ERROR = 'An unknown error prevented Firefly III from executing the upgrade commands. Sorry.'; + public const string BASEDIR_ERROR = 'Firefly III cannot execute the upgrade commands. It is not allowed to because of an open_basedir restriction.'; + public const string FORBIDDEN_ERROR = 'Internal PHP function "proc_close" is disabled for your installation. Auto-migration is not possible.'; + public const string OTHER_ERROR = 'An unknown error prevented Firefly III from executing the upgrade commands. Sorry.'; private string $lastError; private array $upgradeCommands; @@ -58,6 +56,7 @@ class InstallController extends Controller */ public function __construct() { + parent::__construct(); // empty on purpose. $this->upgradeCommands = [ // there are 5 initial commands @@ -107,18 +106,18 @@ class InstallController extends Controller 'errorMessage' => null, ]; - Log::debug(sprintf('Will now run commands. Request index is %d', $requestIndex)); + app('log')->debug(sprintf('Will now run commands. Request index is %d', $requestIndex)); $indexes = array_values(array_keys($this->upgradeCommands)); if (array_key_exists($requestIndex, $indexes)) { $command = $indexes[$requestIndex]; $parameters = $this->upgradeCommands[$command]; - Log::debug(sprintf('Will now execute command "%s" with parameters', $command), $parameters); + app('log')->debug(sprintf('Will now execute command "%s" with parameters', $command), $parameters); try { $result = $this->executeCommand($command, $parameters); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); - if (strpos($e->getMessage(), 'open_basedir restriction in effect')) { + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); + if (str_contains($e->getMessage(), 'open_basedir restriction in effect')) { $this->lastError = self::BASEDIR_ERROR; } $result = false; @@ -144,21 +143,21 @@ class InstallController extends Controller */ private function executeCommand(string $command, array $args): bool { - Log::debug(sprintf('Will now call command %s with args.', $command), $args); + app('log')->debug(sprintf('Will now call command %s with args.', $command), $args); try { if ('generate-keys' === $command) { $this->keys(); } if ('generate-keys' !== $command) { Artisan::call($command, $args); - Log::debug(Artisan::output()); + app('log')->debug(Artisan::output()); } } catch (Exception $e) { // intentional generic exception throw new FireflyException($e->getMessage(), 0, $e); } // clear cache as well. Cache::clear(); - Preferences::mark(); + app('preferences')->mark(); return true; } @@ -168,11 +167,8 @@ class InstallController extends Controller */ public function keys(): void { - // switch on PHP version. - $keys = []; - // switch on class existence. - Log::info('Will run PHP8 code.'); - $keys = RSA::createKey(4096); + + $key = RSA::createKey(4096); [$publicKey, $privateKey] = [ Passport::keyPath('oauth-public.key'), @@ -183,7 +179,7 @@ class InstallController extends Controller return; } - file_put_contents($publicKey, $keys['publickey']); - file_put_contents($privateKey, $keys['privatekey']); + file_put_contents($publicKey, (string)$key->getPublicKey()); + file_put_contents($privateKey, $key->toString('PKCS1')); } } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 1fd2592349..7bc69c140f 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -34,7 +34,6 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -130,9 +129,9 @@ class TagController extends Controller $subTitleIcon = 'fa-tag'; $location = $this->repository->getLocation($tag); - $latitude = $location ? $location->latitude : config('firefly.default_location.latitude'); - $longitude = $location ? $location->longitude : config('firefly.default_location.longitude'); - $zoomLevel = $location ? $location->zoom_level : config('firefly.default_location.zoom_level'); + $latitude = null !== $location ? $location->latitude : config('firefly.default_location.latitude'); + $longitude = null !== $location ? $location->longitude : config('firefly.default_location.longitude'); + $zoomLevel = null !== $location ? $location->zoom_level : config('firefly.default_location.zoom_level'); $hasLocation = null !== $location; $locations = [ 'location' => [ @@ -181,9 +180,11 @@ class TagController extends Controller } /** + * @param Request $request * + * @return RedirectResponse */ - public function massDestroy(Request $request) + public function massDestroy(Request $request): RedirectResponse { $tags = $request->get('tags'); if (null === $tags || !is_array($tags)) { @@ -200,7 +201,7 @@ class TagController extends Controller $count++; } } - session()->flash('success', (string)trans_choice('firefly.deleted_x_tags', $count)); + session()->flash('success', trans_choice('firefly.deleted_x_tags', $count)); return redirect(route('tags.index')); } @@ -242,8 +243,8 @@ class TagController extends Controller $subTitleIcon = 'fa-tag'; $page = (int)$request->get('page'); $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; - $start = $start ?? session('start'); - $end = $end ?? session('end'); + $start ??= session('start'); + $end ??= session('end'); $location = $this->repository->getLocation($tag); $attachments = $this->repository->getAttachments($tag); $subTitle = trans( @@ -256,7 +257,7 @@ class TagController extends Controller ); $startPeriod = $this->repository->firstUseDate($tag); - $startPeriod = $startPeriod ?? today(config('app.timezone')); + $startPeriod ??= today(config('app.timezone')); $endPeriod = clone $end; $periods = $this->getTagPeriodOverview($tag, $startPeriod, $endPeriod); $path = route('tags.show', [$tag->id, $start->format('Y-m-d'), $end->format('Y-m-d')]); @@ -317,16 +318,16 @@ class TagController extends Controller public function store(TagFormRequest $request): RedirectResponse { $data = $request->collectTagData(); - Log::debug('Data from request', $data); + app('log')->debug('Data from request', $data); $result = $this->repository->store($data); - Log::debug('Data after storage', $result->toArray()); + app('log')->debug('Data after storage', $result->toArray()); session()->flash('success', (string)trans('firefly.created_tag', ['tag' => $data['tag']])); app('preferences')->mark(); // store attachment(s): - /** @var array $files */ + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachmentsHelper->saveAttachmentsForModel($result, $files); @@ -365,7 +366,7 @@ class TagController extends Controller app('preferences')->mark(); // store new attachment(s): - /** @var array $files */ + /** @var array|null $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; if (null !== $files && !auth()->user()->hasRole('demo')) { $this->attachmentsHelper->saveAttachmentsForModel($tag, $files); diff --git a/app/Http/Controllers/Transaction/BulkController.php b/app/Http/Controllers/Transaction/BulkController.php index 28f4831ea4..58f889bcb2 100644 --- a/app/Http/Controllers/Transaction/BulkController.php +++ b/app/Http/Controllers/Transaction/BulkController.php @@ -34,7 +34,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; /** @@ -128,7 +127,7 @@ class BulkController extends Controller } app('preferences')->mark(); - $request->session()->flash('success', (string)trans_choice('firefly.mass_edited_transactions_success', $count)); + $request->session()->flash('success', trans_choice('firefly.mass_edited_transactions_success', $count)); // redirect to previous URL: return redirect($this->getPreviousUrl('transactions.bulk-edit.url')); @@ -146,7 +145,7 @@ class BulkController extends Controller if (true === $ignoreUpdate) { return false; } - Log::debug(sprintf('Set budget to %d', $budgetId)); + app('log')->debug(sprintf('Set budget to %d', $budgetId)); $this->repository->updateBudget($journal, $budgetId); return true; @@ -162,7 +161,7 @@ class BulkController extends Controller private function updateJournalTags(TransactionJournal $journal, string $action, array $tags): bool { if ('do_replace' === $action) { - Log::debug(sprintf('Set tags to %s', implode(',', $tags))); + app('log')->debug(sprintf('Set tags to %s', implode(',', $tags))); $this->repository->updateTags($journal, $tags); } if ('do_append' === $action) { @@ -186,7 +185,7 @@ class BulkController extends Controller if (true === $ignoreUpdate) { return false; } - Log::debug(sprintf('Set budget to %s', $category)); + app('log')->debug(sprintf('Set budget to %s', $category)); $this->repository->updateCategory($journal, $category); return true; diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index ac5b9e6cab..55a062addb 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -33,7 +33,6 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Services\Internal\Update\JournalUpdateService; use FireflyIII\Support\Http\Controllers\ModelInformation; use FireflyIII\Transformers\TransactionGroupTransformer; @@ -42,7 +41,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; /** @@ -55,7 +53,6 @@ class ConvertController extends Controller use ModelInformation; private AccountRepositoryInterface $accountRepository; - private JournalRepositoryInterface $repository; /** * ConvertController constructor. @@ -69,7 +66,6 @@ class ConvertController extends Controller // some useful repositories: $this->middleware( function ($request, $next) { - $this->repository = app(JournalRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class); app('view')->share('title', (string)trans('firefly.transactions')); app('view')->share('mainTitleIcon', 'fa-exchange'); @@ -119,7 +115,7 @@ class ConvertController extends Controller ]; if ($sourceType->type === $destinationType->type) { // cannot convert to its own type. - Log::debug('This is already a transaction of the expected type..'); + app('log')->debug('This is already a transaction of the expected type..'); session()->flash('info', (string)trans('firefly.convert_is_already_type_' . $destinationType->type)); return redirect(route('transactions.show', [$group->id])); diff --git a/app/Http/Controllers/Transaction/DeleteController.php b/app/Http/Controllers/Transaction/DeleteController.php index 8e2acc65bb..9c039827d7 100644 --- a/app/Http/Controllers/Transaction/DeleteController.php +++ b/app/Http/Controllers/Transaction/DeleteController.php @@ -34,7 +34,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -79,7 +78,7 @@ class DeleteController extends Controller return $this->redirectGroupToAccount($group); } - Log::debug(sprintf('Start of delete view for group #%d', $group->id)); + app('log')->debug(sprintf('Start of delete view for group #%d', $group->id)); $journal = $group->transactionJournals->first(); if (null === $journal) { @@ -89,7 +88,7 @@ class DeleteController extends Controller $subTitle = (string)trans('firefly.delete_' . $objectType, ['description' => $group->title ?? $journal->description]); $previous = app('steam')->getSafePreviousUrl(); // put previous url in session - Log::debug('Will try to remember previous URL'); + app('log')->debug('Will try to remember previous URL'); $this->rememberPreviousUrl('transactions.delete.url'); return view('transactions.delete', compact('group', 'journal', 'subTitle', 'objectType', 'previous')); @@ -100,11 +99,11 @@ class DeleteController extends Controller * * @param TransactionGroup $group * - * @return RedirectResponse + * @return RedirectResponse|Redirector */ - public function destroy(TransactionGroup $group): RedirectResponse + public function destroy(TransactionGroup $group): RedirectResponse | Redirector { - Log::debug(sprintf('Now in %s(#%d).', __METHOD__, $group->id)); + app('log')->debug(sprintf('Now in %s(#%d).', __METHOD__, $group->id)); if (!$this->isEditableGroup($group)) { return $this->redirectGroupToAccount($group); } @@ -118,10 +117,10 @@ class DeleteController extends Controller // grab asset account(s) from group: $accounts = []; - /** @var TransactionJournal $journal */ - foreach ($group->transactionJournals as $journal) { + /** @var TransactionJournal $currentJournal */ + foreach ($group->transactionJournals as $currentJournal) { /** @var Transaction $transaction */ - foreach ($journal->transactions as $transaction) { + foreach ($currentJournal->transactions as $transaction) { $type = $transaction->account->accountType->type; // if is valid liability, trigger event! if (in_array($type, config('firefly.valid_liabilities'), true)) { @@ -134,7 +133,7 @@ class DeleteController extends Controller /** @var Account $account */ foreach ($accounts as $account) { - Log::debug(sprintf('Now going to trigger updated account event for account #%d', $account->id)); + app('log')->debug(sprintf('Now going to trigger updated account event for account #%d', $account->id)); event(new UpdatedAccount($account)); } app('preferences')->mark(); diff --git a/app/Http/Controllers/Transaction/IndexController.php b/app/Http/Controllers/Transaction/IndexController.php index 74636f6fb2..895e2f5b93 100644 --- a/app/Http/Controllers/Transaction/IndexController.php +++ b/app/Http/Controllers/Transaction/IndexController.php @@ -96,7 +96,7 @@ class IndexController extends Controller if (null === $end) { // get last transaction ever? $last = $this->repository->getLast(); - $end = $last ? $last->date : session('end'); + $end = null !== $last ? $last->date : session('end'); } [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; @@ -147,7 +147,7 @@ class IndexController extends Controller $first = $this->repository->firstNull(); $start = null === $first ? new Carbon() : $first->date; $last = $this->repository->getLast(); - $end = $last ? $last->date : today(config('app.timezone')); + $end = null !== $last ? $last->date : today(config('app.timezone')); $subTitle = (string)trans('firefly.all_' . $objectType); /** @var GroupCollectorInterface $collector */ diff --git a/app/Http/Controllers/Transaction/LinkController.php b/app/Http/Controllers/Transaction/LinkController.php index f111b4460d..d3ed4e2f29 100644 --- a/app/Http/Controllers/Transaction/LinkController.php +++ b/app/Http/Controllers/Transaction/LinkController.php @@ -33,7 +33,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; use Illuminate\View\View; /** @@ -123,7 +122,7 @@ class LinkController extends Controller { $linkInfo = $request->getLinkInfo(); - Log::debug('We are here (store)'); + app('log')->debug('We are here (store)'); $other = $this->journalRepository->find($linkInfo['transaction_journal_id']); if (null === $other) { session()->flash('error', (string)trans('firefly.invalid_link_selection')); @@ -144,7 +143,7 @@ class LinkController extends Controller return redirect(route('transactions.show', [$journal->transaction_group_id])); } - Log::debug(sprintf('Journal is %d, opposing is %d', $journal->id, $other->id)); + app('log')->debug(sprintf('Journal is %d, opposing is %d', $journal->id, $other->id)); $this->repository->storeLink($linkInfo, $other, $journal); session()->flash('success', (string)trans('firefly.journals_linked')); diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index c1837268a2..30411a6521 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -98,27 +98,27 @@ class MassController extends Controller */ public function destroy(MassDeleteJournalRequest $request) { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $ids = $request->get('confirm_mass_delete'); $count = 0; if (is_array($ids)) { - Log::debug('Array of IDs', $ids); + app('log')->debug('Array of IDs', $ids); /** @var string $journalId */ foreach ($ids as $journalId) { - Log::debug(sprintf('Searching for ID #%d', $journalId)); - /** @var TransactionJournal $journal */ + app('log')->debug(sprintf('Searching for ID #%d', $journalId)); + /** @var TransactionJournal|null $journal */ $journal = $this->repository->find((int)$journalId); - if (null !== $journal && (int)$journalId === (int)$journal->id) { + if (null !== $journal && (int)$journalId === $journal->id) { $this->repository->destroyJournal($journal); ++$count; - Log::debug(sprintf('Deleted transaction journal #%d', $journalId)); + app('log')->debug(sprintf('Deleted transaction journal #%d', $journalId)); continue; } - Log::debug(sprintf('Could not find transaction journal #%d', $journalId)); + app('log')->debug(sprintf('Could not find transaction journal #%d', $journalId)); } } app('preferences')->mark(); - session()->flash('success', (string)trans_choice('firefly.mass_deleted_transactions_success', $count)); + session()->flash('success', trans_choice('firefly.mass_deleted_transactions_success', $count)); // redirect to previous URL: return redirect($this->getPreviousUrl('transactions.mass-delete.url')); @@ -190,7 +190,7 @@ class MassController extends Controller } app('preferences')->mark(); - session()->flash('success', (string)trans_choice('firefly.mass_edited_transactions_success', $count)); + session()->flash('success', trans_choice('firefly.mass_edited_transactions_success', $count)); // redirect to previous URL: return redirect($this->getPreviousUrl('transactions.mass-edit.url')); @@ -224,7 +224,7 @@ class MassController extends Controller 'amount' => $this->getStringFromRequest($request, $journal->id, 'amount'), 'foreign_amount' => $this->getStringFromRequest($request, $journal->id, 'foreign_amount'), ]; - Log::debug(sprintf('Will update journal #%d with data.', $journal->id), $data); + app('log')->debug(sprintf('Will update journal #%d with data.', $journal->id), $data); // call service to update. $service->setData($data); @@ -252,7 +252,8 @@ class MassController extends Controller try { $carbon = Carbon::parse($value[$journalId]); } catch (InvalidArgumentException $e) { - $e->getMessage(); + Log::warning(sprintf('Could not parse "%s" but dont mind', $value[$journalId])); + Log::warning($e->getMessage()); return null; } diff --git a/app/Http/Controllers/Transaction/ShowController.php b/app/Http/Controllers/Transaction/ShowController.php index 870d871347..cf67431732 100644 --- a/app/Http/Controllers/Transaction/ShowController.php +++ b/app/Http/Controllers/Transaction/ShowController.php @@ -32,7 +32,6 @@ use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface use FireflyIII\Transformers\TransactionGroupTransformer; use Illuminate\Contracts\View\Factory; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\View\View; use Symfony\Component\HttpFoundation\ParameterBag; @@ -76,15 +75,14 @@ class ShowController extends Controller } /** - * @param Request $request * @param TransactionGroup $transactionGroup * * @return Factory|View * @throws FireflyException */ - public function show(Request $request, TransactionGroup $transactionGroup) + public function show(TransactionGroup $transactionGroup) { - /** @var TransactionJournal $first */ + /** @var TransactionJournal|null $first */ $first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']); $splits = $transactionGroup->transactionJournals()->count(); @@ -105,7 +103,7 @@ class ShowController extends Controller $amounts = $this->getAmounts($groupArray); $accounts = $this->getAccounts($groupArray); - foreach ($groupArray['transactions'] as $index => $transaction) { + foreach (array_keys($groupArray['transactions']) as $index) { $groupArray['transactions'][$index]['tags'] = $this->repository->getTagObjects( $groupArray['transactions'][$index]['transaction_journal_id'] ); @@ -192,7 +190,10 @@ class ShowController extends Controller */ private function getAccounts(array $group): array { - $accounts = []; + $accounts = [ + 'source' => [], + 'destination' => [], + ]; foreach ($group['transactions'] as $transaction) { $accounts['source'][] = [ diff --git a/app/Http/Controllers/TransactionCurrency/CreateController.php b/app/Http/Controllers/TransactionCurrency/CreateController.php index c3be3b01f2..7f1967ec8d 100644 --- a/app/Http/Controllers/TransactionCurrency/CreateController.php +++ b/app/Http/Controllers/TransactionCurrency/CreateController.php @@ -111,7 +111,7 @@ class CreateController extends Controller $user = auth()->user(); $data = $request->getCurrencyData(); if (!$this->userRepository->hasRole($user, 'owner')) { - Log::error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.'); + app('log')->error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.'); Log::channel('audit')->info('Tried to create (POST) currency without admin rights.', $data); return redirect($this->getPreviousUrl('currencies.create.url'))->withInput(); @@ -121,7 +121,7 @@ class CreateController extends Controller try { $currency = $this->repository->store($data); } catch (FireflyException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); Log::channel('audit')->info('Could not store (POST) currency without admin rights.', $data); $request->session()->flash('error', (string)trans('firefly.could_not_store_currency')); $currency = null; diff --git a/app/Http/Controllers/TransactionCurrency/IndexController.php b/app/Http/Controllers/TransactionCurrency/IndexController.php index d3e00e8952..1aa032d016 100644 --- a/app/Http/Controllers/TransactionCurrency/IndexController.php +++ b/app/Http/Controllers/TransactionCurrency/IndexController.php @@ -84,7 +84,7 @@ class IndexController extends Controller // order so default is on top: $collection = $collection->sortBy( - function (TransactionCurrency $currency) { + static function (TransactionCurrency $currency) { $default = true === $currency->userDefault ? 0 : 1; $enabled = true === $currency->userEnabled ? 0 : 1; return sprintf('%s-%s-%s', $default, $enabled, $currency->code); diff --git a/app/Http/Controllers/Webhooks/CreateController.php b/app/Http/Controllers/Webhooks/CreateController.php index 4bf123ef0b..bae29cbffb 100644 --- a/app/Http/Controllers/Webhooks/CreateController.php +++ b/app/Http/Controllers/Webhooks/CreateController.php @@ -42,7 +42,7 @@ class CreateController extends Controller // translations: $this->middleware( - function ($request, $next) { + static function ($request, $next) { app('view')->share('mainTitleIcon', 'fa-bolt'); app('view')->share('subTitleIcon', 'fa-plus'); app('view')->share('title', (string)trans('firefly.webhooks')); diff --git a/app/Http/Controllers/Webhooks/DeleteController.php b/app/Http/Controllers/Webhooks/DeleteController.php index 7d0cd15ae7..4ad1173cfd 100644 --- a/app/Http/Controllers/Webhooks/DeleteController.php +++ b/app/Http/Controllers/Webhooks/DeleteController.php @@ -45,7 +45,7 @@ class DeleteController extends Controller // translations: $this->middleware( - function ($request, $next) { + static function ($request, $next) { app('view')->share('mainTitleIcon', 'fa-bolt'); app('view')->share('subTitleIcon', 'fa-trash'); app('view')->share('title', (string)trans('firefly.webhooks')); diff --git a/app/Http/Controllers/Webhooks/EditController.php b/app/Http/Controllers/Webhooks/EditController.php index 01a028334c..12c0e9816d 100644 --- a/app/Http/Controllers/Webhooks/EditController.php +++ b/app/Http/Controllers/Webhooks/EditController.php @@ -45,7 +45,7 @@ class EditController extends Controller // translations: $this->middleware( - function ($request, $next) { + static function ($request, $next) { app('view')->share('mainTitleIcon', 'fa-bolt'); app('view')->share('subTitleIcon', 'fa-pencil'); app('view')->share('title', (string)trans('firefly.webhooks')); diff --git a/app/Http/Controllers/Webhooks/IndexController.php b/app/Http/Controllers/Webhooks/IndexController.php index fb5067aa21..886828e891 100644 --- a/app/Http/Controllers/Webhooks/IndexController.php +++ b/app/Http/Controllers/Webhooks/IndexController.php @@ -26,7 +26,6 @@ namespace FireflyIII\Http\Controllers\Webhooks; use FireflyIII\Http\Controllers\Controller; use Illuminate\Contracts\View\Factory; -use Illuminate\Http\Request; use Illuminate\View\View; /** @@ -43,7 +42,7 @@ class IndexController extends Controller // translations: $this->middleware( - function ($request, $next) { + static function ($request, $next) { app('view')->share('mainTitleIcon', 'fa-bolt'); app('view')->share('title', (string)trans('firefly.webhooks')); @@ -53,13 +52,9 @@ class IndexController extends Controller } /** - * Show debug info. - * - * @param Request $request - * * @return Factory|View */ - public function index(Request $request) + public function index() { return view('webhooks.index'); } diff --git a/app/Http/Controllers/Webhooks/ShowController.php b/app/Http/Controllers/Webhooks/ShowController.php index 233523f96f..f6fe12c778 100644 --- a/app/Http/Controllers/Webhooks/ShowController.php +++ b/app/Http/Controllers/Webhooks/ShowController.php @@ -45,7 +45,7 @@ class ShowController extends Controller // translations: $this->middleware( - function ($request, $next) { + static function ($request, $next) { app('view')->share('mainTitleIcon', 'fa-bolt'); app('view')->share('subTitleIcon', 'fa-bolt'); app('view')->share('title', (string)trans('firefly.webhooks')); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 83e302cd3f..f1ff165468 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -59,11 +59,6 @@ use PragmaRX\Google2FALaravel\Middleware as MFAMiddleware; */ class Kernel extends HttpKernel { - /** - * The application's global HTTP middleware stack. - * - * @var array - */ protected $middleware = [ SecureHeaders::class, @@ -74,13 +69,6 @@ class Kernel extends HttpKernel TrustProxies::class, InstallationId::class, ]; - /** - * The application's route middleware. - * - * These middleware may be assigned to groups or used individually. - * - * @var array - */ protected $middlewareAliases = [ 'auth' => Authenticate::class, @@ -90,11 +78,6 @@ class Kernel extends HttpKernel 'guest' => RedirectIfAuthenticated::class, 'throttle' => ThrottleRequests::class, ]; - /** - * The application's route middleware groups. - * - * @var array - */ protected $middlewareGroups = [ // does not check login @@ -206,13 +189,6 @@ class Kernel extends HttpKernel 'bindings', ], ]; - /** - * The priority-sorted list of middleware. - * - * This forces non-global middleware to always be in the given order. - * - * @var array - */ protected $middlewarePriority = [ StartFireflySession::class, diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 476ceabe80..388a566dda 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -30,7 +30,6 @@ use FireflyIII\User; use Illuminate\Auth\AuthenticationException; use Illuminate\Contracts\Auth\Factory as Auth; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; /** * Class Authenticate @@ -84,6 +83,7 @@ class Authenticate * @return mixed * @throws FireflyException * @throws AuthenticationException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function authenticate($request, array $guards) { @@ -128,16 +128,16 @@ class Authenticate private function validateBlockedUser(?User $user, array $guards): void { if (null === $user) { - Log::warning('User is null, throw exception?'); + app('log')->warning('User is null, throw exception?'); } if (null !== $user) { - // Log::debug(get_class($user)); + // app('log')->debug(get_class($user)); if (1 === (int)$user->blocked) { $message = (string)trans('firefly.block_account_logout'); if ('email_changed' === $user->blocked_code) { $message = (string)trans('firefly.email_changed_logout'); } - Log::warning('User is blocked, cannot use authentication method.'); + app('log')->warning('User is blocked, cannot use authentication method.'); app('session')->flash('logoutMessage', $message); /** @noinspection PhpUndefinedMethodInspection */ $this->auth->logout(); // @phpstan-ignore-line (thinks function is undefined) diff --git a/app/Http/Middleware/Installer.php b/app/Http/Middleware/Installer.php index feb97cad43..9e1920a891 100644 --- a/app/Http/Middleware/Installer.php +++ b/app/Http/Middleware/Installer.php @@ -30,7 +30,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\System\OAuthKeys; use Illuminate\Database\QueryException; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; /** * Class Installer @@ -52,7 +51,7 @@ class Installer */ public function handle($request, Closure $next) { - //Log::debug(sprintf('Installer middleware for URL %s', $request->url())); + //app('log')->debug(sprintf('Installer middleware for URL %s', $request->url())); // ignore installer in test environment. if ('testing' === config('app.env')) { return $next($request); @@ -61,7 +60,7 @@ class Installer $url = $request->url(); $strpos = stripos($url, '/install'); if (false !== $strpos) { - //Log::debug(sprintf('URL is %s, will NOT run installer middleware', $url)); + //app('log')->debug(sprintf('URL is %s, will NOT run installer middleware', $url)); return $next($request); } @@ -86,13 +85,13 @@ class Installer */ private function hasNoTables(): bool { - //Log::debug('Now in routine hasNoTables()'); + //app('log')->debug('Now in routine hasNoTables()'); try { DB::table('users')->count(); } catch (QueryException $e) { $message = $e->getMessage(); - Log::error(sprintf('Error message trying to access users-table: %s', $message)); + app('log')->error(sprintf('Error message trying to access users-table: %s', $message)); if ($this->isAccessDenied($message)) { throw new FireflyException( 'It seems your database configuration is not correct. Please verify the username and password in your .env file.', @@ -109,7 +108,7 @@ class Installer throw new FireflyException(sprintf('Could not access the database: %s', $message), 0, $e); } - //Log::debug('Everything seems OK with the tables.'); + //app('log')->debug('Everything seems OK with the tables.'); return false; } @@ -160,7 +159,7 @@ class Installer return true; } - //Log::info(sprintf('Configured DB version (%d) equals expected DB version (%d)', $dbVersion, $configVersion)); + //app('log')->info(sprintf('Configured DB version (%d) equals expected DB version (%d)', $dbVersion, $configVersion)); return false; } @@ -187,7 +186,7 @@ class Installer return true; } - //Log::info(sprintf('Installed Firefly III version (%s) equals expected Firefly III version (%s)', $dbVersion, $configVersion)); + //app('log')->info(sprintf('Installed Firefly III version (%s) equals expected Firefly III version (%s)', $dbVersion, $configVersion)); return false; } diff --git a/app/Http/Middleware/InterestingMessage.php b/app/Http/Middleware/InterestingMessage.php index dca641ef2e..60f139e566 100644 --- a/app/Http/Middleware/InterestingMessage.php +++ b/app/Http/Middleware/InterestingMessage.php @@ -31,7 +31,6 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\Webhook; use Illuminate\Http\Request; -use Preferences; /** * Class InterestingMessage @@ -54,23 +53,23 @@ class InterestingMessage } if ($this->groupMessage($request)) { - Preferences::mark(); + app('preferences')->mark(); $this->handleGroupMessage($request); } if ($this->accountMessage($request)) { - Preferences::mark(); + app('preferences')->mark(); $this->handleAccountMessage($request); } if ($this->billMessage($request)) { - Preferences::mark(); + app('preferences')->mark(); $this->handleBillMessage($request); } if ($this->webhookMessage($request)) { - Preferences::mark(); + app('preferences')->mark(); $this->handleWebhookMessage($request); } if ($this->currencyMessage($request)) { - Preferences::mark(); + app('preferences')->mark(); $this->handleCurrencyMessage($request); } @@ -110,7 +109,7 @@ class InterestingMessage $message = $request->get('message'); // send message about newly created transaction group. - /** @var TransactionGroup $group */ + /** @var TransactionGroup|null $group */ $group = auth()->user()->transactionGroups()->with(['transactionJournals', 'transactionJournals.transactionType'])->find((int)$transactionGroupId); if (null === $group) { @@ -119,7 +118,7 @@ class InterestingMessage $count = $group->transactionJournals->count(); - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $journal */ $journal = $group->transactionJournals->first(); if (null === $journal) { return; @@ -164,7 +163,7 @@ class InterestingMessage $accountId = $request->get('account_id'); $message = $request->get('message'); - /** @var Account $account */ + /** @var Account|null $account */ $account = auth()->user()->accounts()->withTrashed()->find($accountId); if (null === $account) { @@ -204,7 +203,7 @@ class InterestingMessage $billId = $request->get('bill_id'); $message = $request->get('message'); - /** @var Bill $bill */ + /** @var Bill|null $bill */ $bill = auth()->user()->bills()->withTrashed()->find($billId); if (null === $bill) { @@ -241,7 +240,7 @@ class InterestingMessage $webhookId = $request->get('webhook_id'); $message = $request->get('message'); - /** @var Webhook $webhook */ + /** @var Webhook|null $webhook */ $webhook = auth()->user()->webhooks()->withTrashed()->find($webhookId); if (null === $webhook) { @@ -272,6 +271,11 @@ class InterestingMessage return null !== $code && null !== $message; } + /** + * @param Request $request + * + * @return void + */ private function handleCurrencyMessage(Request $request): void { // params: @@ -279,7 +283,7 @@ class InterestingMessage $code = $request->get('code'); $message = $request->get('message'); - /** @var TransactionCurrency $webhook */ + /** @var TransactionCurrency|null $currency */ $currency = TransactionCurrency::whereCode($code)->first(); if (null === $currency) { diff --git a/app/Http/Middleware/IsDemoUser.php b/app/Http/Middleware/IsDemoUser.php index e174c8d95e..e5ff35a34e 100644 --- a/app/Http/Middleware/IsDemoUser.php +++ b/app/Http/Middleware/IsDemoUser.php @@ -27,7 +27,6 @@ use Closure; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; /** * Class IsDemoUser. @@ -44,7 +43,7 @@ class IsDemoUser */ public function handle(Request $request, Closure $next) { - /** @var User $user */ + /** @var User|null $user */ $user = $request->user(); if (null === $user) { return $next($request); @@ -53,7 +52,7 @@ class IsDemoUser /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); if ($repository->hasRole($user, 'demo')) { - Log::info('User is a demo user.'); + app('log')->info('User is a demo user.'); $request->session()->flash('info', (string)trans('firefly.not_available_demo_user')); $current = $request->url(); $previous = $request->session()->previousUrl(); diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 997dcd218a..78d56c5a99 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -29,7 +29,6 @@ use Closure; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Http\Controllers\RequestInformation; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; /** * Class SessionFilter. @@ -48,7 +47,7 @@ class Range */ public function handle(Request $request, Closure $next) { - if ($request->user()) { + if (null !== $request->user()) { // set start, end and finish: $this->setRange(); @@ -70,9 +69,13 @@ class Range // ignore preference. set the range to be the current month: if (!app('session')->has('start') && !app('session')->has('end')) { $viewRange = app('preferences')->get('viewRange', '1M')->data; - $today = today(config('app.timezone')); - $start = app('navigation')->updateStartDate($viewRange, $today); - $end = app('navigation')->updateEndDate($viewRange, $start); + if (is_array($viewRange)) { + $viewRange = '1M'; + } + + $today = today(config('app.timezone')); + $start = app('navigation')->updateStartDate((string)$viewRange, $today); + $end = app('navigation')->updateEndDate((string)$viewRange, $start); app('session')->put('start', $start); app('session')->put('end', $end); @@ -108,7 +111,7 @@ class Range // send error to view, if could not set money format if (false === $moneyResult) { - Log::error('Could not set locale. The following array doesnt work: ', $localeArray); + app('log')->error('Could not set locale. The following array doesnt work: ', $localeArray); app('view')->share('invalidMonetaryLocale', true); } diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php index 049725b850..4903cf6d1b 100644 --- a/app/Http/Middleware/TrimStrings.php +++ b/app/Http/Middleware/TrimStrings.php @@ -32,11 +32,6 @@ use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware; */ class TrimStrings extends Middleware { - /** - * The names of the attributes that should not be trimmed. - * - * @var array - */ protected $except = [ 'password', diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index 9301910560..4796680562 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -37,9 +37,9 @@ use Illuminate\Foundation\Http\FormRequest; */ class AccountFormRequest extends FormRequest { - use ConvertsDataTypes; use AppendsLocationData; use ChecksLogin; + use ConvertsDataTypes; protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS]; @@ -122,7 +122,7 @@ class AccountFormRequest extends FormRequest ]; $rules = Location::requestRules($rules); - /** @var Account $account */ + /** @var Account|null $account */ $account = $this->route()->parameter('account'); if (null !== $account) { // add rules: diff --git a/app/Http/Requests/AttachmentFormRequest.php b/app/Http/Requests/AttachmentFormRequest.php index c09915b069..3df4d8da60 100644 --- a/app/Http/Requests/AttachmentFormRequest.php +++ b/app/Http/Requests/AttachmentFormRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class AttachmentFormRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. diff --git a/app/Http/Requests/BillStoreRequest.php b/app/Http/Requests/BillStoreRequest.php index 8b2ef19b54..1061e7d0d5 100644 --- a/app/Http/Requests/BillStoreRequest.php +++ b/app/Http/Requests/BillStoreRequest.php @@ -32,8 +32,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class BillStoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. @@ -74,7 +74,7 @@ class BillStoreRequest extends FormRequest 'date' => 'required|date', 'bill_end_date' => 'nullable|date', 'extension_date' => 'nullable|date', - 'repeat_freq' => sprintf('required|in:%s', join(',', config('firefly.bill_periods'))), + 'repeat_freq' => sprintf('required|in:%s', implode(',', config('firefly.bill_periods'))), 'skip' => 'required|integer|gte:0|lte:31', 'active' => 'boolean', ]; diff --git a/app/Http/Requests/BillUpdateRequest.php b/app/Http/Requests/BillUpdateRequest.php index 89ffa5b0a7..7d84ef2c2c 100644 --- a/app/Http/Requests/BillUpdateRequest.php +++ b/app/Http/Requests/BillUpdateRequest.php @@ -33,8 +33,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class BillUpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. @@ -78,7 +78,7 @@ class BillUpdateRequest extends FormRequest 'date' => 'required|date', 'bill_end_date' => 'nullable|date', 'extension_date' => 'nullable|date', - 'repeat_freq' => sprintf('required|in:%s', join(',', config('firefly.bill_periods'))), + 'repeat_freq' => sprintf('required|in:%s', implode(',', config('firefly.bill_periods'))), 'skip' => 'required|integer|gte:0|lte:31', 'active' => 'boolean', 'notes' => 'between:1,65536|nullable', diff --git a/app/Http/Requests/BudgetFormStoreRequest.php b/app/Http/Requests/BudgetFormStoreRequest.php index 3165ee7e78..1cd04340e5 100644 --- a/app/Http/Requests/BudgetFormStoreRequest.php +++ b/app/Http/Requests/BudgetFormStoreRequest.php @@ -34,9 +34,9 @@ use Illuminate\Validation\Validator; */ class BudgetFormStoreRequest extends FormRequest { + use ChecksLogin; use ConvertsDataTypes; use ValidatesAutoBudgetRequest; - use ChecksLogin; /** * Returns the data required by the controller. diff --git a/app/Http/Requests/BudgetFormUpdateRequest.php b/app/Http/Requests/BudgetFormUpdateRequest.php index ab61f774f5..4cf7c48ce4 100644 --- a/app/Http/Requests/BudgetFormUpdateRequest.php +++ b/app/Http/Requests/BudgetFormUpdateRequest.php @@ -35,9 +35,9 @@ use Illuminate\Validation\Validator; */ class BudgetFormUpdateRequest extends FormRequest { + use ChecksLogin; use ConvertsDataTypes; use ValidatesAutoBudgetRequest; - use ChecksLogin; /** * Returns the data required by the controller. @@ -65,7 +65,7 @@ class BudgetFormUpdateRequest extends FormRequest { $nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name'; - /** @var Budget $budget */ + /** @var Budget|null $budget */ $budget = $this->route()->parameter('budget'); if (null !== $budget) { diff --git a/app/Http/Requests/BulkEditJournalRequest.php b/app/Http/Requests/BulkEditJournalRequest.php index 1f8c04074e..1598e22a79 100644 --- a/app/Http/Requests/BulkEditJournalRequest.php +++ b/app/Http/Requests/BulkEditJournalRequest.php @@ -32,8 +32,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class BulkEditJournalRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Rules for this request. diff --git a/app/Http/Requests/CategoryFormRequest.php b/app/Http/Requests/CategoryFormRequest.php index 0e3c309d36..9be9ad514c 100644 --- a/app/Http/Requests/CategoryFormRequest.php +++ b/app/Http/Requests/CategoryFormRequest.php @@ -33,8 +33,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class CategoryFormRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get information for the controller. @@ -57,7 +57,7 @@ class CategoryFormRequest extends FormRequest public function rules(): array { $nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name'; - /** @var Category $category */ + /** @var Category|null $category */ $category = $this->route()->parameter('category'); if (null !== $category) { diff --git a/app/Http/Requests/CurrencyFormRequest.php b/app/Http/Requests/CurrencyFormRequest.php index b0048f8496..2b5c3a1eaf 100644 --- a/app/Http/Requests/CurrencyFormRequest.php +++ b/app/Http/Requests/CurrencyFormRequest.php @@ -33,8 +33,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class CurrencyFormRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. @@ -68,7 +68,7 @@ class CurrencyFormRequest extends FormRequest 'enabled' => 'in:0,1', ]; - /** @var TransactionCurrency $currency */ + /** @var TransactionCurrency|null $currency */ $currency = $this->route()->parameter('currency'); if (null !== $currency) { diff --git a/app/Http/Requests/InviteUserFormRequest.php b/app/Http/Requests/InviteUserFormRequest.php index d751e26f54..23cdfe9300 100644 --- a/app/Http/Requests/InviteUserFormRequest.php +++ b/app/Http/Requests/InviteUserFormRequest.php @@ -33,8 +33,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class InviteUserFormRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Rules for this request. diff --git a/app/Http/Requests/JournalLinkRequest.php b/app/Http/Requests/JournalLinkRequest.php index 7eb14cbee6..68f2f53d42 100644 --- a/app/Http/Requests/JournalLinkRequest.php +++ b/app/Http/Requests/JournalLinkRequest.php @@ -33,8 +33,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class JournalLinkRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. diff --git a/app/Http/Requests/NewUserFormRequest.php b/app/Http/Requests/NewUserFormRequest.php index a09ba41480..5a3f4b4380 100644 --- a/app/Http/Requests/NewUserFormRequest.php +++ b/app/Http/Requests/NewUserFormRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class NewUserFormRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Rules for this request. diff --git a/app/Http/Requests/ObjectGroupFormRequest.php b/app/Http/Requests/ObjectGroupFormRequest.php index cabec0d41d..cf77969ebc 100644 --- a/app/Http/Requests/ObjectGroupFormRequest.php +++ b/app/Http/Requests/ObjectGroupFormRequest.php @@ -33,8 +33,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class ObjectGroupFormRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. @@ -55,7 +55,7 @@ class ObjectGroupFormRequest extends FormRequest */ public function rules(): array { - /** @var ObjectGroup $objectGroup */ + /** @var ObjectGroup|null $objectGroup */ $objectGroup = $this->route()->parameter('objectGroup'); $titleRule = 'required|between:1,255|uniqueObjectGroup'; diff --git a/app/Http/Requests/PiggyBankStoreRequest.php b/app/Http/Requests/PiggyBankStoreRequest.php index b4baa67c46..9cb20e0467 100644 --- a/app/Http/Requests/PiggyBankStoreRequest.php +++ b/app/Http/Requests/PiggyBankStoreRequest.php @@ -32,8 +32,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class PiggyBankStoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. diff --git a/app/Http/Requests/PiggyBankUpdateRequest.php b/app/Http/Requests/PiggyBankUpdateRequest.php index 4f7558689c..34389570e4 100644 --- a/app/Http/Requests/PiggyBankUpdateRequest.php +++ b/app/Http/Requests/PiggyBankUpdateRequest.php @@ -33,8 +33,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class PiggyBankUpdateRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. diff --git a/app/Http/Requests/ReconciliationStoreRequest.php b/app/Http/Requests/ReconciliationStoreRequest.php index c9a1613f20..ac7215592e 100644 --- a/app/Http/Requests/ReconciliationStoreRequest.php +++ b/app/Http/Requests/ReconciliationStoreRequest.php @@ -27,15 +27,14 @@ use FireflyIII\Rules\ValidJournals; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Support\Facades\Log; /** * Class ReconciliationStoreRequest */ class ReconciliationStoreRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. @@ -57,7 +56,7 @@ class ReconciliationStoreRequest extends FormRequest 'journals' => $transactions, 'reconcile' => $this->convertString('reconcile'), ]; - Log::debug('In ReconciliationStoreRequest::getAll(). Will now return data.'); + app('log')->debug('In ReconciliationStoreRequest::getAll(). Will now return data.'); return $data; } diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index dce42861b4..f8334b0c84 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -33,7 +33,6 @@ use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\AccountValidator; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -41,8 +40,8 @@ use Illuminate\Validation\Validator; */ class RecurrenceFormRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get the data required by the controller. @@ -260,7 +259,7 @@ class RecurrenceFormRequest extends FormRequest } // update some rules in case the user is editing a post: - /** @var Recurrence $recurrence */ + /** @var Recurrence|null $recurrence */ $recurrence = $this->route()->parameter('recurrence'); if ($recurrence instanceof Recurrence) { $rules['id'] = 'required|numeric|exists:recurrences,id'; @@ -297,7 +296,7 @@ class RecurrenceFormRequest extends FormRequest */ public function validateAccountInformation(Validator $validator): void { - Log::debug('Now in validateAccountInformation (RecurrenceFormRequest)()'); + app('log')->debug('Now in validateAccountInformation (RecurrenceFormRequest)()'); /** @var AccountValidator $accountValidator */ $accountValidator = app(AccountValidator::class); $data = $validator->getData(); diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index 94e6f83f64..2620ca025f 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -33,7 +33,6 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class CategoryFormRequest. @@ -152,19 +151,19 @@ class ReportFormRequest extends FormRequest // validate as date // if regex for YYYY-MM-DD: $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][\d]|3[01])$/'; - if (preg_match($pattern, $string)) { + if (false !== preg_match($pattern, $string)) { try { $date = new Carbon($parts[1]); } catch (Exception $e) { // intentional generic exception $error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage()); - Log::error($error); - Log::error($e->getTraceAsString()); + app('log')->error($error); + app('log')->error($e->getTraceAsString()); throw new FireflyException($error, 0, $e); } return $date; } $error = sprintf('"%s" is not a valid date range: %s', $range, 'invalid format :('); - Log::error($error); + app('log')->error($error); throw new FireflyException($error, 0); } return $date; @@ -187,19 +186,19 @@ class ReportFormRequest extends FormRequest // validate as date // if regex for YYYY-MM-DD: $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][\d]|3[01])$/'; - if (preg_match($pattern, $string)) { + if (false !== preg_match($pattern, $string)) { try { $date = new Carbon($parts[0]); } catch (Exception $e) { // intentional generic exception $error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage()); - Log::error($error); - Log::error($e->getTraceAsString()); + app('log')->error($error); + app('log')->error($e->getTraceAsString()); throw new FireflyException($error, 0, $e); } return $date; } $error = sprintf('"%s" is not a valid date range: %s', $range, 'invalid format :('); - Log::error($error); + app('log')->error($error); throw new FireflyException($error, 0); } @@ -218,14 +217,14 @@ class ReportFormRequest extends FormRequest $set = $this->get('tag'); $collection = new Collection(); if (is_array($set)) { - Log::debug('Set is:', $set); + app('log')->debug('Set is:', $set); } if (!is_array($set)) { - Log::error(sprintf('Set is not an array! "%s"', $set)); + app('log')->error(sprintf('Set is not an array! "%s"', $set)); return $collection; } foreach ($set as $tagTag) { - Log::debug(sprintf('Now searching for "%s"', $tagTag)); + app('log')->debug(sprintf('Now searching for "%s"', $tagTag)); $tag = $repository->findByTag($tagTag); if (null !== $tag) { $collection->push($tag); diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index d27371e4d7..7a3fe63f29 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -34,9 +34,9 @@ use Illuminate\Foundation\Http\FormRequest; */ class RuleFormRequest extends FormRequest { + use ChecksLogin; use ConvertsDataTypes; use GetRuleConfiguration; - use ChecksLogin; /** * Get all data for controller. @@ -164,7 +164,7 @@ class RuleFormRequest extends FormRequest 'strict' => 'in:0,1', ]; - /** @var Rule $rule */ + /** @var Rule|null $rule */ $rule = $this->route()->parameter('rule'); if (null !== $rule) { diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php index 91da192e06..60fbaa41af 100644 --- a/app/Http/Requests/RuleGroupFormRequest.php +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class RuleGroupFormRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data for controller. @@ -65,7 +65,7 @@ class RuleGroupFormRequest extends FormRequest { $titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title'; - /** @var RuleGroup $ruleGroup */ + /** @var RuleGroup|null $ruleGroup */ $ruleGroup = $this->route()->parameter('ruleGroup'); if (null !== $ruleGroup) { diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index c006faaef7..e631d35232 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -35,9 +35,9 @@ use Illuminate\Foundation\Http\FormRequest; */ class TagFormRequest extends FormRequest { - use ConvertsDataTypes; use AppendsLocationData; use ChecksLogin; + use ConvertsDataTypes; /** * Get all data for controller. @@ -64,7 +64,7 @@ class TagFormRequest extends FormRequest { $idRule = ''; - /** @var Tag $tag */ + /** @var Tag|null $tag */ $tag = $this->route()->parameter('tag'); $tagRule = 'required|max:1024|min:1|uniqueObjectForUser:tags,tag'; if (null !== $tag) { diff --git a/app/Http/Requests/TestRuleFormRequest.php b/app/Http/Requests/TestRuleFormRequest.php index 39b7cc62d3..b5166f1eea 100644 --- a/app/Http/Requests/TestRuleFormRequest.php +++ b/app/Http/Requests/TestRuleFormRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class TestRuleFormRequest extends FormRequest { - use GetRuleConfiguration; use ChecksLogin; + use GetRuleConfiguration; /** * Rules for this request. diff --git a/app/Http/Requests/TriggerRecurrenceRequest.php b/app/Http/Requests/TriggerRecurrenceRequest.php index 205141f368..640c338570 100644 --- a/app/Http/Requests/TriggerRecurrenceRequest.php +++ b/app/Http/Requests/TriggerRecurrenceRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class TriggerRecurrenceRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Returns the data required by the controller. diff --git a/app/Http/Requests/UserFormRequest.php b/app/Http/Requests/UserFormRequest.php index 1305bda1fc..4aaa03d224 100644 --- a/app/Http/Requests/UserFormRequest.php +++ b/app/Http/Requests/UserFormRequest.php @@ -34,8 +34,8 @@ use Illuminate\Foundation\Http\FormRequest; */ class UserFormRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; + use ConvertsDataTypes; /** * Get data for controller. diff --git a/app/Jobs/CreateAutoBudgetLimits.php b/app/Jobs/CreateAutoBudgetLimits.php index 9c86241ea2..ca62f4d3d2 100644 --- a/app/Jobs/CreateAutoBudgetLimits.php +++ b/app/Jobs/CreateAutoBudgetLimits.php @@ -36,7 +36,6 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class CreateAutoBudgetLimits @@ -62,7 +61,7 @@ class CreateAutoBudgetLimits implements ShouldQueue $newDate = clone $date; $newDate->startOfDay(); $this->date = $newDate; - Log::debug(sprintf('Created new CreateAutoBudgetLimits("%s")', $this->date->format('Y-m-d'))); + app('log')->debug(sprintf('Created new CreateAutoBudgetLimits("%s")', $this->date->format('Y-m-d'))); } } @@ -73,9 +72,9 @@ class CreateAutoBudgetLimits implements ShouldQueue */ public function handle(): void { - Log::debug(sprintf('Now at start of CreateAutoBudgetLimits() job for %s.', $this->date->format('D d M Y'))); + app('log')->debug(sprintf('Now at start of CreateAutoBudgetLimits() job for %s.', $this->date->format('D d M Y'))); $autoBudgets = AutoBudget::get(); - Log::debug(sprintf('Found %d auto budgets.', $autoBudgets->count())); + app('log')->debug(sprintf('Found %d auto budgets.', $autoBudgets->count())); foreach ($autoBudgets as $autoBudget) { $this->handleAutoBudget($autoBudget); } @@ -89,18 +88,18 @@ class CreateAutoBudgetLimits implements ShouldQueue private function handleAutoBudget(AutoBudget $autoBudget): void { if (null === $autoBudget->budget) { - Log::info(sprintf('Auto budget #%d is associated with a deleted budget.', $autoBudget->id)); + app('log')->info(sprintf('Auto budget #%d is associated with a deleted budget.', $autoBudget->id)); $autoBudget->delete(); return; } if (false === $autoBudget->budget->active) { - Log::info(sprintf('Auto budget #%d is associated with an inactive budget.', $autoBudget->id)); + app('log')->info(sprintf('Auto budget #%d is associated with an inactive budget.', $autoBudget->id)); return; } if (!$this->isMagicDay($autoBudget)) { - Log::info( + app('log')->info( sprintf( 'Today (%s) is not a magic day for %s auto-budget #%d (part of budget #%d "%s")', $this->date->format('Y-m-d'), @@ -110,11 +109,11 @@ class CreateAutoBudgetLimits implements ShouldQueue $autoBudget->budget->name ) ); - Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } - Log::info( + app('log')->info( sprintf( 'Today (%s) is a magic day for %s auto-budget #%d (part of budget #%d "%s")', $this->date->format('Y-m-d'), @@ -136,7 +135,7 @@ class CreateAutoBudgetLimits implements ShouldQueue // that's easy: create one. // do nothing else. $this->createBudgetLimit($autoBudget, $start, $end); - Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } @@ -144,18 +143,18 @@ class CreateAutoBudgetLimits implements ShouldQueue if (null === $budgetLimit && AutoBudget::AUTO_BUDGET_ROLLOVER === (int)$autoBudget->auto_budget_type) { // budget limit exists already, $this->createRollover($autoBudget); - Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } if (null === $budgetLimit && AutoBudget::AUTO_BUDGET_ADJUSTED === (int)$autoBudget->auto_budget_type) { // budget limit exists already, $this->createAdjustedLimit($autoBudget); - Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } - Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); } /** @@ -207,7 +206,7 @@ class CreateAutoBudgetLimits implements ShouldQueue */ private function findBudgetLimit(Budget $budget, Carbon $start, Carbon $end): ?BudgetLimit { - Log::debug( + app('log')->debug( sprintf( 'Going to find a budget limit for budget #%d ("%s") between %s and %s', $budget->id, @@ -228,11 +227,11 @@ class CreateAutoBudgetLimits implements ShouldQueue * @param Carbon $end * @param string|null $amount */ - private function createBudgetLimit(AutoBudget $autoBudget, Carbon $start, Carbon $end, ?string $amount = null) + private function createBudgetLimit(AutoBudget $autoBudget, Carbon $start, Carbon $end, ?string $amount = null): void { - Log::debug(sprintf('No budget limit exist. Must create one for auto-budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('No budget limit exist. Must create one for auto-budget #%d', $autoBudget->id)); if (null !== $amount) { - Log::debug(sprintf('Amount is overruled and will be set to %s', $amount)); + app('log')->debug(sprintf('Amount is overruled and will be set to %s', $amount)); } $budgetLimit = new BudgetLimit(); $budgetLimit->budget()->associate($autoBudget->budget); @@ -241,10 +240,10 @@ class CreateAutoBudgetLimits implements ShouldQueue $budgetLimit->end_date = $end; $budgetLimit->amount = $amount ?? $autoBudget->amount; $budgetLimit->period = $autoBudget->period; - $budgetLimit->generated = true; + $budgetLimit->generated = 1; $budgetLimit->save(); - Log::debug(sprintf('Created budget limit #%d.', $budgetLimit->id)); + app('log')->debug(sprintf('Created budget limit #%d.', $budgetLimit->id)); } /** @@ -254,7 +253,7 @@ class CreateAutoBudgetLimits implements ShouldQueue */ private function createRollover(AutoBudget $autoBudget): void { - Log::debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id)); // current period: $start = app('navigation')->startOfPeriod($this->date, $autoBudget->period); $end = app('navigation')->endOfPeriod($start, $autoBudget->period); @@ -263,7 +262,7 @@ class CreateAutoBudgetLimits implements ShouldQueue $previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period); $previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period); - Log::debug( + app('log')->debug( sprintf( 'Current period is %s-%s, so previous period is %s-%s', $start->format('Y-m-d'), @@ -277,39 +276,39 @@ class CreateAutoBudgetLimits implements ShouldQueue $budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd); if (null === $budgetLimit) { - Log::debug('No budget limit exists in previous period, so create one.'); + app('log')->debug('No budget limit exists in previous period, so create one.'); // if not, create it and we're done. $this->createBudgetLimit($autoBudget, $start, $end); - Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } - Log::debug('Budget limit exists for previous period.'); + app('log')->debug('Budget limit exists for previous period.'); // if has one, calculate expenses and use that as a base. $repository = app(OperationsRepositoryInterface::class); $repository->setUser($autoBudget->budget->user); $spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency); - $currencyId = (int)$autoBudget->transaction_currency_id; + $currencyId = $autoBudget->transaction_currency_id; $spentAmount = $spent[$currencyId]['sum'] ?? '0'; - Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount)); + app('log')->debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount)); // if you spent more in previous budget period, than whatever you had previous budget period, the amount resets // previous budget limit + spent $budgetLeft = bcadd($budgetLimit->amount, $spentAmount); $totalAmount = $autoBudget->amount; - Log::debug(sprintf('Total amount left for previous budget period is %s', $budgetLeft)); + app('log')->debug(sprintf('Total amount left for previous budget period is %s', $budgetLeft)); if (-1 !== bccomp('0', $budgetLeft)) { - Log::info(sprintf('The amount left is negative, so it will be reset to %s.', $totalAmount)); + app('log')->info(sprintf('The amount left is negative, so it will be reset to %s.', $totalAmount)); } if (1 !== bccomp('0', $budgetLeft)) { $totalAmount = bcadd($budgetLeft, $totalAmount); - Log::info(sprintf('The amount left is positive, so the new amount will be %s.', $totalAmount)); + app('log')->info(sprintf('The amount left is positive, so the new amount will be %s.', $totalAmount)); } // create budget limit: $this->createBudgetLimit($autoBudget, $start, $end, $totalAmount); - Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); } /** @@ -319,7 +318,7 @@ class CreateAutoBudgetLimits implements ShouldQueue */ private function createAdjustedLimit(AutoBudget $autoBudget): void { - Log::debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id)); // current period: $start = app('navigation')->startOfPeriod($this->date, $autoBudget->period); $end = app('navigation')->endOfPeriod($start, $autoBudget->period); @@ -328,7 +327,7 @@ class CreateAutoBudgetLimits implements ShouldQueue $previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period); $previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period); - Log::debug( + app('log')->debug( sprintf( 'Current period is %s-%s, so previous period is %s-%s', $start->format('Y-m-d'), @@ -342,45 +341,45 @@ class CreateAutoBudgetLimits implements ShouldQueue $budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd); if (null === $budgetLimit) { - Log::debug('No budget limit exists in previous period, so create one.'); + app('log')->debug('No budget limit exists in previous period, so create one.'); // if not, create standard amount, and we're done. $this->createBudgetLimit($autoBudget, $start, $end); return; } - Log::debug('Budget limit exists for previous period.'); + app('log')->debug('Budget limit exists for previous period.'); // if has one, calculate expenses and use that as a base. $repository = app(OperationsRepositoryInterface::class); $repository->setUser($autoBudget->budget->user); $spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency); - $currencyId = (int)$autoBudget->transaction_currency_id; + $currencyId = $autoBudget->transaction_currency_id; $spentAmount = $spent[$currencyId]['sum'] ?? '0'; - Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount)); + app('log')->debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount)); // what you spent in previous period PLUS the amount for the current period, // if that is more than zero, that's the amount that will be set. $budgetAvailable = bcadd(bcadd($budgetLimit->amount, $autoBudget->amount), $spentAmount); $totalAmount = $autoBudget->amount; - Log::debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable)); + app('log')->debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable)); if (-1 !== bccomp($budgetAvailable, $totalAmount)) { - Log::info(sprintf('There is no overspending, no need to adjust. Budget limit amount will be %s.', $budgetAvailable)); + app('log')->info(sprintf('There is no overspending, no need to adjust. Budget limit amount will be %s.', $budgetAvailable)); // create budget limit: $this->createBudgetLimit($autoBudget, $start, $end, $budgetAvailable); } if (1 !== bccomp($budgetAvailable, $totalAmount) && 1 === bccomp($budgetAvailable, '0')) { - Log::info(sprintf('There was overspending, so the new amount will be %s.', $budgetAvailable)); + app('log')->info(sprintf('There was overspending, so the new amount will be %s.', $budgetAvailable)); // create budget limit: $this->createBudgetLimit($autoBudget, $start, $end, $budgetAvailable); } if (1 !== bccomp($budgetAvailable, $totalAmount) && -1 === bccomp($budgetAvailable, '0')) { - Log::info('There was overspending, but so much even this period cant fix that. Reset it to 1.'); + app('log')->info('There was overspending, but so much even this period cant fix that. Reset it to 1.'); // create budget limit: $this->createBudgetLimit($autoBudget, $start, $end, '1'); } - Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); } /** diff --git a/app/Jobs/CreateRecurringTransactions.php b/app/Jobs/CreateRecurringTransactions.php index ec00f99e90..f9852517a7 100644 --- a/app/Jobs/CreateRecurringTransactions.php +++ b/app/Jobs/CreateRecurringTransactions.php @@ -42,7 +42,6 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class CreateRecurringTransactions. @@ -93,7 +92,7 @@ class CreateRecurringTransactions implements ShouldQueue $this->recurrences = new Collection(); $this->groups = new Collection(); - Log::debug(sprintf('Created new CreateRecurringTransactions("%s")', $this->date->format('Y-m-d'))); + app('log')->debug(sprintf('Created new CreateRecurringTransactions("%s")', $this->date->format('Y-m-d'))); } /** @@ -109,25 +108,25 @@ class CreateRecurringTransactions implements ShouldQueue */ public function handle(): void { - Log::debug(sprintf('Now at start of CreateRecurringTransactions() job for %s.', $this->date->format('D d M Y'))); + app('log')->debug(sprintf('Now at start of CreateRecurringTransactions() job for %s.', $this->date->format('D d M Y'))); // only use recurrences from database if there is no collection submitted. if (0 !== count($this->recurrences)) { - Log::debug('Using predetermined set of recurrences.'); + app('log')->debug('Using predetermined set of recurrences.'); } if (0 === count($this->recurrences)) { - Log::debug('Grab all recurrences from the database.'); + app('log')->debug('Grab all recurrences from the database.'); $this->recurrences = $this->repository->getAll(); } $result = []; $count = $this->recurrences->count(); $this->submitted = $count; - Log::debug(sprintf('Count of collection is %d', $count)); + app('log')->debug(sprintf('Count of collection is %d', $count)); // filter recurrences: $filtered = $this->filterRecurrences($this->recurrences); - Log::debug(sprintf('Left after filtering is %d', $filtered->count())); + app('log')->debug(sprintf('Left after filtering is %d', $filtered->count())); /** @var Recurrence $recurrence */ foreach ($filtered as $recurrence) { if (!array_key_exists($recurrence->user_id, $result)) { @@ -140,20 +139,20 @@ class CreateRecurringTransactions implements ShouldQueue // clear cache for user app('preferences')->setForUser($recurrence->user, 'lastActivity', microtime()); - Log::debug(sprintf('Now at recurrence #%d of user #%d', $recurrence->id, $recurrence->user_id)); + app('log')->debug(sprintf('Now at recurrence #%d of user #%d', $recurrence->id, $recurrence->user_id)); $createdReps = $this->handleRepetitions($recurrence); - Log::debug(sprintf('Done with recurrence #%d', $recurrence->id)); + app('log')->debug(sprintf('Done with recurrence #%d', $recurrence->id)); $result[$recurrence->user_id] = $result[$recurrence->user_id]->merge($createdReps); $this->executed++; } - Log::debug('Now running report thing.'); + app('log')->debug('Now running report thing.'); // will now send email to users. foreach ($result as $userId => $journals) { event(new RequestedReportOnJournals($userId, $journals)); } - Log::debug('Done with handle()'); + app('log')->debug('Done with handle()'); // clear cache: app('preferences')->mark(); @@ -183,10 +182,10 @@ class CreateRecurringTransactions implements ShouldQueue */ private function validRecurrence(Recurrence $recurrence): bool { - Log::debug(sprintf('Now filtering recurrence #%d, owned by user #%d', $recurrence->id, $recurrence->user_id)); + app('log')->debug(sprintf('Now filtering recurrence #%d, owned by user #%d', $recurrence->id, $recurrence->user_id)); // is not active. if (!$this->active($recurrence)) { - Log::info(sprintf('Recurrence #%d is not active. Skipped.', $recurrence->id)); + app('log')->info(sprintf('Recurrence #%d is not active. Skipped.', $recurrence->id)); return false; } @@ -194,14 +193,14 @@ class CreateRecurringTransactions implements ShouldQueue // has repeated X times. $journalCount = $this->repository->getJournalCount($recurrence); if (0 !== $recurrence->repetitions && $journalCount >= $recurrence->repetitions && false === $this->force) { - Log::info(sprintf('Recurrence #%d has run %d times, so will run no longer.', $recurrence->id, $recurrence->repetitions)); + app('log')->info(sprintf('Recurrence #%d has run %d times, so will run no longer.', $recurrence->id, $recurrence->repetitions)); return false; } // is no longer running if ($this->repeatUntilHasPassed($recurrence)) { - Log::info( + app('log')->info( sprintf( 'Recurrence #%d was set to run until %s, and today\'s date is %s. Skipped.', $recurrence->id, @@ -215,7 +214,7 @@ class CreateRecurringTransactions implements ShouldQueue // first_date is in the future if ($this->hasNotStartedYet($recurrence)) { - Log::info( + app('log')->info( sprintf( 'Recurrence #%d is set to run on %s, and today\'s date is %s. Skipped.', $recurrence->id, @@ -229,11 +228,11 @@ class CreateRecurringTransactions implements ShouldQueue // already fired today (with success): if (false === $this->force && $this->hasFiredToday($recurrence)) { - Log::info(sprintf('Recurrence #%d has already fired today. Skipped.', $recurrence->id)); + app('log')->info(sprintf('Recurrence #%d has already fired today. Skipped.', $recurrence->id)); return false; } - Log::debug('Will be included.'); + app('log')->debug('Will be included.'); return true; } @@ -273,7 +272,7 @@ class CreateRecurringTransactions implements ShouldQueue private function hasNotStartedYet(Recurrence $recurrence): bool { $startDate = $this->getStartDate($recurrence); - Log::debug(sprintf('Start date is %s', $startDate->format('Y-m-d'))); + app('log')->debug(sprintf('Start date is %s', $startDate->format('Y-m-d'))); return $startDate->gt($this->date); } @@ -322,7 +321,7 @@ class CreateRecurringTransactions implements ShouldQueue $collection = new Collection(); /** @var RecurrenceRepetition $repetition */ foreach ($recurrence->recurrenceRepetitions as $repetition) { - Log::debug( + app('log')->debug( sprintf( 'Now repeating %s with value "%s", skips every %d time(s)', $repetition->repetition_type, @@ -386,18 +385,18 @@ class CreateRecurringTransactions implements ShouldQueue if ($date->ne($this->date)) { return null; } - Log::debug(sprintf('%s IS today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d'))); + app('log')->debug(sprintf('%s IS today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d'))); // count created journals on THIS day. $journalCount = $this->repository->getJournalCount($recurrence, $date, $date); if ($journalCount > 0 && false === $this->force) { - Log::info(sprintf('Already created %d journal(s) for date %s', $journalCount, $date->format('Y-m-d'))); + app('log')->info(sprintf('Already created %d journal(s) for date %s', $journalCount, $date->format('Y-m-d'))); return null; } if ($this->repository->createdPreviously($recurrence, $date) && false === $this->force) { - Log::info('There is a transaction already made for this date, so will not be created now'); + app('log')->info('There is a transaction already made for this date, so will not be created now'); return null; } @@ -417,7 +416,7 @@ class CreateRecurringTransactions implements ShouldQueue $groupTitle = $first->description; } if (0 === $count) { - Log::error('No transactions to be created in this recurrence. Cannot continue.'); + app('log')->error('No transactions to be created in this recurrence. Cannot continue.'); return null; } @@ -431,7 +430,7 @@ class CreateRecurringTransactions implements ShouldQueue /** @var TransactionGroup $group */ $group = $this->groupRepository->store($array); $this->created++; - Log::info(sprintf('Created new transaction group #%d', $group->id)); + app('log')->info(sprintf('Created new transaction group #%d', $group->id)); // trigger event: event(new StoredTransactionGroup($group, $recurrence->apply_rules, true)); @@ -469,7 +468,7 @@ class CreateRecurringTransactions implements ShouldQueue 'type' => null === $first->transactionType ? strtolower($recurrence->transactionType->type) : strtolower($first->transactionType->type), 'date' => $date, 'user' => $recurrence->user_id, - 'currency_id' => (int)$transaction->transaction_currency_id, + 'currency_id' => $transaction->transaction_currency_id, 'currency_code' => null, 'description' => $first->description, 'amount' => $transaction->amount, @@ -486,7 +485,7 @@ class CreateRecurringTransactions implements ShouldQueue 'foreign_amount' => $transaction->foreign_amount, 'reconciled' => false, 'identifier' => $index, - 'recurrence_id' => (int)$recurrence->id, + 'recurrence_id' => $recurrence->id, 'order' => $index, 'notes' => (string)trans('firefly.created_from_recurrence', ['id' => $recurrence->id, 'title' => $recurrence->title]), 'tags' => $this->repository->getTags($transaction), diff --git a/app/Jobs/DownloadExchangeRates.php b/app/Jobs/DownloadExchangeRates.php index 552c310778..ae0d3ebc9a 100644 --- a/app/Jobs/DownloadExchangeRates.php +++ b/app/Jobs/DownloadExchangeRates.php @@ -37,7 +37,6 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class DownloadExchangeRates @@ -74,7 +73,7 @@ class DownloadExchangeRates implements ShouldQueue $newDate = clone $date; $newDate->startOfDay(); $this->date = $newDate; - Log::debug(sprintf('Created new DownloadExchangeRates("%s")', $this->date->format('Y-m-d'))); + app('log')->debug(sprintf('Created new DownloadExchangeRates("%s")', $this->date->format('Y-m-d'))); } } @@ -83,7 +82,7 @@ class DownloadExchangeRates implements ShouldQueue */ public function handle(): void { - Log::debug('Now in handle()'); + app('log')->debug('Now in handle()'); $currencies = $this->repository->getCompleteSet(); /** @var TransactionCurrency $currency */ @@ -100,7 +99,7 @@ class DownloadExchangeRates implements ShouldQueue */ private function downloadRates(TransactionCurrency $currency): void { - Log::debug(sprintf('Now downloading new exchange rates for currency %s.', $currency->code)); + app('log')->debug(sprintf('Now downloading new exchange rates for currency %s.', $currency->code)); $base = sprintf('%s/%s/%s', (string)config('cer.url'), $this->date->year, $this->date->isoWeek); $client = new Client(); $url = sprintf('%s/%s.json', $base, $currency->code); @@ -122,6 +121,9 @@ class DownloadExchangeRates implements ShouldQueue return; } $date = Carbon::createFromFormat('Y-m-d', $json['date'], config('app.timezone')); + if (false === $date) { + return; + } $this->saveRates($currency, $date, $json['rates']); } @@ -137,10 +139,10 @@ class DownloadExchangeRates implements ShouldQueue foreach ($rates as $code => $rate) { $to = $this->getCurrency($code); if (null === $to) { - Log::debug(sprintf('Currency %s is not in use, do not save rate.', $code)); + app('log')->debug(sprintf('Currency %s is not in use, do not save rate.', $code)); continue; } - Log::debug(sprintf('Currency %s is in use.', $code)); + app('log')->debug(sprintf('Currency %s is in use.', $code)); $this->saveRate($currency, $to, $date, $rate); } } @@ -154,22 +156,22 @@ class DownloadExchangeRates implements ShouldQueue { // if we have it already, don't bother searching for it again. if (array_key_exists($code, $this->active)) { - Log::debug(sprintf('Already know what the result is of searching for %s', $code)); + app('log')->debug(sprintf('Already know what the result is of searching for %s', $code)); return $this->active[$code]; } // find it in the database. $currency = $this->repository->findByCode($code); if (null === $currency) { - Log::debug(sprintf('Did not find currency %s.', $code)); + app('log')->debug(sprintf('Did not find currency %s.', $code)); $this->active[$code] = null; return null; } if (false === $currency->enabled) { - Log::debug(sprintf('Currency %s is not enabled.', $code)); + app('log')->debug(sprintf('Currency %s is not enabled.', $code)); $this->active[$code] = null; return null; } - Log::debug(sprintf('Currency %s is enabled.', $code)); + app('log')->debug(sprintf('Currency %s is enabled.', $code)); $this->active[$code] = $currency; return $currency; @@ -189,7 +191,7 @@ class DownloadExchangeRates implements ShouldQueue $this->repository->setUser($user); $existing = $this->repository->getExchangeRate($from, $to, $date); if (null === $existing) { - Log::debug(sprintf('Saved rate from %s to %s for user #%d.', $from->code, $to->code, $user->id)); + app('log')->debug(sprintf('Saved rate from %s to %s for user #%d.', $from->code, $to->code, $user->id)); $this->repository->setExchangeRate($from, $to, $date, $rate); } } diff --git a/app/Jobs/MailError.php b/app/Jobs/MailError.php index bb4b916b7d..9d48ce949e 100644 --- a/app/Jobs/MailError.php +++ b/app/Jobs/MailError.php @@ -24,12 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Jobs; use Exception; -use FireflyIII\Exceptions\FireflyException; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Message; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Log; use Mail; use Symfony\Component\Mailer\Exception\TransportException; @@ -65,15 +63,14 @@ class MailError extends Job implements ShouldQueue $debug = $exceptionData; unset($debug['stackTrace']); unset($debug['headers']); - Log::error(sprintf('Exception is: %s', json_encode($debug))); + app('log')->error(sprintf('Exception is: %s', json_encode($debug))); } /** * Execute the job. * - * @throws FireflyException */ - public function handle() + public function handle(): void { $email = (string)config('firefly.site_owner'); $args = $this->exception; @@ -81,29 +78,29 @@ class MailError extends Job implements ShouldQueue $args['user'] = $this->userData; $args['ip'] = $this->ipAddress; $args['token'] = config('firefly.ipinfo_token'); - if ($this->attempts() < 3 && strlen($email) > 0) { + if ($this->attempts() < 3 && $email !== '') { try { Mail::send( ['emails.error-html', 'emails.error-text'], $args, - function (Message $message) use ($email) { + static function (Message $message) use ($email) { if ('mail@example.com' !== $email) { $message->to($email, $email)->subject((string)trans('email.error_subject')); } } ); - } catch (Exception | TransportException $e) { // intentional generic exception + } catch (Exception | TransportException $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { - Log::warning('[Bcc] Could not email or log the error. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[Bcc] Could not email or log the error. Please validate your email settings, use the .env.example file as a guide.'); return; } if (str_contains($message, 'RFC 2822')) { - Log::warning('[RFC] Could not email or log the error. Please validate your email settings, use the .env.example file as a guide.'); + app('log')->warning('[RFC] Could not email or log the error. Please validate your email settings, use the .env.example file as a guide.'); return; } - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); } } } diff --git a/app/Jobs/SendWebhookMessage.php b/app/Jobs/SendWebhookMessage.php index 42dfdf5d46..3539d74ed9 100644 --- a/app/Jobs/SendWebhookMessage.php +++ b/app/Jobs/SendWebhookMessage.php @@ -31,7 +31,6 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Log; /** * Class SendWebhookMessage @@ -62,7 +61,7 @@ class SendWebhookMessage implements ShouldQueue */ public function handle(): void { - Log::debug(sprintf('Now handling webhook message #%d', $this->message->id)); + app('log')->debug(sprintf('Now handling webhook message #%d', $this->message->id)); // send job! $sender = app(WebhookSenderInterface::class); $sender->setMessage($this->message); diff --git a/app/Jobs/WarnAboutBills.php b/app/Jobs/WarnAboutBills.php index 7438d9f760..31fdcf0599 100644 --- a/app/Jobs/WarnAboutBills.php +++ b/app/Jobs/WarnAboutBills.php @@ -32,7 +32,6 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Log; /** * Class WarnAboutBills @@ -67,7 +66,7 @@ class WarnAboutBills implements ShouldQueue $this->force = false; - Log::debug(sprintf('Created new WarnAboutBills("%s")', $this->date->format('Y-m-d'))); + app('log')->debug(sprintf('Created new WarnAboutBills("%s")', $this->date->format('Y-m-d'))); } /** @@ -75,11 +74,11 @@ class WarnAboutBills implements ShouldQueue */ public function handle(): void { - Log::debug(sprintf('Now at start of WarnAboutBills() job for %s.', $this->date->format('D d M Y'))); + app('log')->debug(sprintf('Now at start of WarnAboutBills() job for %s.', $this->date->format('D d M Y'))); $bills = Bill::all(); /** @var Bill $bill */ foreach ($bills as $bill) { - Log::debug(sprintf('Now checking bill #%d ("%s")', $bill->id, $bill->name)); + app('log')->debug(sprintf('Now checking bill #%d ("%s")', $bill->id, $bill->name)); if ($this->hasDateFields($bill)) { if ($this->needsWarning($bill, 'end_date')) { $this->sendWarning($bill, 'end_date'); @@ -89,7 +88,7 @@ class WarnAboutBills implements ShouldQueue } } } - Log::debug('Done with handle()'); + app('log')->debug('Done with handle()'); // clear cache: app('preferences')->mark(); @@ -103,11 +102,11 @@ class WarnAboutBills implements ShouldQueue private function hasDateFields(Bill $bill): bool { if (false === $bill->active) { - Log::debug('Bill is not active.'); + app('log')->debug('Bill is not active.'); return false; } if (null === $bill->end_date && null === $bill->extension_date) { - Log::debug('Bill has no date fields.'); + app('log')->debug('Bill has no date fields.'); return false; } return true; @@ -126,7 +125,7 @@ class WarnAboutBills implements ShouldQueue } $diff = $this->getDiff($bill, $field); $list = config('firefly.bill_reminder_periods'); - Log::debug(sprintf('Difference in days for field "%s" ("%s") is %d day(s)', $field, $bill->$field->format('Y-m-d'), $diff)); + app('log')->debug(sprintf('Difference in days for field "%s" ("%s") is %d day(s)', $field, $bill->$field->format('Y-m-d'), $diff)); if (in_array($diff, $list, true)) { return true; } @@ -155,7 +154,7 @@ class WarnAboutBills implements ShouldQueue private function sendWarning(Bill $bill, string $field): void { $diff = $this->getDiff($bill, $field); - Log::debug('Will now send warning!'); + app('log')->debug('Will now send warning!'); event(new WarnUserAboutBill($bill, $field, $diff)); } diff --git a/app/Mail/AccessTokenCreatedMail.php b/app/Mail/AccessTokenCreatedMail.php index 185300fa13..6a4f1db4bf 100644 --- a/app/Mail/AccessTokenCreatedMail.php +++ b/app/Mail/AccessTokenCreatedMail.php @@ -40,9 +40,7 @@ class AccessTokenCreatedMail extends Mailable /** * AccessTokenCreatedMail constructor. */ - public function __construct() - { - } + public function __construct() {} /** * Build the message. diff --git a/app/Mail/AdminTestMail.php b/app/Mail/AdminTestMail.php index 502f015361..e0861e5999 100644 --- a/app/Mail/AdminTestMail.php +++ b/app/Mail/AdminTestMail.php @@ -44,9 +44,7 @@ class AdminTestMail extends Mailable /** * AdminTestMail constructor. */ - public function __construct() - { - } + public function __construct() {} /** * Build the message. diff --git a/app/Mail/InvitationMail.php b/app/Mail/InvitationMail.php index faa1fa8582..f26184547b 100644 --- a/app/Mail/InvitationMail.php +++ b/app/Mail/InvitationMail.php @@ -53,7 +53,7 @@ class InvitationMail extends Mailable $this->invitee = $invitee; $this->admin = $admin; $this->url = $url; - $this->host = parse_url($url, PHP_URL_HOST); + $this->host = (string)parse_url($url, PHP_URL_HOST); } /** diff --git a/app/Mail/NewIPAddressWarningMail.php b/app/Mail/NewIPAddressWarningMail.php index bddb54d960..a9da5a0ebb 100644 --- a/app/Mail/NewIPAddressWarningMail.php +++ b/app/Mail/NewIPAddressWarningMail.php @@ -28,7 +28,6 @@ use FireflyIII\Exceptions\FireflyException; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Log; /** * Class NewIPAddressWarningMail @@ -64,7 +63,7 @@ class NewIPAddressWarningMail extends Mailable try { $hostName = app('steam')->getHostName($this->ipAddress); } catch (FireflyException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); $hostName = $this->ipAddress; } if ($hostName !== $this->ipAddress) { diff --git a/app/Mail/ReportNewJournalsMail.php b/app/Mail/ReportNewJournalsMail.php index 62e6a12cd4..24820ab5b7 100644 --- a/app/Mail/ReportNewJournalsMail.php +++ b/app/Mail/ReportNewJournalsMail.php @@ -67,7 +67,7 @@ class ReportNewJournalsMail extends Mailable return $this ->markdown('emails.report-new-journals') - ->subject((string)trans_choice('email.new_journals_subject', $this->groups->count())); + ->subject(trans_choice('email.new_journals_subject', $this->groups->count())); } /** diff --git a/app/Models/Account.php b/app/Models/Account.php index 502516f467..376090edf7 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -25,6 +25,8 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Casts\Attribute; @@ -42,36 +44,36 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class Account * - * @property int $id - * @property \Illuminate\Support\Carbon|null $created_at - * @property \Illuminate\Support\Carbon|null $updated_at - * @property \Illuminate\Support\Carbon|null $deleted_at - * @property int $user_id - * @property int $account_type_id - * @property string $name - * @property string|null $virtual_balance - * @property string|null $iban - * @property bool $active - * @property bool $encrypted - * @property int $order - * @property-read Collection|AccountMeta[] $accountMeta - * @property-read int|null $account_meta_count - * @property AccountType $accountType - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read string $account_number - * @property-read string $edit_name - * @property-read Collection|Location[] $locations - * @property-read int|null $locations_count - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read Collection|ObjectGroup[] $objectGroups - * @property-read int|null $object_groups_count - * @property-read Collection|PiggyBank[] $piggyBanks - * @property-read int|null $piggy_banks_count - * @property-read Collection|Transaction[] $transactions - * @property-read int|null $transactions_count - * @property-read User $user + * @property int $id + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property Carbon|null $deleted_at + * @property int $user_id + * @property int $account_type_id + * @property string $name + * @property string $virtual_balance + * @property string|null $iban + * @property bool $active + * @property bool $encrypted + * @property int $order + * @property-read Collection|AccountMeta[] $accountMeta + * @property-read int|null $account_meta_count + * @property AccountType $accountType + * @property-read Collection|Attachment[] $attachments + * @property-read int|null $attachments_count + * @property-read string $account_number + * @property-read string $edit_name + * @property-read Collection|Location[] $locations + * @property-read int|null $locations_count + * @property-read Collection|Note[] $notes + * @property-read int|null $notes_count + * @property-read Collection|ObjectGroup[] $objectGroups + * @property-read int|null $object_groups_count + * @property-read Collection|PiggyBank[] $piggyBanks + * @property-read int|null $piggy_banks_count + * @property-read Collection|Transaction[] $transactions + * @property-read int|null $transactions_count + * @property-read User $user * @method static EloquentBuilder|Account accountTypeIn($types) * @method static EloquentBuilder|Account newModelQuery() * @method static EloquentBuilder|Account newQuery() @@ -91,31 +93,28 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static EloquentBuilder|Account whereVirtualBalance($value) * @method static Builder|Account withTrashed() * @method static Builder|Account withoutTrashed() - * @property Carbon $lastActivityDate - * @property string $startBalance - * @property string $endBalance - * @property string $difference - * @property string $interest - * @property string $interestPeriod - * @property string $accountTypeString - * @property Location $location - * @property string $liability_direction - * @property string $current_debt - * @property int|null $user_group_id + * @property Carbon $lastActivityDate + * @property string $startBalance + * @property string $endBalance + * @property string $difference + * @property string $interest + * @property string $interestPeriod + * @property string $accountTypeString + * @property Location $location + * @property string $liability_direction + * @property string $current_debt + * @property int $user_group_id * @method static EloquentBuilder|Account whereUserGroupId($value) - * @property-read UserGroup|null $userGroup + * @property-read UserGroup|null $userGroup * @mixin Eloquent */ class Account extends Model { - use SoftDeletes; use HasFactory; + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; + use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -125,9 +124,9 @@ class Account extends Model 'active' => 'boolean', 'encrypted' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban']; - /** @var array Hidden from view */ + protected $hidden = ['encrypted']; private bool $joinedAccountTypes = false; @@ -139,13 +138,13 @@ class Account extends Model * @return Account * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Account + public static function routeBinder(string $value): self { if (auth()->check()) { $accountId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var Account $account */ + /** @var Account|null $account */ $account = $user->accounts()->with(['accountType'])->find($accountId); if (null !== $account) { return $account; @@ -185,12 +184,12 @@ class Account extends Model */ public function getAccountNumberAttribute(): string { - /** @var AccountMeta $metaValue */ + /** @var AccountMeta|null $metaValue */ $metaValue = $this->accountMeta() ->where('name', 'account_number') ->first(); - return $metaValue ? $metaValue->data : ''; + return null !== $metaValue ? $metaValue->data : ''; } /** @@ -292,6 +291,38 @@ class Account extends Model return $this->belongsTo(UserGroup::class); } + /** + * @return Attribute + */ + protected function accountId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * Get the user ID + * + * @return Attribute + */ + protected function accountTypeId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + /** * Get the virtual balance * @@ -300,7 +331,8 @@ class Account extends Model protected function virtualBalance(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } + } diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index 6d7a31aaa2..95f309ad8b 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -23,11 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Carbon; /** * Class AccountMeta @@ -52,17 +53,14 @@ use Illuminate\Support\Carbon; */ class AccountMeta extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['account_id', 'name', 'data']; /** @var string The table to store the data in */ protected $table = 'account_meta'; @@ -78,20 +76,19 @@ class AccountMeta extends Model /** * @param mixed $value * - * @return mixed + * @return string */ - public function getDataAttribute($value): string + public function getDataAttribute(mixed $value): string { return (string)json_decode($value, true); } /** * @param mixed $value - * - */ - public function setDataAttribute($value): void + public function setDataAttribute(mixed $value): void { $this->attributes['data'] = json_encode($value); } + } diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index b166052150..09b279ff8b 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -23,12 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\AccountType @@ -50,33 +51,30 @@ use Illuminate\Support\Carbon; */ class AccountType extends Model { - public const ASSET = 'Asset account'; - public const BENEFICIARY = 'Beneficiary account'; - public const CASH = 'Cash account'; - public const CREDITCARD = 'Credit card'; - public const DEBT = 'Debt'; - public const DEFAULT = 'Default account'; - public const EXPENSE = 'Expense account'; - public const IMPORT = 'Import account'; - public const INITIAL_BALANCE = 'Initial balance account'; - public const LIABILITY_CREDIT = 'Liability credit account'; - public const LOAN = 'Loan'; - public const MORTGAGE = 'Mortgage'; - public const RECONCILIATION = 'Reconciliation account'; - public const REVENUE = 'Revenue account'; + use ReturnsIntegerIdTrait; + + public const string ASSET = 'Asset account'; + public const string BENEFICIARY = 'Beneficiary account'; + public const string CASH = 'Cash account'; + public const string CREDITCARD = 'Credit card'; + public const string DEBT = 'Debt'; + public const string DEFAULT = 'Default account'; + public const string EXPENSE = 'Expense account'; + public const string IMPORT = 'Import account'; + public const string INITIAL_BALANCE = 'Initial balance account'; + public const string LIABILITY_CREDIT = 'Liability credit account'; + public const string LOAN = 'Loan'; + public const string MORTGAGE = 'Mortgage'; + public const string RECONCILIATION = 'Reconciliation account'; + public const string REVENUE = 'Revenue account'; - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['type']; /** @@ -86,4 +84,6 @@ class AccountType extends Model { return $this->hasMany(Account::class); } + + } diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index 235a8c1663..a22246ebb5 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -23,8 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -32,7 +36,6 @@ use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -51,7 +54,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property string|null $title * @property string|null $description * @property string $mime - * @property int $size + * @property int|string $size * @property bool $uploaded * @property string $notes_text * @property-read Model|Eloquent $attachable @@ -78,19 +81,16 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Attachment whereUserId($value) * @method static Builder|Attachment withTrashed() * @method static Builder|Attachment withoutTrashed() - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|Attachment whereUserGroupId($value) * @mixin Eloquent */ class Attachment extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -98,7 +98,7 @@ class Attachment extends Model 'deleted_at' => 'datetime', 'uploaded' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['attachable_id', 'attachable_type', 'user_id', 'md5', 'filename', 'mime', 'title', 'description', 'size', 'uploaded']; /** @@ -109,13 +109,13 @@ class Attachment extends Model * @return Attachment * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Attachment + public static function routeBinder(string $value): self { if (auth()->check()) { $attachmentId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var Attachment $attachment */ + /** @var Attachment|null $attachment */ $attachment = $user->attachments()->find($attachmentId); if (null !== $attachment) { return $attachment; @@ -160,4 +160,15 @@ class Attachment extends Model { return $this->morphMany(Note::class, 'noteable'); } + + /** + * @return Attribute + */ + protected function attachableId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + } diff --git a/app/Models/AuditLogEntry.php b/app/Models/AuditLogEntry.php index fac7d1a7ef..e48d0ecaf6 100644 --- a/app/Models/AuditLogEntry.php +++ b/app/Models/AuditLogEntry.php @@ -24,12 +24,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Support\Carbon; /** * Class AuditLogEntry @@ -68,6 +70,7 @@ use Illuminate\Support\Carbon; */ class AuditLogEntry extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; protected $casts @@ -92,4 +95,26 @@ class AuditLogEntry extends Model { return $this->morphTo(); } + + /** + * @return Attribute + */ + protected function auditableId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function changerId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + } diff --git a/app/Models/AutoBudget.php b/app/Models/AutoBudget.php index 41f446da71..ed02be17fe 100644 --- a/app/Models/AutoBudget.php +++ b/app/Models/AutoBudget.php @@ -24,13 +24,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\AutoBudget @@ -41,7 +42,7 @@ use Illuminate\Support\Carbon; * @property Carbon|null $deleted_at * @property int $budget_id * @property int $transaction_currency_id - * @property int $auto_budget_type + * @property int|string $auto_budget_type * @property string $amount * @property string $period * @property-read Budget $budget @@ -65,11 +66,12 @@ use Illuminate\Support\Carbon; */ class AutoBudget extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - public const AUTO_BUDGET_ADJUSTED = 3; - public const AUTO_BUDGET_RESET = 1; - public const AUTO_BUDGET_ROLLOVER = 2; + public const int AUTO_BUDGET_ADJUSTED = 3; + public const int AUTO_BUDGET_RESET = 1; + public const int AUTO_BUDGET_ROLLOVER = 2; protected $fillable = ['budget_id', 'amount', 'period']; /** @@ -94,7 +96,28 @@ class AutoBudget extends Model protected function amount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } + + /** + * @return Attribute + */ + protected function budgetId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function transactionCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + } diff --git a/app/Models/AvailableBudget.php b/app/Models/AvailableBudget.php index eac01d5043..182dd22c3e 100644 --- a/app/Models/AvailableBudget.php +++ b/app/Models/AvailableBudget.php @@ -23,14 +23,16 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -62,19 +64,16 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget whereUserId($value) * @method static Builder|AvailableBudget withTrashed() * @method static Builder|AvailableBudget withoutTrashed() - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|AvailableBudget whereUserGroupId($value) * @mixin Eloquent */ class AvailableBudget extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -84,7 +83,7 @@ class AvailableBudget extends Model 'end_date' => 'date', 'transaction_currency_id' => 'int', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['user_id', 'user_group_id', 'transaction_currency_id', 'amount', 'start_date', 'end_date']; /** @@ -95,13 +94,13 @@ class AvailableBudget extends Model * @return AvailableBudget * @throws NotFoundHttpException */ - public static function routeBinder(string $value): AvailableBudget + public static function routeBinder(string $value): self { if (auth()->check()) { $availableBudgetId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var AvailableBudget $availableBudget */ + /** @var AvailableBudget|null $availableBudget */ $availableBudget = $user->availableBudgets()->find($availableBudgetId); if (null !== $availableBudget) { return $availableBudget; @@ -132,7 +131,18 @@ class AvailableBudget extends Model protected function amount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } + + /** + * @return Attribute + */ + protected function transactionCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + } diff --git a/app/Models/Bill.php b/app/Models/Bill.php index f9f2510fc2..73bf79a971 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -23,7 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; @@ -31,9 +34,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphMany; +use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -44,7 +47,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property Carbon|null $updated_at * @property Carbon|null $deleted_at * @property int $user_id - * @property int|null $transaction_currency_id + * @property int $transaction_currency_id * @property string $name * @property string $match * @property string $amount_min @@ -95,19 +98,16 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Bill whereUserId($value) * @method static Builder|Bill withTrashed() * @method static Builder|Bill withoutTrashed() - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|Bill whereUserGroupId($value) * @mixin Eloquent */ class Bill extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -123,7 +123,7 @@ class Bill extends Model 'match_encrypted' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = [ 'name', @@ -141,7 +141,7 @@ class Bill extends Model 'end_date', 'extension_date', ]; - /** @var array Hidden from view */ + protected $hidden = ['amount_min_encrypted', 'amount_max_encrypted', 'name_encrypted', 'match_encrypted']; /** @@ -152,13 +152,13 @@ class Bill extends Model * @return Bill * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Bill + public static function routeBinder(string $value): self { if (auth()->check()) { $billId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var Bill $bill */ + /** @var Bill|null $bill */ $bill = $user->bills()->find($billId); if (null !== $bill) { return $bill; @@ -192,9 +192,9 @@ class Bill extends Model } /** - * Get all of the tags for the post. + * Get all the tags for the post. */ - public function objectGroups() + public function objectGroups(): MorphToMany { return $this->morphToMany(ObjectGroup::class, 'object_groupable'); } @@ -242,7 +242,7 @@ class Bill extends Model protected function amountMax(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } @@ -254,7 +254,41 @@ class Bill extends Model protected function amountMin(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * Get the skip + * + * @return Attribute + */ + protected function skip(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function transactionCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + } diff --git a/app/Models/Budget.php b/app/Models/Budget.php index 88e6a30138..88168dc69e 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -23,8 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -33,7 +37,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -75,7 +78,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|Budget withTrashed() * @method static Builder|Budget withoutTrashed() * @property string $email - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|Budget whereUserGroupId($value) * @property-read Collection|Note[] $notes * @property-read int|null $notes_count @@ -83,13 +86,10 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class Budget extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -98,9 +98,9 @@ class Budget extends Model 'active' => 'boolean', 'encrypted' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['user_id', 'name', 'active', 'order', 'user_group_id']; - /** @var array Hidden from view */ + protected $hidden = ['encrypted']; /** @@ -111,13 +111,13 @@ class Budget extends Model * @return Budget * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Budget + public static function routeBinder(string $value): self { if (auth()->check()) { $budgetId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var Budget $budget */ + /** @var Budget|null $budget */ $budget = $user->budgets()->find($budgetId); if (null !== $budget) { return $budget; @@ -181,4 +181,15 @@ class Budget extends Model { return $this->belongsToMany(Transaction::class, 'budget_transaction', 'budget_id'); } + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + } diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index 59cec2c1f1..a228f7cd9c 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -23,15 +23,16 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; use FireflyIII\Events\Model\BudgetLimit\Created; use FireflyIII\Events\Model\BudgetLimit\Deleted; use FireflyIII\Events\Model\BudgetLimit\Updated; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -41,13 +42,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property Carbon|null $created_at * @property Carbon|null $updated_at * @property int $budget_id - * @property int|null $transaction_currency_id + * @property int $transaction_currency_id * @property Carbon $start_date * @property Carbon|null $end_date * @property string $amount * @property string $spent * @property string|null $period - * @property int $generated + * @property int|string $generated * @property-read Budget $budget * @property-read TransactionCurrency|null $transactionCurrency * @method static Builder|BudgetLimit newModelQuery() @@ -67,11 +68,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class BudgetLimit extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + protected $casts = [ 'created_at' => 'datetime', @@ -86,7 +84,7 @@ class BudgetLimit extends Model 'updated' => Updated::class, 'deleted' => Deleted::class, ]; - /** @var array Fields that can be filled */ + protected $fillable = ['budget_id', 'start_date', 'end_date', 'amount', 'transaction_currency_id']; /** @@ -97,7 +95,7 @@ class BudgetLimit extends Model * @return BudgetLimit * @throws NotFoundHttpException */ - public static function routeBinder(string $value): BudgetLimit + public static function routeBinder(string $value): self { if (auth()->check()) { $budgetLimitId = (int)$value; @@ -136,7 +134,29 @@ class BudgetLimit extends Model protected function amount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } + + /** + * @return Attribute + */ + protected function budgetId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function transactionCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + } diff --git a/app/Models/Category.php b/app/Models/Category.php index dc52f22bbb..76065276e0 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -25,6 +25,8 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; @@ -39,9 +41,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * FireflyIII\Models\Category * * @property int $id - * @property \Illuminate\Support\Carbon|null $created_at - * @property \Illuminate\Support\Carbon|null $updated_at - * @property \Illuminate\Support\Carbon|null $deleted_at + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property Carbon|null $deleted_at * @property int $user_id * @property string $name * @property Carbon $lastActivity @@ -68,19 +70,16 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Category whereUserId($value) * @method static Builder|Category withTrashed() * @method static Builder|Category withoutTrashed() - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|Category whereUserGroupId($value) * @mixin Eloquent */ class Category extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -88,9 +87,9 @@ class Category extends Model 'deleted_at' => 'datetime', 'encrypted' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['user_id', 'user_group_id', 'name']; - /** @var array Hidden from view */ + protected $hidden = ['encrypted']; /** @@ -101,13 +100,13 @@ class Category extends Model * @return Category * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Category + public static function routeBinder(string $value): self { if (auth()->check()) { $categoryId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var Category $category */ + /** @var Category|null $category */ $category = $user->categories()->find($categoryId); if (null !== $category) { return $category; @@ -155,4 +154,5 @@ class Category extends Model { return $this->belongsToMany(Transaction::class, 'category_transaction', 'category_id'); } + } diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 8abf4eb569..2613a32a89 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -23,11 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\Configuration @@ -54,13 +55,10 @@ use Illuminate\Support\Carbon; */ class Configuration extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', @@ -91,4 +89,6 @@ class Configuration extends Model { $this->attributes['data'] = json_encode($value); } + + } diff --git a/app/Models/CurrencyExchangeRate.php b/app/Models/CurrencyExchangeRate.php index 31a95e8348..86b317bf97 100644 --- a/app/Models/CurrencyExchangeRate.php +++ b/app/Models/CurrencyExchangeRate.php @@ -23,14 +23,16 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Support\Carbon; /** * Class CurrencyExchangeRate @@ -44,7 +46,7 @@ use Illuminate\Support\Carbon; * @property int $to_currency_id * @property Carbon $date * @property string $rate - * @property string|null $user_rate + * @property string $user_rate * @property-read TransactionCurrency $fromCurrency * @property-read TransactionCurrency $toCurrency * @property-read User $user @@ -61,7 +63,7 @@ use Illuminate\Support\Carbon; * @method static Builder|CurrencyExchangeRate whereUpdatedAt($value) * @method static Builder|CurrencyExchangeRate whereUserId($value) * @method static Builder|CurrencyExchangeRate whereUserRate($value) - * @property int|null $user_group_id + * @property int $user_group_id * @method static Builder|CurrencyExchangeRate whereUserGroupId($value) * @method static Builder|CurrencyExchangeRate onlyTrashed() * @method static Builder|CurrencyExchangeRate withTrashed() @@ -70,9 +72,11 @@ use Illuminate\Support\Carbon; */ class CurrencyExchangeRate extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** @var array Convert these fields to other data types */ + protected $casts = [ 'created_at' => 'datetime', @@ -108,13 +112,33 @@ class CurrencyExchangeRate extends Model return $this->belongsTo(User::class); } + /** + * @return Attribute + */ + protected function fromCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + /** * @return Attribute */ protected function rate(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, + ); + } + + /** + * @return Attribute + */ + protected function toCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, ); } @@ -124,7 +148,9 @@ class CurrencyExchangeRate extends Model protected function userRate(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } + + } diff --git a/app/Models/GroupMembership.php b/app/Models/GroupMembership.php index 1af5447862..9e1a3852b8 100644 --- a/app/Models/GroupMembership.php +++ b/app/Models/GroupMembership.php @@ -24,12 +24,15 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Carbon; /** * Class GroupMembership @@ -58,6 +61,9 @@ use Illuminate\Support\Carbon; */ class GroupMembership extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; + protected $fillable = ['user_id', 'user_group_id', 'user_role_id']; /** @@ -83,4 +89,16 @@ class GroupMembership extends Model { return $this->belongsTo(UserRole::class); } + + /** + * @return Attribute + */ + protected function userRoleId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + } diff --git a/app/Models/InvitedUser.php b/app/Models/InvitedUser.php index b0203f49f3..e3acc26f8a 100644 --- a/app/Models/InvitedUser.php +++ b/app/Models/InvitedUser.php @@ -26,6 +26,8 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; @@ -59,6 +61,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class InvitedUser extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; + protected $casts = [ 'expires' => 'datetime', @@ -73,11 +78,11 @@ class InvitedUser extends Model * * @return InvitedUser */ - public static function routeBinder(string $value): InvitedUser + public static function routeBinder(string $value): self { if (auth()->check()) { $attemptId = (int)$value; - /** @var InvitedUser $attempt */ + /** @var InvitedUser|null $attempt */ $attempt = self::find($attemptId); if (null !== $attempt) { return $attempt; @@ -93,4 +98,6 @@ class InvitedUser extends Model { return $this->belongsTo(User::class); } + + } diff --git a/app/Models/LinkType.php b/app/Models/LinkType.php index 1703672153..3acbfdbf27 100644 --- a/app/Models/LinkType.php +++ b/app/Models/LinkType.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -64,13 +65,10 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class LinkType extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', @@ -79,7 +77,7 @@ class LinkType extends Model 'editable' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['name', 'inward', 'outward', 'editable']; /** @@ -91,7 +89,7 @@ class LinkType extends Model * * @throws NotFoundHttpException */ - public static function routeBinder(string $value): LinkType + public static function routeBinder(string $value): self { if (auth()->check()) { $linkTypeId = (int)$value; @@ -110,4 +108,6 @@ class LinkType extends Model { return $this->hasMany(TransactionJournalLink::class); } + + } diff --git a/app/Models/Location.php b/app/Models/Location.php index 76ea452351..c67085f2de 100644 --- a/app/Models/Location.php +++ b/app/Models/Location.php @@ -24,13 +24,15 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphTo; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\Location @@ -63,11 +65,8 @@ use Illuminate\Support\Carbon; */ class Location extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + protected $casts = [ 'created_at' => 'datetime', @@ -77,7 +76,7 @@ class Location extends Model 'latitude' => 'float', 'longitude' => 'float', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['locatable_id', 'locatable_type', 'latitude', 'longitude', 'zoom_level']; /** @@ -114,4 +113,16 @@ class Location extends Model { return $this->morphTo(); } + + /** + * @return Attribute + */ + protected function locatableId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + } diff --git a/app/Models/Note.php b/app/Models/Note.php index 1a8a2cab11..f4ef3f46e8 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -23,12 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\Note @@ -60,20 +62,17 @@ use Illuminate\Support\Carbon; */ class Note extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['title', 'text', 'noteable_id', 'noteable_type']; /** @@ -84,4 +83,16 @@ class Note extends Model { return $this->morphTo(); } + + /** + * @return Attribute + */ + protected function noteableId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + } diff --git a/app/Models/ObjectGroup.php b/app/Models/ObjectGroup.php index 5524921312..8d0f610ec8 100644 --- a/app/Models/ObjectGroup.php +++ b/app/Models/ObjectGroup.php @@ -24,14 +24,17 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphToMany; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -61,17 +64,15 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|ObjectGroup whereTitle($value) * @method static Builder|ObjectGroup whereUpdatedAt($value) * @method static Builder|ObjectGroup whereUserId($value) - * @property int|null $user_group_id + * @property int $user_group_id * @method static Builder|ObjectGroup whereUserGroupId($value) * @mixin Eloquent */ class ObjectGroup extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; + protected $casts = [ 'created_at' => 'datetime', @@ -89,11 +90,11 @@ class ObjectGroup extends Model * @return ObjectGroup * @throws NotFoundHttpException */ - public static function routeBinder(string $value): ObjectGroup + public static function routeBinder(string $value): self { if (auth()->check()) { $objectGroupId = (int)$value; - /** @var ObjectGroup $objectGroup */ + /** @var ObjectGroup|null $objectGroup */ $objectGroup = self::where('object_groups.id', $objectGroupId) ->where('object_groups.user_id', auth()->user()->id)->first(); if (null !== $objectGroup) { @@ -134,4 +135,16 @@ class ObjectGroup extends Model { return $this->morphedByMany(PiggyBank::class, 'object_groupable'); } + + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 47c5aa6de0..45e0b24088 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -23,16 +23,18 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphMany; +use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -83,13 +85,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class PiggyBank extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - /** - * The attributes that should be cast to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -101,9 +99,9 @@ class PiggyBank extends Model 'active' => 'boolean', 'encrypted' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['name', 'account_id', 'order', 'targetamount', 'startdate', 'targetdate', 'active']; - /** @var array Hidden from view */ + protected $hidden = ['targetamount_encrypted', 'encrypted']; /** @@ -114,7 +112,7 @@ class PiggyBank extends Model * @return PiggyBank * @throws NotFoundHttpException */ - public static function routeBinder(string $value): PiggyBank + public static function routeBinder(string $value): self { if (auth()->check()) { $piggyBankId = (int)$value; @@ -145,7 +143,7 @@ class PiggyBank extends Model } /** - * Get all of the piggy bank's notes. + * Get all the piggy bank's notes. */ public function notes(): MorphMany { @@ -155,7 +153,7 @@ class PiggyBank extends Model /** * Get all the tags for the post. */ - public function objectGroups() + public function objectGroups(): MorphToMany { return $this->morphToMany(ObjectGroup::class, 'object_groupable'); } @@ -185,6 +183,26 @@ class PiggyBank extends Model $this->attributes['targetamount'] = (string)$value; } + /** + * @return Attribute + */ + protected function accountId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + /** * Get the max amount * @@ -193,7 +211,8 @@ class PiggyBank extends Model protected function targetamount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } + } diff --git a/app/Models/PiggyBankEvent.php b/app/Models/PiggyBankEvent.php index 8334aac725..67cfd2fff3 100644 --- a/app/Models/PiggyBankEvent.php +++ b/app/Models/PiggyBankEvent.php @@ -23,12 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\PiggyBankEvent @@ -56,20 +57,17 @@ use Illuminate\Support\Carbon; */ class PiggyBankEvent extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'date' => 'date', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['piggy_bank_id', 'transaction_journal_id', 'date', 'amount']; - /** @var array Hidden from view */ + protected $hidden = ['amount_encrypted']; /** @@ -105,7 +103,17 @@ class PiggyBankEvent extends Model protected function amount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, + ); + } + + /** + * @return Attribute + */ + protected function piggyBankId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index c00650b38c..8e6568f7b5 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -25,6 +25,7 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; @@ -33,14 +34,14 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * FireflyIII\Models\PiggyBankRepetition * - * @property int $id - * @property \Illuminate\Support\Carbon|null $created_at - * @property \Illuminate\Support\Carbon|null $updated_at - * @property int $piggy_bank_id - * @property \Illuminate\Support\Carbon|null $startdate - * @property \Illuminate\Support\Carbon|null $targetdate - * @property string $currentamount - * @property-read PiggyBank $piggyBank + * @property int $id + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property int $piggy_bank_id + * @property Carbon|null $startdate + * @property Carbon|null $targetdate + * @property string $currentamount + * @property-read PiggyBank $piggyBank * @method static EloquentBuilder|PiggyBankRepetition newModelQuery() * @method static EloquentBuilder|PiggyBankRepetition newQuery() * @method static EloquentBuilder|PiggyBankRepetition onDates(Carbon $start, Carbon $target) @@ -57,11 +58,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; */ class PiggyBankRepetition extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + protected $casts = [ 'created_at' => 'datetime', @@ -69,7 +67,7 @@ class PiggyBankRepetition extends Model 'startdate' => 'date', 'targetdate' => 'date', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['piggy_bank_id', 'startdate', 'targetdate', 'currentamount']; /** @@ -103,13 +101,13 @@ class PiggyBankRepetition extends Model public function scopeRelevantOnDate(EloquentBuilder $query, Carbon $date) { return $query->where( - function (EloquentBuilder $q) use ($date) { + static function (EloquentBuilder $q) use ($date) { $q->where('startdate', '<=', $date->format('Y-m-d 00:00:00')); $q->orWhereNull('startdate'); } ) ->where( - function (EloquentBuilder $q) use ($date) { + static function (EloquentBuilder $q) use ($date) { $q->where('targetdate', '>=', $date->format('Y-m-d 00:00:00')); $q->orWhereNull('targetdate'); } @@ -133,7 +131,17 @@ class PiggyBankRepetition extends Model protected function currentamount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, + ); + } + + /** + * @return Attribute + */ + protected function piggyBankId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/Preference.php b/app/Models/Preference.php index e83e2e60d1..f249f4ae1f 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -23,12 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -54,11 +56,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class Preference extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; + protected $casts = [ 'created_at' => 'datetime', @@ -66,7 +66,7 @@ class Preference extends Model 'data' => 'array', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['user_id', 'data', 'name']; /** @@ -77,7 +77,7 @@ class Preference extends Model * @return Preference * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Preference + public static function routeBinder(string $value): self { if (auth()->check()) { /** @var User $user */ @@ -92,10 +92,10 @@ class Preference extends Model } $default = config('firefly.default_preferences'); if (array_key_exists($value, $default)) { - $preference = new Preference(); + $preference = new self(); $preference->name = $value; $preference->data = $default[$value]; - $preference->user_id = $user->id; + $preference->user_id = (int)$user->id; $preference->save(); return $preference; diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index df6f6f21a3..678c9d984b 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -23,8 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -32,7 +36,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -46,10 +49,10 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property int $transaction_type_id * @property string $title * @property string $description - * @property Carbon $first_date + * @property Carbon|null $first_date * @property Carbon|null $repeat_until * @property Carbon|null $latest_date - * @property int $repetitions + * @property int|string $repetitions * @property bool $apply_rules * @property bool $active * @property-read Collection|Attachment[] $attachments @@ -85,19 +88,17 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Recurrence whereUserId($value) * @method static Builder|Recurrence withTrashed() * @method static Builder|Recurrence withoutTrashed() - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|Recurrence whereUserGroupId($value) * @mixin Eloquent */ class Recurrence extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', @@ -113,7 +114,7 @@ class Recurrence extends Model 'active' => 'bool', 'apply_rules' => 'bool', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['user_id', 'transaction_type_id', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active']; /** @var string The table to store the data in */ @@ -127,13 +128,13 @@ class Recurrence extends Model * @return Recurrence * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Recurrence + public static function routeBinder(string $value): self { if (auth()->check()) { $recurrenceId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var Recurrence $recurrence */ + /** @var Recurrence|null $recurrence */ $recurrence = $user->recurrences()->find($recurrenceId); if (null !== $recurrence) { return $recurrence; @@ -205,4 +206,14 @@ class Recurrence extends Model { return $this->belongsTo(TransactionType::class); } + + /** + * @return Attribute + */ + protected function transactionTypeId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RecurrenceMeta.php b/app/Models/RecurrenceMeta.php index d5e764fb61..7d7f333d97 100644 --- a/app/Models/RecurrenceMeta.php +++ b/app/Models/RecurrenceMeta.php @@ -23,12 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\RecurrenceMeta @@ -58,13 +60,10 @@ use Illuminate\Support\Carbon; */ class RecurrenceMeta extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', @@ -73,7 +72,7 @@ class RecurrenceMeta extends Model 'name' => 'string', 'value' => 'string', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['recurrence_id', 'name', 'value']; /** @var string The table to store the data in */ protected $table = 'recurrences_meta'; @@ -85,4 +84,14 @@ class RecurrenceMeta extends Model { return $this->belongsTo(Recurrence::class); } + + /** + * @return Attribute + */ + protected function recurrenceId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RecurrenceRepetition.php b/app/Models/RecurrenceRepetition.php index 8e8970e4a3..0446b074a7 100644 --- a/app/Models/RecurrenceRepetition.php +++ b/app/Models/RecurrenceRepetition.php @@ -23,12 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\RecurrenceRepetition @@ -62,18 +64,15 @@ use Illuminate\Support\Carbon; */ class RecurrenceRepetition extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - public const WEEKEND_DO_NOTHING = 1; - public const WEEKEND_SKIP_CREATION = 2; - public const WEEKEND_TO_FRIDAY = 3; - public const WEEKEND_TO_MONDAY = 4; + public const int WEEKEND_DO_NOTHING = 1; + public const int WEEKEND_SKIP_CREATION = 2; + public const int WEEKEND_TO_FRIDAY = 3; + public const int WEEKEND_TO_MONDAY = 4; + - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -84,7 +83,7 @@ class RecurrenceRepetition extends Model 'repetition_skip' => 'int', 'weekend' => 'int', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip']; /** @var string The table to store the data in */ protected $table = 'recurrences_repetitions'; @@ -96,4 +95,34 @@ class RecurrenceRepetition extends Model { return $this->belongsTo(Recurrence::class); } + + /** + * @return Attribute + */ + protected function recurrenceId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function repetitionSkip(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function weekend(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index e7911de4a7..fce3ad652f 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; @@ -31,7 +33,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\RecurrenceTransaction @@ -42,11 +43,11 @@ use Illuminate\Support\Carbon; * @property Carbon|null $deleted_at * @property int $recurrence_id * @property int $transaction_currency_id - * @property int|null $foreign_currency_id + * @property int|string|null $foreign_currency_id * @property int $source_id * @property int $destination_id * @property string $amount - * @property string|null $foreign_amount + * @property string $foreign_amount * @property string $description * @property-read Account $destinationAccount * @property-read TransactionCurrency|null $foreignCurrency @@ -80,13 +81,10 @@ use Illuminate\Support\Carbon; */ class RecurrenceTransaction extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', @@ -96,7 +94,7 @@ class RecurrenceTransaction extends Model 'foreign_amount' => 'string', 'description' => 'string', ]; - /** @var array Fields that can be filled */ + protected $fillable = [ 'recurrence_id', @@ -173,7 +171,17 @@ class RecurrenceTransaction extends Model protected function amount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, + ); + } + + /** + * @return Attribute + */ + protected function destinationId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, ); } @@ -183,7 +191,48 @@ class RecurrenceTransaction extends Model protected function foreignAmount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, + ); + + } + + /** + * @return Attribute + */ + protected function recurrenceId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function sourceId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function transactionCurrencyId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function userId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/RecurrenceTransactionMeta.php b/app/Models/RecurrenceTransactionMeta.php index c290cba2fb..82c48fb642 100644 --- a/app/Models/RecurrenceTransactionMeta.php +++ b/app/Models/RecurrenceTransactionMeta.php @@ -23,12 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\RecurrenceTransactionMeta @@ -37,7 +39,7 @@ use Illuminate\Support\Carbon; * @property Carbon|null $created_at * @property Carbon|null $updated_at * @property Carbon|null $deleted_at - * @property int $rt_id + * @property int|string $rt_id * @property string $name * @property mixed $value * @property-read RecurrenceTransaction $recurrenceTransaction @@ -58,13 +60,10 @@ use Illuminate\Support\Carbon; */ class RecurrenceTransactionMeta extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', @@ -73,7 +72,7 @@ class RecurrenceTransactionMeta extends Model 'name' => 'string', 'value' => 'string', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['rt_id', 'name', 'value']; /** @var string The table to store the data in */ protected $table = 'rt_meta'; @@ -85,4 +84,14 @@ class RecurrenceTransactionMeta extends Model { return $this->belongsTo(RecurrenceTransaction::class, 'rt_id'); } + + /** + * @return Attribute + */ + protected function rtId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/Role.php b/app/Models/Role.php index d9cb20b297..b417d10001 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\Role @@ -55,18 +56,15 @@ use Illuminate\Support\Carbon; */ class Role extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['name', 'display_name', 'description']; /** diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 18e2e6d1dd..d3f4aeef49 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -23,15 +23,18 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -74,20 +77,18 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Rule whereUserId($value) * @method static Builder|Rule withTrashed() * @method static Builder|Rule withoutTrashed() - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|Rule whereUserGroupId($value) * @property-read UserGroup|null $userGroup * @mixin Eloquent */ class Rule extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', @@ -99,7 +100,7 @@ class Rule extends Model 'id' => 'int', 'strict' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['rule_group_id', 'order', 'active', 'title', 'description', 'user_id', 'strict']; /** @@ -110,13 +111,13 @@ class Rule extends Model * @return Rule * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Rule + public static function routeBinder(string $value): self { if (auth()->check()) { $ruleId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var Rule $rule */ + /** @var Rule|null $rule */ $rule = $user->rules()->find($ruleId); if (null !== $rule) { return $rule; @@ -174,4 +175,24 @@ class Rule extends Model { return $this->belongsTo(UserGroup::class); } + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function ruleGroupId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index 977aa71354..217462bfa4 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -23,11 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\RuleAction @@ -36,8 +38,8 @@ use Illuminate\Support\Carbon; * @property Carbon|null $created_at * @property Carbon|null $updated_at * @property int $rule_id - * @property string $action_type - * @property string $action_value + * @property string|null $action_type + * @property string|null $action_value * @property int $order * @property bool $active * @property bool $stop_processing @@ -58,11 +60,8 @@ use Illuminate\Support\Carbon; */ class RuleAction extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + protected $casts = [ 'created_at' => 'datetime', @@ -72,7 +71,7 @@ class RuleAction extends Model 'stop_processing' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['rule_id', 'action_type', 'action_value', 'order', 'active', 'stop_processing']; /** @@ -82,4 +81,25 @@ class RuleAction extends Model { return $this->belongsTo(Rule::class); } + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function ruleId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + } diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index ae8863fdd3..6d6fe9026d 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -23,15 +23,18 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -42,7 +45,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property Carbon|null $updated_at * @property Carbon|null $deleted_at * @property int $user_id - * @property string $title + * @property string|null $title * @property string|null $description * @property int $order * @property bool $active @@ -66,19 +69,17 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup whereUserId($value) * @method static Builder|RuleGroup withTrashed() * @method static Builder|RuleGroup withoutTrashed() - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup whereUserGroupId($value) * @mixin Eloquent */ class RuleGroup extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', @@ -89,7 +90,7 @@ class RuleGroup extends Model 'order' => 'int', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['user_id', 'user_group_id', 'stop_processing', 'order', 'title', 'description', 'active']; /** @@ -100,13 +101,13 @@ class RuleGroup extends Model * @return RuleGroup * @throws NotFoundHttpException */ - public static function routeBinder(string $value): RuleGroup + public static function routeBinder(string $value): self { if (auth()->check()) { $ruleGroupId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var RuleGroup $ruleGroup */ + /** @var RuleGroup|null $ruleGroup */ $ruleGroup = $user->ruleGroups()->find($ruleGroupId); if (null !== $ruleGroup) { return $ruleGroup; @@ -130,4 +131,14 @@ class RuleGroup extends Model { return $this->hasMany(Rule::class); } + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php index 20cf189375..8194592e41 100644 --- a/app/Models/RuleTrigger.php +++ b/app/Models/RuleTrigger.php @@ -23,11 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\RuleTrigger @@ -36,8 +38,8 @@ use Illuminate\Support\Carbon; * @property Carbon|null $created_at * @property Carbon|null $updated_at * @property int $rule_id - * @property string $trigger_type - * @property string $trigger_value + * @property string|null $trigger_type + * @property string|null $trigger_value * @property int $order * @property bool $active * @property bool $stop_processing @@ -58,11 +60,8 @@ use Illuminate\Support\Carbon; */ class RuleTrigger extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + protected $casts = [ 'created_at' => 'datetime', @@ -72,7 +71,7 @@ class RuleTrigger extends Model 'stop_processing' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['rule_id', 'trigger_type', 'trigger_value', 'order', 'active', 'stop_processing']; /** @@ -82,4 +81,25 @@ class RuleTrigger extends Model { return $this->belongsTo(Rule::class); } + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function ruleId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 1e0e28fd14..9b5d35f1d8 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -23,7 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; @@ -32,7 +35,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -75,19 +77,17 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|Tag whereZoomLevel($value) * @method static Builder|Tag withTrashed() * @method static Builder|Tag withoutTrashed() - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|Tag whereUserGroupId($value) * @mixin Eloquent */ class Tag extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', @@ -98,7 +98,7 @@ class Tag extends Model 'latitude' => 'float', 'longitude' => 'float', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'description', 'tagMode']; protected $hidden = ['zoomLevel', 'latitude', 'longitude']; @@ -111,13 +111,13 @@ class Tag extends Model * @return Tag * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Tag + public static function routeBinder(string $value): self { if (auth()->check()) { $tagId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var Tag $tag */ + /** @var Tag|null $tag */ $tag = $user->tags()->find($tagId); if (null !== $tag) { return $tag; diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index b726594471..88f78c51dc 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -25,6 +25,7 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; @@ -37,31 +38,31 @@ use Illuminate\Database\Eloquent\SoftDeletes; /** * FireflyIII\Models\Transaction * - * @property int $id - * @property \Illuminate\Support\Carbon|null $created_at - * @property \Illuminate\Support\Carbon|null $updated_at - * @property \Illuminate\Support\Carbon|null $deleted_at - * @property bool $reconciled - * @property int $account_id - * @property int $transaction_journal_id - * @property string|null $description - * @property int|null $transaction_currency_id - * @property string $modified - * @property string $modified_foreign - * @property string $date - * @property string $max_date - * @property string $amount - * @property string|null $foreign_amount - * @property int|null $foreign_currency_id - * @property int $identifier - * @property-read Account $account - * @property-read Collection|Budget[] $budgets - * @property-read int|null $budgets_count - * @property-read Collection|Category[] $categories - * @property-read int|null $categories_count - * @property-read TransactionCurrency|null $foreignCurrency - * @property-read TransactionCurrency|null $transactionCurrency - * @property-read TransactionJournal $transactionJournal + * @property int $id + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property Carbon|null $deleted_at + * @property bool $reconciled + * @property int $account_id + * @property int $transaction_journal_id + * @property string|null $description + * @property int|null $transaction_currency_id + * @property string|int|null $modified + * @property string|int|null $modified_foreign + * @property string $date + * @property string $max_date + * @property string $amount + * @property string|null $foreign_amount + * @property int|null $foreign_currency_id + * @property int $identifier + * @property-read Account $account + * @property-read Collection|Budget[] $budgets + * @property-read int|null $budgets_count + * @property-read Collection|Category[] $categories + * @property-read int|null $categories_count + * @property-read TransactionCurrency|null $foreignCurrency + * @property-read TransactionCurrency|null $transactionCurrency + * @property-read TransactionJournal $transactionJournal * @method static Builder|Transaction after(Carbon $date) * @method static Builder|Transaction before(Carbon $date) * @method static Builder|Transaction newModelQuery() @@ -84,19 +85,16 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @method static Builder|Transaction whereUpdatedAt($value) * @method static \Illuminate\Database\Query\Builder|Transaction withTrashed() * @method static \Illuminate\Database\Query\Builder|Transaction withoutTrashed() - * @property int $the_count + * @property int|string $the_count * @mixin Eloquent */ class Transaction extends Model { - use SoftDeletes; use HasFactory; + use ReturnsIntegerIdTrait; + use SoftDeletes; + - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -107,7 +105,7 @@ class Transaction extends Model 'bill_name_encrypted' => 'boolean', 'reconciled' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = [ 'account_id', @@ -120,7 +118,7 @@ class Transaction extends Model 'foreign_amount', 'reconciled', ]; - /** @var array Hidden from view */ + protected $hidden = ['encrypted']; /** @@ -189,9 +187,7 @@ class Transaction extends Model public static function isJoined(Builder $query, string $table): bool { $joins = $query->getQuery()->joins; - if (null === $joins) { - return false; - } + foreach ($joins as $join) { if ($join->table === $table) { return true; @@ -258,6 +254,16 @@ class Transaction extends Model return $this->belongsTo(TransactionJournal::class); } + /** + * @return Attribute + */ + protected function accountId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + /** * Get the amount * @@ -266,7 +272,7 @@ class Transaction extends Model protected function amount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, ); } @@ -278,7 +284,17 @@ class Transaction extends Model protected function foreignAmount(): Attribute { return Attribute::make( - get: fn ($value) => (string)$value, + get: static fn($value) => (string)$value, + ); + } + + /** + * @return Attribute + */ + protected function transactionJournalId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, ); } } diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index 9825eb5562..ca84cce988 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -23,37 +23,39 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\TransactionCurrency * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property bool $enabled - * @property bool $userDefault - * @property bool $userEnabled - * @property string $code - * @property string $name - * @property string $symbol - * @property int $decimal_places - * @property-read Collection|BudgetLimit[] $budgetLimits - * @property-read int|null $budget_limits_count - * @property-read Collection|TransactionJournal[] $transactionJournals - * @property-read int|null $transaction_journals_count - * @property-read Collection|Transaction[] $transactions - * @property-read int|null $transactions_count + * @property int $id + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property Carbon|null $deleted_at + * @property bool $enabled + * @property bool|null $userDefault + * @property bool|null $userEnabled + * @property string $code + * @property string $name + * @property string $symbol + * @property int $decimal_places + * @property-read Collection|BudgetLimit[] $budgetLimits + * @property-read int|null $budget_limits_count + * @property-read Collection|TransactionJournal[] $transactionJournals + * @property-read int|null $transaction_journals_count + * @property-read Collection|Transaction[] $transactions + * @property-read int|null $transactions_count * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency newQuery() * @method static Builder|TransactionCurrency onlyTrashed() @@ -69,21 +71,19 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|TransactionCurrency whereUpdatedAt($value) * @method static Builder|TransactionCurrency withTrashed() * @method static Builder|TransactionCurrency withoutTrashed() - * @property-read Collection $userGroups - * @property-read int|null $user_groups_count - * @property-read Collection $users - * @property-read int|null $users_count + * @property-read Collection $userGroups + * @property-read int|null $user_groups_count + * @property-read Collection $users + * @property-read int|null $users_count * @mixin Eloquent */ class TransactionCurrency extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + public ?bool $userDefault; + public ?bool $userEnabled; protected $casts = [ 'created_at' => 'datetime', @@ -92,7 +92,7 @@ class TransactionCurrency extends Model 'decimal_places' => 'int', 'enabled' => 'bool', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['name', 'code', 'symbol', 'decimal_places', 'enabled']; /** @@ -103,7 +103,7 @@ class TransactionCurrency extends Model * @return TransactionCurrency * @throws NotFoundHttpException */ - public static function routeBinder(string $value): TransactionCurrency + public static function routeBinder(string $value): self { if (auth()->check()) { $currencyId = (int)$value; @@ -116,14 +116,6 @@ class TransactionCurrency extends Model throw new NotFoundHttpException(); } - /** - * @return HasMany - */ - public function budgetLimits(): HasMany - { - return $this->hasMany(BudgetLimit::class); - } - /** * @param User $user * @@ -133,10 +125,18 @@ class TransactionCurrency extends Model { $current = $user->userGroup->currencies()->where('transaction_currencies.id', $this->id)->first(); $default = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); - $this->userDefault = (int)$default->id === (int)$this->id; + $this->userDefault = $default->id === $this->id; $this->userEnabled = null !== $current; } + /** + * @return HasMany + */ + public function budgetLimits(): HasMany + { + return $this->hasMany(BudgetLimit::class); + } + /** * @return HasMany */ @@ -172,4 +172,14 @@ class TransactionCurrency extends Model { return $this->belongsToMany(User::class)->withTimestamps()->withPivot('user_default'); } + + /** + * @return Attribute + */ + protected function decimalPlaces(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index 06a58e9257..5cf4a599f6 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -23,7 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; @@ -31,8 +34,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; -use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -59,20 +60,18 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup whereUserId($value) * @method static Builder|TransactionGroup withTrashed() * @method static Builder|TransactionGroup withoutTrashed() - * @property int|null $user_group_id + * @property int $user_group_id * @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup whereUserGroupId($value) * @property-read UserGroup|null $userGroup * @mixin Eloquent */ class TransactionGroup extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'id' => 'integer', @@ -83,7 +82,7 @@ class TransactionGroup extends Model 'date' => 'datetime', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['user_id', 'user_group_id', 'title']; /** @@ -94,24 +93,24 @@ class TransactionGroup extends Model * @return TransactionGroup * @throws NotFoundHttpException */ - public static function routeBinder(string $value): TransactionGroup + public static function routeBinder(string $value): self { - Log::debug(sprintf('Now in %s("%s")', __METHOD__, $value)); + app('log')->debug(sprintf('Now in %s("%s")', __METHOD__, $value)); if (auth()->check()) { $groupId = (int)$value; /** @var User $user */ $user = auth()->user(); - Log::debug(sprintf('User authenticated as %s', $user->email)); - /** @var TransactionGroup $group */ + app('log')->debug(sprintf('User authenticated as %s', $user->email)); + /** @var TransactionGroup|null $group */ $group = $user->transactionGroups() ->with(['transactionJournals', 'transactionJournals.transactions']) ->where('transaction_groups.id', $groupId)->first(['transaction_groups.*']); if (null !== $group) { - Log::debug(sprintf('Found group #%d.', $group->id)); + app('log')->debug(sprintf('Found group #%d.', $group->id)); return $group; } } - Log::debug('Found no group.'); + app('log')->debug('Found no group.'); throw new NotFoundHttpException(); } diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 39a5bf5e5b..0a85d38b6e 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -25,9 +25,12 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -41,50 +44,50 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\TransactionJournal * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property int $user_id - * @property int $transaction_type_id - * @property int|null $transaction_group_id - * @property int|null $bill_id - * @property int|null $transaction_currency_id - * @property string $description - * @property Carbon $date - * @property Carbon|null $interest_date - * @property Carbon|null $book_date - * @property Carbon|null $process_date - * @property int $order - * @property int $tag_count - * @property string $transaction_type_type - * @property bool $encrypted - * @property bool $completed - * @property-read Collection|Attachment[] $attachments - * @property-read int|null $attachments_count - * @property-read Bill|null $bill - * @property-read Collection|Budget[] $budgets - * @property-read int|null $budgets_count - * @property-read Collection|Category[] $categories - * @property-read int|null $categories_count - * @property-read Collection|TransactionJournalLink[] $destJournalLinks - * @property-read int|null $dest_journal_links_count - * @property-read Collection|Note[] $notes - * @property-read int|null $notes_count - * @property-read Collection|PiggyBankEvent[] $piggyBankEvents - * @property-read int|null $piggy_bank_events_count - * @property-read Collection|TransactionJournalLink[] $sourceJournalLinks - * @property-read int|null $source_journal_links_count - * @property-read Collection|Tag[] $tags - * @property-read int|null $tags_count - * @property-read TransactionCurrency|null $transactionCurrency - * @property-read TransactionGroup|null $transactionGroup - * @property-read Collection|TransactionJournalMeta[] $transactionJournalMeta - * @property-read int|null $transaction_journal_meta_count - * @property-read TransactionType $transactionType - * @property-read Collection|Transaction[] $transactions - * @property-read int|null $transactions_count - * @property-read User $user + * @property int $id + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property Carbon|null $deleted_at + * @property int $user_id + * @property int $transaction_type_id + * @property int|string|null $transaction_group_id + * @property int|string|null $bill_id + * @property int|string|null $transaction_currency_id + * @property string|null $description + * @property Carbon $date + * @property Carbon|null $interest_date + * @property Carbon|null $book_date + * @property Carbon|null $process_date + * @property int $order + * @property int $tag_count + * @property string $transaction_type_type + * @property bool $encrypted + * @property bool $completed + * @property-read Collection|Attachment[] $attachments + * @property-read int|null $attachments_count + * @property-read Bill|null $bill + * @property-read Collection|Budget[] $budgets + * @property-read int|null $budgets_count + * @property-read Collection|Category[] $categories + * @property-read int|null $categories_count + * @property-read Collection|TransactionJournalLink[] $destJournalLinks + * @property-read int|null $dest_journal_links_count + * @property-read Collection|Note[] $notes + * @property-read int|null $notes_count + * @property-read Collection|PiggyBankEvent[] $piggyBankEvents + * @property-read int|null $piggy_bank_events_count + * @property-read Collection|TransactionJournalLink[] $sourceJournalLinks + * @property-read int|null $source_journal_links_count + * @property-read Collection|Tag[] $tags + * @property-read int|null $tags_count + * @property-read TransactionCurrency|null $transactionCurrency + * @property-read TransactionGroup|null $transactionGroup + * @property-read Collection|TransactionJournalMeta[] $transactionJournalMeta + * @property-read int|null $transaction_journal_meta_count + * @property-read TransactionType $transactionType + * @property-read Collection|Transaction[] $transactions + * @property-read int|null $transactions_count + * @property-read User $user * @method static EloquentBuilder|TransactionJournal after(Carbon $date) * @method static EloquentBuilder|TransactionJournal before(Carbon $date) * @method static EloquentBuilder|TransactionJournal newModelQuery() @@ -112,25 +115,23 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static EloquentBuilder|TransactionJournal whereUserId($value) * @method static \Illuminate\Database\Query\Builder|TransactionJournal withTrashed() * @method static \Illuminate\Database\Query\Builder|TransactionJournal withoutTrashed() - * @property-read Collection|Location[] $locations - * @property-read int|null $locations_count - * @property int $the_count - * @property int|null $user_group_id + * @property-read Collection|Location[] $locations + * @property-read int|null $locations_count + * @property int|string $the_count + * @property int $user_group_id * @method static EloquentBuilder|TransactionJournal whereUserGroupId($value) - * @property-read Collection $auditLogEntries - * @property-read int|null $audit_log_entries_count + * @property-read Collection $auditLogEntries + * @property-read int|null $audit_log_entries_count * @mixin Eloquent */ class TransactionJournal extends Model { - use SoftDeletes; use HasFactory; + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; + use SoftDeletes; + - /** - * The attributes that should be casted to native types. - * - * @var array - */ protected $casts = [ 'created_at' => 'datetime', @@ -146,7 +147,7 @@ class TransactionJournal extends Model 'completed' => 'boolean', ]; - /** @var array Fields that can be filled */ + protected $fillable = [ 'user_id', @@ -160,7 +161,7 @@ class TransactionJournal extends Model 'order', 'date', ]; - /** @var array Hidden from view */ + protected $hidden = ['encrypted']; /** @@ -171,13 +172,13 @@ class TransactionJournal extends Model * @return TransactionJournal * @throws NotFoundHttpException */ - public static function routeBinder(string $value): TransactionJournal + public static function routeBinder(string $value): self { if (auth()->check()) { $journalId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $journal */ $journal = $user->transactionJournals()->where('transaction_journals.id', $journalId)->first(['transaction_journals.*']); if (null !== $journal) { return $journal; @@ -330,9 +331,6 @@ class TransactionJournal extends Model public static function isJoined(Builder $query, string $table): bool { $joins = $query->getQuery()->joins; - if (null === $joins) { - return false; - } foreach ($joins as $join) { if ($join->table === $table) { return true; @@ -397,4 +395,24 @@ class TransactionJournal extends Model { return $this->hasMany(Transaction::class); } + + /** + * @return Attribute + */ + protected function order(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function transactionTypeId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/TransactionJournalLink.php b/app/Models/TransactionJournalLink.php index 30740c9990..6f5dfe0f91 100644 --- a/app/Models/TransactionJournalLink.php +++ b/app/Models/TransactionJournalLink.php @@ -23,13 +23,15 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphMany; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -63,11 +65,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class TransactionJournalLink extends Model { - /** - * The attributes that should be casted to native types. - * - * @var array - */ + use ReturnsIntegerIdTrait; + protected $casts = [ 'created_at' => 'datetime', @@ -85,7 +84,7 @@ class TransactionJournalLink extends Model * * @throws NotFoundHttpException */ - public static function routeBinder(string $value): TransactionJournalLink + public static function routeBinder(string $value): self { if (auth()->check()) { $linkId = (int)$value; @@ -133,4 +132,34 @@ class TransactionJournalLink extends Model { return $this->belongsTo(TransactionJournal::class, 'source_id'); } + + /** + * @return Attribute + */ + protected function destinationId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function linkTypeId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function sourceId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index 7c27a31e88..03b209d2a3 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -23,12 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; /** * FireflyIII\Models\TransactionJournalMeta @@ -60,20 +62,17 @@ use Illuminate\Support\Carbon; */ class TransactionJournalMeta extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', ]; - /** @var array Fields that can be filled */ + protected $fillable = ['transaction_journal_id', 'name', 'data', 'hash']; /** @var string The table to store the data in */ protected $table = 'journal_meta'; @@ -97,7 +96,7 @@ class TransactionJournalMeta extends Model { $data = json_encode($value); $this->attributes['data'] = $data; - $this->attributes['hash'] = hash('sha256', $data); + $this->attributes['hash'] = hash('sha256', (string)$data); } /** @@ -107,4 +106,14 @@ class TransactionJournalMeta extends Model { return $this->belongsTo(TransactionJournal::class); } + + /** + * @return Attribute + */ + protected function transactionJournalId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 3fc2d21efc..64c9414ef9 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -23,13 +23,14 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -57,15 +58,16 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class TransactionType extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; - public const DEPOSIT = 'Deposit'; - public const INVALID = 'Invalid'; - public const LIABILITY_CREDIT = 'Liability credit'; - public const OPENING_BALANCE = 'Opening balance'; - public const RECONCILIATION = 'Reconciliation'; - public const TRANSFER = 'Transfer'; - public const WITHDRAWAL = 'Withdrawal'; + public const string DEPOSIT = 'Deposit'; + public const string INVALID = 'Invalid'; + public const string LIABILITY_CREDIT = 'Liability credit'; + public const string OPENING_BALANCE = 'Opening balance'; + public const string RECONCILIATION = 'Reconciliation'; + public const string TRANSFER = 'Transfer'; + public const string WITHDRAWAL = 'Withdrawal'; protected $casts = [ @@ -83,7 +85,7 @@ class TransactionType extends Model * @return TransactionType * @throws NotFoundHttpException */ - public static function routeBinder(string $type): TransactionType + public static function routeBinder(string $type): self { if (!auth()->check()) { throw new NotFoundHttpException(); diff --git a/app/Models/UserGroup.php b/app/Models/UserGroup.php index 134e2a82da..bff29b6ab0 100644 --- a/app/Models/UserGroup.php +++ b/app/Models/UserGroup.php @@ -24,8 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; use FireflyIII\Enums\UserRoleEnum; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -33,19 +35,18 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class UserGroup * - * @property int $id - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property string|null $deleted_at - * @property string $title - * @property-read Collection|GroupMembership[] $groupMemberships - * @property-read int|null $group_memberships_count + * @property int $id + * @property Carbon|null $created_at + * @property Carbon|null $updated_at + * @property string|null $deleted_at + * @property string $title + * @property-read Collection|GroupMembership[] $groupMemberships + * @property-read int|null $group_memberships_count * @method static Builder|UserGroup newModelQuery() * @method static Builder|UserGroup newQuery() * @method static Builder|UserGroup query() @@ -54,8 +55,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static Builder|UserGroup whereId($value) * @method static Builder|UserGroup whereTitle($value) * @method static Builder|UserGroup whereUpdatedAt($value) - * @property-read Collection $accounts - * @property-read int|null $accounts_count + * @property-read Collection $accounts + * @property-read int|null $accounts_count * @property-read Collection $availableBudgets * @property-read int|null $available_budgets_count * @property-read Collection $bills @@ -77,21 +78,23 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property-read Collection $recurrences * @property-read int|null $recurrences_count * @property-read Collection $ruleGroups - * @property-read int|null $rule_groups_count - * @property-read Collection $rules - * @property-read int|null $rules_count - * @property-read Collection $tags - * @property-read int|null $tags_count - * @property-read Collection $transactionGroups - * @property-read int|null $transaction_groups_count - * @property-read Collection $webhooks - * @property-read int|null $webhooks_count - * @property-read Collection $currencies - * @property-read int|null $currencies_count + * @property-read int|null $rule_groups_count + * @property-read Collection $rules + * @property-read int|null $rules_count + * @property-read Collection $tags + * @property-read int|null $tags_count + * @property-read Collection $transactionGroups + * @property-read int|null $transaction_groups_count + * @property-read Collection $webhooks + * @property-read int|null $webhooks_count + * @property-read Collection $currencies + * @property-read int|null $currencies_count * @mixin Eloquent */ class UserGroup extends Model { + use ReturnsIntegerIdTrait; + protected $fillable = ['title']; /** @@ -102,20 +105,21 @@ class UserGroup extends Model * @return UserGroup * @throws NotFoundHttpException */ - public static function routeBinder(string $value): UserGroup + public static function routeBinder(string $value): self { if (auth()->check()) { $userGroupId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var UserGroup $userGroup */ - $userGroup = UserGroup::find($userGroupId); + /** @var UserGroup|null $userGroup */ + $userGroup = self::find($userGroupId); if (null === $userGroup) { throw new NotFoundHttpException(); } // need at least ready only to be aware of the user group's existence, // but owner/full role (in the group) or global owner role may overrule this. - if ($user->hasRoleInGroup($userGroup, UserRoleEnum::READ_ONLY, true, true)) { + $access = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::READ_ONLY) || $user->hasRole('owner'); + if ($access) { return $userGroup; } } diff --git a/app/Models/UserRole.php b/app/Models/UserRole.php index 5dbfa9b27c..7c902a3c61 100644 --- a/app/Models/UserRole.php +++ b/app/Models/UserRole.php @@ -24,12 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Support\Carbon; /** * Class UserRole @@ -53,6 +54,8 @@ use Illuminate\Support\Carbon; */ class UserRole extends Model { + use ReturnsIntegerIdTrait; + protected $fillable = ['title']; /** diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index 4d8a64bf7f..9d060a42cd 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -23,10 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; use FireflyIII\Enums\WebhookDelivery; use FireflyIII\Enums\WebhookResponse; use FireflyIII\Enums\WebhookTrigger; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -34,7 +37,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -73,12 +75,14 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property string $secret * @method static Builder|Webhook whereSecret($value) * @method static Builder|Webhook whereTitle($value) - * @property int|null $user_group_id + * @property int $user_group_id * @method static Builder|Webhook whereUserGroupId($value) * @mixin Eloquent */ class Webhook extends Model { + use ReturnsIntegerIdTrait; + use ReturnsIntegerUserIdTrait; use SoftDeletes; protected $casts @@ -179,13 +183,13 @@ class Webhook extends Model * @return Webhook * @throws NotFoundHttpException */ - public static function routeBinder(string $value): Webhook + public static function routeBinder(string $value): self { if (auth()->check()) { $webhookId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var Webhook $webhook */ + /** @var Webhook|null $webhook */ $webhook = $user->webhooks()->find($webhookId); if (null !== $webhook) { return $webhook; diff --git a/app/Models/WebhookAttempt.php b/app/Models/WebhookAttempt.php index 2dd236663e..c591bd469e 100644 --- a/app/Models/WebhookAttempt.php +++ b/app/Models/WebhookAttempt.php @@ -23,13 +23,15 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\User; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Query\Builder; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -40,7 +42,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property Carbon|null $updated_at * @property string|null $deleted_at * @property int $webhook_message_id - * @property int $status_code + * @property int|string $status_code * @property string|null $logs * @property string|null $response * @property-read WebhookMessage $webhookMessage @@ -62,6 +64,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class WebhookAttempt extends Model { + use ReturnsIntegerIdTrait; use SoftDeletes; /** @@ -72,13 +75,13 @@ class WebhookAttempt extends Model * @return WebhookAttempt * @throws NotFoundHttpException */ - public static function routeBinder(string $value): WebhookAttempt + public static function routeBinder(string $value): self { if (auth()->check()) { $attemptId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var WebhookAttempt $attempt */ + /** @var WebhookAttempt|null $attempt */ $attempt = self::find($attemptId); if (null !== $attempt && $attempt->webhookMessage->webhook->user_id === $user->id) { return $attempt; @@ -94,4 +97,14 @@ class WebhookAttempt extends Model { return $this->belongsTo(WebhookMessage::class); } + + /** + * @return Attribute + */ + protected function webhookMessageId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } } diff --git a/app/Models/WebhookMessage.php b/app/Models/WebhookMessage.php index f04bafae99..20c88b0da1 100644 --- a/app/Models/WebhookMessage.php +++ b/app/Models/WebhookMessage.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Eloquent; +use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; @@ -31,7 +33,6 @@ use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Support\Carbon; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -69,6 +70,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class WebhookMessage extends Model { + use ReturnsIntegerIdTrait; + protected $casts = [ 'sent' => 'boolean', @@ -86,13 +89,13 @@ class WebhookMessage extends Model * @return WebhookMessage * @throws NotFoundHttpException */ - public static function routeBinder(string $value): WebhookMessage + public static function routeBinder(string $value): self { if (auth()->check()) { $messageId = (int)$value; /** @var User $user */ $user = auth()->user(); - /** @var WebhookMessage $message */ + /** @var WebhookMessage|null $message */ $message = self::find($messageId); if (null !== $message && $message->webhook->user_id === $user->id) { return $message; @@ -125,7 +128,17 @@ class WebhookMessage extends Model protected function sent(): Attribute { return Attribute::make( - get: fn ($value) => (bool)$value, + get: static fn($value) => (bool)$value, + ); + } + + /** + * @return Attribute + */ + protected function webhookId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, ); } } diff --git a/app/Notifications/Admin/TestNotification.php b/app/Notifications/Admin/TestNotification.php index 5af18e03c9..3d3a276092 100644 --- a/app/Notifications/Admin/TestNotification.php +++ b/app/Notifications/Admin/TestNotification.php @@ -53,6 +53,7 @@ class TestNotification extends Notification * Get the array representation of the notification. * * @param mixed $notifiable + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return array */ @@ -67,6 +68,7 @@ class TestNotification extends Notification * Get the mail representation of the notification. * * @param mixed $notifiable + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return MailMessage */ @@ -81,6 +83,7 @@ class TestNotification extends Notification * Get the Slack representation of the notification. * * @param mixed $notifiable + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return SlackMessage */ @@ -91,6 +94,7 @@ class TestNotification extends Notification /** * Get the notification's delivery channels. + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @param mixed $notifiable * diff --git a/app/Notifications/Admin/UserInvitation.php b/app/Notifications/Admin/UserInvitation.php index 45384ca8d8..a9f05e7014 100644 --- a/app/Notifications/Admin/UserInvitation.php +++ b/app/Notifications/Admin/UserInvitation.php @@ -56,6 +56,7 @@ class UserInvitation extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -70,6 +71,7 @@ class UserInvitation extends Notification * @param mixed $notifiable * * @return MailMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { @@ -84,6 +86,7 @@ class UserInvitation extends Notification * @param mixed $notifiable * * @return SlackMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) { @@ -98,6 +101,7 @@ class UserInvitation extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { diff --git a/app/Notifications/Admin/UserRegistration.php b/app/Notifications/Admin/UserRegistration.php index 4b0bce8971..cccb74cb8a 100644 --- a/app/Notifications/Admin/UserRegistration.php +++ b/app/Notifications/Admin/UserRegistration.php @@ -56,6 +56,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -70,6 +71,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return MailMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { @@ -84,6 +86,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return SlackMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) { @@ -96,6 +99,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { diff --git a/app/Notifications/Admin/VersionCheckResult.php b/app/Notifications/Admin/VersionCheckResult.php index ac0c195457..49dfaffa66 100644 --- a/app/Notifications/Admin/VersionCheckResult.php +++ b/app/Notifications/Admin/VersionCheckResult.php @@ -56,6 +56,7 @@ class VersionCheckResult extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -70,6 +71,7 @@ class VersionCheckResult extends Notification * @param mixed $notifiable * * @return MailMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { @@ -84,11 +86,12 @@ class VersionCheckResult extends Notification * @param mixed $notifiable * * @return SlackMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) { return (new SlackMessage())->content($this->message) - ->attachment(function ($attachment) { + ->attachment(static function ($attachment) { $attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases'); }); } @@ -99,6 +102,7 @@ class VersionCheckResult extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { diff --git a/app/Notifications/User/BillReminder.php b/app/Notifications/User/BillReminder.php index d3824aca52..d017273a77 100644 --- a/app/Notifications/User/BillReminder.php +++ b/app/Notifications/User/BillReminder.php @@ -61,6 +61,7 @@ class BillReminder extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -75,6 +76,7 @@ class BillReminder extends Notification * @param mixed $notifiable * * @return MailMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { @@ -94,6 +96,7 @@ class BillReminder extends Notification * @param mixed $notifiable * * @return SlackMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) { @@ -105,7 +108,7 @@ class BillReminder extends Notification $url = route('bills.show', [$bill->id]); return (new SlackMessage()) ->warning() - ->attachment(function ($attachment) use ($bill, $url) { + ->attachment(static function ($attachment) use ($bill, $url) { $attachment->title((string)trans('firefly.visit_bill', ['name' => $bill->name]), $url); }) ->content($message); @@ -117,13 +120,17 @@ class BillReminder extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { /** @var User|null $user */ $user = auth()->user(); - $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (UrlValidator::isValidWebhookURL($slackUrl)) { + $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; + if (is_array($slackUrl)) { + $slackUrl = ''; + } + if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Notifications/User/NewAccessToken.php b/app/Notifications/User/NewAccessToken.php index 907d27df9e..6ee9e4ce80 100644 --- a/app/Notifications/User/NewAccessToken.php +++ b/app/Notifications/User/NewAccessToken.php @@ -43,9 +43,7 @@ class NewAccessToken extends Notification * * @return void */ - public function __construct() - { - } + public function __construct() {} /** * Get the array representation of the notification. @@ -53,6 +51,7 @@ class NewAccessToken extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -67,6 +66,7 @@ class NewAccessToken extends Notification * @param mixed $notifiable * * @return MailMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { @@ -81,6 +81,7 @@ class NewAccessToken extends Notification * @param mixed $notifiable * * @return SlackMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) { @@ -93,13 +94,17 @@ class NewAccessToken extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { /** @var User|null $user */ $user = auth()->user(); - $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (UrlValidator::isValidWebhookURL($slackUrl)) { + $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; + if (is_array($slackUrl)) { + $slackUrl = ''; + } + if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Notifications/User/RuleActionFailed.php b/app/Notifications/User/RuleActionFailed.php index caeef8a5b4..4f4ec4e48a 100644 --- a/app/Notifications/User/RuleActionFailed.php +++ b/app/Notifications/User/RuleActionFailed.php @@ -66,6 +66,7 @@ class RuleActionFailed extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -80,6 +81,7 @@ class RuleActionFailed extends Notification * @param mixed $notifiable * * @return SlackMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) { @@ -88,9 +90,9 @@ class RuleActionFailed extends Notification $ruleTitle = $this->ruleTitle; $ruleLink = $this->ruleLink; - return (new SlackMessage())->content($this->message)->attachment(function ($attachment) use ($groupTitle, $groupLink) { + return (new SlackMessage())->content($this->message)->attachment(static function ($attachment) use ($groupTitle, $groupLink) { $attachment->title((string)trans('rules.inspect_transaction', ['title' => $groupTitle]), $groupLink); - })->attachment(function ($attachment) use ($ruleTitle, $ruleLink) { + })->attachment(static function ($attachment) use ($ruleTitle, $ruleLink) { $attachment->title((string)trans('rules.inspect_rule', ['title' => $ruleTitle]), $ruleLink); }); } @@ -101,13 +103,17 @@ class RuleActionFailed extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { /** @var User|null $user */ $user = auth()->user(); - $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (UrlValidator::isValidWebhookURL($slackUrl)) { + $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; + if (is_array($slackUrl)) { + $slackUrl = ''; + } + if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { app('log')->debug('Will send ruleActionFailed through Slack!'); return ['slack']; } diff --git a/app/Notifications/User/TransactionCreation.php b/app/Notifications/User/TransactionCreation.php index ee41ce8e72..5eefc7717c 100644 --- a/app/Notifications/User/TransactionCreation.php +++ b/app/Notifications/User/TransactionCreation.php @@ -53,6 +53,7 @@ class TransactionCreation extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -67,12 +68,13 @@ class TransactionCreation extends Notification * @param mixed $notifiable * * @return MailMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { return (new MailMessage()) ->markdown('emails.report-new-journals', ['transformed' => $this->collection]) - ->subject((string)trans_choice('email.new_journals_subject', count($this->collection))); + ->subject(trans_choice('email.new_journals_subject', count($this->collection))); } /** @@ -81,6 +83,7 @@ class TransactionCreation extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { diff --git a/app/Notifications/User/UserLogin.php b/app/Notifications/User/UserLogin.php index b93436c782..5f0a22728e 100644 --- a/app/Notifications/User/UserLogin.php +++ b/app/Notifications/User/UserLogin.php @@ -31,7 +31,6 @@ use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; -use Illuminate\Support\Facades\Log; /** * Class UserLogin @@ -58,6 +57,7 @@ class UserLogin extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -72,6 +72,7 @@ class UserLogin extends Notification * @param mixed $notifiable * * @return MailMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { @@ -80,7 +81,7 @@ class UserLogin extends Notification try { $hostName = app('steam')->getHostName($this->ip); } catch (FireflyException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); $hostName = $this->ip; } if ($hostName !== $this->ip) { @@ -98,6 +99,7 @@ class UserLogin extends Notification * @param mixed $notifiable * * @return SlackMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toSlack($notifiable) { @@ -105,7 +107,7 @@ class UserLogin extends Notification try { $hostName = app('steam')->getHostName($this->ip); } catch (FireflyException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); $hostName = $this->ip; } if ($hostName !== $this->ip) { @@ -121,13 +123,17 @@ class UserLogin extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { /** @var User|null $user */ $user = auth()->user(); - $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; - if (UrlValidator::isValidWebhookURL($slackUrl)) { + $slackUrl = null === $user ? '' : app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; + if (is_array($slackUrl)) { + $slackUrl = ''; + } + if (UrlValidator::isValidWebhookURL((string)$slackUrl)) { return ['mail', 'slack']; } return ['mail']; diff --git a/app/Notifications/User/UserNewPassword.php b/app/Notifications/User/UserNewPassword.php index 4d69465717..bf0516fdb7 100644 --- a/app/Notifications/User/UserNewPassword.php +++ b/app/Notifications/User/UserNewPassword.php @@ -53,6 +53,7 @@ class UserNewPassword extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -67,6 +68,7 @@ class UserNewPassword extends Notification * @param mixed $notifiable * * @return MailMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { @@ -81,6 +83,7 @@ class UserNewPassword extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { diff --git a/app/Notifications/User/UserRegistration.php b/app/Notifications/User/UserRegistration.php index dab21b71ba..526065bec6 100644 --- a/app/Notifications/User/UserRegistration.php +++ b/app/Notifications/User/UserRegistration.php @@ -40,9 +40,7 @@ class UserRegistration extends Notification * * @return void */ - public function __construct() - { - } + public function __construct() {} /** * Get the array representation of the notification. @@ -50,6 +48,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toArray($notifiable) { @@ -64,6 +63,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return MailMessage + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function toMail($notifiable) { @@ -78,6 +78,7 @@ class UserRegistration extends Notification * @param mixed $notifiable * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function via($notifiable) { diff --git a/app/Providers/AccountServiceProvider.php b/app/Providers/AccountServiceProvider.php index e0e40daea8..3f18539064 100644 --- a/app/Providers/AccountServiceProvider.php +++ b/app/Providers/AccountServiceProvider.php @@ -42,9 +42,7 @@ class AccountServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -62,7 +60,7 @@ class AccountServiceProvider extends ServiceProvider { $this->app->bind( AccountRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepository::class); @@ -77,7 +75,7 @@ class AccountServiceProvider extends ServiceProvider $this->app->bind( AdminAccountRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var AdminAccountRepositoryInterface $repository */ $repository = app(AdminAccountRepository::class); @@ -113,7 +111,7 @@ class AccountServiceProvider extends ServiceProvider { $this->app->bind( AccountTaskerInterface::class, - function (Application $app) { + static function (Application $app) { /** @var AccountTaskerInterface $tasker */ $tasker = app(AccountTasker::class); diff --git a/app/Providers/AdminServiceProvider.php b/app/Providers/AdminServiceProvider.php index baa781b603..307efca1ff 100644 --- a/app/Providers/AdminServiceProvider.php +++ b/app/Providers/AdminServiceProvider.php @@ -36,9 +36,7 @@ class AdminServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -55,7 +53,7 @@ class AdminServiceProvider extends ServiceProvider { $this->app->bind( LinkTypeRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var LinkTypeRepository $repository */ $repository = app(LinkTypeRepository::class); // reference to auth is not understood by phpstan. diff --git a/app/Providers/AttachmentServiceProvider.php b/app/Providers/AttachmentServiceProvider.php index c6ba8cae34..6947d5d8a1 100644 --- a/app/Providers/AttachmentServiceProvider.php +++ b/app/Providers/AttachmentServiceProvider.php @@ -36,9 +36,7 @@ class AttachmentServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -47,7 +45,7 @@ class AttachmentServiceProvider extends ServiceProvider { $this->app->bind( AttachmentRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var AttachmentRepositoryInterface $repository */ $repository = app(AttachmentRepository::class); // reference to auth is not understood by phpstan. diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 331499dab6..9c7dfe117e 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -34,11 +34,6 @@ use Laravel\Passport\Passport; */ class AuthServiceProvider extends ServiceProvider { - /** - * The policy mappings for the application. - * - * @var array - */ protected $policies = [ // 'FireflyIII\Model' => 'FireflyIII\Policies\ModelPolicy', @@ -46,6 +41,7 @@ class AuthServiceProvider extends ServiceProvider /** * Register any authentication / authorization services. + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return void */ @@ -53,7 +49,7 @@ class AuthServiceProvider extends ServiceProvider { Auth::provider( 'remote_user_provider', - function ($app, array $config) { + static function ($app, array $config) { return new RemoteUserProvider(); } ); diff --git a/app/Providers/BillServiceProvider.php b/app/Providers/BillServiceProvider.php index dac9f83a73..409b3ba4a7 100644 --- a/app/Providers/BillServiceProvider.php +++ b/app/Providers/BillServiceProvider.php @@ -38,9 +38,7 @@ class BillServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -49,7 +47,7 @@ class BillServiceProvider extends ServiceProvider { $this->app->bind( BillRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var BillRepositoryInterface $repository */ $repository = app(BillRepository::class); @@ -65,7 +63,7 @@ class BillServiceProvider extends ServiceProvider // administration variant $this->app->bind( AdminBillRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var AdminBillRepositoryInterface $repository */ $repository = app(AdminBillRepository::class); diff --git a/app/Providers/BudgetServiceProvider.php b/app/Providers/BudgetServiceProvider.php index 6f7b2ab1d0..c48674fd7f 100644 --- a/app/Providers/BudgetServiceProvider.php +++ b/app/Providers/BudgetServiceProvider.php @@ -50,9 +50,7 @@ class BudgetServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. diff --git a/app/Providers/CategoryServiceProvider.php b/app/Providers/CategoryServiceProvider.php index c5c27b54b7..d8bc20a299 100644 --- a/app/Providers/CategoryServiceProvider.php +++ b/app/Providers/CategoryServiceProvider.php @@ -40,9 +40,7 @@ class CategoryServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. diff --git a/app/Providers/CurrencyServiceProvider.php b/app/Providers/CurrencyServiceProvider.php index 47b9070c33..6643cfa4c4 100644 --- a/app/Providers/CurrencyServiceProvider.php +++ b/app/Providers/CurrencyServiceProvider.php @@ -38,9 +38,7 @@ class CurrencyServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -49,7 +47,7 @@ class CurrencyServiceProvider extends ServiceProvider { $this->app->bind( CurrencyRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var CurrencyRepository $repository */ $repository = app(CurrencyRepository::class); // phpstan does not get the reference to auth @@ -62,7 +60,7 @@ class CurrencyServiceProvider extends ServiceProvider ); $this->app->bind( GroupCurrencyRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var GroupCurrencyRepository $repository */ $repository = app(GroupCurrencyRepository::class); // phpstan does not get the reference to auth diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 0d93ba7b23..8df7fe412a 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -91,11 +91,6 @@ use Laravel\Passport\Events\AccessTokenCreated; */ class EventServiceProvider extends ServiceProvider { - /** - * The event listener mappings for the application. - * - * @var array - */ protected $listen = [ // is a User related event. diff --git a/app/Providers/FireflySessionProvider.php b/app/Providers/FireflySessionProvider.php index f07474003c..3ffeb23a0d 100644 --- a/app/Providers/FireflySessionProvider.php +++ b/app/Providers/FireflySessionProvider.php @@ -51,7 +51,7 @@ class FireflySessionProvider extends ServiceProvider { $this->app->singleton( 'session', - function ($app) { + static function ($app) { return new SessionManager($app); } ); @@ -64,7 +64,7 @@ class FireflySessionProvider extends ServiceProvider { $this->app->singleton( 'session.store', - function ($app) { + static function ($app) { // First, we will create the session manager which is responsible for the // creation of the various session drivers when they are needed by the // application instance, and will resolve them on a lazy load basis. diff --git a/app/Providers/JournalServiceProvider.php b/app/Providers/JournalServiceProvider.php index 0dee21d3b6..50235ad37e 100644 --- a/app/Providers/JournalServiceProvider.php +++ b/app/Providers/JournalServiceProvider.php @@ -46,9 +46,7 @@ class JournalServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. diff --git a/app/Providers/PiggyBankServiceProvider.php b/app/Providers/PiggyBankServiceProvider.php index 4c60ba9ff5..309650aaf8 100644 --- a/app/Providers/PiggyBankServiceProvider.php +++ b/app/Providers/PiggyBankServiceProvider.php @@ -38,9 +38,7 @@ class PiggyBankServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -49,7 +47,7 @@ class PiggyBankServiceProvider extends ServiceProvider { $this->app->bind( PiggyBankRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var PiggyBankRepository $repository */ $repository = app(PiggyBankRepository::class); if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth) @@ -62,7 +60,7 @@ class PiggyBankServiceProvider extends ServiceProvider $this->app->bind( AdminPiggyBankRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var AdminPiggyBankRepository $repository */ $repository = app(AdminPiggyBankRepository::class); if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth) diff --git a/app/Providers/RecurringServiceProvider.php b/app/Providers/RecurringServiceProvider.php index b954885476..884dc1f036 100644 --- a/app/Providers/RecurringServiceProvider.php +++ b/app/Providers/RecurringServiceProvider.php @@ -36,9 +36,7 @@ class RecurringServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -47,7 +45,7 @@ class RecurringServiceProvider extends ServiceProvider { $this->app->bind( RecurringRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var RecurringRepositoryInterface $repository */ $repository = app(RecurringRepository::class); diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 2950640f53..ebe9eb2c0f 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -31,19 +31,7 @@ use Illuminate\Support\Facades\Route; */ class RouteServiceProvider extends ServiceProvider { - /** - * The path to the "home" route for your application. - * - * @var string - */ - public const HOME = '/'; - /** - * This namespace is applied to your controller routes. - * - * In addition, it is set as the URL generator's root namespace. - * - * @var string - */ + public const string HOME = '/'; protected $namespace = ''; /** diff --git a/app/Providers/RuleGroupServiceProvider.php b/app/Providers/RuleGroupServiceProvider.php index 01297995ae..e43ee595e2 100644 --- a/app/Providers/RuleGroupServiceProvider.php +++ b/app/Providers/RuleGroupServiceProvider.php @@ -36,9 +36,7 @@ class RuleGroupServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -47,7 +45,7 @@ class RuleGroupServiceProvider extends ServiceProvider { $this->app->bind( RuleGroupRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var RuleGroupRepository $repository */ $repository = app(RuleGroupRepository::class); if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth) diff --git a/app/Providers/RuleServiceProvider.php b/app/Providers/RuleServiceProvider.php index 62dfb0e3e7..9073b9cc7d 100644 --- a/app/Providers/RuleServiceProvider.php +++ b/app/Providers/RuleServiceProvider.php @@ -36,9 +36,7 @@ class RuleServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -47,7 +45,7 @@ class RuleServiceProvider extends ServiceProvider { $this->app->bind( RuleRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var RuleRepository $repository */ $repository = app(RuleRepository::class); if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth) diff --git a/app/Providers/SearchServiceProvider.php b/app/Providers/SearchServiceProvider.php index 3bdbcdf5e6..0bb2b1056b 100644 --- a/app/Providers/SearchServiceProvider.php +++ b/app/Providers/SearchServiceProvider.php @@ -36,9 +36,7 @@ class SearchServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -47,7 +45,7 @@ class SearchServiceProvider extends ServiceProvider { $this->app->bind( SearchInterface::class, - function (Application $app) { + static function (Application $app) { /** @var OperatorQuerySearch $search */ $search = app(OperatorQuerySearch::class); if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth) diff --git a/app/Providers/TagServiceProvider.php b/app/Providers/TagServiceProvider.php index 17f5e107d2..ae29a28caa 100644 --- a/app/Providers/TagServiceProvider.php +++ b/app/Providers/TagServiceProvider.php @@ -38,9 +38,7 @@ class TagServiceProvider extends ServiceProvider /** * Bootstrap the application services. */ - public function boot(): void - { - } + public function boot(): void {} /** * Register the application services. @@ -49,7 +47,7 @@ class TagServiceProvider extends ServiceProvider { $this->app->bind( TagRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var TagRepository $repository */ $repository = app(TagRepository::class); @@ -63,7 +61,7 @@ class TagServiceProvider extends ServiceProvider $this->app->bind( OperationsRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var OperationsRepository $repository */ $repository = app(OperationsRepository::class); diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 80bdd00a9e..d41fbe3bc6 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -42,7 +42,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; use Storage; @@ -117,7 +116,7 @@ class AccountRepository implements AccountRepositoryInterface ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') ->where('accounts.active', true) ->where( - function (EloquentBuilder $q1) use ($number) { + static function (EloquentBuilder $q1) use ($number) { // @phpstan-ignore-line $json = json_encode($number); $q1->where('account_meta.name', '=', 'account_number'); $q1->where('account_meta.data', '=', $json); @@ -165,17 +164,17 @@ class AccountRepository implements AccountRepositoryInterface $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); $query->whereIn('account_types.type', $types); } - Log::debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]); + app('log')->debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]); $query->where('accounts.name', $name); - /** @var Account $account */ + /** @var Account|null $account */ $account = $query->first(['accounts.*']); if (null === $account) { - Log::debug(sprintf('There is no account with name "%s" of types', $name), $types); + app('log')->debug(sprintf('There is no account with name "%s" of types', $name), $types); return null; } - Log::debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id)); + app('log')->debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id)); return $account; } @@ -220,7 +219,7 @@ class AccountRepository implements AccountRepositoryInterface { $query = $this->user->accounts()->with( [ - 'accountmeta' => function (HasMany $query) { + 'accountmeta' => static function (HasMany $query) { $query->where('name', 'account_role'); }, 'attachments', @@ -251,7 +250,7 @@ class AccountRepository implements AccountRepositoryInterface static function (Attachment $attachment) use ($disk) { $notes = $attachment->notes()->first(); $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; + $attachment->notes_text = null !== $notes ? $notes->text : ''; return $attachment; } @@ -280,7 +279,7 @@ class AccountRepository implements AccountRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -306,7 +305,7 @@ class AccountRepository implements AccountRepositoryInterface { $query = $this->user->accounts()->with( [ - 'accountmeta' => function (HasMany $query) { + 'accountmeta' => static function (HasMany $query) { $query->where('name', 'account_role'); }, ] @@ -364,7 +363,7 @@ class AccountRepository implements AccountRepositoryInterface return null; } - return (string)$transaction->amount; + return $transaction->amount; } /** @@ -434,12 +433,13 @@ class AccountRepository implements AccountRepositoryInterface $name = trans('firefly.reconciliation_account_name', ['name' => $account->name, 'currency' => $currency->code]); /** @var AccountType $type */ - $type = AccountType::where('type', AccountType::RECONCILIATION)->first(); + $type = AccountType::where('type', AccountType::RECONCILIATION)->first(); + + /** @var Account|null $current */ $current = $this->user->accounts()->where('account_type_id', $type->id) ->where('name', $name) ->first(); - /** @var Account $current */ if (null !== $current) { return $current; } @@ -493,7 +493,7 @@ class AccountRepository implements AccountRepositoryInterface public function getMetaValue(Account $account, string $field): ?string { $result = $account->accountMeta->filter( - function (AccountMeta $meta) use ($field) { + static function (AccountMeta $meta) use ($field) { return strtolower($meta->name) === strtolower($field); } ); @@ -568,14 +568,14 @@ class AccountRepository implements AccountRepositoryInterface ]; if (array_key_exists(ucfirst($type), $sets)) { $order = (int)$this->getAccountsByType($sets[ucfirst($type)])->max('order'); - Log::debug(sprintf('Return max order of "%s" set: %d', $type, $order)); + app('log')->debug(sprintf('Return max order of "%s" set: %d', $type, $order)); return $order; } $specials = [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION]; $order = (int)$this->getAccountsByType($specials)->max('order'); - Log::debug(sprintf('Return max order of "%s" set (specials!): %d', $type, $order)); + app('log')->debug(sprintf('Return max order of "%s" set (specials!): %d', $type, $order)); return $order; } @@ -635,6 +635,7 @@ class AccountRepository implements AccountRepositoryInterface */ public function oldestJournal(Account $account): ?TransactionJournal { + /** @var TransactionJournal|null $first */ $first = $account->transactions() ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->orderBy('transaction_journals.date', 'ASC') @@ -643,7 +644,7 @@ class AccountRepository implements AccountRepositoryInterface ->orderBy('transaction_journals.id', 'ASC') ->first(['transaction_journals.id']); if (null !== $first) { - return TransactionJournal::find((int)$first->id); + return TransactionJournal::find($first->id); } return null; @@ -670,7 +671,7 @@ class AccountRepository implements AccountRepositoryInterface continue; } if ($index !== (int)$account->order) { - Log::debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order)); + app('log')->debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order)); $account->order = $index; $account->save(); } @@ -728,10 +729,10 @@ class AccountRepository implements AccountRepositoryInterface foreach ($parts as $part) { $search = sprintf('%%%s%%', $part); $dbQuery->where( - function (EloquentBuilder $q1) use ($search) { + static function (EloquentBuilder $q1) use ($search) { // @phpstan-ignore-line $q1->where('accounts.iban', 'LIKE', $search); $q1->orWhere( - function (EloquentBuilder $q2) use ($search) { + static function (EloquentBuilder $q2) use ($search) { $q2->where('account_meta.name', '=', 'account_number'); $q2->where('account_meta.data', 'LIKE', $search); } diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index 4e71af2b63..3c2b7d8d92 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -123,8 +123,8 @@ interface AccountRepositoryInterface public function getAccountsById(array $accountIds): Collection; /** - * @param array $types - * @param array|null $sort + * @param array $types + * @param array|null $sort * * @return Collection */ diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index e30e983ef8..45df92a6cb 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -32,7 +32,6 @@ use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -57,11 +56,11 @@ class AccountTasker implements AccountTaskerInterface $yesterday->subDay(); $startSet = app('steam')->balancesByAccounts($accounts, $yesterday); $endSet = app('steam')->balancesByAccounts($accounts, $end); - Log::debug('Start of accountreport'); + app('log')->debug('Start of accountreport'); /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); - $defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user); + $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); $return = [ 'accounts' => [], @@ -72,7 +71,7 @@ class AccountTasker implements AccountTaskerInterface foreach ($accounts as $account) { $id = $account->id; $currency = $repository->getAccountCurrency($account) ?? $defaultCurrency; - $return['sums'][$currency->id] = $return['sums'][$currency->id] ?? [ + $return['sums'][$currency->id] ??= [ 'start' => '0', 'end' => '0', 'difference' => '0', @@ -99,9 +98,9 @@ class AccountTasker implements AccountTaskerInterface // first journal exists, and is on start, then this is the actual opening balance: if (null !== $first && $first->date->isSameDay($start) && TransactionType::OPENING_BALANCE === $first->transactionType->type) { - Log::debug(sprintf('Date of first journal for %s is %s', $account->name, $first->date->format('Y-m-d'))); + app('log')->debug(sprintf('Date of first journal for %s is %s', $account->name, $first->date->format('Y-m-d'))); $entry['start_balance'] = $first->transactions()->where('account_id', $account->id)->first()->amount; - Log::debug(sprintf('Account %s was opened on %s, so opening balance is %f', $account->name, $start->format('Y-m-d'), $entry['start_balance'])); + app('log')->debug(sprintf('Account %s was opened on %s, so opening balance is %f', $account->name, $start->format('Y-m-d'), $entry['start_balance'])); } $return['sums'][$currency->id]['start'] = bcadd($return['sums'][$currency->id]['start'], $entry['start_balance']); $return['sums'][$currency->id]['end'] = bcadd($return['sums'][$currency->id]['end'], $entry['end_balance']); @@ -161,7 +160,7 @@ class AccountTasker implements AccountTaskerInterface */ private function groupExpenseByDestination(array $array): array { - $defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user); + $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); /** @var CurrencyRepositoryInterface $currencyRepos */ $currencyRepos = app(CurrencyRepositoryInterface::class); $currencies = [$defaultCurrency->id => $defaultCurrency,]; @@ -175,8 +174,8 @@ class AccountTasker implements AccountTaskerInterface $sourceId = (int)$journal['destination_account_id']; $currencyId = (int)$journal['currency_id']; $key = sprintf('%s-%s', $sourceId, $currencyId); - $currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepos->find($currencyId); - $report['accounts'][$key] = $report['accounts'][$key] ?? [ + $currencies[$currencyId] ??= $currencyRepos->find($currencyId); + $report['accounts'][$key] ??= [ 'id' => $sourceId, 'name' => $journal['destination_account_name'], 'sum' => '0', @@ -190,7 +189,7 @@ class AccountTasker implements AccountTaskerInterface ]; $report['accounts'][$key]['sum'] = bcadd($report['accounts'][$key]['sum'], $journal['amount']); - Log::debug(sprintf('Sum for %s is now %s', $journal['destination_account_name'], $report['accounts'][$key]['sum'])); + app('log')->debug(sprintf('Sum for %s is now %s', $journal['destination_account_name'], $report['accounts'][$key]['sum'])); ++$report['accounts'][$key]['count']; } @@ -201,7 +200,7 @@ class AccountTasker implements AccountTaskerInterface $report['accounts'][$key]['average'] = bcdiv($report['accounts'][$key]['sum'], (string)$report['accounts'][$key]['count']); } $currencyId = $report['accounts'][$key]['currency_id']; - $report['sums'][$currencyId] = $report['sums'][$currencyId] ?? [ + $report['sums'][$currencyId] ??= [ 'sum' => '0', 'currency_id' => $report['accounts'][$key]['currency_id'], 'currency_name' => $report['accounts'][$key]['currency_name'], @@ -258,7 +257,7 @@ class AccountTasker implements AccountTaskerInterface */ private function groupIncomeBySource(array $array): array { - $defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user); + $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); /** @var CurrencyRepositoryInterface $currencyRepos */ $currencyRepos = app(CurrencyRepositoryInterface::class); $currencies = [$defaultCurrency->id => $defaultCurrency,]; @@ -273,7 +272,7 @@ class AccountTasker implements AccountTaskerInterface $currencyId = (int)$journal['currency_id']; $key = sprintf('%s-%s', $sourceId, $currencyId); if (!array_key_exists($key, $report['accounts'])) { - $currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepos->find($currencyId); + $currencies[$currencyId] ??= $currencyRepos->find($currencyId); $report['accounts'][$key] = [ 'id' => $sourceId, 'name' => $journal['source_account_name'], @@ -297,7 +296,7 @@ class AccountTasker implements AccountTaskerInterface $report['accounts'][$key]['average'] = bcdiv($report['accounts'][$key]['sum'], (string)$report['accounts'][$key]['count']); } $currencyId = $report['accounts'][$key]['currency_id']; - $report['sums'][$currencyId] = $report['sums'][$currencyId] ?? [ + $report['sums'][$currencyId] ??= [ 'sum' => '0', 'currency_id' => $report['accounts'][$key]['currency_id'], 'currency_name' => $report['accounts'][$key]['currency_name'], @@ -316,7 +315,7 @@ class AccountTasker implements AccountTaskerInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } diff --git a/app/Repositories/Account/OperationsRepository.php b/app/Repositories/Account/OperationsRepository.php index df5a0fa99e..6fa75b60bc 100644 --- a/app/Repositories/Account/OperationsRepository.php +++ b/app/Repositories/Account/OperationsRepository.php @@ -83,7 +83,7 @@ class OperationsRepository implements OperationsRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -100,7 +100,7 @@ class OperationsRepository implements OperationsRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; $journalId = (int)$journal['transaction_journal_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'currency_id' => $journal['currency_id'], 'currency_name' => $journal['currency_name'], @@ -111,7 +111,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; $array[$currencyId]['transaction_journals'][$journalId] = [ - 'amount' => app('steam')->$direction((string)$journal['amount']), + 'amount' => app('steam')->$direction((string)$journal['amount']), // @phpstan-ignore-line 'date' => $journal['date'], 'transaction_journal_id' => $journalId, 'budget_name' => $journal['budget_name'], @@ -151,6 +151,7 @@ class OperationsRepository implements OperationsRepositoryInterface /** * @inheritDoc + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpenses( Carbon $start, @@ -158,7 +159,8 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null - ): array { + ): array + { $journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency); return $this->groupByCurrency($journals, 'negative'); @@ -173,6 +175,7 @@ class OperationsRepository implements OperationsRepositoryInterface * @param string $type * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ private function getTransactionsForSum( string $type, @@ -181,7 +184,8 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $opposing = null, ?TransactionCurrency $currency = null - ): array { + ): array + { $start->startOfDay(); $end->endOfDay(); @@ -260,7 +264,7 @@ class OperationsRepository implements OperationsRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -268,12 +272,12 @@ class OperationsRepository implements OperationsRepositoryInterface 'currency_code' => $journal['currency_code'], 'currency_decimal_places' => $journal['currency_decimal_places'], ]; - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->$direction($journal['amount'])); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->$direction($journal['amount'])); // @phpstan-ignore-line // also do foreign amount: $foreignId = (int)$journal['foreign_currency_id']; if (0 !== $foreignId) { - $array[$foreignId] = $array[$foreignId] ?? [ + $array[$foreignId] ??= [ 'sum' => '0', 'currency_id' => $foreignId, 'currency_name' => $journal['foreign_currency_name'], @@ -281,7 +285,7 @@ class OperationsRepository implements OperationsRepositoryInterface 'currency_code' => $journal['foreign_currency_code'], 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], ]; - $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->$direction($journal['foreign_amount'])); + $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->$direction($journal['foreign_amount']));// @phpstan-ignore-line } } @@ -290,6 +294,7 @@ class OperationsRepository implements OperationsRepositoryInterface /** * @inheritDoc + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpensesByDestination( Carbon $start, @@ -297,7 +302,8 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null - ): array { + ): array + { $journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency); return $this->groupByDirection($journals, 'destination', 'negative'); @@ -318,7 +324,7 @@ class OperationsRepository implements OperationsRepositoryInterface foreach ($journals as $journal) { $key = sprintf('%s-%s', $journal[$idKey], $journal['currency_id']); - $array[$key] = $array[$key] ?? [ + $array[$key] ??= [ 'id' => $journal[$idKey], 'name' => $journal[$nameKey], 'sum' => '0', @@ -328,12 +334,12 @@ class OperationsRepository implements OperationsRepositoryInterface 'currency_code' => $journal['currency_code'], 'currency_decimal_places' => $journal['currency_decimal_places'], ]; - $array[$key]['sum'] = bcadd($array[$key]['sum'], app('steam')->$method((string)$journal['amount'])); + $array[$key]['sum'] = bcadd($array[$key]['sum'], app('steam')->$method((string)$journal['amount']));// @phpstan-ignore-line // also do foreign amount: if (0 !== (int)$journal['foreign_currency_id']) { $key = sprintf('%s-%s', $journal[$idKey], $journal['foreign_currency_id']); - $array[$key] = $array[$key] ?? [ + $array[$key] ??= [ 'id' => $journal[$idKey], 'name' => $journal[$nameKey], 'sum' => '0', @@ -343,7 +349,7 @@ class OperationsRepository implements OperationsRepositoryInterface 'currency_code' => $journal['foreign_currency_code'], 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], ]; - $array[$key]['sum'] = bcadd($array[$key]['sum'], app('steam')->$method((string)$journal['foreign_amount'])); + $array[$key]['sum'] = bcadd($array[$key]['sum'], app('steam')->$method((string)$journal['foreign_amount']));// @phpstan-ignore-line } } @@ -352,6 +358,7 @@ class OperationsRepository implements OperationsRepositoryInterface /** * @inheritDoc + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpensesBySource( Carbon $start, @@ -359,7 +366,8 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null - ): array { + ): array + { $journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency); return $this->groupByDirection($journals, 'source', 'negative'); @@ -367,6 +375,7 @@ class OperationsRepository implements OperationsRepositoryInterface /** * @inheritDoc + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumIncome( Carbon $start, @@ -374,7 +383,8 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null - ): array { + ): array + { $journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency); return $this->groupByCurrency($journals, 'positive'); @@ -382,6 +392,7 @@ class OperationsRepository implements OperationsRepositoryInterface /** * @inheritDoc + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumIncomeByDestination( Carbon $start, @@ -389,7 +400,8 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null - ): array { + ): array + { $journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency); return $this->groupByDirection($journals, 'destination', 'positive'); @@ -397,6 +409,7 @@ class OperationsRepository implements OperationsRepositoryInterface /** * @inheritDoc + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumIncomeBySource( Carbon $start, @@ -404,7 +417,8 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null - ): array { + ): array + { $journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency); return $this->groupByDirection($journals, 'source', 'positive'); @@ -459,7 +473,7 @@ class OperationsRepository implements OperationsRepositoryInterface $amount = app('steam')->positive($journal['amount']); // source first - $return[$sourceKey] = $return[$sourceKey] ?? [ + $return[$sourceKey] ??= [ 'id' => (string)$sourceId, 'name' => $journal['source_account_name'], 'difference' => '0', @@ -473,7 +487,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; // dest next: - $return[$destKey] = $return[$destKey] ?? [ + $return[$destKey] ??= [ 'id' => (string)$destinationId, 'name' => $journal['destination_account_name'], 'difference' => '0', @@ -503,7 +517,7 @@ class OperationsRepository implements OperationsRepositoryInterface // same as above: // source first - $return[$sourceKey] = $return[$sourceKey] ?? [ + $return[$sourceKey] ??= [ 'id' => (string)$sourceId, 'name' => $journal['source_account_name'], 'difference' => '0', @@ -517,7 +531,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; // dest next: - $return[$destKey] = $return[$destKey] ?? [ + $return[$destKey] ??= [ 'id' => (string)$destinationId, 'name' => $journal['destination_account_name'], 'difference' => '0', diff --git a/app/Repositories/Account/OperationsRepositoryInterface.php b/app/Repositories/Account/OperationsRepositoryInterface.php index 3e02b58cb8..43c6d65905 100644 --- a/app/Repositories/Account/OperationsRepositoryInterface.php +++ b/app/Repositories/Account/OperationsRepositoryInterface.php @@ -75,6 +75,7 @@ interface OperationsRepositoryInterface * @param TransactionCurrency|null $currency * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpenses( Carbon $start, @@ -95,6 +96,7 @@ interface OperationsRepositoryInterface * @param TransactionCurrency|null $currency * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpensesByDestination( Carbon $start, @@ -115,6 +117,7 @@ interface OperationsRepositoryInterface * @param TransactionCurrency|null $currency * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpensesBySource( Carbon $start, @@ -134,6 +137,7 @@ interface OperationsRepositoryInterface * @param TransactionCurrency|null $currency * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumIncome( Carbon $start, @@ -154,6 +158,7 @@ interface OperationsRepositoryInterface * @param TransactionCurrency|null $currency * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumIncomeByDestination( Carbon $start, @@ -174,6 +179,7 @@ interface OperationsRepositoryInterface * @param TransactionCurrency|null $currency * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumIncomeBySource( Carbon $start, diff --git a/app/Repositories/Attachment/AttachmentRepository.php b/app/Repositories/Attachment/AttachmentRepository.php index 3b65837c20..e3c0392007 100644 --- a/app/Repositories/Attachment/AttachmentRepository.php +++ b/app/Repositories/Attachment/AttachmentRepository.php @@ -34,7 +34,6 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use League\Flysystem\UnableToDeleteFile; use LogicException; @@ -87,7 +86,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface try { $unencryptedContent = Crypt::decrypt($encryptedContent); // verified } catch (DecryptException $e) { - Log::debug(sprintf('Could not decrypt attachment #%d but this is fine: %s', $attachment->id, $e->getMessage())); + app('log')->debug(sprintf('Could not decrypt attachment #%d but this is fine: %s', $attachment->id, $e->getMessage())); $unencryptedContent = $encryptedContent; } } @@ -157,7 +156,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -207,7 +206,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface try { $dbNote->delete(); } catch (LogicException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); } } diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index cc3f91b14c..356542d0b2 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -43,7 +43,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Query\JoinClause; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; use Storage; @@ -95,7 +94,7 @@ class BillRepository implements BillRepositoryInterface $set = $this->user->bills()->orderBy('order', 'ASC')->get(); $current = 1; foreach ($set as $bill) { - if ((int)$bill->order !== $current) { + if ($bill->order !== $current) { $bill->order = $current; $bill->save(); } @@ -138,22 +137,22 @@ class BillRepository implements BillRepositoryInterface public function findBill(?int $billId, ?string $billName): ?Bill { if (null !== $billId) { - $searchResult = $this->find((int)$billId); + $searchResult = $this->find($billId); if (null !== $searchResult) { - Log::debug(sprintf('Found bill based on #%d, will return it.', $billId)); + app('log')->debug(sprintf('Found bill based on #%d, will return it.', $billId)); return $searchResult; } } if (null !== $billName) { - $searchResult = $this->findByName((string)$billName); + $searchResult = $this->findByName($billName); if (null !== $searchResult) { - Log::debug(sprintf('Found bill based on "%s", will return it.', $billName)); + app('log')->debug(sprintf('Found bill based on "%s", will return it.', $billName)); return $searchResult; } } - Log::debug('Found nothing'); + app('log')->debug('Found nothing'); return null; } @@ -200,7 +199,7 @@ class BillRepository implements BillRepositoryInterface static function (Attachment $attachment) use ($disk) { $notes = $attachment->notes()->first(); $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; + $attachment->notes_text = null !== $notes ? $notes->text : ''; return $attachment; } @@ -285,13 +284,10 @@ class BillRepository implements BillRepositoryInterface */ public function getNoteText(Bill $bill): string { - /** @var Note $note */ + /** @var Note|null $note */ $note = $bill->notes()->first(); - if (null !== $note) { - return (string)$note->text; - } + return (string)$note?->text; - return ''; } /** @@ -315,7 +311,7 @@ class BillRepository implements BillRepositoryInterface $transaction = $journal->transactions()->where('amount', '<', 0)->first(); $currencyId = (int)$journal->transaction_currency_id; $currency = $journal->transactionCurrency; - $result[$currencyId] = $result[$currencyId] ?? [ + $result[$currencyId] ??= [ 'sum' => '0', 'count' => 0, 'avg' => '0', @@ -345,7 +341,7 @@ class BillRepository implements BillRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -373,7 +369,7 @@ class BillRepository implements BillRepositoryInterface */ public function getPaidDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection { - //Log::debug('Now in getPaidDatesInRange()'); + //app('log')->debug('Now in getPaidDatesInRange()'); return $bill->transactionJournals() ->before($end)->after($start)->get( @@ -420,7 +416,7 @@ class BillRepository implements BillRepositoryInterface $array = []; /** @var Rule $rule */ foreach ($rules as $rule) { - $array[$rule->action_value] = $array[$rule->action_value] ?? []; + $array[$rule->action_value] ??= []; $array[$rule->action_value][] = ['id' => $rule->id, 'title' => $rule->title, 'active' => $rule->active]; } $return = []; @@ -453,14 +449,14 @@ class BillRepository implements BillRepositoryInterface /** @var TransactionJournal $journal */ foreach ($journals as $journal) { - /** @var Transaction $transaction */ + /** @var Transaction|null $transaction */ $transaction = $journal->transactions()->where('amount', '<', 0)->first(); if (null === $transaction) { continue; } $currencyId = (int)$journal->transaction_currency_id; $currency = $journal->transactionCurrency; - $result[$currencyId] = $result[$currencyId] ?? [ + $result[$currencyId] ??= [ 'sum' => '0', 'count' => 0, 'avg' => '0', @@ -498,7 +494,7 @@ class BillRepository implements BillRepositoryInterface $journal = $bill->user->transactionJournals()->find((int)$transaction['transaction_journal_id']); $journal->bill_id = $bill->id; $journal->save(); - Log::debug(sprintf('Linked journal #%d to bill #%d', $journal->id, $bill->id)); + app('log')->debug(sprintf('Linked journal #%d to bill #%d', $journal->id, $bill->id)); } } @@ -522,12 +518,12 @@ class BillRepository implements BillRepositoryInterface } // find the most recent date for this bill NOT in the future. Cache this date: $start = clone $bill->date; - Log::debug('nextExpectedMatch: Start is ' . $start->format('Y-m-d')); + app('log')->debug('nextExpectedMatch: Start is ' . $start->format('Y-m-d')); while ($start < $date) { - Log::debug(sprintf('$start (%s) < $date (%s)', $start->format('Y-m-d'), $date->format('Y-m-d'))); + app('log')->debug(sprintf('$start (%s) < $date (%s)', $start->format('Y-m-d'), $date->format('Y-m-d'))); $start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); - Log::debug('Start is now ' . $start->format('Y-m-d')); + app('log')->debug('Start is now ' . $start->format('Y-m-d')); } $end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); @@ -537,12 +533,12 @@ class BillRepository implements BillRepositoryInterface if ($journalCount > 0) { // this period had in fact a bill. The new start is the current end, and we create a new end. - Log::debug(sprintf('Journal count is %d, so start becomes %s', $journalCount, $end->format('Y-m-d'))); + app('log')->debug(sprintf('Journal count is %d, so start becomes %s', $journalCount, $end->format('Y-m-d'))); $start = clone $end; $end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); } - Log::debug('nextExpectedMatch: Final start is ' . $start->format('Y-m-d')); - Log::debug('nextExpectedMatch: Matching end is ' . $end->format('Y-m-d')); + app('log')->debug('nextExpectedMatch: Final start is ' . $start->format('Y-m-d')); + app('log')->debug('nextExpectedMatch: Matching end is ' . $end->format('Y-m-d')); $cache->store($start); @@ -623,7 +619,7 @@ class BillRepository implements BillRepositoryInterface $set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']); $currency = $bill->transactionCurrency; - $return[$currency->id] = $return[$currency->id] ?? [ + $return[$currency->id] ??= [ 'id' => (string)$currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, @@ -637,8 +633,8 @@ class BillRepository implements BillRepositoryInterface /** @var Transaction|null $sourceTransaction */ $sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first(); if (null !== $sourceTransaction) { - $amount = (string)$sourceTransaction->amount; - if ((int)$sourceTransaction->foreign_currency_id === (int)$currency->id) { + $amount = $sourceTransaction->amount; + if ((int)$sourceTransaction->foreign_currency_id === $currency->id) { // use foreign amount instead! $amount = (string)$sourceTransaction->foreign_amount; } @@ -657,7 +653,7 @@ class BillRepository implements BillRepositoryInterface return $this->user->bills() ->where('active', true) ->orderBy('bills.name', 'ASC') - ->get(['bills.*', DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount'),]); + ->get(['bills.*', DB::raw('((bills.amount_min + bills.amount_max) / 2) AS expectedAmount'),]); // @phpstan-ignore-line } /** @@ -680,7 +676,7 @@ class BillRepository implements BillRepositoryInterface if ($total > 0) { $currency = $bill->transactionCurrency; $average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2'); - $return[$currency->id] = $return[$currency->id] ?? [ + $return[$currency->id] ??= [ 'id' => (string)$currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, @@ -708,21 +704,21 @@ class BillRepository implements BillRepositoryInterface { $set = new Collection(); $currentStart = clone $start; - //Log::debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); - //Log::debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); + //app('log')->debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); + //app('log')->debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); while ($currentStart <= $end) { - //Log::debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); + //app('log')->debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - //Log::debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + //app('log')->debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); if ($nextExpectedMatch > $end) {// If nextExpectedMatch is after end, we continue break; } $set->push(clone $nextExpectedMatch); - //Log::debug(sprintf('Now %d dates in set.', $set->count())); + //app('log')->debug(sprintf('Now %d dates in set.', $set->count())); $nextExpectedMatch->addDay(); - //Log::debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + //app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); $currentStart = clone $nextExpectedMatch; } diff --git a/app/Repositories/Budget/AvailableBudgetRepository.php b/app/Repositories/Budget/AvailableBudgetRepository.php index 86ef18f8c4..7a4d4e6f10 100644 --- a/app/Repositories/Budget/AvailableBudgetRepository.php +++ b/app/Repositories/Budget/AvailableBudgetRepository.php @@ -72,7 +72,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface $query = $this->user->availableBudgets()->with(['transactionCurrency']); if (null !== $start && null !== $end) { $query->where( - static function (Builder $q1) use ($start, $end) { + static function (Builder $q1) use ($start, $end) { // @phpstan-ignore-line $q1->where('start_date', '=', $start->format('Y-m-d')); $q1->where('end_date', '=', $end->format('Y-m-d')); } @@ -133,13 +133,14 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface */ public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string { - $amount = '0'; + $amount = '0'; + /** @var AvailableBudget|null $availableBudget */ $availableBudget = $this->user->availableBudgets() ->where('transaction_currency_id', $currency->id) ->where('start_date', $start->format('Y-m-d')) ->where('end_date', $end->format('Y-m-d'))->first(); if (null !== $availableBudget) { - $amount = (string)$availableBudget->amount; + $amount = $availableBudget->amount; } return $amount; @@ -248,8 +249,8 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface $availableBudget = new AvailableBudget(); $availableBudget->user()->associate($this->user); $availableBudget->transactionCurrency()->associate($currency); - $availableBudget->start_date = $start->format('Y-m-d'); - $availableBudget->end_date = $end->format('Y-m-d'); + $availableBudget->start_date = $start->startOfDay(); + $availableBudget->end_date = $end->endOfDay(); } $availableBudget->amount = $amount; $availableBudget->save(); @@ -262,7 +263,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } diff --git a/app/Repositories/Budget/BudgetLimitRepository.php b/app/Repositories/Budget/BudgetLimitRepository.php index 23c41a4c1d..8efc508858 100644 --- a/app/Repositories/Budget/BudgetLimitRepository.php +++ b/app/Repositories/Budget/BudgetLimitRepository.php @@ -33,7 +33,6 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -62,30 +61,30 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // same complex where query as below. ->where( static function (Builder $q5) use ($start, $end) { - $q5->where( - static function (Builder $q1) use ($start, $end) { - $q1->where( - static function (Builder $q2) use ($start, $end) { - $q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d')); - $q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d')); - } - ) - ->orWhere( - static function (Builder $q3) use ($start, $end) { - $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d')); - $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d')); - } - ); - } - ) - ->orWhere( - static function (Builder $q4) use ($start, $end) { - // or start is before start AND end is after end. - $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d')); - $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d')); - } - ); + $q5->where( + static function (Builder $q1) use ($start, $end) { + $q1->where( + static function (Builder $q2) use ($start, $end) { + $q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d')); + $q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d')); } + ) + ->orWhere( + static function (Builder $q3) use ($start, $end) { + $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d')); + $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d')); + } + ); + } + ) + ->orWhere( + static function (Builder $q4) use ($start, $end) { + // or start is before start AND end is after end. + $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d')); + $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d')); + } + ); + } ) ->where('budget_limits.transaction_currency_id', $currency->id) ->whereNull('budgets.deleted_at') @@ -241,7 +240,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // when both dates are set: return $budget->budgetlimits() ->where( - static function (Builder $q5) use ($start, $end) { + static function (Builder $q5) use ($start, $end) { // @phpstan-ignore-line $q5->where( static function (Builder $q1) use ($start, $end) { // budget limit ends within period @@ -254,9 +253,9 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // budget limit start within period ->orWhere( static function (Builder $q3) use ($start, $end) { - $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00')); - $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 23:59:59')); - } + $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00')); + $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 23:59:59')); + } ); } ) @@ -276,7 +275,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -295,7 +294,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface $factory = app(TransactionCurrencyFactory::class); $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null); if (null === $currency) { - $currency = app('amount')->getDefaultCurrencyByUser($this->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); } $currency->enabled = true; $currency->save(); @@ -315,7 +314,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface if (null !== $limit) { throw new FireflyException('200027: Budget limit already exists.'); } - Log::debug('No existing budget limit, create a new one'); + app('log')->debug('No existing budget limit, create a new one'); // or create one and return it. $limit = new BudgetLimit(); @@ -325,7 +324,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface $limit->amount = $data['amount']; $limit->transaction_currency_id = $currency->id; $limit->save(); - Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $data['amount'])); + app('log')->debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $data['amount'])); return $limit; } @@ -372,7 +371,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface } // catch unexpected null: if (null === $currency) { - $currency = $budgetLimit->transactionCurrency ?? app('amount')->getDefaultCurrencyByUser($this->user); + $currency = $budgetLimit->transactionCurrency ?? app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); } $currency->enabled = true; $currency->save(); @@ -398,11 +397,11 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface $limits = $budget->budgetlimits() ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) - ->count(['budget_limits.*']); - Log::debug(sprintf('Found %d budget limits.', $limits)); + ->count('budget_limits.*'); + app('log')->debug(sprintf('Found %d budget limits.', $limits)); // there might be a budget limit for these dates: - /** @var BudgetLimit $limit */ + /** @var BudgetLimit|null $limit */ $limit = $budget->budgetlimits() ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) @@ -410,7 +409,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // if more than 1 limit found, delete the others: if ($limits > 1 && null !== $limit) { - Log::debug(sprintf('Found more than 1, delete all except #%d', $limit->id)); + app('log')->debug(sprintf('Found more than 1, delete all except #%d', $limit->id)); $budget->budgetlimits() ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00')) ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00')) @@ -421,20 +420,20 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // Returns 0 if the two operands are equal, // 1 if the left_operand is larger than the right_operand, -1 otherwise. if (null !== $limit && bccomp($amount, '0') <= 0) { - Log::debug(sprintf('%s is zero, delete budget limit #%d', $amount, $limit->id)); + app('log')->debug(sprintf('%s is zero, delete budget limit #%d', $amount, $limit->id)); $limit->delete(); return null; } // update if exists: if (null !== $limit) { - Log::debug(sprintf('Existing budget limit is #%d, update this to amount %s', $limit->id, $amount)); + app('log')->debug(sprintf('Existing budget limit is #%d, update this to amount %s', $limit->id, $amount)); $limit->amount = $amount; $limit->save(); return $limit; } - Log::debug('No existing budget limit, create a new one'); + app('log')->debug('No existing budget limit, create a new one'); // or create one and return it. $limit = new BudgetLimit(); $limit->budget()->associate($budget); @@ -442,7 +441,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface $limit->end_date = $end->startOfDay(); $limit->amount = $amount; $limit->save(); - Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount)); + app('log')->debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount)); return $limit; } diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 069a204e4d..ceb7a89a71 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -44,7 +44,6 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\QueryException; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; use Storage; @@ -91,7 +90,7 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function budgetedInPeriod(Carbon $start, Carbon $end): array { - Log::debug(sprintf('Now in budgetedInPeriod("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'))); + app('log')->debug(sprintf('Now in budgetedInPeriod("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'))); $return = []; /** @var BudgetLimitRepository $limitRepository */ $limitRepository = app(BudgetLimitRepository::class); @@ -99,13 +98,13 @@ class BudgetRepository implements BudgetRepositoryInterface $budgets = $this->getActiveBudgets(); /** @var Budget $budget */ foreach ($budgets as $budget) { - Log::debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); + app('log')->debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); $limits = $limitRepository->getBudgetLimits($budget, $start, $end); /** @var BudgetLimit $limit */ foreach ($limits as $limit) { - Log::debug(sprintf('Budget limit #%d', $limit->id)); + app('log')->debug(sprintf('Budget limit #%d', $limit->id)); $currency = $limit->transactionCurrency; - $return[$currency->id] = $return[$currency->id] ?? [ + $return[$currency->id] ??= [ 'id' => (string)$currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, @@ -115,24 +114,24 @@ class BudgetRepository implements BudgetRepositoryInterface ]; // same period if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) { - $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string)$limit->amount); - Log::debug(sprintf('Add full amount [1]: %s', $limit->amount)); + $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount); + app('log')->debug(sprintf('Add full amount [1]: %s', $limit->amount)); continue; } // limit is inside of date range if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) { - $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string)$limit->amount); - Log::debug(sprintf('Add full amount [2]: %s', $limit->amount)); + $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount); + app('log')->debug(sprintf('Add full amount [2]: %s', $limit->amount)); continue; } $total = $limit->start_date->diffInDays($limit->end_date) + 1; // include the day itself. $days = $this->daysInOverlap($limit, $start, $end); - $amount = bcmul(bcdiv((string)$limit->amount, (string)$total), (string)$days); + $amount = bcmul(bcdiv($limit->amount, (string)$total), (string)$days); $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount); - Log::debug( + app('log')->debug( sprintf( 'Amount per day: %s (%s over %d days). Total amount for %d days: %s', - bcdiv((string)$limit->amount, (string)$total), + bcdiv($limit->amount, (string)$total), $limit->amount, $total, $days, @@ -149,7 +148,7 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -209,19 +208,19 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function budgetedInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array { - Log::debug(sprintf('Now in budgetedInPeriod(#%d, "%s", "%s")', $budget->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); + app('log')->debug(sprintf('Now in budgetedInPeriod(#%d, "%s", "%s")', $budget->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); $return = []; /** @var BudgetLimitRepository $limitRepository */ $limitRepository = app(BudgetLimitRepository::class); $limitRepository->setUser($this->user); - Log::debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); + app('log')->debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); $limits = $limitRepository->getBudgetLimits($budget, $start, $end); /** @var BudgetLimit $limit */ foreach ($limits as $limit) { - Log::debug(sprintf('Budget limit #%d', $limit->id)); + app('log')->debug(sprintf('Budget limit #%d', $limit->id)); $currency = $limit->transactionCurrency; - $return[$currency->id] = $return[$currency->id] ?? [ + $return[$currency->id] ??= [ 'id' => (string)$currency->id, 'name' => $currency->name, 'symbol' => $currency->symbol, @@ -231,24 +230,24 @@ class BudgetRepository implements BudgetRepositoryInterface ]; // same period if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) { - $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string)$limit->amount); - Log::debug(sprintf('Add full amount [1]: %s', $limit->amount)); + $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount); + app('log')->debug(sprintf('Add full amount [1]: %s', $limit->amount)); continue; } // limit is inside of date range if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) { - $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string)$limit->amount); - Log::debug(sprintf('Add full amount [2]: %s', $limit->amount)); + $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $limit->amount); + app('log')->debug(sprintf('Add full amount [2]: %s', $limit->amount)); continue; } $total = $limit->start_date->diffInDays($limit->end_date) + 1; // include the day itself. $days = $this->daysInOverlap($limit, $start, $end); - $amount = bcmul(bcdiv((string)$limit->amount, (string)$total), (string)$days); + $amount = bcmul(bcdiv($limit->amount, (string)$total), (string)$days); $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount); - Log::debug( + app('log')->debug( sprintf( 'Amount per day: %s (%s over %d days). Total amount for %d days: %s', - bcdiv((string)$limit->amount, (string)$total), + bcdiv($limit->amount, (string)$total), $limit->amount, $total, $days, @@ -291,7 +290,7 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function update(Budget $budget, array $data): Budget { - Log::debug('Now in update()'); + app('log')->debug('Now in update()'); $oldName = $budget->name; if (array_key_exists('name', $data)) { @@ -342,12 +341,12 @@ class BudgetRepository implements BudgetRepositoryInterface ->whereIn('rule_actions.action_type', $types) ->where('rule_actions.action_value', $oldName) ->get(['rule_actions.*']); - Log::debug(sprintf('Found %d actions to update.', $actions->count())); + app('log')->debug(sprintf('Found %d actions to update.', $actions->count())); /** @var RuleAction $action */ foreach ($actions as $action) { $action->action_value = $newName; $action->save(); - Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); + app('log')->debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); } } @@ -363,12 +362,12 @@ class BudgetRepository implements BudgetRepositoryInterface ->whereIn('rule_triggers.trigger_type', $types) ->where('rule_triggers.trigger_value', $oldName) ->get(['rule_triggers.*']); - Log::debug(sprintf('Found %d triggers to update.', $triggers->count())); + app('log')->debug(sprintf('Found %d triggers to update.', $triggers->count())); /** @var RuleTrigger $trigger */ foreach ($triggers as $trigger) { $trigger->trigger_value = $newName; $trigger->save(); - Log::debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); + app('log')->debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); } } @@ -417,7 +416,7 @@ class BudgetRepository implements BudgetRepositoryInterface $autoBudget = $this->getAutoBudget($budget); // grab default currency: - $currency = app('amount')->getDefaultCurrencyByUser($this->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); if (null === $autoBudget) { // at this point it's a blind assumption auto_budget_type is 1 or 2. @@ -490,8 +489,8 @@ class BudgetRepository implements BudgetRepositoryInterface $budgets = $this->getBudgets(); /** @var Budget $budget */ foreach ($budgets as $budget) { - DB::table('budget_transaction')->where('budget_id', (int)$budget->id)->delete(); - DB::table('budget_transaction_journal')->where('budget_id', (int)$budget->id)->delete(); + DB::table('budget_transaction')->where('budget_id', $budget->id)->delete(); + DB::table('budget_transaction_journal')->where('budget_id', $budget->id)->delete(); RecurrenceTransactionMeta::where('name', 'budget_id')->where('value', (string)$budget->id)->delete(); RuleAction::where('action_type', 'set_budget')->where('action_value', (string)$budget->id)->delete(); $budget->delete(); @@ -526,17 +525,17 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function findBudget(?int $budgetId, ?string $budgetName): ?Budget { - Log::debug('Now in findBudget()'); - Log::debug(sprintf('Searching for budget with ID #%d...', $budgetId)); + app('log')->debug('Now in findBudget()'); + app('log')->debug(sprintf('Searching for budget with ID #%d...', $budgetId)); $result = $this->find((int)$budgetId); if (null === $result && null !== $budgetName && '' !== $budgetName) { - Log::debug(sprintf('Searching for budget with name %s...', $budgetName)); - $result = $this->findByName((string)$budgetName); + app('log')->debug(sprintf('Searching for budget with name %s...', $budgetName)); + $result = $this->findByName($budgetName); } if (null !== $result) { - Log::debug(sprintf('Found budget #%d: %s', $result->id, $result->name)); + app('log')->debug(sprintf('Found budget #%d: %s', $result->id, $result->name)); } - Log::debug(sprintf('Found result is null? %s', var_export(null === $result, true))); + app('log')->debug(sprintf('Found result is null? %s', var_export(null === $result, true))); return $result; } @@ -590,7 +589,7 @@ class BudgetRepository implements BudgetRepositoryInterface static function (Attachment $attachment) use ($disk) { $notes = $attachment->notes()->first(); $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; + $attachment->notes_text = null !== $notes ? $notes->text : ''; return $attachment; } @@ -665,7 +664,7 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function spentInPeriod(Carbon $start, Carbon $end): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $start->startOfDay(); $end->endOfDay(); @@ -695,7 +694,7 @@ class BudgetRepository implements BudgetRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'id' => (string)$currencyId, 'name' => $journal['currency_name'], 'symbol' => $journal['currency_symbol'], @@ -708,7 +707,7 @@ class BudgetRepository implements BudgetRepositoryInterface // also do foreign amount: $foreignId = (int)$journal['foreign_currency_id']; if (0 !== $foreignId) { - $array[$foreignId] = $array[$foreignId] ?? [ + $array[$foreignId] ??= [ 'id' => (string)$foreignId, 'name' => $journal['foreign_currency_name'], 'symbol' => $journal['foreign_currency_symbol'], @@ -728,7 +727,7 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function spentInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $start->startOfDay(); $end->endOfDay(); @@ -758,7 +757,7 @@ class BudgetRepository implements BudgetRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'id' => (string)$currencyId, 'name' => $journal['currency_name'], 'symbol' => $journal['currency_symbol'], @@ -771,7 +770,7 @@ class BudgetRepository implements BudgetRepositoryInterface // also do foreign amount: $foreignId = (int)$journal['foreign_currency_id']; if (0 !== $foreignId) { - $array[$foreignId] = $array[$foreignId] ?? [ + $array[$foreignId] ??= [ 'id' => (string)$foreignId, 'name' => $journal['foreign_currency_name'], 'symbol' => $journal['foreign_currency_symbol'], @@ -807,8 +806,8 @@ class BudgetRepository implements BudgetRepositoryInterface ] ); } catch (QueryException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException('400002: Could not store budget.', 0, $e); } @@ -848,7 +847,7 @@ class BudgetRepository implements BudgetRepositoryInterface $currency = $repos->findByCode((string)$data['currency_code']); } if (null === $currency) { - $currency = app('amount')->getDefaultCurrencyByUser($this->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); } $autoBudget = new AutoBudget(); diff --git a/app/Repositories/Budget/NoBudgetRepository.php b/app/Repositories/Budget/NoBudgetRepository.php index 037a3c3a08..69ee28cfc0 100644 --- a/app/Repositories/Budget/NoBudgetRepository.php +++ b/app/Repositories/Budget/NoBudgetRepository.php @@ -64,7 +64,7 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $data[$currencyId] = $data[$currencyId] ?? [ + $data[$currencyId] ??= [ 'id' => 0, 'name' => sprintf('%s (%s)', trans('firefly.no_budget'), $journal['currency_name']), 'sum' => '0', @@ -143,7 +143,7 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -178,7 +178,7 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], diff --git a/app/Repositories/Budget/OperationsRepository.php b/app/Repositories/Budget/OperationsRepository.php index 3289b9e4f6..893eca39c3 100644 --- a/app/Repositories/Budget/OperationsRepository.php +++ b/app/Repositories/Budget/OperationsRepository.php @@ -33,7 +33,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * @@ -53,23 +52,23 @@ class OperationsRepository implements OperationsRepositoryInterface */ public function budgetedPerDay(Budget $budget): string { - Log::debug(sprintf('Now with budget #%d "%s"', $budget->id, $budget->name)); + app('log')->debug(sprintf('Now with budget #%d "%s"', $budget->id, $budget->name)); $total = '0'; $count = 0; foreach ($budget->budgetlimits as $limit) { $diff = $limit->start_date->diffInDays($limit->end_date); $diff = 0 === $diff ? 1 : $diff; - $amount = (string)$limit->amount; + $amount = $limit->amount; $perDay = bcdiv($amount, (string)$diff); $total = bcadd($total, $perDay); $count++; - Log::debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total)); + app('log')->debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total)); } $avg = $total; if ($count > 0) { $avg = bcdiv($total, (string)$count); } - Log::debug(sprintf('%s / %d = %s = average.', $total, $count, $avg)); + app('log')->debug(sprintf('%s / %d = %s = average.', $total, $count, $avg)); return $avg; } @@ -106,7 +105,7 @@ class OperationsRepository implements OperationsRepositoryInterface $currencyId = (int)$journal['currency_id']; $key = sprintf('%d-%d', $budgetId, $currencyId); - $data[$key] = $data[$key] ?? [ + $data[$key] ??= [ 'id' => $budgetId, 'name' => sprintf('%s (%s)', $budgetName, $journal['currency_name']), 'sum' => '0', @@ -147,7 +146,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $budgets && $budgets->count() > 0) { $collector->setBudgets($budgets); } - if (null === $budgets || (null !== $budgets && 0 === $budgets->count())) { + if (null === $budgets || 0 === $budgets->count()) { $collector->setBudgets($this->getBudgets()); } $collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation(); @@ -165,7 +164,7 @@ class OperationsRepository implements OperationsRepositoryInterface } // info about the currency: - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'budgets' => [], 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -175,7 +174,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; // info about the categories: - $array[$currencyId]['budgets'][$budgetId] = $array[$currencyId]['budgets'][$budgetId] ?? [ + $array[$currencyId]['budgets'][$budgetId] ??= [ 'id' => $budgetId, 'name' => $budgetName, 'transaction_journals' => [], @@ -205,7 +204,7 @@ class OperationsRepository implements OperationsRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -221,67 +220,6 @@ class OperationsRepository implements OperationsRepositoryInterface return $repos->getActiveBudgets(); } - /** - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - * @deprecated - */ - public function spentInPeriodMc(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setUser($this->user); - $collector->setRange($start, $end)->setBudgets($budgets)->withBudgetInformation(); - - if ($accounts->count() > 0) { - $collector->setAccounts($accounts); - } - // TODO possible candidate for "get extracted groups" method. - $set = $collector->getGroups(); - $return = []; - $total = []; - $currencies = []; - /** @var array $group */ - foreach ($set as $group) { - /** @var array $transaction */ - foreach ($group['transactions'] as $transaction) { - $code = $transaction['currency_code']; - if (!array_key_exists($code, $currencies)) { - $currencies[$code] = [ - 'id' => $transaction['currency_id'], - 'decimal_places' => $transaction['currency_decimal_places'], - 'code' => $transaction['currency_code'], - 'name' => $transaction['currency_name'], - 'symbol' => $transaction['currency_symbol'], - ]; - } - $total[$code] = array_key_exists($code, $total) ? bcadd($total[$code], $transaction['amount']) : $transaction['amount']; - } - } - /** - * @var string $code - * @var string $spent - */ - foreach ($total as $code => $spent) { - /** @var TransactionCurrency $currency */ - $currency = $currencies[$code]; - $return[] = [ - 'currency_id' => (string)$currency['id'], - 'currency_code' => $code, - 'currency_name' => $currency['name'], - 'currency_symbol' => $currency['symbol'], - 'currency_decimal_places' => $currency['decimal_places'], - 'amount' => app('steam')->bcround($spent, $currency['decimal_places']), - ]; - } - - return $return; - } - /** * @param Carbon $start * @param Carbon $end @@ -290,7 +228,7 @@ class OperationsRepository implements OperationsRepositoryInterface * @param TransactionCurrency|null $currency * * @return array - * @deprecated + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpenses( Carbon $start, @@ -298,8 +236,9 @@ class OperationsRepository implements OperationsRepositoryInterface ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null - ): array { - //Log::debug(sprintf('Now in %s', __METHOD__)); + ): array + { + //app('log')->debug(sprintf('Now in %s', __METHOD__)); $start->startOfDay(); $end->endOfDay(); @@ -341,7 +280,7 @@ class OperationsRepository implements OperationsRepositoryInterface // same but for foreign currencies: if (null !== $currency) { - //Log::debug(sprintf('Currency is "%s".', $currency->name)); + //app('log')->debug(sprintf('Currency is "%s".', $currency->name)); /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]) @@ -351,7 +290,7 @@ class OperationsRepository implements OperationsRepositoryInterface $collector->setAccounts($accounts); } $result = $collector->getExtractedJournals(); - //Log::debug(sprintf('Found %d journals with currency %s.', count($result), $currency->code)); + //app('log')->debug(sprintf('Found %d journals with currency %s.', count($result), $currency->code)); // do not use array_merge because you want keys to overwrite (otherwise you get double results): $journals = $result + $journals; } @@ -359,7 +298,7 @@ class OperationsRepository implements OperationsRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -372,7 +311,7 @@ class OperationsRepository implements OperationsRepositoryInterface // also do foreign amount: $foreignId = (int)$journal['foreign_currency_id']; if (0 !== $foreignId) { - $array[$foreignId] = $array[$foreignId] ?? [ + $array[$foreignId] ??= [ 'sum' => '0', 'currency_id' => $foreignId, 'currency_name' => $journal['foreign_currency_name'], @@ -387,21 +326,4 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } - /** - * For now, simply refer to whichever repository holds this function. - * TODO perhaps better in the future. - * - * @param Budget $budget - * @param Carbon|null $start - * @param Carbon|null $end - * - * @return Collection - */ - private function getBudgetLimits(Budget $budget, Carbon $start = null, Carbon $end = null): Collection - { - /** @var BudgetLimitRepositoryInterface $blRepository */ - $blRepository = app(BudgetLimitRepositoryInterface::class); - - return $blRepository->getBudgetLimits($budget, $start, $end); - } } diff --git a/app/Repositories/Budget/OperationsRepositoryInterface.php b/app/Repositories/Budget/OperationsRepositoryInterface.php index 1e2ab042f8..bed08af39f 100644 --- a/app/Repositories/Budget/OperationsRepositoryInterface.php +++ b/app/Repositories/Budget/OperationsRepositoryInterface.php @@ -75,23 +75,7 @@ interface OperationsRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void; - /** - * Return multi-currency spent information. - * - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - * @deprecated - */ - public function spentInPeriodMc(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array; - - /** - * TODO this method was marked as deprecated but I'm not sure why. - * * @param Carbon $start * @param Carbon $end * @param Collection|null $accounts @@ -99,7 +83,7 @@ interface OperationsRepositoryInterface * @param TransactionCurrency|null $currency * * @return array - * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function sumExpenses( Carbon $start, diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index d922fe3658..6a05ef73bb 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -38,7 +38,6 @@ use FireflyIII\Services\Internal\Update\CategoryUpdateService; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Storage; /** @@ -125,11 +124,11 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function findCategory(?int $categoryId, ?string $categoryName): ?Category { - Log::debug('Now in findCategory()'); - Log::debug(sprintf('Searching for category with ID #%d...', $categoryId)); + app('log')->debug('Now in findCategory()'); + app('log')->debug(sprintf('Searching for category with ID #%d...', $categoryId)); $result = $this->find((int)$categoryId); if (null === $result) { - Log::debug(sprintf('Searching for category with name %s...', $categoryName)); + app('log')->debug(sprintf('Searching for category with name %s...', $categoryName)); $result = $this->findByName((string)$categoryName); if (null === $result && '' !== (string)$categoryName) { // create it! @@ -137,9 +136,9 @@ class CategoryRepository implements CategoryRepositoryInterface } } if (null !== $result) { - Log::debug(sprintf('Found category #%d: %s', $result->id, $result->name)); + app('log')->debug(sprintf('Found category #%d: %s', $result->id, $result->name)); } - Log::debug(sprintf('Found category result is null? %s', var_export(null === $result, true))); + app('log')->debug(sprintf('Found category result is null? %s', var_export(null === $result, true))); return $result; } @@ -201,7 +200,7 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -307,7 +306,7 @@ class CategoryRepository implements CategoryRepositoryInterface static function (Attachment $attachment) use ($disk) { $notes = $attachment->notes()->first(); $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; + $attachment->notes_text = null !== $notes ? $notes->text : ''; return $attachment; } diff --git a/app/Repositories/Category/NoCategoryRepository.php b/app/Repositories/Category/NoCategoryRepository.php index f43f674c87..533b3d93dd 100644 --- a/app/Repositories/Category/NoCategoryRepository.php +++ b/app/Repositories/Category/NoCategoryRepository.php @@ -62,7 +62,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'categories' => [], 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -71,7 +71,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface 'currency_decimal_places' => $journal['currency_decimal_places'], ]; // info about the non-existent category: - $array[$currencyId]['categories'][0] = $array[$currencyId]['categories'][0] ?? [ + $array[$currencyId]['categories'][0] ??= [ 'id' => 0, 'name' => (string)trans('firefly.noCategory'), 'transaction_journals' => [], @@ -95,7 +95,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -124,7 +124,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'categories' => [], 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -134,7 +134,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface ]; // info about the non-existent category: - $array[$currencyId]['categories'][0] = $array[$currencyId]['categories'][0] ?? [ + $array[$currencyId]['categories'][0] ??= [ 'id' => 0, 'name' => (string)trans('firefly.noCategory'), 'transaction_journals' => [], @@ -175,7 +175,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -212,7 +212,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -243,7 +243,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], diff --git a/app/Repositories/Category/OperationsRepository.php b/app/Repositories/Category/OperationsRepository.php index 9211a0d060..f33dde6db6 100644 --- a/app/Repositories/Category/OperationsRepository.php +++ b/app/Repositories/Category/OperationsRepository.php @@ -63,7 +63,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $categories && $categories->count() > 0) { $collector->setCategories($categories); } - if (null === $categories || (null !== $categories && 0 === $categories->count())) { + if (null === $categories || 0 === $categories->count()) { $collector->setCategories($this->getCategories()); } $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation(); @@ -81,7 +81,7 @@ class OperationsRepository implements OperationsRepositoryInterface } // info about the currency: - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'categories' => [], 'currency_id' => (string)$currencyId, 'currency_name' => $journal['currency_name'], @@ -91,7 +91,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; // info about the categories: - $array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [ + $array[$currencyId]['categories'][$categoryId] ??= [ 'id' => (string)$categoryId, 'name' => $categoryName, 'transaction_journals' => [], @@ -121,7 +121,7 @@ class OperationsRepository implements OperationsRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -159,7 +159,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $categories && $categories->count() > 0) { $collector->setCategories($categories); } - if (null === $categories || (null !== $categories && 0 === $categories->count())) { + if (null === $categories || 0 === $categories->count()) { $collector->setCategories($this->getCategories()); } $collector->withCategoryInformation()->withAccountInformation(); @@ -177,7 +177,7 @@ class OperationsRepository implements OperationsRepositoryInterface } // info about the currency: - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'categories' => [], 'currency_id' => (string)$currencyId, 'currency_name' => $journal['currency_name'], @@ -187,7 +187,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; // info about the categories: - $array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [ + $array[$currencyId]['categories'][$categoryId] ??= [ 'id' => (string)$categoryId, 'name' => $categoryName, 'transaction_journals' => [], @@ -223,7 +223,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $categories && $categories->count() > 0) { $collector->setCategories($categories); } - if (null === $categories || (null !== $categories && 0 === $categories->count())) { + if (null === $categories || 0 === $categories->count()) { $collector->setCategories($this->getCategories()); } $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation(); @@ -241,7 +241,7 @@ class OperationsRepository implements OperationsRepositoryInterface } // info about the currency: - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'categories' => [], 'currency_id' => (string)$currencyId, 'currency_name' => $journal['currency_name'], @@ -251,7 +251,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; // info about the categories: - $array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [ + $array[$currencyId]['categories'][$categoryId] ??= [ 'id' => (string)$categoryId, 'name' => $categoryName, 'transaction_journals' => [], @@ -288,7 +288,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $categories && $categories->count() > 0) { $collector->setCategories($categories); } - if (null === $categories || (null !== $categories && 0 === $categories->count())) { + if (null === $categories || 0 === $categories->count()) { $collector->setCategories($this->getCategories()); } $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation(); @@ -306,7 +306,7 @@ class OperationsRepository implements OperationsRepositoryInterface } // info about the currency: - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'categories' => [], 'currency_id' => (string)$currencyId, 'currency_name' => $journal['currency_name'], @@ -316,7 +316,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; // info about the categories: - $array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [ + $array[$currencyId]['categories'][$categoryId] ??= [ 'id' => (string)$categoryId, 'name' => $categoryName, 'transaction_journals' => [], @@ -361,7 +361,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $accounts && $accounts->count() > 0) { $collector->setAccounts($accounts); } - if (null === $categories || (null !== $categories && 0 === $categories->count())) { + if (null === $categories || 0 === $categories->count()) { $categories = $this->getCategories(); } $collector->setCategories($categories); @@ -371,7 +371,7 @@ class OperationsRepository implements OperationsRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => (string)$currencyId, 'currency_name' => $journal['currency_name'], @@ -405,7 +405,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $accounts && $accounts->count() > 0) { $collector->setAccounts($accounts); } - if (null === $categories || (null !== $categories && 0 === $categories->count())) { + if (null === $categories || 0 === $categories->count()) { $categories = $this->getCategories(); } $collector->setCategories($categories); @@ -414,7 +414,7 @@ class OperationsRepository implements OperationsRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => (string)$currencyId, 'currency_name' => $journal['currency_name'], @@ -448,7 +448,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $accounts && $accounts->count() > 0) { $collector->setAccounts($accounts); } - if (null === $categories || (null !== $categories && 0 === $categories->count())) { + if (null === $categories || 0 === $categories->count()) { $categories = $this->getCategories(); } $collector->setCategories($categories); @@ -457,7 +457,7 @@ class OperationsRepository implements OperationsRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'sum' => '0', 'currency_id' => (string)$currencyId, 'currency_name' => $journal['currency_name'], diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index 4cb267b9f9..e02219e249 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -29,7 +29,6 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class CurrencyRepository. @@ -77,18 +76,18 @@ class CurrencyRepository implements CurrencyRepositoryInterface { if ($fromCurrency->id === $toCurrency->id) { $rate = new CurrencyExchangeRate(); - $rate->rate = 1; + $rate->rate = "1"; $rate->id = 0; return $rate; } - /** @var CurrencyExchangeRate $rate */ + /** @var CurrencyExchangeRate|null $rate */ $rate = $this->user->currencyExchangeRates() ->where('from_currency_id', $fromCurrency->id) ->where('to_currency_id', $toCurrency->id) ->where('date', $date->format('Y-m-d'))->first(); if (null !== $rate) { - Log::debug(sprintf('Found cached exchange rate in database for %s to %s on %s', $fromCurrency->code, $toCurrency->code, $date->format('Y-m-d'))); + app('log')->debug(sprintf('Found cached exchange rate in database for %s to %s on %s', $fromCurrency->code, $toCurrency->code, $date->format('Y-m-d'))); return $rate; } @@ -124,7 +123,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } diff --git a/app/Repositories/Journal/JournalAPIRepository.php b/app/Repositories/Journal/JournalAPIRepository.php index 6eb5f743a1..55b3020092 100644 --- a/app/Repositories/Journal/JournalAPIRepository.php +++ b/app/Repositories/Journal/JournalAPIRepository.php @@ -74,7 +74,7 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface static function (Attachment $attachment) use ($disk) { $notes = $attachment->notes()->first(); $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; // TODO should not set notes like this. + $attachment->notes_text = null !== $notes ? $notes->text : ''; // TODO should not set notes like this. return $attachment; } @@ -102,7 +102,7 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface { $events = $journal->piggyBankEvents()->get(); $events->each( - function (PiggyBankEvent $event) { + static function (PiggyBankEvent $event) { $event->piggyBank = $event->piggyBank()->withTrashed()->first(); } ); @@ -115,7 +115,7 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } diff --git a/app/Repositories/Journal/JournalCLIRepository.php b/app/Repositories/Journal/JournalCLIRepository.php index 8f37b8f1fd..186cc4189b 100644 --- a/app/Repositories/Journal/JournalCLIRepository.php +++ b/app/Repositories/Journal/JournalCLIRepository.php @@ -120,7 +120,6 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface $cache->addProperty($field); if ($cache->has()) { - $result = null; return new Carbon($cache->get()); } @@ -199,9 +198,9 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface */ public function getSplitJournals(): Collection { - $query = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->groupBy('transaction_journals.id'); - $result = $query->get(['transaction_journals.id as id', DB::raw('count(transactions.id) as transaction_count')]); + $query = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->groupBy('transaction_journals.id'); + $result = $query->get(['transaction_journals.id as id', DB::raw('count(transactions.id) as transaction_count')]); // @phpstan-ignore-line $journalIds = []; /** @var stdClass $row */ foreach ($result as $row) { diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index bf34aa5c94..5f5abbdb8e 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -89,7 +89,7 @@ class JournalRepository implements JournalRepositoryInterface */ public function firstNull(): ?TransactionJournal { - /** @var TransactionJournal $entry */ + /** @var TransactionJournal|null $entry */ $entry = $this->user->transactionJournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']); $result = null; if (null !== $entry) { @@ -104,7 +104,7 @@ class JournalRepository implements JournalRepositoryInterface */ public function getDestinationAccount(TransactionJournal $journal): Account { - /** @var Transaction $transaction */ + /** @var Transaction|null $transaction */ $transaction = $journal->transactions()->with('account')->where('amount', '>', 0)->first(); if (null === $transaction) { throw new FireflyException(sprintf('Your administration is broken. Transaction journal #%d has no destination transaction.', $journal->id)); @@ -142,7 +142,7 @@ class JournalRepository implements JournalRepositoryInterface */ public function getLast(): ?TransactionJournal { - /** @var TransactionJournal $entry */ + /** @var TransactionJournal|null $entry */ $entry = $this->user->transactionJournals()->orderBy('date', 'DESC')->first(['transaction_journals.*']); $result = null; if (null !== $entry) { @@ -159,13 +159,9 @@ class JournalRepository implements JournalRepositoryInterface */ public function getLinkNoteText(TransactionJournalLink $link): string { - /** @var Note $note */ + /** @var Note|null $note */ $note = $link->notes()->first(); - if (null !== $note) { - return $note->text ?? ''; - } - - return ''; + return (string)$note?->text; } /** @@ -202,7 +198,7 @@ class JournalRepository implements JournalRepositoryInterface */ public function getSourceAccount(TransactionJournal $journal): Account { - /** @var Transaction $transaction */ + /** @var Transaction|null $transaction */ $transaction = $journal->transactions()->with('account')->where('amount', '<', 0)->first(); if (null === $transaction) { throw new FireflyException(sprintf('Your administration is broken. Transaction journal #%d has no source transaction.', $journal->id)); @@ -216,7 +212,7 @@ class JournalRepository implements JournalRepositoryInterface */ public function reconcileById(int $journalId): void { - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $journal */ $journal = $this->user->transactionJournals()->find($journalId); $journal?->transactions()->update(['reconciled' => true]); } @@ -257,7 +253,7 @@ class JournalRepository implements JournalRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -267,7 +263,7 @@ class JournalRepository implements JournalRepositoryInterface */ public function unreconcileById(int $journalId): void { - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $journal */ $journal = $this->user->transactionJournals()->find($journalId); $journal?->transactions()->update(['reconciled' => false]); } diff --git a/app/Repositories/LinkType/LinkTypeRepository.php b/app/Repositories/LinkType/LinkTypeRepository.php index d6c841ff02..cdb6dd0331 100644 --- a/app/Repositories/LinkType/LinkTypeRepository.php +++ b/app/Repositories/LinkType/LinkTypeRepository.php @@ -32,7 +32,6 @@ use FireflyIII\Models\TransactionJournalLink; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class LinkTypeRepository. @@ -114,7 +113,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface */ public function findLink(TransactionJournal $one, TransactionJournal $two): bool { - Log::debug(sprintf('Now in findLink(%d, %d)', $one->id, $two->id)); + app('log')->debug(sprintf('Now in findLink(%d, %d)', $one->id, $two->id)); $count = TransactionJournalLink::whereDestinationId($one->id)->whereSourceId($two->id)->count(); $opposingCount = TransactionJournalLink::whereDestinationId($two->id)->whereSourceId($one->id)->count(); @@ -199,7 +198,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface $merged = $outward->merge($inward); return $merged->filter( - function (TransactionJournalLink $link) { + static function (TransactionJournalLink $link) { return null !== $link->source && null !== $link->destination; } ); @@ -210,7 +209,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -263,13 +262,13 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface $link = new TransactionJournalLink(); $link->linkType()->associate($linkType); if ('inward' === $information['direction']) { - Log::debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->inward, $inward->id, $outward->id)); + app('log')->debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->inward, $inward->id, $outward->id)); $link->source()->associate($inward); $link->destination()->associate($outward); } if ('outward' === $information['direction']) { - Log::debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->outward, $outward->id, $inward->id)); + app('log')->debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->outward, $outward->id, $inward->id)); $link->source()->associate($outward); $link->destination()->associate($inward); } @@ -348,7 +347,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface */ public function switchLinkById(int $linkId): bool { - /** @var TransactionJournalLink $link */ + /** @var TransactionJournalLink|null $link */ $link = TransactionJournalLink::find($linkId); if (null !== $link && $link->source->user->id === $this->user->id) { $this->switchLink($link); @@ -383,8 +382,8 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface */ public function updateLink(TransactionJournalLink $journalLink, array $data): TransactionJournalLink { - $journalLink->source_id = $data['inward_id'] ?: $journalLink->source_id; - $journalLink->destination_id = $data['outward_id'] ?: $journalLink->destination_id; + $journalLink->source_id = null === $data['inward_id'] ? $journalLink->source_id : $data['inward_id']; + $journalLink->destination_id = null === $data['outward_id'] ? $journalLink->destination_id : $data['outward_id']; $journalLink->save(); if (array_key_exists('link_type_name', $data)) { $linkType = LinkType::whereName($data['link_type_name'])->first(); @@ -395,7 +394,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface $journalLink->refresh(); } - $journalLink->link_type_id = $data['link_type_id'] ?: $journalLink->link_type_id; + $journalLink->link_type_id = null === $data['link_type_id'] ? $journalLink->link_type_id : $data['link_type_id']; $journalLink->save(); if (array_key_exists('notes', $data) && null !== $data['notes']) { $this->setNoteText($journalLink, $data['notes']); diff --git a/app/Repositories/ObjectGroup/ObjectGroupRepository.php b/app/Repositories/ObjectGroup/ObjectGroupRepository.php index a82c9b7f9d..6da8b643c2 100644 --- a/app/Repositories/ObjectGroup/ObjectGroupRepository.php +++ b/app/Repositories/ObjectGroup/ObjectGroupRepository.php @@ -30,7 +30,6 @@ use FireflyIII\Models\PiggyBank; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class ObjectGroupRepository @@ -114,13 +113,13 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface */ public function resetOrder(): void { - Log::debug('Now in resetOrder'); + app('log')->debug('Now in resetOrder'); $list = $this->get(); $index = 1; /** @var ObjectGroup $objectGroup */ foreach ($list as $objectGroup) { - if ($index !== (int)$objectGroup->order) { - Log::debug( + if ($index !== $objectGroup->order) { + app('log')->debug( sprintf('objectGroup #%d ("%s"): order should %d be but is %d.', $objectGroup->id, $objectGroup->title, $index, $objectGroup->order) ); $objectGroup->order = $index; @@ -156,7 +155,7 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -184,7 +183,7 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface */ public function setOrder(ObjectGroup $objectGroup, int $newOrder): ObjectGroup { - $oldOrder = (int)$objectGroup->order; + $oldOrder = $objectGroup->order; if ($newOrder > $oldOrder) { $this->user->objectGroups()->where('object_groups.order', '<=', $newOrder)->where('object_groups.order', '>', $oldOrder) @@ -203,7 +202,7 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface $objectGroup->save(); } - Log::debug(sprintf('Objectgroup #%d order is now %d', $objectGroup->id, $newOrder)); + app('log')->debug(sprintf('Objectgroup #%d order is now %d', $objectGroup->id, $newOrder)); return $objectGroup; } diff --git a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php index 216d666d49..dbcc7d7c36 100644 --- a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php +++ b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php @@ -34,7 +34,6 @@ use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -53,13 +52,13 @@ trait ModifiesPiggyBanks */ public function addAmountToRepetition(PiggyBankRepetition $repetition, string $amount, TransactionJournal $journal): void { - Log::debug(sprintf('addAmountToRepetition: %s', $amount)); + app('log')->debug(sprintf('addAmountToRepetition: %s', $amount)); if (-1 === bccomp($amount, '0')) { - Log::debug('Remove amount.'); + app('log')->debug('Remove amount.'); $this->removeAmount($repetition->piggyBank, bcmul($amount, '-1'), $journal); } if (1 === bccomp($amount, '0')) { - Log::debug('Add amount.'); + app('log')->debug('Add amount.'); $this->addAmount($repetition->piggyBank, $amount, $journal); } } @@ -80,7 +79,7 @@ trait ModifiesPiggyBanks $repetition->currentamount = bcsub($repetition->currentamount, $amount); $repetition->save(); - Log::debug('addAmount [a]: Trigger change for negative amount.'); + app('log')->debug('addAmount [a]: Trigger change for negative amount.'); event(new ChangedAmount($piggyBank, bcmul($amount, '-1'), $journal, null)); return true; @@ -103,7 +102,7 @@ trait ModifiesPiggyBanks $repetition->currentamount = bcadd($currentAmount, $amount); $repetition->save(); - Log::debug('addAmount [b]: Trigger change for positive amount.'); + app('log')->debug('addAmount [b]: Trigger change for positive amount.'); event(new ChangedAmount($piggyBank, $amount, $journal, null)); return true; @@ -120,7 +119,7 @@ trait ModifiesPiggyBanks { $today = today(config('app.timezone')); $leftOnAccount = $this->leftOnAccount($piggyBank, $today); - $savedSoFar = (string)$this->getRepetition($piggyBank)->currentamount; + $savedSoFar = $this->getRepetition($piggyBank)->currentamount; $maxAmount = $leftOnAccount; $leftToSave = null; if (0 !== bccomp($piggyBank->targetamount, '0')) { @@ -131,11 +130,11 @@ trait ModifiesPiggyBanks $compare = bccomp($amount, $maxAmount); $result = $compare <= 0; - Log::debug(sprintf('Left on account: %s on %s', $leftOnAccount, $today->format('Y-m-d'))); - Log::debug(sprintf('Saved so far: %s', $savedSoFar)); - Log::debug(sprintf('Left to save: %s', $leftToSave)); - Log::debug(sprintf('Maximum amount: %s', $maxAmount)); - Log::debug(sprintf('Compare <= 0? %d, so %s', $compare, var_export($result, true))); + app('log')->debug(sprintf('Left on account: %s on %s', $leftOnAccount, $today->format('Y-m-d'))); + app('log')->debug(sprintf('Saved so far: %s', $savedSoFar)); + app('log')->debug(sprintf('Left to save: %s', $leftToSave)); + app('log')->debug(sprintf('Maximum amount: %s', $maxAmount)); + app('log')->debug(sprintf('Compare <= 0? %d, so %s', $compare, var_export($result, true))); return $result; } @@ -202,11 +201,11 @@ trait ModifiesPiggyBanks $repetition->save(); if (-1 === bccomp($difference, '0')) { - Log::debug('addAmount [c]: Trigger change for negative amount.'); + app('log')->debug('addAmount [c]: Trigger change for negative amount.'); event(new ChangedAmount($piggyBank, $difference, null, null)); } if (1 === bccomp($difference, '0')) { - Log::debug('addAmount [d]: Trigger change for positive amount.'); + app('log')->debug('addAmount [d]: Trigger change for positive amount.'); event(new ChangedAmount($piggyBank, $difference, null, null)); } @@ -252,7 +251,7 @@ trait ModifiesPiggyBanks /** @var PiggyBank $piggyBank */ $piggyBank = PiggyBank::create($piggyData); } catch (QueryException $e) { - Log::error(sprintf('Could not store piggy bank: %s', $e->getMessage()), $piggyData); + app('log')->error(sprintf('Could not store piggy bank: %s', $e->getMessage()), $piggyData); throw new FireflyException('400005: Could not store new piggy bank.', 0, $e); } @@ -298,8 +297,8 @@ trait ModifiesPiggyBanks $set = $this->user->piggyBanks()->orderBy('piggy_banks.order', 'ASC')->get(['piggy_banks.*']); $current = 1; foreach ($set as $piggyBank) { - if ((int)$piggyBank->order !== $current) { - Log::debug(sprintf('Piggy bank #%d ("%s") was at place %d but should be on %d', $piggyBank->id, $piggyBank->name, $piggyBank->order, $current)); + if ($piggyBank->order !== $current) { + app('log')->debug(sprintf('Piggy bank #%d ("%s") was at place %d but should be on %d', $piggyBank->id, $piggyBank->name, $piggyBank->order, $current)); $piggyBank->order = $current; $piggyBank->save(); } @@ -312,14 +311,14 @@ trait ModifiesPiggyBanks */ public function setOrder(PiggyBank $piggyBank, int $newOrder): bool { - $oldOrder = (int)$piggyBank->order; - //Log::debug(sprintf('Will move piggy bank #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); + $oldOrder = $piggyBank->order; + //app('log')->debug(sprintf('Will move piggy bank #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); if ($newOrder > $oldOrder) { $this->user->piggyBanks()->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder) ->where('piggy_banks.id', '!=', $piggyBank->id) ->decrement('piggy_banks.order'); $piggyBank->order = $newOrder; - Log::debug(sprintf('[1] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); + app('log')->debug(sprintf('[1] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); $piggyBank->save(); return true; @@ -329,7 +328,7 @@ trait ModifiesPiggyBanks ->where('piggy_banks.id', '!=', $piggyBank->id) ->increment('piggy_banks.order'); $piggyBank->order = $newOrder; - Log::debug(sprintf('[2] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); + app('log')->debug(sprintf('[2] Order of piggy #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); $piggyBank->save(); return true; @@ -374,7 +373,7 @@ trait ModifiesPiggyBanks } // update the order of the piggy bank: - $oldOrder = (int)$piggyBank->order; + $oldOrder = $piggyBank->order; $newOrder = (int)($data['order'] ?? $oldOrder); if ($oldOrder !== $newOrder) { $this->setOrder($piggyBank, $newOrder); @@ -405,11 +404,8 @@ trait ModifiesPiggyBanks return $piggyBank; } - // remove if name is empty. Should be overruled by ID. - if ('' === $objectGroupTitle) { - $piggyBank->objectGroups()->sync([]); - $piggyBank->save(); - } + $piggyBank->objectGroups()->sync([]); + $piggyBank->save(); } // try also with ID: diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 95288dd956..0f647ff5db 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -36,7 +36,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; use Storage; @@ -66,25 +65,25 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function findPiggyBank(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank { - Log::debug('Searching for piggy information.'); + app('log')->debug('Searching for piggy information.'); if (null !== $piggyBankId) { - $searchResult = $this->find((int)$piggyBankId); + $searchResult = $this->find($piggyBankId); if (null !== $searchResult) { - Log::debug(sprintf('Found piggy based on #%d, will return it.', $piggyBankId)); + app('log')->debug(sprintf('Found piggy based on #%d, will return it.', $piggyBankId)); return $searchResult; } } if (null !== $piggyBankName) { - $searchResult = $this->findByName((string)$piggyBankName); + $searchResult = $this->findByName($piggyBankName); if (null !== $searchResult) { - Log::debug(sprintf('Found piggy based on "%s", will return it.', $piggyBankName)); + app('log')->debug(sprintf('Found piggy based on "%s", will return it.', $piggyBankName)); return $searchResult; } } - Log::debug('Found nothing'); + app('log')->debug('Found nothing'); return null; } @@ -126,7 +125,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface static function (Attachment $attachment) use ($disk) { $notes = $attachment->notes()->first(); $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; // TODO setting the text to the 'notes' field doesn't work. + $attachment->notes_text = null !== $notes ? $notes->text : ''; return $attachment; } @@ -147,7 +146,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return '0'; } - return (string)$rep->currentamount; + return $rep->currentamount; } /** @@ -183,7 +182,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function getExactAmount(PiggyBank $piggyBank, PiggyBankRepetition $repetition, TransactionJournal $journal): string { - Log::debug(sprintf('Now in getExactAmount(%d, %d, %d)', $piggyBank->id, $repetition->id, $journal->id)); + app('log')->debug(sprintf('Now in getExactAmount(%d, %d, %d)', $piggyBank->id, $repetition->id, $journal->id)); $operator = null; $currency = null; @@ -195,10 +194,10 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface $accountRepos = app(AccountRepositoryInterface::class); $accountRepos->setUser($this->user); - $defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user); + $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); $piggyBankCurrency = $accountRepos->getAccountCurrency($piggyBank->account) ?? $defaultCurrency; - Log::debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBankCurrency->code)); + app('log')->debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBankCurrency->code)); /** @var Transaction $source */ $source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first(); @@ -209,64 +208,64 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface if ($source->account_id === $piggyBank->account_id) { $operator = 'negative'; $currency = $accountRepos->getAccountCurrency($source->account) ?? $defaultCurrency; - Log::debug(sprintf('Currency will draw money out of piggy bank. Source currency is %s', $currency->code)); + app('log')->debug(sprintf('Currency will draw money out of piggy bank. Source currency is %s', $currency->code)); } // matches destination, which means amount will be added to piggy. if ($destination->account_id === $piggyBank->account_id) { $operator = 'positive'; $currency = $accountRepos->getAccountCurrency($destination->account) ?? $defaultCurrency; - Log::debug(sprintf('Currency will add money to piggy bank. Destination currency is %s', $currency->code)); + app('log')->debug(sprintf('Currency will add money to piggy bank. Destination currency is %s', $currency->code)); } if (null === $operator || null === $currency) { - Log::debug('Currency is NULL and operator is NULL, return "0".'); + app('log')->debug('Currency is NULL and operator is NULL, return "0".'); return '0'; } // currency of the account + the piggy bank currency are almost the same. // which amount from the transaction matches? $amount = null; - if ((int)$source->transaction_currency_id === (int)$currency->id) { - Log::debug('Use normal amount'); - $amount = app('steam')->$operator($source->amount); + if ((int)$source->transaction_currency_id === $currency->id) { + app('log')->debug('Use normal amount'); + $amount = app('steam')->$operator($source->amount); // @phpstan-ignore-line } - if ((int)$source->foreign_currency_id === (int)$currency->id) { - Log::debug('Use foreign amount'); - $amount = app('steam')->$operator($source->foreign_amount); + if ((int)$source->foreign_currency_id === $currency->id) { + app('log')->debug('Use foreign amount'); + $amount = app('steam')->$operator($source->foreign_amount); // @phpstan-ignore-line } if (null === $amount) { - Log::debug('No match on currency, so amount remains null, return "0".'); + app('log')->debug('No match on currency, so amount remains null, return "0".'); return '0'; } - Log::debug(sprintf('The currency is %s and the amount is %s', $currency->code, $amount)); - $room = bcsub((string)$piggyBank->targetamount, (string)$repetition->currentamount); + app('log')->debug(sprintf('The currency is %s and the amount is %s', $currency->code, $amount)); + $room = bcsub($piggyBank->targetamount, $repetition->currentamount); $compare = bcmul($repetition->currentamount, '-1'); - if (bccomp((string)$piggyBank->targetamount, '0') === 0) { + if (bccomp($piggyBank->targetamount, '0') === 0) { // amount is zero? then the "room" is positive amount of we wish to add or remove. $room = app('steam')->positive($amount); - Log::debug(sprintf('Room is now %s', $room)); + app('log')->debug(sprintf('Room is now %s', $room)); } - Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); + app('log')->debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); // if the amount is positive, make sure it fits in piggy bank: if (1 === bccomp($amount, '0') && bccomp($room, $amount) === -1) { // amount is positive and $room is smaller than $amount - Log::debug(sprintf('Room in piggy bank for extra money is %f', $room)); - Log::debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); - Log::debug(sprintf('New amount is %f', $room)); + app('log')->debug(sprintf('Room in piggy bank for extra money is %f', $room)); + app('log')->debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); + app('log')->debug(sprintf('New amount is %f', $room)); return $room; } // amount is negative and $currentamount is smaller than $amount if (bccomp($amount, '0') === -1 && 1 === bccomp($compare, $amount)) { - Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount)); - Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); - Log::debug(sprintf('New amount is %f', $compare)); + app('log')->debug(sprintf('Max amount to remove is %f', $repetition->currentamount)); + app('log')->debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); + app('log')->debug(sprintf('New amount is %f', $compare)); return $compare; } @@ -279,7 +278,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -301,13 +300,9 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function getNoteText(PiggyBank $piggyBank): string { - /** @var Note $note */ + /** @var Note|null $note */ $note = $piggyBank->notes()->first(); - if (null === $note) { - return ''; - } - - return $note->text; + return (string)$note?->text; } /** diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index 5687387227..4a67a0cae5 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -47,7 +47,6 @@ use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -69,23 +68,23 @@ class RecurringRepository implements RecurringRepositoryInterface { // if not, loop set and try to read the recurrence_date. If it matches start or end, return it as well. $set - = TransactionJournalMeta::where(function (Builder $q1) use ($recurrence) { - $q1->where('name', 'recurrence_id'); - $q1->where('data', json_encode((string)$recurrence->id)); - })->get(['journal_meta.transaction_journal_id']); + = TransactionJournalMeta::where(static function (Builder $q1) use ($recurrence) { + $q1->where('name', 'recurrence_id'); + $q1->where('data', json_encode((string)$recurrence->id)); + })->get(['journal_meta.transaction_journal_id']); // there are X journals made for this recurrence. Any of them meant for today? foreach ($set as $journalMeta) { - $count = TransactionJournalMeta::where(function (Builder $q2) use ($date) { + $count = TransactionJournalMeta::where(static function (Builder $q2) use ($date) { $string = (string)$date; - Log::debug(sprintf('Search for date: %s', json_encode($string))); + app('log')->debug(sprintf('Search for date: %s', json_encode($string))); $q2->where('name', 'recurrence_date'); $q2->where('data', json_encode($string)); }) ->where('transaction_journal_id', $journalMeta->transaction_journal_id) ->count(); if ($count > 0) { - Log::debug(sprintf('Looks like journal #%d was already created', $journalMeta->transaction_journal_id)); + app('log')->debug(sprintf('Looks like journal #%d was already created', $journalMeta->transaction_journal_id)); return true; } } @@ -239,7 +238,7 @@ class RecurringRepository implements RecurringRepositoryInterface if (null !== $end) { $query->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00')); } - return $query->count(['transaction_journals.id']); + return $query->count('transaction_journals.id'); } /** @@ -267,13 +266,9 @@ class RecurringRepository implements RecurringRepositoryInterface */ public function getNoteText(Recurrence $recurrence): string { - /** @var Note $note */ + /** @var Note|null $note */ $note = $recurrence->notes()->first(); - if (null !== $note) { - return (string)$note->text; - } - - return ''; + return (string)$note?->text; } /** @@ -350,7 +345,7 @@ class RecurringRepository implements RecurringRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -437,7 +432,7 @@ class RecurringRepository implements RecurringRepositoryInterface */ public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array { - Log::debug('Now in getXOccurrencesSince()'); + app('log')->debug('Now in getXOccurrencesSince()'); $skipMod = $repetition->repetition_skip + 1; $occurrences = []; if ('daily' === $repetition->repetition_type) { @@ -496,10 +491,14 @@ class RecurringRepository implements RecurringRepositoryInterface */ public function repetitionDescription(RecurrenceRepetition $repetition): string { - Log::debug('Now in repetitionDescription()'); + app('log')->debug('Now in repetitionDescription()'); /** @var Preference $pref */ $pref = app('preferences')->getForUser($this->user, 'language', config('firefly.default_language', 'en_US')); $language = $pref->data; + if (is_array($language)) { + $language = 'en_US'; + } + $language = (string)$language; if ('daily' === $repetition->repetition_type) { return (string)trans('firefly.recurring_daily', [], $language); } @@ -535,8 +534,11 @@ class RecurringRepository implements RecurringRepositoryInterface } if ('yearly' === $repetition->repetition_type) { // - $today = today(config('app.timezone'))->endOfYear(); - $repDate = Carbon::createFromFormat('Y-m-d', $repetition->repetition_moment); + $today = today(config('app.timezone'))->endOfYear(); + $repDate = Carbon::createFromFormat('Y-m-d', $repetition->repetition_moment); + if (false === $repDate) { + $repDate = clone $today; + } $diffInYears = $today->diffInYears($repDate); $repDate->addYears($diffInYears); // technically not necessary. $string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js')); @@ -574,12 +576,7 @@ class RecurringRepository implements RecurringRepositoryInterface /** @var RecurrenceFactory $factory */ $factory = app(RecurrenceFactory::class); $factory->setUser($this->user); - $result = $factory->create($data); - if (null === $result) { - throw new FireflyException($factory->getErrors()->first()); - } - - return $result; + return $factory->create($data); } /** @@ -622,8 +619,8 @@ class RecurringRepository implements RecurringRepositoryInterface $mutator = clone $start; $mutator->startOfDay(); $skipMod = $repetition->repetition_skip + 1; - Log::debug(sprintf('Calculating occurrences for rep type "%s"', $repetition->repetition_type)); - Log::debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d'))); + app('log')->debug(sprintf('Calculating occurrences for rep type "%s"', $repetition->repetition_type)); + app('log')->debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d'))); if ('daily' === $repetition->repetition_type) { $occurrences = $this->getDailyInRange($mutator, $end, $skipMod); diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index e34c731cc8..6e7a96e799 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -34,7 +34,6 @@ use FireflyIII\Support\Search\OperatorQuerySearch; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class RuleRepository. @@ -298,9 +297,9 @@ class RuleRepository implements RuleRepositoryInterface // reset order: $this->resetRuleOrder($ruleGroup); - Log::debug('Done with resetting.'); + app('log')->debug('Done with resetting.'); if (array_key_exists('order', $data)) { - Log::debug(sprintf('User has submitted order %d', $data['order'])); + app('log')->debug(sprintf('User has submitted order %d', $data['order'])); $this->setOrder($rule, $data['order']); } @@ -367,7 +366,7 @@ class RuleRepository implements RuleRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -377,11 +376,11 @@ class RuleRepository implements RuleRepositoryInterface */ public function setOrder(Rule $rule, int $newOrder): void { - $oldOrder = (int)$rule->order; - $groupId = (int)$rule->rule_group_id; + $oldOrder = $rule->order; + $groupId = $rule->rule_group_id; $maxOrder = $this->maxOrder($rule->ruleGroup); $newOrder = $newOrder > $maxOrder ? $maxOrder + 1 : $newOrder; - Log::debug(sprintf('New order will be %d', $newOrder)); + app('log')->debug(sprintf('New order will be %d', $newOrder)); if ($newOrder > $oldOrder) { $this->user->rules() @@ -391,7 +390,7 @@ class RuleRepository implements RuleRepositoryInterface ->where('rules.id', '!=', $rule->id) ->decrement('rules.order'); $rule->order = $newOrder; - Log::debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder)); + app('log')->debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder)); $rule->save(); return; @@ -404,7 +403,7 @@ class RuleRepository implements RuleRepositoryInterface ->where('rules.id', '!=', $rule->id) ->increment('rules.order'); $rule->order = $newOrder; - Log::debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder)); + app('log')->debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder)); $rule->save(); } diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index d26fe17600..5178ac123a 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -32,7 +32,6 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class RuleGroupRepository. @@ -152,8 +151,8 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface $count = 1; /** @var Rule $entry */ foreach ($set as $entry) { - if ((int)$entry->order !== $count) { - Log::debug(sprintf('Rule #%d was on spot %d but must be on spot %d', $entry->id, $entry->order, $count)); + if ($entry->order !== $count) { + app('log')->debug(sprintf('Rule #%d was on spot %d but must be on spot %d', $entry->id, $entry->order, $count)); $entry->order = $count; $entry->save(); } @@ -179,10 +178,10 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface $index = 1; /** @var RuleAction $action */ foreach ($actions as $action) { - if ((int)$action->order !== $index) { + if ($action->order !== $index) { $action->order = $index; $action->save(); - Log::debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index)); + app('log')->debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index)); } $index++; } @@ -201,11 +200,11 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface $index = 1; /** @var RuleTrigger $trigger */ foreach ($triggers as $trigger) { - $order = (int)$trigger->order; + $order = $trigger->order; if ($order !== $index) { $trigger->order = $index; $trigger->save(); - Log::debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index)); + app('log')->debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index)); } $index++; } @@ -319,23 +318,23 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface if (null === $filter) { return $groups; } - Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter)); + app('log')->debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter)); return $groups->map( - function (RuleGroup $group) use ($filter) { - Log::debug(sprintf('Now filtering group #%d', $group->id)); + static function (RuleGroup $group) use ($filter) { + app('log')->debug(sprintf('Now filtering group #%d', $group->id)); // filter the rules in the rule group: $group->rules = $group->rules->filter( - function (Rule $rule) use ($filter) { - Log::debug(sprintf('Now filtering rule #%d', $rule->id)); + static function (Rule $rule) use ($filter) { + app('log')->debug(sprintf('Now filtering rule #%d', $rule->id)); foreach ($rule->ruleTriggers as $trigger) { if ('user_action' === $trigger->trigger_type && $filter === $trigger->trigger_value) { - Log::debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter)); + app('log')->debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter)); return true; } } - Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter)); + app('log')->debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter)); return false; } @@ -382,23 +381,23 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface if (null === $filter) { return $groups; } - Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter)); + app('log')->debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter)); return $groups->map( - function (RuleGroup $group) use ($filter) { - Log::debug(sprintf('Now filtering group #%d', $group->id)); + static function (RuleGroup $group) use ($filter) { + app('log')->debug(sprintf('Now filtering group #%d', $group->id)); // filter the rules in the rule group: $group->rules = $group->rules->filter( - function (Rule $rule) use ($filter) { - Log::debug(sprintf('Now filtering rule #%d', $rule->id)); + static function (Rule $rule) use ($filter) { + app('log')->debug(sprintf('Now filtering rule #%d', $rule->id)); foreach ($rule->ruleTriggers as $trigger) { if ('user_action' === $trigger->trigger_type && $filter === $trigger->trigger_value) { - Log::debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter)); + app('log')->debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter)); return true; } } - Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter)); + app('log')->debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter)); return false; } @@ -448,7 +447,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -484,14 +483,14 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface */ public function setOrder(RuleGroup $ruleGroup, int $newOrder): void { - $oldOrder = (int)$ruleGroup->order; + $oldOrder = $ruleGroup->order; if ($newOrder > $oldOrder) { $this->user->ruleGroups()->where('rule_groups.order', '<=', $newOrder)->where('rule_groups.order', '>', $oldOrder) ->where('rule_groups.id', '!=', $ruleGroup->id) ->decrement('order'); $ruleGroup->order = $newOrder; - Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder)); + app('log')->debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder)); $ruleGroup->save(); return; @@ -501,7 +500,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface ->where('rule_groups.id', '!=', $ruleGroup->id) ->increment('order'); $ruleGroup->order = $newOrder; - Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder)); + app('log')->debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder)); $ruleGroup->save(); } diff --git a/app/Repositories/Tag/OperationsRepository.php b/app/Repositories/Tag/OperationsRepository.php index 874afc2f4e..f4b66aed92 100644 --- a/app/Repositories/Tag/OperationsRepository.php +++ b/app/Repositories/Tag/OperationsRepository.php @@ -67,7 +67,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $tags && $tags->count() > 0) { $collector->setTags($tags); } - if (null === $tags || (null !== $tags && 0 === $tags->count())) { + if (null === $tags || 0 === $tags->count()) { $collector->setTags($this->getTags()); } $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation()->withTagInformation(); @@ -76,7 +76,7 @@ class OperationsRepository implements OperationsRepositoryInterface $listedJournals = []; foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'tags' => [], 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -95,7 +95,7 @@ class OperationsRepository implements OperationsRepositoryInterface continue; } $listedJournals[] = $journalId; - $array[$currencyId]['tags'][$tagId] = $array[$currencyId]['tags'][$tagId] ?? [ + $array[$currencyId]['tags'][$tagId] ??= [ 'id' => $tagId, 'name' => $tagName, 'transaction_journals' => [], @@ -124,7 +124,7 @@ class OperationsRepository implements OperationsRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -166,7 +166,7 @@ class OperationsRepository implements OperationsRepositoryInterface if (null !== $tags && $tags->count() > 0) { $collector->setTags($tags); } - if (null === $tags || (null !== $tags && 0 === $tags->count())) { + if (null === $tags || 0 === $tags->count()) { $collector->setTags($this->getTags()); } $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation()->withTagInformation(); @@ -176,7 +176,7 @@ class OperationsRepository implements OperationsRepositoryInterface foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'tags' => [], 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -196,7 +196,7 @@ class OperationsRepository implements OperationsRepositoryInterface } $listedJournals[] = $journalId; - $array[$currencyId]['tags'][$tagId] = $array[$currencyId]['tags'][$tagId] ?? [ + $array[$currencyId]['tags'][$tagId] ??= [ 'id' => $tagId, 'name' => $tagName, 'transaction_journals' => [], diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 6b64f7e81f..6b72491531 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -36,7 +36,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Storage; /** @@ -88,7 +87,7 @@ class TagRepository implements TagRepositoryInterface */ public function get(): Collection { - return $this->user->tags()->orderBy('tag', 'ASC')->get(); + return $this->user->tags()->orderBy('tag', 'ASC')->get(['tags.*']); } /** @@ -114,7 +113,7 @@ class TagRepository implements TagRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -162,7 +161,7 @@ class TagRepository implements TagRepositoryInterface return $set->each( static function (Attachment $attachment) use ($disk) { - /** @var Note $note */ + /** @var Note|null $note */ $note = $attachment->notes()->first(); // only used in v1 view of tags $attachment->file_exists = $disk->exists($attachment->fileName()); @@ -183,12 +182,12 @@ class TagRepository implements TagRepositoryInterface // add date range (or not): if (null === $year) { - Log::debug('Get tags without a date.'); + app('log')->debug('Get tags without a date.'); $tagQuery->whereNull('tags.date'); } if (null !== $year) { - Log::debug(sprintf('Get tags with year %s.', $year)); + app('log')->debug(sprintf('Get tags with year %s.', $year)); $tagQuery->where('tags.date', '>=', $year . '-01-01 00:00:00')->where('tags.date', '<=', $year . '-12-31 23:59:59'); } $collection = $tagQuery->get(); @@ -330,7 +329,7 @@ class TagRepository implements TagRepositoryInterface /** @var array $journal */ foreach ($journals as $journal) { $currencyId = (int)$journal['currency_id']; - $sums[$currencyId] = $sums[$currencyId] ?? [ + $sums[$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], 'currency_symbol' => $journal['currency_symbol'], @@ -352,7 +351,7 @@ class TagRepository implements TagRepositoryInterface $foreignCurrencyId = $journal['foreign_currency_id']; if (null !== $foreignCurrencyId && 0 !== $foreignCurrencyId) { - $sums[$foreignCurrencyId] = $sums[$foreignCurrencyId] ?? [ + $sums[$foreignCurrencyId] ??= [ 'currency_id' => $foreignCurrencyId, 'currency_name' => $journal['foreign_currency_name'], 'currency_symbol' => $journal['foreign_currency_symbol'], @@ -375,6 +374,26 @@ class TagRepository implements TagRepositoryInterface return $sums; } + /** + * @inheritDoc + */ + public function tagEndsWith(string $query): Collection + { + $search = sprintf('%%%s', $query); + + return $this->user->tags()->where('tag', 'LIKE', $search)->get(['tags.*']); + } + + /** + * @inheritDoc + */ + public function tagStartsWith(string $query): Collection + { + $search = sprintf('%s%%', $query); + + return $this->user->tags()->where('tag', 'LIKE', $search)->get(['tags.*']); + } + /** * @param Tag $tag * @param Carbon $start diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index 8343eb4725..6e667964d8 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -188,6 +188,24 @@ interface TagRepositoryInterface */ public function sumsOfTag(Tag $tag, ?Carbon $start, ?Carbon $end): array; + /** + * Find one or more tags that start with the string in the query + * + * @param string $query + * + * @return Collection + */ + public function tagEndsWith(string $query): Collection; + + /** + * Find one or more tags that start with the string in the query + * + * @param string $query + * + * @return Collection + */ + public function tagStartsWith(string $query): Collection; + /** * @param Tag $tag * @param Carbon $start diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index aeb9411303..7bd677d8e7 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -48,7 +48,6 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -86,7 +85,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface */ public function destroy(TransactionGroup $group): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $service = new TransactionGroupDestroyService(); $service->destroy($group); } @@ -176,8 +175,8 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface $result = []; /** @var Attachment $attachment */ foreach ($set as $attachment) { - $journalId = (int)$attachment->attachable_id; - $result[$journalId] = $result[$journalId] ?? []; + $journalId = $attachment->attachable_id; + $result[$journalId] ??= []; $current = $attachment->toArray(); $current['file_exists'] = true; $current['notes'] = $repository->getNoteText($attachment); @@ -194,7 +193,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof user) { $this->user = $user; } } @@ -242,7 +241,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface /** @var TransactionJournalLink $entry */ foreach ($set as $entry) { $journalId = in_array($entry->source_id, $journals, true) ? $entry->source_id : $entry->destination_id; - $return[$journalId] = $return[$journalId] ?? []; + $return[$journalId] ??= []; // phpstan: the editable field is provided by the query. @@ -402,7 +401,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface { $return = []; $journals = $group->transactionJournals->pluck('id')->toArray(); - $currency = app('amount')->getDefaultCurrencyByUser($this->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); $data = PiggyBankEvent::whereIn('transaction_journal_id', $journals) ->with('piggyBank', 'piggyBank.account') ->get(['piggy_bank_events.*']); @@ -418,8 +417,8 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface if (null !== $currencyPreference) { $currency = TransactionCurrency::where('id', $currencyPreference->data)->first(); } - $journalId = (int)$row->transaction_journal_id; - $return[$journalId] = $return[$journalId] ?? []; + $journalId = $row->transaction_journal_id; + $return[$journalId] ??= []; $return[$journalId][] = [ 'piggy' => $row->piggyBank->name, @@ -480,8 +479,8 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface throw new DuplicateTransactionException($e->getMessage(), 0, $e); } catch (FireflyException $e) { app('log')->warning('Group repository caught group factory with an exception!'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException($e->getMessage(), 0, $e); } } diff --git a/app/Repositories/TransactionType/TransactionTypeRepository.php b/app/Repositories/TransactionType/TransactionTypeRepository.php index e5db6a1308..529c7f7bb0 100644 --- a/app/Repositories/TransactionType/TransactionTypeRepository.php +++ b/app/Repositories/TransactionType/TransactionTypeRepository.php @@ -25,7 +25,6 @@ namespace FireflyIII\Repositories\TransactionType; use FireflyIII\Models\TransactionType; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class TransactionTypeRepository @@ -40,18 +39,18 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface */ public function findTransactionType(?TransactionType $type, ?string $typeString): TransactionType { - Log::debug('Now looking for a transaction type.'); + app('log')->debug('Now looking for a transaction type.'); if (null !== $type) { - Log::debug(sprintf('Found $type in parameters, its %s. Will return it.', $type->type)); + app('log')->debug(sprintf('Found $type in parameters, its %s. Will return it.', $type->type)); return $type; } - $typeString = $typeString ?? TransactionType::WITHDRAWAL; + $typeString ??= TransactionType::WITHDRAWAL; $search = $this->findByType($typeString); if (null === $search) { $search = $this->findByType(TransactionType::WITHDRAWAL); } - Log::debug(sprintf('Tried to search for "%s", came up with "%s". Will return it.', $typeString, $search->type)); + app('log')->debug(sprintf('Tried to search for "%s", came up with "%s". Will return it.', $typeString, $search->type)); return $search; } diff --git a/app/Repositories/User/UserRepository.php b/app/Repositories/User/UserRepository.php index 73b9c1edad..b2430d05ed 100644 --- a/app/Repositories/User/UserRepository.php +++ b/app/Repositories/User/UserRepository.php @@ -34,7 +34,6 @@ use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\QueryException; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Str; /** @@ -124,7 +123,7 @@ class UserRepository implements UserRepositoryInterface */ public function deleteInvite(InvitedUser $invite): void { - Log::debug(sprintf('Deleting invite #%d', $invite->id)); + app('log')->debug(sprintf('Deleting invite #%d', $invite->id)); $invite->delete(); } @@ -136,7 +135,7 @@ class UserRepository implements UserRepositoryInterface */ public function destroy(User $user): bool { - Log::debug(sprintf('Calling delete() on user %d', $user->id)); + app('log')->debug(sprintf('Calling delete() on user %d', $user->id)); $user->groupMemberships()->delete(); $user->delete(); @@ -155,7 +154,7 @@ class UserRepository implements UserRepositoryInterface foreach ($groups as $group) { $count = $group->groupMemberships()->count(); if (0 === $count) { - Log::info(sprintf('Deleted empty group #%d ("%s")', $group->id, $group->title)); + app('log')->info(sprintf('Deleted empty group #%d ("%s")', $group->id, $group->title)); $group->delete(); } } @@ -227,7 +226,7 @@ class UserRepository implements UserRepositoryInterface */ public function getRolesInGroup(User $user, int $groupId): array { - /** @var UserGroup $group */ + /** @var UserGroup|null $group */ $group = UserGroup::find($groupId); if (null === $group) { throw new FireflyException(sprintf('Could not find group #%d', $groupId)); @@ -300,10 +299,12 @@ class UserRepository implements UserRepositoryInterface if (null === $user) { return false; } - /** @var Role $userRole */ - foreach ($user->roles as $userRole) { - if ($userRole->name === $role) { - return true; + if ($user instanceof User) { + /** @var Role $userRole */ + foreach ($user->roles as $userRole) { + if ($userRole->name === $role) { + return true; + } } } @@ -334,7 +335,7 @@ class UserRepository implements UserRepositoryInterface public function redeemCode(string $code): void { $obj = InvitedUser::where('invite_code', $code)->where('redeemed', 0)->first(); - if ($obj) { + if (null !== $obj) { $obj->redeemed = true; $obj->save(); } @@ -385,7 +386,7 @@ class UserRepository implements UserRepositoryInterface { $roleObject = Role::where('name', $role)->first(); if (null === $roleObject) { - Log::error(sprintf('Could not find role "%s" in attachRole()', $role)); + app('log')->error(sprintf('Could not find role "%s" in attachRole()', $role)); return false; } @@ -394,7 +395,7 @@ class UserRepository implements UserRepositoryInterface $user->roles()->attach($roleObject); } catch (QueryException $e) { // don't care - Log::error(sprintf('Query exception when giving user a role: %s', $e->getMessage())); + app('log')->error(sprintf('Query exception when giving user a role: %s', $e->getMessage())); } return true; diff --git a/app/Repositories/UserGroup/UserGroupRepository.php b/app/Repositories/UserGroup/UserGroupRepository.php index 7be53c78f8..a12b5da7d1 100644 --- a/app/Repositories/UserGroup/UserGroupRepository.php +++ b/app/Repositories/UserGroup/UserGroupRepository.php @@ -52,7 +52,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface $memberships = $userGroup->groupMemberships()->get(); /** @var GroupMembership $membership */ foreach ($memberships as $membership) { - /** @var User $user */ + /** @var User|null $user */ $user = $membership->user()->first(); if (null === $user) { continue; @@ -85,7 +85,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface 'recurrences', 'rules', 'ruleGroups', 'tags', 'transactionGroups', 'transactionJournals', 'piggyBanks', 'accounts', 'webhooks', ]; foreach ($objects as $object) { - foreach ($userGroup->$object()->get() as $item) { + foreach ($userGroup->$object()->get() as $item) { // @phpstan-ignore-line $item->delete(); } } @@ -104,7 +104,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface $memberships = $this->user->groupMemberships()->get(); /** @var GroupMembership $membership */ foreach ($memberships as $membership) { - /** @var UserGroup $group */ + /** @var UserGroup|null $group */ $group = $membership->userGroup()->first(); if (null !== $group) { $collection->push($group); @@ -130,12 +130,13 @@ class UserGroupRepository implements UserGroupRepositoryInterface while ($exists && $loop < 10) { $existingGroup = $this->findByName($groupName); if (null === $existingGroup) { - $exists = false; + $exists = false; + /** @var UserGroup|null $existingGroup */ $existingGroup = $this->store(['user' => $user, 'title' => $groupName]); } if (null !== $existingGroup) { // group already exists - $groupName = sprintf('%s-%s', $user->email, substr(sha1((string)(rand(1000, 9999) . microtime())), 0, 4)); + $groupName = sprintf('%s-%s', $user->email, substr(sha1((rand(1000, 9999) . microtime())), 0, 4)); } $loop++; } @@ -182,7 +183,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface public function setUser(Authenticatable | User | null $user): void { app('log')->debug(sprintf('Now in %s', __METHOD__)); - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } @@ -205,12 +206,15 @@ class UserGroupRepository implements UserGroupRepositoryInterface { $owner = UserRole::whereTitle(UserRoleEnum::OWNER)->first(); app('log')->debug('in update membership'); + /** @var User|null $user */ $user = null; if (array_key_exists('id', $data)) { + /** @var User|null $user */ $user = User::find($data['id']); app('log')->debug('Found user by ID'); } if (array_key_exists('email', $data) && '' !== (string)$data['email']) { + /** @var User|null $user */ $user = User::whereEmail($data['email'])->first(); app('log')->debug('Found user by email'); } @@ -220,11 +224,11 @@ class UserGroupRepository implements UserGroupRepositoryInterface return $userGroup; } // count the number of members in the group right now: - $membershipCount = $userGroup->groupMemberships()->distinct()->get(['group_memberships.user_id'])->count(); + $membershipCount = $userGroup->groupMemberships()->distinct()->count('group_memberships.user_id'); // if it's 1: if (1 === $membershipCount) { - $lastUserId = (int)$userGroup->groupMemberships()->distinct()->first(['group_memberships.user_id'])->user_id; + $lastUserId = $userGroup->groupMemberships()->distinct()->first(['group_memberships.user_id'])->user_id; // if this is also the user we're editing right now, and we remove all of their roles: if ($lastUserId === (int)$user->id && 0 === count($data['roles'])) { app('log')->debug('User is last in this group, refuse to act'); @@ -242,7 +246,11 @@ class UserGroupRepository implements UserGroupRepositoryInterface ->where('user_role_id', $owner->id) ->where('user_id', '!=', $user->id)->count(); // if there are no other owners and the current users does not get or keep the owner role, refuse. - if (0 === $ownerCount && (0 === count($data['roles']) || (count($data['roles']) > 0 && !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)))) { + if ( + 0 === $ownerCount && + (0 === count($data['roles']) || + (count($data['roles']) > 0 && // @phpstan-ignore-line + !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)))) { app('log')->debug('User needs to keep owner role in this group, refuse to act'); throw new FireflyException('The last owner in this user group must keep the "owner" role.'); } @@ -273,11 +281,11 @@ class UserGroupRepository implements UserGroupRepositoryInterface private function simplifyListByName(array $roles): array { if (in_array(UserRoleEnum::OWNER->value, $roles, true)) { - app('log')->debug(sprintf('List of roles is [%1$s] but this includes "%2$s", so return [%2$s]', join(',', $roles), UserRoleEnum::OWNER->value)); + app('log')->debug(sprintf('List of roles is [%1$s] but this includes "%2$s", so return [%2$s]', implode(',', $roles), UserRoleEnum::OWNER->value)); return [UserRoleEnum::OWNER->value]; } if (in_array(UserRoleEnum::FULL->value, $roles, true)) { - app('log')->debug(sprintf('List of roles is [%1$s] but this includes "%2$s", so return [%2$s]', join(',', $roles), UserRoleEnum::FULL->value)); + app('log')->debug(sprintf('List of roles is [%1$s] but this includes "%2$s", so return [%2$s]', implode(',', $roles), UserRoleEnum::FULL->value)); return [UserRoleEnum::FULL->value]; } return $roles; diff --git a/app/Repositories/UserGroups/Account/AccountRepository.php b/app/Repositories/UserGroups/Account/AccountRepository.php index 937d504af7..432107147a 100644 --- a/app/Repositories/UserGroups/Account/AccountRepository.php +++ b/app/Repositories/UserGroups/Account/AccountRepository.php @@ -30,8 +30,8 @@ use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; +use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class AccountRepository @@ -40,6 +40,50 @@ class AccountRepository implements AccountRepositoryInterface { use UserGroupTrait; + /** + * @inheritDoc + */ + public function findByAccountNumber(string $number, array $types): ?Account + { + $dbQuery = $this->userGroup + ->accounts() + ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') + ->where('accounts.active', true) + ->where( + static function (EloquentBuilder $q1) use ($number) { // @phpstan-ignore-line + $json = json_encode($number); + $q1->where('account_meta.name', '=', 'account_number'); + $q1->where('account_meta.data', '=', $json); + } + ); + + if (0 !== count($types)) { + $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $dbQuery->whereIn('account_types.type', $types); + } + /** @var Account|null */ + return $dbQuery->first(['accounts.*']); + } + + /** + * @param string $iban + * @param array $types + * + * @return Account|null + */ + public function findByIbanNull(string $iban, array $types): ?Account + { + $query = $this->userGroup->accounts()->where('iban', '!=', '')->whereNotNull('iban'); + + if (0 !== count($types)) { + $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $query->whereIn('account_types.type', $types); + } + + /** @var Account|null */ + return $query->where('iban', $iban)->first(['accounts.*']); + } + /** * @inheritDoc */ @@ -51,17 +95,17 @@ class AccountRepository implements AccountRepositoryInterface $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); $query->whereIn('account_types.type', $types); } - Log::debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]); + app('log')->debug(sprintf('Searching for account named "%s" (of user #%d) of the following type(s)', $name, $this->user->id), ['types' => $types]); $query->where('accounts.name', $name); - /** @var Account $account */ + /** @var Account|null $account */ $account = $query->first(['accounts.*']); if (null === $account) { - Log::debug(sprintf('There is no account with name "%s" of types', $name), $types); + app('log')->debug(sprintf('There is no account with name "%s" of types', $name), $types); return null; } - Log::debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id)); + app('log')->debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id)); return $account; } @@ -99,7 +143,7 @@ class AccountRepository implements AccountRepositoryInterface public function getMetaValue(Account $account, string $field): ?string { $result = $account->accountMeta->filter( - function (AccountMeta $meta) use ($field) { + static function (AccountMeta $meta) use ($field) { return strtolower($meta->name) === strtolower($field); } ); diff --git a/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php b/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php index 379615acbe..60d17c1ff3 100644 --- a/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php +++ b/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php @@ -27,6 +27,8 @@ namespace FireflyIII\Repositories\UserGroups\Account; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Models\UserGroup; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -41,6 +43,22 @@ interface AccountRepositoryInterface */ public function find(int $accountId): ?Account; + /** + * @param string $number + * @param array $types + * + * @return Account|null + */ + public function findByAccountNumber(string $number, array $types): ?Account; + + /** + * @param string $iban + * @param array $types + * + * @return Account|null + */ + public function findByIbanNull(string $iban, array $types): ?Account; + /** * @param string $name * @param array $types @@ -97,5 +115,19 @@ interface AccountRepositoryInterface */ public function searchAccount(string $query, array $types, int $limit): Collection; + /** + * @param User $user + * + * @return void + */ + public function setUser(User $user): void; + + /** + * @param UserGroup $userGroup + * + * @return void + */ + public function setUserGroup(UserGroup $userGroup): void; + } diff --git a/app/Repositories/UserGroups/Bill/BillRepository.php b/app/Repositories/UserGroups/Bill/BillRepository.php index 94c985eef0..5fe1f3f012 100644 --- a/app/Repositories/UserGroups/Bill/BillRepository.php +++ b/app/Repositories/UserGroups/Bill/BillRepository.php @@ -49,7 +49,7 @@ class BillRepository implements BillRepositoryInterface $set = $this->userGroup->bills()->orderBy('order', 'ASC')->get(); $current = 1; foreach ($set as $bill) { - if ((int)$bill->order !== $current) { + if ($bill->order !== $current) { $bill->order = $current; $bill->save(); } @@ -81,19 +81,19 @@ class BillRepository implements BillRepositoryInterface /** @var Collection $set */ $set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']); $currency = $bill->transactionCurrency; - $currencyId = (int)$bill->transaction_currency_id; + $currencyId = $bill->transaction_currency_id; - $return[$currencyId] = $return[$currencyId] ?? [ + $return[$currencyId] ??= [ 'currency_id' => (string)$currency->id, 'currency_name' => $currency->name, 'currency_symbol' => $currency->symbol, 'currency_code' => $currency->code, - 'currency_decimal_places' => (int)$currency->decimal_places, + 'currency_decimal_places' => $currency->decimal_places, 'native_id' => (string)$default->id, 'native_name' => $default->name, 'native_symbol' => $default->symbol, 'native_code' => $default->code, - 'native_decimal_places' => (int)$default->decimal_places, + 'native_decimal_places' => $default->decimal_places, 'sum' => '0', 'native_sum' => '0', ]; @@ -103,18 +103,18 @@ class BillRepository implements BillRepositoryInterface /** @var Transaction|null $sourceTransaction */ $sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first(); if (null !== $sourceTransaction) { - $amount = (string)$sourceTransaction->amount; - if ((int)$sourceTransaction->foreign_currency_id === (int)$currency->id) { + $amount = $sourceTransaction->amount; + if ((int)$sourceTransaction->foreign_currency_id === $currency->id) { // use foreign amount instead! $amount = (string)$sourceTransaction->foreign_amount; } // convert to native currency $nativeAmount = $amount; - if ($currencyId !== (int)$default->id) { + if ($currencyId !== $default->id) { // get rate and convert. $nativeAmount = $converter->convert($currency, $default, $transactionJournal->date, $amount); } - if ((int)$sourceTransaction->foreign_currency_id === (int)$default->id) { + if ((int)$sourceTransaction->foreign_currency_id === $default->id) { // ignore conversion, use foreign amount $nativeAmount = (string)$sourceTransaction->foreign_amount; } @@ -154,20 +154,20 @@ class BillRepository implements BillRepositoryInterface if ($total > 0) { $currency = $bill->transactionCurrency; - $currencyId = (int)$bill->transaction_currency_id; + $currencyId = $bill->transaction_currency_id; $average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2'); $nativeAverage = $converter->convert($currency, $default, $start, $average); - $return[$currencyId] = $return[$currencyId] ?? [ + $return[$currencyId] ??= [ 'currency_id' => (string)$currency->id, 'currency_name' => $currency->name, 'currency_symbol' => $currency->symbol, 'currency_code' => $currency->code, - 'currency_decimal_places' => (int)$currency->decimal_places, + 'currency_decimal_places' => $currency->decimal_places, 'native_id' => (string)$default->id, 'native_name' => $default->name, 'native_symbol' => $default->symbol, 'native_code' => $default->code, - 'native_decimal_places' => (int)$default->decimal_places, + 'native_decimal_places' => $default->decimal_places, 'sum' => '0', 'native_sum' => '0', ]; @@ -193,21 +193,21 @@ class BillRepository implements BillRepositoryInterface { $set = new Collection(); $currentStart = clone $start; - //Log::debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); - //Log::debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); + //app('log')->debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); + //app('log')->debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); while ($currentStart <= $end) { - //Log::debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); + //app('log')->debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - //Log::debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + //app('log')->debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); if ($nextExpectedMatch > $end) {// If nextExpectedMatch is after end, we continue break; } $set->push(clone $nextExpectedMatch); - //Log::debug(sprintf('Now %d dates in set.', $set->count())); + //app('log')->debug(sprintf('Now %d dates in set.', $set->count())); $nextExpectedMatch->addDay(); - //Log::debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + //app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); $currentStart = clone $nextExpectedMatch; } diff --git a/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php b/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php index ba68704938..6a5882bdf2 100644 --- a/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php +++ b/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php @@ -27,6 +27,7 @@ namespace FireflyIII\Repositories\UserGroups\Bill; use Carbon\Carbon; use FireflyIII\Models\Bill; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -76,6 +77,13 @@ interface BillRepositoryInterface */ public function nextDateMatch(Bill $bill, Carbon $date): Carbon; + /** + * @param User $user + * + * @return void + */ + public function setUser(User $user): void; + /** * Collect multi-currency of sum of bills already paid. * diff --git a/app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php b/app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php index 29748532eb..15d1d908ea 100644 --- a/app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php +++ b/app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php @@ -53,18 +53,18 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface ->where('end_date', $end->format('Y-m-d'))->get(); /** @var AvailableBudget $availableBudget */ foreach ($availableBudgets as $availableBudget) { - $currencyId = (int)$availableBudget->transaction_currency_id; - $return[$currencyId] = $return[$currencyId] ?? [ + $currencyId = $availableBudget->transaction_currency_id; + $return[$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_code' => $availableBudget->transactionCurrency->code, 'currency_symbol' => $availableBudget->transactionCurrency->symbol, 'currency_name' => $availableBudget->transactionCurrency->name, - 'currency_decimal_places' => (int)$availableBudget->transactionCurrency->decimal_places, + 'currency_decimal_places' => $availableBudget->transactionCurrency->decimal_places, 'native_id' => $default->id, 'native_code' => $default->code, 'native_symbol' => $default->symbol, 'native_name' => $default->name, - 'native_decimal_places' => (int)$default->decimal_places, + 'native_decimal_places' => $default->decimal_places, 'amount' => '0', 'native_amount' => '0', ]; diff --git a/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php b/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php index 6a24ad24da..8008460342 100644 --- a/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php +++ b/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\UserGroups\Budget; use Carbon\Carbon; +use FireflyIII\User; /** * Interface AvailableBudgetRepositoryInterface @@ -40,4 +41,11 @@ interface AvailableBudgetRepositoryInterface */ public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array; + /** + * @param User $user + * + * @return void + */ + public function setUser(User $user): void; + } diff --git a/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php b/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php index 6d9ef85332..1230ea6a36 100644 --- a/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php @@ -25,6 +25,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\UserGroups\Budget; +use FireflyIII\Models\UserGroup; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -36,4 +38,18 @@ interface BudgetRepositoryInterface * @return Collection */ public function getActiveBudgets(): Collection; + + /** + * @param User $user + * + * @return void + */ + public function setUser(User $user): void; + + /** + * @param UserGroup $userGroup + * + * @return void + */ + public function setUserGroup(UserGroup $userGroup): void; } diff --git a/app/Repositories/UserGroups/Budget/OperationsRepository.php b/app/Repositories/UserGroups/Budget/OperationsRepository.php index 919a9f6414..f5e82482db 100644 --- a/app/Repositories/UserGroups/Budget/OperationsRepository.php +++ b/app/Repositories/UserGroups/Budget/OperationsRepository.php @@ -72,7 +72,7 @@ class OperationsRepository implements OperationsRepositoryInterface } // info about the currency: - $array[$currencyId] = $array[$currencyId] ?? [ + $array[$currencyId] ??= [ 'budgets' => [], 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -82,7 +82,7 @@ class OperationsRepository implements OperationsRepositoryInterface ]; // info about the budgets: - $array[$currencyId]['budgets'][$budgetId] = $array[$currencyId]['budgets'][$budgetId] ?? [ + $array[$currencyId]['budgets'][$budgetId] ??= [ 'id' => $budgetId, 'name' => $budgetName, 'transaction_journals' => [], diff --git a/app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php b/app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php index d6313824dc..6bc5fa5ade 100644 --- a/app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php +++ b/app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\UserGroups\Budget; use Carbon\Carbon; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -46,4 +47,11 @@ interface OperationsRepositoryInterface * @return array */ public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array; + + /** + * @param User $user + * + * @return void + */ + public function setUser(User $user): void; } diff --git a/app/Repositories/UserGroups/Currency/CurrencyRepository.php b/app/Repositories/UserGroups/Currency/CurrencyRepository.php index a421b9c760..27cb8dbe0c 100644 --- a/app/Repositories/UserGroups/Currency/CurrencyRepository.php +++ b/app/Repositories/UserGroups/Currency/CurrencyRepository.php @@ -39,7 +39,6 @@ use FireflyIII\Services\Internal\Destroy\CurrencyDestroyService; use FireflyIII\Services\Internal\Update\CurrencyUpdateService; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -114,7 +113,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface // is being used in accounts (as integer) $meta = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id') ->whereNull('accounts.deleted_at') - ->where('account_meta.name', 'currency_id')->where('account_meta.data', json_encode((int)$currency->id))->count(); + ->where('account_meta.name', 'currency_id')->where('account_meta.data', json_encode($currency->id))->count(); if ($meta > 0) { app('log')->info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); @@ -180,12 +179,12 @@ class CurrencyRepository implements CurrencyRepositoryInterface { $all = TransactionCurrency::orderBy('code', 'ASC')->get(); $local = $this->get(); - return $all->map(function (TransactionCurrency $current) use ($local) { - $hasId = $local->contains(function (TransactionCurrency $entry) use ($current) { - return (int)$entry->id === (int)$current->id; + return $all->map(static function (TransactionCurrency $current) use ($local) { + $hasId = $local->contains(static function (TransactionCurrency $entry) use ($current) { + return $entry->id === $current->id; }); - $isDefault = $local->contains(function (TransactionCurrency $entry) use ($current) { - return 1 === (int)$entry->pivot->group_default && (int)$entry->id === (int)$current->id; + $isDefault = $local->contains(static function (TransactionCurrency $entry) use ($current) { + return 1 === (int)$entry->pivot->group_default && $entry->id === $current->id; }); $current->userEnabled = $hasId; $current->userDefault = $isDefault; @@ -194,14 +193,12 @@ class CurrencyRepository implements CurrencyRepositoryInterface } /** - * Get the user group's currencies. - * - * @return Collection + * @inheritDoc */ public function get(): Collection { $all = $this->userGroup->currencies()->orderBy('code', 'ASC')->withPivot(['group_default'])->get(); - $all->map(function (TransactionCurrency $current) { + $all->map(static function (TransactionCurrency $current) { $current->userEnabled = true; $current->userDefault = 1 === (int)$current->pivot->group_default; return $current; @@ -262,17 +259,14 @@ class CurrencyRepository implements CurrencyRepositoryInterface $result = $this->findCurrencyNull($currencyId, $currencyCode); if (null === $result) { - Log::debug('Grabbing default currency for this user...'); - $result = app('amount')->getDefaultCurrencyByUser($this->user); + app('log')->debug('Grabbing default currency for this user...'); + /** @var TransactionCurrency|null $result */ + $result = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); } - if (null === $result) { - Log::debug('Grabbing EUR as fallback.'); - $result = $this->findByCode('EUR'); - } - Log::debug(sprintf('Final result: %s', $result->code)); + app('log')->debug(sprintf('Final result: %s', $result->code)); if (false === $result->enabled) { - Log::debug(sprintf('Also enabled currency %s', $result->code)); + app('log')->debug(sprintf('Also enabled currency %s', $result->code)); $this->enable($result); } @@ -289,14 +283,14 @@ class CurrencyRepository implements CurrencyRepositoryInterface */ public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency { - Log::debug('Now in findCurrencyNull()'); + app('log')->debug('Now in findCurrencyNull()'); $result = $this->find((int)$currencyId); if (null === $result) { - Log::debug(sprintf('Searching for currency with code %s...', $currencyCode)); + app('log')->debug(sprintf('Searching for currency with code %s...', $currencyCode)); $result = $this->findByCode((string)$currencyCode); } if (null !== $result && false === $result->enabled) { - Log::debug(sprintf('Also enabled currency %s', $result->code)); + app('log')->debug(sprintf('Also enabled currency %s', $result->code)); $this->enable($result); } diff --git a/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php b/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php index 1ba5fe9667..a1ca8278c9 100644 --- a/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php @@ -27,6 +27,7 @@ namespace FireflyIII\Repositories\UserGroups\Currency; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\User; use Illuminate\Support\Collection; interface CurrencyRepositoryInterface @@ -114,7 +115,7 @@ interface CurrencyRepositoryInterface /** * Get the user group's currencies. * - * @return Collection + * @return Collection */ public function get(): Collection; @@ -154,6 +155,13 @@ interface CurrencyRepositoryInterface */ public function searchCurrency(string $search, int $limit): Collection; + /** + * @param User $user + * + * @return void + */ + public function setUser(User $user): void; + /** * @param array $data * diff --git a/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php b/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php index 5a2cd1b938..5ecc648d1b 100644 --- a/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\UserGroups\Journal; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -39,4 +40,11 @@ interface JournalRepositoryInterface * @return Collection */ public function searchJournalDescriptions(string $search, int $limit): Collection; + + /** + * @param User $user + * + * @return void + */ + public function setUser(User $user): void; } diff --git a/app/Repositories/Webhook/WebhookRepository.php b/app/Repositories/Webhook/WebhookRepository.php index 451e376bae..9bd15a33fb 100644 --- a/app/Repositories/Webhook/WebhookRepository.php +++ b/app/Repositories/Webhook/WebhookRepository.php @@ -98,7 +98,7 @@ class WebhookRepository implements WebhookRepositoryInterface ->where('webhook_messages.errored', 0) ->get(['webhook_messages.*']) ->filter( - function (WebhookMessage $message) { + static function (WebhookMessage $message) { return $message->webhookAttempts()->count() <= 2; } )->splice(0, 3); @@ -109,7 +109,7 @@ class WebhookRepository implements WebhookRepositoryInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } diff --git a/app/Rules/BelongsUser.php b/app/Rules/BelongsUser.php index 296dc580b8..40eb1c625b 100644 --- a/app/Rules/BelongsUser.php +++ b/app/Rules/BelongsUser.php @@ -33,7 +33,6 @@ use FireflyIII\Models\Category; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionJournal; use Illuminate\Contracts\Validation\ValidationRule; -use Illuminate\Support\Facades\Log; /** * Class BelongsUser @@ -50,8 +49,7 @@ class BelongsUser implements ValidationRule $fail('validation.belongs_user')->translate(); return; } - $attribute = (string)$attribute; - Log::debug(sprintf('Going to validate %s', $attribute)); + app('log')->debug(sprintf('Going to validate %s', $attribute)); $result = match ($attribute) { 'piggy_bank_id' => $this->validatePiggyBankId((int)$value), @@ -136,11 +134,11 @@ class BelongsUser implements ValidationRule } $count = 0; foreach ($objects as $object) { - $objectValue = trim((string)$object->$field); - Log::debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); + $objectValue = trim((string)$object->$field); // @phpstan-ignore-line + app('log')->debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); if ($objectValue === $value) { $count++; - Log::debug(sprintf('Hit! Count is now %d', $count)); + app('log')->debug(sprintf('Hit! Count is now %d', $count)); } } @@ -185,7 +183,7 @@ class BelongsUser implements ValidationRule private function validateBillName(string $value): bool { $count = $this->countField(Bill::class, 'name', $value); - Log::debug(sprintf('Result of countField for bill name "%s" is %d', $value, $count)); + app('log')->debug(sprintf('Result of countField for bill name "%s" is %d', $value, $count)); return 1 === $count; } diff --git a/app/Rules/BelongsUserGroup.php b/app/Rules/BelongsUserGroup.php index 54cc6a57e2..e74194bf6a 100644 --- a/app/Rules/BelongsUserGroup.php +++ b/app/Rules/BelongsUserGroup.php @@ -33,7 +33,6 @@ use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\UserGroup; use Illuminate\Contracts\Validation\ValidationRule; -use Illuminate\Support\Facades\Log; /** * Class BelongsUserGroup @@ -65,8 +64,7 @@ class BelongsUserGroup implements ValidationRule $fail('validation.belongs_user_or_user_group')->translate(); return; } - $attribute = (string)$attribute; - Log::debug(sprintf('Group: Going to validate "%s"', $attribute)); + app('log')->debug(sprintf('Group: Going to validate "%s"', $attribute)); $result = match ($attribute) { 'piggy_bank_id' => $this->validatePiggyBankId((int)$value), @@ -151,7 +149,7 @@ class BelongsUserGroup implements ValidationRule } $count = 0; foreach ($objects as $object) { - $objectValue = trim((string)$object->$field); + $objectValue = trim((string)$object->$field); // @phpstan-ignore-line app('log')->debug(sprintf('Comparing object "%s" with value "%s"', $objectValue, $value)); if ($objectValue === $value) { $count++; diff --git a/app/Rules/IsAssetAccountId.php b/app/Rules/IsAssetAccountId.php index 46d2e04e42..e77e5c2fae 100644 --- a/app/Rules/IsAssetAccountId.php +++ b/app/Rules/IsAssetAccountId.php @@ -23,45 +23,37 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use Illuminate\Contracts\Validation\Rule; +use Illuminate\Contracts\Validation\ValidationRule; /** * * Class IsAssetAccountId */ -class IsAssetAccountId implements Rule +class IsAssetAccountId implements ValidationRule { /** - * Get the validation error message. This is not translated because only the API uses it. + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @return string + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function message(): string - { - return 'This is not an asset account.'; - } - - /** - * Determine if the validation rule passes. - * - * @param string $attribute - * @param mixed $value - * - * @return bool - */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { $accountId = (int)$value; - $account = Account::with('accountType')->find($accountId); + /** @var Account|null $account */ + $account = Account::with('accountType')->find($accountId); if (null === $account) { - return false; + $fail('validation.no_asset_account')->translate(); + return; } if ($account->accountType->type !== AccountType::ASSET && $account->accountType->type !== AccountType::DEFAULT) { - return false; + $fail('validation.no_asset_account')->translate(); } - - return true; } } diff --git a/app/Rules/IsBoolean.php b/app/Rules/IsBoolean.php index 3019b38ad5..bd45f1f7b3 100644 --- a/app/Rules/IsBoolean.php +++ b/app/Rules/IsBoolean.php @@ -24,46 +24,37 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class IsBoolean */ -class IsBoolean implements Rule +class IsBoolean implements ValidationRule { /** - * Get the validation error message. + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @return string + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function message(): string - { - return (string)trans('validation.boolean'); - } - - /** - * Determine if the validation rule passes. - * - * @param string $attribute - * @param mixed $value - * - * @return bool - */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { if (is_bool($value)) { - return true; + return; } - if (is_int($value) && 0 === $value) { - return true; + if (0 === $value) { + return; } - if (is_int($value) && 1 === $value) { - return true; + if (1 === $value) { + return; } - if (is_string($value) && in_array($value, ['0', '1', 'true', 'false', 'on', 'off', 'yes', 'no', 'y', 'n'], true)) { - return true; + if (in_array($value, ['0', '1', 'true', 'false', 'on', 'off', 'yes', 'no', 'y', 'n'], true)) { + return; } - - return false; + $fail('validation.boolean')->translate(); } } diff --git a/app/Rules/IsDateOrTime.php b/app/Rules/IsDateOrTime.php index 8a6132ae58..f3e3284efe 100644 --- a/app/Rules/IsDateOrTime.php +++ b/app/Rules/IsDateOrTime.php @@ -27,67 +27,61 @@ namespace FireflyIII\Rules; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; use Carbon\Exceptions\InvalidFormatException; -use Illuminate\Contracts\Validation\Rule; -use Illuminate\Support\Facades\Log; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class IsDateOrTime */ -class IsDateOrTime implements Rule +class IsDateOrTime implements ValidationRule { /** - * Get the validation error message. + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @return string + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function message() - { - return (string)trans('validation.date_or_time'); - } - - /** - * Determine if the validation rule passes. - * - * @param string $attribute - * @param mixed $value - * - * @return bool - */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { $value = (string)$value; if ('' === $value) { - return false; + $fail('validation.date_or_time')->translate(); + return; } if (10 === strlen($value)) { // probably a date format. try { Carbon::createFromFormat('Y-m-d', $value); - } catch (InvalidDateException $e) { - Log::error(sprintf('"%s" is not a valid date: %s', $value, $e->getMessage())); + } catch (InvalidDateException $e) { // @phpstan-ignore-line + app('log')->error(sprintf('"%s" is not a valid date: %s', $value, $e->getMessage())); - return false; - } catch (InvalidFormatException $e) { - Log::error(sprintf('"%s" is of an invalid format: %s', $value, $e->getMessage())); + $fail('validation.date_or_time')->translate(); + return; + } catch (InvalidFormatException $e) { // @phpstan-ignore-line + app('log')->error(sprintf('"%s" is of an invalid format: %s', $value, $e->getMessage())); - return false; + $fail('validation.date_or_time')->translate(); + return; } - return true; + return; } // is an atom string, I hope? try { Carbon::parse($value); - } catch (InvalidDateException $e) { - Log::error(sprintf('"%s" is not a valid date or time: %s', $value, $e->getMessage())); + } catch (InvalidDateException $e) { // @phpstan-ignore-line + app('log')->error(sprintf('"%s" is not a valid date or time: %s', $value, $e->getMessage())); - return false; + $fail('validation.date_or_time')->translate(); + return; } catch (InvalidFormatException $e) { - Log::error(sprintf('"%s" is of an invalid format: %s', $value, $e->getMessage())); + app('log')->error(sprintf('"%s" is of an invalid format: %s', $value, $e->getMessage())); - return false; + $fail('validation.date_or_time')->translate(); + return; } - - return true; } } diff --git a/app/Rules/IsDuplicateTransaction.php b/app/Rules/IsDuplicateTransaction.php index 2bfb59089d..4f61474828 100644 --- a/app/Rules/IsDuplicateTransaction.php +++ b/app/Rules/IsDuplicateTransaction.php @@ -23,30 +23,25 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** + * TODO not sure where this is used. + * * Class IsDuplicateTransaction */ -class IsDuplicateTransaction implements Rule +class IsDuplicateTransaction implements ValidationRule { private string $value; /** * @inheritDoc */ - public function message() - { - return $this->value; - } - - /** - * @inheritDoc - */ - public function passes($attribute, $value) + public function validate(string $attribute, mixed $value, Closure $fail): void { $this->value = $value; - return false; + $fail($this->value); } } diff --git a/app/Rules/IsTransferAccount.php b/app/Rules/IsTransferAccount.php index 8cf7d14b05..373be6b236 100644 --- a/app/Rules/IsTransferAccount.php +++ b/app/Rules/IsTransferAccount.php @@ -24,37 +24,28 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Models\TransactionType; use FireflyIII\Validation\AccountValidator; -use Illuminate\Contracts\Validation\Rule; -use Illuminate\Support\Facades\Log; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class IsTransferAccount */ -class IsTransferAccount implements Rule +class IsTransferAccount implements ValidationRule { /** - * Get the validation error message. + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @return string + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function message(): string + public function validate(string $attribute, mixed $value, Closure $fail): void { - return (string)trans('validation.not_transfer_account'); - } - - /** - * Determine if the validation rule passes. - * - * @param string $attribute - * @param mixed $value - * - * @return bool - */ - public function passes($attribute, $value): bool - { - Log::debug(sprintf('Now in %s(%s)', __METHOD__, $value)); + app('log')->debug(sprintf('Now in %s(%s)', __METHOD__, $value)); /** @var AccountValidator $validator */ $validator = app(AccountValidator::class); $validator->setTransactionType(TransactionType::TRANSFER); @@ -62,14 +53,16 @@ class IsTransferAccount implements Rule $validAccount = $validator->validateSource(['name' => (string)$value,]); if (true === $validAccount) { - Log::debug('Found account based on name. Return true.'); + app('log')->debug('Found account based on name. Return true.'); // found by name, use repos to return. - return true; + return; } $validAccount = $validator->validateSource(['id' => (int)$value,]); - Log::debug(sprintf('Search by id (%d), result is %s.', (int)$value, var_export($validAccount, true))); + app('log')->debug(sprintf('Search by id (%d), result is %s.', (int)$value, var_export($validAccount, true))); - return false !== $validAccount; + if (false === $validAccount) { + $fail('validation.not_transfer_account')->translate(); + } } } diff --git a/app/Rules/IsValidAttachmentModel.php b/app/Rules/IsValidAttachmentModel.php index fa21c25991..f58ec2f95c 100644 --- a/app/Rules/IsValidAttachmentModel.php +++ b/app/Rules/IsValidAttachmentModel.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget; @@ -39,16 +40,15 @@ use FireflyIII\Repositories\Journal\JournalAPIRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; -use Illuminate\Contracts\Validation\Rule; -use Illuminate\Support\Facades\Log; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class IsValidAttachmentModel */ -class IsValidAttachmentModel implements Rule +class IsValidAttachmentModel implements ValidationRule { /** @var string */ - private $model; + private string $model; /** * IsValidAttachmentModel constructor. @@ -77,46 +77,35 @@ class IsValidAttachmentModel implements Rule } /** - * Get the validation error message. + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @return string + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function message(): string - { - return (string)trans('validation.model_id_invalid'); - } - - /** - * Determine if the validation rule passes. - * - * @param string $attribute - * @param mixed $value - * - * @return bool - */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { if (!auth()->check()) { - return false; + $fail('validation.model_id_invalid')->translate(); + return; } - $methods = [ - Account::class => 'validateAccount', - Bill::class => 'validateBill', - Budget::class => 'validateBudget', - Category::class => 'validateCategory', - PiggyBank::class => 'validatePiggyBank', - Tag::class => 'validateTag', - Transaction::class => 'validateTransaction', - TransactionJournal::class => 'validateJournal', - ]; - if (!array_key_exists($this->model, $methods)) { - Log::error(sprintf('Cannot validate model "%s" in %s.', substr($this->model, 0, 20), __METHOD__)); + $result = match ($this->model) { + Account::class => $this->validateAccount((int)$value), + Bill::class => $this->validateBill((int)$value), + Budget::class => $this->validateBudget((int)$value), + Category::class => $this->validateCategory((int)$value), + PiggyBank::class => $this->validatePiggyBank((int)$value), + Tag::class => $this->validateTag((int)$value), + Transaction::class => $this->validateTransaction((int)$value), + TransactionJournal::class => $this->validateJournal((int)$value), + default => false, + }; - return false; + if (false === $result) { + $fail('validation.model_id_invalid')->translate(); } - $method = $methods[$this->model]; - - return $this->$method((int)$value); } /** @@ -175,19 +164,6 @@ class IsValidAttachmentModel implements Rule return null !== $repository->find($value); } - /** - * @param int $value - * - * @return bool - */ - private function validateJournal(int $value): bool - { - $repository = app(JournalRepositoryInterface::class); - $repository->setUser(auth()->user()); - - return null !== $repository->find($value); - } - /** * @param int $value * @@ -227,6 +203,19 @@ class IsValidAttachmentModel implements Rule $repository = app(JournalAPIRepositoryInterface::class); $repository->setUser(auth()->user()); - return null !== $repository->findTransaction((int)$value); + return null !== $repository->findTransaction($value); + } + + /** + * @param int $value + * + * @return bool + */ + private function validateJournal(int $value): bool + { + $repository = app(JournalRepositoryInterface::class); + $repository->setUser(auth()->user()); + + return null !== $repository->find($value); } } diff --git a/app/Rules/IsValidBulkClause.php b/app/Rules/IsValidBulkClause.php index 7a8bda7692..e5aaf17e8a 100644 --- a/app/Rules/IsValidBulkClause.php +++ b/app/Rules/IsValidBulkClause.php @@ -24,14 +24,15 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Validator; use JsonException; /** * Class IsValidBulkClause */ -class IsValidBulkClause implements Rule +class IsValidBulkClause implements ValidationRule { private string $error; private array $rules; @@ -54,19 +55,20 @@ class IsValidBulkClause implements Rule } /** - * @param string $attribute - * @param mixed $value + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @return bool + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { $result = $this->basicValidation((string)$value); if (false === $result) { - return false; + $fail($this->error); } - - return true; } /** @@ -107,7 +109,7 @@ class IsValidBulkClause implements Rule 'value' => $this->rules[$clause][$arrayKey], ]); if ($validator->fails()) { - $this->error = sprintf('%s: %s: %s', $clause, $arrayKey, join(', ', ($validator->errors()->get('value')))); + $this->error = sprintf('%s: %s: %s', $clause, $arrayKey, implode(', ', ($validator->errors()->get('value')))); return false; } diff --git a/app/Rules/LessThanPiggyTarget.php b/app/Rules/LessThanPiggyTarget.php index e19589ea74..7b1dd17d7a 100644 --- a/app/Rules/LessThanPiggyTarget.php +++ b/app/Rules/LessThanPiggyTarget.php @@ -24,12 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class LessThanPiggyTarget */ -class LessThanPiggyTarget implements Rule +class LessThanPiggyTarget implements ValidationRule { /** * Get the validation error message. @@ -42,15 +43,16 @@ class LessThanPiggyTarget implements Rule } /** - * Determine if the validation rule passes. + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @param string $attribute - * @param mixed $value + * @return void * - * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { - return true; + // TODO not sure if this is still used. } } diff --git a/app/Rules/UniqueAccountNumber.php b/app/Rules/UniqueAccountNumber.php index c9aaddafe8..5fae561d1d 100644 --- a/app/Rules/UniqueAccountNumber.php +++ b/app/Rules/UniqueAccountNumber.php @@ -23,16 +23,16 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountType; -use Illuminate\Contracts\Validation\Rule; -use Illuminate\Support\Facades\Log; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class UniqueAccountNumber */ -class UniqueAccountNumber implements Rule +class UniqueAccountNumber implements ValidationRule { private ?Account $account; private ?string $expectedType; @@ -46,7 +46,7 @@ class UniqueAccountNumber implements Rule */ public function __construct(?Account $account, ?string $expectedType) { - Log::debug('Constructed UniqueAccountNumber'); + app('log')->debug('Constructed UniqueAccountNumber'); $this->account = $account; $this->expectedType = $expectedType; // a very basic fix to make sure we get the correct account type: @@ -59,7 +59,7 @@ class UniqueAccountNumber implements Rule if ('asset' === $expectedType) { $this->expectedType = AccountType::ASSET; } - Log::debug(sprintf('Expected type is "%s"', $this->expectedType)); + app('log')->debug(sprintf('Expected type is "%s"', $this->expectedType)); } /** @@ -74,29 +74,29 @@ class UniqueAccountNumber implements Rule } /** - * Determine if the validation rule passes. + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @param string $attribute - * @param mixed $value - * - * @return bool + * @return void * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { if (!auth()->check()) { - return true; + return; } if (null === $this->expectedType) { - return true; + return; } $maxCounts = $this->getMaxOccurrences(); foreach ($maxCounts as $type => $max) { $count = $this->countHits($type, $value); - Log::debug(sprintf('Count for "%s" and account number "%s" is %d', $type, $value, $count)); + app('log')->debug(sprintf('Count for "%s" and account number "%s" is %d', $type, $value, $count)); if ($count > $max) { - Log::debug( + app('log')->debug( sprintf( 'account number "%s" is in use with %d account(s) of type "%s", which is too much for expected type "%s"', $value, @@ -106,12 +106,11 @@ class UniqueAccountNumber implements Rule ) ); - return false; + $fail('validation.unique_account_number_for_user')->translate(); + return; } } - Log::debug('Account number is valid.'); - - return true; + app('log')->debug('Account number is valid.'); } /** diff --git a/app/Rules/UniqueIban.php b/app/Rules/UniqueIban.php index 6a0c3940cb..8d1bea0cfd 100644 --- a/app/Rules/UniqueIban.php +++ b/app/Rules/UniqueIban.php @@ -27,7 +27,6 @@ use Closure; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use Illuminate\Contracts\Validation\ValidationRule; -use Illuminate\Support\Facades\Log; /** * Class UniqueIban @@ -96,6 +95,8 @@ class UniqueIban implements ValidationRule * * @return bool * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * */ public function passes($attribute, $value): bool { @@ -109,15 +110,15 @@ class UniqueIban implements ValidationRule foreach ($maxCounts as $type => $max) { $count = $this->countHits($type, $value); - Log::debug(sprintf('Count for "%s" and IBAN "%s" is %d', $type, $value, $count)); + app('log')->debug(sprintf('Count for "%s" and IBAN "%s" is %d', $type, $value, $count)); if ($count > $max) { - Log::debug( + app('log')->debug( sprintf( 'IBAN "%s" is in use with %d account(s) of type "%s", which is too much for expected types "%s"', $value, $count, $type, - join(', ', $this->expectedTypes) + implode(', ', $this->expectedTypes) ) ); diff --git a/app/Rules/ValidJournals.php b/app/Rules/ValidJournals.php index 11e56e89cf..4a286f36f7 100644 --- a/app/Rules/ValidJournals.php +++ b/app/Rules/ValidJournals.php @@ -24,53 +24,42 @@ declare(strict_types=1); namespace FireflyIII\Rules; +use Closure; use FireflyIII\Models\TransactionJournal; -use Illuminate\Contracts\Validation\Rule; -use Illuminate\Support\Facades\Log; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class ValidJournals * */ -class ValidJournals implements Rule +class ValidJournals implements ValidationRule { /** - * Get the validation error message. + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @return string + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function message(): string + public function validate(string $attribute, mixed $value, Closure $fail): void { - return (string)trans('validation.invalid_selection'); - } - - /** - * Determine if the validation rule passes. - * - * @param string $attribute - * @param mixed $value - * - * @return bool - * - */ - public function passes($attribute, $value): bool - { - Log::debug('In ValidJournals::passes'); + app('log')->debug('In ValidJournals::passes'); if (!is_array($value)) { - return true; + return; } $userId = auth()->user()->id; foreach ($value as $journalId) { $count = TransactionJournal::where('id', $journalId)->where('user_id', $userId)->count(); if (0 === $count) { - Log::debug(sprintf('Count for transaction #%d and user #%d is zero! Return FALSE', $journalId, $userId)); + app('log')->debug(sprintf('Count for transaction #%d and user #%d is zero! Return FALSE', $journalId, $userId)); - return false; + $fail('validation.invalid_selection')->translate(); + return; } } - Log::debug('Return true!'); - - return true; + app('log')->debug('Return true!'); } } diff --git a/app/Rules/ValidRecurrenceRepetitionType.php b/app/Rules/ValidRecurrenceRepetitionType.php index 508835a5fb..4f680b108d 100644 --- a/app/Rules/ValidRecurrenceRepetitionType.php +++ b/app/Rules/ValidRecurrenceRepetitionType.php @@ -23,52 +23,43 @@ declare(strict_types=1); namespace FireflyIII\Rules; -use Illuminate\Contracts\Validation\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class ValidRecurrenceRepetitionType * */ -class ValidRecurrenceRepetitionType implements Rule +class ValidRecurrenceRepetitionType implements ValidationRule { - /** - * Get the validation error message. - * - * @return string - */ - public function message(): string - { - return (string)trans('validation.valid_recurrence_rep_type'); - } - /** * Determine if the validation rule passes. * - * @param string $attribute - * @param mixed $value - * - * @return bool + * @param string $attribute + * @param mixed $value + * @param Closure $fail * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { $value = (string)$value; if ('daily' === $value) { - return true; + return; } //monthly,17 //ndom,3,7 if (in_array(substr($value, 0, 6), ['yearly', 'weekly'], true)) { - return true; + return; } if (str_starts_with($value, 'monthly')) { - return true; + return; } if (str_starts_with($value, 'ndom')) { - return true; + return; } - return false; + $fail('validation.valid_recurrence_rep_type')->translate(); } } diff --git a/app/Rules/ValidRecurrenceRepetitionValue.php b/app/Rules/ValidRecurrenceRepetitionValue.php index 10ec16ea84..f665eaabd3 100644 --- a/app/Rules/ValidRecurrenceRepetitionValue.php +++ b/app/Rules/ValidRecurrenceRepetitionValue.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Rules; use Carbon\Carbon; -use Illuminate\Contracts\Validation\Rule; -use Illuminate\Support\Facades\Log; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; use InvalidArgumentException; /** @@ -33,56 +33,46 @@ use InvalidArgumentException; * */ -class ValidRecurrenceRepetitionValue implements Rule +class ValidRecurrenceRepetitionValue implements ValidationRule { /** - * Get the validation error message. + * @param string $attribute + * @param mixed $value + * @param Closure $fail * - * @return string + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function message(): string - { - return (string)trans('validation.valid_recurrence_rep_type'); - } - - /** - * Determine if the validation rule passes. - * - * @param string $attribute - * @param mixed $value - * - * @return bool - * - */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { $value = (string)$value; if ('daily' === $value) { - return true; + return; } - if (str_starts_with($value, 'monthly')) { - return $this->validateMonthly($value); + if (str_starts_with($value, 'monthly') && $this->validateMonthly($value)) { + return; } // Value is like: ndom,3,7 // nth x-day of the month. - if (str_starts_with($value, 'ndom')) { - return $this->validateNdom($value); + if (str_starts_with($value, 'ndom') && $this->validateNdom($value)) { + return; } // Value is like: weekly,7 - if (str_starts_with($value, 'weekly')) { - return $this->validateWeekly($value); + if (str_starts_with($value, 'weekly') && $this->validateWeekly($value)) { + return; } // Value is like: yearly,2018-01-01 - if (str_starts_with($value, 'yearly')) { - return $this->validateYearly($value); + if (str_starts_with($value, 'yearly') && $this->validateYearly($value)) { + return; } - return false; + $fail('validation.valid_recurrence_rep_type')->translate(); } /** @@ -141,8 +131,8 @@ class ValidRecurrenceRepetitionValue implements Rule $dateString = substr($value, 7); try { Carbon::createFromFormat('Y-m-d', $dateString); - } catch (InvalidArgumentException $e) { - Log::debug(sprintf('Could not parse date %s: %s', $dateString, $e->getMessage())); + } catch (InvalidArgumentException $e) { // @phpstan-ignore-line + app('log')->debug(sprintf('Could not parse date %s: %s', $dateString, $e->getMessage())); return false; } diff --git a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php index 2f09340c09..fa683b1e34 100644 --- a/app/Services/FireflyIIIOrg/Update/UpdateRequest.php +++ b/app/Services/FireflyIIIOrg/Update/UpdateRequest.php @@ -28,7 +28,6 @@ use Carbon\Carbon; use FireflyIII\Events\NewVersionAvailable; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -43,7 +42,7 @@ class UpdateRequest implements UpdateRequestInterface */ public function getUpdateInformation(string $channel): array { - Log::debug(sprintf('Now in getUpdateInformation(%s)', $channel)); + app('log')->debug(sprintf('Now in getUpdateInformation(%s)', $channel)); $information = [ 'level' => 'error', 'message' => (string)trans('firefly.unknown_error'), @@ -52,8 +51,8 @@ class UpdateRequest implements UpdateRequestInterface // try get array from update server: $updateInfo = $this->contactServer($channel); if ('error' === $updateInfo['level']) { - Log::error('Update information contains an error.'); - Log::error($updateInfo['message']); + app('log')->error('Update information contains an error.'); + app('log')->error($updateInfo['message']); $information['message'] = $updateInfo['message']; return $information; @@ -70,7 +69,7 @@ class UpdateRequest implements UpdateRequestInterface */ private function contactServer(string $channel): array { - Log::debug(sprintf('Now in contactServer(%s)', $channel)); + app('log')->debug(sprintf('Now in contactServer(%s)', $channel)); // always fall back to current version: $return = [ 'version' => config('firefly.version'), @@ -80,7 +79,7 @@ class UpdateRequest implements UpdateRequestInterface ]; $url = config('firefly.update_endpoint'); - Log::debug(sprintf('Going to call %s', $url)); + app('log')->debug(sprintf('Going to call %s', $url)); try { $client = new Client(); $options = [ @@ -91,17 +90,17 @@ class UpdateRequest implements UpdateRequestInterface ]; $res = $client->request('GET', $url, $options); } catch (GuzzleException $e) { - Log::error('Ran into Guzzle error.'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error('Ran into Guzzle error.'); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $return['message'] = sprintf('Guzzle: %s', strip_tags($e->getMessage())); return $return; } if (200 !== $res->getStatusCode()) { - Log::error(sprintf('Response status from server is %d.', $res->getStatusCode())); - Log::error((string)$res->getBody()); + app('log')->error(sprintf('Response status from server is %d.', $res->getStatusCode())); + app('log')->error((string)$res->getBody()); $return['message'] = sprintf('Error: %d', $res->getStatusCode()); return $return; @@ -110,25 +109,30 @@ class UpdateRequest implements UpdateRequestInterface try { $json = json_decode($body, true, 512, JSON_THROW_ON_ERROR); } catch (JsonException $e) { - Log::error('Body is not valid JSON'); - Log::error($body); + app('log')->error('Body is not valid JSON'); + app('log')->error($body); $return['message'] = 'Invalid JSON :('; return $return; } if (!array_key_exists($channel, $json['firefly_iii'])) { - Log::error(sprintf('No valid update channel "%s"', $channel)); - Log::error($body); + app('log')->error(sprintf('No valid update channel "%s"', $channel)); + app('log')->error($body); $return['message'] = sprintf('Unknown update channel "%s" :(', $channel); } // parse response a bit. No message yet. - $response = $json['firefly_iii'][$channel]; + $response = $json['firefly_iii'][$channel]; + $date = Carbon::createFromFormat('Y-m-d', $response['date']); + if (false === $date) { + $date = today(config('app.timezone')); + } $return['version'] = $response['version']; $return['level'] = 'success'; - $return['date'] = Carbon::createFromFormat('Y-m-d', $response['date'])->startOfDay(); - Log::info('Response from update server', $response); + $return['date'] = $date->startOfDay(); + + app('log')->info('Response from update server', $response); return $return; } @@ -140,7 +144,7 @@ class UpdateRequest implements UpdateRequestInterface */ private function parseResult(array $information): array { - Log::debug('Now in parseResult()', $information); + app('log')->debug('Now in parseResult()', $information); $return = [ 'level' => 'error', 'message' => (string)trans('firefly.unknown_error'), @@ -155,13 +159,13 @@ class UpdateRequest implements UpdateRequestInterface $compare = version_compare($latest, $current); - Log::debug(sprintf('Current version is "%s", latest is "%s", result is: %d', $current, $latest, $compare)); + app('log')->debug(sprintf('Current version is "%s", latest is "%s", result is: %d', $current, $latest, $compare)); // -1: you're running a newer version: if (-1 === $compare) { $return['level'] = 'info'; $return['message'] = (string)trans('firefly.update_newer_version_alert', ['your_version' => $current, 'new_version' => $latest]); - Log::debug('User is running a newer version', $return); + app('log')->debug('User is running a newer version', $return); return $return; } @@ -169,7 +173,7 @@ class UpdateRequest implements UpdateRequestInterface if (0 === $compare) { $return['level'] = 'info'; $return['message'] = (string)trans('firefly.update_current_version_alert', ['version' => $current]); - Log::debug('User is the current version.', $return); + app('log')->debug('User is the current version.', $return); return $return; } @@ -191,7 +195,7 @@ class UpdateRequest implements UpdateRequestInterface 'days' => $expectedDiff, ] ); - Log::debug('Release is very fresh.', $return); + app('log')->debug('Release is very fresh.', $return); return $return; } @@ -206,22 +210,22 @@ class UpdateRequest implements UpdateRequestInterface 'date' => $released->isoFormat((string)trans('config.month_and_day_js')), ] ); - Log::debug('New release is old enough.'); + app('log')->debug('New release is old enough.'); // add warning in case of alpha or beta: // append warning if beta or alpha. $isBeta = $information['is_beta'] ?? false; if (true === $isBeta) { $return['message'] = sprintf('%s %s', $return['message'], trans('firefly.update_version_beta')); - Log::debug('New release is also a beta!'); + app('log')->debug('New release is also a beta!'); } $isAlpha = $information['is_alpha'] ?? false; if (true === $isAlpha) { $return['message'] = sprintf('%s %s', $return['message'], trans('firefly.update_version_alpha')); - Log::debug('New release is also a alpha!'); + app('log')->debug('New release is also a alpha!'); } - Log::debug('New release is here!', $return); + app('log')->debug('New release is here!', $return); // send event, this may result in a notification. event(new NewVersionAvailable($return['message'])); diff --git a/app/Services/Internal/Destroy/AccountDestroyService.php b/app/Services/Internal/Destroy/AccountDestroyService.php index af4fd4cace..dc8be9c16e 100644 --- a/app/Services/Internal/Destroy/AccountDestroyService.php +++ b/app/Services/Internal/Destroy/AccountDestroyService.php @@ -31,7 +31,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Support\Facades\Log; use stdClass; /** @@ -75,15 +74,15 @@ class AccountDestroyService */ private function destroyOpeningBalance(Account $account): void { - Log::debug(sprintf('Searching for opening balance for account #%d "%s"', $account->id, $account->name)); + app('log')->debug(sprintf('Searching for opening balance for account #%d "%s"', $account->id, $account->name)); $set = $account->transactions() ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->where('transaction_types.type', TransactionType::OPENING_BALANCE) ->get(['transactions.transaction_journal_id']); if ($set->count() > 0) { - $journalId = (int)$set->first()->transaction_journal_id; - Log::debug(sprintf('Found opening balance journal with ID #%d', $journalId)); + $journalId = $set->first()->transaction_journal_id; + app('log')->debug(sprintf('Found opening balance journal with ID #%d', $journalId)); // get transactions with this journal (should be just one): $transactions = Transaction::where('transaction_journal_id', $journalId) @@ -91,9 +90,9 @@ class AccountDestroyService ->get(); /** @var Transaction $transaction */ foreach ($transactions as $transaction) { - Log::debug(sprintf('Found transaction with ID #%d', $transaction->id)); + app('log')->debug(sprintf('Found transaction with ID #%d', $transaction->id)); $ibAccount = $transaction->account; - Log::debug(sprintf('Connected to account #%d "%s"', $ibAccount->id, $ibAccount->name)); + app('log')->debug(sprintf('Connected to account #%d "%s"', $ibAccount->id, $ibAccount->name)); $ibAccount->accountMeta()->delete(); $transaction->delete(); @@ -114,12 +113,12 @@ class AccountDestroyService */ public function moveTransactions(Account $account, Account $moveTo): void { - Log::debug(sprintf('Move from account #%d to #%d', $account->id, $moveTo->id)); + app('log')->debug(sprintf('Move from account #%d to #%d', $account->id, $moveTo->id)); DB::table('transactions')->where('account_id', $account->id)->update(['account_id' => $moveTo->id]); $collection = Transaction::groupBy('transaction_journal_id', 'account_id') ->where('account_id', $moveTo->id) - ->get(['transaction_journal_id', 'account_id', DB::raw('count(*) as the_count')]); + ->get(['transaction_journal_id', 'account_id', DB::raw('count(*) as the_count')]); // @phpstan-ignore-line if (0 === $collection->count()) { return; } @@ -130,10 +129,10 @@ class AccountDestroyService /** @var stdClass $row */ foreach ($collection as $row) { if ((int)$row->the_count > 1) { - $journalId = (int)$row->transaction_journal_id; + $journalId = $row->transaction_journal_id; $journal = $user->transactionJournals()->find($journalId); if (null !== $journal) { - Log::debug(sprintf('Deleted journal #%d because it has the same source as destination.', $journal->id)); + app('log')->debug(sprintf('Deleted journal #%d because it has the same source as destination.', $journal->id)); $service->destroy($journal); } } @@ -158,14 +157,14 @@ class AccountDestroyService /** @var JournalDestroyService $service */ $service = app(JournalDestroyService::class); - Log::debug('Now trigger account delete response #' . $account->id); + app('log')->debug('Now trigger account delete response #' . $account->id); /** @var Transaction $transaction */ foreach ($account->transactions()->get() as $transaction) { - Log::debug('Now at transaction #' . $transaction->id); - /** @var TransactionJournal $journal */ + app('log')->debug('Now at transaction #' . $transaction->id); + /** @var TransactionJournal|null $journal */ $journal = $transaction->transactionJournal()->first(); if (null !== $journal) { - Log::debug('Call for deletion of journal #' . $journal->id); + app('log')->debug('Call for deletion of journal #' . $journal->id); $service->destroy($journal); } } diff --git a/app/Services/Internal/Destroy/BudgetDestroyService.php b/app/Services/Internal/Destroy/BudgetDestroyService.php index d9675f743a..29856c8773 100644 --- a/app/Services/Internal/Destroy/BudgetDestroyService.php +++ b/app/Services/Internal/Destroy/BudgetDestroyService.php @@ -46,10 +46,10 @@ class BudgetDestroyService } // also delete all relations between categories and transaction journals: - DB::table('budget_transaction_journal')->where('budget_id', (int)$budget->id)->delete(); + DB::table('budget_transaction_journal')->where('budget_id', $budget->id)->delete(); // also delete all relations between categories and transactions: - DB::table('budget_transaction')->where('budget_id', (int)$budget->id)->delete(); + DB::table('budget_transaction')->where('budget_id', $budget->id)->delete(); // also delete all budget limits foreach ($budget->budgetlimits()->get() as $limit) { diff --git a/app/Services/Internal/Destroy/CategoryDestroyService.php b/app/Services/Internal/Destroy/CategoryDestroyService.php index 36c2f74ef4..15daf3d6e2 100644 --- a/app/Services/Internal/Destroy/CategoryDestroyService.php +++ b/app/Services/Internal/Destroy/CategoryDestroyService.php @@ -41,10 +41,10 @@ class CategoryDestroyService $category->delete(); // also delete all relations between categories and transaction journals: - DB::table('category_transaction_journal')->where('category_id', (int)$category->id)->delete(); + DB::table('category_transaction_journal')->where('category_id', $category->id)->delete(); // also delete all relations between categories and transactions: - DB::table('category_transaction')->where('category_id', (int)$category->id)->delete(); + DB::table('category_transaction')->where('category_id', $category->id)->delete(); // delete references to category from recurring transactions. DB::table('rt_meta')->where('name', 'category_id')->where('value', $category->id)->delete(); diff --git a/app/Services/Internal/Destroy/JournalDestroyService.php b/app/Services/Internal/Destroy/JournalDestroyService.php index 0c52c769bd..a49771a149 100644 --- a/app/Services/Internal/Destroy/JournalDestroyService.php +++ b/app/Services/Internal/Destroy/JournalDestroyService.php @@ -29,7 +29,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Models\TransactionJournalMeta; -use Illuminate\Support\Facades\Log; /** * Class JournalDestroyService @@ -41,17 +40,17 @@ class JournalDestroyService */ public function destroy(TransactionJournal $journal): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); /** @var Transaction $transaction */ foreach ($journal->transactions()->get() as $transaction) { - Log::debug(sprintf('Will now delete transaction #%d', $transaction->id)); + app('log')->debug(sprintf('Will now delete transaction #%d', $transaction->id)); $transaction->delete(); } // also delete journal_meta entries. /** @var TransactionJournalMeta $meta */ foreach ($journal->transactionJournalMeta()->get() as $meta) { - Log::debug(sprintf('Will now delete meta-entry #%d', $meta->id)); + app('log')->debug(sprintf('Will now delete meta-entry #%d', $meta->id)); $meta->delete(); } diff --git a/app/Services/Internal/Destroy/TransactionGroupDestroyService.php b/app/Services/Internal/Destroy/TransactionGroupDestroyService.php index 6ef875d46b..94152f0cb9 100644 --- a/app/Services/Internal/Destroy/TransactionGroupDestroyService.php +++ b/app/Services/Internal/Destroy/TransactionGroupDestroyService.php @@ -25,7 +25,6 @@ namespace FireflyIII\Services\Internal\Destroy; use FireflyIII\Events\DestroyedTransactionGroup; use FireflyIII\Models\TransactionGroup; -use Illuminate\Support\Facades\Log; /** * Class TransactionGroupDestroyService @@ -39,7 +38,7 @@ class TransactionGroupDestroyService */ public function destroy(TransactionGroup $transactionGroup): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); /** @var JournalDestroyService $service */ $service = app(JournalDestroyService::class); foreach ($transactionGroup->transactionJournals as $journal) { diff --git a/app/Services/Internal/Support/AccountServiceTrait.php b/app/Services/Internal/Support/AccountServiceTrait.php index d7db07baab..c456ea5ca3 100644 --- a/app/Services/Internal/Support/AccountServiceTrait.php +++ b/app/Services/Internal/Support/AccountServiceTrait.php @@ -38,7 +38,6 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService; -use Illuminate\Support\Facades\Log; use JsonException; use Validator; @@ -64,7 +63,7 @@ trait AccountServiceTrait $rules = ['iban' => 'required|iban']; $validator = Validator::make($data, $rules); if ($validator->fails()) { - Log::info(sprintf('Detected invalid IBAN ("%s"). Return NULL instead.', $iban)); + app('log')->info(sprintf('Detected invalid IBAN ("%s"). Return NULL instead.', $iban)); return null; } @@ -138,7 +137,7 @@ trait AccountServiceTrait $data['account_role'] = ''; } - if ($account->accountType->type === AccountType::ASSET && array_key_exists('account_role', $data) && 'ccAsset' === $data['account_role']) { + if ($account->accountType->type === AccountType::ASSET && 'ccAsset' === $data['account_role']) { $fields = $this->validCCFields; } /** @var AccountMetaFactory $factory */ @@ -204,11 +203,11 @@ trait AccountServiceTrait } if ('' !== $data['opening_balance'] && array_key_exists('opening_balance_date', $data) && '' !== $data['opening_balance_date'] && $data['opening_balance_date'] instanceof Carbon) { - Log::debug('Array has valid opening balance data.'); + app('log')->debug('Array has valid opening balance data.'); return true; } - Log::debug('Array does not have valid opening balance data.'); + app('log')->debug('Array does not have valid opening balance data.'); return false; } @@ -224,8 +223,12 @@ trait AccountServiceTrait */ protected function createOBGroup(Account $account, array $data): TransactionGroup { - Log::debug('Now going to create an OB group.'); - $language = app('preferences')->getForUser($account->user, 'language', 'en_US')->data; + app('log')->debug('Now going to create an OB group.'); + $language = app('preferences')->getForUser($account->user, 'language', 'en_US')->data; + if (is_array($language)) { + $language = 'en_US'; + } + $language = (string)$language; $sourceId = null; $sourceName = null; $destId = null; @@ -234,19 +237,19 @@ trait AccountServiceTrait // amount is positive. if (1 === bccomp($amount, '0')) { - Log::debug(sprintf('Amount is %s, which is positive. Source is a new IB account, destination is #%d', $amount, $account->id)); + app('log')->debug(sprintf('Amount is %s, which is positive. Source is a new IB account, destination is #%d', $amount, $account->id)); $sourceName = trans('firefly.initial_balance_description', ['account' => $account->name], $language); $destId = $account->id; } // amount is not positive if (-1 === bccomp($amount, '0')) { - Log::debug(sprintf('Amount is %s, which is negative. Destination is a new IB account, source is #%d', $amount, $account->id)); + app('log')->debug(sprintf('Amount is %s, which is negative. Destination is a new IB account, source is #%d', $amount, $account->id)); $destName = trans('firefly.initial_balance_account', ['account' => $account->name], $language); $sourceId = $account->id; } // amount is 0 if (0 === bccomp($amount, '0')) { - Log::debug('Amount is zero, so will not make an OB group.'); + app('log')->debug('Amount is zero, so will not make an OB group.'); throw new FireflyException('Amount for new opening balance was unexpectedly 0.'); } @@ -256,7 +259,7 @@ trait AccountServiceTrait // get or grab currency: $currency = $this->accountRepository->getAccountCurrency($account); if (null === $currency) { - $currency = app('default')->getDefaultCurrencyByUser($account->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); } // submit to factory: @@ -289,7 +292,7 @@ trait AccountServiceTrait ], ], ]; - Log::debug('Going for submission in createOBGroup', $submission); + app('log')->debug('Going for submission in createOBGroup', $submission); /** @var TransactionGroupFactory $factory */ $factory = app(TransactionGroupFactory::class); @@ -298,8 +301,8 @@ trait AccountServiceTrait try { $group = $factory->create($submission); } catch (DuplicateTransactionException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException($e->getMessage(), 0, $e); } @@ -313,11 +316,11 @@ trait AccountServiceTrait */ protected function deleteCreditTransaction(Account $account): void { - Log::debug(sprintf('deleteCreditTransaction() for account #%d', $account->id)); + app('log')->debug(sprintf('deleteCreditTransaction() for account #%d', $account->id)); $creditGroup = $this->getCreditTransaction($account); if (null !== $creditGroup) { - Log::debug('Credit journal found, delete journal.'); + app('log')->debug('Credit journal found, delete journal.'); /** @var TransactionGroupDestroyService $service */ $service = app(TransactionGroupDestroyService::class); $service->destroy($creditGroup); @@ -333,7 +336,7 @@ trait AccountServiceTrait */ protected function getCreditTransaction(Account $account): ?TransactionGroup { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); return $this->accountRepository->getCreditTransactionGroup($account); } @@ -345,12 +348,12 @@ trait AccountServiceTrait */ protected function deleteOBGroup(Account $account): void { - Log::debug(sprintf('deleteOB() for account #%d', $account->id)); + app('log')->debug(sprintf('deleteOB() for account #%d', $account->id)); $openingBalanceGroup = $this->getOBGroup($account); // opening balance data? update it! if (null !== $openingBalanceGroup) { - Log::debug('Opening balance journal found, delete journal.'); + app('log')->debug('Opening balance journal found, delete journal.'); /** @var TransactionGroupDestroyService $service */ $service = app(TransactionGroupDestroyService::class); $service->destroy($openingBalanceGroup); @@ -387,7 +390,7 @@ trait AccountServiceTrait if (null === $currency) { // use default currency: - $currency = app('amount')->getDefaultCurrencyByUser($this->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); } $currency->enabled = true; $currency->save(); @@ -410,10 +413,10 @@ trait AccountServiceTrait */ protected function updateCreditTransaction(Account $account, string $direction, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); if (0 === bccomp($openingBalance, '0')) { - Log::debug('Amount is zero, so will not update liability credit/debit group.'); + app('log')->debug('Amount is zero, so will not update liability credit/debit group.'); throw new FireflyException('Amount for update liability credit/debit was unexpectedly 0.'); } // if direction is "debit" (i owe this debt), amount is negative. @@ -434,7 +437,7 @@ trait AccountServiceTrait // if exists, update: $currency = $this->accountRepository->getAccountCurrency($account); if (null === $currency) { - $currency = app('default')->getDefaultCurrencyByUser($account->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); } // simply grab the first journal and change it: @@ -471,14 +474,18 @@ trait AccountServiceTrait */ protected function createCreditTransaction(Account $account, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup { - Log::debug('Now going to create an createCreditTransaction.'); + app('log')->debug('Now going to create an createCreditTransaction.'); if (0 === bccomp($openingBalance, '0')) { - Log::debug('Amount is zero, so will not make an liability credit group.'); + app('log')->debug('Amount is zero, so will not make an liability credit group.'); throw new FireflyException('Amount for new liability credit was unexpectedly 0.'); } $language = app('preferences')->getForUser($account->user, 'language', 'en_US')->data; + if (is_array($language)) { + $language = 'en_US'; + } + $language = (string)$language; // set source and/or destination based on whether the amount is positive or negative. // first, assume the amount is positive and go from there: @@ -501,7 +508,7 @@ trait AccountServiceTrait // get or grab currency: $currency = $this->accountRepository->getAccountCurrency($account); if (null === $currency) { - $currency = app('default')->getDefaultCurrencyByUser($account->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); } // submit to factory: @@ -534,7 +541,7 @@ trait AccountServiceTrait ], ], ]; - Log::debug('Going for submission in createCreditTransaction', $submission); + app('log')->debug('Going for submission in createCreditTransaction', $submission); /** @var TransactionGroupFactory $factory */ $factory = app(TransactionGroupFactory::class); @@ -543,8 +550,8 @@ trait AccountServiceTrait try { $group = $factory->create($submission); } catch (DuplicateTransactionException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException($e->getMessage(), 0, $e); } @@ -561,7 +568,7 @@ trait AccountServiceTrait */ private function getObJournal(TransactionGroup $group): TransactionJournal { - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $journal */ $journal = $group->transactionJournals()->first(); if (null === $journal) { throw new FireflyException(sprintf('Group #%d has no OB journal', $group->id)); @@ -581,7 +588,7 @@ trait AccountServiceTrait */ private function getOBTransaction(TransactionJournal $journal, Account $account): Transaction { - /** @var Transaction $transaction */ + /** @var Transaction|null $transaction */ $transaction = $journal->transactions()->where('account_id', '!=', $account->id)->first(); if (null === $transaction) { throw new FireflyException(sprintf('Could not get OB transaction for journal #%d', $journal->id)); @@ -599,7 +606,7 @@ trait AccountServiceTrait */ private function getNotOBTransaction(TransactionJournal $journal, Account $account): Transaction { - /** @var Transaction $transaction */ + /** @var Transaction|null $transaction */ $transaction = $journal->transactions()->where('account_id', $account->id)->first(); if (null === $transaction) { throw new FireflyException(sprintf('Could not get non-OB transaction for journal #%d', $journal->id)); @@ -622,18 +629,18 @@ trait AccountServiceTrait */ protected function updateOBGroupV2(Account $account, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); // create if not exists: $obGroup = $this->getOBGroup($account); if (null === $obGroup) { return $this->createOBGroupV2($account, $openingBalance, $openingBalanceDate); } - Log::debug('Update OB group'); + app('log')->debug('Update OB group'); // if exists, update: $currency = $this->accountRepository->getAccountCurrency($account); if (null === $currency) { - $currency = app('default')->getDefaultCurrencyByUser($account->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); } // simply grab the first journal and change it: @@ -646,7 +653,7 @@ trait AccountServiceTrait // if amount is negative: if (1 === bccomp('0', $openingBalance)) { - Log::debug('Amount is negative.'); + app('log')->debug('Amount is negative.'); // account transaction loses money: $accountTransaction->amount = app('steam')->negative($openingBalance); $accountTransaction->transaction_currency_id = $currency->id; @@ -656,7 +663,7 @@ trait AccountServiceTrait $obTransaction->transaction_currency_id = $currency->id; } if (-1 === bccomp('0', $openingBalance)) { - Log::debug('Amount is positive.'); + app('log')->debug('Amount is positive.'); // account gains money: $accountTransaction->amount = app('steam')->positive($openingBalance); $accountTransaction->transaction_currency_id = $currency->id; @@ -685,8 +692,12 @@ trait AccountServiceTrait */ protected function createOBGroupV2(Account $account, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup { - Log::debug('Now going to create an OB group.'); - $language = app('preferences')->getForUser($account->user, 'language', 'en_US')->data; + app('log')->debug('Now going to create an OB group.'); + $language = app('preferences')->getForUser($account->user, 'language', 'en_US')->data; + if (is_array($language)) { + $language = 'en_US'; + } + $language = (string)$language; $sourceId = null; $sourceName = null; $destId = null; @@ -694,19 +705,19 @@ trait AccountServiceTrait // amount is positive. if (1 === bccomp($openingBalance, '0')) { - Log::debug(sprintf('Amount is %s, which is positive. Source is a new IB account, destination is #%d', $openingBalance, $account->id)); + app('log')->debug(sprintf('Amount is %s, which is positive. Source is a new IB account, destination is #%d', $openingBalance, $account->id)); $sourceName = trans('firefly.initial_balance_description', ['account' => $account->name], $language); $destId = $account->id; } // amount is not positive if (-1 === bccomp($openingBalance, '0')) { - Log::debug(sprintf('Amount is %s, which is negative. Destination is a new IB account, source is #%d', $openingBalance, $account->id)); + app('log')->debug(sprintf('Amount is %s, which is negative. Destination is a new IB account, source is #%d', $openingBalance, $account->id)); $destName = trans('firefly.initial_balance_account', ['account' => $account->name], $language); $sourceId = $account->id; } // amount is 0 if (0 === bccomp($openingBalance, '0')) { - Log::debug('Amount is zero, so will not make an OB group.'); + app('log')->debug('Amount is zero, so will not make an OB group.'); throw new FireflyException('Amount for new opening balance was unexpectedly 0.'); } @@ -716,7 +727,7 @@ trait AccountServiceTrait // get or grab currency: $currency = $this->accountRepository->getAccountCurrency($account); if (null === $currency) { - $currency = app('default')->getDefaultCurrencyByUser($account->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); } // submit to factory: @@ -749,7 +760,7 @@ trait AccountServiceTrait ], ], ]; - Log::debug('Going for submission in createOBGroupV2', $submission); + app('log')->debug('Going for submission in createOBGroupV2', $submission); /** @var TransactionGroupFactory $factory */ $factory = app(TransactionGroupFactory::class); @@ -758,8 +769,8 @@ trait AccountServiceTrait try { $group = $factory->create($submission); } catch (DuplicateTransactionException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException($e->getMessage(), 0, $e); } diff --git a/app/Services/Internal/Support/BillServiceTrait.php b/app/Services/Internal/Support/BillServiceTrait.php index a30385423b..06ab5e443a 100644 --- a/app/Services/Internal/Support/BillServiceTrait.php +++ b/app/Services/Internal/Support/BillServiceTrait.php @@ -26,7 +26,6 @@ namespace FireflyIII\Services\Internal\Support; use FireflyIII\Models\Bill; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; -use Illuminate\Support\Facades\Log; /** * Trait BillServiceTrait @@ -52,7 +51,7 @@ trait BillServiceTrait /** @var RuleAction $ruleAction */ foreach ($set as $ruleAction) { - Log::debug(sprintf('Updated rule action #%d to search for new bill name "%s"', $ruleAction->id, $newName)); + app('log')->debug(sprintf('Updated rule action #%d to search for new bill name "%s"', $ruleAction->id, $newName)); $ruleAction->action_value = $newName; $ruleAction->save(); } diff --git a/app/Services/Internal/Support/CreditRecalculateService.php b/app/Services/Internal/Support/CreditRecalculateService.php index c221ab4131..ae726fd6e0 100644 --- a/app/Services/Internal/Support/CreditRecalculateService.php +++ b/app/Services/Internal/Support/CreditRecalculateService.php @@ -32,7 +32,6 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Illuminate\Support\Facades\Log; /** * Class CreditRecalculateService @@ -85,8 +84,8 @@ class CreditRecalculateService try { $this->findByJournal($journal); } catch (FireflyException $e) { - Log::error($e->getTraceAsString()); - Log::error(sprintf('Could not find work account for transaction group #%d.', $this->group->id)); + app('log')->error($e->getTraceAsString()); + app('log')->error(sprintf('Could not find work account for transaction group #%d.', $this->group->id)); } } } @@ -131,11 +130,12 @@ class CreditRecalculateService */ private function getAccountByDirection(TransactionJournal $journal, string $direction): Account { - /** @var Transaction $transaction */ + /** @var Transaction|null $transaction */ $transaction = $journal->transactions()->where('amount', $direction, '0')->first(); if (null === $transaction) { throw new FireflyException(sprintf('Cannot find "%s"-transaction of journal #%d', $direction, $journal->id)); } + /** @var Account|null $foundAccount */ $foundAccount = $transaction->account; if (null === $foundAccount) { throw new FireflyException(sprintf('Cannot find "%s"-account of transaction #%d of journal #%d', $direction, $transaction->id, $journal->id)); @@ -197,7 +197,7 @@ class CreditRecalculateService $startOfDebt = $this->repository->getOpeningBalanceAmount($account) ?? '0'; $leftOfDebt = app('steam')->positive($startOfDebt); $currency = $this->repository->getAccountCurrency($account); - $decimals = (int)($currency?->decimal_places ?? 2); + $decimals = $currency?->decimal_places ?? 2; app('log')->debug(sprintf('Start of debt is "%s", so initial left of debt is "%s"', app('steam')->bcround($startOfDebt, $decimals), app('steam')->bcround($leftOfDebt, $decimals))); /** @var AccountMetaFactory $factory */ @@ -238,7 +238,7 @@ class CreditRecalculateService $source = $openingBalance->transactions()->where('amount', '<', 0)->first(); /** @var Transaction $dest */ $dest = $openingBalance->transactions()->where('amount', '>', 0)->first(); - if ((int)$source->account_id !== $account->id) { + if ($source->account_id !== $account->id) { app('log')->info(sprintf('Liability #%d has a reversed opening balance. Will fix this now.', $account->id)); app('log')->debug(sprintf('Source amount "%s" is now "%s"', $source->amount, app('steam')->positive($source->amount))); app('log')->debug(sprintf('Destination amount "%s" is now "%s"', $dest->amount, app('steam')->negative($dest->amount))); @@ -273,8 +273,7 @@ class CreditRecalculateService $journal = $transaction->transactionJournal; $foreignCurrency = $transaction->foreignCurrency; $accountCurrency = $this->repository->getAccountCurrency($account); - $groupId = $journal->transaction_group_id; - $decimals = (int)$accountCurrency->decimal_places; + $decimals = $accountCurrency->decimal_places; $type = $journal->transactionType->type; /** @var Transaction $destTransaction */ $destTransaction = $journal->transactions()->where('amount', '>', '0')->first(); @@ -307,7 +306,7 @@ class CreditRecalculateService // because we're lending person X more money if ( $type === TransactionType::WITHDRAWAL - && (int)$account->id === (int)$transaction->account_id + && $account->id === $transaction->account_id && 1 === bccomp($usedAmount, '0') && 'credit' === $direction ) { @@ -323,7 +322,7 @@ class CreditRecalculateService // because we're sending money away from the loan (like loan forgiveness) if ( $type === TransactionType::WITHDRAWAL - && (int)$account->id === (int)$sourceTransaction->account_id + && $account->id === $sourceTransaction->account_id && -1 === bccomp($usedAmount, '0') && 'credit' === $direction ) { @@ -339,7 +338,7 @@ class CreditRecalculateService // because the person is paying us back. if ( $type === TransactionType::DEPOSIT - && (int)$account->id === (int)$transaction->account_id + && $account->id === $transaction->account_id && -1 === bccomp($usedAmount, '0') && 'credit' === $direction ) { @@ -355,7 +354,7 @@ class CreditRecalculateService // because the person is having to pay more money. if ( $type === TransactionType::DEPOSIT - && (int)$account->id === (int)$destTransaction->account_id + && $account->id === $destTransaction->account_id && 1 === bccomp($usedAmount, '0') && 'credit' === $direction ) { @@ -369,7 +368,7 @@ class CreditRecalculateService // because the person has to pay more back. if ( $type === TransactionType::TRANSFER - && (int)$account->id === (int)$destTransaction->account_id + && $account->id === $destTransaction->account_id && 1 === bccomp($usedAmount, '0') && 'credit' === $direction ) { @@ -384,7 +383,7 @@ class CreditRecalculateService // because we're paying off the debt if ( $type === TransactionType::WITHDRAWAL - && (int)$account->id === (int)$transaction->account_id + && $account->id === $transaction->account_id && 1 === bccomp($usedAmount, '0') && 'debit' === $direction ) { @@ -399,7 +398,7 @@ class CreditRecalculateService // because we are borrowing more money. if ( $type === TransactionType::DEPOSIT - && (int)$account->id === (int)$transaction->account_id + && $account->id === $transaction->account_id && -1 === bccomp($usedAmount, '0') && 'debit' === $direction ) { @@ -414,7 +413,7 @@ class CreditRecalculateService // because we are paying interest. if ( $type === TransactionType::WITHDRAWAL - && (int)$account->id === (int)$transaction->account_id + && $account->id === $transaction->account_id && -1 === bccomp($usedAmount, '0') && 'debit' === $direction ) { @@ -433,7 +432,7 @@ class CreditRecalculateService return $result; } - Log::warning(sprintf('[-1] Catch-all, should not happen. Left of debt = %s', app('steam')->bcround($leftOfDebt, $decimals))); + app('log')->warning(sprintf('[-1] Catch-all, should not happen. Left of debt = %s', app('steam')->bcround($leftOfDebt, $decimals))); return $leftOfDebt; } diff --git a/app/Services/Internal/Support/JournalServiceTrait.php b/app/Services/Internal/Support/JournalServiceTrait.php index f82bfd83db..8dd6a93a96 100644 --- a/app/Services/Internal/Support/JournalServiceTrait.php +++ b/app/Services/Internal/Support/JournalServiceTrait.php @@ -35,7 +35,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\NullArrayObject; -use Illuminate\Support\Facades\Log; /** * Trait JournalServiceTrait @@ -59,7 +58,7 @@ trait JournalServiceTrait protected function getAccount(string $transactionType, string $direction, array $data): ?Account { // some debug logging: - Log::debug(sprintf('Now in getAccount(%s)', $direction), $data); + app('log')->debug(sprintf('Now in getAccount(%s)', $direction), $data); // expected type of source account, in order of preference /** @var array $array */ @@ -69,7 +68,7 @@ trait JournalServiceTrait // and now try to find it, based on the type of transaction. $message = 'Transaction = %s, %s account should be in: %s. Direction is %s.'; - Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType] ?? ['UNKNOWN']), $direction)); + app('log')->debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType] ?? ['UNKNOWN']), $direction)); $result = $this->findAccountById($data, $expectedTypes[$transactionType]); $result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType]); @@ -83,7 +82,7 @@ trait JournalServiceTrait // this account. In such a case, the name search must be retried with a new name. if (null !== $nameResult && null === $numberResult && null === $ibanResult && '' !== (string)$data['iban'] && '' !== (string)$nameResult->iban) { $data['name'] = sprintf('%s (%s)', $data['name'], $data['iban']); - Log::debug(sprintf('Search again using the new name, "%s".', $data['name'])); + app('log')->debug(sprintf('Search again using the new name, "%s".', $data['name'])); $result = $this->findAccountByName(null, $data, $expectedTypes[$transactionType]); } @@ -94,7 +93,7 @@ trait JournalServiceTrait // if the result is NULL but the ID is set, an account could exist of the wrong type. // that data can be used to create a new account of the right type. if (null === $result && null !== $data['id'] && null !== $creatableType) { - Log::debug(sprintf('Account #%d may exist and be of the wrong type, use data to create one of the right type.', $data['id'])); + app('log')->debug(sprintf('Account #%d may exist and be of the wrong type, use data to create one of the right type.', $data['id'])); $temp = $this->findAccountById(['id' => $data['id']], []); if (null !== $temp) { $tempData = [ @@ -107,11 +106,11 @@ trait JournalServiceTrait } } if (null === $result && null !== $creatableType) { - Log::debug('If nothing is found, create it.'); + app('log')->debug('If nothing is found, create it.'); $result = $this->createAccount($result, $data, $creatableType); } if (null === $result) { - Log::debug('If cant be created, return cash account.'); + app('log')->debug('If cant be created, return cash account.'); $result = $this->getCashAccount($result, $data, $expectedTypes[$transactionType]); } return $result; @@ -129,19 +128,19 @@ trait JournalServiceTrait if (null !== $data['id']) { $search = $this->accountRepository->find((int)$data['id']); if (null !== $search && in_array($search->accountType->type, $types, true)) { - Log::debug( + app('log')->debug( sprintf('Found "account_id" object: #%d, "%s" of type %s (1)', $search->id, $search->name, $search->accountType->type) ); return $search; } if (null !== $search && 0 === count($types)) { - Log::debug( + app('log')->debug( sprintf('Found "account_id" object: #%d, "%s" of type %s (2)', $search->id, $search->name, $search->accountType->type) ); return $search; } } - Log::debug(sprintf('Found no account by ID #%d of types', $data['id']), $types); + app('log')->debug(sprintf('Found no account by ID #%d of types', $data['id']), $types); return null; } @@ -155,24 +154,24 @@ trait JournalServiceTrait private function findAccountByIban(?Account $account, array $data, array $types): ?Account { if (null !== $account) { - Log::debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); + app('log')->debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); return $account; } if (null === $data['iban'] || '' === $data['iban']) { - Log::debug('IBAN is empty, will not search for IBAN.'); + app('log')->debug('IBAN is empty, will not search for IBAN.'); return null; } // find by preferred type. $source = $this->accountRepository->findByIbanNull($data['iban'], [$types[0]]); // or any expected type. - $source = $source ?? $this->accountRepository->findByIbanNull($data['iban'], $types); + $source ??= $this->accountRepository->findByIbanNull($data['iban'], $types); if (null !== $source) { - Log::debug(sprintf('Found "account_iban" object: #%d, %s', $source->id, $source->name)); + app('log')->debug(sprintf('Found "account_iban" object: #%d, %s', $source->id, $source->name)); return $source; } - Log::debug(sprintf('Found no account with IBAN "%s" of expected types', $data['iban']), $types); + app('log')->debug(sprintf('Found no account with IBAN "%s" of expected types', $data['iban']), $types); return null; } @@ -186,26 +185,26 @@ trait JournalServiceTrait private function findAccountByNumber(?Account $account, array $data, array $types): ?Account { if (null !== $account) { - Log::debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); + app('log')->debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); return $account; } if (null === $data['number'] || '' === $data['number']) { - Log::debug('Account number is empty, will not search for account number.'); + app('log')->debug('Account number is empty, will not search for account number.'); return null; } // find by preferred type. $source = $this->accountRepository->findByAccountNumber((string)$data['number'], [$types[0]]); // or any expected type. - $source = $source ?? $this->accountRepository->findByAccountNumber((string)$data['number'], $types); + $source ??= $this->accountRepository->findByAccountNumber((string)$data['number'], $types); if (null !== $source) { - Log::debug(sprintf('Found account: #%d, %s', $source->id, $source->name)); + app('log')->debug(sprintf('Found account: #%d, %s', $source->id, $source->name)); return $source; } - Log::debug(sprintf('Found no account with account number "%s" of expected types', $data['number']), $types); + app('log')->debug(sprintf('Found no account with account number "%s" of expected types', $data['number']), $types); return null; } @@ -219,11 +218,11 @@ trait JournalServiceTrait private function findAccountByName(?Account $account, array $data, array $types): ?Account { if (null !== $account) { - Log::debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); + app('log')->debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name)); return $account; } if (null === $data['name'] || '' === $data['name']) { - Log::debug('Account name is empty, will not search for account name.'); + app('log')->debug('Account name is empty, will not search for account name.'); return null; } @@ -231,14 +230,14 @@ trait JournalServiceTrait $source = $this->accountRepository->findByName($data['name'], [$types[0]]); // or any expected type. - $source = $source ?? $this->accountRepository->findByName($data['name'], $types); + $source ??= $this->accountRepository->findByName($data['name'], $types); if (null !== $source) { - Log::debug(sprintf('Found "account_name" object: #%d, %s', $source->id, $source->name)); + app('log')->debug(sprintf('Found "account_name" object: #%d, %s', $source->id, $source->name)); return $source; } - Log::debug(sprintf('Found no account with account name "%s" of expected types', $data['name']), $types); + app('log')->debug(sprintf('Found no account with account name "%s" of expected types', $data['name']), $types); return null; } @@ -271,10 +270,10 @@ trait JournalServiceTrait */ private function createAccount(?Account $account, array $data, string $preferredType): ?Account { - Log::debug('Now in createAccount()', $data); + app('log')->debug('Now in createAccount()', $data); // return new account. if (null !== $account) { - Log::debug( + app('log')->debug( sprintf( 'Was given %s account #%d ("%s") so will simply return that.', $account->accountType->type, @@ -290,17 +289,17 @@ trait JournalServiceTrait } // fix name of account if only IBAN is given: if ('' === (string)$data['name'] && '' !== (string)$data['iban']) { - Log::debug(sprintf('Account name is now IBAN ("%s")', $data['iban'])); + app('log')->debug(sprintf('Account name is now IBAN ("%s")', $data['iban'])); $data['name'] = $data['iban']; } // fix name of account if only number is given: if ('' === (string)$data['name'] && '' !== (string)$data['number']) { - Log::debug(sprintf('Account name is now account number ("%s")', $data['number'])); + app('log')->debug(sprintf('Account name is now account number ("%s")', $data['number'])); $data['name'] = $data['number']; } // if name is still NULL, return NULL. if ('' === (string)$data['name']) { - Log::debug('Account name is still NULL, return NULL.'); + app('log')->debug('Account name is still NULL, return NULL.'); return null; } //$data['name'] = $data['name'] ?? '(no name)'; @@ -348,7 +347,7 @@ trait JournalServiceTrait && in_array(AccountType::CASH, $types, true)) { $account = $this->accountRepository->getCashAccount(); } - Log::debug('Cannot return cash account, return input instead.'); + app('log')->debug('Cannot return cash account, return input instead.'); return $account; } @@ -363,7 +362,7 @@ trait JournalServiceTrait if ('' === $amount) { throw new FireflyException(sprintf('The amount cannot be an empty string: "%s"', $amount)); } - Log::debug(sprintf('Now in getAmount("%s")', $amount)); + app('log')->debug(sprintf('Now in getAmount("%s")', $amount)); if (0 === bccomp('0', $amount)) { throw new FireflyException(sprintf('The amount seems to be zero: "%s"', $amount)); } @@ -379,21 +378,21 @@ trait JournalServiceTrait protected function getForeignAmount(?string $amount): ?string { if (null === $amount) { - Log::debug('No foreign amount info in array. Return NULL'); + app('log')->debug('No foreign amount info in array. Return NULL'); return null; } if ('' === $amount) { - Log::debug('Foreign amount is empty string, return NULL.'); + app('log')->debug('Foreign amount is empty string, return NULL.'); return null; } if (0 === bccomp('0', $amount)) { - Log::debug('Foreign amount is 0.0, return NULL.'); + app('log')->debug('Foreign amount is 0.0, return NULL.'); return null; } - Log::debug(sprintf('Foreign amount is %s', $amount)); + app('log')->debug(sprintf('Foreign amount is %s', $amount)); return $amount; } @@ -413,7 +412,7 @@ trait JournalServiceTrait } $budget = $this->budgetRepository->findBudget($data['budget_id'], $data['budget_name']); if (null !== $budget) { - Log::debug(sprintf('Link budget #%d to journal #%d', $budget->id, $journal->id)); + app('log')->debug(sprintf('Link budget #%d to journal #%d', $budget->id, $journal->id)); $journal->budgets()->sync([$budget->id]); return; @@ -432,7 +431,7 @@ trait JournalServiceTrait { $category = $this->categoryRepository->findCategory($data['category_id'], $data['category_name']); if (null !== $category) { - Log::debug(sprintf('Link category #%d to journal #%d', $category->id, $journal->id)); + app('log')->debug(sprintf('Link category #%d to journal #%d', $category->id, $journal->id)); $journal->categories()->sync([$category->id]); return; @@ -458,14 +457,12 @@ trait JournalServiceTrait } $note->text = $notes; $note->save(); - Log::debug(sprintf('Stored notes for journal #%d', $journal->id)); + app('log')->debug(sprintf('Stored notes for journal #%d', $journal->id)); return; } - if ('' === $notes && null !== $note) { - // try to delete existing notes. - $note->delete(); - } + // try to delete existing notes. + $note?->delete(); } /** @@ -478,28 +475,28 @@ trait JournalServiceTrait */ protected function storeTags(TransactionJournal $journal, ?array $tags): void { - Log::debug('Now in storeTags()', $tags ?? []); + app('log')->debug('Now in storeTags()', $tags ?? []); $this->tagFactory->setUser($journal->user); $set = []; if (!is_array($tags)) { - Log::debug('Tags is not an array, break.'); + app('log')->debug('Tags is not an array, break.'); return; } - Log::debug('Start of loop.'); + app('log')->debug('Start of loop.'); foreach ($tags as $string) { $string = (string)$string; - Log::debug(sprintf('Now at tag "%s"', $string)); + app('log')->debug(sprintf('Now at tag "%s"', $string)); if ('' !== $string) { $tag = $this->tagFactory->findOrCreate($string); if (null !== $tag) { - $set[] = (int)$tag->id; + $set[] = $tag->id; } } } $set = array_unique($set); - Log::debug('End of loop.'); - Log::debug(sprintf('Total nr. of tags: %d', count($tags)), $tags); + app('log')->debug('End of loop.'); + app('log')->debug(sprintf('Total nr. of tags: %d', count($tags)), $tags); $journal->tags()->sync($set); } } diff --git a/app/Services/Internal/Support/LocationServiceTrait.php b/app/Services/Internal/Support/LocationServiceTrait.php index c2e1766e68..26281af7a2 100644 --- a/app/Services/Internal/Support/LocationServiceTrait.php +++ b/app/Services/Internal/Support/LocationServiceTrait.php @@ -40,7 +40,7 @@ trait LocationServiceTrait */ protected function storeNewLocation(Model $model, array $data): ?Location { - $data['store_location'] = $data['store_location'] ?? false; + $data['store_location'] ??= false; if ($data['store_location']) { $location = new Location(); $location->latitude = $data['latitude'] ?? config('firefly.default_location.latitude'); diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php index 324764f1b4..6c66716c01 100644 --- a/app/Services/Internal/Support/RecurringTransactionTrait.php +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -40,7 +40,6 @@ use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Validation\AccountValidator; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -107,9 +106,9 @@ trait RecurringTransactionTrait */ protected function createTransactions(Recurrence $recurrence, array $transactions): void { - Log::debug('Now in createTransactions()'); + app('log')->debug('Now in createTransactions()'); foreach ($transactions as $index => $array) { - Log::debug(sprintf('Now at transaction #%d', $index)); + app('log')->debug(sprintf('Now at transaction #%d', $index)); $sourceTypes = config(sprintf('firefly.expected_source_types.source.%s', $recurrence->transactionType->type)); $destTypes = config(sprintf('firefly.expected_source_types.destination.%s', $recurrence->transactionType->type)); $source = $this->findAccount($sourceTypes, $array['source_id'], null); @@ -120,10 +119,10 @@ trait RecurringTransactionTrait $currency = $factory->find($array['currency_id'] ?? null, $array['currency_code'] ?? null); $foreignCurrency = $factory->find($array['foreign_currency_id'] ?? null, $array['foreign_currency_code'] ?? null); if (null === $currency) { - $currency = app('amount')->getDefaultCurrencyByUser($recurrence->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($recurrence->user->userGroup); } - Log::debug( + app('log')->debug( sprintf('Will set the validator type to %s based on the type of the recurrence (#%d).', $recurrence->transactionType->type, $recurrence->id) ); @@ -197,7 +196,7 @@ trait RecurringTransactionTrait $repository->setUser($this->user); // if user has submitted an account ID, search for it. - $result = $repository->find((int)$accountId); + $result = $repository->find($accountId); if (null !== $result) { return $result; } @@ -222,7 +221,7 @@ trait RecurringTransactionTrait try { $result = $factory->findOrCreate($accountName, $expectedType); } catch (FireflyException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); } } } @@ -366,7 +365,7 @@ trait RecurringTransactionTrait */ protected function deleteTransactions(Recurrence $recurrence): void { - Log::debug('deleteTransactions()'); + app('log')->debug('deleteTransactions()'); /** @var RecurrenceTransaction $transaction */ foreach ($recurrence->recurrenceTransactions as $transaction) { $transaction->recurrenceTransactionMeta()->delete(); diff --git a/app/Services/Internal/Support/TransactionTypeTrait.php b/app/Services/Internal/Support/TransactionTypeTrait.php index d06f93af88..47f9fac10d 100644 --- a/app/Services/Internal/Support/TransactionTypeTrait.php +++ b/app/Services/Internal/Support/TransactionTypeTrait.php @@ -26,7 +26,6 @@ namespace FireflyIII\Services\Internal\Support; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\TransactionTypeFactory; use FireflyIII\Models\TransactionType; -use Illuminate\Support\Facades\Log; /** * Trait TransactionTypeTrait @@ -48,7 +47,7 @@ trait TransactionTypeTrait $factory = app(TransactionTypeFactory::class); $transactionType = $factory->find($type); if (null === $transactionType) { - Log::error(sprintf('Could not find transaction type for "%s"', $type)); + app('log')->error(sprintf('Could not find transaction type for "%s"', $type)); throw new FireflyException(sprintf('Could not find transaction type for "%s"', $type)); } diff --git a/app/Services/Internal/Update/AccountUpdateService.php b/app/Services/Internal/Update/AccountUpdateService.php index 5630684833..49ee1a0e57 100644 --- a/app/Services/Internal/Update/AccountUpdateService.php +++ b/app/Services/Internal/Update/AccountUpdateService.php @@ -31,7 +31,6 @@ use FireflyIII\Models\Location; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Services\Internal\Support\AccountServiceTrait; use FireflyIII\User; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -47,7 +46,6 @@ class AccountUpdateService protected array $validCCFields; protected array $validFields; private array $canHaveOpeningBalance; - private array $canHaveVirtual; private User $user; /** @@ -55,7 +53,6 @@ class AccountUpdateService */ public function __construct() { - $this->canHaveVirtual = config('firefly.can_have_virtual_amounts'); $this->canHaveOpeningBalance = config('firefly.can_have_opening_balance'); $this->validAssetFields = config('firefly.valid_asset_fields'); $this->validCCFields = config('firefly.valid_cc_fields'); @@ -75,7 +72,7 @@ class AccountUpdateService */ public function update(Account $account, array $data): Account { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $this->accountRepository->setUser($account->user); $this->user = $account->user; $account = $this->updateAccount($account, $data); @@ -198,21 +195,21 @@ class AccountUpdateService { // skip if no order info if (!array_key_exists('order', $data) || $data['order'] === $account->order) { - Log::debug(sprintf('Account order will not be touched because its not set or already at %d.', $account->order)); + app('log')->debug(sprintf('Account order will not be touched because its not set or already at %d.', $account->order)); return $account; } // skip if not of orderable type. $type = $account->accountType->type; if (!in_array($type, [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], true)) { - Log::debug('Will not change order of this account.'); + app('log')->debug('Will not change order of this account.'); return $account; } // get account type ID's because a join and an update is hard: - $oldOrder = (int)$account->order; + $oldOrder = $account->order; $newOrder = $data['order']; - Log::debug(sprintf('Order is set to be updated from %s to %s', $oldOrder, $newOrder)); + app('log')->debug(sprintf('Order is set to be updated from %s to %s', $oldOrder, $newOrder)); $list = $this->getTypeIds([AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT]); if ($type === AccountType::ASSET) { $list = $this->getTypeIds([AccountType::ASSET]); @@ -224,7 +221,7 @@ class AccountUpdateService ->whereIn('accounts.account_type_id', $list) ->decrement('order'); $account->order = $newOrder; - Log::debug(sprintf('Order of account #%d ("%s") is now %d', $account->id, $account->name, $newOrder)); + app('log')->debug(sprintf('Order of account #%d ("%s") is now %d', $account->id, $account->name, $newOrder)); $account->save(); return $account; @@ -235,7 +232,7 @@ class AccountUpdateService ->whereIn('accounts.account_type_id', $list) ->increment('order'); $account->order = $newOrder; - Log::debug(sprintf('Order of account #%d ("%s") is now %d', $account->id, $account->name, $newOrder)); + app('log')->debug(sprintf('Order of account #%d ("%s") is now %d', $account->id, $account->name, $newOrder)); $account->save(); return $account; @@ -253,7 +250,7 @@ class AccountUpdateService foreach ($array as $type) { /** @var AccountType $type */ $type = AccountType::whereType($type)->first(); - $return[] = (int)$type->id; + $return[] = $type->id; } return $return; @@ -341,17 +338,20 @@ class AccountUpdateService return; } $array = $preference->data; - Log::debug('Old array is: ', $array); - Log::debug(sprintf('Must remove : %d', $account->id)); - $removeAccountId = (int)$account->id; + if (!is_array($array)) { + $array = [$array]; + } + app('log')->debug('Old array is: ', $array); + app('log')->debug(sprintf('Must remove : %d', $account->id)); + $removeAccountId = $account->id; $new = []; foreach ($array as $value) { if ((int)$value !== $removeAccountId) { - Log::debug(sprintf('Will include: %d', $value)); + app('log')->debug(sprintf('Will include: %d', $value)); $new[] = (int)$value; } } - Log::debug('Final new array is', $new); + app('log')->debug('Final new array is', $new); app('preferences')->setForUser($account->user, 'frontpageAccounts', $new); } } diff --git a/app/Services/Internal/Update/BillUpdateService.php b/app/Services/Internal/Update/BillUpdateService.php index 025bdf4f0d..82429d5886 100644 --- a/app/Services/Internal/Update/BillUpdateService.php +++ b/app/Services/Internal/Update/BillUpdateService.php @@ -33,7 +33,6 @@ use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Services\Internal\Support\BillServiceTrait; use FireflyIII\User; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -61,7 +60,7 @@ class BillUpdateService if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { $factory = app(TransactionCurrencyFactory::class); $currency = $factory->find((int)($data['currency_id'] ?? null), $data['currency_code'] ?? null) ?? - app('amount')->getDefaultCurrencyByUser($bill->user); + app('amount')->getDefaultCurrencyByUserGroup($bill->user->userGroup); // enable the currency if it isn't. $currency->enabled = true; @@ -88,7 +87,7 @@ class BillUpdateService // update order. if (array_key_exists('order', $data)) { // update the order of the piggy bank: - $oldOrder = (int)$bill->order; + $oldOrder = $bill->order; $newOrder = (int)($data['order'] ?? $oldOrder); if ($oldOrder !== $newOrder) { $this->updateOrder($bill, $oldOrder, $newOrder); @@ -114,10 +113,8 @@ class BillUpdateService return $bill; } // remove if name is empty. Should be overruled by ID. - if ('' === $objectGroupTitle) { - $bill->objectGroups()->sync([]); - $bill->save(); - } + $bill->objectGroups()->sync([]); + $bill->save(); } if (array_key_exists('object_group_id', $data)) { // try also with ID: @@ -131,10 +128,8 @@ class BillUpdateService return $bill; } - if (0 === $objectGroupId) { - $bill->objectGroups()->sync([]); - $bill->save(); - } + $bill->objectGroups()->sync([]); + $bill->save(); } return $bill; @@ -214,17 +209,17 @@ class BillUpdateService */ private function updateBillTriggers(Bill $bill, array $oldData, array $newData): void { - Log::debug(sprintf('Now in updateBillTriggers(%d, "%s")', $bill->id, $bill->name)); + app('log')->debug(sprintf('Now in updateBillTriggers(%d, "%s")', $bill->id, $bill->name)); /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $repository->setUser($bill->user); $rules = $repository->getRulesForBill($bill); if (0 === $rules->count()) { - Log::debug('Found no rules.'); + app('log')->debug('Found no rules.'); return; } - Log::debug(sprintf('Found %d rules', $rules->count())); + app('log')->debug(sprintf('Found %d rules', $rules->count())); $fields = [ 'name' => 'description_contains', 'amount_min' => 'amount_more', @@ -236,7 +231,7 @@ class BillUpdateService continue; } if ($oldData[$field] === $newData[$field]) { - Log::debug(sprintf('Field %s is unchanged ("%s"), continue.', $field, $oldData[$field])); + app('log')->debug(sprintf('Field %s is unchanged ("%s"), continue.', $field, $oldData[$field])); continue; } $this->updateRules($rules, $ruleTriggerKey, $oldData[$field], $newData[$field]); @@ -255,14 +250,14 @@ class BillUpdateService foreach ($rules as $rule) { $trigger = $this->getRuleTrigger($rule, $key); if (null !== $trigger && $trigger->trigger_value === $oldValue) { - Log::debug(sprintf('Updated rule trigger #%d from value "%s" to value "%s"', $trigger->id, $oldValue, $newValue)); + app('log')->debug(sprintf('Updated rule trigger #%d from value "%s" to value "%s"', $trigger->id, $oldValue, $newValue)); $trigger->trigger_value = $newValue; $trigger->save(); continue; } if (null !== $trigger && $trigger->trigger_value !== $oldValue && in_array($key, ['amount_more', 'amount_less'], true) && 0 === bccomp($trigger->trigger_value, $oldValue)) { - Log::debug(sprintf('Updated rule trigger #%d from value "%s" to value "%s"', $trigger->id, $oldValue, $newValue)); + app('log')->debug(sprintf('Updated rule trigger #%d from value "%s" to value "%s"', $trigger->id, $oldValue, $newValue)); $trigger->trigger_value = $newValue; $trigger->save(); } diff --git a/app/Services/Internal/Update/CategoryUpdateService.php b/app/Services/Internal/Update/CategoryUpdateService.php index 0d2cdba3f7..10ae3ca6f3 100644 --- a/app/Services/Internal/Update/CategoryUpdateService.php +++ b/app/Services/Internal/Update/CategoryUpdateService.php @@ -29,7 +29,7 @@ use FireflyIII\Models\Note; use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleTrigger; -use Illuminate\Support\Facades\Log; +use FireflyIII\User; /** * Class CategoryUpdateService @@ -38,7 +38,7 @@ use Illuminate\Support\Facades\Log; */ class CategoryUpdateService { - private $user; + private User $user; /** * Constructor. @@ -46,7 +46,9 @@ class CategoryUpdateService public function __construct() { if (auth()->check()) { - $this->user = auth()->user(); + /** @var User $user */ + $user = auth()->user(); + $this->user = $user; } } @@ -94,12 +96,12 @@ class CategoryUpdateService ->whereIn('rule_triggers.trigger_type', $types) ->where('rule_triggers.trigger_value', $oldName) ->get(['rule_triggers.*']); - Log::debug(sprintf('Found %d triggers to update.', $triggers->count())); + app('log')->debug(sprintf('Found %d triggers to update.', $triggers->count())); /** @var RuleTrigger $trigger */ foreach ($triggers as $trigger) { $trigger->trigger_value = $newName; $trigger->save(); - Log::debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); + app('log')->debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); } } @@ -115,12 +117,12 @@ class CategoryUpdateService ->whereIn('rule_actions.action_type', $types) ->where('rule_actions.action_value', $oldName) ->get(['rule_actions.*']); - Log::debug(sprintf('Found %d actions to update.', $actions->count())); + app('log')->debug(sprintf('Found %d actions to update.', $actions->count())); /** @var RuleAction $action */ foreach ($actions as $action) { $action->action_value = $newName; $action->save(); - Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); + app('log')->debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); } } diff --git a/app/Services/Internal/Update/CurrencyUpdateService.php b/app/Services/Internal/Update/CurrencyUpdateService.php index 01fe1692f6..02faa86a1f 100644 --- a/app/Services/Internal/Update/CurrencyUpdateService.php +++ b/app/Services/Internal/Update/CurrencyUpdateService.php @@ -55,10 +55,10 @@ class CurrencyUpdateService $currency->enabled = false; if (array_key_exists('decimal_places', $data) && is_int($data['decimal_places'])) { - $currency->decimal_places = (int)$data['decimal_places']; + $currency->decimal_places = $data['decimal_places']; } - unset($currency->userEnabled); - unset($currency->userDefault); + $currency->userEnabled = null; + $currency->userDefault = null; $currency->save(); return $currency; diff --git a/app/Services/Internal/Update/GroupCloneService.php b/app/Services/Internal/Update/GroupCloneService.php index 21a0e3bbc4..5cf3734f88 100644 --- a/app/Services/Internal/Update/GroupCloneService.php +++ b/app/Services/Internal/Update/GroupCloneService.php @@ -49,7 +49,7 @@ class GroupCloneService $newGroup = $group->replicate(); $newGroup->save(); foreach ($group->transactionJournals as $journal) { - $this->cloneJournal($journal, $newGroup, (int)$group->id); + $this->cloneJournal($journal, $newGroup, $group->id); } return $newGroup; @@ -105,7 +105,7 @@ class GroupCloneService // add relation. // TODO clone ALL linked piggy banks - /** @var PiggyBankEvent $event */ + /** @var PiggyBankEvent|null $event */ $event = $journal->piggyBankEvents()->first(); if (null !== $event) { $piggyBank = $event->piggyBank; diff --git a/app/Services/Internal/Update/GroupUpdateService.php b/app/Services/Internal/Update/GroupUpdateService.php index 3fc5e45876..7ced30befc 100644 --- a/app/Services/Internal/Update/GroupUpdateService.php +++ b/app/Services/Internal/Update/GroupUpdateService.php @@ -30,7 +30,6 @@ use FireflyIII\Factory\TransactionJournalFactory; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Services\Internal\Destroy\JournalDestroyService; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -51,13 +50,13 @@ class GroupUpdateService */ public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup { - Log::debug(sprintf('Now in %s', __METHOD__)); - Log::debug('Now in group update service', $data); + app('log')->debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug('Now in group update service', $data); /** @var array $transactions */ $transactions = $data['transactions'] ?? []; // update group name. if (array_key_exists('group_title', $data)) { - Log::debug(sprintf('Update transaction group #%d title.', $transactionGroup->id)); + app('log')->debug(sprintf('Update transaction group #%d title.', $transactionGroup->id)); $oldTitle = $transactionGroup->title; $transactionGroup->title = $data['group_title']; $transactionGroup->save(); @@ -74,7 +73,7 @@ class GroupUpdateService if (0 === count($transactions)) { - Log::debug('No transactions submitted, do nothing.'); + app('log')->debug('No transactions submitted, do nothing.'); return $transactionGroup; } @@ -82,7 +81,7 @@ class GroupUpdateService if (1 === count($transactions) && 1 === $transactionGroup->transactionJournals()->count()) { /** @var TransactionJournal $first */ $first = $transactionGroup->transactionJournals()->first(); - Log::debug( + app('log')->debug( sprintf('Will now update journal #%d (only journal in group #%d)', $first->id, $transactionGroup->id) ); $this->updateTransactionJournal($transactionGroup, $first, reset($transactions)); @@ -92,14 +91,14 @@ class GroupUpdateService return $transactionGroup; } - Log::debug('Going to update split group.'); + app('log')->debug('Going to update split group.'); $existing = $transactionGroup->transactionJournals->pluck('id')->toArray(); $updated = $this->updateTransactions($transactionGroup, $transactions); - Log::debug('Array of updated IDs: ', $updated); + app('log')->debug('Array of updated IDs: ', $updated); if (0 === count($updated)) { - Log::error('There were no transactions updated or created. Will not delete anything.'); + app('log')->error('There were no transactions updated or created. Will not delete anything.'); $transactionGroup->refresh(); app('preferences')->mark(); @@ -107,7 +106,7 @@ class GroupUpdateService } $result = array_diff($existing, $updated); - Log::debug('Result of DIFF: ', $result); + app('log')->debug('Result of DIFF: ', $result); if (count($result) > 0) { /** @var string $deletedId */ foreach ($result as $deletedId) { @@ -136,8 +135,9 @@ class GroupUpdateService TransactionGroup $transactionGroup, TransactionJournal $journal, array $data - ): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + ): void + { + app('log')->debug(sprintf('Now in %s', __METHOD__)); if (0 === count($data)) { return; } @@ -163,7 +163,7 @@ class GroupUpdateService */ private function updateTransactions(TransactionGroup $transactionGroup, array $transactions): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); // updated or created transaction journals: $updated = []; /** @@ -171,40 +171,40 @@ class GroupUpdateService * @var array $transaction */ foreach ($transactions as $index => $transaction) { - Log::debug(sprintf('Now at #%d of %d', ($index + 1), count($transactions)), $transaction); + app('log')->debug(sprintf('Now at #%d of %d', ($index + 1), count($transactions)), $transaction); $journalId = (int)($transaction['transaction_journal_id'] ?? 0); /** @var TransactionJournal|null $journal */ $journal = $transactionGroup->transactionJournals()->find($journalId); if (null === $journal) { - Log::debug('This entry has no existing journal: make a new split.'); + app('log')->debug('This entry has no existing journal: make a new split.'); // force the transaction type on the transaction data. // by plucking it from another journal in the group: if (!array_key_exists('type', $transaction)) { - Log::debug('No transaction type is indicated.'); + app('log')->debug('No transaction type is indicated.'); /** @var TransactionJournal|null $randomJournal */ $randomJournal = $transactionGroup->transactionJournals()->inRandomOrder()->with( ['transactionType'] )->first(); if (null !== $randomJournal) { $transaction['type'] = $randomJournal->transactionType->type; - Log::debug(sprintf('Transaction type set to %s.', $transaction['type'])); + app('log')->debug(sprintf('Transaction type set to %s.', $transaction['type'])); } } - Log::debug('Call createTransactionJournal'); + app('log')->debug('Call createTransactionJournal'); $newJournal = $this->createTransactionJournal($transactionGroup, $transaction); - Log::debug('Done calling createTransactionJournal'); + app('log')->debug('Done calling createTransactionJournal'); if (null !== $newJournal) { $updated[] = $newJournal->id; } if (null === $newJournal) { - Log::error('createTransactionJournal returned NULL, indicating something went wrong.'); + app('log')->error('createTransactionJournal returned NULL, indicating something went wrong.'); } } if (null !== $journal) { - Log::debug('Call updateTransactionJournal'); + app('log')->debug('Call updateTransactionJournal'); $this->updateTransactionJournal($transactionGroup, $journal, $transaction); $updated[] = $journal->id; - Log::debug('Done calling updateTransactionJournal'); + app('log')->debug('Done calling updateTransactionJournal'); } } @@ -234,8 +234,8 @@ class GroupUpdateService try { $collection = $factory->create($submission); } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); throw new FireflyException( sprintf('Could not create new transaction journal: %s', $e->getMessage()), 0, @@ -243,7 +243,7 @@ class GroupUpdateService ); } $collection->each( - function (TransactionJournal $journal) use ($transactionGroup) { + static function (TransactionJournal $journal) use ($transactionGroup) { $transactionGroup->transactionJournals()->save($journal); } ); diff --git a/app/Services/Internal/Update/JournalUpdateService.php b/app/Services/Internal/Update/JournalUpdateService.php index e0f190f999..b02401cf79 100644 --- a/app/Services/Internal/Update/JournalUpdateService.php +++ b/app/Services/Internal/Update/JournalUpdateService.php @@ -43,7 +43,6 @@ use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Services\Internal\Support\JournalServiceTrait; use FireflyIII\Support\NullArrayObject; use FireflyIII\Validation\AccountValidator; -use Illuminate\Support\Facades\Log; /** * Class to centralise code that updates a journal given the input by system. @@ -140,15 +139,15 @@ class JournalUpdateService */ public function update(): void { - Log::debug(sprintf('Now in %s', __METHOD__)); - Log::debug(sprintf('Now in JournalUpdateService for journal #%d.', $this->transactionJournal->id)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in JournalUpdateService for journal #%d.', $this->transactionJournal->id)); $this->data['reconciled'] = array_key_exists('reconciled', $this->data) ? $this->data['reconciled'] : false; // can we update account data using the new type? if ($this->hasValidAccounts()) { - Log::info('Account info is valid, now update.'); + app('log')->info('Account info is valid, now update.'); // update accounts: $this->updateAccounts(); @@ -194,7 +193,7 @@ class JournalUpdateService */ private function hasValidSourceAccount(): bool { - Log::debug('Now in hasValidSourceAccount().'); + app('log')->debug('Now in hasValidSourceAccount().'); $sourceId = $this->data['source_id'] ?? null; $sourceName = $this->data['source_name'] ?? null; @@ -206,7 +205,7 @@ class JournalUpdateService // make new account validator. $expectedType = $this->getExpectedType(); - Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); + app('log')->debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); // make a new validator. /** @var AccountValidator $validator */ @@ -215,7 +214,7 @@ class JournalUpdateService $validator->setUser($this->transactionJournal->user); $result = $validator->validateSource(['id' => $sourceId]); - Log::debug( + app('log')->debug( sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true)) ); @@ -266,7 +265,7 @@ class JournalUpdateService 0 )->first(); } - Log::debug(sprintf('getSourceTransaction: %s', $this->sourceTransaction->amount)); + app('log')->debug(sprintf('getSourceTransaction: %s', $this->sourceTransaction->amount)); return $this->sourceTransaction; } @@ -282,7 +281,7 @@ class JournalUpdateService */ private function getExpectedType(): string { - Log::debug('Now in getExpectedType()'); + app('log')->debug('Now in getExpectedType()'); if ($this->hasFields(['type'])) { return ucfirst('opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type']); } @@ -295,12 +294,12 @@ class JournalUpdateService */ private function hasValidDestinationAccount(): bool { - Log::debug('Now in hasValidDestinationAccount().'); + app('log')->debug('Now in hasValidDestinationAccount().'); $destId = $this->data['destination_id'] ?? null; $destName = $this->data['destination_name'] ?? null; if (!$this->hasFields(['destination_id', 'destination_name'])) { - Log::debug('No destination info submitted, grab the original data.'); + app('log')->debug('No destination info submitted, grab the original data.'); $destination = $this->getOriginalDestinationAccount(); $destId = $destination->id; $destName = $destination->name; @@ -308,7 +307,7 @@ class JournalUpdateService // make new account validator. $expectedType = $this->getExpectedType(); - Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); + app('log')->debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); // make a new validator. /** @var AccountValidator $validator */ @@ -317,7 +316,7 @@ class JournalUpdateService $validator->setUser($this->transactionJournal->user); $validator->source = $this->getValidSourceAccount(); $result = $validator->validateDestination(['id' => $destId, 'name' => $destName]); - Log::debug( + app('log')->debug( sprintf( 'hasValidDestinationAccount(%d, "%s") will return %s', $destId, @@ -366,7 +365,7 @@ class JournalUpdateService */ private function getValidSourceAccount(): Account { - Log::debug('Now in getValidSourceAccount().'); + app('log')->debug('Now in getValidSourceAccount().'); if (!$this->hasFields(['source_id', 'source_name'])) { return $this->getOriginalSourceAccount(); @@ -384,12 +383,12 @@ class JournalUpdateService try { $result = $this->getAccount($expectedType, 'source', $sourceInfo); } catch (FireflyException $e) { - Log::error(sprintf('Cant get the valid source account: %s', $e->getMessage())); + app('log')->error(sprintf('Cant get the valid source account: %s', $e->getMessage())); $result = $this->getOriginalSourceAccount(); } - Log::debug(sprintf('getValidSourceAccount() will return #%d ("%s")', $result->id, $result->name)); + app('log')->debug(sprintf('getValidSourceAccount() will return #%d ("%s")', $result->id, $result->name)); return $result; } @@ -404,7 +403,7 @@ class JournalUpdateService // cowardly refuse to update if both accounts are the same. if ($source->id === $destination->id) { - Log::error(sprintf('Source + dest accounts are equal (%d, "%s")', $source->id, $source->name)); + app('log')->error(sprintf('Source + dest accounts are equal (%d, "%s")', $source->id, $source->name)); return; } @@ -420,8 +419,8 @@ class JournalUpdateService // refresh transactions. $this->sourceTransaction->refresh(); $this->destinationTransaction->refresh(); - Log::debug(sprintf('Will set source to #%d ("%s")', $source->id, $source->name)); - Log::debug(sprintf('Will set dest to #%d ("%s")', $destination->id, $destination->name)); + app('log')->debug(sprintf('Will set source to #%d ("%s")', $source->id, $source->name)); + app('log')->debug(sprintf('Will set dest to #%d ("%s")', $destination->id, $destination->name)); } /** @@ -431,7 +430,7 @@ class JournalUpdateService */ private function getValidDestinationAccount(): Account { - Log::debug('Now in getValidDestinationAccount().'); + app('log')->debug('Now in getValidDestinationAccount().'); if (!$this->hasFields(['destination_id', 'destination_name'])) { return $this->getOriginalDestinationAccount(); @@ -447,11 +446,11 @@ class JournalUpdateService // make new account validator. $expectedType = $this->getExpectedType(); - Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); + app('log')->debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); try { $result = $this->getAccount($expectedType, 'destination', $destInfo); } catch (FireflyException $e) { - Log::error(sprintf('getValidDestinationAccount() threw unexpected error: %s', $e->getMessage())); + app('log')->error(sprintf('getValidDestinationAccount() threw unexpected error: %s', $e->getMessage())); $result = $this->getOriginalDestinationAccount(); } @@ -463,10 +462,10 @@ class JournalUpdateService */ private function updateType(): void { - Log::debug('Now in updateType()'); + app('log')->debug('Now in updateType()'); if ($this->hasFields(['type'])) { $type = 'opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type']; - Log::debug( + app('log')->debug( sprintf( 'Trying to change journal #%d from a %s to a %s.', $this->transactionJournal->id, @@ -479,7 +478,7 @@ class JournalUpdateService $typeFactory = app(TransactionTypeFactory::class); $result = $typeFactory->find($this->data['type']); if (null !== $result) { - Log::debug('Changed transaction type!'); + app('log')->debug('Changed transaction type!'); $this->transactionJournal->transaction_type_id = $result->id; $this->transactionJournal->save(); @@ -488,7 +487,7 @@ class JournalUpdateService return; } - Log::debug('No type field present.'); + app('log')->debug('No type field present.'); } /** @@ -507,7 +506,7 @@ class JournalUpdateService $billName = (string)($this->data['bill_name'] ?? ''); $bill = $this->billRepository->findBill($billId, $billName); $this->transactionJournal->bill_id = $bill?->id; - Log::debug('Updated bill ID'); + app('log')->debug('Updated bill ID'); } } @@ -530,20 +529,20 @@ class JournalUpdateService $value = new Carbon($value); } // do some parsing. - Log::debug(sprintf('Create date value from string "%s".', $value)); + app('log')->debug(sprintf('Create date value from string "%s".', $value)); } event( new TriggeredAuditLog( $this->transactionJournal->user, $this->transactionJournal, sprintf('update_%s', $fieldName), - $this->transactionJournal->$fieldName, + $this->transactionJournal->$fieldName, // @phpstan-ignore-line $value ) ); - $this->transactionJournal->$fieldName = $value; - Log::debug(sprintf('Updated %s', $fieldName)); + $this->transactionJournal->$fieldName = $value;// @phpstan-ignore-line + app('log')->debug(sprintf('Updated %s', $fieldName)); } } @@ -554,7 +553,7 @@ class JournalUpdateService { // update category if ($this->hasFields(['category_id', 'category_name'])) { - Log::debug('Will update category.'); + app('log')->debug('Will update category.'); $this->storeCategory($this->transactionJournal, new NullArrayObject($this->data)); } @@ -567,7 +566,7 @@ class JournalUpdateService { // update budget if ($this->hasFields(['budget_id', 'budget_name'])) { - Log::debug('Will update budget.'); + app('log')->debug('Will update budget.'); $this->storeBudget($this->transactionJournal, new NullArrayObject($this->data)); } // is transfer? remove budget @@ -582,7 +581,7 @@ class JournalUpdateService private function updateTags(): void { if ($this->hasFields(['tags'])) { - Log::debug('Will update tags.'); + app('log')->debug('Will update tags.'); $tags = $this->data['tags'] ?? null; $this->storeTags($this->transactionJournal, $tags); } @@ -618,13 +617,13 @@ class JournalUpdateService // update meta fields. // first string if ($this->hasFields($this->metaString)) { - Log::debug('Meta string fields are present.'); + app('log')->debug('Meta string fields are present.'); $this->updateMetaFields(); } // then date fields. if ($this->hasFields($this->metaDate)) { - Log::debug('Meta date fields are present.'); + app('log')->debug('Meta date fields are present.'); $this->updateMetaDateFields(); } } @@ -640,7 +639,7 @@ class JournalUpdateService foreach ($this->metaString as $field) { if ($this->hasFields([$field])) { $value = '' === $this->data[$field] ? null : $this->data[$field]; - Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value)); + app('log')->debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value)); $set = [ 'journal' => $this->transactionJournal, 'name' => $field, @@ -663,12 +662,12 @@ class JournalUpdateService if ($this->hasFields([$field])) { try { $value = '' === (string)$this->data[$field] ? null : new Carbon($this->data[$field]); - } catch (InvalidDateException $e) { - Log::debug(sprintf('%s is not a valid date value: %s', $this->data[$field], $e->getMessage())); + } catch (InvalidDateException $e) { // @phpstan-ignore-line + app('log')->debug(sprintf('%s is not a valid date value: %s', $this->data[$field], $e->getMessage())); return; } - Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value)); + app('log')->debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value)); $set = [ 'journal' => $this->transactionJournal, 'name' => $field, @@ -691,24 +690,22 @@ class JournalUpdateService $currencyId = $this->data['currency_id'] ?? null; $currencyCode = $this->data['currency_code'] ?? null; $currency = $this->currencyRepository->findCurrency($currencyId, $currencyCode); - if (null !== $currency) { - // update currency everywhere. - $this->transactionJournal->transaction_currency_id = $currency->id; - $this->transactionJournal->save(); + // update currency everywhere. + $this->transactionJournal->transaction_currency_id = $currency->id; + $this->transactionJournal->save(); - $source = $this->getSourceTransaction(); - $source->transaction_currency_id = $currency->id; - $source->save(); + $source = $this->getSourceTransaction(); + $source->transaction_currency_id = $currency->id; + $source->save(); - $dest = $this->getDestinationTransaction(); - $dest->transaction_currency_id = $currency->id; - $dest->save(); + $dest = $this->getDestinationTransaction(); + $dest->transaction_currency_id = $currency->id; + $dest->save(); - // refresh transactions. - $this->sourceTransaction->refresh(); - $this->destinationTransaction->refresh(); - Log::debug(sprintf('Updated currency to #%d (%s)', $currency->id, $currency->code)); - } + // refresh transactions. + $this->sourceTransaction->refresh(); + $this->destinationTransaction->refresh(); + app('log')->debug(sprintf('Updated currency to #%d (%s)', $currency->id, $currency->code)); } /** @@ -716,17 +713,17 @@ class JournalUpdateService */ private function updateAmount(): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); if (!$this->hasFields(['amount'])) { return; } $value = $this->data['amount'] ?? ''; - Log::debug(sprintf('Amount is now "%s"', $value)); + app('log')->debug(sprintf('Amount is now "%s"', $value)); try { $amount = $this->getAmount($value); } catch (FireflyException $e) { - Log::debug(sprintf('getAmount("%s") returns error: %s', $value, $e->getMessage())); + app('log')->debug(sprintf('getAmount("%s") returns error: %s', $value, $e->getMessage())); return; } @@ -739,7 +736,7 @@ class JournalUpdateService // refresh transactions. $this->sourceTransaction->refresh(); $this->destinationTransaction->refresh(); - Log::debug(sprintf('Updated amount to "%s"', $amount)); + app('log')->debug(sprintf('Updated amount to "%s"', $amount)); } /** @@ -766,7 +763,7 @@ class JournalUpdateService // not the same as normal currency if (null !== $foreignCurrency && $foreignCurrency->id === $this->transactionJournal->transaction_currency_id) { - Log::error(sprintf('Foreign currency is equal to normal currency (%s)', $foreignCurrency->code)); + app('log')->error(sprintf('Foreign currency is equal to normal currency (%s)', $foreignCurrency->code)); return; } @@ -780,7 +777,7 @@ class JournalUpdateService $dest->foreign_amount = app('steam')->positive($foreignAmount); $dest->save(); - Log::debug( + app('log')->debug( sprintf( 'Update foreign info to %s (#%d) %s', $foreignCurrency->code, @@ -803,9 +800,9 @@ class JournalUpdateService $dest->foreign_currency_id = null; $dest->foreign_amount = null; $dest->save(); - Log::debug(sprintf('Foreign amount is "%s" so remove foreign amount info.', $amount)); + app('log')->debug(sprintf('Foreign amount is "%s" so remove foreign amount info.', $amount)); } - Log::info('Not enough info to update foreign currency info.'); + app('log')->info('Not enough info to update foreign currency info.'); // refresh transactions. $this->sourceTransaction->refresh(); diff --git a/app/Services/Internal/Update/RecurrenceUpdateService.php b/app/Services/Internal/Update/RecurrenceUpdateService.php index 559f333fd3..0253b7a33e 100644 --- a/app/Services/Internal/Update/RecurrenceUpdateService.php +++ b/app/Services/Internal/Update/RecurrenceUpdateService.php @@ -32,7 +32,6 @@ use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Services\Internal\Support\RecurringTransactionTrait; use FireflyIII\Services\Internal\Support\TransactionTypeTrait; use FireflyIII\User; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -42,8 +41,8 @@ use JsonException; */ class RecurrenceUpdateService { - use TransactionTypeTrait; use RecurringTransactionTrait; + use TransactionTypeTrait; private User $user; @@ -99,7 +98,7 @@ class RecurrenceUpdateService // update all repetitions if (array_key_exists('repetitions', $data)) { - Log::debug('Will update repetitions array'); + app('log')->debug('Will update repetitions array'); // update each repetition or throw error yay $this->updateRepetitions($recurrence, $data['repetitions'] ?? []); } @@ -148,14 +147,14 @@ class RecurrenceUpdateService } // user added or removed repetitions, delete all and recreate: if ($originalCount !== count($repetitions)) { - Log::debug('Delete existing repetitions and create new ones.'); + app('log')->debug('Delete existing repetitions and create new ones.'); $this->deleteRepetitions($recurrence); $this->createRepetitions($recurrence, $repetitions); return; } // loop all and try to match them: - Log::debug('Loop and find'); + app('log')->debug('Loop and find'); foreach ($repetitions as $current) { $match = $this->matchRepetition($recurrence, $current); if (null === $match) { @@ -186,7 +185,7 @@ class RecurrenceUpdateService { $originalCount = $recurrence->recurrenceRepetitions()->count(); if (1 === $originalCount) { - Log::debug('Return the first one'); + app('log')->debug('Return the first one'); /** @var RecurrenceRepetition|null */ return $recurrence->recurrenceRepetitions()->first(); } @@ -219,12 +218,12 @@ class RecurrenceUpdateService */ private function updateTransactions(Recurrence $recurrence, array $transactions): void { - Log::debug('Now in updateTransactions()'); + app('log')->debug('Now in updateTransactions()'); $originalCount = $recurrence->recurrenceTransactions()->count(); - Log::debug(sprintf('Original count is %d', $originalCount)); + app('log')->debug(sprintf('Original count is %d', $originalCount)); if (0 === count($transactions)) { // won't drop transactions, rather avoid. - Log::warning('No transactions to update, too scared to continue!'); + app('log')->warning('No transactions to update, too scared to continue!'); return; } $combinations = []; @@ -235,7 +234,7 @@ class RecurrenceUpdateService foreach ($originalTransactions as $i => $originalTransaction) { foreach ($transactions as $ii => $submittedTransaction) { if (array_key_exists('id', $submittedTransaction) && (int)$originalTransaction['id'] === (int)$submittedTransaction['id']) { - Log::debug(sprintf('Match original transaction #%d with an entry in the submitted array.', $originalTransaction['id'])); + app('log')->debug(sprintf('Match original transaction #%d with an entry in the submitted array.', $originalTransaction['id'])); $combinations[] = [ 'original' => $originalTransaction, 'submitted' => $submittedTransaction, @@ -250,7 +249,7 @@ class RecurrenceUpdateService */ if (1 === count($originalTransactions) && 1 === count($transactions)) { $first = array_shift($originalTransactions); - Log::debug(sprintf('One left of each, link them (ID is #%d)', $first['id'])); + app('log')->debug(sprintf('One left of each, link them (ID is #%d)', $first['id'])); $combinations[] = [ 'original' => $first, 'submitted' => array_shift($transactions), @@ -265,7 +264,7 @@ class RecurrenceUpdateService } // anything left in the original transactions array can be deleted. foreach ($originalTransactions as $original) { - Log::debug(sprintf('Original transaction #%d is unmatched, delete it!', $original['id'])); + app('log')->debug(sprintf('Original transaction #%d is unmatched, delete it!', $original['id'])); $this->deleteTransaction($recurrence, (int)$original['id']); } // anything left is new. @@ -284,7 +283,7 @@ class RecurrenceUpdateService $submitted = $combination['submitted']; /** @var RecurrenceTransaction $transaction */ $transaction = $recurrence->recurrenceTransactions()->find($original['id']); - Log::debug(sprintf('Now in updateCombination(#%d)', $original['id'])); + app('log')->debug(sprintf('Now in updateCombination(#%d)', $original['id'])); $currencyFactory = app(TransactionCurrencyFactory::class); @@ -292,22 +291,28 @@ class RecurrenceUpdateService $currency = null; $foreignCurrency = null; if (array_key_exists('currency_id', $submitted) || array_key_exists('currency_code', $submitted)) { - $currency = $currencyFactory->find($submitted['currency_id'] ?? null, $currency['currency_code'] ?? null); + $currency = $currencyFactory->find( + array_key_exists('currency_id', $submitted) ? (int)$submitted['currency_id'] : null, + array_key_exists('currency_code', $submitted) ? $submitted['currency_code'] : null + ); } if (null === $currency) { unset($submitted['currency_id'], $submitted['currency_code']); } if (null !== $currency) { - $submitted['currency_id'] = (int)$currency->id; + $submitted['currency_id'] = $currency->id; } if (array_key_exists('foreign_currency_id', $submitted) || array_key_exists('foreign_currency_code', $submitted)) { - $foreignCurrency = $currencyFactory->find($submitted['foreign_currency_id'] ?? null, $currency['foreign_currency_code'] ?? null); + $foreignCurrency = $currencyFactory->find( + array_key_exists('foreign_currency_id', $submitted) ? (int)$submitted['foreign_currency_id'] : null, + array_key_exists('foreign_currency_code', $submitted) ? $submitted['foreign_currency_code'] : null + ); } if (null === $foreignCurrency) { unset($submitted['foreign_currency_id'], $currency['foreign_currency_code']); } if (null !== $foreignCurrency) { - $submitted['foreign_currency_id'] = (int)$foreignCurrency->id; + $submitted['foreign_currency_id'] = $foreignCurrency->id; } // update fields that are part of the recurring transaction itself. @@ -336,13 +341,13 @@ class RecurrenceUpdateService // reset category if name is set but empty: // can be removed when v1 is retired. if (array_key_exists('category_name', $submitted) && '' === (string)$submitted['category_name']) { - Log::debug('Category name is submitted but is empty. Set category to be empty.'); + app('log')->debug('Category name is submitted but is empty. Set category to be empty.'); $submitted['category_name'] = null; $submitted['category_id'] = 0; } if (array_key_exists('category_id', $submitted)) { - Log::debug(sprintf('Category ID is submitted, set category to be %d.', (int)$submitted['category_id'])); + app('log')->debug(sprintf('Category ID is submitted, set category to be %d.', (int)$submitted['category_id'])); $this->setCategory($transaction, (int)$submitted['category_id']); } @@ -362,7 +367,7 @@ class RecurrenceUpdateService */ private function deleteTransaction(Recurrence $recurrence, int $transactionId): void { - Log::debug(sprintf('Will delete transaction #%d in recurrence #%d.', $transactionId, $recurrence->id)); + app('log')->debug(sprintf('Will delete transaction #%d in recurrence #%d.', $transactionId, $recurrence->id)); $recurrence->recurrenceTransactions()->where('id', $transactionId)->delete(); } } diff --git a/app/Services/Password/PwndVerifierV2.php b/app/Services/Password/PwndVerifierV2.php index 76d7bf1a2f..6db7ac8e91 100644 --- a/app/Services/Password/PwndVerifierV2.php +++ b/app/Services/Password/PwndVerifierV2.php @@ -26,7 +26,6 @@ namespace FireflyIII\Services\Password; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\RequestException; -use Illuminate\Support\Facades\Log; /** * Class PwndVerifierV2. @@ -57,28 +56,28 @@ class PwndVerifierV2 implements Verifier 'timeout' => 3.1415, ]; - Log::debug(sprintf('hash prefix is %s', $prefix)); - Log::debug(sprintf('rest is %s', $rest)); + app('log')->debug(sprintf('hash prefix is %s', $prefix)); + app('log')->debug(sprintf('rest is %s', $rest)); try { $client = new Client(); $res = $client->request('GET', $url, $opt); } catch (GuzzleException | RequestException $e) { - Log::error(sprintf('Could not verify password security: %s', $e->getMessage())); + app('log')->error(sprintf('Could not verify password security: %s', $e->getMessage())); return true; } - Log::debug(sprintf('Status code returned is %d', $res->getStatusCode())); + app('log')->debug(sprintf('Status code returned is %d', $res->getStatusCode())); if (404 === $res->getStatusCode()) { return true; } $strpos = stripos($res->getBody()->getContents(), $rest); if (false === $strpos) { - Log::debug(sprintf('%s was not found in result body. Return true.', $rest)); + app('log')->debug(sprintf('%s was not found in result body. Return true.', $rest)); return true; } - Log::debug(sprintf('Found %s, return FALSE.', $rest)); + app('log')->debug(sprintf('Found %s, return FALSE.', $rest)); return false; } diff --git a/app/Services/Webhook/StandardWebhookSender.php b/app/Services/Webhook/StandardWebhookSender.php index 0d4bff7069..da8ee71d71 100644 --- a/app/Services/Webhook/StandardWebhookSender.php +++ b/app/Services/Webhook/StandardWebhookSender.php @@ -30,7 +30,6 @@ use FireflyIII\Models\WebhookMessage; use GuzzleHttp\Client; use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\RequestException; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -62,9 +61,9 @@ class StandardWebhookSender implements WebhookSenderInterface try { $signature = $signatureGenerator->generate($this->message); } catch (FireflyException $e) { - Log::error('Did not send message because of a Firefly III Exception.'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error('Did not send message because of a Firefly III Exception.'); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $attempt = new WebhookAttempt(); $attempt->webhookMessage()->associate($this->message); $attempt->status_code = 0; @@ -77,14 +76,14 @@ class StandardWebhookSender implements WebhookSenderInterface return; } - Log::debug(sprintf('Trying to send webhook message #%d', $this->message->id)); + app('log')->debug(sprintf('Trying to send webhook message #%d', $this->message->id)); try { $json = json_encode($this->message->message, JSON_THROW_ON_ERROR); } catch (JsonException $e) { - Log::error('Did not send message because of a JSON error.'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error('Did not send message because of a JSON error.'); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $attempt = new WebhookAttempt(); $attempt->webhookMessage()->associate($this->message); $attempt->status_code = 0; @@ -111,9 +110,9 @@ class StandardWebhookSender implements WebhookSenderInterface try { $res = $client->request('POST', $this->message->webhook->url, $options); } catch (RequestException | ConnectException $e) { - Log::error('The webhook could NOT be submitted but Firefly III caught the error below.'); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); + app('log')->error('The webhook could NOT be submitted but Firefly III caught the error below.'); + app('log')->error($e->getMessage()); + app('log')->error($e->getTraceAsString()); $logs = sprintf("%s\n%s", $e->getMessage(), $e->getTraceAsString()); @@ -127,9 +126,9 @@ class StandardWebhookSender implements WebhookSenderInterface $attempt->status_code = 0; if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse')) { $attempt->status_code = $e->hasResponse() ? $e->getResponse()->getStatusCode() : 0; - Log::error(sprintf('The status code of the error response is: %d', $attempt->status_code)); + app('log')->error(sprintf('The status code of the error response is: %d', $attempt->status_code)); $body = (string)($e->hasResponse() ? $e->getResponse()->getBody() : ''); - Log::error(sprintf('The body of the error response is: %s', $body)); + app('log')->error(sprintf('The body of the error response is: %s', $body)); } $attempt->logs = $logs; $attempt->save(); @@ -139,9 +138,9 @@ class StandardWebhookSender implements WebhookSenderInterface $this->message->sent = true; $this->message->save(); - Log::debug(sprintf('Webhook message #%d was sent. Status code %d', $this->message->id, $res->getStatusCode())); - Log::debug(sprintf('Webhook request body size: %d bytes', strlen($json))); - Log::debug(sprintf('Response body: %s', $res->getBody())); + app('log')->debug(sprintf('Webhook message #%d was sent. Status code %d', $this->message->id, $res->getStatusCode())); + app('log')->debug(sprintf('Webhook request body size: %d bytes', strlen($json))); + app('log')->debug(sprintf('Response body: %s', $res->getBody())); } /** diff --git a/app/Support/Amount.php b/app/Support/Amount.php index cef4fbb730..568387c61f 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -28,7 +28,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\UserGroup; use FireflyIII\User; -use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Support\Collection; use NumberFormatter; @@ -52,7 +51,7 @@ class Amount */ public function formatAnything(TransactionCurrency $format, string $amount, bool $coloured = null): string { - return $this->formatFlat($format->symbol, (int)$format->decimal_places, $amount, $coloured); + return $this->formatFlat($format->symbol, $format->decimal_places, $amount, $coloured); } /** @@ -72,13 +71,13 @@ class Amount { $locale = app('steam')->getLocale(); $rounded = app('steam')->bcround($amount, $decimalPlaces); - $coloured = $coloured ?? true; + $coloured ??= true; $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); $fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol); $fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); $fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); - $result = $fmt->format((float)$rounded); // intentional float + $result = (string)$fmt->format((float)$rounded); // intentional float if (true === $coloured) { if (1 === bccomp($rounded, '0')) { @@ -121,29 +120,11 @@ class Amount /** @var User $user */ $user = auth()->user(); - return $this->getDefaultCurrencyByUser($user); - } - - /** - * @param User $user - * @deprecated use getDefaultCurrencyByUserGroup instead. - * @return TransactionCurrency - */ - public function getDefaultCurrencyByUser(User $user): TransactionCurrency - { return $this->getDefaultCurrencyByUserGroup($user->userGroup); } /** - * @return TransactionCurrency - */ - public function getSystemCurrency(): TransactionCurrency - { - return TransactionCurrency::where('code', 'EUR')->first(); - } - - /** - * @param User $user + * @param UserGroup $userGroup * * @return TransactionCurrency */ @@ -158,6 +139,7 @@ class Amount $default = $userGroup->currencies()->where('group_default', true)->first(); if (null === $default) { $default = $this->getSystemCurrency(); + // could be the user group has no default right now. $userGroup->currencies()->sync([$default->id => ['group_default' => true]]); } $cache->store($default); @@ -165,6 +147,25 @@ class Amount return $default; } + /** + * @return TransactionCurrency + */ + public function getSystemCurrency(): TransactionCurrency + { + return TransactionCurrency::where('code', 'EUR')->first(); + } + + /** + * @param User $user + * + * @return TransactionCurrency + * @deprecated use getDefaultCurrencyByUserGroup instead. + */ + public function getDefaultCurrencyByUser(User $user): TransactionCurrency + { + return $this->getDefaultCurrencyByUserGroup($user->userGroup); + } + /** * This method returns the correct format rules required by accounting.js, * the library used to format amounts in charts. @@ -304,20 +305,4 @@ class Amount return $format; } - - /** - * @param string $value - * - * @return string - */ - private function tryDecrypt(string $value): string - { - try { - $value = Crypt::decrypt($value); // verified - } catch (DecryptException $e) { - // @ignoreException - } - - return $value; - } } diff --git a/app/Support/Authentication/RemoteUserGuard.php b/app/Support/Authentication/RemoteUserGuard.php index c5ae8198d1..a03662a869 100644 --- a/app/Support/Authentication/RemoteUserGuard.php +++ b/app/Support/Authentication/RemoteUserGuard.php @@ -31,7 +31,6 @@ use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Foundation\Application; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -40,9 +39,9 @@ use Psr\Container\NotFoundExceptionInterface; */ class RemoteUserGuard implements Guard { - protected Application $application; - protected $provider; - protected $user; + protected Application $application; + protected UserProvider $provider; + protected User | null $user; /** * Create a new authentication guard. @@ -55,9 +54,9 @@ class RemoteUserGuard implements Guard */ public function __construct(UserProvider $provider, Application $app) { - /** @var Request $request */ + /** @var Request|null $request */ $request = $app->get('request'); - Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri())); + app('log')->debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri())); $this->application = $app; $this->provider = $provider; $this->user = null; @@ -68,9 +67,9 @@ class RemoteUserGuard implements Guard */ public function authenticate(): void { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); if (null !== $this->user) { - Log::debug(sprintf('%s is found: #%d, "%s".', get_class($this->user), $this->user->id, $this->user->email)); + app('log')->debug(sprintf('%s is found: #%d, "%s".', get_class($this->user), $this->user->id, $this->user->email)); return; } @@ -79,16 +78,16 @@ class RemoteUserGuard implements Guard $userID = request()->server($header) ?? null; if (function_exists('apache_request_headers')) { - Log::debug('Use apache_request_headers to find user ID.'); + app('log')->debug('Use apache_request_headers to find user ID.'); $userID = request()->server($header) ?? apache_request_headers()[$header] ?? null; } if (null === $userID || '' === $userID) { - Log::error(sprintf('No user in header "%s".', $header)); + app('log')->error(sprintf('No user in header "%s".', $header)); throw new FireflyException('The guard header was unexpectedly empty. See the logs.'); } - Log::debug(sprintf('User ID found in header is "%s"', $userID)); + app('log')->debug(sprintf('User ID found in header is "%s"', $userID)); /** @var User $retrievedUser */ $retrievedUser = $this->provider->retrieveById($userID); @@ -109,7 +108,7 @@ class RemoteUserGuard implements Guard } } - Log::debug(sprintf('Result of getting user from provider: %s', $retrievedUser->email)); + app('log')->debug(sprintf('Result of getting user from provider: %s', $retrievedUser->email)); $this->user = $retrievedUser; } @@ -118,7 +117,7 @@ class RemoteUserGuard implements Guard */ public function guest(): bool { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); return !$this->check(); } @@ -127,8 +126,8 @@ class RemoteUserGuard implements Guard */ public function check(): bool { - Log::debug(sprintf('Now at %s', __METHOD__)); - return !is_null($this->user()); + app('log')->debug(sprintf('Now at %s', __METHOD__)); + return null !== $this->user(); } /** @@ -136,10 +135,10 @@ class RemoteUserGuard implements Guard */ public function user(): ?User { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); $user = $this->user; if (null === $user) { - Log::debug('User is NULL'); + app('log')->debug('User is NULL'); return null; } @@ -151,26 +150,31 @@ class RemoteUserGuard implements Guard */ public function hasUser(): bool { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); throw new FireflyException('Did not implement RemoteUserGuard::hasUser()'); } /** * @inheritDoc + * @SuppressWarnings(PHPMD.ShortMethodName) */ - public function id(): ?User + public function id(): int | string | null { - Log::debug(sprintf('Now at %s', __METHOD__)); - return $this->user; + app('log')->debug(sprintf('Now at %s', __METHOD__)); + return $this->user?->id; } /** * @inheritDoc */ - public function setUser(Authenticatable $user) + public function setUser(Authenticatable | User | null $user) { - Log::debug(sprintf('Now at %s', __METHOD__)); - $this->user = $user; + app('log')->debug(sprintf('Now at %s', __METHOD__)); + if ($user instanceof User) { + $this->user = $user; + return; + } + app('log')->error(sprintf('Did not set user at %s', __METHOD__)); } /** @@ -178,7 +182,7 @@ class RemoteUserGuard implements Guard */ public function validate(array $credentials = []) { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); throw new FireflyException('Did not implement RemoteUserGuard::validate()'); } @@ -187,7 +191,7 @@ class RemoteUserGuard implements Guard */ public function viaRemember(): bool { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); return false; } } diff --git a/app/Support/Authentication/RemoteUserProvider.php b/app/Support/Authentication/RemoteUserProvider.php index 73499a7679..a7ef0938b1 100644 --- a/app/Support/Authentication/RemoteUserProvider.php +++ b/app/Support/Authentication/RemoteUserProvider.php @@ -30,7 +30,6 @@ use FireflyIII\Models\Role; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\UserProvider; -use Illuminate\Support\Facades\Log; use Str; /** @@ -43,7 +42,7 @@ class RemoteUserProvider implements UserProvider */ public function retrieveByCredentials(array $credentials) { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); throw new FireflyException(sprintf('Did not implement %s', __METHOD__)); } @@ -52,10 +51,10 @@ class RemoteUserProvider implements UserProvider */ public function retrieveById($identifier): User { - Log::debug(sprintf('Now at %s(%s)', __METHOD__, $identifier)); + app('log')->debug(sprintf('Now at %s(%s)', __METHOD__, $identifier)); $user = User::where('email', $identifier)->first(); if (null === $user) { - Log::debug(sprintf('User with email "%s" not found. Will be created.', $identifier)); + app('log')->debug(sprintf('User with email "%s" not found. Will be created.', $identifier)); $user = User::create( [ 'blocked' => false, @@ -72,7 +71,7 @@ class RemoteUserProvider implements UserProvider // make sure the user gets an administration as well. CreateGroupMemberships::createGroupMembership($user); } - Log::debug(sprintf('Going to return user #%d (%s)', $user->id, $user->email)); + app('log')->debug(sprintf('Going to return user #%d (%s)', $user->id, $user->email)); return $user; } @@ -82,7 +81,7 @@ class RemoteUserProvider implements UserProvider */ public function retrieveByToken($identifier, $token) { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); throw new FireflyException(sprintf('A) Did not implement %s', __METHOD__)); } @@ -91,7 +90,7 @@ class RemoteUserProvider implements UserProvider */ public function updateRememberToken(Authenticatable $user, $token) { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); throw new FireflyException(sprintf('B) Did not implement %s', __METHOD__)); } @@ -100,7 +99,7 @@ class RemoteUserProvider implements UserProvider */ public function validateCredentials(Authenticatable $user, array $credentials) { - Log::debug(sprintf('Now at %s', __METHOD__)); + app('log')->debug(sprintf('Now at %s', __METHOD__)); throw new FireflyException(sprintf('C) Did not implement %s', __METHOD__)); } } diff --git a/app/Support/Binder/AccountList.php b/app/Support/Binder/AccountList.php index 728f77f402..3859b5be1a 100644 --- a/app/Support/Binder/AccountList.php +++ b/app/Support/Binder/AccountList.php @@ -26,7 +26,6 @@ namespace FireflyIII\Support\Binder; use FireflyIII\Models\AccountType; use Illuminate\Routing\Route; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -69,7 +68,7 @@ class AccountList implements BinderInterface return $collection; } } - Log::error(sprintf('Trying to show account list (%s), but user is not logged in or list is empty.', $route->uri)); + app('log')->error(sprintf('Trying to show account list (%s), but user is not logged in or list is empty.', $route->uri)); throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/BudgetList.php b/app/Support/Binder/BudgetList.php index 8545171385..df1130b80d 100644 --- a/app/Support/Binder/BudgetList.php +++ b/app/Support/Binder/BudgetList.php @@ -54,7 +54,7 @@ class BudgetList implements BinderInterface $list = array_unique(array_map('\intval', explode(',', $value))); - if (0 === count($list)) { + if (0 === count($list)) { // @phpstan-ignore-line app('log')->warning('Budget list count is zero, return 404.'); throw new NotFoundHttpException(); } diff --git a/app/Support/Binder/CLIToken.php b/app/Support/Binder/CLIToken.php index 207224fb3c..fcb1889880 100644 --- a/app/Support/Binder/CLIToken.php +++ b/app/Support/Binder/CLIToken.php @@ -26,7 +26,6 @@ namespace FireflyIII\Support\Binder; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Routing\Route; -use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -48,19 +47,19 @@ class CLIToken implements BinderInterface $users = $repository->all(); // check for static token - if ($value === config('firefly.static_cron_token') && 32 === strlen((string)config('firefly.static_cron_token'))) { + if ($value === config('firefly.static_cron_token') && 32 === strlen(config('firefly.static_cron_token'))) { return $value; } foreach ($users as $user) { $accessToken = app('preferences')->getForUser($user, 'access_token'); if (null !== $accessToken && $accessToken->data === $value) { - Log::info(sprintf('Recognized user #%d (%s) from his acccess token.', $user->id, $user->email)); + app('log')->info(sprintf('Recognized user #%d (%s) from his access token.', $user->id, $user->email)); return $value; } } - Log::error(sprintf('Recognized no users by access token "%s"', $value)); + app('log')->error(sprintf('Recognized no users by access token "%s"', $value)); throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/CategoryList.php b/app/Support/Binder/CategoryList.php index f140cc7607..8722688763 100644 --- a/app/Support/Binder/CategoryList.php +++ b/app/Support/Binder/CategoryList.php @@ -51,7 +51,7 @@ class CategoryList implements BinderInterface } $list = array_unique(array_map('\intval', explode(',', $value))); - if (0 === count($list)) { + if (0 === count($list)) { // @phpstan-ignore-line throw new NotFoundHttpException(); } diff --git a/app/Support/Binder/Date.php b/app/Support/Binder/Date.php index 8ba226fbb5..11f65ee027 100644 --- a/app/Support/Binder/Date.php +++ b/app/Support/Binder/Date.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; use FireflyIII\Helpers\Fiscal\FiscalHelperInterface; use Illuminate\Routing\Route; -use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -65,16 +64,16 @@ class Date implements BinderInterface ]; if (array_key_exists($value, $magicWords)) { $return = $magicWords[$value]; - Log::debug(sprintf('User requests "%s", so will return "%s"', $value, $return)); + app('log')->debug(sprintf('User requests "%s", so will return "%s"', $value, $return)); return $return; } try { $result = new Carbon($value); - } catch (InvalidDateException $e) { + } catch (InvalidDateException $e) { // @phpstan-ignore-line $message = sprintf('Could not parse date "%s" for user #%d: %s', $value, auth()->user()->id, $e->getMessage()); - Log::error($message); + app('log')->error($message); throw new NotFoundHttpException($message, $e); } diff --git a/app/Support/Binder/DynamicConfigKey.php b/app/Support/Binder/DynamicConfigKey.php index e3fb1c608d..90a1b7bf65 100644 --- a/app/Support/Binder/DynamicConfigKey.php +++ b/app/Support/Binder/DynamicConfigKey.php @@ -45,6 +45,7 @@ class DynamicConfigKey * * @return string * @throws NotFoundHttpException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public static function routeBinder(string $value, Route $route): string { diff --git a/app/Support/Binder/EitherConfigKey.php b/app/Support/Binder/EitherConfigKey.php index f41db15cbf..cc62184c68 100644 --- a/app/Support/Binder/EitherConfigKey.php +++ b/app/Support/Binder/EitherConfigKey.php @@ -62,6 +62,8 @@ class EitherConfigKey * * @return string * @throws NotFoundHttpException + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public static function routeBinder(string $value, Route $route): string { diff --git a/app/Support/Binder/JournalList.php b/app/Support/Binder/JournalList.php index a65f91df28..b2594dbb87 100644 --- a/app/Support/Binder/JournalList.php +++ b/app/Support/Binder/JournalList.php @@ -70,7 +70,7 @@ class JournalList implements BinderInterface protected static function parseList(string $value): array { $list = array_unique(array_map('\intval', explode(',', $value))); - if (0 === count($list)) { + if (0 === count($list)) { // @phpstan-ignore-line throw new NotFoundHttpException(); } diff --git a/app/Support/Binder/TagList.php b/app/Support/Binder/TagList.php index 4d1e92904e..d2c2988883 100644 --- a/app/Support/Binder/TagList.php +++ b/app/Support/Binder/TagList.php @@ -27,7 +27,6 @@ use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Routing\Route; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -51,10 +50,10 @@ class TagList implements BinderInterface ->get(); } $list = array_unique(array_map('\strtolower', explode(',', $value))); - Log::debug('List of tags is', $list); + app('log')->debug('List of tags is', $list); - if (0 === count($list)) { - Log::error('Tag list is empty.'); + if (0 === count($list)) { // @phpstan-ignore-line + app('log')->error('Tag list is empty.'); throw new NotFoundHttpException(); } @@ -81,7 +80,7 @@ class TagList implements BinderInterface return $collection; } } - Log::error('TagList: user is not logged in.'); + app('log')->error('TagList: user is not logged in.'); throw new NotFoundHttpException(); } } diff --git a/app/Support/Binder/TagOrId.php b/app/Support/Binder/TagOrId.php index 8c79ec13a2..d02fb49812 100644 --- a/app/Support/Binder/TagOrId.php +++ b/app/Support/Binder/TagOrId.php @@ -26,7 +26,6 @@ namespace FireflyIII\Support\Binder; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Routing\Route; -use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -54,10 +53,10 @@ class TagOrId implements BinderInterface if (null !== $result) { return $result; } - Log::error('TagOrId: tag not found.'); + app('log')->error('TagOrId: tag not found.'); throw new NotFoundHttpException(); } - Log::error('TagOrId: user is not logged in.'); + app('log')->error('TagOrId: user is not logged in.'); throw new NotFoundHttpException(); } } diff --git a/app/Support/Calendar/Calculator.php b/app/Support/Calendar/Calculator.php index b09ba58c59..7981470b19 100644 --- a/app/Support/Calendar/Calculator.php +++ b/app/Support/Calendar/Calculator.php @@ -26,7 +26,7 @@ declare(strict_types=1); namespace FireflyIII\Support\Calendar; use Carbon\Carbon; -use FireflyIII\Support\Calendar\Exceptions\IntervalException; +use FireflyIII\Exceptions\IntervalException; use SplObjectStorage; /** @@ -34,7 +34,7 @@ use SplObjectStorage; */ class Calculator { - public const DEFAULT_INTERVAL = 1; + public const int DEFAULT_INTERVAL = 1; private static ?SplObjectStorage $intervalMap = null; private static array $intervals = []; @@ -83,7 +83,7 @@ class Calculator */ private static function loadIntervalMap(): SplObjectStorage { - if (self::$intervalMap != null) { + if (self::$intervalMap !== null) { return self::$intervalMap; } self::$intervalMap = new SplObjectStorage(); diff --git a/app/Support/Calendar/Periodicity/Bimonthly.php b/app/Support/Calendar/Periodicity/Bimonthly.php index 849e7b19c4..75e2de620a 100644 --- a/app/Support/Calendar/Periodicity/Bimonthly.php +++ b/app/Support/Calendar/Periodicity/Bimonthly.php @@ -30,5 +30,5 @@ namespace FireflyIII\Support\Calendar\Periodicity; */ final class Bimonthly extends Monthly { - public const INTERVAL = 2; + public const int INTERVAL = 2; } diff --git a/app/Support/Calendar/Periodicity/Fortnightly.php b/app/Support/Calendar/Periodicity/Fortnightly.php index 680d9d2341..1647092a59 100644 --- a/app/Support/Calendar/Periodicity/Fortnightly.php +++ b/app/Support/Calendar/Periodicity/Fortnightly.php @@ -30,5 +30,5 @@ namespace FireflyIII\Support\Calendar\Periodicity; */ final class Fortnightly extends Weekly { - public const INTERVAL = 2; + public const int INTERVAL = 2; } diff --git a/app/Support/Calendar/Periodicity/HalfYearly.php b/app/Support/Calendar/Periodicity/HalfYearly.php index 5e34b92a93..b19a803b02 100644 --- a/app/Support/Calendar/Periodicity/HalfYearly.php +++ b/app/Support/Calendar/Periodicity/HalfYearly.php @@ -30,5 +30,5 @@ namespace FireflyIII\Support\Calendar\Periodicity; */ final class HalfYearly extends Monthly { - public const INTERVAL = 6; + public const int INTERVAL = 6; } diff --git a/app/Support/Calendar/Periodicity/Interval.php b/app/Support/Calendar/Periodicity/Interval.php index c3a3a06805..62269fb7b5 100644 --- a/app/Support/Calendar/Periodicity/Interval.php +++ b/app/Support/Calendar/Periodicity/Interval.php @@ -30,14 +30,14 @@ namespace FireflyIII\Support\Calendar\Periodicity; */ abstract class Interval implements Interspacable { - public const INTERVAL = 1; + public const int INTERVAL = 1; /** * @param int $skip * * @return int */ - public function skip(int $skip): int + final public function skip(int $skip): int { return static::INTERVAL * $skip; } diff --git a/app/Support/Calendar/Periodicity/Quarterly.php b/app/Support/Calendar/Periodicity/Quarterly.php index 839a7131f6..8c958f33a6 100644 --- a/app/Support/Calendar/Periodicity/Quarterly.php +++ b/app/Support/Calendar/Periodicity/Quarterly.php @@ -30,5 +30,5 @@ namespace FireflyIII\Support\Calendar\Periodicity; */ final class Quarterly extends Monthly { - public const INTERVAL = 3; + public const int INTERVAL = 3; } diff --git a/app/Support/Chart/Budget/FrontpageChartGenerator.php b/app/Support/Chart/Budget/FrontpageChartGenerator.php index 086092da0d..3d681750fc 100644 --- a/app/Support/Chart/Budget/FrontpageChartGenerator.php +++ b/app/Support/Chart/Budget/FrontpageChartGenerator.php @@ -159,7 +159,7 @@ class FrontpageChartGenerator /** @var array $entry */ foreach ($spent as $entry) { // only spent the entry where the entry's currency matches the budget limit's currency - if ($entry['currency_id'] === (int)$limit->transaction_currency_id) { + if ($entry['currency_id'] === $limit->transaction_currency_id) { $data = $this->processRow($data, $budget, $limit, $entry); } } diff --git a/app/Support/Chart/Category/FrontpageChartGenerator.php b/app/Support/Chart/Category/FrontpageChartGenerator.php index c708797692..1bdae34f96 100644 --- a/app/Support/Chart/Category/FrontpageChartGenerator.php +++ b/app/Support/Chart/Category/FrontpageChartGenerator.php @@ -128,7 +128,7 @@ class FrontpageChartGenerator { $currencyId = (int)$currency['currency_id']; - $this->currencies[$currencyId] = $this->currencies[$currencyId] ?? [ + $this->currencies[$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_name' => $currency['currency_name'], 'currency_symbol' => $currency['currency_symbol'], diff --git a/app/Support/Chart/Category/WholePeriodChartGenerator.php b/app/Support/Chart/Category/WholePeriodChartGenerator.php index 808750a97a..9c4b8ef915 100644 --- a/app/Support/Chart/Category/WholePeriodChartGenerator.php +++ b/app/Support/Chart/Category/WholePeriodChartGenerator.php @@ -152,7 +152,7 @@ class WholePeriodChartGenerator $return = []; foreach ($array as $block) { foreach ($block as $currencyId => $currencyRow) { - $return[$currencyId] = $return[$currencyId] ?? [ + $return[$currencyId] ??= [ 'currency_id' => $currencyId, 'currency_name' => $currencyRow['currency_name'], 'currency_symbol' => $currencyRow['currency_symbol'], diff --git a/app/Support/Cronjobs/AutoBudgetCronjob.php b/app/Support/Cronjobs/AutoBudgetCronjob.php index bc540fa8b2..6008e834c8 100644 --- a/app/Support/Cronjobs/AutoBudgetCronjob.php +++ b/app/Support/Cronjobs/AutoBudgetCronjob.php @@ -27,7 +27,6 @@ namespace FireflyIII\Support\Cronjobs; use Carbon\Carbon; use FireflyIII\Jobs\CreateAutoBudgetLimits; use FireflyIII\Models\Configuration; -use Illuminate\Support\Facades\Log; /** * Class AutoBudgetCronjob @@ -45,26 +44,22 @@ class AutoBudgetCronjob extends AbstractCronjob $diff = time() - $lastTime; $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { - Log::info('Auto budget cron-job has never fired before.'); + app('log')->info('Auto budget cron-job has never fired before.'); } // less than half a day ago: if ($lastTime > 0 && $diff <= 43200) { - Log::info(sprintf('It has been %s since the auto budget cron-job has fired.', $diffForHumans)); + app('log')->info(sprintf('It has been %s since the auto budget cron-job has fired.', $diffForHumans)); if (false === $this->force) { - Log::info('The auto budget cron-job will not fire now.'); + app('log')->info('The auto budget cron-job will not fire now.'); $this->message = sprintf('It has been %s since the auto budget cron-job has fired. It will not fire now.', $diffForHumans); return; } - - // fire job regardless. - if (true === $this->force) { - Log::info('Execution of the auto budget cron-job has been FORCED.'); - } + app('log')->info('Execution of the auto budget cron-job has been FORCED.'); } if ($lastTime > 0 && $diff > 43200) { - Log::info(sprintf('It has been %s since the auto budget cron-job has fired. It will fire now!', $diffForHumans)); + app('log')->info(sprintf('It has been %s since the auto budget cron-job has fired. It will fire now!', $diffForHumans)); } $this->fireAutoBudget(); @@ -76,7 +71,7 @@ class AutoBudgetCronjob extends AbstractCronjob */ private function fireAutoBudget(): void { - Log::info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d'))); + app('log')->info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d'))); /** @var CreateAutoBudgetLimits $job */ $job = app(CreateAutoBudgetLimits::class, [$this->date]); $job->setDate($this->date); @@ -89,6 +84,6 @@ class AutoBudgetCronjob extends AbstractCronjob $this->message = 'Auto-budget cron job fired successfully.'; app('fireflyconfig')->set('last_ab_job', (int)$this->date->format('U')); - Log::info('Done with auto budget cron job task.'); + app('log')->info('Done with auto budget cron job task.'); } } diff --git a/app/Support/Cronjobs/BillWarningCronjob.php b/app/Support/Cronjobs/BillWarningCronjob.php index 2b3c03f512..2e290c18f7 100644 --- a/app/Support/Cronjobs/BillWarningCronjob.php +++ b/app/Support/Cronjobs/BillWarningCronjob.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Jobs\WarnAboutBills; use FireflyIII\Models\Configuration; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -43,7 +42,7 @@ class BillWarningCronjob extends AbstractCronjob */ public function fire(): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); /** @var Configuration $config */ $config = app('fireflyconfig')->get('last_bw_job', 0); $lastTime = (int)$config->data; @@ -51,13 +50,13 @@ class BillWarningCronjob extends AbstractCronjob $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { - Log::info('The bill warning cron-job has never fired before.'); + app('log')->info('The bill warning cron-job has never fired before.'); } // less than half a day ago: if ($lastTime > 0 && $diff <= 43200) { - Log::info(sprintf('It has been %s since the bill warning cron-job has fired.', $diffForHumans)); + app('log')->info(sprintf('It has been %s since the bill warning cron-job has fired.', $diffForHumans)); if (false === $this->force) { - Log::info('The cron-job will not fire now.'); + app('log')->info('The cron-job will not fire now.'); $this->message = sprintf('It has been %s since the bill warning cron-job has fired. It will not fire now.', $diffForHumans); $this->jobFired = false; $this->jobErrored = false; @@ -66,14 +65,11 @@ class BillWarningCronjob extends AbstractCronjob return; } - // fire job regardless. - if (true === $this->force) { - Log::info('Execution of the bill warning cron-job has been FORCED.'); - } + app('log')->info('Execution of the bill warning cron-job has been FORCED.'); } if ($lastTime > 0 && $diff > 43200) { - Log::info(sprintf('It has been %s since the bill warning cron-job has fired. It will fire now!', $diffForHumans)); + app('log')->info(sprintf('It has been %s since the bill warning cron-job has fired. It will fire now!', $diffForHumans)); } $this->fireWarnings(); @@ -86,7 +82,7 @@ class BillWarningCronjob extends AbstractCronjob */ private function fireWarnings(): void { - Log::info(sprintf('Will now fire bill warning job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); + app('log')->info(sprintf('Will now fire bill warning job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); /** @var WarnAboutBills $job */ $job = app(WarnAboutBills::class); $job->setDate($this->date); @@ -100,7 +96,7 @@ class BillWarningCronjob extends AbstractCronjob $this->message = 'Bill warning cron job fired successfully.'; app('fireflyconfig')->set('last_bw_job', (int)$this->date->format('U')); - Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int)$this->date->format('U'))); - Log::info('Done with bill warning cron job task.'); + app('log')->info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int)$this->date->format('U'))); + app('log')->info('Done with bill warning cron job task.'); } } diff --git a/app/Support/Cronjobs/ExchangeRatesCronjob.php b/app/Support/Cronjobs/ExchangeRatesCronjob.php index c76131777a..9793078677 100644 --- a/app/Support/Cronjobs/ExchangeRatesCronjob.php +++ b/app/Support/Cronjobs/ExchangeRatesCronjob.php @@ -27,7 +27,6 @@ namespace FireflyIII\Support\Cronjobs; use Carbon\Carbon; use FireflyIII\Jobs\DownloadExchangeRates; use FireflyIII\Models\Configuration; -use Illuminate\Support\Facades\Log; /** * Class ExchangeRatesCronjob @@ -45,26 +44,23 @@ class ExchangeRatesCronjob extends AbstractCronjob $diff = time() - $lastTime; $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { - Log::info('Exchange rates cron-job has never fired before.'); + app('log')->info('Exchange rates cron-job has never fired before.'); } // less than half a day ago: if ($lastTime > 0 && $diff <= 43200) { - Log::info(sprintf('It has been %s since the exchange rates cron-job has fired.', $diffForHumans)); + app('log')->info(sprintf('It has been %s since the exchange rates cron-job has fired.', $diffForHumans)); if (false === $this->force) { - Log::info('The exchange rates cron-job will not fire now.'); + app('log')->info('The exchange rates cron-job will not fire now.'); $this->message = sprintf('It has been %s since the exchange rates cron-job has fired. It will not fire now.', $diffForHumans); return; } - // fire job regardless. - if (true === $this->force) { - Log::info('Execution of the exchange rates cron-job has been FORCED.'); - } + app('log')->info('Execution of the exchange rates cron-job has been FORCED.'); } if ($lastTime > 0 && $diff > 43200) { - Log::info(sprintf('It has been %s since the exchange rates cron-job has fired. It will fire now!', $diffForHumans)); + app('log')->info(sprintf('It has been %s since the exchange rates cron-job has fired. It will fire now!', $diffForHumans)); } $this->fireExchangeRateJob(); @@ -76,7 +72,7 @@ class ExchangeRatesCronjob extends AbstractCronjob */ private function fireExchangeRateJob(): void { - Log::info(sprintf('Will now fire exchange rates cron job task for date "%s".', $this->date->format('Y-m-d'))); + app('log')->info(sprintf('Will now fire exchange rates cron job task for date "%s".', $this->date->format('Y-m-d'))); /** @var DownloadExchangeRates $job */ $job = app(DownloadExchangeRates::class); $job->setDate($this->date); @@ -89,6 +85,6 @@ class ExchangeRatesCronjob extends AbstractCronjob $this->message = 'Exchange rates cron job fired successfully.'; app('fireflyconfig')->set('last_cer_job', (int)$this->date->format('U')); - Log::info('Done with exchange rates job task.'); + app('log')->info('Done with exchange rates job task.'); } } diff --git a/app/Support/Cronjobs/RecurringCronjob.php b/app/Support/Cronjobs/RecurringCronjob.php index cdb2b989ff..3388398140 100644 --- a/app/Support/Cronjobs/RecurringCronjob.php +++ b/app/Support/Cronjobs/RecurringCronjob.php @@ -27,7 +27,6 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Jobs\CreateRecurringTransactions; use FireflyIII\Models\Configuration; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -43,7 +42,7 @@ class RecurringCronjob extends AbstractCronjob */ public function fire(): void { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); /** @var Configuration $config */ $config = app('fireflyconfig')->get('last_rt_job', 0); $lastTime = (int)$config->data; @@ -51,13 +50,13 @@ class RecurringCronjob extends AbstractCronjob $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); if (0 === $lastTime) { - Log::info('Recurring transactions cron-job has never fired before.'); + app('log')->info('Recurring transactions cron-job has never fired before.'); } // less than half a day ago: if ($lastTime > 0 && $diff <= 43200) { - Log::info(sprintf('It has been %s since the recurring transactions cron-job has fired.', $diffForHumans)); + app('log')->info(sprintf('It has been %s since the recurring transactions cron-job has fired.', $diffForHumans)); if (false === $this->force) { - Log::info('The cron-job will not fire now.'); + app('log')->info('The cron-job will not fire now.'); $this->message = sprintf('It has been %s since the recurring transactions cron-job has fired. It will not fire now.', $diffForHumans); $this->jobFired = false; $this->jobErrored = false; @@ -65,15 +64,11 @@ class RecurringCronjob extends AbstractCronjob return; } - - // fire job regardless. - if (true === $this->force) { - Log::info('Execution of the recurring transaction cron-job has been FORCED.'); - } + app('log')->info('Execution of the recurring transaction cron-job has been FORCED.'); } if ($lastTime > 0 && $diff > 43200) { - Log::info(sprintf('It has been %s since the recurring transactions cron-job has fired. It will fire now!', $diffForHumans)); + app('log')->info(sprintf('It has been %s since the recurring transactions cron-job has fired. It will fire now!', $diffForHumans)); } $this->fireRecurring(); @@ -86,7 +81,7 @@ class RecurringCronjob extends AbstractCronjob */ private function fireRecurring(): void { - Log::info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); + app('log')->info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); /** @var CreateRecurringTransactions $job */ $job = app(CreateRecurringTransactions::class); $job->setDate($this->date); @@ -100,7 +95,7 @@ class RecurringCronjob extends AbstractCronjob $this->message = 'Recurring transactions cron job fired successfully.'; app('fireflyconfig')->set('last_rt_job', (int)$this->date->format('U')); - Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int)$this->date->format('U'))); - Log::info('Done with recurring cron job task.'); + app('log')->info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int)$this->date->format('U'))); + app('log')->info('Done with recurring cron job task.'); } } diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index fc78951bea..222d4de291 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -27,7 +27,6 @@ use Eloquent; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Support\Form\FormSupport; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -50,7 +49,7 @@ class ExpandedForm */ public function amountNoCurrency(string $name, $value = null, array $options = null): string { - $options = $options ?? []; + $options ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -65,7 +64,7 @@ class ExpandedForm try { $html = view('form.amount-no-currency', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render amountNoCurrency(): %s', $e->getMessage())); + app('log')->error(sprintf('Could not render amountNoCurrency(): %s', $e->getMessage())); $html = 'Could not render amountNoCurrency.'; throw new FireflyException($html, 0, $e); } @@ -84,8 +83,8 @@ class ExpandedForm */ public function checkbox(string $name, int $value = null, $checked = null, array $options = null): string { - $options = $options ?? []; - $value = $value ?? 1; + $options ??= []; + $value ??= 1; $options['checked'] = true === $checked; if (app('session')->has('preFilled')) { @@ -102,7 +101,7 @@ class ExpandedForm try { $html = view('form.checkbox', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render checkbox(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render checkbox(): %s', $e->getMessage())); $html = 'Could not render checkbox.'; throw new FireflyException($html, 0, $e); } @@ -128,7 +127,7 @@ class ExpandedForm try { $html = view('form.date', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render date(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render date(): %s', $e->getMessage())); $html = 'Could not render date.'; throw new FireflyException($html, 0, $e); } @@ -145,14 +144,14 @@ class ExpandedForm */ public function file(string $name, array $options = null): string { - $options = $options ?? []; + $options ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); try { $html = view('form.file', compact('classes', 'name', 'label', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render file(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render file(): %s', $e->getMessage())); $html = 'Could not render file.'; throw new FireflyException($html, 0, $e); } @@ -170,16 +169,16 @@ class ExpandedForm */ public function integer(string $name, $value = null, array $options = null): string { - $options = $options ?? []; + $options ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); - $options['step'] = $options['step'] ?? '1'; + $options['step'] ??= '1'; try { $html = view('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render integer(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render integer(): %s', $e->getMessage())); $html = 'Could not render integer.'; throw new FireflyException($html, 0, $e); } @@ -197,7 +196,7 @@ class ExpandedForm */ public function location(string $name, $value = null, array $options = null): string { - $options = $options ?? []; + $options ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -205,7 +204,7 @@ class ExpandedForm try { $html = view('form.location', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render location(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render location(): %s', $e->getMessage())); $html = 'Could not render location.'; throw new FireflyException($html, 0, $e); } @@ -227,7 +226,7 @@ class ExpandedForm /** @var Eloquent $entry */ foreach ($set as $entry) { // All Eloquent models have an ID - $entryId = (int)$entry->id; // @phpstan-ignore-line + $entryId = $entry->id; // @phpstan-ignore-line $current = $entry->toArray(); $title = null; foreach ($fields as $field) { @@ -265,7 +264,7 @@ class ExpandedForm try { $html = view('form.object_group', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render objectGroup(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render objectGroup(): %s', $e->getMessage())); $html = 'Could not render objectGroup.'; throw new FireflyException($html, 0, $e); } @@ -285,7 +284,7 @@ class ExpandedForm try { $html = view('form.options', compact('type', 'name'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render select(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage())); $html = 'Could not render optionsList.'; throw new FireflyException($html, 0, $e); } @@ -308,7 +307,7 @@ class ExpandedForm try { $html = view('form.password', compact('classes', 'name', 'label', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render password(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render password(): %s', $e->getMessage())); $html = 'Could not render password.'; throw new FireflyException($html, 0, $e); } @@ -337,7 +336,7 @@ class ExpandedForm try { $html = view('form.percentage', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render percentage(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render percentage(): %s', $e->getMessage())); $html = 'Could not render percentage.'; throw new FireflyException($html, 0, $e); } @@ -361,7 +360,7 @@ class ExpandedForm try { $html = view('form.static', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render staticText(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render staticText(): %s', $e->getMessage())); $html = 'Could not render staticText.'; throw new FireflyException($html, 0, $e); } @@ -386,7 +385,7 @@ class ExpandedForm try { $html = view('form.text', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render text(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render text(): %s', $e->getMessage())); $html = 'Could not render text.'; throw new FireflyException($html, 0, $e); } @@ -417,7 +416,7 @@ class ExpandedForm try { $html = view('form.textarea', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render textarea(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render textarea(): %s', $e->getMessage())); $html = 'Could not render textarea.'; throw new FireflyException($html, 0, $e); } diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index 393ec30f08..7e740e07f7 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -53,7 +53,6 @@ use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\User; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use League\Csv\CannotInsertRecord; use League\Csv\Exception; use League\Csv\Writer; @@ -67,8 +66,8 @@ class ExportDataGenerator { use ConvertsDataTypes; - private const ADD_RECORD_ERR = 'Could not add record to set: %s'; - private const EXPORT_ERR = 'Could not export to string: %s'; + private const string ADD_RECORD_ERR = 'Could not add record to set: %s'; + private const string EXPORT_ERR = 'Could not export to string: %s'; private Collection $accounts; private Carbon $end; private bool $exportAccounts; @@ -212,7 +211,7 @@ class ExportDataGenerator try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -289,7 +288,7 @@ class ExportDataGenerator try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -356,7 +355,7 @@ class ExportDataGenerator try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -407,7 +406,7 @@ class ExportDataGenerator try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -487,7 +486,7 @@ class ExportDataGenerator try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -651,7 +650,7 @@ class ExportDataGenerator try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -782,7 +781,7 @@ class ExportDataGenerator try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -837,7 +836,7 @@ class ExportDataGenerator try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } @@ -936,7 +935,7 @@ class ExportDataGenerator $journal['budget_name'], $journal['bill_name'], $this->mergeTags($journal['tags']), - $this->clearString($journal['notes'], true), + $this->clearStringKeepNewlines($journal['notes']), // export also the optional fields (ALL) @@ -990,7 +989,7 @@ class ExportDataGenerator try { $string = $csv->toString(); } catch (Exception $e) { // intentional generic exception - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); throw new FireflyException(sprintf(self::EXPORT_ERR, $e->getMessage()), 0, $e); } diff --git a/app/Support/FireflyConfig.php b/app/Support/FireflyConfig.php index 86dcd29dbf..5f5e7026a7 100644 --- a/app/Support/FireflyConfig.php +++ b/app/Support/FireflyConfig.php @@ -28,7 +28,6 @@ use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Configuration; use Illuminate\Database\QueryException; -use Illuminate\Support\Facades\Log; /** * Class FireflyConfig. @@ -104,7 +103,7 @@ class FireflyConfig try { $config = Configuration::whereName($name)->whereNull('deleted_at')->first(); } catch (QueryException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); $item = new Configuration(); $item->name = $name; $item->data = $value; @@ -137,7 +136,7 @@ class FireflyConfig public function getFresh(string $name, $default = null): ?Configuration { $config = Configuration::where('name', $name)->first(['id', 'name', 'data']); - if ($config) { + if (null !== $config) { return $config; } // no preference found and default is null: diff --git a/app/Support/Form/AccountForm.php b/app/Support/Form/AccountForm.php index 0710bb6967..52c169f273 100644 --- a/app/Support/Form/AccountForm.php +++ b/app/Support/Form/AccountForm.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -133,7 +132,7 @@ class AccountForm */ public function assetAccountCheckList(string $name, array $options = null): string { - $options = $options ?? []; + $options ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -147,7 +146,7 @@ class AccountForm try { $html = view('form.assetAccountCheckList', compact('classes', 'selected', 'name', 'label', 'options', 'grouped'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render assetAccountCheckList(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render assetAccountCheckList(): %s', $e->getMessage())); $html = 'Could not render assetAccountCheckList.'; throw new FireflyException($html, 0, $e); } diff --git a/app/Support/Form/CurrencyForm.php b/app/Support/Form/CurrencyForm.php index 0fc4487edd..376dbf1bfa 100644 --- a/app/Support/Form/CurrencyForm.php +++ b/app/Support/Form/CurrencyForm.php @@ -23,12 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Support\Form; -use Amount as Amt; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -69,11 +67,10 @@ class CurrencyForm $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); $options['step'] = 'any'; - $defaultCurrency = $options['currency'] ?? Amt::getDefaultCurrency(); + $defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency(); /** @var Collection $currencies */ $currencies = app('amount')->getCurrencies(); unset($options['currency'], $options['placeholder']); - // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) $preFilled = session('preFilled'); if (!is_array($preFilled)) { @@ -82,13 +79,13 @@ class CurrencyForm $key = 'amount_currency_id_' . $name; $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $defaultCurrency->id; - Log::debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); + app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); // find this currency in set of currencies: foreach ($currencies as $currency) { if ($currency->id === $sentCurrencyId) { $defaultCurrency = $currency; - Log::debug(sprintf('default currency is now %s', $defaultCurrency->code)); + app('log')->debug(sprintf('default currency is now %s', $defaultCurrency->code)); break; } } @@ -100,7 +97,7 @@ class CurrencyForm try { $html = view('form.' . $view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); $html = 'Could not render currencyField.'; throw new FireflyException($html, 0, $e); } @@ -141,7 +138,7 @@ class CurrencyForm $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); $options['step'] = 'any'; - $defaultCurrency = $options['currency'] ?? Amt::getDefaultCurrency(); + $defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency(); /** @var Collection $currencies */ $currencies = app('amount')->getAllCurrencies(); unset($options['currency'], $options['placeholder']); @@ -154,13 +151,13 @@ class CurrencyForm $key = 'amount_currency_id_' . $name; $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $defaultCurrency->id; - Log::debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); + app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); // find this currency in set of currencies: foreach ($currencies as $currency) { if ($currency->id === $sentCurrencyId) { $defaultCurrency = $currency; - Log::debug(sprintf('default currency is now %s', $defaultCurrency->code)); + app('log')->debug(sprintf('default currency is now %s', $defaultCurrency->code)); break; } } @@ -172,7 +169,7 @@ class CurrencyForm try { $html = view('form.' . $view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); $html = 'Could not render currencyField.'; throw new FireflyException($html, 0, $e); } diff --git a/app/Support/Form/FormSupport.php b/app/Support/Form/FormSupport.php index ebb78e43aa..7aa8825d3e 100644 --- a/app/Support/Form/FormSupport.php +++ b/app/Support/Form/FormSupport.php @@ -26,7 +26,6 @@ namespace FireflyIII\Support\Form; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Illuminate\Support\Facades\Log; use Illuminate\Support\MessageBag; use Throwable; @@ -45,7 +44,7 @@ trait FormSupport */ public function select(string $name, array $list = null, $selected = null, array $options = null): string { - $list = $list ?? []; + $list ??= []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -54,7 +53,7 @@ trait FormSupport try { $html = view('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render select(): %s', $e->getMessage())); + app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage())); $html = 'Could not render select.'; } @@ -69,7 +68,7 @@ trait FormSupport */ protected function label(string $name, array $options = null): string { - $options = $options ?? []; + $options ??= []; if (array_key_exists('label', $options)) { return $options['label']; } @@ -87,7 +86,7 @@ trait FormSupport */ protected function expandOptionArray(string $name, $label, array $options = null): array { - $options = $options ?? []; + $options ??= []; $name = str_replace('[]', '', $name); $options['class'] = 'form-control'; $options['id'] = 'ffInput_' . $name; @@ -105,7 +104,7 @@ trait FormSupport protected function getHolderClasses(string $name): string { // Get errors from session: - /** @var MessageBag $errors */ + /** @var MessageBag|null $errors */ $errors = session('errors'); $classes = 'form-group'; @@ -157,8 +156,8 @@ trait FormSupport $date = null; try { $date = today(config('app.timezone')); - } catch (InvalidDateException $e) { - Log::error($e->getMessage()); + } catch (InvalidDateException $e) { // @phpstan-ignore-line + app('log')->error($e->getMessage()); } return $date; diff --git a/app/Support/Form/PiggyBankForm.php b/app/Support/Form/PiggyBankForm.php index 2f7e865483..f7b9ab5cea 100644 --- a/app/Support/Form/PiggyBankForm.php +++ b/app/Support/Form/PiggyBankForm.php @@ -71,7 +71,7 @@ class PiggyBankForm $groupTitle = $group->title; $groupOrder = $group->order; } - $subList[$groupOrder] = $subList[$groupOrder] ?? [ + $subList[$groupOrder] ??= [ 'group' => [ 'title' => $groupTitle, ], diff --git a/app/Support/Form/RuleForm.php b/app/Support/Form/RuleForm.php index 522e2f0729..3353371b43 100644 --- a/app/Support/Form/RuleForm.php +++ b/app/Support/Form/RuleForm.php @@ -67,7 +67,7 @@ class RuleForm */ public function ruleGroupListWithEmpty(string $name, $value = null, array $options = null): string { - $options = $options ?? []; + $options ??= []; $options['class'] = 'form-control'; /** @var RuleGroupRepositoryInterface $groupRepos */ $groupRepos = app(RuleGroupRepositoryInterface::class); diff --git a/app/Support/Http/Api/ConvertsExchangeRates.php b/app/Support/Http/Api/ConvertsExchangeRates.php index 214b7e9d06..d5476bc0d0 100644 --- a/app/Support/Http/Api/ConvertsExchangeRates.php +++ b/app/Support/Http/Api/ConvertsExchangeRates.php @@ -171,7 +171,7 @@ trait ConvertsExchangeRates private function convertAmount(string $amount, TransactionCurrency $from, TransactionCurrency $to, ?Carbon $date = null): string { app('log')->debug(sprintf('Converting %s from %s to %s', $amount, $from->code, $to->code)); - $date = $date ?? today(config('app.timezone')); + $date ??= today(config('app.timezone')); $rate = $this->getRate($from, $to, $date); return bcmul($amount, $rate); diff --git a/app/Support/Http/Api/ExchangeRateConverter.php b/app/Support/Http/Api/ExchangeRateConverter.php index 2051366405..8e9ec07ed9 100644 --- a/app/Support/Http/Api/ExchangeRateConverter.php +++ b/app/Support/Http/Api/ExchangeRateConverter.php @@ -78,12 +78,12 @@ class ExchangeRateConverter private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string { // first attempt: - $rate = $this->getFromDB((int)$from->id, (int)$to->id, $date->format('Y-m-d')); + $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); if (null !== $rate) { return $rate; } // no result. perhaps the other way around? - $rate = $this->getFromDB((int)$to->id, (int)$from->id, $date->format('Y-m-d')); + $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); if (null !== $rate) { return bcdiv('1', $rate); } @@ -123,7 +123,7 @@ class ExchangeRateConverter } app('log')->debug(sprintf('Going to get rate #%d->#%d (%s) from DB.', $from, $to, $date)); - /** @var CurrencyExchangeRate $result */ + /** @var CurrencyExchangeRate|null $result */ $result = auth()->user() ->currencyExchangeRates() ->where('from_currency_id', $from) @@ -151,16 +151,16 @@ class ExchangeRateConverter private function getEuroRate(TransactionCurrency $currency, Carbon $date): string { $euroId = $this->getEuroId(); - if ($euroId === (int)$currency->id) { + if ($euroId === $currency->id) { return '1'; } - $rate = $this->getFromDB((int)$currency->id, $euroId, $date->format('Y-m-d')); + $rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d')); if (null !== $rate) { // app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate)); return $rate; } - $rate = $this->getFromDB($euroId, (int)$currency->id, $date->format('Y-m-d')); + $rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d')); if (null !== $rate) { return bcdiv('1', $rate); // app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate)); @@ -194,7 +194,7 @@ class ExchangeRateConverter if (null === $euro) { throw new FireflyException('Cannot find EUR in system, cannot do currency conversion.'); } - $cache->store((int)$euro->id); - return (int)$euro->id; + $cache->store($euro->id); + return $euro->id; } } diff --git a/app/Support/Http/Api/ValidatesUserGroupTrait.php b/app/Support/Http/Api/ValidatesUserGroupTrait.php index abc8666155..69041f154e 100644 --- a/app/Support/Http/Api/ValidatesUserGroupTrait.php +++ b/app/Support/Http/Api/ValidatesUserGroupTrait.php @@ -52,7 +52,7 @@ trait ValidatesUserGroupTrait $user = auth()->user(); if (!$request->has('user_group_id')) { $group = $user->userGroup; - app('log')->debug(sprintf('validateUserGroup: no user group submitted, return default group #%d.', $group->id)); + app('log')->debug(sprintf('validateUserGroup: no user group submitted, return default group #%d.', $group?->id)); return $group; } $groupId = (int)$request->get('user_group_id'); diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index c21eb7b3b5..aa5493ef77 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -216,11 +216,22 @@ trait AugumentData /** @var BudgetLimit $entry */ foreach ($set as $entry) { $currency = $entry->transactionCurrency; + + if (null === $currency) { + $currency = app('amount')->getDefaultCurrency(); + } + // clone because these objects change each other. $currentStart = clone $entry->start_date; - $currentEnd = clone $entry->end_date; + $currentEnd = null === $entry->end_date ? null : clone $entry->end_date; + + if (null === $currentEnd) { + $currentEnd = clone $currentStart; + $currentEnd->addMonth(); + } + $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $currency); - $spent = $expenses[(int)$currency->id]['sum'] ?? '0'; + $spent = $expenses[$currency->id]['sum'] ?? '0'; $entry->spent = $spent; $limits->push($entry); @@ -251,7 +262,7 @@ trait AugumentData $name = $journal['source_account_name']; } - $grouped[$name] = $grouped[$name] ?? '0'; + $grouped[$name] ??= '0'; $grouped[$name] = bcadd($journal['amount'], $grouped[$name]); } diff --git a/app/Support/Http/Controllers/ChartGeneration.php b/app/Support/Http/Controllers/ChartGeneration.php index bfb84f7937..16cc521d5d 100644 --- a/app/Support/Http/Controllers/ChartGeneration.php +++ b/app/Support/Http/Controllers/ChartGeneration.php @@ -30,7 +30,6 @@ use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -60,7 +59,7 @@ trait ChartGeneration if ($cache->has()) { return $cache->get(); } - Log::debug('Regenerate chart.account.account-balance-chart from scratch.'); + app('log')->debug('Regenerate chart.account.account-balance-chart from scratch.'); $locale = app('steam')->getLocale(); /** @var GeneratorInterface $generator */ $generator = app(GeneratorInterface::class); diff --git a/app/Support/Http/Controllers/CreateStuff.php b/app/Support/Http/Controllers/CreateStuff.php index a856a2ea64..14192fbd63 100644 --- a/app/Support/Http/Controllers/CreateStuff.php +++ b/app/Support/Http/Controllers/CreateStuff.php @@ -112,12 +112,12 @@ trait CreateStuff return; } - $keys = RSA::createKey(4096); + $key = RSA::createKey(4096); Log::alert('NO OAuth keys were found. They have been created.'); - file_put_contents($publicKey, $keys['publickey']); - file_put_contents($privateKey, $keys['privatekey']); + file_put_contents($publicKey, (string)$key->getPublicKey()); + file_put_contents($privateKey, $key->toString('PKCS1')); } /** diff --git a/app/Support/Http/Controllers/GetConfigurationData.php b/app/Support/Http/Controllers/GetConfigurationData.php index 0cfe7b8019..eebadefb10 100644 --- a/app/Support/Http/Controllers/GetConfigurationData.php +++ b/app/Support/Http/Controllers/GetConfigurationData.php @@ -25,7 +25,6 @@ namespace FireflyIII\Support\Http\Controllers; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -80,7 +79,7 @@ trait GetConfigurationData $steps[] = $currentStep; } } - Log::debug(sprintf('Total basic steps for %s is %d', $routeKey, count($steps))); + app('log')->debug(sprintf('Total basic steps for %s is %d', $routeKey, count($steps))); return $steps; } @@ -210,7 +209,7 @@ trait GetConfigurationData } } } - Log::debug(sprintf('Total specific steps for route "%s" and page "%s" (routeKey is "%s") is %d', $route, $specificPage, $routeKey, count($steps))); + app('log')->debug(sprintf('Total specific steps for route "%s" and page "%s" (routeKey is "%s") is %d', $route, $specificPage, $routeKey, count($steps))); return $steps; } @@ -221,9 +220,9 @@ trait GetConfigurationData protected function verifyRecurringCronJob(): void { $config = app('fireflyconfig')->get('last_rt_job', 0); - $lastTime = (int)$config->data; + $lastTime = (int)$config?->data; $now = time(); - Log::debug(sprintf('verifyRecurringCronJob: last time is %d ("%s"), now is %d', $lastTime, $config->data, $now)); + app('log')->debug(sprintf('verifyRecurringCronJob: last time is %d ("%s"), now is %d', $lastTime, $config?->data, $now)); if (0 === $lastTime) { request()->session()->flash('info', trans('firefly.recurring_never_cron')); diff --git a/app/Support/Http/Controllers/ModelInformation.php b/app/Support/Http/Controllers/ModelInformation.php index 7d3d42e7ce..0491a8c741 100644 --- a/app/Support/Http/Controllers/ModelInformation.php +++ b/app/Support/Http/Controllers/ModelInformation.php @@ -30,7 +30,6 @@ use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -60,8 +59,8 @@ trait ModelInformation ] )->render(); } catch (Throwable $e) { - Log::error(sprintf('Throwable was thrown in getActionsForBill(): %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Throwable was thrown in getActionsForBill(): %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); $result = 'Could not render view. See log files.'; throw new FireflyException($result, 0, $e); } @@ -80,8 +79,11 @@ trait ModelInformation /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); // types of liability: - $debt = $repository->getAccountTypeByType(AccountType::DEBT); - $loan = $repository->getAccountTypeByType(AccountType::LOAN); + /** @var AccountType $debt */ + $debt = $repository->getAccountTypeByType(AccountType::DEBT); + /** @var AccountType $loan */ + $loan = $repository->getAccountTypeByType(AccountType::LOAN); + /** @var AccountType $mortgage */ $mortgage = $repository->getAccountTypeByType(AccountType::MORTGAGE); $liabilityTypes = [ $debt->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::DEBT)), @@ -129,7 +131,7 @@ trait ModelInformation $result = []; $billTriggers = ['currency_is', 'amount_more', 'amount_less', 'description_contains']; $values = [ - $bill->transactionCurrency()->first()->name, + $bill->transactionCurrency()->first()?->name, $bill->amount_min, $bill->amount_max, $bill->name, @@ -147,10 +149,9 @@ trait ModelInformation ] )->render(); } catch (Throwable $e) { - Log::debug(sprintf('Throwable was thrown in getTriggersForBill(): %s', $e->getMessage())); - Log::debug($e->getTraceAsString()); - $string = ''; - throw new FireflyException('Could not render trigger', 0, $e); + app('log')->debug(sprintf('Throwable was thrown in getTriggersForBill(): %s', $e->getMessage())); + app('log')->debug($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not render trigger: %s', $e->getMessage()), 0, $e); } if ('' !== $string) { $result[] = $string; @@ -168,7 +169,7 @@ trait ModelInformation */ private function getTriggersForJournal(TransactionJournal $journal): array { - // See reference nr. 40 + // TODO duplicated code. $operators = config('search.operators'); $triggers = []; foreach ($operators as $key => $operator) { @@ -198,7 +199,7 @@ trait ModelInformation // currency $journalTriggers[$index] = 'currency_is'; - $values[$index] = sprintf('%s (%s)', $journal->transactionCurrency->name, $journal->transactionCurrency->code); + $values[$index] = sprintf('%s (%s)', $journal->transactionCurrency?->name, $journal->transactionCurrency?->code); $index++; // amount_exactly: @@ -250,29 +251,25 @@ trait ModelInformation $values[$index] = $notes->text; } - foreach ($journalTriggers as $index => $trigger) { + foreach ($journalTriggers as $ii => $trigger) { try { - $string = view( - 'rules.partials.trigger', - [ - 'oldTrigger' => $trigger, - 'oldValue' => $values[$index], - 'oldChecked' => false, - 'count' => $index + 1, - 'triggers' => $triggers, - ] - )->render(); + $renderInfo = [ + 'oldTrigger' => $trigger, + 'oldValue' => $values[$ii], + 'oldChecked' => false, + 'count' => $ii + 1, + 'triggers' => $triggers, + ]; + $string = view('rules.partials.trigger', $renderInfo)->render(); } catch (Throwable $e) { - Log::debug(sprintf('Throwable was thrown in getTriggersForJournal(): %s', $e->getMessage())); - Log::debug($e->getTraceAsString()); - $string = ''; - throw new FireflyException('Could not render trigger', 0, $e); + app('log')->debug(sprintf('Throwable was thrown in getTriggersForJournal(): %s', $e->getMessage())); + app('log')->debug($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not render trigger: %s', $e->getMessage()), 0, $e); } if ('' !== $string) { $result[] = $string; } } - return $result; } } diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php index 8bdacc157d..0d4045bed6 100644 --- a/app/Support/Http/Controllers/PeriodOverview.php +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -33,7 +33,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -409,14 +408,14 @@ trait PeriodOverview */ protected function getNoCategoryPeriodOverview(Carbon $theDate): array { - Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); + app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); $range = app('navigation')->getViewRange(true); $first = $this->journalRepos->firstNull(); $start = null === $first ? new Carbon() : $first->date; $end = clone $theDate; - Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); - Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); + app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); + app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); // properties for cache $dates = app('navigation')->blockPeriods($start, $end, $range); @@ -462,7 +461,7 @@ trait PeriodOverview 'transferred' => $this->groupByCurrency($transferred), ]; } - Log::debug('End of loops'); + app('log')->debug('End of loops'); return $entries; } diff --git a/app/Support/Http/Controllers/RenderPartialViews.php b/app/Support/Http/Controllers/RenderPartialViews.php index c68c358255..38820f78fc 100644 --- a/app/Support/Http/Controllers/RenderPartialViews.php +++ b/app/Support/Http/Controllers/RenderPartialViews.php @@ -36,7 +36,6 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\Search\OperatorQuerySearch; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -65,12 +64,16 @@ trait RenderPartialViews $accountRepos = app(AccountRepositoryInterface::class); $account = $accountRepos->find((int)$attributes['accountId']); + if (null === $budget || null === $account) { + throw new FireflyException('Could not render popup.report.balance-amount because budget or account is null.'); + } + $journals = $popupHelper->balanceForBudget($budget, $account, $attributes); try { $view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; throw new FireflyException($view, 0, $e); } @@ -93,7 +96,7 @@ trait RenderPartialViews try { $result = view('reports.options.budget', compact('budgets'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); + app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } @@ -126,7 +129,7 @@ trait RenderPartialViews try { $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; throw new FireflyException($view, 0, $e); } @@ -155,7 +158,7 @@ trait RenderPartialViews try { $view = view('popup.report.category-entry', compact('journals', 'category'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; throw new FireflyException($view, 0, $e); } @@ -178,7 +181,7 @@ trait RenderPartialViews try { $result = view('reports.options.category', compact('categories'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.options.category: %s', $e->getMessage())); + app('log')->error(sprintf('Cannot render reports.options.category: %s', $e->getMessage())); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } @@ -221,7 +224,7 @@ trait RenderPartialViews try { $result = view('reports.options.double', compact('set'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); + app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } @@ -256,7 +259,7 @@ trait RenderPartialViews try { $view = view('popup.report.expense-entry', compact('journals', 'account'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; throw new FireflyException($view, 0, $e); } @@ -292,8 +295,8 @@ trait RenderPartialViews ] )->render(); } catch (Throwable $e) { - Log::debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } @@ -331,7 +334,7 @@ trait RenderPartialViews if ('user_action' !== $entry->trigger_type) { $count = ($index + 1); try { - $rootOperator = OperatorQuerySearch::getRootOperator($entry->trigger_type); + $rootOperator = OperatorQuerySearch::getRootOperator((string)$entry->trigger_type); if (str_starts_with($rootOperator, '-')) { $rootOperator = substr($rootOperator, 1); } @@ -341,14 +344,14 @@ trait RenderPartialViews 'oldTrigger' => $rootOperator, 'oldValue' => $entry->trigger_value, 'oldChecked' => $entry->stop_processing, - 'oldProhibited' => str_starts_with($entry->trigger_type, '-'), + 'oldProhibited' => str_starts_with((string)$entry->trigger_type, '-'), 'count' => $count, 'triggers' => $triggers, ] )->render(); } catch (Throwable $e) { - Log::debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } @@ -385,7 +388,7 @@ trait RenderPartialViews try { $view = view('popup.report.income-entry', compact('journals', 'account'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Could not render: %s', $e->getMessage())); + app('log')->error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; throw new FireflyException($view, 0, $e); } @@ -404,7 +407,7 @@ trait RenderPartialViews try { $result = view('reports.options.no-options')->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.options.no-options: %s', $e->getMessage())); + app('log')->error(sprintf('Cannot render reports.options.no-options: %s', $e->getMessage())); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } @@ -428,7 +431,7 @@ trait RenderPartialViews try { $result = view('reports.options.tag', compact('tags'))->render(); } catch (Throwable $e) { - Log::error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); + app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); $result = 'Could not render view.'; throw new FireflyException($result, 0, $e); } diff --git a/app/Support/Http/Controllers/RequestInformation.php b/app/Support/Http/Controllers/RequestInformation.php index 28780e340f..be19d03318 100644 --- a/app/Support/Http/Controllers/RequestInformation.php +++ b/app/Support/Http/Controllers/RequestInformation.php @@ -33,9 +33,7 @@ use FireflyIII\User; use Hash; use Illuminate\Contracts\Validation\Validator as ValidatorContract; use Illuminate\Routing\Route; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Validator; -use InvalidArgumentException; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Route as RouteFacade; @@ -56,7 +54,7 @@ trait RequestInformation $url = url()->to('/'); $parts = parse_url($url); - return $parts['host']; + return $parts['host'] ?? ''; } /** @@ -130,7 +128,9 @@ trait RequestInformation */ final protected function getSpecificPageName(): string // get request info { - return null === RouteFacade::current()->parameter('objectType') ? '' : '_' . RouteFacade::current()->parameter('objectType'); + /** @var string|null $param */ + $param = RouteFacade::current()->parameter('objectType'); + return null === $param ? '' : sprintf('_%s', $param); } /** @@ -168,23 +168,22 @@ trait RequestInformation */ final protected function parseAttributes(array $attributes): array // parse input + return result { - $attributes['location'] = $attributes['location'] ?? ''; + $attributes['location'] ??= ''; $attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', new Route('get', '', [])); - try { - $attributes['startDate'] = Carbon::createFromFormat('Ymd', $attributes['startDate'])->startOfDay(); - } catch (InvalidArgumentException $e) { - Log::debug(sprintf('Not important error message: %s', $e->getMessage())); - $date = today(config('app.timezone'))->startOfMonth(); - $attributes['startDate'] = $date; + $date = Carbon::createFromFormat('Ymd', $attributes['startDate']); + if (false === $date) { + $date = today(config('app.timezone')); } + $date->startOfMonth(); + $attributes['startDate'] = $date; - try { - $attributes['endDate'] = Carbon::createFromFormat('Ymd', $attributes['endDate'])->endOfDay(); - } catch (InvalidArgumentException $e) { - Log::debug(sprintf('Not important error message: %s', $e->getMessage())); - $date = today(config('app.timezone'))->startOfMonth(); - $attributes['endDate'] = $date; + $date2 = Carbon::createFromFormat('Ymd', $attributes['endDate']); + if (false === $date2) { + $date2 = today(config('app.timezone')); } + $date2->endOfDay(); + $attributes['endDate'] = $date2; + return $attributes; } diff --git a/app/Support/Http/Controllers/RuleManagement.php b/app/Support/Http/Controllers/RuleManagement.php index 874829cc6d..5d06fd0fd1 100644 --- a/app/Support/Http/Controllers/RuleManagement.php +++ b/app/Support/Http/Controllers/RuleManagement.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Support\Search\OperatorQuerySearch; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; use Throwable; /** @@ -60,8 +59,8 @@ trait RuleManagement ] )->render(); } catch (Throwable $e) { - Log::error(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } $index++; @@ -107,8 +106,8 @@ trait RuleManagement ] )->render(); } catch (Throwable $e) { - Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } $index++; @@ -139,12 +138,14 @@ trait RuleManagement $index = 0; foreach ($submittedOperators as $operator) { + $rootOperator = OperatorQuerySearch::getRootOperator($operator['type']); + $needsContext = (bool)config(sprintf('search.operators.%s.needs_context', $rootOperator)); try { $renderedEntries[] = view( 'rules.partials.trigger', [ - 'oldTrigger' => OperatorQuerySearch::getRootOperator($operator['type']), - 'oldValue' => $operator['value'], + 'oldTrigger' => $rootOperator, + 'oldValue' => $needsContext ? $operator['value'] : '', 'oldChecked' => false, 'oldProhibited' => $operator['prohibited'] ?? false, 'count' => $index + 1, @@ -152,8 +153,8 @@ trait RuleManagement ] )->render(); } catch (Throwable $e) { - Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e); } $index++; diff --git a/app/Support/Http/Controllers/UserNavigation.php b/app/Support/Http/Controllers/UserNavigation.php index 6fffe00a08..e6fbf90b48 100644 --- a/app/Support/Http/Controllers/UserNavigation.php +++ b/app/Support/Http/Controllers/UserNavigation.php @@ -31,7 +31,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use Illuminate\Http\RedirectResponse; use Illuminate\Routing\Redirector; -use Illuminate\Support\Facades\Log; /** * Trait UserNavigation @@ -53,9 +52,9 @@ trait UserNavigation */ final protected function getPreviousUrl(string $identifier): string { - Log::debug(sprintf('Trying to retrieve URL stored under "%s"', $identifier)); + app('log')->debug(sprintf('Trying to retrieve URL stored under "%s"', $identifier)); $url = (string)session($identifier); - Log::debug(sprintf('The URL is %s', $url)); + app('log')->debug(sprintf('The URL is %s', $url)); return app('steam')->getSafeUrl($url, route('index')); } @@ -107,7 +106,7 @@ trait UserNavigation /** @var Transaction|null $transaction */ $transaction = $account->transactions()->first(); if (null === $transaction) { - Log::error(sprintf('Account #%d has no transactions. Dont know where it belongs.', $account->id)); + app('log')->error(sprintf('Account #%d has no transactions. Dont know where it belongs.', $account->id)); session()->flash('error', trans('firefly.cant_find_redirect_account')); return redirect(route('index')); @@ -116,7 +115,7 @@ trait UserNavigation /** @var Transaction|null $other */ $other = $journal->transactions()->where('id', '!=', $transaction->id)->first(); if (null === $other) { - Log::error(sprintf('Account #%d has no valid journals. Dont know where it belongs.', $account->id)); + app('log')->error(sprintf('Account #%d has no valid journals. Dont know where it belongs.', $account->id)); session()->flash('error', trans('firefly.cant_find_redirect_account')); return redirect(route('index')); @@ -138,7 +137,7 @@ trait UserNavigation /** @var TransactionJournal|null $journal */ $journal = $group->transactionJournals()->first(); if (null === $journal) { - Log::error(sprintf('No journals in group #%d', $group->id)); + app('log')->error(sprintf('No journals in group #%d', $group->id)); return redirect(route('index')); } @@ -166,7 +165,7 @@ trait UserNavigation $return = app('steam')->getSafePreviousUrl(); session()->put($identifier, $return); - Log::debug(sprintf('rememberPreviousUrl: %s: "%s"', $identifier, $return)); + app('log')->debug(sprintf('rememberPreviousUrl: %s: "%s"', $identifier, $return)); return $return; } diff --git a/app/Support/Logging/AuditProcessor.php b/app/Support/Logging/AuditProcessor.php index 8e64183298..d23d8b32fe 100644 --- a/app/Support/Logging/AuditProcessor.php +++ b/app/Support/Logging/AuditProcessor.php @@ -43,7 +43,7 @@ class AuditProcessor if (auth()->check()) { $message = sprintf( 'AUDIT: %s (%s (%s) -> %s:%s)', - $record['message'], + $record['message'], // @phpstan-ignore-line app('request')->ip(), auth()->user()->email, request()->method(), @@ -54,7 +54,7 @@ class AuditProcessor $message = sprintf( 'AUDIT: %s (%s -> %s:%s)', - $record['message'], + $record['message'], // @phpstan-ignore-line app('request')->ip(), request()->method(), request()->url() diff --git a/app/Support/Models/BillDateCalculator.php b/app/Support/Models/BillDateCalculator.php new file mode 100644 index 0000000000..c64cee19f0 --- /dev/null +++ b/app/Support/Models/BillDateCalculator.php @@ -0,0 +1,141 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Models; + +use Carbon\Carbon; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; + +class BillDateCalculator +{ + /** + * Returns the dates a bill needs to be paid. + * + * @param Carbon $earliest + * @param Carbon $latest + * @param Carbon $billStart + * @param string $period + * @param int $skip + * @param Carbon|null $lastPaid + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function getPayDates(Carbon $earliest, Carbon $latest, Carbon $billStart, string $period, int $skip, ?Carbon $lastPaid): array + { + Log::debug('Now in BillDateCalculator::getPayDates()'); + Log::debug(sprintf('Dates must be between %s and %s.', $earliest->format('Y-m-d'), $latest->format('Y-m-d'))); + Log::debug(sprintf('Bill started on %s, period is "%s", skip is %d, last paid = "%s".', $billStart->format('Y-m-d'), $period, $skip, $lastPaid?->format('Y-m-d'))); + + + $set = new Collection(); + $currentStart = clone $earliest; + + // 2023-06-23 subDay to fix 7655 + $currentStart->subDay(); + $loop = 0; + Log::debug('Start of loop'); + while ($currentStart <= $latest) { + Log::debug(sprintf('Current start is %s', $currentStart->format('Y-m-d'))); + $nextExpectedMatch = $this->nextDateMatch(clone $currentStart, clone $billStart, $period, $skip); + Log::debug(sprintf('Next expected match is %s', $nextExpectedMatch->format('Y-m-d'))); + + // If nextExpectedMatch is after end, we stop looking: + if ($nextExpectedMatch->gt($latest)) { + Log::debug('Next expected match is after $latest.'); + if ($set->count() > 0) { + Log::debug(sprintf('Already have %d date(s), so we can safely break.', $set->count())); + break; + } + Log::debug('Add date to set anyway, since we had no dates yet.'); + $set->push(clone $nextExpectedMatch); + continue; + } + + // add to set, if the date is ON or after the start parameter + // AND date is after last paid date + if ( + $nextExpectedMatch->gte($earliest) // date is after "earliest possible date" + && (null === $lastPaid || $nextExpectedMatch->gt($lastPaid)) // date is after last paid date, if that date is not NULL + ) { + Log::debug('Add date to set, because it is after earliest possible date and after last paid date.'); + $set->push(clone $nextExpectedMatch); + } + + // 2023-10 + // for the next loop, go to end of period, THEN add day. + $nextExpectedMatch->addDay(); + $currentStart = clone $nextExpectedMatch; + + $loop++; + if ($loop > 12) { + Log::debug('Loop is more than 12, so we break.'); + break; + } + } + Log::debug('end of loop'); + $simple = $set->map( + static function (Carbon $date) { + return $date->format('Y-m-d'); + } + ); + Log::debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray()); + + return $simple->toArray(); + + } + + /** + * Given a bill and a date, this method will tell you at which moment this bill expects its next + * transaction given the earliest date this could happen. + * + * That date must be AFTER $billStartDate, as a sanity check. + * + * @param Carbon $earliest + * @param Carbon $billStartDate + * @param string $period + * @param int $skip + * + * @return Carbon + */ + protected function nextDateMatch(Carbon $earliest, Carbon $billStartDate, string $period, int $skip): Carbon + { + Log::debug(sprintf('Bill start date is %s', $billStartDate->format('Y-m-d'))); + if ($earliest->lt($billStartDate)) { + Log::debug('Earliest possible date is after bill start, so just return bill start date.'); + return $billStartDate; + } + + $steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate); + $result = clone $billStartDate; + if ($steps > 0) { + $steps -= 1; + Log::debug(sprintf('Steps is %d, because addPeriod already adds 1.', $steps)); + $result = app('navigation')->addPeriod($billStartDate, $period, $steps); + } + Log::debug(sprintf('Number of steps is %d, added to %s, result is %s', $steps, $billStartDate->format('Y-m-d'), $result->format('Y-m-d'))); + return $result; + } + +} diff --git a/app/Support/Models/ReturnsIntegerIdTrait.php b/app/Support/Models/ReturnsIntegerIdTrait.php new file mode 100644 index 0000000000..d84925c07f --- /dev/null +++ b/app/Support/Models/ReturnsIntegerIdTrait.php @@ -0,0 +1,45 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Models; + +use Illuminate\Database\Eloquent\Casts\Attribute; + +/** + * Trait ReturnsIntegerIdTrait + */ +trait ReturnsIntegerIdTrait +{ + /** + * Get the ID + * @SuppressWarnings(PHPMD.ShortMethodName) + * + * @return Attribute + */ + protected function id(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } +} diff --git a/app/Support/Models/ReturnsIntegerUserIdTrait.php b/app/Support/Models/ReturnsIntegerUserIdTrait.php new file mode 100644 index 0000000000..08e4d30484 --- /dev/null +++ b/app/Support/Models/ReturnsIntegerUserIdTrait.php @@ -0,0 +1,54 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Models; + +use Illuminate\Database\Eloquent\Casts\Attribute; + +/** + * Trait ReturnsIntegerUserIdTrait + */ +trait ReturnsIntegerUserIdTrait +{ + /** + * Get the user group ID + * + * @return Attribute + */ + protected function userGroupId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } + + /** + * @return Attribute + */ + protected function userId(): Attribute + { + return Attribute::make( + get: static fn($value) => (int)$value, + ); + } +} diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index fbd3e3a0ab..712deef93d 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -25,9 +25,9 @@ namespace FireflyIII\Support; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Exceptions\IntervalException; use FireflyIII\Helpers\Fiscal\FiscalHelperInterface; use FireflyIII\Support\Calendar\Calculator; -use FireflyIII\Support\Calendar\Exceptions\IntervalException; use FireflyIII\Support\Calendar\Periodicity; use Illuminate\Support\Facades\Log; use Psr\Container\ContainerExceptionInterface; @@ -46,7 +46,7 @@ class Navigation */ public function __construct(Calculator $calculator = null) { - $this->calculator = ($calculator instanceof Calculator) ?: new Calculator(); + $this->calculator = $calculator instanceof Calculator ? $calculator : new Calculator(); } /** @@ -91,7 +91,7 @@ class Navigation Log::error(sprintf( 'The periodicity %s is unknown. Choose one of available periodicity: %s', $repeatFreq, - join(', ', array_keys($functionMap)) + implode(', ', array_keys($functionMap)) )); return $theDate; } @@ -112,7 +112,7 @@ class Navigation return $this->calculator->nextDateByInterval($epoch, $periodicity, $skipInterval); } catch (IntervalException $exception) { Log::warning($exception->getMessage(), ['exception' => $exception]); - } catch (Throwable $exception) { + } catch (Throwable $exception) { // @phpstan-ignore-line Log::error($exception->getMessage(), ['exception' => $exception]); } @@ -145,8 +145,8 @@ class Navigation $workEnd = clone $loopDate; while ($loopCount < 13) { // make range: - $workStart = \Navigation::startOfPeriod($workStart, $range); - $workEnd = \Navigation::endOfPeriod($workStart, $range); + $workStart = $this->startOfPeriod($workStart, $range); + $workEnd = $this->endOfPeriod($workStart, $range); // make sure we don't go overboard if ($workEnd->gt($start)) { @@ -213,7 +213,7 @@ class Navigation ]; if (array_key_exists($repeatFreq, $functionMap)) { $function = $functionMap[$repeatFreq]; - $date->$function(); + $date->$function(); // @phpstan-ignore-line return $date; } @@ -325,7 +325,7 @@ class Navigation $function = $functionMap[$repeatFreq]; if (array_key_exists($repeatFreq, $modifierMap)) { - $currentEnd->$function($modifierMap[$repeatFreq]); + $currentEnd->$function($modifierMap[$repeatFreq]); // @phpstan-ignore-line if (in_array($repeatFreq, $subDay, true)) { $currentEnd->subDay(); } @@ -333,7 +333,7 @@ class Navigation return $currentEnd; } - $currentEnd->$function(); + $currentEnd->$function(); // @phpstan-ignore-line $currentEnd->endOfDay(); if (in_array($repeatFreq, $subDay, true)) { $currentEnd->subDay(); @@ -351,7 +351,7 @@ class Navigation */ public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int { - app('log')->debug(sprintf( + Log::debug(sprintf( 'diffInPeriods: %s (skip: %d), between %s and %s.', $period, $skip, @@ -367,32 +367,33 @@ class Navigation 'yearly' => 'floatDiffInYears', ]; if (!array_key_exists($period, $map)) { - app('log')->warning(sprintf('No diffInPeriods for period "%s"', $period)); + Log::warning(sprintf('No diffInPeriods for period "%s"', $period)); return 1; } $func = $map[$period]; // first do the diff - $diff = $beginning->$func($end); + $floatDiff = $beginning->$func($end); // @phpstan-ignore-line + // then correct for quarterly or half-year if ('quarterly' === $period) { - app('log')->debug(sprintf('Q: Corrected %f to %f', $diff, $diff / 3)); - $diff = $diff / 3; + Log::debug(sprintf('Q: Corrected %f to %f', $floatDiff, $floatDiff / 3)); + $floatDiff /= 3; } if ('half-year' === $period) { - app('log')->debug(sprintf('H: Corrected %f to %f', $diff, $diff / 6)); - $diff = $diff / 6; + Log::debug(sprintf('H: Corrected %f to %f', $floatDiff, $floatDiff / 6)); + $floatDiff /= 6; } // then do ceil() - $diff = ceil($diff); + $diff = ceil($floatDiff); - app('log')->debug(sprintf('Diff is %f (%d)', $beginning->$func($end), $diff)); + Log::debug(sprintf('Diff is %f periods (%d rounded up)', $floatDiff, $diff)); if ($skip > 0) { $parameter = $skip + 1; $diff = ceil($diff / $parameter) * $parameter; - app('log')->debug(sprintf( + Log::debug(sprintf( 'diffInPeriods: skip is %d, so param is %d, and diff becomes %d', $skip, $parameter, @@ -433,7 +434,7 @@ class Navigation if (array_key_exists($repeatFreq, $functionMap)) { $function = $functionMap[$repeatFreq]; - $currentEnd->$function(); + $currentEnd->$function(); // @phpstan-ignore-line } if (null !== $maxDate && $currentEnd > $maxDate) { @@ -455,7 +456,11 @@ class Navigation */ public function getViewRange(bool $correct): string { - $range = (string)app('preferences')->get('viewRange', '1M')?->data ?? '1M'; + $range = app('preferences')->get('viewRange', '1M')?->data ?? '1M'; + if (is_array($range)) { + $range = '1M'; + } + $range = (string)$range; if (!$correct) { return $range; } @@ -507,7 +512,7 @@ class Navigation $formatted = $begin->format($format); $displayed = $begin->isoFormat($displayFormat); $entries[$formatted] = $displayed; - $begin->$increment(); + $begin->$increment(); // @phpstan-ignore-line } return $entries; @@ -562,7 +567,7 @@ class Navigation ]; if (array_key_exists($repeatFrequency, $formatMap)) { - return $date->isoFormat((string)$formatMap[$repeatFrequency]); + return $date->isoFormat($formatMap[$repeatFrequency]); } if ('3M' === $repeatFrequency || 'quarter' === $repeatFrequency) { $quarter = ceil($theDate->month / 3); @@ -700,7 +705,7 @@ class Navigation */ public function subtractPeriod(Carbon $theDate, string $repeatFreq, int $subtract = null): Carbon { - $subtract = $subtract ?? 1; + $subtract ??= 1; $date = clone $theDate; // 1D 1W 1M 3M 6M 1Y $functionMap = [ @@ -725,7 +730,7 @@ class Navigation ]; if (array_key_exists($repeatFreq, $functionMap)) { $function = $functionMap[$repeatFreq]; - $date->$function($subtract); + $date->$function($subtract); // @phpstan-ignore-line return $date; } @@ -800,7 +805,7 @@ class Navigation if (array_key_exists($range, $functionMap)) { $function = $functionMap[$range]; - $end->$function(); + $end->$function(); // @phpstan-ignore-line return $end; } @@ -861,7 +866,7 @@ class Navigation ]; if (array_key_exists($range, $functionMap)) { $function = $functionMap[$range]; - $start->$function(); + $start->$function(); // @phpstan-ignore-line return $start; } diff --git a/app/Support/NullArrayObject.php b/app/Support/NullArrayObject.php index 035488f626..36a4d4da48 100644 --- a/app/Support/NullArrayObject.php +++ b/app/Support/NullArrayObject.php @@ -32,6 +32,7 @@ use ArrayObject; */ class NullArrayObject extends ArrayObject { + /** @var mixed|null */ public $default = null; /** @@ -40,7 +41,7 @@ class NullArrayObject extends ArrayObject * @param array $array * @param null $default */ - /* @phpstan-ignore-next-line */ + /** @phpstan-ignore-next-line */ public function __construct(array $array, $default = null) { parent::__construct($array); diff --git a/app/Support/ParseDateString.php b/app/Support/ParseDateString.php index 1e30b47bb3..68bf6e9d82 100644 --- a/app/Support/ParseDateString.php +++ b/app/Support/ParseDateString.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Support; use Carbon\Carbon; +use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Exceptions\FireflyException; use Illuminate\Support\Facades\Log; @@ -80,7 +81,7 @@ class ParseDateString */ public function parseDate(string $date): Carbon { - Log::debug(sprintf('parseDate("%s")', $date)); + app('log')->debug(sprintf('parseDate("%s")', $date)); $date = strtolower($date); // parse keywords: if (in_array($date, $this->keywords, true)) { @@ -89,7 +90,7 @@ class ParseDateString // if regex for YYYY-MM-DD: $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; - if (preg_match($pattern, $date)) { + if (false !== preg_match($pattern, $date)) { return $this->parseDefaultDate($date); } @@ -108,7 +109,7 @@ class ParseDateString // maybe a date range if (10 === strlen($date) && (str_contains($date, 'xx') || str_contains($date, 'xxxx'))) { - Log::debug(sprintf('[c] Detected a date range ("%s"), return a fake date.', $date)); + app('log')->debug(sprintf('[c] Detected a date range ("%s"), return a fake date.', $date)); // very lazy way to parse the date without parsing it, because this specific function // cant handle date ranges. return new Carbon('1984-09-17'); @@ -152,7 +153,16 @@ class ParseDateString */ protected function parseDefaultDate(string $date): Carbon { - return Carbon::createFromFormat('Y-m-d', $date); + $result = false; + try { + $result = Carbon::createFromFormat('Y-m-d', $date); + } catch (InvalidFormatException $e) { // @phpstan-ignore-line + Log::error(sprintf('parseDefaultDate("%s") ran into an error, but dont mind: %s', $date, $e->getMessage())); + } + if (false === $result) { + $result = today(config('app.timezone'))->startOfDay(); + } + return $result; } /** @@ -162,7 +172,7 @@ class ParseDateString */ protected function parseRelativeDate(string $date): Carbon { - Log::debug(sprintf('Now in parseRelativeDate("%s")', $date)); + app('log')->debug(sprintf('Now in parseRelativeDate("%s")', $date)); $parts = explode(' ', $date); $today = today(config('app.timezone'))->startOfDay(); $functions = [ @@ -183,27 +193,27 @@ class ParseDateString ]; foreach ($parts as $part) { - Log::debug(sprintf('Now parsing part "%s"', $part)); + app('log')->debug(sprintf('Now parsing part "%s"', $part)); $part = trim($part); // verify if correct $pattern = '/[+-]\d+[wqmdy]/'; $res = preg_match($pattern, $part); if (0 === $res || false === $res) { - Log::error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); + app('log')->error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); continue; } $direction = str_starts_with($part, '+') ? 1 : 0; $period = $part[strlen($part) - 1]; $number = (int)substr($part, 1, -1); if (!array_key_exists($period, $functions[$direction])) { - Log::error(sprintf('No method for direction %d and period "%s".', $direction, $period)); + app('log')->error(sprintf('No method for direction %d and period "%s".', $direction, $period)); continue; } $func = $functions[$direction][$period]; - Log::debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d'))); - $today->$func($number); - Log::debug(sprintf('Resulting date is %s', $today->format('Y-m-d'))); + app('log')->debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d'))); + $today->$func($number); // @phpstan-ignore-line + app('log')->debug(sprintf('Resulting date is %s', $today->format('Y-m-d'))); } return $today; @@ -255,12 +265,12 @@ class ParseDateString { // if regex for xxxx-xx-DD: $pattern = '/^xxxx-xx-(0[1-9]|[12]\d|3[01])$/'; - if (preg_match($pattern, $date)) { - Log::debug(sprintf('"%s" is a day range.', $date)); + if (false !== preg_match($pattern, $date)) { + app('log')->debug(sprintf('"%s" is a day range.', $date)); return true; } - Log::debug(sprintf('"%s" is not a day range.', $date)); + app('log')->debug(sprintf('"%s" is not a day range.', $date)); return false; } @@ -290,12 +300,12 @@ class ParseDateString { // if regex for xxxx-MM-xx: $pattern = '/^xxxx-(0[1-9]|1[012])-xx$/'; - if (preg_match($pattern, $date)) { - Log::debug(sprintf('"%s" is a month range.', $date)); + if (false !== preg_match($pattern, $date)) { + app('log')->debug(sprintf('"%s" is a month range.', $date)); return true; } - Log::debug(sprintf('"%s" is not a month range.', $date)); + app('log')->debug(sprintf('"%s" is not a month range.', $date)); return false; } @@ -309,7 +319,7 @@ class ParseDateString */ protected function parseMonthRange(string $date): array { - Log::debug(sprintf('parseMonthRange: Parsed "%s".', $date)); + app('log')->debug(sprintf('parseMonthRange: Parsed "%s".', $date)); $parts = explode('-', $date); return [ @@ -326,12 +336,12 @@ class ParseDateString { // if regex for YYYY-xx-xx: $pattern = '/^(19|20)\d\d-xx-xx$/'; - if (preg_match($pattern, $date)) { - Log::debug(sprintf('"%s" is a year range.', $date)); + if (false !== preg_match($pattern, $date)) { + app('log')->debug(sprintf('"%s" is a year range.', $date)); return true; } - Log::debug(sprintf('"%s" is not a year range.', $date)); + app('log')->debug(sprintf('"%s" is not a year range.', $date)); return false; } @@ -345,7 +355,7 @@ class ParseDateString */ protected function parseYearRange(string $date): array { - Log::debug(sprintf('parseYearRange: Parsed "%s"', $date)); + app('log')->debug(sprintf('parseYearRange: Parsed "%s"', $date)); $parts = explode('-', $date); return [ @@ -362,12 +372,12 @@ class ParseDateString { // if regex for xxxx-MM-DD: $pattern = '/^xxxx-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; - if (preg_match($pattern, $date)) { - Log::debug(sprintf('"%s" is a month/day range.', $date)); + if (false !== preg_match($pattern, $date)) { + app('log')->debug(sprintf('"%s" is a month/day range.', $date)); return true; } - Log::debug(sprintf('"%s" is not a month/day range.', $date)); + app('log')->debug(sprintf('"%s" is not a month/day range.', $date)); return false; } @@ -381,7 +391,7 @@ class ParseDateString */ private function parseMonthDayRange(string $date): array { - Log::debug(sprintf('parseMonthDayRange: Parsed "%s".', $date)); + app('log')->debug(sprintf('parseMonthDayRange: Parsed "%s".', $date)); $parts = explode('-', $date); return [ @@ -399,12 +409,12 @@ class ParseDateString { // if regex for YYYY-xx-DD: $pattern = '/^(19|20)\d\d-xx-(0[1-9]|[12]\d|3[01])$/'; - if (preg_match($pattern, $date)) { - Log::debug(sprintf('"%s" is a day/year range.', $date)); + if (false !== preg_match($pattern, $date)) { + app('log')->debug(sprintf('"%s" is a day/year range.', $date)); return true; } - Log::debug(sprintf('"%s" is not a day/year range.', $date)); + app('log')->debug(sprintf('"%s" is not a day/year range.', $date)); return false; } @@ -418,7 +428,7 @@ class ParseDateString */ private function parseDayYearRange(string $date): array { - Log::debug(sprintf('parseDayYearRange: Parsed "%s".', $date)); + app('log')->debug(sprintf('parseDayYearRange: Parsed "%s".', $date)); $parts = explode('-', $date); return [ @@ -436,12 +446,12 @@ class ParseDateString { // if regex for YYYY-MM-xx: $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-xx$/'; - if (preg_match($pattern, $date)) { - Log::debug(sprintf('"%s" is a month/year range.', $date)); + if (false !== preg_match($pattern, $date)) { + app('log')->debug(sprintf('"%s" is a month/year range.', $date)); return true; } - Log::debug(sprintf('"%s" is not a month/year range.', $date)); + app('log')->debug(sprintf('"%s" is not a month/year range.', $date)); return false; } @@ -455,7 +465,7 @@ class ParseDateString */ protected function parseMonthYearRange(string $date): array { - Log::debug(sprintf('parseMonthYearRange: Parsed "%s".', $date)); + app('log')->debug(sprintf('parseMonthYearRange: Parsed "%s".', $date)); $parts = explode('-', $date); return [ diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 9aa647b04b..2cc76ef3bf 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -76,14 +76,14 @@ class Preferences } /** - * @param User $user - * @param string $name - * @param null|string|int $default + * @param User $user + * @param string $name + * @param null|string|int|bool|array $default * * @return Preference|null * @throws FireflyException */ - public function getForUser(User $user, string $name, $default = null): ?Preference + public function getForUser(User $user, string $name, string | int | bool | null | array $default = null): ?Preference { if ('currencyPreference' === $name) { throw new FireflyException('No longer supports "currencyPreference", please refactor me.'); @@ -168,7 +168,7 @@ class Preferences } if (null === $pref) { $pref = new Preference(); - $pref->user_id = $user->id; + $pref->user_id = (int)$user->id; $pref->name = $name; } $pref->data = $value; @@ -286,7 +286,7 @@ class Preferences $lastActivity = implode(',', $lastActivity); } - return hash('sha256', $lastActivity); + return hash('sha256', (string)$lastActivity); } /** diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index 739a0c3a25..3ffc6ee0c3 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -77,7 +77,7 @@ class BudgetReportGenerator /** @var Account $account */ foreach ($this->accounts as $account) { $accountId = $account->id; - $this->report[$accountId] = $this->report[$accountId] ?? [ + $this->report[$accountId] ??= [ 'name' => $account->name, 'id' => $account->id, 'iban' => $account->iban, @@ -117,7 +117,7 @@ class BudgetReportGenerator $sourceAccountId = $journal['source_account_id']; $this->report[$sourceAccountId]['currencies'][$currencyId] - = $this->report[$sourceAccountId]['currencies'][$currencyId] ?? [ + ??= [ 'currency_id' => $expenses['currency_id'], 'currency_symbol' => $expenses['currency_symbol'], 'currency_name' => $expenses['currency_name'], @@ -126,7 +126,7 @@ class BudgetReportGenerator ]; $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] - = $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] ?? '0'; + ??= '0'; $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] = bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], $journal['amount']); @@ -168,8 +168,8 @@ class BudgetReportGenerator */ private function processBudget(Budget $budget): void { - $budgetId = (int)$budget->id; - $this->report['budgets'][$budgetId] = $this->report['budgets'][$budgetId] ?? [ + $budgetId = $budget->id; + $this->report['budgets'][$budgetId] ??= [ 'budget_id' => $budgetId, 'budget_name' => $budget->name, 'no_budget' => false, @@ -192,16 +192,16 @@ class BudgetReportGenerator */ private function processLimit(Budget $budget, BudgetLimit $limit): void { - $budgetId = (int)$budget->id; - $limitId = (int)$limit->id; + $budgetId = $budget->id; + $limitId = $limit->id; $limitCurrency = $limit->transactionCurrency ?? $this->currency; - $currencyId = (int)$limitCurrency->id; + $currencyId = $limitCurrency->id; $expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection([$budget])); $spent = $expenses[$currencyId]['sum'] ?? '0'; $left = -1 === bccomp(bcadd($limit->amount, $spent), '0') ? '0' : bcadd($limit->amount, $spent); $overspent = 1 === bccomp(bcmul($spent, '-1'), $limit->amount) ? bcadd($spent, $limit->amount) : '0'; - $this->report['budgets'][$budgetId]['budget_limits'][$limitId] = $this->report['budgets'][$budgetId]['budget_limits'][$limitId] ?? [ + $this->report['budgets'][$budgetId]['budget_limits'][$limitId] ??= [ 'budget_limit_id' => $limitId, 'start_date' => $limit->start_date, 'end_date' => $limit->end_date, @@ -220,7 +220,7 @@ class BudgetReportGenerator // make sum information: $this->report['sums'][$currencyId] - = $this->report['sums'][$currencyId] ?? [ + ??= [ 'budgeted' => '0', 'spent' => '0', 'left' => '0', @@ -284,9 +284,9 @@ class BudgetReportGenerator $this->report['sums'][$nbCurrencyId]['currency_decimal_places'] = $nbCurrencyDp; // append other sums because they might be missing: - $this->report['sums'][$nbCurrencyId]['overspent'] = $this->report['sums'][$nbCurrencyId]['overspent'] ?? '0'; - $this->report['sums'][$nbCurrencyId]['left'] = $this->report['sums'][$nbCurrencyId]['left'] ?? '0'; - $this->report['sums'][$nbCurrencyId]['budgeted'] = $this->report['sums'][$nbCurrencyId]['budgeted'] ?? '0'; + $this->report['sums'][$nbCurrencyId]['overspent'] ??= '0'; + $this->report['sums'][$nbCurrencyId]['left'] ??= '0'; + $this->report['sums'][$nbCurrencyId]['budgeted'] ??= '0'; } } @@ -314,7 +314,7 @@ class BudgetReportGenerator if (0 !== bccomp($budgeted, '0') && 0 !== bccomp($totalBudgeted, '0')) { $budgetedPct = round((float)bcmul(bcdiv($budgeted, $totalBudgeted), '100')); } - $this->report['sums'][$currencyId]['budgeted'] = $this->report['sums'][$currencyId]['budgeted'] ?? '0'; + $this->report['sums'][$currencyId]['budgeted'] ??= '0'; $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['spent_pct'] = $spentPct; $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['budgeted_pct'] = $budgetedPct; } @@ -373,6 +373,6 @@ class BudgetReportGenerator $this->blRepository->setUser($user); $this->opsRepository->setUser($user); $this->nbRepository->setUser($user); - $this->currency = app('amount')->getDefaultCurrencyByUser($user); + $this->currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); } } diff --git a/app/Support/Report/Category/CategoryReportGenerator.php b/app/Support/Report/Category/CategoryReportGenerator.php index f8e85f9cb4..6ac8709201 100644 --- a/app/Support/Report/Category/CategoryReportGenerator.php +++ b/app/Support/Report/Category/CategoryReportGenerator.php @@ -107,7 +107,7 @@ class CategoryReportGenerator */ private function processCurrencyArray(int $currencyId, array $currencyRow): void { - $this->report['sums'][$currencyId] = $this->report['sums'][$currencyId] ?? [ + $this->report['sums'][$currencyId] ??= [ 'spent' => '0', 'earned' => '0', 'sum' => '0', @@ -136,7 +136,7 @@ class CategoryReportGenerator private function processCategoryRow(int $currencyId, array $currencyRow, int $categoryId, array $categoryRow): void { $key = sprintf('%s-%s', $currencyId, $categoryId); - $this->report['categories'][$key] = $this->report['categories'][$key] ?? [ + $this->report['categories'][$key] ??= [ 'id' => $categoryId, 'title' => $categoryRow['name'], 'currency_id' => $currencyRow['currency_id'], diff --git a/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php b/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php index 89588bd41b..1cbb6f3920 100644 --- a/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php +++ b/app/Support/Repositories/Recurring/CalculateRangeOccurrences.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Support\Repositories\Recurring; use Carbon\Carbon; -use Illuminate\Support\Facades\Log; /** * Trait CalculateRangeOccurrences @@ -140,30 +139,30 @@ trait CalculateRangeOccurrences { $return = []; $attempts = 0; - Log::debug('Rep is weekly.'); + app('log')->debug('Rep is weekly.'); // monday = 1 // sunday = 7 $dayOfWeek = (int)$moment; - Log::debug(sprintf('DoW in repetition is %d, in mutator is %d', $dayOfWeek, $start->dayOfWeekIso)); + app('log')->debug(sprintf('DoW in repetition is %d, in mutator is %d', $dayOfWeek, $start->dayOfWeekIso)); if ($start->dayOfWeekIso > $dayOfWeek) { // day has already passed this week, add one week: $start->addWeek(); - Log::debug(sprintf('Jump to next week, so mutator is now: %s', $start->format('Y-m-d'))); + app('log')->debug(sprintf('Jump to next week, so mutator is now: %s', $start->format('Y-m-d'))); } // today is wednesday (3), expected is friday (5): add two days. // today is friday (5), expected is monday (1), subtract four days. - Log::debug(sprintf('Mutator is now: %s', $start->format('Y-m-d'))); + app('log')->debug(sprintf('Mutator is now: %s', $start->format('Y-m-d'))); $dayDifference = $dayOfWeek - $start->dayOfWeekIso; $start->addDays($dayDifference); - Log::debug(sprintf('Mutator is now: %s', $start->format('Y-m-d'))); + app('log')->debug(sprintf('Mutator is now: %s', $start->format('Y-m-d'))); while ($start <= $end) { if (0 === $attempts % $skipMod && $start->lte($start) && $end->gte($start)) { - Log::debug('Date is in range of start+end, add to set.'); + app('log')->debug('Date is in range of start+end, add to set.'); $return[] = clone $start; } $attempts++; $start->addWeek(); - Log::debug(sprintf('Mutator is now (end of loop): %s', $start->format('Y-m-d'))); + app('log')->debug(sprintf('Mutator is now (end of loop): %s', $start->format('Y-m-d'))); } return $return; diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php index f4c52b5580..66139faa7e 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Support\Repositories\Recurring; use Carbon\Carbon; -use Illuminate\Support\Facades\Log; /** * Class CalculateXOccurrencesSince @@ -45,7 +44,7 @@ trait CalculateXOccurrencesSince */ protected function getXDailyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $return = []; $mutator = clone $date; $total = 0; @@ -73,10 +72,11 @@ trait CalculateXOccurrencesSince * @param string $moment * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function getXMonthlyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { - Log::debug(sprintf('Now in %s(%s, %s, %d)', __METHOD__, $date->format('Y-m-d'), $afterDate->format('Y-m-d'), $count)); + app('log')->debug(sprintf('Now in %s(%s, %s, %d)', __METHOD__, $date->format('Y-m-d'), $afterDate->format('Y-m-d'), $count)); $return = []; $mutator = clone $date; $total = 0; @@ -84,10 +84,10 @@ trait CalculateXOccurrencesSince $dayOfMonth = (int)$moment; $dayOfMonth = 0 === $dayOfMonth ? 1 : $dayOfMonth; if ($mutator->day > $dayOfMonth) { - Log::debug(sprintf('%d is after %d, add a month. Mutator is now', $mutator->day, $dayOfMonth)); + app('log')->debug(sprintf('%d is after %d, add a month. Mutator is now', $mutator->day, $dayOfMonth)); // day has passed already, add a month. $mutator->addMonth(); - Log::debug(sprintf('%s', $mutator->format('Y-m-d'))); + app('log')->debug(sprintf('%s', $mutator->format('Y-m-d'))); } while ($total < $count) { @@ -99,7 +99,7 @@ trait CalculateXOccurrencesSince } $attempts++; $mutator = $mutator->endOfMonth()->addDay(); - Log::debug(sprintf('Mutator is now %s', $mutator->format('Y-m-d'))); + app('log')->debug(sprintf('Mutator is now %s', $mutator->format('Y-m-d'))); } return $return; @@ -116,10 +116,11 @@ trait CalculateXOccurrencesSince * @param string $moment * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function getXNDomOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $return = []; $total = 0; $attempts = 0; @@ -156,10 +157,11 @@ trait CalculateXOccurrencesSince * @param string $moment * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function getXWeeklyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); $return = []; $total = 0; $attempts = 0; @@ -201,10 +203,11 @@ trait CalculateXOccurrencesSince * @param string $moment * * @return array + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function getXYearlyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { - Log::debug(sprintf('Now in %s(%s, %d, %d, %s)', __METHOD__, $date->format('Y-m-d'), $date->format('Y-m-d'), $count, $skipMod)); + app('log')->debug(sprintf('Now in %s(%s, %d, %d, %s)', __METHOD__, $date->format('Y-m-d'), $date->format('Y-m-d'), $count, $skipMod)); $return = []; $mutator = clone $date; $total = 0; @@ -212,19 +215,19 @@ trait CalculateXOccurrencesSince $date = new Carbon($moment); $date->year = $mutator->year; if ($mutator > $date) { - Log::debug( + app('log')->debug( sprintf('mutator (%s) > date (%s), so add a year to date (%s)', $mutator->format('Y-m-d'), $date->format('Y-m-d'), $date->format('Y-m-d')) ); $date->addYear(); - Log::debug(sprintf('Date is now %s', $date->format('Y-m-d'))); + app('log')->debug(sprintf('Date is now %s', $date->format('Y-m-d'))); } $obj = clone $date; while ($total < $count) { - Log::debug(sprintf('total (%d) < count (%d) so go.', $total, $count)); - Log::debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod)); - Log::debug(sprintf('Obj (%s) gte afterdate (%s)? %s', $obj->format('Y-m-d'), $afterDate->format('Y-m-d'), var_export($obj->gte($afterDate), true))); + app('log')->debug(sprintf('total (%d) < count (%d) so go.', $total, $count)); + app('log')->debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod)); + app('log')->debug(sprintf('Obj (%s) gte afterdate (%s)? %s', $obj->format('Y-m-d'), $afterDate->format('Y-m-d'), var_export($obj->gte($afterDate), true))); if (0 === $attempts % $skipMod && $obj->gte($afterDate)) { - Log::debug('All conditions true, add obj.'); + app('log')->debug('All conditions true, add obj.'); $return[] = clone $obj; $total++; } diff --git a/app/Support/Repositories/Recurring/FiltersWeekends.php b/app/Support/Repositories/Recurring/FiltersWeekends.php index 58da6897ba..efc792a1cc 100644 --- a/app/Support/Repositories/Recurring/FiltersWeekends.php +++ b/app/Support/Repositories/Recurring/FiltersWeekends.php @@ -27,7 +27,6 @@ namespace FireflyIII\Support\Repositories\Recurring; use Carbon\Carbon; use FireflyIII\Models\RecurrenceRepetition; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Trait FiltersWeekends @@ -45,9 +44,9 @@ trait FiltersWeekends */ protected function filterWeekends(RecurrenceRepetition $repetition, array $dates): array { - Log::debug(sprintf('Now in %s', __METHOD__)); - if ((int)$repetition->weekend === RecurrenceRepetition::WEEKEND_DO_NOTHING) { - Log::debug('Repetition will not be filtered on weekend days.'); + app('log')->debug(sprintf('Now in %s', __METHOD__)); + if ($repetition->weekend === RecurrenceRepetition::WEEKEND_DO_NOTHING) { + app('log')->debug('Repetition will not be filtered on weekend days.'); return $dates; } @@ -57,7 +56,7 @@ trait FiltersWeekends $isWeekend = $date->isWeekend(); if (!$isWeekend) { $return[] = clone $date; - //Log::debug(sprintf('Date is %s, not a weekend date.', $date->format('D d M Y'))); + //app('log')->debug(sprintf('Date is %s, not a weekend date.', $date->format('D d M Y'))); continue; } @@ -65,7 +64,7 @@ trait FiltersWeekends if ($repetition->weekend === RecurrenceRepetition::WEEKEND_TO_FRIDAY) { $clone = clone $date; $clone->addDays(5 - $date->dayOfWeekIso); - Log::debug( + app('log')->debug( sprintf('Date is %s, and this is in the weekend, so corrected to %s (Friday).', $date->format('D d M Y'), $clone->format('D d M Y')) ); $return[] = clone $clone; @@ -76,22 +75,22 @@ trait FiltersWeekends if ($repetition->weekend === RecurrenceRepetition::WEEKEND_TO_MONDAY) { $clone = clone $date; $clone->addDays(8 - $date->dayOfWeekIso); - Log::debug( + app('log')->debug( sprintf('Date is %s, and this is in the weekend, so corrected to %s (Monday).', $date->format('D d M Y'), $clone->format('D d M Y')) ); $return[] = $clone; continue; } - //Log::debug(sprintf('Date is %s, removed from final result', $date->format('D d M Y'))); + //app('log')->debug(sprintf('Date is %s, removed from final result', $date->format('D d M Y'))); } // filter unique dates - Log::debug(sprintf('Count before filtering: %d', count($dates))); + app('log')->debug(sprintf('Count before filtering: %d', count($dates))); $collection = new Collection($return); $filtered = $collection->unique(); $return = $filtered->toArray(); - Log::debug(sprintf('Count after filtering: %d', count($return))); + app('log')->debug(sprintf('Count after filtering: %d', count($return))); return $return; } diff --git a/app/Support/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php index 87e18cb43c..30ca44bb27 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -63,11 +63,15 @@ trait UserGroupTrait * @param Authenticatable|User|null $user * * @return void + * @throws FireflyException */ public function setUser(Authenticatable | User | null $user): void { - if (null !== $user) { - $this->user = $user; + if ($user instanceof User) { + $this->user = $user; + if (null === $user->userGroup) { + throw new FireflyException(sprintf('User #%d has no user group.', $user->id)); + } $this->userGroup = $user->userGroup; } } diff --git a/app/Support/Request/AppendsLocationData.php b/app/Support/Request/AppendsLocationData.php index 5d126ffb17..b6e21bf76f 100644 --- a/app/Support/Request/AppendsLocationData.php +++ b/app/Support/Request/AppendsLocationData.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Support\Request; -use Illuminate\Support\Facades\Log; - /** * Trait AppendsLocationData */ @@ -50,7 +48,7 @@ trait AppendsLocationData */ protected function appendLocationData(array $data, ?string $prefix): array { - Log::debug(sprintf('Now in appendLocationData("%s")', $prefix), $data); + app('log')->debug(sprintf('Now in appendLocationData("%s")', $prefix), $data); $data['store_location'] = false; $data['update_location'] = false; $data['remove_location'] = false; @@ -67,7 +65,7 @@ trait AppendsLocationData // for a POST (store), all fields must be present and not NULL. if ($isValidPOST) { - Log::debug('Method is POST and all fields present and not NULL.'); + app('log')->debug('Method is POST and all fields present and not NULL.'); $data['store_location'] = true; $data['longitude'] = $this->convertString($longitudeKey); $data['latitude'] = $this->convertString($latitudeKey); @@ -76,18 +74,18 @@ trait AppendsLocationData // for a PUT (api update) or POST update (UI) if ($isValidPUT) { - Log::debug('Method is PUT and all fields present and not NULL.'); + app('log')->debug('Method is PUT and all fields present and not NULL.'); $data['update_location'] = true; $data['longitude'] = $this->convertString($longitudeKey); $data['latitude'] = $this->convertString($latitudeKey); $data['zoom_level'] = $this->convertString($zoomLevelKey); } if ($isValidEmptyPUT) { - Log::debug('Method is PUT and all fields present and NULL.'); + app('log')->debug('Method is PUT and all fields present and NULL.'); $data['remove_location'] = true; } - Log::debug(sprintf('Returning longitude: "%s", latitude: "%s", zoom level: "%s"', $data['longitude'], $data['latitude'], $data['zoom_level'])); - Log::debug( + app('log')->debug(sprintf('Returning longitude: "%s", latitude: "%s", zoom level: "%s"', $data['longitude'], $data['latitude'], $data['zoom_level'])); + app('log')->debug( sprintf( 'Returning actions: store: %s, update: %s, delete: %s', var_export($data['store_location'], true), @@ -119,40 +117,40 @@ trait AppendsLocationData * * @return bool */ - private function isValidPOST(?string $prefix): bool + private function isValidPost(?string $prefix): bool { - Log::debug('Now in isValidPOST()'); + app('log')->debug('Now in isValidPost()'); $longitudeKey = $this->getLocationKey($prefix, 'longitude'); $latitudeKey = $this->getLocationKey($prefix, 'latitude'); $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); $hasLocationKey = $this->getLocationKey($prefix, 'has_location'); // fields must not be null: if (null !== $this->get($longitudeKey) && null !== $this->get($latitudeKey) && null !== $this->get($zoomLevelKey)) { - Log::debug('All fields present'); + app('log')->debug('All fields present'); // if is POST and route contains API, this is enough: if ('POST' === $this->method() && $this->routeIs('api.v1.*')) { - Log::debug('Is API location'); + app('log')->debug('Is API location'); return true; } // if is POST and route does not contain API, must also have "has_location" = true - if ('POST' === $this->method() && $this->routeIs('*.store') && !$this->routeIs('api.v1.*') && $hasLocationKey) { - Log::debug('Is POST + store route.'); + if ('POST' === $this->method() && $this->routeIs('*.store') && !$this->routeIs('api.v1.*') && '' !== $hasLocationKey) { + app('log')->debug('Is POST + store route.'); $hasLocation = $this->boolean($hasLocationKey); if (true === $hasLocation) { - Log::debug('Has form form location'); + app('log')->debug('Has form form location'); return true; } - Log::debug('Does not have form location'); + app('log')->debug('Does not have form location'); return false; } - Log::debug('Is not POST API or POST form'); + app('log')->debug('Is not POST API or POST form'); return false; } - Log::debug('Fields not present'); + app('log')->debug('Fields not present'); return false; } @@ -180,6 +178,7 @@ trait AppendsLocationData * @param bool $default * * @return mixed + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ abstract public function boolean($key = null, $default = false); @@ -194,36 +193,36 @@ trait AppendsLocationData $latitudeKey = $this->getLocationKey($prefix, 'latitude'); $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); $hasLocationKey = $this->getLocationKey($prefix, 'has_location'); - Log::debug('Now in isValidPUT()'); + app('log')->debug('Now in isValidPUT()'); // all fields must be set: if (null !== $this->get($longitudeKey) && null !== $this->get($latitudeKey) && null !== $this->get($zoomLevelKey)) { - Log::debug('All fields present.'); + app('log')->debug('All fields present.'); // must be PUT and API route: if ('PUT' === $this->method() && $this->routeIs('api.v1.*')) { - Log::debug('Is API location'); + app('log')->debug('Is API location'); return true; } // if POST and not API route, must also have "has_location" // if is POST and route does not contain API, must also have "has_location" = true - if ('POST' === $this->method() && $this->routeIs('*.update') && !$this->routeIs('api.v1.*') && $hasLocationKey) { - Log::debug('Is POST + store route.'); + if ('POST' === $this->method() && $this->routeIs('*.update') && !$this->routeIs('api.v1.*') && '' !== $hasLocationKey) { + app('log')->debug('Is POST + store route.'); $hasLocation = $this->boolean($hasLocationKey); if (true === $hasLocation) { - Log::debug('Has form location data + has_location'); + app('log')->debug('Has form location data + has_location'); return true; } - Log::debug('Does not have form location'); + app('log')->debug('Does not have form location'); return false; } - Log::debug('Is not POST API or POST form'); + app('log')->debug('Is not POST API or POST form'); return false; } - Log::debug('Fields not present'); + app('log')->debug('Fields not present'); return false; } @@ -240,9 +239,9 @@ trait AppendsLocationData $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); return ( - null === $this->get($longitudeKey) - && null === $this->get($latitudeKey) - && null === $this->get($zoomLevelKey)) + null === $this->get($longitudeKey) + && null === $this->get($latitudeKey) + && null === $this->get($zoomLevelKey)) && ( 'PUT' === $this->method() || ('POST' === $this->method() && $this->routeIs('*.update')) diff --git a/app/Support/Request/ChecksLogin.php b/app/Support/Request/ChecksLogin.php index e0d236f993..fa610fb7c5 100644 --- a/app/Support/Request/ChecksLogin.php +++ b/app/Support/Request/ChecksLogin.php @@ -26,7 +26,6 @@ namespace FireflyIII\Support\Request; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\UserGroup; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Trait ChecksLogin @@ -40,13 +39,13 @@ trait ChecksLogin */ public function authorize(): bool { - Log::debug(sprintf('Now in %s', __METHOD__)); + app('log')->debug(sprintf('Now in %s', __METHOD__)); // Only allow logged-in users $check = auth()->check(); if (!$check) { return false; } - if (!property_exists($this, 'acceptedRoles')) { + if (!property_exists($this, 'acceptedRoles')) { // @phpstan-ignore-line app('log')->debug('Request class has no acceptedRoles array'); return true; // check for false already took place. } @@ -61,7 +60,8 @@ trait ChecksLogin /** @var UserRoleEnum $role */ foreach ($this->acceptedRoles as $role) { // system owner cannot overrule this, MUST be member of the group. - if ($user->hasRoleInGroup($userGroup, $role, true, false)) { + $access = $user->hasRoleInGroupOrOwner($userGroup, $role); + if ($access) { return true; } } @@ -79,8 +79,8 @@ trait ChecksLogin /** @var User $user */ $user = auth()->user(); app('log')->debug('Now in getUserGroup()'); - /** @var UserGroup $userGroup */ - $userGroup = $this->route()->parameter('userGroup'); + /** @var UserGroup|null $userGroup */ + $userGroup = $this->route()?->parameter('userGroup'); if (null === $userGroup) { app('log')->debug('Request class has no userGroup parameter, but perhaps there is a parameter.'); $userGroupId = (int)$this->get('user_group_id'); diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index 89c7d4b70c..59765c94c4 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -28,67 +28,14 @@ use Carbon\Exceptions\InvalidDateException; use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Trait ConvertsDataTypes */ trait ConvertsDataTypes { - /** - * Return integer value. - * - * @param string $field - * - * @return int - */ - public function convertInteger(string $field): int - { - return (int)$this->get($field); - } - - /** - * Abstract method that always exists in the Request classes that use this - * trait, OR a stub needs to be added by any other class that uses this train. - * - * @param string $key - * @param mixed|null $default - * - * @return mixed - */ - abstract public function get(string $key, mixed $default = null): mixed; - - /** - * Return string value. - * - * @param string $field - * - * @return string - */ - public function convertString(string $field): string - { - $entry = $this->get($field); - if (!is_scalar($entry)) { - return ''; - } - return $this->clearString((string)$entry, false); - } - - /** - * @param string|null $string - * @param bool $keepNewlines - * - * @return string|null - */ - public function clearString(?string $string, bool $keepNewlines = true): ?string - { - if (null === $string) { - return null; - } - if ('' === $string) { - return ''; - } - $search = [ + private array $characters + = [ "\0", // NUL "\f", // form feed "\v", // vertical tab @@ -137,24 +84,91 @@ trait ConvertsDataTypes "\u{202F}", // narrow no-break space "\u{3000}", // ideographic space "\u{FEFF}", // zero width no -break space + "\r", // carriage return ]; - $replace = "\x20"; // plain old normal space - $string = str_replace($search, $replace, $string); - $secondSearch = $keepNewlines ? ["\r"] : ["\r", "\n", "\t", "\036", "\025"]; - $string = str_replace($secondSearch, '', $string); + /** + * Return integer value. + * + * @param string $field + * + * @return int + */ + public function convertInteger(string $field): int + { + return (int)$this->get($field); + } + + /** + * Abstract method that always exists in the Request classes that use this + * trait, OR a stub needs to be added by any other class that uses this train. + * + * @param string $key + * @param mixed|null $default + * + * @return mixed + */ + abstract public function get(string $key, mixed $default = null): mixed; + + /** + * Return string value. + * + * @param string $field + * + * @return string + */ + public function convertString(string $field): string + { + $entry = $this->get($field); + if (!is_scalar($entry)) { + return ''; + } + return (string)$this->clearString((string)$entry); + } + + /** + * @param string|null $string + * + * @return string|null + */ + public function clearString(?string $string): ?string + { + $string = $this->clearStringKeepNewlines($string); - // clear zalgo text (TODO also in API v2) - $string = preg_replace('/(\pM{2})\pM+/u', '\1', $string); if (null === $string) { return null; } if ('' === $string) { return ''; } + + // then remove newlines too: + $string = str_replace(["\r", "\n", "\t", "\036", "\025"], '', $string); + return trim($string); } + /** + * @param string|null $string + * + * @return string|null + */ + public function clearStringKeepNewlines(?string $string): ?string + { + if (null === $string) { + return null; + } + if ('' === $string) { + return ''; + } + $string = str_replace($this->characters, "\x20", $string); + + // clear zalgo text (TODO also in API v2) + $string = preg_replace('/(\pM{2})\pM+/u', '\1', $string); + + return trim((string)$string); + } + /** * TODO duplicate, see SelectTransactionsRequest * @@ -167,7 +181,7 @@ trait ConvertsDataTypes /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); - if (method_exists($this, 'validateUserGroup')) { + if (method_exists($this, 'validateUserGroup')) { // @phpstan-ignore-line $userGroup = $this->validateUserGroup($this); if (null !== $userGroup) { $repository->setUserGroup($userGroup); @@ -200,7 +214,7 @@ trait ConvertsDataTypes */ public function stringWithNewlines(string $field): string { - return $this->clearString((string)($this->get($field) ?? '')); + return (string)$this->clearStringKeepNewlines((string)($this->get($field) ?? '')); } /** @@ -256,7 +270,7 @@ trait ConvertsDataTypes */ protected function convertDateTime(?string $string): ?Carbon { - $value = $this->get($string); + $value = $this->get((string)$string); if (null === $value) { return null; } @@ -267,25 +281,29 @@ trait ConvertsDataTypes // probably a date format. try { $carbon = Carbon::createFromFormat('Y-m-d', $value); - } catch (InvalidDateException $e) { - Log::error(sprintf('[1] "%s" is not a valid date: %s', $value, $e->getMessage())); + } catch (InvalidDateException $e) { // @phpstan-ignore-line + app('log')->error(sprintf('[1] "%s" is not a valid date: %s', $value, $e->getMessage())); return null; - } catch (InvalidFormatException $e) { - Log::error(sprintf('[2] "%s" is of an invalid format: %s', $value, $e->getMessage())); + } catch (InvalidFormatException $e) { // @phpstan-ignore-line + app('log')->error(sprintf('[2] "%s" is of an invalid format: %s', $value, $e->getMessage())); return null; } + if (false === $carbon) { + app('log')->error(sprintf('[2] "%s" is of an invalid format.', $value)); + return null; + } return $carbon; } // is an atom string, I hope? try { $carbon = Carbon::parse($value); - } catch (InvalidDateException $e) { - Log::error(sprintf('[3] "%s" is not a valid date or time: %s', $value, $e->getMessage())); + } catch (InvalidDateException $e) { // @phpstan-ignore-line + app('log')->error(sprintf('[3] "%s" is not a valid date or time: %s', $value, $e->getMessage())); return null; } catch (InvalidFormatException $e) { - Log::error(sprintf('[4] "%s" is of an invalid format: %s', $value, $e->getMessage())); + app('log')->error(sprintf('[4] "%s" is of an invalid format: %s', $value, $e->getMessage())); return null; } @@ -329,11 +347,11 @@ trait ConvertsDataTypes // @ignoreException } if (null === $carbon) { - Log::debug(sprintf('Invalid date: %s', $string)); + app('log')->debug(sprintf('Invalid date: %s', $string)); return null; } - Log::debug(sprintf('Date object: %s (%s)', $carbon->toW3cString(), $carbon->getTimezone())); + app('log')->debug(sprintf('Date object: %s (%s)', $carbon->toW3cString(), $carbon->getTimezone())); return $carbon; } @@ -350,9 +368,9 @@ trait ConvertsDataTypes { $return = []; foreach ($fields as $field => $info) { - if ($this->has($info[0])) { + if (true === $this->has($info[0])) { $method = $info[1]; - $return[$field] = $this->$method($info[0]); + $return[$field] = $this->$method($info[0]); // @phpstan-ignore-line } } @@ -380,12 +398,12 @@ trait ConvertsDataTypes { $result = null; try { - $result = $this->get($field) ? new Carbon($this->get($field), config('app.timezone')) : null; + $result = '' !== (string)$this->get($field) ? new Carbon((string)$this->get($field), config('app.timezone')) : null; } catch (InvalidFormatException $e) { // @ignoreException } if (null === $result) { - Log::debug(sprintf('Exception when parsing date "%s".', $this->get($field))); + app('log')->debug(sprintf('Exception when parsing date "%s".', $this->get($field))); } return $result; @@ -419,7 +437,7 @@ trait ConvertsDataTypes */ protected function nullableInteger(string $field): ?int { - if (!$this->has($field)) { + if (false === $this->has($field)) { return null; } diff --git a/app/Support/Search/AccountSearch.php b/app/Support/Search/AccountSearch.php index 7ca86aa79e..2f2c838d27 100644 --- a/app/Support/Search/AccountSearch.php +++ b/app/Support/Search/AccountSearch.php @@ -35,15 +35,15 @@ use Illuminate\Support\Collection; class AccountSearch implements GenericSearchInterface { /** @var string */ - public const SEARCH_ALL = 'all'; + public const string SEARCH_ALL = 'all'; /** @var string */ - public const SEARCH_IBAN = 'iban'; + public const string SEARCH_IBAN = 'iban'; /** @var string */ - public const SEARCH_ID = 'id'; + public const string SEARCH_ID = 'id'; /** @var string */ - public const SEARCH_NAME = 'name'; + public const string SEARCH_NAME = 'name'; /** @var string */ - public const SEARCH_NUMBER = 'number'; + public const string SEARCH_NUMBER = 'number'; private string $field; private string $query; private array $types; @@ -69,7 +69,7 @@ class AccountSearch implements GenericSearchInterface default: case self::SEARCH_ALL: $searchQuery->where( - static function (Builder $q) use ($like) { + static function (Builder $q) use ($like) { // @phpstan-ignore-line $q->where('accounts.id', 'LIKE', $like); $q->orWhere('accounts.name', 'LIKE', $like); $q->orWhere('accounts.iban', 'LIKE', $like); @@ -77,7 +77,7 @@ class AccountSearch implements GenericSearchInterface ); // meta data: $searchQuery->orWhere( - static function (Builder $q) use ($originalQuery) { + static function (Builder $q) use ($originalQuery) { // @phpstan-ignore-line $json = json_encode($originalQuery, JSON_THROW_ON_ERROR); $q->where('account_meta.name', '=', 'account_number'); $q->where('account_meta.data', 'LIKE', $json); @@ -96,7 +96,7 @@ class AccountSearch implements GenericSearchInterface case self::SEARCH_NUMBER: // meta data: $searchQuery->Where( - static function (Builder $q) use ($originalQuery) { + static function (Builder $q) use ($originalQuery) { // @phpstan-ignore-line $json = json_encode($originalQuery, JSON_THROW_ON_ERROR); $q->where('account_meta.name', 'account_number'); $q->where('account_meta.data', $json); @@ -139,7 +139,7 @@ class AccountSearch implements GenericSearchInterface */ public function setUser(User | Authenticatable | null $user): void { - if (null !== $user) { + if ($user instanceof User) { $this->user = $user; } } diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index 470067caea..083c0e3d83 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -24,6 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Support\Search; use Carbon\Carbon; +use FireflyIII\Enums\SearchDirection; +use FireflyIII\Enums\StringPosition; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; @@ -54,8 +56,9 @@ use Gdbots\QueryParser\Node\Word; use Gdbots\QueryParser\QueryParser; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; use LogicException; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use TypeError; /** @@ -70,6 +73,8 @@ class OperatorQuerySearch implements SearchInterface private CategoryRepositoryInterface $categoryRepository; private GroupCollectorInterface $collector; private CurrencyRepositoryInterface $currencyRepository; + private array $excludeTags; + private array $includeTags; private array $invalidOperators; private int $limit; private Collection $operators; @@ -87,10 +92,12 @@ class OperatorQuerySearch implements SearchInterface */ public function __construct() { - Log::debug('Constructed OperatorQuerySearch'); + app('log')->debug('Constructed OperatorQuerySearch'); $this->operators = new Collection(); $this->page = 1; $this->words = []; + $this->excludeTags = []; + $this->includeTags = []; $this->prohibitedWords = []; $this->invalidOperators = []; $this->limit = 25; @@ -149,22 +156,24 @@ class OperatorQuerySearch implements SearchInterface * @inheritDoc * @throws FireflyException */ - public function parseQuery(string $query) + public function parseQuery(string $query): void { - Log::debug(sprintf('Now in parseQuery(%s)', $query)); + app('log')->debug(sprintf('Now in parseQuery(%s)', $query)); $parser = new QueryParser(); try { $query1 = $parser->parse($query); } catch (TypeError | LogicException $e) { - Log::error($e->getMessage()); - Log::error(sprintf('Could not parse search: "%s".', $query)); + app('log')->error($e->getMessage()); + app('log')->error(sprintf('Could not parse search: "%s".', $query)); throw new FireflyException(sprintf('Invalid search value "%s". See the logs.', e($query)), 0, $e); } - Log::debug(sprintf('Found %d node(s)', count($query1->getNodes()))); + app('log')->debug(sprintf('Found %d node(s)', count($query1->getNodes()))); foreach ($query1->getNodes() as $searchNode) { $this->handleSearchNode($searchNode); } + $this->parseTagInstructions(); + $this->collector->setSearchWords($this->words); $this->collector->excludeSearchWords($this->prohibitedWords); @@ -178,10 +187,10 @@ class OperatorQuerySearch implements SearchInterface private function handleSearchNode(Node $searchNode): void { $class = get_class($searchNode); - Log::debug(sprintf('Now in handleSearchNode(%s)', $class)); + app('log')->debug(sprintf('Now in handleSearchNode(%s)', $class)); switch ($class) { default: - Log::error(sprintf('Cannot handle node %s', $class)); + app('log')->error(sprintf('Cannot handle node %s', $class)); throw new FireflyException(sprintf('Firefly III search cant handle "%s"-nodes', $class)); case Subquery::class: // loop all notes in subquery: @@ -199,11 +208,11 @@ class OperatorQuerySearch implements SearchInterface case Emoji::class: case Mention::class: $allWords = (string)$searchNode->getValue(); - Log::debug(sprintf('Add words "%s" to search string, because Node class is "%s"', $allWords, $class)); + app('log')->debug(sprintf('Add words "%s" to search string, because Node class is "%s"', $allWords, $class)); $this->words[] = $allWords; break; case Field::class: - Log::debug(sprintf('Now handle Node class %s', $class)); + app('log')->debug(sprintf('Now handle Node class %s', $class)); /** @var Field $searchNode */ // used to search for x:y $operator = strtolower($searchNode->getValue()); @@ -212,8 +221,14 @@ class OperatorQuerySearch implements SearchInterface $context = config(sprintf('search.operators.%s.needs_context', $operator)); // is an operator that needs no context, and value is false, then prohibited = true. - if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context) { + if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context && !$prohibited) { $prohibited = true; + $value = 'true'; + } + // if the operator is prohibited, but the value is false, do an uno reverse + if ('false' === $value && $prohibited && in_array($operator, $this->validOperators, true) && false === $context) { + $prohibited = false; + $value = 'true'; } // must be valid operator: @@ -227,10 +242,10 @@ class OperatorQuerySearch implements SearchInterface 'prohibited' => $prohibited, ] ); - Log::debug(sprintf('Added operator type "%s"', $operator)); + app('log')->debug(sprintf('Added operator type "%s"', $operator)); } if (!in_array($operator, $this->validOperators, true)) { - Log::debug(sprintf('Added INVALID operator type "%s"', $operator)); + app('log')->debug(sprintf('Added INVALID operator type "%s"', $operator)); $this->invalidOperators[] = [ 'type' => $operator, 'value' => (string)$value, @@ -242,126 +257,127 @@ class OperatorQuerySearch implements SearchInterface /** * * @throws FireflyException + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function updateCollector(string $operator, string $value, bool $prohibited): bool { if ($prohibited) { - Log::debug(sprintf('Operator "%s" is now "%s"', $operator, sprintf('-%s', $operator))); + app('log')->debug(sprintf('Operator "%s" is now "%s"', $operator, sprintf('-%s', $operator))); $operator = sprintf('-%s', $operator); } - Log::debug(sprintf('Now in updateCollector("%s", "%s")', $operator, $value)); + app('log')->debug(sprintf('Now in updateCollector("%s", "%s")', $operator, $value)); // check if alias, replace if necessary: $operator = self::getRootOperator($operator); switch ($operator) { default: - Log::error(sprintf('No such operator: %s', $operator)); + app('log')->error(sprintf('No such operator: %s', $operator)); throw new FireflyException(sprintf('Unsupported search operator: "%s"', $operator)); - // some search operators are ignored, basically: + // some search operators are ignored, basically: case 'user_action': - Log::info(sprintf('Ignore search operator "%s"', $operator)); + app('log')->info(sprintf('Ignore search operator "%s"', $operator)); return false; - // - // all account related searches: - // + // + // all account related searches: + // case 'account_is': - $this->searchAccount($value, 3, 4); + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS); break; case '-account_is': - $this->searchAccount($value, 3, 4, true); + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS, true); break; case 'account_contains': - $this->searchAccount($value, 3, 3); + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::CONTAINS); break; case '-account_contains': - $this->searchAccount($value, 3, 3, true); + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::CONTAINS, true); break; case 'account_ends': - $this->searchAccount($value, 3, 2); + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::ENDS); break; case '-account_ends': - $this->searchAccount($value, 3, 2, true); + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::ENDS, true); break; case 'account_starts': - $this->searchAccount($value, 3, 1); + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::STARTS); break; case '-account_starts': - $this->searchAccount($value, 3, 1, true); + $this->searchAccount($value, SearchDirection::BOTH, StringPosition::STARTS, true); break; case 'account_nr_is': - $this->searchAccountNr($value, 3, 4); + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::IS); break; case '-account_nr_is': - $this->searchAccountNr($value, 3, 4, true); + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::IS, true); break; case 'account_nr_contains': - $this->searchAccountNr($value, 3, 3); + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::CONTAINS); break; case '-account_nr_contains': - $this->searchAccountNr($value, 3, 3, true); + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::CONTAINS, true); break; case 'account_nr_ends': - $this->searchAccountNr($value, 3, 2); + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::ENDS); break; case '-account_nr_ends': - $this->searchAccountNr($value, 3, 2, true); + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::ENDS, true); break; case 'account_nr_starts': - $this->searchAccountNr($value, 3, 1); + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::STARTS); break; case '-account_nr_starts': - $this->searchAccountNr($value, 3, 1, true); + $this->searchAccountNr($value, SearchDirection::BOTH, StringPosition::STARTS, true); break; case 'source_account_starts': - $this->searchAccount($value, 1, 1); + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::STARTS); break; case '-source_account_starts': - $this->searchAccount($value, 1, 1, true); + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::STARTS, true); break; case 'source_account_ends': - $this->searchAccount($value, 1, 2); + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::ENDS); break; case '-source_account_ends': - $this->searchAccount($value, 1, 2, true); + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::ENDS, true); break; case 'source_account_is': - $this->searchAccount($value, 1, 4); + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::IS); break; case '-source_account_is': - $this->searchAccount($value, 1, 4, true); + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::IS, true); break; case 'source_account_nr_starts': - $this->searchAccountNr($value, 1, 1); + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::STARTS); break; case '-source_account_nr_starts': - $this->searchAccountNr($value, 1, 1, true); + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::STARTS, true); break; case 'source_account_nr_ends': - $this->searchAccountNr($value, 1, 2); + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::ENDS); break; case '-source_account_nr_ends': - $this->searchAccountNr($value, 1, 2, true); + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::ENDS, true); break; case 'source_account_nr_is': - $this->searchAccountNr($value, 1, 4); + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::IS); break; case '-source_account_nr_is': - $this->searchAccountNr($value, 1, 4, true); + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::IS, true); break; case 'source_account_nr_contains': - $this->searchAccountNr($value, 1, 3); + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::CONTAINS); break; case '-source_account_nr_contains': - $this->searchAccountNr($value, 1, 3, true); + $this->searchAccountNr($value, SearchDirection::SOURCE, StringPosition::CONTAINS, true); break; case 'source_account_contains': - $this->searchAccount($value, 1, 3); + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::CONTAINS); break; case '-source_account_contains': - $this->searchAccount($value, 1, 3, true); + $this->searchAccount($value, SearchDirection::SOURCE, StringPosition::CONTAINS, true); break; case 'source_account_id': $account = $this->accountRepository->find((int)$value); @@ -400,52 +416,52 @@ class OperatorQuerySearch implements SearchInterface $this->collector->excludeIds($parts); break; case 'destination_account_starts': - $this->searchAccount($value, 2, 1); + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::STARTS); break; case '-destination_account_starts': - $this->searchAccount($value, 2, 1, true); + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::STARTS, true); break; case 'destination_account_ends': - $this->searchAccount($value, 2, 2); + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::ENDS); break; case '-destination_account_ends': - $this->searchAccount($value, 2, 2, true); + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::ENDS, true); break; case 'destination_account_nr_starts': - $this->searchAccountNr($value, 2, 1); + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::STARTS); break; case '-destination_account_nr_starts': - $this->searchAccountNr($value, 2, 1, true); + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::STARTS, true); break; case 'destination_account_nr_ends': - $this->searchAccountNr($value, 2, 2); + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::ENDS); break; case '-destination_account_nr_ends': - $this->searchAccountNr($value, 2, 2, true); + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::ENDS, true); break; case 'destination_account_nr_is': - $this->searchAccountNr($value, 2, 4); + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::IS); break; case '-destination_account_nr_is': - $this->searchAccountNr($value, 2, 4, true); + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::IS, true); break; case 'destination_account_is': - $this->searchAccount($value, 2, 4); + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::IS); break; case '-destination_account_is': - $this->searchAccount($value, 2, 4, true); + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::IS, true); break; case 'destination_account_nr_contains': - $this->searchAccountNr($value, 2, 3); + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::CONTAINS); break; case '-destination_account_nr_contains': - $this->searchAccountNr($value, 2, 3, true); + $this->searchAccountNr($value, SearchDirection::DESTINATION, StringPosition::CONTAINS, true); break; case 'destination_account_contains': - $this->searchAccount($value, 2, 3); + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::CONTAINS); break; case '-destination_account_contains': - $this->searchAccount($value, 2, 3, true); + $this->searchAccount($value, SearchDirection::DESTINATION, StringPosition::CONTAINS, true); break; case 'destination_account_id': $account = $this->accountRepository->find((int)$value); @@ -497,9 +513,9 @@ class OperatorQuerySearch implements SearchInterface $this->collector->findNothing(); } break; - // - // cash account - // + // + // cash account + // case 'source_is_cash': $account = $this->getCashAccount(); $this->collector->setSourceAccounts(new Collection([$account])); @@ -524,9 +540,9 @@ class OperatorQuerySearch implements SearchInterface $account = $this->getCashAccount(); $this->collector->excludeAccounts(new Collection([$account])); break; - // - // description - // + // + // description + // case 'description_starts': $this->collector->descriptionStarts([$value]); break; @@ -553,9 +569,9 @@ class OperatorQuerySearch implements SearchInterface case '-description_is': $this->collector->descriptionIsNot($value); break; - // - // currency - // + // + // currency + // case 'currency_is': $currency = $this->findCurrency($value); if (null !== $currency) { @@ -592,21 +608,21 @@ class OperatorQuerySearch implements SearchInterface $this->collector->findNothing(); } break; - // - // attachments - // + // + // attachments + // case 'has_attachments': case '-has_no_attachments': - Log::debug('Set collector to filter on attachments.'); + app('log')->debug('Set collector to filter on attachments.'); $this->collector->hasAttachments(); break; case 'has_no_attachments': case '-has_attachments': - Log::debug('Set collector to filter on NO attachments.'); + app('log')->debug('Set collector to filter on NO attachments.'); $this->collector->hasNoAttachments(); break; - // - // categories + // + // categories case '-has_any_category': case 'has_no_category': $this->collector->withoutCategory(); @@ -684,9 +700,9 @@ class OperatorQuerySearch implements SearchInterface $this->collector->findNothing(); } break; - // - // budgets - // + // + // budgets + // case '-has_any_budget': case 'has_no_budget': $this->collector->withoutBudget(); @@ -765,9 +781,9 @@ class OperatorQuerySearch implements SearchInterface $this->collector->findNothing(); } break; - // - // bill - // + // + // bill + // case '-has_any_bill': case 'has_no_bill': $this->collector->withoutBill(); @@ -844,9 +860,9 @@ class OperatorQuerySearch implements SearchInterface $this->collector->findNothing(); } break; - // - // tags - // + // + // tags + // case '-has_any_tag': case 'has_no_tag': $this->collector->withoutTags(); @@ -859,24 +875,93 @@ class OperatorQuerySearch implements SearchInterface case 'tag_is': $result = $this->tagRepository->findByTag($value); if (null !== $result) { - $this->collector->setTags(new Collection([$result])); + $this->includeTags[] = $result->id; + $this->includeTags = array_unique($this->includeTags); } // no tags found means search must result in nothing. if (null === $result) { - Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); $this->collector->findNothing(); } break; + case 'tag_contains': + $tags = $this->tagRepository->searchTag($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->includeTags = array_unique(array_merge($this->includeTags, $ids)); + } + break; + case 'tag_starts': + $tags = $this->tagRepository->tagStartsWith($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->includeTags = array_unique(array_merge($this->includeTags, $ids)); + } + break; + case '-tag_starts': + $tags = $this->tagRepository->tagStartsWith($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->excludeTags = array_unique(array_merge($this->includeTags, $ids)); + } + break; + case 'tag_ends': + $tags = $this->tagRepository->tagEndsWith($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->includeTags = array_unique(array_merge($this->includeTags, $ids)); + } + break; + case '-tag_ends': + $tags = $this->tagRepository->tagEndsWith($value); + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->excludeTags = array_unique(array_merge($this->includeTags, $ids)); + } + break; + case '-tag_contains': + $tags = $this->tagRepository->searchTag($value)->keyBy('id'); + + if (0 === $tags->count()) { + app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); + $this->collector->findNothing(); + } + if ($tags->count() > 0) { + $ids = array_values($tags->pluck('id')->toArray()); + $this->excludeTags = array_unique(array_merge($this->excludeTags, $ids)); + } + break; case '-tag_is': case 'tag_is_not': - $result = $this->tagRepository->searchTag($value); - if ($result->count() > 0) { - $this->collector->setWithoutSpecificTags($result); + $result = $this->tagRepository->findByTag($value); + if (null !== $result) { + $this->excludeTags[] = $result->id; + $this->excludeTags = array_unique($this->excludeTags); } break; - // - // notes - // + // + // notes + // case 'notes_contains': $this->collector->notesContain($value); break; @@ -915,93 +1000,93 @@ class OperatorQuerySearch implements SearchInterface case '-reconciled': $this->collector->isNotReconciled(); break; - // - // amount - // + // + // amount + // case 'amount_is': // strip comma's, make dots. - Log::debug(sprintf('Original value "%s"', $value)); - $value = str_replace(',', '.', (string)$value); + app('log')->debug(sprintf('Original value "%s"', $value)); + $value = str_replace(',', '.', $value); $amount = app('steam')->positive($value); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountIs($amount); break; case '-amount_is': // strip comma's, make dots. - Log::debug(sprintf('Original value "%s"', $value)); - $value = str_replace(',', '.', (string)$value); + app('log')->debug(sprintf('Original value "%s"', $value)); + $value = str_replace(',', '.', $value); $amount = app('steam')->positive($value); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountIsNot($amount); break; case 'foreign_amount_is': // strip comma's, make dots. - $value = str_replace(',', '.', (string)$value); + $value = str_replace(',', '.', $value); $amount = app('steam')->positive($value); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountIs($amount); break; case '-foreign_amount_is': // strip comma's, make dots. - $value = str_replace(',', '.', (string)$value); + $value = str_replace(',', '.', $value); $amount = app('steam')->positive($value); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountIsNot($amount); break; case '-amount_more': case 'amount_less': // strip comma's, make dots. - $value = str_replace(',', '.', (string)$value); + $value = str_replace(',', '.', $value); $amount = app('steam')->positive($value); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountLess($amount); break; case '-foreign_amount_more': case 'foreign_amount_less': // strip comma's, make dots. - $value = str_replace(',', '.', (string)$value); + $value = str_replace(',', '.', $value); $amount = app('steam')->positive($value); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountLess($amount); break; case '-amount_less': case 'amount_more': - Log::debug(sprintf('Now handling operator "%s"', $operator)); + app('log')->debug(sprintf('Now handling operator "%s"', $operator)); // strip comma's, make dots. - $value = str_replace(',', '.', (string)$value); + $value = str_replace(',', '.', $value); $amount = app('steam')->positive($value); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->amountMore($amount); break; case '-foreign_amount_less': case 'foreign_amount_more': - Log::debug(sprintf('Now handling operator "%s"', $operator)); + app('log')->debug(sprintf('Now handling operator "%s"', $operator)); // strip comma's, make dots. - $value = str_replace(',', '.', (string)$value); + $value = str_replace(',', '.', $value); $amount = app('steam')->positive($value); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); $this->collector->foreignAmountMore($amount); break; - // - // transaction type - // + // + // transaction type + // case 'transaction_type': $this->collector->setTypes([ucfirst($value)]); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); break; case '-transaction_type': $this->collector->excludeTypes([ucfirst($value)]); - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); break; - // - // dates - // + // + // dates + // case '-date_on': case 'date_on': $range = $this->parseDateRange($operator, $value); @@ -1116,44 +1201,44 @@ class OperatorQuerySearch implements SearchInterface case 'created_at_on': case '-created_at_on': - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); $range = $this->parseDateRange($operator, $value); $this->setExactObjectDateParams('created_at', $range, $prohibited); return false; case 'created_at_before': case '-created_at_after': - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); $range = $this->parseDateRange($operator, $value); $this->setObjectDateBeforeParams('created_at', $range); return false; case 'created_at_after': case '-created_at_before': - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); $range = $this->parseDateRange($operator, $value); $this->setObjectDateAfterParams('created_at', $range); return false; case 'updated_at_on': case '-updated_at_on': - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); $range = $this->parseDateRange($operator, $value); $this->setExactObjectDateParams('updated_at', $range, $prohibited); return false; case 'updated_at_before': case '-updated_at_after': - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); $range = $this->parseDateRange($operator, $value); $this->setObjectDateBeforeParams('updated_at', $range); return false; case 'updated_at_after': case '-updated_at_before': - Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); + app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); $range = $this->parseDateRange($operator, $value); $this->setObjectDateAfterParams('updated_at', $range); return false; - // - // external URL - // + // + // external URL + // case '-any_external_url': case 'no_external_url': $this->collector->withoutExternalUrl(); @@ -1196,9 +1281,9 @@ class OperatorQuerySearch implements SearchInterface $this->collector->externalUrlDoesNotEnd($value); break; - // - // other fields - // + // + // other fields + // case 'external_id_is': $this->collector->setExternalId($value); break; @@ -1341,11 +1426,11 @@ class OperatorQuerySearch implements SearchInterface if (str_starts_with($original, '-')) { $return = sprintf('-%s', $config['alias_for']); } - Log::debug(sprintf('"%s" is an alias for "%s", so return that instead.', $original, $return)); + app('log')->debug(sprintf('"%s" is an alias for "%s", so return that instead.', $original, $return)); return $return; } - Log::debug(sprintf('"%s" is not an alias.', $operator)); + app('log')->debug(sprintf('"%s" is not an alias.', $operator)); return $original; } @@ -1354,14 +1439,15 @@ class OperatorQuerySearch implements SearchInterface * searchDirection: 1 = source (default), 2 = destination, 3 = both * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is * - * @param string $value - * @param int $searchDirection - * @param int $stringPosition - * @param bool $prohibited + * @param string $value + * @param SearchDirection $searchDirection + * @param StringPosition $stringPosition + * @param bool $prohibited + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ - private function searchAccount(string $value, int $searchDirection, int $stringPosition, bool $prohibited = false): void + private function searchAccount(string $value, SearchDirection $searchDirection, StringPosition $stringPosition, bool $prohibited = false): void { - Log::debug(sprintf('searchAccount("%s", %d, %d)', $value, $stringPosition, $searchDirection)); + app('log')->debug(sprintf('searchAccount("%s", %s, %s)', $value, $stringPosition->name, $searchDirection->name)); // search direction (default): for source accounts $searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE]; @@ -1371,7 +1457,7 @@ class OperatorQuerySearch implements SearchInterface } // search direction: for destination accounts - if (2 === $searchDirection) { + if (SearchDirection::DESTINATION === $searchDirection) { // destination // destination can be $searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE]; $collectorMethod = 'setDestinationAccounts'; @@ -1380,7 +1466,7 @@ class OperatorQuerySearch implements SearchInterface } } // either account could be: - if (3 === $searchDirection) { + if (SearchDirection::BOTH === $searchDirection) { $searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE, AccountType::REVENUE]; $collectorMethod = 'setAccounts'; if ($prohibited) { @@ -1391,53 +1477,55 @@ class OperatorQuerySearch implements SearchInterface $stringMethod = 'str_starts_with'; // string position: ends with: - if (2 === $stringPosition) { + if (StringPosition::ENDS === $stringPosition) { $stringMethod = 'str_ends_with'; } - if (3 === $stringPosition) { + if (StringPosition::CONTAINS === $stringPosition) { $stringMethod = 'str_contains'; } - if (4 === $stringPosition) { + if (StringPosition::IS === $stringPosition) { $stringMethod = 'stringIsEqual'; } // get accounts: $accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337); if (0 === $accounts->count()) { - Log::debug('Found zero accounts, search for non existing account, NO results will be returned.'); + app('log')->debug('Found zero accounts, search for non existing account, NO results will be returned.'); $this->collector->findNothing(); return; } - Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); + app('log')->debug(sprintf('Found %d accounts, will filter.', $accounts->count())); $filtered = $accounts->filter( - function (Account $account) use ($value, $stringMethod) { + static function (Account $account) use ($value, $stringMethod) { return $stringMethod(strtolower($account->name), strtolower($value)); } ); if (0 === $filtered->count()) { - Log::debug('Left with zero accounts, so cannot find anything, NO results will be returned.'); + app('log')->debug('Left with zero accounts, so cannot find anything, NO results will be returned.'); $this->collector->findNothing(); return; } - Log::debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); - $this->collector->$collectorMethod($filtered); + app('log')->debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); + $this->collector->$collectorMethod($filtered); // @phpstan-ignore-line } /** + * TODO make enums * searchDirection: 1 = source (default), 2 = destination, 3 = both * stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is * - * @param string $value - * @param int $searchDirection - * @param int $stringPosition - * @param bool $prohibited + * @param string $value + * @param SearchDirection $searchDirection + * @param StringPosition $stringPosition + * @param bool $prohibited + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ - private function searchAccountNr(string $value, int $searchDirection, int $stringPosition, bool $prohibited = false): void + private function searchAccountNr(string $value, SearchDirection $searchDirection, StringPosition $stringPosition, bool $prohibited = false): void { - Log::debug(sprintf('searchAccountNr(%s, %d, %d)', $value, $searchDirection, $stringPosition)); + app('log')->debug(sprintf('searchAccountNr(%s, %d, %d)', $value, $searchDirection->name, $stringPosition->name)); // search direction (default): for source accounts $searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE]; @@ -1447,7 +1535,7 @@ class OperatorQuerySearch implements SearchInterface } // search direction: for destination accounts - if (2 === $searchDirection) { + if (SearchDirection::DESTINATION === $searchDirection) { // destination can be $searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE]; $collectorMethod = 'setDestinationAccounts'; @@ -1457,7 +1545,7 @@ class OperatorQuerySearch implements SearchInterface } // either account could be: - if (3 === $searchDirection) { + if (SearchDirection::BOTH === $searchDirection) { $searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE, AccountType::REVENUE]; $collectorMethod = 'setAccounts'; if (true === $prohibited) { @@ -1469,31 +1557,31 @@ class OperatorQuerySearch implements SearchInterface $stringMethod = 'str_starts_with'; // string position: ends with: - if (2 === $stringPosition) { + if (StringPosition::ENDS === $stringPosition) { $stringMethod = 'str_ends_with'; } - if (3 === $stringPosition) { + if (StringPosition::CONTAINS === $stringPosition) { $stringMethod = 'str_contains'; } - if (4 === $stringPosition) { + if (StringPosition::IS === $stringPosition) { $stringMethod = 'stringIsEqual'; } // search for accounts: $accounts = $this->accountRepository->searchAccountNr($value, $searchTypes, 1337); if (0 === $accounts->count()) { - Log::debug('Found zero accounts, search for invalid account.'); + app('log')->debug('Found zero accounts, search for invalid account.'); $this->collector->findNothing(); return; } // if found, do filter - Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); + app('log')->debug(sprintf('Found %d accounts, will filter.', $accounts->count())); $filtered = $accounts->filter( - function (Account $account) use ($value, $stringMethod) { + static function (Account $account) use ($value, $stringMethod) { // either IBAN or account number - $ibanMatch = $stringMethod(strtolower((string)$account->iban), strtolower((string)$value)); + $ibanMatch = $stringMethod(strtolower((string)$account->iban), strtolower($value)); $accountNrMatch = false; /** @var AccountMeta $meta */ foreach ($account->accountMeta as $meta) { @@ -1507,13 +1595,13 @@ class OperatorQuerySearch implements SearchInterface ); if (0 === $filtered->count()) { - Log::debug('Left with zero, search for invalid account'); + app('log')->debug('Left with zero, search for invalid account'); $this->collector->findNothing(); return; } - Log::debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); - $this->collector->$collectorMethod($filtered); + app('log')->debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod)); + $this->collector->$collectorMethod($filtered);// @phpstan-ignore-line } /** @@ -1559,10 +1647,10 @@ class OperatorQuerySearch implements SearchInterface try { $parsedDate = $parser->parseDate($value); } catch (FireflyException $e) { - Log::debug(sprintf('Could not parse date "%s", will return empty array.', $value)); + app('log')->debug(sprintf('Could not parse date "%s", will return empty array.', $value)); $this->invalidOperators[] = [ 'type' => $type, - 'value' => (string)$value, + 'value' => $value, ]; return []; } @@ -1575,6 +1663,7 @@ class OperatorQuerySearch implements SearchInterface /** * * @throws FireflyException + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setExactDateParams(array $range, bool $prohibited = false): void { @@ -1588,43 +1677,59 @@ class OperatorQuerySearch implements SearchInterface default: throw new FireflyException(sprintf('Cannot handle key "%s" in setExactParameters()', $key)); case 'exact': - Log::debug(sprintf('Set date_is_exact value "%s"', $value->format('Y-m-d'))); - $this->collector->setRange($value, $value); - $this->operators->push(['type' => 'date_on', 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + app('log')->debug(sprintf('Set date_is_exact value "%s"', $value->format('Y-m-d'))); + $this->collector->setRange($value, $value); + $this->operators->push(['type' => 'date_on', 'value' => $value->format('Y-m-d'),]); + } break; case 'exact_not': - $this->collector->excludeRange($value, $value); - $this->operators->push(['type' => 'not_date_on', 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + $this->collector->excludeRange($value, $value); + $this->operators->push(['type' => 'not_date_on', 'value' => $value->format('Y-m-d'),]); + } break; case 'year': - Log::debug(sprintf('Set date_is_exact YEAR value "%s"', $value)); - $this->collector->yearIs($value); - $this->operators->push(['type' => 'date_on_year', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_exact YEAR value "%s"', $value)); + $this->collector->yearIs($value); + $this->operators->push(['type' => 'date_on_year', 'value' => $value,]); + } break; case 'year_not': - Log::debug(sprintf('Set date_is_exact_not YEAR value "%s"', $value)); - $this->collector->yearIsNot($value); - $this->operators->push(['type' => 'not_date_on_year', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_exact_not YEAR value "%s"', $value)); + $this->collector->yearIsNot($value); + $this->operators->push(['type' => 'not_date_on_year', 'value' => $value,]); + } break; case 'month': - Log::debug(sprintf('Set date_is_exact MONTH value "%s"', $value)); - $this->collector->monthIs($value); - $this->operators->push(['type' => 'date_on_month', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_exact MONTH value "%s"', $value)); + $this->collector->monthIs($value); + $this->operators->push(['type' => 'date_on_month', 'value' => $value,]); + } break; case 'month_not': - Log::debug(sprintf('Set date_is_exact not MONTH value "%s"', $value)); - $this->collector->monthIsNot($value); - $this->operators->push(['type' => 'not_date_on_month', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_exact not MONTH value "%s"', $value)); + $this->collector->monthIsNot($value); + $this->operators->push(['type' => 'not_date_on_month', 'value' => $value,]); + } break; case 'day': - Log::debug(sprintf('Set date_is_exact DAY value "%s"', $value)); - $this->collector->dayIs($value); - $this->operators->push(['type' => 'date_on_day', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_exact DAY value "%s"', $value)); + $this->collector->dayIs($value); + $this->operators->push(['type' => 'date_on_day', 'value' => $value,]); + } break; case 'day_not': - Log::debug(sprintf('Set not date_is_exact DAY value "%s"', $value)); - $this->collector->dayIsNot($value); - $this->operators->push(['type' => 'not_date_on_day', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set not date_is_exact DAY value "%s"', $value)); + $this->collector->dayIsNot($value); + $this->operators->push(['type' => 'not_date_on_day', 'value' => $value,]); + } break; } } @@ -1633,6 +1738,7 @@ class OperatorQuerySearch implements SearchInterface /** * * @throws FireflyException + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setDateBeforeParams(array $range, bool $prohibited = false): void { @@ -1646,23 +1752,31 @@ class OperatorQuerySearch implements SearchInterface default: throw new FireflyException(sprintf('Cannot handle key "%s" in setDateBeforeParams()', $key)); case 'exact': - $this->collector->setBefore($value); - $this->operators->push(['type' => 'date_before', 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + $this->collector->setBefore($value); + $this->operators->push(['type' => 'date_before', 'value' => $value->format('Y-m-d'),]); + } break; case 'year': - Log::debug(sprintf('Set date_is_before YEAR value "%s"', $value)); - $this->collector->yearBefore($value); - $this->operators->push(['type' => 'date_before_year', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_before YEAR value "%s"', $value)); + $this->collector->yearBefore($value); + $this->operators->push(['type' => 'date_before_year', 'value' => $value,]); + } break; case 'month': - Log::debug(sprintf('Set date_is_before MONTH value "%s"', $value)); - $this->collector->monthBefore($value); - $this->operators->push(['type' => 'date_before_month', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_before MONTH value "%s"', $value)); + $this->collector->monthBefore($value); + $this->operators->push(['type' => 'date_before_month', 'value' => $value,]); + } break; case 'day': - Log::debug(sprintf('Set date_is_before DAY value "%s"', $value)); - $this->collector->dayBefore($value); - $this->operators->push(['type' => 'date_before_day', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_before DAY value "%s"', $value)); + $this->collector->dayBefore($value); + $this->operators->push(['type' => 'date_before_day', 'value' => $value,]); + } break; } } @@ -1671,8 +1785,9 @@ class OperatorQuerySearch implements SearchInterface /** * * @throws FireflyException + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ - private function setDateAfterParams(array $range, bool $prohibited = false) + private function setDateAfterParams(array $range, bool $prohibited = false): void { /** * @var string $key @@ -1684,23 +1799,31 @@ class OperatorQuerySearch implements SearchInterface default: throw new FireflyException(sprintf('Cannot handle key "%s" in setDateAfterParams()', $key)); case 'exact': - $this->collector->setAfter($value); - $this->operators->push(['type' => 'date_after', 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + $this->collector->setAfter($value); + $this->operators->push(['type' => 'date_after', 'value' => $value->format('Y-m-d'),]); + } break; case 'year': - Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value)); - $this->collector->yearAfter($value); - $this->operators->push(['type' => 'date_after_year', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_after YEAR value "%s"', $value)); + $this->collector->yearAfter($value); + $this->operators->push(['type' => 'date_after_year', 'value' => $value,]); + } break; case 'month': - Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value)); - $this->collector->monthAfter($value); - $this->operators->push(['type' => 'date_after_month', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_after MONTH value "%s"', $value)); + $this->collector->monthAfter($value); + $this->operators->push(['type' => 'date_after_month', 'value' => $value,]); + } break; case 'day': - Log::debug(sprintf('Set date_is_after DAY value "%s"', $value)); - $this->collector->dayAfter($value); - $this->operators->push(['type' => 'date_after_day', 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_after DAY value "%s"', $value)); + $this->collector->dayAfter($value); + $this->operators->push(['type' => 'date_after_day', 'value' => $value,]); + } break; } } @@ -1708,10 +1831,11 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setExactMetaDateParams(string $field, array $range, bool $prohibited = false): void { - Log::debug('Now in setExactMetaDateParams()'); + app('log')->debug('Now in setExactMetaDateParams()'); /** * @var string $key * @var Carbon|string $value @@ -1722,44 +1846,60 @@ class OperatorQuerySearch implements SearchInterface default: throw new FireflyException(sprintf('Cannot handle key "%s" in setExactMetaDateParams()', $key)); case 'exact': - Log::debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); - $this->collector->setMetaDateRange($value, $value, $field); - $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + app('log')->debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); + $this->collector->setMetaDateRange($value, $value, $field); + $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]); + } break; case 'exact_not': - Log::debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); - $this->collector->excludeMetaDateRange($value, $value, $field); - $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + app('log')->debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); + $this->collector->excludeMetaDateRange($value, $value, $field); + $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d'),]); + } break; case 'year': - Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); - $this->collector->metaYearIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); + $this->collector->metaYearIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]); + } break; case 'year_not': - Log::debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); - $this->collector->metaYearIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); + $this->collector->metaYearIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value,]); + } break; case 'month': - Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); - $this->collector->metaMonthIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); + $this->collector->metaMonthIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]); + } break; case 'month_not': - Log::debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); - $this->collector->metaMonthIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); + $this->collector->metaMonthIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value,]); + } break; case 'day': - Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); - $this->collector->metaDayIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); + $this->collector->metaDayIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]); + } break; case 'day_not': - Log::debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); - $this->collector->metaDayIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); + $this->collector->metaDayIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value,]); + } break; } } @@ -1767,6 +1907,7 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setMetaDateBeforeParams(string $field, array $range, bool $prohibited = false): void { @@ -1780,23 +1921,31 @@ class OperatorQuerySearch implements SearchInterface default: throw new FireflyException(sprintf('Cannot handle key "%s" in setMetaDateBeforeParams()', $key)); case 'exact': - $this->collector->setMetaBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + $this->collector->setMetaBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d'),]); + } break; case 'year': - Log::debug(sprintf('Set %s_is_before YEAR value "%s"', $field, $value)); - $this->collector->metaYearBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_before YEAR value "%s"', $field, $value)); + $this->collector->metaYearBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value,]); + } break; case 'month': - Log::debug(sprintf('Set %s_is_before MONTH value "%s"', $field, $value)); - $this->collector->metaMonthBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_before MONTH value "%s"', $field, $value)); + $this->collector->metaMonthBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value,]); + } break; case 'day': - Log::debug(sprintf('Set %s_is_before DAY value "%s"', $field, $value)); - $this->collector->metaDayBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_before DAY value "%s"', $field, $value)); + $this->collector->metaDayBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value,]); + } break; } } @@ -1804,6 +1953,7 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setMetaDateAfterParams(string $field, array $range, bool $prohibited = false): void { @@ -1817,23 +1967,31 @@ class OperatorQuerySearch implements SearchInterface default: throw new FireflyException(sprintf('Cannot handle key "%s" in setMetaDateAfterParams()', $key)); case 'exact': - $this->collector->setMetaAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + $this->collector->setMetaAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d'),]); + } break; case 'year': - Log::debug(sprintf('Set %s_is_after YEAR value "%s"', $field, $value)); - $this->collector->metaYearAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_after YEAR value "%s"', $field, $value)); + $this->collector->metaYearAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value,]); + } break; case 'month': - Log::debug(sprintf('Set %s_is_after MONTH value "%s"', $field, $value)); - $this->collector->metaMonthAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_after MONTH value "%s"', $field, $value)); + $this->collector->metaMonthAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value,]); + } break; case 'day': - Log::debug(sprintf('Set %s_is_after DAY value "%s"', $field, $value)); - $this->collector->metaDayAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_after DAY value "%s"', $field, $value)); + $this->collector->metaDayAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value,]); + } break; } } @@ -1841,6 +1999,7 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setExactObjectDateParams(string $field, array $range, bool $prohibited = false): void { @@ -1854,44 +2013,60 @@ class OperatorQuerySearch implements SearchInterface default: throw new FireflyException(sprintf('Cannot handle key "%s" in setExactObjectDateParams()', $key)); case 'exact': - Log::debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); - $this->collector->setObjectRange($value, clone $value, $field); - $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + app('log')->debug(sprintf('Set %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); + $this->collector->setObjectRange($value, clone $value, $field); + $this->operators->push(['type' => sprintf('%s_on', $field), 'value' => $value->format('Y-m-d'),]); + } break; case 'exact_not': - Log::debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); - $this->collector->excludeObjectRange($value, clone $value, $field); - $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + app('log')->debug(sprintf('Set NOT %s_is_exact value "%s"', $field, $value->format('Y-m-d'))); + $this->collector->excludeObjectRange($value, clone $value, $field); + $this->operators->push(['type' => sprintf('not_%s_on', $field), 'value' => $value->format('Y-m-d'),]); + } break; case 'year': - Log::debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); - $this->collector->objectYearIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_exact YEAR value "%s"', $field, $value)); + $this->collector->objectYearIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_year', $field), 'value' => $value,]); + } break; case 'year_not': - Log::debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); - $this->collector->objectYearIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set NOT %s_is_exact YEAR value "%s"', $field, $value)); + $this->collector->objectYearIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_year', $field), 'value' => $value,]); + } break; case 'month': - Log::debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); - $this->collector->objectMonthIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_exact MONTH value "%s"', $field, $value)); + $this->collector->objectMonthIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_month', $field), 'value' => $value,]); + } break; case 'month_not': - Log::debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); - $this->collector->objectMonthIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set NOT %s_is_exact MONTH value "%s"', $field, $value)); + $this->collector->objectMonthIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_month', $field), 'value' => $value,]); + } break; case 'day': - Log::debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); - $this->collector->objectDayIs($value, $field); - $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set %s_is_exact DAY value "%s"', $field, $value)); + $this->collector->objectDayIs($value, $field); + $this->operators->push(['type' => sprintf('%s_on_day', $field), 'value' => $value,]); + } break; case 'day_not': - Log::debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); - $this->collector->objectDayIsNot($value, $field); - $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set NOT %s_is_exact DAY value "%s"', $field, $value)); + $this->collector->objectDayIsNot($value, $field); + $this->operators->push(['type' => sprintf('not_%s_on_day', $field), 'value' => $value,]); + } break; } } @@ -1900,6 +2075,7 @@ class OperatorQuerySearch implements SearchInterface /** * * @throws FireflyException + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setObjectDateBeforeParams(string $field, array $range, bool $prohibited = false): void { @@ -1913,23 +2089,31 @@ class OperatorQuerySearch implements SearchInterface default: throw new FireflyException(sprintf('Cannot handle key "%s" in setObjectDateBeforeParams()', $key)); case 'exact': - $this->collector->setObjectBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + $this->collector->setObjectBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before', $field), 'value' => $value->format('Y-m-d'),]); + } break; case 'year': - Log::debug(sprintf('Set date_is_before YEAR value "%s"', $value)); - $this->collector->objectYearBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_before YEAR value "%s"', $value)); + $this->collector->objectYearBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_year', $field), 'value' => $value,]); + } break; case 'month': - Log::debug(sprintf('Set date_is_before MONTH value "%s"', $value)); - $this->collector->objectMonthBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_before MONTH value "%s"', $value)); + $this->collector->objectMonthBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_month', $field), 'value' => $value,]); + } break; case 'day': - Log::debug(sprintf('Set date_is_before DAY value "%s"', $value)); - $this->collector->objectDayBefore($value, $field); - $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_before DAY value "%s"', $value)); + $this->collector->objectDayBefore($value, $field); + $this->operators->push(['type' => sprintf('%s_before_day', $field), 'value' => $value,]); + } break; } } @@ -1938,6 +2122,7 @@ class OperatorQuerySearch implements SearchInterface /** * * @throws FireflyException + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ private function setObjectDateAfterParams(string $field, array $range, bool $prohibited = false): void { @@ -1951,28 +2136,74 @@ class OperatorQuerySearch implements SearchInterface default: throw new FireflyException(sprintf('Cannot handle key "%s" in setObjectDateAfterParams()', $key)); case 'exact': - $this->collector->setObjectAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d'),]); + if ($value instanceof Carbon) { + $this->collector->setObjectAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after', $field), 'value' => $value->format('Y-m-d'),]); + } break; case 'year': - Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value)); - $this->collector->objectYearAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_after YEAR value "%s"', $value)); + $this->collector->objectYearAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_year', $field), 'value' => $value,]); + } break; case 'month': - Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value)); - $this->collector->objectMonthAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_after MONTH value "%s"', $value)); + $this->collector->objectMonthAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_month', $field), 'value' => $value,]); + } break; case 'day': - Log::debug(sprintf('Set date_is_after DAY value "%s"', $value)); - $this->collector->objectDayAfter($value, $field); - $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value,]); + if (is_string($value)) { + app('log')->debug(sprintf('Set date_is_after DAY value "%s"', $value)); + $this->collector->objectDayAfter($value, $field); + $this->operators->push(['type' => sprintf('%s_after_day', $field), 'value' => $value,]); + } break; } } } + /** + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + private function parseTagInstructions(): void + { + app('log')->debug('Now in parseTagInstructions()'); + // if exclude tags, remove excluded tags. + if (count($this->excludeTags) > 0) { + app('log')->debug(sprintf('%d exclude tag(s)', count($this->excludeTags))); + $collection = new Collection; + foreach ($this->excludeTags as $tagId) { + $tag = $this->tagRepository->find($tagId); + if (null !== $tag) { + app('log')->debug(sprintf('Exclude tag "%s"', $tag->tag)); + $collection->push($tag); + } + } + app('log')->debug(sprintf('Selecting all tags except %d excluded tag(s).', $collection->count())); + $this->collector->setWithoutSpecificTags($collection); + } + // if include tags, include them: + if (count($this->includeTags) > 0) { + app('log')->debug(sprintf('%d include tag(s)', count($this->includeTags))); + $collection = new Collection; + foreach ($this->includeTags as $tagId) { + $tag = $this->tagRepository->find($tagId); + if (null !== $tag) { + app('log')->debug(sprintf('Include tag "%s"', $tag->tag)); + $collection->push($tag); + } + } + $this->collector->setTags($collection); + } + + } + /** * @inheritDoc */ diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php index 100168f36e..3ce60fd9bf 100644 --- a/app/Support/Search/SearchInterface.php +++ b/app/Support/Search/SearchInterface.php @@ -61,7 +61,7 @@ interface SearchInterface /** * @param string $query */ - public function parseQuery(string $query); + public function parseQuery(string $query): void; /** * @return float @@ -91,5 +91,5 @@ interface SearchInterface /** * @param User $user */ - public function setUser(User $user); + public function setUser(User $user): void; } diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 335f246abf..59faf20c14 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -118,7 +118,7 @@ class Steam $cache = new CacheProperties(); $cache->addProperty($account->id); $cache->addProperty('balance-in-range'); - $cache->addProperty($currency ? $currency->id : 0); + $cache->addProperty(null !== $currency ? $currency->id : 0); $cache->addProperty($start); $cache->addProperty($end); if ($cache->has()) { @@ -135,9 +135,9 @@ class Steam if (null === $currency) { $repository = app(AccountRepositoryInterface::class); $repository->setUser($account->user); - $currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user); + $currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); } - $currencyId = (int)$currency->id; + $currencyId = $currency->id; $start->addDay(); @@ -152,12 +152,12 @@ class Steam ->orderBy('transaction_journals.date', 'ASC') ->whereNull('transaction_journals.deleted_at') ->get( - [ - 'transaction_journals.date', - 'transactions.transaction_currency_id', - DB::raw('SUM(transactions.amount) AS modified'), - 'transactions.foreign_currency_id', - DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'), + [ // @phpstan-ignore-line + 'transaction_journals.date', + 'transactions.transaction_currency_id', + DB::raw('SUM(transactions.amount) AS modified'), + 'transactions.foreign_currency_id', + DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'), ] ); @@ -165,8 +165,8 @@ class Steam /** @var Transaction $entry */ foreach ($set as $entry) { // normal amount and foreign amount - $modified = null === $entry->modified ? '0' : (string)$entry->modified; - $foreignModified = null === $entry->modified_foreign ? '0' : (string)$entry->modified_foreign; + $modified = (string)(null === $entry->modified ? '0' : $entry->modified); + $foreignModified = (string)(null === $entry->modified_foreign ? '0' : $entry->modified_foreign); $amount = '0'; if ($currencyId === (int)$entry->transaction_currency_id || 0 === $currencyId) { // use normal amount: @@ -176,7 +176,7 @@ class Steam // use foreign amount: $amount = $foreignModified; } - + Log::debug(sprintf('Trying to add %s and %s.', var_export($currentBalance, true), var_export($amount, true))); $currentBalance = bcadd($currentBalance, $amount); $carbon = new Carbon($entry->date, config('app.timezone')); $date = $carbon->format('Y-m-d'); @@ -205,14 +205,14 @@ class Steam $cache->addProperty($account->id); $cache->addProperty('balance'); $cache->addProperty($date); - $cache->addProperty($currency ? $currency->id : 0); + $cache->addProperty(null !== $currency ? $currency->id : 0); if ($cache->has()) { return $cache->get(); } /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); if (null === $currency) { - $currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user); + $currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); } // first part: get all balances in own currency: $transactions = $account->transactions() @@ -230,7 +230,7 @@ class Steam ->get(['transactions.foreign_amount'])->toArray(); $foreignBalance = $this->sumTransactions($transactions, 'foreign_amount'); $balance = bcadd($nativeBalance, $foreignBalance); - $virtual = null === $account->virtual_balance ? '0' : (string)$account->virtual_balance; + $virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance; $balance = bcadd($balance, $virtual); $cache->store($balance); @@ -295,10 +295,13 @@ class Steam $currentBalance = $startBalance; /** @var Transaction $transaction */ foreach ($set as $transaction) { - $day = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date'], config('app.timezone')); + $day = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date'], config('app.timezone')); + if (false === $day) { + $day = today(config('app.timezone')); + } $format = $day->format('Y-m-d'); // if the transaction is in the expected currency, change nothing. - if ((int)$transaction['transaction_currency_id'] === (int)$native->id) { + if ((int)$transaction['transaction_currency_id'] === $native->id) { // change the current balance, set it to today, continue the loop. $currentBalance = bcadd($currentBalance, $transaction['amount']); $balances[$format] = $currentBalance; @@ -306,16 +309,16 @@ class Steam continue; } // if foreign currency is in the expected currency, do nothing: - if ((int)$transaction['foreign_currency_id'] === (int)$native->id) { + if ((int)$transaction['foreign_currency_id'] === $native->id) { $currentBalance = bcadd($currentBalance, $transaction['foreign_amount']); $balances[$format] = $currentBalance; app('log')->debug(sprintf('%s: transaction in %s (foreign), new balance is %s.', $format, $native->code, $currentBalance)); continue; } // otherwise, convert 'amount' to the necessary currency: - $currencyId = (int)$transaction['transaction_currency_id']; - $currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId); - + $currencyId = (int)$transaction['transaction_currency_id']; + $currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId); + $currencies[$currencyId] = $currency; $rate = $converter->getCurrencyRate($currency, $native, $day); $convertedAmount = bcmul($transaction['amount'], $rate); @@ -362,15 +365,13 @@ class Steam $cache->addProperty($date); $cache->addProperty($native->id); if ($cache->has()) { - // return $cache->get(); + return $cache->get(); } /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $currency = $repository->getAccountCurrency($account); - if (null === $currency) { - throw new FireflyException('Cannot get converted account balance: no currency found for account.'); - } - if ((int)$native->id === (int)$currency->id) { + $currency = null === $currency ? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup) : $currency; + if ($native->id === $currency->id) { return $this->balance($account, $date); } /** @@ -448,9 +449,12 @@ class Steam // but we need to convert each transaction separately because the date difference may // incur huge currency changes. $converter = new ExchangeRateConverter(); - foreach ($new as $index => $set) { + foreach ($new as $set) { foreach ($set as $transaction) { - $date = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date']); + $date = Carbon::createFromFormat('Y-m-d H:i:s', $transaction['date']); + if (false === $date) { + $date = today(config('app.timezone')); + } $rate = $converter->getCurrencyRate($currency, $native, $date); $convertedAmount = bcmul($transaction['amount'], $rate); $balance = bcadd($balance, $convertedAmount); @@ -467,7 +471,7 @@ class Steam } // add virtual balance (also needs conversion) - $virtual = null === $account->virtual_balance ? '0' : (string)$account->virtual_balance; + $virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance; $virtual = $converter->convert($currency, $native, $account->created_at, $virtual); $balance = bcadd($balance, $virtual); @@ -535,8 +539,8 @@ class Steam $result = []; /** @var Account $account */ foreach ($accounts as $account) { - $default = app('amount')->getDefaultCurrencyByUser($account->user); - $result[(int)$account->id] + $default = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); + $result[$account->id] = [ 'balance' => $this->balance($account, $date), 'native_balance' => $this->balanceConverted($account, $date, $default), @@ -600,8 +604,8 @@ class Steam ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) ->groupBy('transactions.transaction_currency_id'); - $balances = $query->get(['transactions.transaction_currency_id', DB::raw('SUM(transactions.amount) as sum_for_currency')]); - $return = []; + $balances = $query->get(['transactions.transaction_currency_id', DB::raw('SUM(transactions.amount) as sum_for_currency')]); // @phpstan-ignore-line + $return = []; /** @var stdClass $entry */ foreach ($balances as $entry) { $return[(int)$entry->transaction_currency_id] = (string)$entry->sum_for_currency; @@ -632,7 +636,7 @@ class Steam $number = sprintf('%.12f', $number); } - // Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); + // app('log')->debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); if (str_contains($number, '.')) { if ($number[0] !== '-') { return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision); @@ -719,7 +723,7 @@ class Steam } catch (Exception $e) { // intentional generic exception throw new FireflyException($e->getMessage(), 0, $e); } - return $hostName; + return (string)$hostName; } /** @@ -734,12 +738,13 @@ class Steam $set = auth()->user()->transactions() ->whereIn('transactions.account_id', $accounts) ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) - ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]); + ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]); // @phpstan-ignore-line + /** @var Transaction $entry */ foreach ($set as $entry) { $date = new Carbon($entry->max_date, config('app.timezone')); $date->setTimezone(config('app.timezone')); - $list[(int)$entry->account_id] = $date; + $list[$entry->account_id] = $date; } return $list; @@ -756,9 +761,14 @@ class Steam public function getLocale(): string // get preference { $locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; + if (is_array($locale)) { + $locale = 'equal'; + } if ('equal' === $locale) { $locale = $this->getLanguage(); } + $locale = (string)$locale; + // Check for Windows to replace the locale correctly. if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { @@ -812,7 +822,7 @@ class Steam */ public function getSafePreviousUrl(): string { - //Log::debug(sprintf('getSafePreviousUrl: "%s"', session()->previousUrl())); + //app('log')->debug(sprintf('getSafePreviousUrl: "%s"', session()->previousUrl())); return session()->previousUrl() ?? route('index'); } @@ -826,7 +836,7 @@ class Steam */ public function getSafeUrl(string $unknownUrl, string $safeUrl): string { - //Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); + //app('log')->debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); $returnUrl = $safeUrl; $unknownHost = parse_url($unknownUrl, PHP_URL_HOST); $safeHost = parse_url($safeUrl, PHP_URL_HOST); @@ -880,10 +890,10 @@ class Steam return $value; } - $number = substr($value, 0, strpos($value, 'E')); + $number = substr($value, 0, (int)strpos($value, 'E')); if (str_contains($number, '.')) { - $post = strlen(substr($number, strpos($number, '.') + 1)); - $mantis = substr($value, strpos($value, 'E') + 1); + $post = strlen(substr($number, (int)strpos($number, '.') + 1)); + $mantis = substr($value, (int)strpos($value, 'E') + 1); if ($mantis < 0) { $post += abs((int)$mantis); } @@ -956,8 +966,8 @@ class Steam $amount = bcmul($amount, '-1'); } } catch (ValueError $e) { - Log::error(sprintf('ValueError in Steam::positive("%s"): %s', $amount, $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('ValueError in Steam::positive("%s"): %s', $amount, $e->getMessage())); + app('log')->error($e->getTraceAsString()); return '0'; } diff --git a/app/Support/System/GeneratesInstallationId.php b/app/Support/System/GeneratesInstallationId.php index beccdaafab..cdd981eb99 100644 --- a/app/Support/System/GeneratesInstallationId.php +++ b/app/Support/System/GeneratesInstallationId.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Support\System; use FireflyIII\Exceptions\FireflyException; -use Illuminate\Support\Facades\Log; use Ramsey\Uuid\Uuid; /** @@ -41,7 +40,7 @@ trait GeneratesInstallationId try { $config = app('fireflyconfig')->get('installation_id'); } catch (FireflyException $e) { - Log::info('Could not create or generate installation ID. Do not continue.'); + app('log')->info('Could not create or generate installation ID. Do not continue.'); return; } @@ -54,7 +53,7 @@ trait GeneratesInstallationId if (null === $config) { $uuid4 = Uuid::uuid4(); $uniqueId = (string)$uuid4; - Log::info(sprintf('Created Firefly III installation ID %s', $uniqueId)); + app('log')->info(sprintf('Created Firefly III installation ID %s', $uniqueId)); app('fireflyconfig')->set('installation_id', $uniqueId); } } diff --git a/app/Support/System/OAuthKeys.php b/app/Support/System/OAuthKeys.php index 8a59ebf739..c548664acb 100644 --- a/app/Support/System/OAuthKeys.php +++ b/app/Support/System/OAuthKeys.php @@ -28,7 +28,6 @@ use Artisan; use Crypt; use FireflyIII\Exceptions\FireflyException; use Illuminate\Contracts\Encryption\DecryptException; -use Illuminate\Support\Facades\Log; use Laravel\Passport\Console\KeysCommand; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -38,8 +37,8 @@ use Psr\Container\NotFoundExceptionInterface; */ class OAuthKeys { - private const PRIVATE_KEY = 'oauth_private_key'; - private const PUBLIC_KEY = 'oauth_public_key'; + private const string PRIVATE_KEY = 'oauth_private_key'; + private const string PUBLIC_KEY = 'oauth_public_key'; /** * @@ -75,8 +74,8 @@ class OAuthKeys $privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data; $publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; } catch (ContainerExceptionInterface | NotFoundExceptionInterface | FireflyException $e) { - Log::error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage())); - Log::error($e->getTraceAsString()); + app('log')->error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage())); + app('log')->error($e->getTraceAsString()); } } if ('' !== $privateKey && '' !== $publicKey) { @@ -131,8 +130,8 @@ class OAuthKeys $privateContent = Crypt::decrypt($privateKey); $publicContent = Crypt::decrypt($publicKey); } catch (DecryptException $e) { - Log::error('Could not decrypt pub/private keypair.'); - Log::error($e->getMessage()); + app('log')->error('Could not decrypt pub/private keypair.'); + app('log')->error($e->getMessage()); // delete config vars from DB: app('fireflyconfig')->delete(self::PRIVATE_KEY); diff --git a/app/Support/Twig/AmountFormat.php b/app/Support/Twig/AmountFormat.php index 5ae6db2121..3b150652c4 100644 --- a/app/Support/Twig/AmountFormat.php +++ b/app/Support/Twig/AmountFormat.php @@ -101,7 +101,7 @@ class AmountFormat extends AbstractExtension return new TwigFunction( 'formatAmountByAccount', static function (AccountModel $account, string $amount, bool $coloured = null): string { - $coloured = $coloured ?? true; + $coloured ??= true; /** @var AccountRepositoryInterface $accountRepos */ $accountRepos = app(AccountRepositoryInterface::class); $currency = $accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency(); @@ -122,8 +122,8 @@ class AmountFormat extends AbstractExtension return new TwigFunction( 'formatAmountBySymbol', static function (string $amount, string $symbol, int $decimalPlaces = null, bool $coloured = null): string { - $decimalPlaces = $decimalPlaces ?? 2; - $coloured = $coloured ?? true; + $decimalPlaces ??= 2; + $coloured ??= true; $currency = new TransactionCurrency(); $currency->symbol = $symbol; $currency->decimal_places = $decimalPlaces; @@ -144,7 +144,7 @@ class AmountFormat extends AbstractExtension return new TwigFunction( 'formatAmountByCurrency', static function (TransactionCurrency $currency, string $amount, bool $coloured = null): string { - $coloured = $coloured ?? true; + $coloured ??= true; return app('amount')->formatAnything($currency, $amount, $coloured); }, diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index 4281ff1539..52d9a01aa8 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -40,7 +40,7 @@ use Twig\TwigFunction; class General extends AbstractExtension { /** - * @return array + * @inheritDoc */ public function getFilters(): array { @@ -114,7 +114,7 @@ class General extends AbstractExtension return 'fa-file-o'; case 'application/pdf': return 'fa-file-pdf-o'; - /* image */ + /* image */ case 'image/png': case 'image/jpeg': case 'image/svg+xml': @@ -122,7 +122,7 @@ class General extends AbstractExtension case 'image/heic-sequence': case 'application/vnd.oasis.opendocument.image': return 'fa-file-image-o'; - /* MS word */ + /* MS word */ case 'application/msword': case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': case 'application/vnd.openxmlformats-officedocument.wordprocessingml.template': @@ -137,7 +137,7 @@ class General extends AbstractExtension case 'application/vnd.oasis.opendocument.text-web': case 'application/vnd.oasis.opendocument.text-master': return 'fa-file-word-o'; - /* MS excel */ + /* MS excel */ case 'application/vnd.ms-excel': case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template': @@ -147,7 +147,7 @@ class General extends AbstractExtension case 'application/vnd.oasis.opendocument.spreadsheet': case 'application/vnd.oasis.opendocument.spreadsheet-template': return 'fa-file-excel-o'; - /* MS powerpoint */ + /* MS powerpoint */ case 'application/vnd.ms-powerpoint': case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': case 'application/vnd.openxmlformats-officedocument.presentationml.template': @@ -158,7 +158,7 @@ class General extends AbstractExtension case 'application/vnd.oasis.opendocument.presentation': case 'application/vnd.oasis.opendocument.presentation-template': return 'fa-file-powerpoint-o'; - /* calc */ + /* calc */ case 'application/vnd.sun.xml.draw': case 'application/vnd.sun.xml.draw.template': case 'application/vnd.stardivision.draw': @@ -357,7 +357,7 @@ class General extends AbstractExtension { return new TwigFunction( 'formatDate', - function (string $date, string $format): string { + static function (string $date, string $format): string { $carbon = new Carbon($date); return $carbon->isoFormat($format); diff --git a/app/Support/Twig/Rule.php b/app/Support/Twig/Rule.php index adc616b129..44ecd420cd 100644 --- a/app/Support/Twig/Rule.php +++ b/app/Support/Twig/Rule.php @@ -33,7 +33,7 @@ use Twig\TwigFunction; class Rule extends AbstractExtension { /** - * @return array + * @inheritDoc */ public function getFunctions(): array { diff --git a/app/Support/Twig/TransactionGroupTwig.php b/app/Support/Twig/TransactionGroupTwig.php index 38e3005eb5..09f09cec7e 100644 --- a/app/Support/Twig/TransactionGroupTwig.php +++ b/app/Support/Twig/TransactionGroupTwig.php @@ -28,6 +28,7 @@ use DB; use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionJournalMeta; use FireflyIII\Models\TransactionType; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -38,8 +39,7 @@ use Twig\TwigFunction; class TransactionGroupTwig extends AbstractExtension { /** - * @return array - * + * @inheritDoc */ public function getFunctions(): array { @@ -201,7 +201,7 @@ class TransactionGroupTwig extends AbstractExtension if ($type === TransactionType::TRANSFER) { $colored = false; } - $result = app('amount')->formatFlat($currency->symbol, (int)$currency->decimal_places, $amount, $colored); + $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); if ($type === TransactionType::TRANSFER) { $result = sprintf('%s', $result); } @@ -244,7 +244,7 @@ class TransactionGroupTwig extends AbstractExtension if ($type === TransactionType::TRANSFER) { $colored = false; } - $result = app('amount')->formatFlat($currency->symbol, (int)$currency->decimal_places, $amount, $colored); + $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); if ($type === TransactionType::TRANSFER) { $result = sprintf('%s', $result); } @@ -279,6 +279,7 @@ class TransactionGroupTwig extends AbstractExtension return new TwigFunction( 'journalGetMetaDate', static function (int $journalId, string $metaField) { + /** @var TransactionJournalMeta|null $entry */ $entry = DB::table('journal_meta') ->where('name', $metaField) ->where('transaction_journal_id', $journalId) @@ -301,6 +302,7 @@ class TransactionGroupTwig extends AbstractExtension return new TwigFunction( 'journalGetMetaField', static function (int $journalId, string $metaField) { + /** @var TransactionJournalMeta|null $entry */ $entry = DB::table('journal_meta') ->where('name', $metaField) ->where('transaction_journal_id', $journalId) diff --git a/app/Support/Twig/Translation.php b/app/Support/Twig/Translation.php index 11b420f0be..806d065b72 100644 --- a/app/Support/Twig/Translation.php +++ b/app/Support/Twig/Translation.php @@ -33,7 +33,7 @@ use Twig\TwigFunction; class Translation extends AbstractExtension { /** - * @return array + * @inheritDoc */ public function getFilters(): array { diff --git a/app/TransactionRules/Actions/AddTag.php b/app/TransactionRules/Actions/AddTag.php index 057473463c..6d8fcc961c 100644 --- a/app/TransactionRules/Actions/AddTag.php +++ b/app/TransactionRules/Actions/AddTag.php @@ -30,7 +30,6 @@ use FireflyIII\Factory\TagFactory; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Class AddTag. @@ -57,7 +56,9 @@ class AddTag implements ActionInterface // journal has this tag maybe? /** @var TagFactory $factory */ $factory = app(TagFactory::class); - $factory->setUser(User::find($journal['user_id'])); + /** @var User $user */ + $user = User::find($journal['user_id']); + $factory->setUser($user); $tag = $factory->findOrCreate($this->action->action_value); if (null === $tag) { @@ -74,14 +75,15 @@ class AddTag implements ActionInterface if (0 === $count) { // add to journal: DB::table('tag_transaction_journal')->insert(['tag_id' => $tag->id, 'transaction_journal_id' => $journal['transaction_journal_id']]); - Log::debug(sprintf('RuleAction AddTag. Added tag #%d ("%s") to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction AddTag. Added tag #%d ("%s") to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); + /** @var TransactionJournal $object */ $object = TransactionJournal::find($journal['transaction_journal_id']); // event for audit log entry event(new TriggeredAuditLog($this->action->rule, $object, 'add_tag', null, $tag->tag)); return true; } - Log::debug( + app('log')->debug( sprintf('RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id']) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.tag_already_added', ['tag' => $this->action->action_value]))); diff --git a/app/TransactionRules/Actions/AppendDescription.php b/app/TransactionRules/Actions/AppendDescription.php index 2a52c2cb79..57ac382767 100644 --- a/app/TransactionRules/Actions/AppendDescription.php +++ b/app/TransactionRules/Actions/AppendDescription.php @@ -50,7 +50,7 @@ class AppendDescription implements ActionInterface */ public function actOnArray(array $journal): bool { - $description = sprintf('%s%s', $journal['description'], $this->action->action_value); + $description = sprintf('%s %s', $journal['description'], $this->action->action_value); DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $description]); // event for audit log entry diff --git a/app/TransactionRules/Actions/AppendDescriptionToNotes.php b/app/TransactionRules/Actions/AppendDescriptionToNotes.php index a10e442140..037aeacd27 100644 --- a/app/TransactionRules/Actions/AppendDescriptionToNotes.php +++ b/app/TransactionRules/Actions/AppendDescriptionToNotes.php @@ -29,7 +29,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class AppendDescriptionToNotes @@ -53,11 +52,11 @@ class AppendDescriptionToNotes implements ActionInterface */ public function actOnArray(array $journal): bool { - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { - Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user'))); + app('log')->error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); + event(new RuleActionFailedOnArray($this->action, $journal, (string)trans('rules.journal_other_user'))); return false; } $note = $object->notes()->first(); diff --git a/app/TransactionRules/Actions/AppendNotes.php b/app/TransactionRules/Actions/AppendNotes.php index 8d130b2521..9d4758116a 100644 --- a/app/TransactionRules/Actions/AppendNotes.php +++ b/app/TransactionRules/Actions/AppendNotes.php @@ -27,7 +27,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class AppendNotes. @@ -60,7 +59,7 @@ class AppendNotes implements ActionInterface $dbNote->noteable_type = TransactionJournal::class; $dbNote->text = ''; } - Log::debug(sprintf('RuleAction AppendNotes appended "%s" to "%s".', $this->action->action_value, $dbNote->text)); + app('log')->debug(sprintf('RuleAction AppendNotes appended "%s" to "%s".', $this->action->action_value, $dbNote->text)); $before = $dbNote->text; $text = sprintf('%s%s', $dbNote->text, $this->action->action_value); $dbNote->text = $text; diff --git a/app/TransactionRules/Actions/AppendNotesToDescription.php b/app/TransactionRules/Actions/AppendNotesToDescription.php index 93469788a8..96379d8a7a 100644 --- a/app/TransactionRules/Actions/AppendNotesToDescription.php +++ b/app/TransactionRules/Actions/AppendNotesToDescription.php @@ -30,7 +30,6 @@ use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Support\Request\ConvertsDataTypes; -use Illuminate\Support\Facades\Log; /** * Class AppendNotesToDescription @@ -56,17 +55,17 @@ class AppendNotesToDescription implements ActionInterface */ public function actOnArray(array $journal): bool { - Log::debug('Now in AppendNotesToDescription'); - /** @var TransactionJournal $object */ + app('log')->debug('Now in AppendNotesToDescription'); + /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { - Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); + app('log')->error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user'))); return false; } $note = $object->notes()->first(); if (null === $note) { - Log::debug('Journal has no notes.'); + app('log')->debug('Journal has no notes.'); $note = new Note(); $note->noteable()->associate($object); $note->text = ''; @@ -74,9 +73,9 @@ class AppendNotesToDescription implements ActionInterface // only append if there is something to append if ('' !== $note->text) { $before = $object->description; - $object->description = trim(sprintf('%s %s', $object->description, (string)$this->clearString($note->text, false))); + $object->description = trim(sprintf('%s %s', $object->description, (string)$this->clearString($note->text))); $object->save(); - Log::debug(sprintf('Journal description is updated to "%s".', $object->description)); + app('log')->debug(sprintf('Journal description is updated to "%s".', $object->description)); event(new TriggeredAuditLog($this->action->rule, $object, 'update_description', $before, $object->description)); diff --git a/app/TransactionRules/Actions/ClearBudget.php b/app/TransactionRules/Actions/ClearBudget.php index 2c79a535fb..a9fde4d6f2 100644 --- a/app/TransactionRules/Actions/ClearBudget.php +++ b/app/TransactionRules/Actions/ClearBudget.php @@ -28,7 +28,6 @@ use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class ClearBudget. @@ -56,7 +55,7 @@ class ClearBudget implements ActionInterface $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $budget = $object->budgets()->first(); if (null === $budget) { - Log::debug(sprintf('RuleAction ClearBudget, no budget in journal #%d.', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction ClearBudget, no budget in journal #%d.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_budget'))); return false; } @@ -65,7 +64,7 @@ class ClearBudget implements ActionInterface event(new TriggeredAuditLog($this->action->rule, $object, 'clear_budget', $budget->name, null)); - Log::debug(sprintf('RuleAction ClearBudget removed all budgets from journal #%d.', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction ClearBudget removed all budgets from journal #%d.', $journal['transaction_journal_id'])); return true; } diff --git a/app/TransactionRules/Actions/ClearCategory.php b/app/TransactionRules/Actions/ClearCategory.php index 22b9df33f7..4c622a25ab 100644 --- a/app/TransactionRules/Actions/ClearCategory.php +++ b/app/TransactionRules/Actions/ClearCategory.php @@ -28,7 +28,6 @@ use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class ClearCategory. @@ -56,7 +55,7 @@ class ClearCategory implements ActionInterface $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $category = $object->categories()->first(); if (null === $category) { - Log::debug(sprintf('RuleAction ClearCategory, no category in journal #%d.', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction ClearCategory, no category in journal #%d.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_category'))); return false; } @@ -65,7 +64,7 @@ class ClearCategory implements ActionInterface event(new TriggeredAuditLog($this->action->rule, $object, 'clear_category', $category->name, null)); - Log::debug(sprintf('RuleAction ClearCategory removed all categories from journal #%d.', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction ClearCategory removed all categories from journal #%d.', $journal['transaction_journal_id'])); return true; } diff --git a/app/TransactionRules/Actions/ClearNotes.php b/app/TransactionRules/Actions/ClearNotes.php index d67a1b3d90..3215f776df 100644 --- a/app/TransactionRules/Actions/ClearNotes.php +++ b/app/TransactionRules/Actions/ClearNotes.php @@ -26,9 +26,9 @@ namespace FireflyIII\TransactionRules\Actions; use DB; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; +use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class ClearNotes. @@ -52,10 +52,12 @@ class ClearNotes implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - $notes = $object->notes()->first(); + /** @var Note|null $notes */ + $notes = $object->notes()->first(); if (null === $notes) { - Log::debug(sprintf('RuleAction ClearNotes, journal #%d has no notes.', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction ClearNotes, journal #%d has no notes.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_notes'))); return false; } @@ -65,7 +67,7 @@ class ClearNotes implements ActionInterface ->where('noteable_id', $journal['transaction_journal_id']) ->where('noteable_type', TransactionJournal::class) ->delete(); - Log::debug(sprintf('RuleAction ClearNotes removed all notes from journal #%d.', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction ClearNotes removed all notes from journal #%d.', $journal['transaction_journal_id'])); event(new TriggeredAuditLog($this->action->rule, $object, 'clear_notes', $before, null)); diff --git a/app/TransactionRules/Actions/ConvertToDeposit.php b/app/TransactionRules/Actions/ConvertToDeposit.php index 36091f7905..33b0840d5f 100644 --- a/app/TransactionRules/Actions/ConvertToDeposit.php +++ b/app/TransactionRules/Actions/ConvertToDeposit.php @@ -35,7 +35,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -65,33 +64,33 @@ class ConvertToDeposit implements ActionInterface /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { - Log::error(sprintf('Cannot find journal #%d, cannot convert to deposit.', $journal['transaction_journal_id'])); + app('log')->error(sprintf('Cannot find journal #%d, cannot convert to deposit.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found'))); return false; } $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { - Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to deposit.', $journal['transaction_group_id'])); + app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot convert to deposit.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); return false; } - Log::debug(sprintf('Convert journal #%d to deposit.', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('Convert journal #%d to deposit.', $journal['transaction_journal_id'])); $type = $object->transactionType->type; if (TransactionType::DEPOSIT === $type) { - Log::error(sprintf('Journal #%d is already a deposit (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); + app('log')->error(sprintf('Journal #%d is already a deposit (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_deposit'))); return false; } if (TransactionType::WITHDRAWAL === $type) { - Log::debug('Going to transform a withdrawal to a deposit.'); + app('log')->debug('Going to transform a withdrawal to a deposit.'); try { $res = $this->convertWithdrawalArray($object); } catch (JsonException | FireflyException $e) { - Log::debug('Could not convert withdrawal to deposit.'); - Log::error($e->getMessage()); + app('log')->debug('Could not convert withdrawal to deposit.'); + app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); return false; } @@ -101,13 +100,13 @@ class ConvertToDeposit implements ActionInterface return $res; } if (TransactionType::TRANSFER === $type) { - Log::debug('Going to transform a transfer to a deposit.'); + app('log')->debug('Going to transform a transfer to a deposit.'); try { $res = $this->convertTransferArray($object); } catch (JsonException | FireflyException $e) { - Log::debug('Could not convert transfer to deposit.'); - Log::error($e->getMessage()); + app('log')->debug('Could not convert transfer to deposit.'); + app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); return false; } @@ -153,7 +152,7 @@ class ConvertToDeposit implements ActionInterface $opposingAccount = $factory->findOrCreate($opposingName, AccountType::REVENUE); } - Log::debug(sprintf('ConvertToDeposit. Action value is "%s", new opposing name is "%s"', $this->action->action_value, $opposingAccount->name)); + app('log')->debug(sprintf('ConvertToDeposit. Action value is "%s", new opposing name is "%s"', $this->action->action_value, $opposingAccount->name)); // update the source transaction and put in the new revenue ID. DB::table('transactions') @@ -174,7 +173,7 @@ class ConvertToDeposit implements ActionInterface ->where('id', '=', $journal->id) ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); - Log::debug('Converted withdrawal to deposit.'); + app('log')->debug('Converted withdrawal to deposit.'); return true; } @@ -245,7 +244,7 @@ class ConvertToDeposit implements ActionInterface $opposingAccount = $factory->findOrCreate($opposingName, AccountType::REVENUE); } - Log::debug(sprintf('ConvertToDeposit. Action value is "%s", revenue name is "%s"', $this->action->action_value, $opposingAccount->name)); + app('log')->debug(sprintf('ConvertToDeposit. Action value is "%s", revenue name is "%s"', $this->action->action_value, $opposingAccount->name)); // update source transaction(s) to be revenue account DB::table('transactions') @@ -260,7 +259,7 @@ class ConvertToDeposit implements ActionInterface ->where('id', '=', $journal->id) ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); - Log::debug('Converted transfer to deposit.'); + app('log')->debug('Converted transfer to deposit.'); return true; } diff --git a/app/TransactionRules/Actions/ConvertToTransfer.php b/app/TransactionRules/Actions/ConvertToTransfer.php index 14fe267fc7..704d63c637 100644 --- a/app/TransactionRules/Actions/ConvertToTransfer.php +++ b/app/TransactionRules/Actions/ConvertToTransfer.php @@ -34,7 +34,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Illuminate\Support\Facades\Log; /** * @@ -63,22 +62,22 @@ class ConvertToTransfer implements ActionInterface /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { - Log::error(sprintf('Cannot find journal #%d, cannot convert to transfer.', $journal['transaction_journal_id'])); + app('log')->error(sprintf('Cannot find journal #%d, cannot convert to transfer.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found'))); return false; } $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { - Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to transfer.', $journal['transaction_group_id'])); + app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot convert to transfer.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); return false; } $type = $object->transactionType->type; $user = $object->user; - $journalId = (int)$object->id; + $journalId = $object->id; if (TransactionType::TRANSFER === $type) { - Log::error( + app('log')->error( sprintf('Journal #%d is already a transfer so cannot be converted (rule #%d).', $object->id, $this->action->rule_id) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_transfer'))); @@ -106,7 +105,7 @@ class ConvertToTransfer implements ActionInterface $opposing = $repository->findByName($this->action->action_value, [$expectedType]); if (null === $opposing) { - Log::error( + app('log')->error( sprintf( 'Journal #%d cannot be converted because no valid %s account with name "%s" exists (rule #%d).', $expectedType, @@ -121,12 +120,12 @@ class ConvertToTransfer implements ActionInterface } if (TransactionType::WITHDRAWAL === $type) { - Log::debug('Going to transform a withdrawal to a transfer.'); + app('log')->debug('Going to transform a withdrawal to a transfer.'); try { $res = $this->convertWithdrawalArray($object, $opposing); } catch (FireflyException $e) { - Log::debug('Could not convert withdrawal to transfer.'); - Log::error($e->getMessage()); + app('log')->debug('Could not convert withdrawal to transfer.'); + app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); return false; } @@ -135,23 +134,20 @@ class ConvertToTransfer implements ActionInterface } return $res; } - if (TransactionType::DEPOSIT === $type) { - Log::debug('Going to transform a deposit to a transfer.'); - try { - $res = $this->convertDepositArray($object, $opposing); - } catch (FireflyException $e) { - Log::debug('Could not convert deposit to transfer.'); - Log::error($e->getMessage()); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); - return false; - } - if (false !== $res) { - event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::TRANSFER)); - } - return $res; + // can only be a deposit at this point. + app('log')->debug('Going to transform a deposit to a transfer.'); + try { + $res = $this->convertDepositArray($object, $opposing); + } catch (FireflyException $e) { + app('log')->debug('Could not convert deposit to transfer.'); + app('log')->error($e->getMessage()); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); + return false; } - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_transfer', ['type' => $type]))); - return false; + if (false !== $res) { + event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::TRANSFER)); + } + return $res; } /** @@ -161,10 +157,10 @@ class ConvertToTransfer implements ActionInterface */ private function getSourceType(int $journalId): string { - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $journal */ $journal = TransactionJournal::find($journalId); if (null === $journal) { - Log::error(sprintf('Journal #%d does not exist. Cannot convert to transfer.', $journalId)); + app('log')->error(sprintf('Journal #%d does not exist. Cannot convert to transfer.', $journalId)); return ''; } return (string)$journal->transactions()->where('amount', '<', 0)->first()?->account?->accountType?->type; @@ -177,10 +173,10 @@ class ConvertToTransfer implements ActionInterface */ private function getDestinationType(int $journalId): string { - /** @var TransactionJournal $journal */ + /** @var TransactionJournal|null $journal */ $journal = TransactionJournal::find($journalId); if (null === $journal) { - Log::error(sprintf('Journal #%d does not exist. Cannot convert to transfer.', $journalId)); + app('log')->error(sprintf('Journal #%d does not exist. Cannot convert to transfer.', $journalId)); return ''; } return (string)$journal->transactions()->where('amount', '>', 0)->first()?->account?->accountType?->type; @@ -200,8 +196,8 @@ class ConvertToTransfer implements ActionInterface private function convertWithdrawalArray(TransactionJournal $journal, Account $opposing): bool { $sourceAccount = $this->getSourceAccount($journal); - if ((int)$sourceAccount->id === (int)$opposing->id) { - Log::error( + if ($sourceAccount->id === $opposing->id) { + app('log')->error( vsprintf( 'Journal #%d has already has "%s" as a source asset. ConvertToTransfer failed. (rule #%d).', [$journal->id, $opposing->name, $this->action->rule_id] @@ -225,7 +221,7 @@ class ConvertToTransfer implements ActionInterface ->where('id', '=', $journal->id) ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); - Log::debug('Converted withdrawal to transfer.'); + app('log')->debug('Converted withdrawal to transfer.'); return true; } @@ -259,8 +255,8 @@ class ConvertToTransfer implements ActionInterface private function convertDepositArray(TransactionJournal $journal, Account $opposing): bool { $destAccount = $this->getDestinationAccount($journal); - if ((int)$destAccount->id === (int)$opposing->id) { - Log::error( + if ($destAccount->id === $opposing->id) { + app('log')->error( vsprintf( 'Journal #%d has already has "%s" as a destination asset. ConvertToTransfer failed. (rule #%d).', [$journal->id, $opposing->name, $this->action->rule_id] @@ -284,7 +280,7 @@ class ConvertToTransfer implements ActionInterface ->where('id', '=', $journal->id) ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); - Log::debug('Converted deposit to transfer.'); + app('log')->debug('Converted deposit to transfer.'); return true; } diff --git a/app/TransactionRules/Actions/ConvertToWithdrawal.php b/app/TransactionRules/Actions/ConvertToWithdrawal.php index f844e213a0..cfd82072a4 100644 --- a/app/TransactionRules/Actions/ConvertToWithdrawal.php +++ b/app/TransactionRules/Actions/ConvertToWithdrawal.php @@ -35,7 +35,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Illuminate\Support\Facades\Log; use JsonException; /** @@ -65,20 +64,20 @@ class ConvertToWithdrawal implements ActionInterface /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { - Log::error(sprintf('Cannot find journal #%d, cannot convert to withdrawal.', $journal['transaction_journal_id'])); + app('log')->error(sprintf('Cannot find journal #%d, cannot convert to withdrawal.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found'))); return false; } $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { - Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to withdrawal.', $journal['transaction_group_id'])); + app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot convert to withdrawal.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); return false; } $type = $object->transactionType->type; if (TransactionType::WITHDRAWAL === $type) { - Log::error(sprintf('Journal #%d is already a withdrawal (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); + app('log')->error(sprintf('Journal #%d is already a withdrawal (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_withdrawal'))); return false; } @@ -87,12 +86,12 @@ class ConvertToWithdrawal implements ActionInterface return false; } if (TransactionType::DEPOSIT === $type) { - Log::debug('Going to transform a deposit to a withdrawal.'); + app('log')->debug('Going to transform a deposit to a withdrawal.'); try { $res = $this->convertDepositArray($object); } catch (JsonException | FireflyException $e) { - Log::debug('Could not convert transfer to deposit.'); - Log::error($e->getMessage()); + app('log')->debug('Could not convert transfer to deposit.'); + app('log')->error($e->getMessage()); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); return false; } @@ -100,23 +99,20 @@ class ConvertToWithdrawal implements ActionInterface return $res; } - if (TransactionType::TRANSFER === $type) { - Log::debug('Going to transform a transfer to a withdrawal.'); + // can only be transfer at this point. + app('log')->debug('Going to transform a transfer to a withdrawal.'); - try { - $res = $this->convertTransferArray($object); - } catch (JsonException | FireflyException $e) { - Log::debug('Could not convert transfer to deposit.'); - Log::error($e->getMessage()); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); - return false; - } - event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::WITHDRAWAL)); - - return $res; + try { + $res = $this->convertTransferArray($object); + } catch (JsonException | FireflyException $e) { + app('log')->debug('Could not convert transfer to deposit.'); + app('log')->error($e->getMessage()); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error'))); + return false; } - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_withdrawal', ['type' => $type]))); - return false; + event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::WITHDRAWAL)); + + return $res; } /** @@ -149,7 +145,7 @@ class ConvertToWithdrawal implements ActionInterface $opposingAccount = $factory->findOrCreate($opposingName, AccountType::EXPENSE); } - Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", expense name is "%s"', $this->action->action_value, $opposingName)); + app('log')->debug(sprintf('ConvertToWithdrawal. Action value is "%s", expense name is "%s"', $this->action->action_value, $opposingName)); // update source transaction(s) to be the original destination account DB::table('transactions') @@ -169,7 +165,7 @@ class ConvertToWithdrawal implements ActionInterface ->where('id', '=', $journal->id) ->update(['transaction_type_id' => $newType->id]); - Log::debug('Converted deposit to withdrawal.'); + app('log')->debug('Converted deposit to withdrawal.'); return true; } @@ -239,7 +235,7 @@ class ConvertToWithdrawal implements ActionInterface $opposingAccount = $factory->findOrCreate($opposingName, AccountType::EXPENSE); } - Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", destination name is "%s"', $this->action->action_value, $opposingName)); + app('log')->debug(sprintf('ConvertToWithdrawal. Action value is "%s", destination name is "%s"', $this->action->action_value, $opposingName)); // update destination transaction(s) to be new expense account. DB::table('transactions') @@ -253,7 +249,7 @@ class ConvertToWithdrawal implements ActionInterface ->where('id', '=', $journal->id) ->update(['transaction_type_id' => $newType->id]); - Log::debug('Converted transfer to withdrawal.'); + app('log')->debug('Converted transfer to withdrawal.'); return true; } diff --git a/app/TransactionRules/Actions/DeleteTransaction.php b/app/TransactionRules/Actions/DeleteTransaction.php index 0f52553457..fffa7c6118 100644 --- a/app/TransactionRules/Actions/DeleteTransaction.php +++ b/app/TransactionRules/Actions/DeleteTransaction.php @@ -29,7 +29,6 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Services\Internal\Destroy\JournalDestroyService; use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService; -use Illuminate\Support\Facades\Log; /** * Class DeleteTransaction. @@ -57,13 +56,14 @@ class DeleteTransaction implements ActionInterface // destroy entire group. if (1 === $count) { - Log::debug( + app('log')->debug( sprintf( 'RuleAction DeleteTransaction DELETED the entire transaction group of journal #%d ("%s").', $journal['transaction_journal_id'], $journal['description'] ) ); + /** @var TransactionGroup $group */ $group = TransactionGroup::find($journal['transaction_group_id']); $service = app(TransactionGroupDestroyService::class); $service->destroy($group); @@ -72,12 +72,13 @@ class DeleteTransaction implements ActionInterface return true; } - Log::debug( + app('log')->debug( sprintf('RuleAction DeleteTransaction DELETED transaction journal #%d ("%s").', $journal['transaction_journal_id'], $journal['description']) ); // trigger delete factory: - $object = TransactionJournal::find($journal['transaction_group_id']); + /** @var TransactionJournal|null $object */ + $object = TransactionJournal::find($journal['transaction_journal_id']); if (null !== $object) { /** @var JournalDestroyService $service */ $service = app(JournalDestroyService::class); diff --git a/app/TransactionRules/Actions/LinkToBill.php b/app/TransactionRules/Actions/LinkToBill.php index 74de2eeb41..3cb703a5bc 100644 --- a/app/TransactionRules/Actions/LinkToBill.php +++ b/app/TransactionRules/Actions/LinkToBill.php @@ -31,7 +31,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Class LinkToBill. @@ -56,6 +55,7 @@ class LinkToBill implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User $user */ $user = User::find($journal['user_id']); /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); @@ -67,7 +67,7 @@ class LinkToBill implements ActionInterface $count = DB::table('transaction_journals')->where('id', '=', $journal['transaction_journal_id']) ->where('bill_id', $bill->id)->count(); if (0 !== $count) { - Log::error( + app('log')->error( sprintf( 'RuleAction LinkToBill could not set the bill of journal #%d to bill "%s": already set.', $journal['transaction_journal_id'], @@ -82,17 +82,18 @@ class LinkToBill implements ActionInterface DB::table('transaction_journals') ->where('id', '=', $journal['transaction_journal_id']) ->update(['bill_id' => $bill->id]); - Log::debug( + app('log')->debug( sprintf('RuleAction LinkToBill set the bill of journal #%d to bill #%d ("%s").', $journal['transaction_journal_id'], $bill->id, $bill->name) ); + /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); event(new TriggeredAuditLog($this->action->rule, $object, 'set_bill', null, $bill->name)); return true; } - Log::error( + app('log')->error( sprintf( 'RuleAction LinkToBill could not set the bill of journal #%d to bill "%s": no such bill found or not a withdrawal.', $journal['transaction_journal_id'], diff --git a/app/TransactionRules/Actions/MoveDescriptionToNotes.php b/app/TransactionRules/Actions/MoveDescriptionToNotes.php index 9d17dd8ae2..b064e3c314 100644 --- a/app/TransactionRules/Actions/MoveDescriptionToNotes.php +++ b/app/TransactionRules/Actions/MoveDescriptionToNotes.php @@ -29,7 +29,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class MoveDescriptionToNotes @@ -54,13 +53,14 @@ class MoveDescriptionToNotes implements ActionInterface */ public function actOnArray(array $journal): bool { - /** @var TransactionJournal $object */ + /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { - Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); + app('log')->error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user'))); return false; } + /** @var Note|null $note */ $note = $object->notes()->first(); if (null === $note) { $note = new Note(); diff --git a/app/TransactionRules/Actions/MoveNotesToDescription.php b/app/TransactionRules/Actions/MoveNotesToDescription.php index 8ce108aa02..e5ee1113cb 100644 --- a/app/TransactionRules/Actions/MoveNotesToDescription.php +++ b/app/TransactionRules/Actions/MoveNotesToDescription.php @@ -29,7 +29,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Support\Request\ConvertsDataTypes; -use Illuminate\Support\Facades\Log; /** * Class MoveNotesToDescription @@ -60,10 +59,10 @@ class MoveNotesToDescription implements ActionInterface */ public function actOnArray(array $journal): bool { - /** @var TransactionJournal $object */ + /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { - Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); + app('log')->error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user'))); return false; } @@ -81,7 +80,7 @@ class MoveNotesToDescription implements ActionInterface } $before = $object->description; $beforeNote = $note->text; - $object->description = (string)$this->clearString($note->text, false); + $object->description = (string)$this->clearString($note->text); $object->save(); $note->delete(); diff --git a/app/TransactionRules/Actions/PrependNotes.php b/app/TransactionRules/Actions/PrependNotes.php index 25534fa25d..3e97f33076 100644 --- a/app/TransactionRules/Actions/PrependNotes.php +++ b/app/TransactionRules/Actions/PrependNotes.php @@ -27,7 +27,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class PrependNotes. @@ -61,7 +60,7 @@ class PrependNotes implements ActionInterface $dbNote->text = ''; } $before = $dbNote->text; - Log::debug(sprintf('RuleAction PrependNotes prepended "%s" to "%s".', $this->action->action_value, $dbNote->text)); + app('log')->debug(sprintf('RuleAction PrependNotes prepended "%s" to "%s".', $this->action->action_value, $dbNote->text)); $text = sprintf('%s%s', $this->action->action_value, $dbNote->text); $dbNote->text = $text; $dbNote->save(); diff --git a/app/TransactionRules/Actions/RemoveAllTags.php b/app/TransactionRules/Actions/RemoveAllTags.php index 87ccbe0b2f..f7dbe97782 100644 --- a/app/TransactionRules/Actions/RemoveAllTags.php +++ b/app/TransactionRules/Actions/RemoveAllTags.php @@ -28,7 +28,6 @@ use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class RemoveAllTags. @@ -55,11 +54,11 @@ class RemoveAllTags implements ActionInterface DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->delete(); $count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->count(); if (0 === $count) { - Log::debug(sprintf('RuleAction RemoveAllTags, journal #%d has no tags.', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction RemoveAllTags, journal #%d has no tags.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_tags_to_remove'))); return false; } - Log::debug(sprintf('RuleAction RemoveAllTags removed all tags from journal %d.', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction RemoveAllTags removed all tags from journal %d.', $journal['transaction_journal_id'])); /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); diff --git a/app/TransactionRules/Actions/RemoveTag.php b/app/TransactionRules/Actions/RemoveTag.php index f3f23ad9e9..0d1fc2a872 100644 --- a/app/TransactionRules/Actions/RemoveTag.php +++ b/app/TransactionRules/Actions/RemoveTag.php @@ -29,7 +29,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Class RemoveTag. @@ -55,11 +54,12 @@ class RemoveTag implements ActionInterface { // if tag does not exist, no need to continue: $name = $this->action->action_value; + /** @var User $user */ $user = User::find($journal['user_id']); $tag = $user->tags()->where('tag', $name)->first(); if (null === $tag) { - Log::debug( + app('log')->debug( sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag exists.', $name, $journal['transaction_journal_id']) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_tag', ['tag' => $name]))); @@ -67,14 +67,14 @@ class RemoveTag implements ActionInterface } $count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->where('tag_id', $tag->id)->count(); if (0 === $count) { - Log::debug( + app('log')->debug( sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag is linked.', $name, $journal['transaction_journal_id']) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_unlink_tag', ['tag' => $name]))); return false; } - Log::debug(sprintf('RuleAction RemoveTag removed tag #%d ("%s") from journal #%d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); + app('log')->debug(sprintf('RuleAction RemoveTag removed tag #%d ("%s") from journal #%d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); DB::table('tag_transaction_journal') ->where('transaction_journal_id', $journal['transaction_journal_id']) ->where('tag_id', $tag->id) diff --git a/app/TransactionRules/Actions/SetBudget.php b/app/TransactionRules/Actions/SetBudget.php index 1dbdc54c18..66804fed0a 100644 --- a/app/TransactionRules/Actions/SetBudget.php +++ b/app/TransactionRules/Actions/SetBudget.php @@ -30,7 +30,6 @@ use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Class SetBudget. @@ -54,12 +53,13 @@ class SetBudget implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User $user */ $user = User::find($journal['user_id']); $search = $this->action->action_value; $budget = $user->budgets()->where('name', $search)->first(); if (null === $budget) { - Log::debug( + app('log')->debug( sprintf( 'RuleAction SetBudget could not set budget of journal #%d to "%s" because no such budget exists.', $journal['transaction_journal_id'], @@ -71,7 +71,7 @@ class SetBudget implements ActionInterface } if (TransactionType::WITHDRAWAL !== $journal['transaction_type_type']) { - Log::debug( + app('log')->debug( sprintf( 'RuleAction SetBudget could not set budget of journal #%d to "%s" because journal is a %s.', $journal['transaction_journal_id'], @@ -88,13 +88,13 @@ class SetBudget implements ActionInterface $object = $user->transactionJournals()->find($journal['transaction_journal_id']); $oldBudget = $object->budgets()->first(); $oldBudgetName = $oldBudget?->name; - if ((int)$oldBudget?->id === (int)$budget->id) { + if ((int)$oldBudget?->id === $budget->id) { event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_linked_to_budget', ['name' => $budget->name]))); return false; } - Log::debug( + app('log')->debug( sprintf('RuleAction SetBudget set the budget of journal #%d to budget #%d ("%s").', $journal['transaction_journal_id'], $budget->id, $budget->name) ); diff --git a/app/TransactionRules/Actions/SetCategory.php b/app/TransactionRules/Actions/SetCategory.php index 30608a3f11..ba7106ac63 100644 --- a/app/TransactionRules/Actions/SetCategory.php +++ b/app/TransactionRules/Actions/SetCategory.php @@ -30,7 +30,6 @@ use FireflyIII\Factory\CategoryFactory; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Class SetCategory. @@ -54,10 +53,11 @@ class SetCategory implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User|null $user */ $user = User::find($journal['user_id']); $search = $this->action->action_value; if (null === $user) { - Log::error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal); + app('log')->error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); return false; } @@ -67,7 +67,7 @@ class SetCategory implements ActionInterface $factory->setUser($user); $category = $factory->findOrCreate(null, $search); if (null === $category) { - Log::debug( + app('log')->debug( sprintf( 'RuleAction SetCategory could not set category of journal #%d to "%s" because no such category exists.', $journal['transaction_journal_id'], @@ -78,7 +78,7 @@ class SetCategory implements ActionInterface return false; } - Log::debug( + app('log')->debug( sprintf( 'RuleAction SetCategory set the category of journal #%d to category #%d ("%s").', $journal['transaction_journal_id'], @@ -92,7 +92,7 @@ class SetCategory implements ActionInterface $object = $user->transactionJournals()->find($journal['transaction_journal_id']); $oldCategory = $object->categories()->first(); $oldCategoryName = $oldCategory?->name; - if ((int)$oldCategory?->id === (int)$category->id) { + if ((int)$oldCategory?->id === $category->id) { event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_linked_to_category', ['name' => $category->name]))); return false; } diff --git a/app/TransactionRules/Actions/SetDescription.php b/app/TransactionRules/Actions/SetDescription.php index 26e3a2be9d..7da6199f67 100644 --- a/app/TransactionRules/Actions/SetDescription.php +++ b/app/TransactionRules/Actions/SetDescription.php @@ -27,7 +27,6 @@ use DB; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class SetDescription. @@ -59,7 +58,7 @@ class SetDescription implements ActionInterface ->where('id', '=', $journal['transaction_journal_id']) ->update(['description' => $this->action->action_value]); - Log::debug( + app('log')->debug( sprintf( 'RuleAction SetDescription changed the description of journal #%d from "%s" to "%s".', $journal['transaction_journal_id'], diff --git a/app/TransactionRules/Actions/SetDestinationAccount.php b/app/TransactionRules/Actions/SetDestinationAccount.php index 1daa030c64..82f3a25c8a 100644 --- a/app/TransactionRules/Actions/SetDestinationAccount.php +++ b/app/TransactionRules/Actions/SetDestinationAccount.php @@ -33,7 +33,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Class SetDestinationAccount. @@ -58,14 +57,14 @@ class SetDestinationAccount implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User $user */ $user = User::find($journal['user_id']); - $type = $journal['transaction_type_type']; /** @var TransactionJournal|null $object */ $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $this->repository = app(AccountRepositoryInterface::class); if (null === $object) { - Log::error('Could not find journal.'); + app('log')->error('Could not find journal.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); return false; } @@ -75,7 +74,7 @@ class SetDestinationAccount implements ActionInterface // if this is a transfer or a deposit, the new destination account must be an asset account or a default account, and it MUST exist: $newAccount = $this->findAssetAccount($type); if ((TransactionType::DEPOSIT === $type || TransactionType::TRANSFER === $type) && null === $newAccount) { - Log::error( + app('log')->error( sprintf( 'Cant change destination account of journal #%d because no asset account with name "%s" exists.', $object->id, @@ -90,18 +89,18 @@ class SetDestinationAccount implements ActionInterface /** @var Transaction|null $source */ $source = $object->transactions()->where('amount', '<', 0)->first(); if (null === $source) { - Log::error('Could not find source transaction.'); + app('log')->error('Could not find source transaction.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction'))); return false; } // account must not be deleted (in the meantime): if (null === $source->account) { - Log::error('Could not find source transaction account.'); + app('log')->error('Could not find source transaction account.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction_account'))); return false; } - if (null !== $newAccount && (int)$newAccount->id === (int)$source->account_id) { - Log::error( + if (null !== $newAccount && $newAccount->id === $source->account_id) { + app('log')->error( sprintf( 'New destination account ID #%d and current source account ID #%d are the same. Do nothing.', $newAccount->id, @@ -119,7 +118,7 @@ class SetDestinationAccount implements ActionInterface $newAccount = $this->findWithdrawalDestinationAccount(); } - Log::debug(sprintf('New destination account is #%d ("%s").', $newAccount->id, $newAccount->name)); + app('log')->debug(sprintf('New destination account is #%d ("%s").', $newAccount->id, $newAccount->name)); event(new TriggeredAuditLog($this->action->rule, $object, 'set_destination', null, $newAccount->name)); @@ -129,7 +128,7 @@ class SetDestinationAccount implements ActionInterface ->where('amount', '>', 0) ->update(['account_id' => $newAccount->id]); - Log::debug(sprintf('Updated journal #%d (group #%d) and gave it new destination account ID.', $object->id, $object->transaction_group_id)); + app('log')->debug(sprintf('Updated journal #%d (group #%d) and gave it new destination account ID.', $object->id, $object->transaction_group_id)); return true; } @@ -144,7 +143,7 @@ class SetDestinationAccount implements ActionInterface // switch on type: $allowed = config(sprintf('firefly.expected_source_types.destination.%s', $type)); $allowed = is_array($allowed) ? $allowed : []; - Log::debug(sprintf('Check config for expected_source_types.destination.%s, result is', $type), $allowed); + app('log')->debug(sprintf('Check config for expected_source_types.destination.%s, result is', $type), $allowed); return $this->repository->findByName($this->action->action_value, $allowed); } @@ -167,7 +166,7 @@ class SetDestinationAccount implements ActionInterface ]; $account = $this->repository->store($data); } - Log::debug(sprintf('Found or created expense account #%d ("%s")', $account->id, $account->name)); + app('log')->debug(sprintf('Found or created expense account #%d ("%s")', $account->id, $account->name)); return $account; } diff --git a/app/TransactionRules/Actions/SetDestinationToCashAccount.php b/app/TransactionRules/Actions/SetDestinationToCashAccount.php new file mode 100644 index 0000000000..549420e08b --- /dev/null +++ b/app/TransactionRules/Actions/SetDestinationToCashAccount.php @@ -0,0 +1,120 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\TransactionRules\Actions; + +use DB; +use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; +use FireflyIII\Events\TriggeredAuditLog; +use FireflyIII\Models\RuleAction; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\User; + +/** + * Class SetDestinationToCashAccount + */ +class SetDestinationToCashAccount implements ActionInterface +{ + private RuleAction $action; + + /** + * TriggerInterface constructor. + * + * @param RuleAction $action + */ + public function __construct(RuleAction $action) + { + $this->action = $action; + } + + /** + * @inheritDoc + */ + public function actOnArray(array $journal): bool + { + /** @var User $user */ + $user = User::find($journal['user_id']); + /** @var TransactionJournal|null $object */ + $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); + $repository = app(AccountRepositoryInterface::class); + + if (null === $object) { + app('log')->error('Could not find journal.'); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); + return false; + } + $type = $object->transactionType->type; + if (TransactionType::WITHDRAWAL !== $type) { + app('log')->error('Transaction must be withdrawal.'); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.not_withdrawal'))); + return false; + } + + // find cash account + $repository->setUser($user); + $cashAccount = $repository->getCashAccount(); + + // new destination account must be different from the current source account: + /** @var Transaction|null $source */ + $source = $object->transactions()->where('amount', '<', 0)->first(); + if (null === $source) { + app('log')->error('Could not find source transaction.'); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction'))); + return false; + } + // account must not be deleted (in the meantime): + if (null === $source->account) { + app('log')->error('Could not find source transaction account.'); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction_account'))); + return false; + } + if ($cashAccount->id === $source->account_id) { + app('log')->error( + sprintf( + 'New destination account ID #%d and current source account ID #%d are the same. Do nothing.', + $cashAccount->id, + $source->account_id + ) + ); + + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_destination', ['name' => $cashAccount->name]))); + return false; + } + + event(new TriggeredAuditLog($this->action->rule, $object, 'set_destination', null, $cashAccount->name)); + + // update destination transaction with new destination account: + DB::table('transactions') + ->where('transaction_journal_id', '=', $object->id) + ->where('amount', '>', 0) + ->update(['account_id' => $cashAccount->id]); + + app('log')->debug(sprintf('Updated journal #%d (group #%d) and gave it new destination account ID.', $object->id, $object->transaction_group_id)); + + return true; + + } +} diff --git a/app/TransactionRules/Actions/SetNotes.php b/app/TransactionRules/Actions/SetNotes.php index a1874c241c..ec4431dbfa 100644 --- a/app/TransactionRules/Actions/SetNotes.php +++ b/app/TransactionRules/Actions/SetNotes.php @@ -27,7 +27,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use Illuminate\Support\Facades\Log; /** * Class SetNotes. @@ -63,7 +62,7 @@ class SetNotes implements ActionInterface $dbNote->text = $this->action->action_value; $dbNote->save(); - Log::debug( + app('log')->debug( sprintf( 'RuleAction SetNotes changed the notes of journal #%d from "%s" to "%s".', $journal['transaction_journal_id'], diff --git a/app/TransactionRules/Actions/SetSourceAccount.php b/app/TransactionRules/Actions/SetSourceAccount.php index 724b47b986..87af4fef4b 100644 --- a/app/TransactionRules/Actions/SetSourceAccount.php +++ b/app/TransactionRules/Actions/SetSourceAccount.php @@ -33,7 +33,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Class SetSourceAccount. @@ -58,13 +57,13 @@ class SetSourceAccount implements ActionInterface */ public function actOnArray(array $journal): bool { + /** @var User $user */ $user = User::find($journal['user_id']); - $type = $journal['transaction_type_type']; /** @var TransactionJournal|null $object */ $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $this->repository = app(AccountRepositoryInterface::class); if (null === $object) { - Log::error('Could not find journal.'); + app('log')->error('Could not find journal.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); return false; } @@ -74,7 +73,7 @@ class SetSourceAccount implements ActionInterface // if this is a transfer or a withdrawal, the new source account must be an asset account or a default account, and it MUST exist: $newAccount = $this->findAssetAccount($type); if ((TransactionType::WITHDRAWAL === $type || TransactionType::TRANSFER === $type) && null === $newAccount) { - Log::error( + app('log')->error( sprintf('Cant change source account of journal #%d because no asset account with name "%s" exists.', $object->id, $this->action->action_value) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value]))); @@ -85,18 +84,18 @@ class SetSourceAccount implements ActionInterface /** @var Transaction|null $destination */ $destination = $object->transactions()->where('amount', '>', 0)->first(); if (null === $destination) { - Log::error('Could not find destination transaction.'); + app('log')->error('Could not find destination transaction.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction'))); return false; } // account must not be deleted (in the meantime): if (null === $destination->account) { - Log::error('Could not find destination transaction account.'); + app('log')->error('Could not find destination transaction account.'); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction_account'))); return false; } - if (null !== $newAccount && (int)$newAccount->id === (int)$destination->account_id) { - Log::error( + if (null !== $newAccount && $newAccount->id === $destination->account_id) { + app('log')->error( sprintf( 'New source account ID #%d and current destination account ID #%d are the same. Do nothing.', $newAccount->id, @@ -113,7 +112,7 @@ class SetSourceAccount implements ActionInterface $newAccount = $this->findDepositSourceAccount(); } - Log::debug(sprintf('New source account is #%d ("%s").', $newAccount->id, $newAccount->name)); + app('log')->debug(sprintf('New source account is #%d ("%s").', $newAccount->id, $newAccount->name)); // update source transaction with new source account: DB::table('transactions') @@ -123,7 +122,7 @@ class SetSourceAccount implements ActionInterface event(new TriggeredAuditLog($this->action->rule, $object, 'set_source', null, $newAccount->name)); - Log::debug(sprintf('Updated journal #%d (group #%d) and gave it new source account ID.', $object->id, $object->transaction_group_id)); + app('log')->debug(sprintf('Updated journal #%d (group #%d) and gave it new source account ID.', $object->id, $object->transaction_group_id)); return true; } @@ -138,7 +137,7 @@ class SetSourceAccount implements ActionInterface // switch on type: $allowed = config(sprintf('firefly.expected_source_types.source.%s', $type)); $allowed = is_array($allowed) ? $allowed : []; - Log::debug(sprintf('Check config for expected_source_types.source.%s, result is', $type), $allowed); + app('log')->debug(sprintf('Check config for expected_source_types.source.%s, result is', $type), $allowed); return $this->repository->findByName($this->action->action_value, $allowed); } @@ -162,7 +161,7 @@ class SetSourceAccount implements ActionInterface ]; $account = $this->repository->store($data); } - Log::debug(sprintf('Found or created revenue account #%d ("%s")', $account->id, $account->name)); + app('log')->debug(sprintf('Found or created revenue account #%d ("%s")', $account->id, $account->name)); return $account; } diff --git a/app/TransactionRules/Actions/SetSourceToCashAccount.php b/app/TransactionRules/Actions/SetSourceToCashAccount.php new file mode 100644 index 0000000000..11339594fb --- /dev/null +++ b/app/TransactionRules/Actions/SetSourceToCashAccount.php @@ -0,0 +1,119 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\TransactionRules\Actions; + +use DB; +use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; +use FireflyIII\Events\TriggeredAuditLog; +use FireflyIII\Models\RuleAction; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\User; + +/** + * Class SetSourceToCashAccount + */ +class SetSourceToCashAccount implements ActionInterface +{ + private RuleAction $action; + + /** + * TriggerInterface constructor. + * + * @param RuleAction $action + */ + public function __construct(RuleAction $action) + { + $this->action = $action; + } + + /** + * @inheritDoc + */ + public function actOnArray(array $journal): bool + { + /** @var User $user */ + $user = User::find($journal['user_id']); + /** @var TransactionJournal|null $object */ + $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); + $repository = app(AccountRepositoryInterface::class); + + if (null === $object) { + app('log')->error('Could not find journal.'); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); + return false; + } + $type = $object->transactionType->type; + if (TransactionType::DEPOSIT !== $type) { + app('log')->error('Transaction must be deposit.'); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.not_deposit'))); + return false; + } + + // find cash account + $repository->setUser($user); + $cashAccount = $repository->getCashAccount(); + + // new source account must be different from the current destination account: + /** @var Transaction|null $destination */ + $destination = $object->transactions()->where('amount', '>', 0)->first(); + if (null === $destination) { + app('log')->error('Could not find destination transaction.'); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction'))); + return false; + } + // account must not be deleted (in the meantime): + if (null === $destination->account) { + app('log')->error('Could not find destination transaction account.'); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction_account'))); + return false; + } + if ($cashAccount->id === $destination->account_id) { + app('log')->error( + sprintf( + 'New source account ID #%d and current destination account ID #%d are the same. Do nothing.', + $cashAccount->id, + $destination->account_id + ) + ); + + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_source', ['name' => $cashAccount->name]))); + return false; + } + + event(new TriggeredAuditLog($this->action->rule, $object, 'set_source', null, $cashAccount->name)); + + // update destination transaction with new destination account: + DB::table('transactions') + ->where('transaction_journal_id', '=', $object->id) + ->where('amount', '<', 0) + ->update(['account_id' => $cashAccount->id]); + + app('log')->debug(sprintf('Updated journal #%d (group #%d) and gave it new source account ID.', $object->id, $object->transaction_group_id)); + + return true; + } +} diff --git a/app/TransactionRules/Actions/SwitchAccounts.php b/app/TransactionRules/Actions/SwitchAccounts.php index 0839dcf5ae..8ea7dc5acd 100644 --- a/app/TransactionRules/Actions/SwitchAccounts.php +++ b/app/TransactionRules/Actions/SwitchAccounts.php @@ -29,7 +29,6 @@ use FireflyIII\Models\RuleAction; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; -use Illuminate\Support\Facades\Log; /** * @@ -58,34 +57,34 @@ class SwitchAccounts implements ActionInterface /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { - Log::error(sprintf('Cannot find journal #%d, cannot switch accounts.', $journal['transaction_journal_id'])); + app('log')->error(sprintf('Cannot find journal #%d, cannot switch accounts.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); return false; } $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { - Log::error(sprintf('Group #%d has more than one transaction in it, cannot switch accounts.', $journal['transaction_group_id'])); + app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot switch accounts.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); return false; } $type = $object->transactionType->type; if (TransactionType::TRANSFER !== $type) { - Log::error(sprintf('Journal #%d is NOT a transfer (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id)); + app('log')->error(sprintf('Journal #%d is NOT a transfer (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_not_transfer'))); return false; } - /** @var Transaction $sourceTransaction */ + /** @var Transaction|null $sourceTransaction */ $sourceTransaction = $object->transactions()->where('amount', '<', 0)->first(); - /** @var Transaction $destTransaction */ + /** @var Transaction|null $destTransaction */ $destTransaction = $object->transactions()->where('amount', '>', 0)->first(); if (null === $sourceTransaction || null === $destTransaction) { - Log::error(sprintf('Journal #%d has no source or destination transaction (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id)); + app('log')->error(sprintf('Journal #%d has no source or destination transaction (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_accounts'))); return false; } - $sourceAccountId = (int)$sourceTransaction->account_id; + $sourceAccountId = $sourceTransaction->account_id; $destinationAccountId = $destTransaction->account_id; $sourceTransaction->account_id = $destinationAccountId; $destTransaction->account_id = $sourceAccountId; diff --git a/app/TransactionRules/Actions/UpdatePiggybank.php b/app/TransactionRules/Actions/UpdatePiggybank.php index c0699f258d..62fceba69b 100644 --- a/app/TransactionRules/Actions/UpdatePiggybank.php +++ b/app/TransactionRules/Actions/UpdatePiggybank.php @@ -30,10 +30,8 @@ use FireflyIII\Models\PiggyBank; use FireflyIII\Models\RuleAction; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\User; -use Illuminate\Support\Facades\Log; /** * Class UpdatePiggybank @@ -57,32 +55,32 @@ class UpdatePiggybank implements ActionInterface */ public function actOnArray(array $journal): bool { - Log::debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id'])); + app('log')->debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id'])); // refresh the transaction type. + /** @var User $user */ $user = User::find($journal['user_id']); /** @var TransactionJournal $journalObj */ $journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']); - $type = TransactionType::find((int)$journalObj->transaction_type_id); $piggyBank = $this->findPiggyBank($user); if (null === $piggyBank) { - Log::info( + app('log')->info( sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $this->action->action_value, $this->action->id, $this->action->rule_id) ); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $this->action->action_value]))); return false; } - Log::debug(sprintf('Found piggy bank #%d ("%s")', $piggyBank->id, $piggyBank->name)); + app('log')->debug(sprintf('Found piggy bank #%d ("%s")', $piggyBank->id, $piggyBank->name)); /** @var Transaction $source */ $source = $journalObj->transactions()->where('amount', '<', 0)->first(); /** @var Transaction $destination */ $destination = $journalObj->transactions()->where('amount', '>', 0)->first(); - if ((int)$source->account_id === (int)$piggyBank->account_id) { - Log::debug('Piggy bank account is linked to source, so remove amount from piggy bank.'); + if ($source->account_id === $piggyBank->account_id) { + app('log')->debug('Piggy bank account is linked to source, so remove amount from piggy bank.'); $this->removeAmount($piggyBank, $journalObj, $destination->amount); event( @@ -102,8 +100,8 @@ class UpdatePiggybank implements ActionInterface return true; } - if ((int)$destination->account_id === (int)$piggyBank->account_id) { - Log::debug('Piggy bank account is linked to source, so add amount to piggy bank.'); + if ($destination->account_id === $piggyBank->account_id) { + app('log')->debug('Piggy bank account is linked to source, so add amount to piggy bank.'); $this->addAmount($piggyBank, $journalObj, $destination->amount); event( @@ -123,7 +121,7 @@ class UpdatePiggybank implements ActionInterface return true; } - Log::info( + app('log')->info( sprintf( 'Piggy bank is not linked to source ("#%d") or destination ("#%d"), so no action will be taken.', $source->account_id, @@ -158,11 +156,11 @@ class UpdatePiggybank implements ActionInterface // how much can we remove from this piggy bank? $toRemove = $repository->getCurrentAmount($piggyBank); - Log::debug(sprintf('Amount is %s, max to remove is %s', $amount, $toRemove)); + app('log')->debug(sprintf('Amount is %s, max to remove is %s', $amount, $toRemove)); // if $amount is bigger than $toRemove, shrink it. $amount = -1 === bccomp($amount, $toRemove) ? $amount : $toRemove; - Log::debug(sprintf('Amount is now %s', $amount)); + app('log')->debug(sprintf('Amount is now %s', $amount)); // if amount is zero, stop. if (0 === bccomp('0', $amount)) { @@ -177,7 +175,7 @@ class UpdatePiggybank implements ActionInterface return; } - Log::debug(sprintf('Will now remove %s from piggy bank.', $amount)); + app('log')->debug(sprintf('Will now remove %s from piggy bank.', $amount)); $repository->removeAmount($piggyBank, $amount, $journal); } @@ -197,14 +195,14 @@ class UpdatePiggybank implements ActionInterface // how much can we add to the piggy bank? if (0 !== bccomp($piggyBank->targetamount, '0')) { $toAdd = bcsub($piggyBank->targetamount, $repository->getCurrentAmount($piggyBank)); - Log::debug(sprintf('Max amount to add to piggy bank is %s, amount is %s', $toAdd, $amount)); + app('log')->debug(sprintf('Max amount to add to piggy bank is %s, amount is %s', $toAdd, $amount)); // update amount to fit: $amount = -1 === bccomp($amount, $toAdd) ? $amount : $toAdd; - Log::debug(sprintf('Amount is now %s', $amount)); + app('log')->debug(sprintf('Amount is now %s', $amount)); } if (0 === bccomp($piggyBank->targetamount, '0')) { - Log::debug('Target amount is zero, can add anything.'); + app('log')->debug('Target amount is zero, can add anything.'); } @@ -221,7 +219,7 @@ class UpdatePiggybank implements ActionInterface return; } - Log::debug(sprintf('Will now add %s to piggy bank.', $amount)); + app('log')->debug(sprintf('Will now add %s to piggy bank.', $amount)); $repository->addAmount($piggyBank, $amount, $journal); } diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index cb7ffa47b0..ff720b8068 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -35,7 +35,6 @@ use FireflyIII\Support\Search\SearchInterface; use FireflyIII\TransactionRules\Factory\ActionFactory; use FireflyIII\User; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; /** * Class SearchRuleEngine @@ -65,7 +64,7 @@ class SearchRuleEngine implements RuleEngineInterface */ public function addOperator(array $operator): void { - Log::debug('Add extra operator: ', $operator); + app('log')->debug('Add extra operator: ', $operator); $this->operators[] = $operator; } @@ -74,7 +73,7 @@ class SearchRuleEngine implements RuleEngineInterface */ public function find(): Collection { - Log::debug('SearchRuleEngine::find()'); + app('log')->debug('SearchRuleEngine::find()'); $collection = new Collection(); foreach ($this->rules as $rule) { $found = new Collection(); @@ -99,7 +98,7 @@ class SearchRuleEngine implements RuleEngineInterface */ private function findStrictRule(Rule $rule): Collection { - Log::debug(sprintf('Now in findStrictRule(#%d)', $rule->id ?? 0)); + app('log')->debug(sprintf('Now in findStrictRule(#%d)', $rule->id ?? 0)); $searchArray = []; $triggers = []; if ($this->refreshTriggers) { @@ -116,13 +115,13 @@ class SearchRuleEngine implements RuleEngineInterface } // if needs no context, value is different: - $needsContext = config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true; + $needsContext = (bool)(config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true); if (false === $needsContext) { - Log::debug(sprintf('SearchRuleEngine:: add a rule trigger: %s:true', $ruleTrigger->trigger_type)); + app('log')->debug(sprintf('SearchRuleEngine:: add a rule trigger (no context): %s:true', $ruleTrigger->trigger_type)); $searchArray[$ruleTrigger->trigger_type][] = 'true'; } if (true === $needsContext) { - Log::debug(sprintf('SearchRuleEngine:: add a rule trigger: %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value)); + app('log')->debug(sprintf('SearchRuleEngine:: add a rule trigger (context): %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value)); $searchArray[$ruleTrigger->trigger_type][] = sprintf('"%s"', $ruleTrigger->trigger_value); } } @@ -130,13 +129,14 @@ class SearchRuleEngine implements RuleEngineInterface // add local operators: foreach ($this->operators as $operator) { - Log::debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value'])); + app('log')->debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value'])); $searchArray[$operator['type']][] = sprintf('"%s"', $operator['value']); } $date = today(config('app.timezone')); if ($this->hasSpecificJournalTrigger($searchArray)) { $date = $this->setDateFromJournalTrigger($searchArray); } + // build and run the search engine. $searchEngine = app(SearchInterface::class); $searchEngine->setUser($this->user); @@ -146,7 +146,9 @@ class SearchRuleEngine implements RuleEngineInterface foreach ($searchArray as $type => $searches) { foreach ($searches as $value) { - $searchEngine->parseQuery(sprintf('%s:%s', $type, $value)); + $query = sprintf('%s:%s', $type, $value); + app('log')->debug(sprintf('SearchRuleEngine:: add query "%s"', $query)); + $searchEngine->parseQuery($query); } } @@ -166,21 +168,21 @@ class SearchRuleEngine implements RuleEngineInterface */ private function hasSpecificJournalTrigger(array $array): bool { - Log::debug('Now in hasSpecificJournalTrigger.'); + app('log')->debug('Now in hasSpecificJournalTrigger.'); $journalTrigger = false; $dateTrigger = false; foreach ($array as $triggerName => $values) { if ('journal_id' === $triggerName && is_array($values) && 1 === count($values)) { - Log::debug('Found a journal_id trigger with 1 journal, true.'); + app('log')->debug('Found a journal_id trigger with 1 journal, true.'); $journalTrigger = true; } if (in_array($triggerName, ['date_is', 'date', 'on', 'date_before', 'before', 'date_after', 'after'], true)) { - Log::debug('Found a date related trigger, set to true.'); + app('log')->debug('Found a date related trigger, set to true.'); $dateTrigger = true; } } $result = $journalTrigger && $dateTrigger; - Log::debug(sprintf('Result of hasSpecificJournalTrigger is %s.', var_export($result, true))); + app('log')->debug(sprintf('Result of hasSpecificJournalTrigger is %s.', var_export($result, true))); return $result; } @@ -192,12 +194,12 @@ class SearchRuleEngine implements RuleEngineInterface */ private function setDateFromJournalTrigger(array $array): Carbon { - Log::debug('Now in setDateFromJournalTrigger()'); + app('log')->debug('Now in setDateFromJournalTrigger()'); $journalId = 0; foreach ($array as $triggerName => $values) { if ('journal_id' === $triggerName && is_array($values) && 1 === count($values)) { $journalId = (int)trim(($values[0] ?? '"0"'), '"'); // follows format "123". - Log::debug(sprintf('Found journal ID #%d', $journalId)); + app('log')->debug(sprintf('Found journal ID #%d', $journalId)); } } if (0 !== $journalId) { @@ -206,12 +208,12 @@ class SearchRuleEngine implements RuleEngineInterface $journal = $repository->find($journalId); if (null !== $journal) { $date = $journal->date; - Log::debug(sprintf('Found journal #%d with date %s.', $journal->id, $journal->date->format('Y-m-d'))); + app('log')->debug(sprintf('Found journal #%d with date %s.', $journal->id, $journal->date->format('Y-m-d'))); return $date; } } - Log::debug('Found no journal, return default date.'); + app('log')->debug('Found no journal, return default date.'); return today(config('app.timezone')); } @@ -249,23 +251,23 @@ class SearchRuleEngine implements RuleEngineInterface continue; } if ('user_action' === $ruleTrigger->trigger_type) { - Log::debug('Skip trigger type.'); + app('log')->debug('Skip trigger type.'); continue; } $searchArray = []; $needsContext = config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true; if (false === $needsContext) { - Log::debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:true', $ruleTrigger->trigger_type)); + app('log')->debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:true', $ruleTrigger->trigger_type)); $searchArray[$ruleTrigger->trigger_type] = 'true'; } if (true === $needsContext) { - Log::debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value)); + app('log')->debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value)); $searchArray[$ruleTrigger->trigger_type] = sprintf('"%s"', $ruleTrigger->trigger_value); } // then, add local operators as well: foreach ($this->operators as $operator) { - Log::debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value'])); + app('log')->debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value'])); $searchArray[$operator['type']] = sprintf('"%s"', $operator['value']); } @@ -281,29 +283,29 @@ class SearchRuleEngine implements RuleEngineInterface $result = $searchEngine->searchTransactions(); $collection = $result->getCollection(); - Log::debug(sprintf('Found in this run, %d transactions', $collection->count())); + app('log')->debug(sprintf('Found in this run, %d transactions', $collection->count())); $total = $total->merge($collection); - Log::debug(sprintf('Total collection is now %d transactions', $total->count())); + app('log')->debug(sprintf('Total collection is now %d transactions', $total->count())); $count++; } - Log::debug(sprintf('Total collection is now %d transactions', $total->count())); - Log::debug(sprintf('Done running %d trigger(s)', $count)); + app('log')->debug(sprintf('Total collection is now %d transactions', $total->count())); + app('log')->debug(sprintf('Done running %d trigger(s)', $count)); // make collection unique $unique = $total->unique( - function (array $group) { + static function (array $group) { $str = ''; foreach ($group['transactions'] as $transaction) { $str = sprintf('%s%d', $str, $transaction['transaction_journal_id']); } $key = sprintf('%d%s', $group['id'], $str); - //Log::debug(sprintf('Return key: %s ', $key)); + //app('log')->debug(sprintf('Return key: %s ', $key)); return $key; } ); - Log::debug(sprintf('SearchRuleEngine:: Found %d transactions using search engine.', $unique->count())); + app('log')->debug(sprintf('SearchRuleEngine:: Found %d transactions using search engine.', $unique->count())); return $unique; } @@ -315,27 +317,27 @@ class SearchRuleEngine implements RuleEngineInterface public function fire(): void { $this->resultCount = []; - Log::debug('SearchRuleEngine::fire()!'); + app('log')->debug('SearchRuleEngine::fire()!'); // if rules and no rule groups, file each rule separately. if (0 !== $this->rules->count()) { - Log::debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count())); + app('log')->debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count())); foreach ($this->rules as $rule) { $this->fireRule($rule); } - Log::debug('SearchRuleEngine:: done processing all rules!'); + app('log')->debug('SearchRuleEngine:: done processing all rules!'); return; } if (0 !== $this->groups->count()) { - Log::debug(sprintf('SearchRuleEngine:: found %d rule group(s) to fire.', $this->groups->count())); + app('log')->debug(sprintf('SearchRuleEngine:: found %d rule group(s) to fire.', $this->groups->count())); // fire each group: /** @var RuleGroup $group */ foreach ($this->groups as $group) { $this->fireGroup($group); } } - Log::debug('SearchRuleEngine:: done processing all rules!'); + app('log')->debug('SearchRuleEngine:: done processing all rules!'); } /** @@ -348,18 +350,18 @@ class SearchRuleEngine implements RuleEngineInterface */ private function fireRule(Rule $rule): bool { - Log::debug(sprintf('Now going to fire rule #%d', $rule->id)); + app('log')->debug(sprintf('Now going to fire rule #%d', $rule->id)); if (false === $rule->active) { - Log::debug(sprintf('Rule #%d is not active!', $rule->id)); + app('log')->debug(sprintf('Rule #%d is not active!', $rule->id)); return false; } if (true === $rule->strict) { - Log::debug(sprintf('Rule #%d is a strict rule.', $rule->id)); + app('log')->debug(sprintf('Rule #%d is a strict rule.', $rule->id)); return $this->fireStrictRule($rule); } - Log::debug(sprintf('Rule #%d is not strict rule.', $rule->id)); + app('log')->debug(sprintf('Rule #%d is not strict rule.', $rule->id)); return $this->fireNonStrictRule($rule); } @@ -374,19 +376,19 @@ class SearchRuleEngine implements RuleEngineInterface */ private function fireStrictRule(Rule $rule): bool { - Log::debug(sprintf('SearchRuleEngine::fireStrictRule(%d)!', $rule->id)); + app('log')->debug(sprintf('SearchRuleEngine::fireStrictRule(%d)!', $rule->id)); $collection = $this->findStrictRule($rule); $this->processResults($rule, $collection); - Log::debug(sprintf('SearchRuleEngine:: done processing strict rule #%d', $rule->id)); + app('log')->debug(sprintf('SearchRuleEngine:: done processing strict rule #%d', $rule->id)); $result = $collection->count() > 0; if (true === $result) { - Log::debug(sprintf('SearchRuleEngine:: rule #%d was triggered (on %d transaction(s)).', $rule->id, $collection->count())); + app('log')->debug(sprintf('SearchRuleEngine:: rule #%d was triggered (on %d transaction(s)).', $rule->id, $collection->count())); return true; } - Log::debug(sprintf('SearchRuleEngine:: rule #%d was not triggered (on %d transaction(s)).', $rule->id, $collection->count())); + app('log')->debug(sprintf('SearchRuleEngine:: rule #%d was not triggered (on %d transaction(s)).', $rule->id, $collection->count())); return false; } @@ -399,7 +401,7 @@ class SearchRuleEngine implements RuleEngineInterface */ private function processResults(Rule $rule, Collection $collection): void { - Log::debug(sprintf('SearchRuleEngine:: Going to process %d results.', $collection->count())); + app('log')->debug(sprintf('SearchRuleEngine:: Going to process %d results.', $collection->count())); /** @var array $group */ foreach ($collection as $group) { $this->processTransactionGroup($rule, $group); @@ -414,7 +416,7 @@ class SearchRuleEngine implements RuleEngineInterface */ private function processTransactionGroup(Rule $rule, array $group): void { - Log::debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction group #%d', $group['id'])); + app('log')->debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction group #%d', $group['id'])); /** @var array $transaction */ foreach ($group['transactions'] as $transaction) { $this->processTransactionJournal($rule, $transaction); @@ -429,7 +431,7 @@ class SearchRuleEngine implements RuleEngineInterface */ private function processTransactionJournal(Rule $rule, array $transaction): void { - Log::debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id'])); + app('log')->debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id'])); $actions = $rule->ruleActions()->orderBy('order', 'ASC')->get(); /** @var RuleAction $ruleAction */ foreach ($actions as $ruleAction) { @@ -452,13 +454,13 @@ class SearchRuleEngine implements RuleEngineInterface */ private function processRuleAction(RuleAction $ruleAction, array $transaction): bool { - Log::debug(sprintf('Executing rule action "%s" with value "%s"', $ruleAction->action_type, $ruleAction->action_value)); + app('log')->debug(sprintf('Executing rule action "%s" with value "%s"', $ruleAction->action_type, $ruleAction->action_value)); $actionClass = ActionFactory::getAction($ruleAction); $result = $actionClass->actOnArray($transaction); $journalId = $transaction['transaction_journal_id'] ?? 0; if (true === $result) { $this->resultCount[$journalId] = array_key_exists($journalId, $this->resultCount) ? $this->resultCount[$journalId]++ : 1; - Log::debug( + app('log')->debug( sprintf( 'Action "%s" on journal #%d was executed, so count a result. Updated transaction journal count is now %d.', $ruleAction->action_type, @@ -468,12 +470,12 @@ class SearchRuleEngine implements RuleEngineInterface ); } if (false === $result) { - Log::debug(sprintf('Action "%s" reports NO changes were made.', $ruleAction->action_type)); + app('log')->debug(sprintf('Action "%s" reports NO changes were made.', $ruleAction->action_type)); } // pick up from the action if it actually acted or not: if ($ruleAction->stop_processing) { - Log::debug(sprintf('Rule action "%s" asks to break, so break!', $ruleAction->action_type)); + app('log')->debug(sprintf('Rule action "%s" asks to break, so break!', $ruleAction->action_type)); return true; } @@ -491,11 +493,11 @@ class SearchRuleEngine implements RuleEngineInterface */ private function fireNonStrictRule(Rule $rule): bool { - Log::debug(sprintf('SearchRuleEngine::fireNonStrictRule(%d)!', $rule->id)); + app('log')->debug(sprintf('SearchRuleEngine::fireNonStrictRule(%d)!', $rule->id)); $collection = $this->findNonStrictRule($rule); $this->processResults($rule, $collection); - Log::debug(sprintf('SearchRuleEngine:: done processing non-strict rule #%d', $rule->id)); + app('log')->debug(sprintf('SearchRuleEngine:: done processing non-strict rule #%d', $rule->id)); return $collection->count() > 0; } @@ -508,17 +510,13 @@ class SearchRuleEngine implements RuleEngineInterface */ private function fireGroup(RuleGroup $group): void { - $all = false; - Log::debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count())); + app('log')->debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count())); /** @var Rule $rule */ foreach ($group->rules as $rule) { - Log::debug(sprintf('Going to fire rule #%d from group #%d', $rule->id, $group->id)); + app('log')->debug(sprintf('Going to fire rule #%d from group #%d', $rule->id, $group->id)); $result = $this->fireRule($rule); - if (true === $result) { - $all = true; - } if (true === $result && true === $rule->stop_processing) { - Log::debug(sprintf('The rule was triggered and rule->stop_processing = true, so group #%d will stop processing further rules.', $group->id)); + app('log')->debug(sprintf('The rule was triggered and rule->stop_processing = true, so group #%d will stop processing further rules.', $group->id)); return; } @@ -548,10 +546,10 @@ class SearchRuleEngine implements RuleEngineInterface */ public function setRuleGroups(Collection $ruleGroups): void { - Log::debug(__METHOD__); + app('log')->debug(__METHOD__); foreach ($ruleGroups as $group) { if ($group instanceof RuleGroup) { - Log::debug(sprintf('Adding a rule group to the SearchRuleEngine: #%d ("%s")', $group->id, $group->title)); + app('log')->debug(sprintf('Adding a rule group to the SearchRuleEngine: #%d ("%s")', $group->id, $group->title)); $this->groups->push($group); } } @@ -562,10 +560,10 @@ class SearchRuleEngine implements RuleEngineInterface */ public function setRules(Collection $rules): void { - Log::debug(__METHOD__); + app('log')->debug(__METHOD__); foreach ($rules as $rule) { if ($rule instanceof Rule) { - Log::debug(sprintf('Adding a rule to the SearchRuleEngine: #%d ("%s")', $rule->id, $rule->title)); + app('log')->debug(sprintf('Adding a rule to the SearchRuleEngine: #%d ("%s")', $rule->id, $rule->title)); $this->rules->push($rule); } } diff --git a/app/TransactionRules/Factory/ActionFactory.php b/app/TransactionRules/Factory/ActionFactory.php index 0f7275e32e..c9adcab529 100644 --- a/app/TransactionRules/Factory/ActionFactory.php +++ b/app/TransactionRules/Factory/ActionFactory.php @@ -27,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\RuleAction; use FireflyIII\Support\Domain; use FireflyIII\TransactionRules\Actions\ActionInterface; -use Illuminate\Support\Facades\Log; /** * Class ActionFactory can create actions. @@ -54,9 +53,9 @@ class ActionFactory public static function getAction(RuleAction $action): ActionInterface { $class = self::getActionClass($action->action_type); - Log::debug(sprintf('self::getActionClass("%s") = "%s"', $action->action_type, $class)); + app('log')->debug(sprintf('self::getActionClass("%s") = "%s"', $action->action_type, $class)); - return new $class($action); + return new $class($action); // @phpstan-ignore-line } /** diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index eaa82dca79..0c377df964 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -92,7 +92,7 @@ class AccountTransformer extends AbstractTransformer } // no order for some accounts: - $order = (int)$account->order; + $order = $account->order; if (!in_array(strtolower($accountType), ['liability', 'liabilities', 'asset'], true)) { $order = null; } @@ -184,7 +184,7 @@ class AccountTransformer extends AbstractTransformer // only grab default when result is null: if (null === $currency) { - $currency = app('amount')->getDefaultCurrencyByUser($account->user); + $currency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); } $currencyId = (string)$currency->id; $currencyCode = $currency->code; @@ -212,7 +212,11 @@ class AccountTransformer extends AbstractTransformer if (null !== $monthlyPaymentDate) { // try classic date: if (10 === strlen($monthlyPaymentDate)) { - $monthlyPaymentDate = Carbon::createFromFormat('!Y-m-d', $monthlyPaymentDate, config('app.timezone'))->toAtomString(); + $object = Carbon::createFromFormat('!Y-m-d', $monthlyPaymentDate, config('app.timezone')); + if (false === $object) { + $object = today(config('app.timezone')); + } + $monthlyPaymentDate = $object->toAtomString(); } if (10 !== strlen($monthlyPaymentDate)) { $monthlyPaymentDate = Carbon::parse($monthlyPaymentDate, config('app.timezone'))->toAtomString(); @@ -240,7 +244,12 @@ class AccountTransformer extends AbstractTransformer $openingBalanceDate = $this->repository->getOpeningBalanceDate($account); } if (null !== $openingBalanceDate) { - $openingBalanceDate = Carbon::createFromFormat('Y-m-d H:i:s', $openingBalanceDate, config('app.timezone'))->toAtomString(); + $object = Carbon::createFromFormat('Y-m-d H:i:s', $openingBalanceDate, config('app.timezone')); + if (false === $object) { + $object = today(config('app.timezone')); + } + $openingBalanceDate = $object->toAtomString(); + } return [$openingBalance, $openingBalanceDate]; diff --git a/app/Transformers/AvailableBudgetTransformer.php b/app/Transformers/AvailableBudgetTransformer.php index 5f24686e0f..5ffe94fab9 100644 --- a/app/Transformers/AvailableBudgetTransformer.php +++ b/app/Transformers/AvailableBudgetTransformer.php @@ -68,7 +68,7 @@ class AvailableBudgetTransformer extends AbstractTransformer 'currency_id' => (string)$currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, + 'currency_decimal_places' => $currency->decimal_places, 'amount' => app('steam')->bcround($availableBudget->amount, $currency->decimal_places), 'start' => $availableBudget->start_date->toAtomString(), 'end' => $availableBudget->end_date->endOfDay()->toAtomString(), diff --git a/app/Transformers/BillTransformer.php b/app/Transformers/BillTransformer.php index b3c4f95681..4ba151783e 100644 --- a/app/Transformers/BillTransformer.php +++ b/app/Transformers/BillTransformer.php @@ -29,6 +29,7 @@ use FireflyIII\Models\Bill; use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Support\Models\BillDateCalculator; use Illuminate\Support\Collection; /** @@ -36,6 +37,7 @@ use Illuminate\Support\Collection; */ class BillTransformer extends AbstractTransformer { + private BillDateCalculator $calculator; private BillRepositoryInterface $repository; /** @@ -46,6 +48,7 @@ class BillTransformer extends AbstractTransformer public function __construct() { $this->repository = app(BillRepositoryInterface::class); + $this->calculator = app(BillDateCalculator::class); } /** @@ -59,59 +62,90 @@ class BillTransformer extends AbstractTransformer { $paidData = $this->paidData($bill); $lastPaidDate = $this->getLastPaidDate($paidData); - $payDates = $this->payDates($bill, $lastPaidDate); - $currency = $bill->transactionCurrency; - $notes = $this->repository->getNoteText($bill); - $notes = '' === $notes ? null : $notes; + + // both params can be NULL, so just in case they are, add some wide margins: + $start = $this->parameters->get('start') ?? today()->subYears(10); + $end = $this->parameters->get('end') ?? today()->addYears(10); + $payDates = $this->calculator->getPayDates($start, $end, $bill->date, $bill->repeat_freq, $bill->skip, $lastPaidDate); + $currency = $bill->transactionCurrency; + $notes = $this->repository->getNoteText($bill); + $notes = '' === $notes ? null : $notes; $this->repository->setUser($bill->user); $objectGroupId = null; $objectGroupOrder = null; $objectGroupTitle = null; - /** @var ObjectGroup $objectGroup */ + /** @var ObjectGroup|null $objectGroup */ $objectGroup = $bill->objectGroups->first(); if (null !== $objectGroup) { - $objectGroupId = (int)$objectGroup->id; - $objectGroupOrder = (int)$objectGroup->order; + $objectGroupId = $objectGroup->id; + $objectGroupOrder = $objectGroup->order; $objectGroupTitle = $objectGroup->title; } $paidDataFormatted = []; $payDatesFormatted = []; - foreach ($paidData['paid_dates'] as $object) { - $object['date'] = Carbon::createFromFormat('!Y-m-d', $object['date'], config('app.timezone'))->toAtomString(); + foreach ($paidData as $object) { + $date = Carbon::createFromFormat('!Y-m-d', $object['date'], config('app.timezone')); + if (false === $date) { + $date = today(config('app.timezone')); + } + $object['date'] = $date->toAtomString(); $paidDataFormatted[] = $object; } foreach ($payDates as $string) { - $payDatesFormatted[] = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone'))->toAtomString(); + $date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone')); + if (false === $date) { + $date = today(config('app.timezone')); + } + $payDatesFormatted[] = $date->toAtomString(); } - $nextExpectedMatch = ''; - if (null !== ($payDates[0] ?? null)) { - $nextExpectedMatch = Carbon::createFromFormat('!Y-m-d', $payDates[0], config('app.timezone'))->toAtomString(); + // next expected match + $nem = null; + $nemDate = null; + $nemDiff = trans('firefly.not_expected_period'); + $firstPayDate = $payDates[0] ?? null; + + if (null !== $firstPayDate) { + $nemDate = Carbon::createFromFormat('!Y-m-d', $firstPayDate, config('app.timezone')); + if (false === $nemDate) { + $nemDate = today(config('app.timezone')); + } + $nem = $nemDate->toAtomString(); + + // nullify again when it's outside the current view range. + if ($nemDate->lt($this->parameters->get('start')) || $nemDate->gt($this->parameters->get('end'))) { + $nem = null; + $nemDate = null; + $firstPayDate = null; + } } - $nextExpectedMatchDiff = trans('firefly.not_expected_period'); + // converting back and forth is bad code but OK. - $temp = new Carbon($nextExpectedMatch); - if ($temp->isToday()) { - $nextExpectedMatchDiff = trans('firefly.today'); - } + if (null !== $nemDate) { + if ($nemDate->isToday()) { + $nemDiff = trans('firefly.today'); + } - $current = $payDatesFormatted[0] ?? null; - if (null !== $current && !$temp->isToday()) { - $temp2 = Carbon::createFromFormat('Y-m-d\TH:i:sP', $current); - $nextExpectedMatchDiff = $temp2->diffForHumans(today(config('app.timezone')), CarbonInterface::DIFF_RELATIVE_TO_NOW); + $current = $payDatesFormatted[0] ?? null; + if (null !== $current && !$nemDate->isToday()) { + $temp2 = Carbon::createFromFormat('Y-m-d\TH:i:sP', $current); + if (false === $temp2) { + $temp2 = today(config('app.timezone')); + } + $nemDiff = trans('firefly.bill_expected_date', ['date' => $temp2->diffForHumans(today(config('app.timezone')), CarbonInterface::DIFF_RELATIVE_TO_NOW)]); + } + unset($temp2); } - unset($temp, $temp2); - return [ - 'id' => (int)$bill->id, + 'id' => $bill->id, 'created_at' => $bill->created_at->toAtomString(), 'updated_at' => $bill->updated_at->toAtomString(), 'currency_id' => (string)$bill->transaction_currency_id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, + 'currency_decimal_places' => $currency->decimal_places, 'name' => $bill->name, 'amount_min' => app('steam')->bcround($bill->amount_min, $currency->decimal_places), 'amount_max' => app('steam')->bcround($bill->amount_max, $currency->decimal_places), @@ -119,17 +153,17 @@ class BillTransformer extends AbstractTransformer 'end_date' => $bill->end_date?->toAtomString(), 'extension_date' => $bill->extension_date?->toAtomString(), 'repeat_freq' => $bill->repeat_freq, - 'skip' => (int)$bill->skip, + 'skip' => $bill->skip, 'active' => $bill->active, - 'order' => (int)$bill->order, + 'order' => $bill->order, 'notes' => $notes, - 'object_group_id' => $objectGroupId ? (string)$objectGroupId : null, + 'object_group_id' => null !== $objectGroupId ? (string)$objectGroupId : null, 'object_group_order' => $objectGroupOrder, 'object_group_title' => $objectGroupTitle, // these fields need work: - 'next_expected_match' => $nextExpectedMatch, - 'next_expected_match_diff' => $nextExpectedMatchDiff, + 'next_expected_match' => $nem, + 'next_expected_match_diff' => $nemDiff, 'pay_dates' => $payDatesFormatted, 'paid_dates' => $paidDataFormatted, 'links' => [ @@ -154,10 +188,7 @@ class BillTransformer extends AbstractTransformer if (null === $this->parameters->get('start') || null === $this->parameters->get('end')) { app('log')->debug('parameters are NULL, return empty array'); - return [ - 'paid_dates' => [], - 'next_expected_match' => null, - ]; + return []; } // 2023-07-1 sub one day from the start date to fix a possible bug (see #7704) // 2023-07-18 this particular date is used to search for the last paid date. @@ -189,15 +220,14 @@ class BillTransformer extends AbstractTransformer $result = []; foreach ($set as $entry) { $result[] = [ - 'transaction_group_id' => (int)$entry->transaction_group_id, - 'transaction_journal_id' => (int)$entry->id, + 'transaction_group_id' => (string)$entry->transaction_group_id, + 'transaction_journal_id' => (string)$entry->id, 'date' => $entry->date->format('Y-m-d'), + 'date_object' => $entry->date, ]; } - return [ - 'paid_dates' => $result - ]; + return $result; } /** @@ -224,120 +254,6 @@ class BillTransformer extends AbstractTransformer return $latest; } - /** - * @param Bill $bill - * @param Carbon $lastPaidDate - * - * @return array - */ - protected function payDates(Bill $bill, ?Carbon $lastPaidDate): array - { - app('log')->debug(sprintf('Now in payDates(#%d, "%s")', $bill->id, $lastPaidDate?->format('Y-m-d'))); - if (null === $this->parameters->get('start') || null === $this->parameters->get('end')) { - app('log')->debug('No start or end date, give empty array.'); - - return []; - } - app('log')->debug(sprintf('Start: %s, end: %s', $this->parameters->get('start')->format('Y-m-d'), $this->parameters->get('end')->format('Y-m-d'))); - $set = new Collection(); - $currentStart = clone $this->parameters->get('start'); - // 2023-06-23 subDay to fix 7655 - $currentStart->subDay(); - $loop = 0; - app('log')->debug('start of loop'); - /* - * De eerste dag van de bill telt sowieso. Vanaf daarna gaan we door tellen. - * Weekly die start op 01-10 - * 01-10: dit is hem dus. - * alle - */ - - - /* - * In de eerste week blijft aantal steps hangen op 0. - * Dus dan krijg je: - * 1 okt: 0 - * 2 okt: 0 - * 3 okt 0 - * en daarna pas begint-ie te lopen. - * maar je moet sowieso een periode verder kijken. - * - * dus stel je begint op 1 oktober monthly. - * dan is de eerste hit (want subday) vanaf 30 sept gerekend. - */ - while ($currentStart <= $this->parameters->get('end')) { - app('log')->debug(sprintf('Current start is %s', $currentStart->format('Y-m-d'))); - $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - - - // If nextExpectedMatch is after end, we continue: - if ($nextExpectedMatch > $this->parameters->get('end')) { - app('log')->debug('Next expected match is after END, so stop looking'); - break; - } - app('log')->debug(sprintf('Next expected match is %s', $nextExpectedMatch->format('Y-m-d'))); - // add to set, if the date is ON or after the start parameter - // AND date is after last paid date - if ($nextExpectedMatch->gte($this->parameters->get('start')) - && (null === $lastPaidDate || $nextExpectedMatch->gt($lastPaidDate)) - ) { - app('log')->debug('Add date to set.'); - $set->push(clone $nextExpectedMatch); - } - - // 2023-10 - // for the next loop, go to end of period, THEN add day. - //$nextExpectedMatch = app('navigation')->endOfPeriod($nextExpectedMatch, $bill->repeat_freq); - $nextExpectedMatch->addDay(); - $currentStart = clone $nextExpectedMatch; - - - $loop++; - if ($loop > 12) { - break; - } - } - app('log')->debug('end of loop'); - $simple = $set->map( - static function (Carbon $date) { - return $date->format('Y-m-d'); - } - ); - app('log')->debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray()); - - return $simple->toArray(); - } - - /** - * Given a bill and a date, this method will tell you at which moment this bill expects its next - * transaction. That date must be AFTER $date as a sanity check. - * - * @param Bill $bill - * @param Carbon $date - * - * @return Carbon - */ - protected function nextDateMatch(Bill $bill, Carbon $date): Carbon - { - app('log')->debug(sprintf('Now in nextDateMatch(#%d, %s)', $bill->id, $date->format('Y-m-d'))); - $start = clone $bill->date; - app('log')->debug(sprintf('Bill start date is %s', $start->format('Y-m-d'))); - if ($start->gt($date)) { - app('log')->debug('Start is after bill start, just return bill start date.'); - return clone $start; - } - - $steps = app('navigation')->diffInPeriods($bill->repeat_freq, $bill->skip, $start, $date); - $result = clone $start; - if ($steps > 0) { - $steps = $steps - 1; - app('log')->debug(sprintf('Steps is %d, because addPeriod already adds 1.', $steps)); - $result = app('navigation')->addPeriod($start, $bill->repeat_freq, $steps); - } - app('log')->debug(sprintf('Number of steps is %d, result is %s', $steps, $result->format('Y-m-d'))); - return $result; - } - /** * @param array $paidData * @@ -347,20 +263,23 @@ class BillTransformer extends AbstractTransformer { app('log')->debug('getLastPaidDate()'); $return = null; - foreach ($paidData['paid_dates'] as $entry) { + foreach ($paidData as $entry) { if (null !== $return) { - $current = Carbon::createFromFormat('!Y-m-d', $entry['date'], config('app.timezone')); + /** @var Carbon $current */ + $current = $entry['date_object']; if ($current->gt($return)) { $return = clone $current; } app('log')->debug(sprintf('Last paid date is: %s', $return->format('Y-m-d'))); } if (null === $return) { - $return = Carbon::createFromFormat('!Y-m-d', $entry['date'], config('app.timezone')); + /** @var Carbon $return */ + $return = $entry['date_object']; app('log')->debug(sprintf('Last paid date is: %s', $return->format('Y-m-d'))); } } app('log')->debug(sprintf('Last paid date is: "%s"', $return?->format('Y-m-d'))); return $return; } + } diff --git a/app/Transformers/BudgetLimitTransformer.php b/app/Transformers/BudgetLimitTransformer.php index f4d9ea6532..bc93efb1d6 100644 --- a/app/Transformers/BudgetLimitTransformer.php +++ b/app/Transformers/BudgetLimitTransformer.php @@ -33,7 +33,6 @@ use League\Fractal\Resource\Item; */ class BudgetLimitTransformer extends AbstractTransformer { - /** @var string[] */ protected array $availableIncludes = [ 'budget', @@ -78,7 +77,7 @@ class BudgetLimitTransformer extends AbstractTransformer $currencySymbol = null; if (null !== $currency) { $amount = $budgetLimit->amount; - $currencyId = (int)$currency->id; + $currencyId = $currency->id; $currencyName = $currency->name; $currencyCode = $currency->code; $currencySymbol = $currency->symbol; diff --git a/app/Transformers/CategoryTransformer.php b/app/Transformers/CategoryTransformer.php index f04df6ced5..03d8bb8e36 100644 --- a/app/Transformers/CategoryTransformer.php +++ b/app/Transformers/CategoryTransformer.php @@ -70,7 +70,7 @@ class CategoryTransformer extends AbstractTransformer $notes = $this->repository->getNoteText($category); return [ - 'id' => (int)$category->id, + 'id' => $category->id, 'created_at' => $category->created_at->toAtomString(), 'updated_at' => $category->updated_at->toAtomString(), 'name' => $category->name, diff --git a/app/Transformers/CurrencyTransformer.php b/app/Transformers/CurrencyTransformer.php index 2764bde256..771f881c8b 100644 --- a/app/Transformers/CurrencyTransformer.php +++ b/app/Transformers/CurrencyTransformer.php @@ -40,7 +40,7 @@ class CurrencyTransformer extends AbstractTransformer public function transform(TransactionCurrency $currency): array { return [ - 'id' => (int)$currency->id, + 'id' => $currency->id, 'created_at' => $currency->created_at->toAtomString(), 'updated_at' => $currency->updated_at->toAtomString(), 'default' => $currency->userDefault, @@ -48,7 +48,7 @@ class CurrencyTransformer extends AbstractTransformer 'name' => $currency->name, 'code' => $currency->code, 'symbol' => $currency->symbol, - 'decimal_places' => (int)$currency->decimal_places, + 'decimal_places' => $currency->decimal_places, 'links' => [ [ 'rel' => 'self', diff --git a/app/Transformers/LinkTypeTransformer.php b/app/Transformers/LinkTypeTransformer.php index 13f3b36eb5..9002a2767d 100644 --- a/app/Transformers/LinkTypeTransformer.php +++ b/app/Transformers/LinkTypeTransformer.php @@ -41,7 +41,7 @@ class LinkTypeTransformer extends AbstractTransformer public function transform(LinkType $linkType): array { return [ - 'id' => (int)$linkType->id, + 'id' => $linkType->id, 'created_at' => $linkType->created_at->toAtomString(), 'updated_at' => $linkType->updated_at->toAtomString(), 'name' => $linkType->name, diff --git a/app/Transformers/ObjectGroupTransformer.php b/app/Transformers/ObjectGroupTransformer.php index 4bc4f70a8b..87b1e2e815 100644 --- a/app/Transformers/ObjectGroupTransformer.php +++ b/app/Transformers/ObjectGroupTransformer.php @@ -60,7 +60,7 @@ class ObjectGroupTransformer extends AbstractTransformer 'created_at' => $objectGroup->created_at?->toAtomString(), 'updated_at' => $objectGroup->updated_at?->toAtomString(), 'title' => $objectGroup->title, - 'order' => (int)$objectGroup->order, + 'order' => $objectGroup->order, 'links' => [ [ 'rel' => 'self', diff --git a/app/Transformers/PiggyBankEventTransformer.php b/app/Transformers/PiggyBankEventTransformer.php index 66e72cb579..3137e76269 100644 --- a/app/Transformers/PiggyBankEventTransformer.php +++ b/app/Transformers/PiggyBankEventTransformer.php @@ -67,7 +67,7 @@ class PiggyBankEventTransformer extends AbstractTransformer $this->piggyRepos->setUser($account->user); // get associated currency or fall back to the default: - $currency = $this->repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user); + $currency = $this->repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); // get associated journal and transaction, if any: $journalId = $event->transaction_journal_id; @@ -85,9 +85,9 @@ class PiggyBankEventTransformer extends AbstractTransformer 'currency_id' => (string)$currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, - 'transaction_journal_id' => $journalId ? (string)$journalId : null, - 'transaction_group_id' => $groupId ? (string)$groupId : null, + 'currency_decimal_places' => $currency->decimal_places, + 'transaction_journal_id' => null !== $journalId ? (string)$journalId : null, + 'transaction_group_id' => null !== $groupId ? (string)$groupId : null, 'links' => [ [ 'rel' => 'self', diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index 43770a8924..f098bba34d 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -67,7 +67,7 @@ class PiggyBankTransformer extends AbstractTransformer $this->piggyRepos->setUser($account->user); // get currency from account, or use default. - $currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user); + $currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); // note $notes = $this->piggyRepos->getNoteText($piggyBank); @@ -76,11 +76,11 @@ class PiggyBankTransformer extends AbstractTransformer $objectGroupId = null; $objectGroupOrder = null; $objectGroupTitle = null; - /** @var ObjectGroup $objectGroup */ + /** @var ObjectGroup|null $objectGroup */ $objectGroup = $piggyBank->objectGroups->first(); if (null !== $objectGroup) { - $objectGroupId = (int)$objectGroup->id; - $objectGroupOrder = (int)$objectGroup->order; + $objectGroupId = $objectGroup->id; + $objectGroupOrder = $objectGroup->order; $objectGroupTitle = $objectGroup->title; } @@ -112,7 +112,7 @@ class PiggyBankTransformer extends AbstractTransformer 'currency_id' => (string)$currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, + 'currency_decimal_places' => $currency->decimal_places, 'target_amount' => $targetAmount, 'percentage' => $percentage, 'current_amount' => $currentAmount, @@ -120,10 +120,10 @@ class PiggyBankTransformer extends AbstractTransformer 'save_per_month' => $savePerMonth, 'start_date' => $startDate, 'target_date' => $targetDate, - 'order' => (int)$piggyBank->order, + 'order' => $piggyBank->order, 'active' => true, 'notes' => $notes, - 'object_group_id' => $objectGroupId ? (string)$objectGroupId : null, + 'object_group_id' => null !== $objectGroupId ? (string)$objectGroupId : null, 'object_group_order' => $objectGroupOrder, 'object_group_title' => $objectGroupTitle, 'links' => [ diff --git a/app/Transformers/PreferenceTransformer.php b/app/Transformers/PreferenceTransformer.php index e108e3effb..0d67f3db0d 100644 --- a/app/Transformers/PreferenceTransformer.php +++ b/app/Transformers/PreferenceTransformer.php @@ -40,7 +40,7 @@ class PreferenceTransformer extends AbstractTransformer public function transform(Preference $preference): array { return [ - 'id' => (int)$preference->id, + 'id' => $preference->id, 'created_at' => $preference->created_at->toAtomString(), 'updated_at' => $preference->updated_at->toAtomString(), 'name' => $preference->name, diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index a480c05d9d..1cbedd0c18 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -26,6 +26,7 @@ namespace FireflyIII\Transformers; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\CategoryFactory; +use FireflyIII\Models\Account; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceRepetition; use FireflyIII\Models\RecurrenceTransaction; @@ -129,8 +130,8 @@ class RecurrenceTransformer extends AbstractTransformer 'updated_at' => $repetition->updated_at->toAtomString(), 'type' => $repetition->repetition_type, 'moment' => $repetition->repetition_moment, - 'skip' => (int)$repetition->repetition_skip, - 'weekend' => (int)$repetition->weekend, + 'skip' => $repetition->repetition_skip, + 'weekend' => $repetition->weekend, 'description' => $this->repository->repetitionDescription($repetition), 'occurrences' => [], ]; @@ -161,7 +162,9 @@ class RecurrenceTransformer extends AbstractTransformer // get all transactions: /** @var RecurrenceTransaction $transaction */ foreach ($recurrence->recurrenceTransactions()->get() as $transaction) { - $sourceAccount = $transaction->sourceAccount; + /** @var Account|null $sourceAccount */ + $sourceAccount = $transaction->sourceAccount; + /** @var Account|null $destinationAccount */ $destinationAccount = $transaction->destinationAccount; $foreignCurrencyCode = null; $foreignCurrencySymbol = null; @@ -171,7 +174,7 @@ class RecurrenceTransformer extends AbstractTransformer $foreignCurrencyId = (int)$transaction->foreign_currency_id; $foreignCurrencyCode = $transaction->foreignCurrency->code; $foreignCurrencySymbol = $transaction->foreignCurrency->symbol; - $foreignCurrencyDp = (int)$transaction->foreignCurrency->decimal_places; + $foreignCurrencyDp = $transaction->foreignCurrency->decimal_places; } // source info: @@ -181,7 +184,7 @@ class RecurrenceTransformer extends AbstractTransformer $sourceIban = null; if (null !== $sourceAccount) { $sourceName = $sourceAccount->name; - $sourceId = (int)$sourceAccount->id; + $sourceId = $sourceAccount->id; $sourceType = $sourceAccount->accountType->type; $sourceIban = $sourceAccount->iban; } @@ -191,7 +194,7 @@ class RecurrenceTransformer extends AbstractTransformer $destinationIban = null; if (null !== $destinationAccount) { $destinationName = $destinationAccount->name; - $destinationId = (int)$destinationAccount->id; + $destinationId = $destinationAccount->id; $destinationType = $destinationAccount->accountType->type; $destinationIban = $destinationAccount->iban; } @@ -205,7 +208,7 @@ class RecurrenceTransformer extends AbstractTransformer 'currency_id' => (string)$transaction->transaction_currency_id, 'currency_code' => $transaction->transactionCurrency->code, 'currency_symbol' => $transaction->transactionCurrency->symbol, - 'currency_decimal_places' => (int)$transaction->transactionCurrency->decimal_places, + 'currency_decimal_places' => $transaction->transactionCurrency->decimal_places, 'foreign_currency_id' => null === $foreignCurrencyId ? null : (string)$foreignCurrencyId, 'foreign_currency_code' => $foreignCurrencyCode, 'foreign_currency_symbol' => $foreignCurrencySymbol, diff --git a/app/Transformers/RuleGroupTransformer.php b/app/Transformers/RuleGroupTransformer.php index bb83a5dc05..31fd555626 100644 --- a/app/Transformers/RuleGroupTransformer.php +++ b/app/Transformers/RuleGroupTransformer.php @@ -40,7 +40,7 @@ class RuleGroupTransformer extends AbstractTransformer public function transform(RuleGroup $ruleGroup): array { return [ - 'id' => (int)$ruleGroup->id, + 'id' => $ruleGroup->id, 'created_at' => $ruleGroup->created_at->toAtomString(), 'updated_at' => $ruleGroup->updated_at->toAtomString(), 'title' => $ruleGroup->title, diff --git a/app/Transformers/RuleTransformer.php b/app/Transformers/RuleTransformer.php index 2a0d3f80a5..1c1837d091 100644 --- a/app/Transformers/RuleTransformer.php +++ b/app/Transformers/RuleTransformer.php @@ -67,7 +67,7 @@ class RuleTransformer extends AbstractTransformer 'rule_group_title' => (string)$rule->ruleGroup->title, 'title' => $rule->title, 'description' => $rule->description, - 'order' => (int)$rule->order, + 'order' => $rule->order, 'active' => $rule->active, 'strict' => $rule->strict, 'stop_processing' => $rule->stop_processing, diff --git a/app/Transformers/TagTransformer.php b/app/Transformers/TagTransformer.php index 067a508cd2..6db04eba5c 100644 --- a/app/Transformers/TagTransformer.php +++ b/app/Transformers/TagTransformer.php @@ -43,7 +43,7 @@ class TagTransformer extends AbstractTransformer public function transform(Tag $tag): array { $date = $tag->date?->format('Y-m-d'); - /** @var Location $location */ + /** @var Location|null $location */ $location = $tag->locations()->first(); $latitude = null; $longitude = null; @@ -55,7 +55,7 @@ class TagTransformer extends AbstractTransformer } return [ - 'id' => (int)$tag->id, + 'id' => $tag->id, 'created_at' => $tag->created_at->toAtomString(), 'updated_at' => $tag->updated_at->toAtomString(), 'tag' => $tag->tag, diff --git a/app/Transformers/TransactionGroupTransformer.php b/app/Transformers/TransactionGroupTransformer.php index 35ce7ac32e..ac9cad159d 100644 --- a/app/Transformers/TransactionGroupTransformer.php +++ b/app/Transformers/TransactionGroupTransformer.php @@ -253,7 +253,7 @@ class TransactionGroupTransformer extends AbstractTransformer } if (null !== $default) { - return (string)$default; + return $default; } return null; @@ -329,10 +329,10 @@ class TransactionGroupTransformer extends AbstractTransformer { try { $result = [ - 'id' => (int)$group->id, + 'id' => $group->id, 'created_at' => $group->created_at->toAtomString(), 'updated_at' => $group->updated_at->toAtomString(), - 'user' => (int)$group->user_id, + 'user' => $group->user_id, 'group_title' => $group->title, 'transactions' => $this->transformJournals($group->transactionJournals), 'links' => [ @@ -382,8 +382,8 @@ class TransactionGroupTransformer extends AbstractTransformer $destination = $this->getDestinationTransaction($journal); $type = $journal->transactionType->type; $currency = $source->transactionCurrency; - $amount = app('steam')->bcround($this->getAmount($type, (string)$source->amount), $currency->decimal_places ?? 0); - $foreignAmount = $this->getForeignAmount($type, null === $source->foreign_amount ? null : (string)$source->foreign_amount); + $amount = app('steam')->bcround($this->getAmount($source->amount), $currency->decimal_places ?? 0); + $foreignAmount = $this->getForeignAmount(null === $source->foreign_amount ? null : $source->foreign_amount); $metaFieldData = $this->groupRepos->getMetaFields($journal->id, $this->metaFields); $metaDates = $this->getDates($this->groupRepos->getMetaDateFields($journal->id, $this->metaDateFields)); $foreignCurrency = $this->getForeignCurrency($source->foreignCurrency); @@ -406,16 +406,16 @@ class TransactionGroupTransformer extends AbstractTransformer } return [ - 'user' => (int)$journal->user_id, - 'transaction_journal_id' => (int)$journal->id, + 'user' => $journal->user_id, + 'transaction_journal_id' => $journal->id, 'type' => strtolower($type), 'date' => $journal->date->toAtomString(), 'order' => $journal->order, - 'currency_id' => (int)$currency->id, + 'currency_id' => $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, + 'currency_decimal_places' => $currency->decimal_places, 'foreign_currency_id' => $foreignCurrency['id'], 'foreign_currency_code' => $foreignCurrency['code'], @@ -427,12 +427,12 @@ class TransactionGroupTransformer extends AbstractTransformer 'description' => $journal->description, - 'source_id' => (int)$source->account_id, + 'source_id' => $source->account_id, 'source_name' => $source->account->name, 'source_iban' => $source->account->iban, 'source_type' => $source->account->accountType->type, - 'destination_id' => (int)$destination->account_id, + 'destination_id' => $destination->account_id, 'destination_name' => $destination->account->name, 'destination_iban' => $destination->account->iban, 'destination_type' => $destination->account->accountType->type, @@ -521,23 +521,21 @@ class TransactionGroupTransformer extends AbstractTransformer } /** - * @param string $type * @param string $amount * * @return string */ - private function getAmount(string $type, string $amount): string + private function getAmount(string $amount): string { return app('steam')->positive($amount); } /** - * @param string $type * @param string|null $foreignAmount * * @return string|null */ - private function getForeignAmount(string $type, ?string $foreignAmount): ?string + private function getForeignAmount(?string $foreignAmount): ?string { $result = null; if (null !== $foreignAmount && '' !== $foreignAmount && bccomp('0', $foreignAmount) !== 0) { @@ -589,10 +587,10 @@ class TransactionGroupTransformer extends AbstractTransformer if (null === $currency) { return $array; } - $array['id'] = (int)$currency->id; + $array['id'] = $currency->id; $array['code'] = $currency->code; $array['symbol'] = $currency->symbol; - $array['decimal_places'] = (int)$currency->decimal_places; + $array['decimal_places'] = $currency->decimal_places; return $array; } @@ -611,7 +609,7 @@ class TransactionGroupTransformer extends AbstractTransformer if (null === $budget) { return $array; } - $array['id'] = (int)$budget->id; + $array['id'] = $budget->id; $array['name'] = $budget->name; return $array; @@ -631,7 +629,7 @@ class TransactionGroupTransformer extends AbstractTransformer if (null === $category) { return $array; } - $array['id'] = (int)$category->id; + $array['id'] = $category->id; $array['name'] = $category->name; return $array; diff --git a/app/Transformers/UserTransformer.php b/app/Transformers/UserTransformer.php index 790ec698eb..9b343cff54 100644 --- a/app/Transformers/UserTransformer.php +++ b/app/Transformers/UserTransformer.php @@ -45,7 +45,7 @@ class UserTransformer extends AbstractTransformer */ public function transform(User $user): array { - $this->repository = $this->repository ?? app(UserRepositoryInterface::class); + $this->repository ??= app(UserRepositoryInterface::class); return [ 'id' => (int)$user->id, diff --git a/app/Transformers/V2/AccountTransformer.php b/app/Transformers/V2/AccountTransformer.php index 8a192d121b..262ea013c3 100644 --- a/app/Transformers/V2/AccountTransformer.php +++ b/app/Transformers/V2/AccountTransformer.php @@ -70,11 +70,11 @@ class AccountTransformer extends AbstractTransformer $currencies = $repository->getByIds($currencyIds); foreach ($currencies as $currency) { - $id = (int)$currency->id; + $id = $currency->id; $this->currencies[$id] = $currency; } foreach ($meta as $entry) { - $id = (int)$entry->account_id; + $id = $entry->account_id; $this->accountMeta[$id][$entry->name] = $entry->data; } // get account types: @@ -84,7 +84,7 @@ class AccountTransformer extends AbstractTransformer ->get(['accounts.id', 'account_types.type']); /** @var AccountType $row */ foreach ($accountTypes as $row) { - $this->accountTypes[(int)$row->id] = (string)config(sprintf('firefly.shortNamesByFullName.%s', $row->type)); + $this->accountTypes[$row->id] = (string)config(sprintf('firefly.shortNamesByFullName.%s', $row->type)); } } @@ -110,12 +110,12 @@ class AccountTransformer extends AbstractTransformer */ public function transform(Account $account): array { - $id = (int)$account->id; + $id = $account->id; // various meta $accountRole = $this->accountMeta[$id]['account_role'] ?? null; $accountType = $this->accountTypes[$id]; - $order = (int)$account->order; + $order = $account->order; // no currency? use default $currency = $this->default; @@ -144,12 +144,12 @@ class AccountTransformer extends AbstractTransformer 'currency_id' => (string)$currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, + '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' => (int)$this->default->decimal_places, + 'native_currency_decimal_places' => $this->default->decimal_places, // balance: 'current_balance' => $balance, diff --git a/app/Transformers/V2/BillTransformer.php b/app/Transformers/V2/BillTransformer.php index f5b87e21fe..382ed6863f 100644 --- a/app/Transformers/V2/BillTransformer.php +++ b/app/Transformers/V2/BillTransformer.php @@ -31,7 +31,6 @@ use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; -use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; @@ -41,23 +40,12 @@ use Illuminate\Support\Facades\DB; */ class BillTransformer extends AbstractTransformer { - private ExchangeRateConverter $converter; - private array $currencies; - private TransactionCurrency $default; - private array $groups; - private array $notes; - private array $paidDates; - private BillRepositoryInterface $repository; - - /** - * BillTransformer constructor. - * - - */ - public function __construct() - { - $this->repository = app(BillRepositoryInterface::class); - } + private ExchangeRateConverter $converter; + private array $currencies; + private TransactionCurrency $default; + private array $groups; + private array $notes; + private array $paidDates; /** * @inheritDoc @@ -74,9 +62,9 @@ class BillTransformer extends AbstractTransformer // start with currencies: /** @var Bill $object */ foreach ($objects as $object) { - $id = (int)$object->transaction_currency_id; - $bills[] = (int)$object->id; - $currencies[$id] = $currencies[$id] ?? TransactionCurrency::find($id); + $id = $object->transaction_currency_id; + $bills[] = $object->id; + $currencies[$id] ??= TransactionCurrency::find($id); } $this->currencies = $currencies; @@ -84,7 +72,7 @@ class BillTransformer extends AbstractTransformer $notes = Note::whereNoteableType(Bill::class)->whereIn('noteable_id', array_keys($bills))->get(); /** @var Note $note */ foreach ($notes as $note) { - $id = (int)$note->noteable_id; + $id = $note->noteable_id; $this->notes[$id] = $note; } // grab object groups: @@ -96,7 +84,7 @@ class BillTransformer extends AbstractTransformer foreach ($set as $entry) { $billId = (int)$entry->object_groupable_id; $id = (int)$entry->object_group_id; - $order = (int)$entry->order; + $order = $entry->order; $this->groups[$billId] = [ 'object_group_id' => $id, 'object_group_title' => $entry->title, @@ -123,16 +111,16 @@ class BillTransformer extends AbstractTransformer $transactions = []; /** @var Transaction $transaction */ foreach ($set as $transaction) { - $journalId = (int)$transaction->transaction_journal_id; + $journalId = $transaction->transaction_journal_id; $transactions[$journalId] = $transaction->toArray(); } /** @var TransactionJournal $journal */ foreach ($journals as $journal) { app('log')->debug(sprintf('Processing journal #%d', $journal->id)); - $transaction = $transactions[(int)$journal->id] ?? []; + $transaction = $transactions[$journal->id] ?? []; $billId = (int)$journal->bill_id; - $currencyId = (int)$transaction['transaction_currency_id'] ?? 0; - $currencies[$currencyId] = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId); + $currencyId = (int)($transaction['transaction_currency_id'] ?? 0); + $currencies[$currencyId] ??= TransactionCurrency::find($currencyId); // foreign currency $foreignCurrencyId = null; @@ -144,26 +132,26 @@ class BillTransformer extends AbstractTransformer if (null !== $transaction['foreign_currency_id']) { app('log')->debug(sprintf('Foreign currency is #%d', $transaction['foreign_currency_id'])); $foreignCurrencyId = (int)$transaction['foreign_currency_id']; - $currencies[$foreignCurrencyId] = $currencies[$foreignCurrencyId] ?? TransactionCurrency::find($foreignCurrencyId); + $currencies[$foreignCurrencyId] ??= TransactionCurrency::find($foreignCurrencyId); $foreignCurrencyCode = $currencies[$foreignCurrencyId]->code; $foreignCurrencyName = $currencies[$foreignCurrencyId]->name; $foreignCurrencySymbol = $currencies[$foreignCurrencyId]->symbol; - $foreignCurrencyDp = (int)$currencies[$foreignCurrencyId]->decimal_places; + $foreignCurrencyDp = $currencies[$foreignCurrencyId]->decimal_places; } $this->paidDates[$billId][] = [ 'transaction_group_id' => (string)$journal->id, 'transaction_journal_id' => (string)$journal->transaction_group_id, 'date' => $journal->date->toAtomString(), - 'currency_id' => (int)$currencies[$currencyId]->id, + 'currency_id' => $currencies[$currencyId]->id, 'currency_code' => $currencies[$currencyId]->code, 'currency_name' => $currencies[$currencyId]->name, 'currency_symbol' => $currencies[$currencyId]->symbol, - 'currency_decimal_places' => (int)$currencies[$currencyId]->decimal_places, - 'native_currency_id' => (int)$currencies[$currencyId]->id, + 'currency_decimal_places' => $currencies[$currencyId]->decimal_places, + 'native_currency_id' => $currencies[$currencyId]->id, 'native_currency_code' => $currencies[$currencyId]->code, 'native_currency_symbol' => $currencies[$currencyId]->symbol, - 'native_currency_decimal_places' => (int)$currencies[$currencyId]->decimal_places, + 'native_currency_decimal_places' => $currencies[$currencyId]->decimal_places, 'foreign_currency_id' => $foreignCurrencyId, 'foreign_currency_code' => $foreignCurrencyCode, 'foreign_currency_name' => $foreignCurrencyName, @@ -192,22 +180,22 @@ class BillTransformer extends AbstractTransformer */ public function transform(Bill $bill): array { - $paidData = $this->paidDates[(int)$bill->id] ?? []; - $nextExpectedMatch = $this->nextExpectedMatch($bill, $this->paidDates[(int)$bill->id] ?? []); + $paidData = $this->paidDates[$bill->id] ?? []; + $nextExpectedMatch = $this->nextExpectedMatch($bill, $this->paidDates[$bill->id] ?? []); $payDates = $this->payDates($bill); - $currency = $this->currencies[(int)$bill->transaction_currency_id]; - $group = $this->groups[(int)$bill->id] ?? null; + $currency = $this->currencies[$bill->transaction_currency_id]; + $group = $this->groups[$bill->id] ?? null; // date for currency conversion /** @var Carbon|null $startParam */ $startParam = $this->parameters->get('start'); - /** @var Carbon|null $start */ + /** @var Carbon|null $date */ $date = null === $startParam ? today() : clone $startParam; $nextExpectedMatchDiff = $this->getNextExpectedMatchDiff($nextExpectedMatch, $payDates); return [ - 'id' => (int)$bill->id, + 'id' => $bill->id, 'created_at' => $bill->created_at->toAtomString(), 'updated_at' => $bill->updated_at->toAtomString(), 'name' => $bill->name, @@ -219,20 +207,20 @@ class BillTransformer extends AbstractTransformer 'currency_code' => $currency->code, 'currency_name' => $currency->name, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, + 'currency_decimal_places' => $currency->decimal_places, 'native_currency_id' => $this->default->id, 'native_currency_code' => $this->default->code, 'native_currency_name' => $this->default->name, 'native_currency_symbol' => $this->default->symbol, - 'native_currency_decimal_places' => (int)$this->default->decimal_places, + 'native_currency_decimal_places' => $this->default->decimal_places, 'date' => $bill->date->toAtomString(), 'end_date' => $bill->end_date?->toAtomString(), 'extension_date' => $bill->extension_date?->toAtomString(), 'repeat_freq' => $bill->repeat_freq, - 'skip' => (int)$bill->skip, + 'skip' => $bill->skip, 'active' => $bill->active, - 'order' => (int)$bill->order, - 'notes' => $this->notes[(int)$bill->id] ?? null, + 'order' => $bill->order, + 'notes' => $this->notes[$bill->id] ?? null, 'object_group_id' => $group ? $group['object_group_id'] : null, 'object_group_order' => $group ? $group['object_group_order'] : null, 'object_group_title' => $group ? $group['object_group_title'] : null, @@ -290,8 +278,8 @@ class BillTransformer extends AbstractTransformer /** * Returns the latest date in the set, or start when set is empty. * - * @param Collection $dates - * @param Carbon $default + * @param array $dates + * @param Carbon $default * * @return Carbon */ diff --git a/app/Transformers/V2/BudgetLimitTransformer.php b/app/Transformers/V2/BudgetLimitTransformer.php index 0b46b0fcae..2c28e62c9c 100644 --- a/app/Transformers/V2/BudgetLimitTransformer.php +++ b/app/Transformers/V2/BudgetLimitTransformer.php @@ -32,7 +32,6 @@ use League\Fractal\Resource\Item; */ class BudgetLimitTransformer extends AbstractTransformer { - /** @var string[] */ protected array $availableIncludes = [ 'budget', @@ -81,7 +80,7 @@ class BudgetLimitTransformer extends AbstractTransformer $currencySymbol = null; if (null !== $currency) { $amount = $budgetLimit->amount; - $currencyId = (int)$currency->id; + $currencyId = $currency->id; $currencyName = $currency->name; $currencyCode = $currency->code; $currencySymbol = $currency->symbol; diff --git a/app/Transformers/V2/BudgetTransformer.php b/app/Transformers/V2/BudgetTransformer.php index 8d25a2628d..664e0f4f88 100644 --- a/app/Transformers/V2/BudgetTransformer.php +++ b/app/Transformers/V2/BudgetTransformer.php @@ -65,8 +65,8 @@ class BudgetTransformer extends AbstractTransformer public function transform(Budget $budget): array { //$this->opsRepository->setUser($budget->user); - $start = $this->parameters->get('start'); - $end = $this->parameters->get('end'); + //$start = $this->parameters->get('start'); + //$end = $this->parameters->get('end'); //$autoBudget = $this->repository->getAutoBudget($budget); // $spent = []; // if (null !== $start && null !== $end) { @@ -116,19 +116,4 @@ class BudgetTransformer extends AbstractTransformer ]; } - /** - * @param array $array - * - * @return array - */ - private function beautify(array $array): array - { - $return = []; - foreach ($array as $data) { - $data['sum'] = number_format((float)$data['sum'], (int)$data['currency_decimal_places'], '.', ''); - $return[] = $data; - } - - return $return; - } } diff --git a/app/Transformers/V2/CurrencyTransformer.php b/app/Transformers/V2/CurrencyTransformer.php index b79b6ee8e4..108a758b5f 100644 --- a/app/Transformers/V2/CurrencyTransformer.php +++ b/app/Transformers/V2/CurrencyTransformer.php @@ -34,10 +34,7 @@ class CurrencyTransformer extends AbstractTransformer /** * @inheritDoc */ - public function collectMetaData(Collection $objects): void - { - - } + public function collectMetaData(Collection $objects): void {} /** * Transform the currency. @@ -49,7 +46,7 @@ class CurrencyTransformer extends AbstractTransformer public function transform(TransactionCurrency $currency): array { return [ - 'id' => (int)$currency->id, + 'id' => $currency->id, 'created_at' => $currency->created_at->toAtomString(), 'updated_at' => $currency->updated_at->toAtomString(), 'default' => $currency->userDefault, @@ -57,7 +54,7 @@ class CurrencyTransformer extends AbstractTransformer 'name' => $currency->name, 'code' => $currency->code, 'symbol' => $currency->symbol, - 'decimal_places' => (int)$currency->decimal_places, + 'decimal_places' => $currency->decimal_places, 'links' => [ [ 'rel' => 'self', diff --git a/app/Transformers/V2/PiggyBankTransformer.php b/app/Transformers/V2/PiggyBankTransformer.php index 82e9b05de6..e00d3ce6ea 100644 --- a/app/Transformers/V2/PiggyBankTransformer.php +++ b/app/Transformers/V2/PiggyBankTransformer.php @@ -80,9 +80,10 @@ class PiggyBankTransformer extends AbstractTransformer $piggyBanks = $objects->pluck('id')->toArray(); $accountInfo = Account::whereIn('id', $objects->pluck('account_id')->toArray())->get(); $currencyPreferences = AccountMeta::where('name', '"currency_id"')->whereIn('account_id', $objects->pluck('account_id')->toArray())->get(); + $currencies = []; /** @var Account $account */ foreach ($accountInfo as $account) { - $id = (int)$account->id; + $id = $account->id; $this->accounts[$id] = [ 'name' => $account->name, ]; @@ -90,8 +91,8 @@ class PiggyBankTransformer extends AbstractTransformer /** @var AccountMeta $preference */ foreach ($currencyPreferences as $preference) { $currencyId = (int)$preference->data; - $accountId = (int)$preference->account_id; - $currencies[$currencyId] = $currencies[$currencyId] ?? TransactionJournal::find($currencyId); + $accountId = $preference->account_id; + $currencies[$currencyId] ??= TransactionJournal::find($currencyId); $this->currencies[$accountId] = $currencies[$currencyId]; } @@ -104,7 +105,7 @@ class PiggyBankTransformer extends AbstractTransformer foreach ($set as $entry) { $piggyBankId = (int)$entry->object_groupable_id; $id = (int)$entry->object_group_id; - $order = (int)$entry->order; + $order = $entry->order; $this->groups[$piggyBankId] = [ 'object_group_id' => $id, 'object_group_title' => $entry->title, @@ -117,7 +118,7 @@ class PiggyBankTransformer extends AbstractTransformer $repetitions = PiggyBankRepetition::whereIn('piggy_bank_id', $piggyBanks)->get(); /** @var PiggyBankRepetition $repetition */ foreach ($repetitions as $repetition) { - $this->repetitions[(int)$repetition->piggy_bank_id] = [ + $this->repetitions[$repetition->piggy_bank_id] = [ 'amount' => $repetition->currentamount, ]; } @@ -127,11 +128,11 @@ class PiggyBankTransformer extends AbstractTransformer $notes = Note::whereNoteableType(PiggyBank::class)->whereIn('noteable_id', array_keys($piggyBanks))->get(); /** @var Note $note */ foreach ($notes as $note) { - $id = (int)$note->noteable_id; + $id = $note->noteable_id; $this->notes[$id] = $note; } - $this->default = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->default = app('amount')->getDefaultCurrencyByUserGroup(auth()->user()->userGroup); $this->converter = new ExchangeRateConverter(); } @@ -179,15 +180,15 @@ class PiggyBankTransformer extends AbstractTransformer $nativeSavePerMonth = null; $startDate = $piggyBank->startdate?->format('Y-m-d'); $targetDate = $piggyBank->targetdate?->format('Y-m-d'); - $accountId = (int)$piggyBank->account_id; + $accountId = $piggyBank->account_id; $accountName = $this->accounts[$accountId]['name'] ?? null; $currency = $this->currencies[$accountId] ?? $this->default; - $currentAmount = app('steam')->bcround($this->repetitions[(int)$piggyBank->id]['amount'] ?? '0', $currency->decimal_places); + $currentAmount = app('steam')->bcround($this->repetitions[$piggyBank->id]['amount'] ?? '0', $currency->decimal_places); $nativeCurrentAmount = $this->converter->convert($this->default, $currency, today(), $currentAmount); $targetAmount = $piggyBank->targetamount; $nativeTargetAmount = $this->converter->convert($this->default, $currency, today(), $targetAmount); - $note = $this->notes[(int)$piggyBank->id] ?? null; - $group = $this->groups[(int)$piggyBank->id] ?? null; + $note = $this->notes[$piggyBank->id] ?? null; + $group = $this->groups[$piggyBank->id] ?? null; if (0 !== bccomp($targetAmount, '0')) { // target amount is not 0.00 $leftToSave = bcsub($targetAmount, $currentAmount); @@ -207,11 +208,11 @@ class PiggyBankTransformer extends AbstractTransformer 'currency_id' => (string)$currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int)$currency->decimal_places, + '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' => (int)$this->default->decimal_places, + 'native_currency_decimal_places' => $this->default->decimal_places, 'current_amount' => $currentAmount, 'native_current_amount' => $nativeCurrentAmount, 'target_amount' => $targetAmount, @@ -223,7 +224,7 @@ class PiggyBankTransformer extends AbstractTransformer 'native_save_per_month' => $nativeSavePerMonth, 'start_date' => $startDate, 'target_date' => $targetDate, - 'order' => (int)$piggyBank->order, + 'order' => $piggyBank->order, 'active' => $piggyBank->active, 'notes' => $note, 'object_group_id' => $group ? $group['object_group_id'] : null, @@ -239,7 +240,12 @@ class PiggyBankTransformer extends AbstractTransformer } /** - * @return string|null + * @param string $currentAmount + * @param string $targetAmount + * @param Carbon|null $startDate + * @param Carbon|null $targetDate + * + * @return string */ private function getSuggestedMonthlyAmount(string $currentAmount, string $targetAmount, ?Carbon $startDate, ?Carbon $targetDate): string { diff --git a/app/Transformers/V2/PreferenceTransformer.php b/app/Transformers/V2/PreferenceTransformer.php index 44131339f3..8d5c6548b9 100644 --- a/app/Transformers/V2/PreferenceTransformer.php +++ b/app/Transformers/V2/PreferenceTransformer.php @@ -49,7 +49,7 @@ class PreferenceTransformer extends AbstractTransformer public function transform(Preference $preference): array { return [ - 'id' => (int)$preference->id, + 'id' => $preference->id, 'created_at' => $preference->created_at->toAtomString(), 'updated_at' => $preference->updated_at->toAtomString(), 'name' => $preference->name, diff --git a/app/Transformers/V2/TransactionGroupTransformer.php b/app/Transformers/V2/TransactionGroupTransformer.php index e67db9c145..77be731803 100644 --- a/app/Transformers/V2/TransactionGroupTransformer.php +++ b/app/Transformers/V2/TransactionGroupTransformer.php @@ -31,7 +31,6 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournalMeta; use FireflyIII\Models\TransactionType; -use FireflyIII\Support\Http\Api\ConvertsExchangeRates; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\NullArrayObject; use Illuminate\Support\Collection; @@ -43,8 +42,6 @@ use stdClass; */ class TransactionGroupTransformer extends AbstractTransformer { - use ConvertsExchangeRates; - private ExchangeRateConverter $converter; private array $currencies = []; private TransactionCurrency $default; @@ -64,7 +61,7 @@ class TransactionGroupTransformer extends AbstractTransformer foreach ($objects as $object) { foreach ($object['sums'] as $sum) { $id = (int)$sum['currency_id']; - $currencies[$id] = $currencies[$id] ?? TransactionCurrency::find($sum['currency_id']); + $currencies[$id] ??= TransactionCurrency::find($sum['currency_id']); } /** @var array $transaction */ foreach ($object['transactions'] as $transaction) { @@ -79,7 +76,7 @@ class TransactionGroupTransformer extends AbstractTransformer $meta = TransactionJournalMeta::whereIn('transaction_journal_id', array_keys($journals))->get(); /** @var TransactionJournalMeta $entry */ foreach ($meta as $entry) { - $id = (int)$entry->transaction_journal_id; + $id = $entry->transaction_journal_id; $this->meta[$id][$entry->name] = $entry->data; } @@ -87,7 +84,7 @@ class TransactionGroupTransformer extends AbstractTransformer $notes = Note::whereNoteableType(TransactionJournal::class)->whereIn('noteable_id', array_keys($journals))->get(); /** @var Note $note */ foreach ($notes as $note) { - $id = (int)$note->noteable_id; + $id = $note->noteable_id; $this->notes[$id] = $note; } @@ -195,7 +192,7 @@ class TransactionGroupTransformer extends AbstractTransformer 'native_currency_code' => $this->default->code, 'native_currency_name' => $this->default->name, 'native_currency_symbol' => $this->default->symbol, - 'native_currency_decimal_places' => (int)$this->default->decimal_places, + 'native_currency_decimal_places' => $this->default->decimal_places, // foreign currency amount: 'foreign_currency_id' => $this->stringFromArray($transaction, 'foreign_currency_id', null), @@ -303,16 +300,28 @@ class TransactionGroupTransformer extends AbstractTransformer } // app('log')->debug(sprintf('Now in date("%s")', $string)); if (10 === strlen($string)) { - return Carbon::createFromFormat('Y-m-d', $string, config('app.timezone')); + $res = Carbon::createFromFormat('Y-m-d', $string, config('app.timezone')); + if (false === $res) { + return null; + } + return $res; } if (25 === strlen($string)) { return Carbon::parse($string, config('app.timezone')); } if (19 === strlen($string) && str_contains($string, 'T')) { - return Carbon::createFromFormat('Y-m-d\TH:i:s', substr($string, 0, 19), config('app.timezone')); + $res = Carbon::createFromFormat('Y-m-d\TH:i:s', substr($string, 0, 19), config('app.timezone')); + if (false === $res) { + return null; + } + return $res; } // 2022-01-01 01:01:01 - return Carbon::createFromFormat('Y-m-d H:i:s', substr($string, 0, 19), config('app.timezone')); + $res = Carbon::createFromFormat('Y-m-d H:i:s', substr($string, 0, 19), config('app.timezone')); + if (false === $res) { + return null; + } + return $res; } } diff --git a/app/Transformers/V2/UserGroupTransformer.php b/app/Transformers/V2/UserGroupTransformer.php index 3f7c44b0d2..f8fb013411 100644 --- a/app/Transformers/V2/UserGroupTransformer.php +++ b/app/Transformers/V2/UserGroupTransformer.php @@ -57,8 +57,8 @@ class UserGroupTransformer extends AbstractTransformer $user = auth()->user(); /** @var UserGroup $userGroup */ foreach ($objects as $userGroup) { - $userGroupId = (int)$userGroup->id; - $access = $user->hasRoleInGroup($userGroup, UserRoleEnum::VIEW_MEMBERSHIPS, true, true); + $userGroupId = $userGroup->id; + $access = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::VIEW_MEMBERSHIPS) || $user->hasRole('owner'); if ($access) { $groupMemberships = $userGroup->groupMemberships()->get(); /** @var GroupMembership $groupMembership */ @@ -84,11 +84,11 @@ class UserGroupTransformer extends AbstractTransformer public function transform(UserGroup $userGroup): array { $return = [ - 'id' => (int)$userGroup->id, + 'id' => $userGroup->id, 'created_at' => $userGroup->created_at->toAtomString(), 'updated_at' => $userGroup->updated_at->toAtomString(), 'title' => $userGroup->title, - 'members' => $this->memberships[(int)$userGroup->id] ?? [], + 'members' => $this->memberships[$userGroup->id] ?? [], ]; // if the user has a specific role in this group, then collect the memberships. diff --git a/app/Transformers/WebhookMessageTransformer.php b/app/Transformers/WebhookMessageTransformer.php index 2ffaa2911c..f4a1bea0b9 100644 --- a/app/Transformers/WebhookMessageTransformer.php +++ b/app/Transformers/WebhookMessageTransformer.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Transformers; use FireflyIII\Models\WebhookMessage; -use Jsonexception; +use JsonException; /** * Class WebhookMessageTransformer diff --git a/app/Transformers/WebhookTransformer.php b/app/Transformers/WebhookTransformer.php index c39e015f58..5ed705f4de 100644 --- a/app/Transformers/WebhookTransformer.php +++ b/app/Transformers/WebhookTransformer.php @@ -34,14 +34,10 @@ use FireflyIII\Models\Webhook; */ class WebhookTransformer extends AbstractTransformer { - private array $enums; - /** * WebhookTransformer constructor. */ - public function __construct() - { - } + public function __construct() {} /** * Transform webhook. @@ -53,7 +49,7 @@ class WebhookTransformer extends AbstractTransformer public function transform(Webhook $webhook): array { return [ - 'id' => (int)$webhook->id, + 'id' => $webhook->id, 'created_at' => $webhook->created_at->toAtomString(), 'updated_at' => $webhook->updated_at->toAtomString(), 'active' => $webhook->active, diff --git a/app/User.php b/app/User.php index 0effda5f39..4ce095271c 100644 --- a/app/User.php +++ b/app/User.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII; +use Carbon\Carbon; use Eloquent; use Exception; use FireflyIII\Enums\UserRoleEnum; @@ -66,7 +67,6 @@ use Illuminate\Notifications\DatabaseNotification; use Illuminate\Notifications\DatabaseNotificationCollection; use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notification; -use Illuminate\Support\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Str; use Laravel\Passport\Client; @@ -80,7 +80,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class User. * - * @property int $id + * @property int|string $id * @property string $email * @property bool $isAdmin * @property bool $has2FA @@ -173,38 +173,18 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class User extends Authenticatable { - use Notifiable; use HasApiTokens; + use Notifiable; - /** - * The attributes that should be cast to native types. - * - * @var array - */ protected $casts - = [ + = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'blocked' => 'boolean', ]; - /** - * The attributes that are mass assignable. - * - * @var array - */ protected $fillable = ['email', 'password', 'blocked', 'blocked_code']; - /** - * The attributes excluded from the model's JSON form. - * - * @var array - */ - protected $hidden = ['password', 'remember_token']; - /** - * The database table used by the model. - * - * @var string - */ - protected $table = 'users'; + protected $hidden = ['password', 'remember_token']; + protected $table = 'users'; /** * @param string $value @@ -212,7 +192,7 @@ class User extends Authenticatable * @return User * @throws NotFoundHttpException */ - public static function routeBinder(string $value): User + public static function routeBinder(string $value): self { if (auth()->check()) { $userId = (int)$value; @@ -380,35 +360,51 @@ class User extends Authenticatable return 'objectguid'; } - /** - * Does the user have role X in group Y? + * Does the user have role X in group Y, or is the user the group owner of has full rights to the group? * * If $allowOverride is set to true, then the roles FULL or OWNER will also be checked, * which means that in most cases the user DOES have access, regardless of the original role submitted in $role. * * @param UserGroup $userGroup * @param UserRoleEnum $role - * @param bool $allowOverride * * @return bool */ - public function hasRoleInGroup(UserGroup $userGroup, UserRoleEnum $role, bool $allowGroupOverride = false, bool $allowSystemOverride = false): bool + public function hasRoleInGroupOrOwner(UserGroup $userGroup, UserRoleEnum $role): bool { - if ($allowSystemOverride && $this->hasRole('owner')) { - app('log')->debug(sprintf('hasRoleInGroup: user "#%d %s" is system owner and allowSystemOverride = true, return true', $this->id, $this->email)); - return true; - } - $roles = [$role->value]; - if ($allowGroupOverride) { - $roles[] = UserRoleEnum::OWNER->value; - $roles[] = UserRoleEnum::FULL->value; - } - app('log')->debug(sprintf('in hasRoleInGroup(%s)', join(', ', $roles))); + $roles = [$role->value, UserRoleEnum::OWNER->value, UserRoleEnum::FULL->value]; + return $this->hasAnyRoleInGroup($userGroup, $roles); + } + + /** + * Does the user have role X in group Y? + * + * @param UserGroup $userGroup + * @param UserRoleEnum $role + * + * @return bool + */ + public function hasSpecificRoleInGroup(UserGroup $userGroup, UserRoleEnum $role): bool + { + return $this->hasAnyRoleInGroup($userGroup, [$role]); + } + + /** + * Does the user have role X, Y or Z in group A? + * + * @param UserGroup $userGroup + * @param array $roles + * + * @return bool + */ + private function hasAnyRoleInGroup(UserGroup $userGroup, array $roles): bool + { + app('log')->debug(sprintf('in hasAnyRoleInGroup(%s)', implode(', ', $roles))); /** @var Collection $dbRoles */ $dbRoles = UserRole::whereIn('title', $roles)->get(); if (0 === $dbRoles->count()) { - app('log')->error(sprintf('Could not find role(s): %s. Probably migration mishap.', join(', ', $roles))); + app('log')->error(sprintf('Could not find role(s): %s. Probably migration mishap.', implode(', ', $roles))); return false; } $dbRolesIds = $dbRoles->pluck('id')->toArray(); @@ -423,7 +419,7 @@ class User extends Authenticatable 'User #%d "%s" does not have roles %s in user group #%d "%s"', $this->id, $this->email, - join(', ', $roles), + implode(', ', $roles), $userGroup->id, $userGroup->title )); @@ -447,37 +443,12 @@ class User extends Authenticatable 'User #%d "%s" does not have roles %s in user group #%d "%s"', $this->id, $this->email, - join(', ', $roles), + implode(', ', $roles), $userGroup->id, $userGroup->title )); return false; - // // not necessary, should always return true: - // $result = $groupMembership->userRole->title === $role->value; - // app('log')->error(sprintf('Does user #%d "%s" have role "%s" in user group #%d "%s"? %s', - // $this->id, $this->email, - // $role->value, $userGroup->id, $userGroup->title, var_export($result, true))); - // return $result; - } - /** - * @param string $role - * - * @return bool - */ - public function hasRole(string $role): bool - { - return $this->roles()->where('name', $role)->count() === 1; - } - - /** - * Link to roles. - * - * @return BelongsToMany - */ - public function roles(): BelongsToMany - { - return $this->belongsToMany(Role::class); } /** @@ -539,8 +510,9 @@ class User extends Authenticatable */ public function routeNotificationFor($driver, $notification = null) { - if (method_exists($this, $method = 'routeNotificationFor' . Str::studly($driver))) { - return $this->{$method}($notification); + $method = 'routeNotificationFor' . Str::studly($driver); + if (method_exists($this, $method)) { + return $this->{$method}($notification); // @phpstan-ignore-line } $email = $this->email; // see if user has alternative email address: @@ -560,6 +532,28 @@ class User extends Authenticatable }; } + /** + * This method refers to the "global" role a user can have, outside of any group they may be part of. + * + * @param string $role + * + * @return bool + */ + public function hasRole(string $role): bool + { + return $this->roles()->where('name', $role)->count() === 1; + } + + /** + * Link to roles. + * + * @return BelongsToMany + */ + public function roles(): BelongsToMany + { + return $this->belongsToMany(Role::class); + } + /** * Route notifications for the Slack channel. * @@ -572,20 +566,28 @@ class User extends Authenticatable public function routeNotificationForSlack(Notification $notification): string { // this check does not validate if the user is owner, Should be done by notification itself. + $res = app('fireflyconfig')->get('slack_webhook_url', '')->data; + if (is_array($res)) { + $res = ''; + } + $res = (string)$res; if ($notification instanceof TestNotification) { - return app('fireflyconfig')->get('slack_webhook_url', '')->data; + return $res; } if ($notification instanceof UserInvitation) { - return app('fireflyconfig')->get('slack_webhook_url', '')->data; + return $res; } if ($notification instanceof UserRegistration) { - return app('fireflyconfig')->get('slack_webhook_url', '')->data; + return $res; } if ($notification instanceof VersionCheckResult) { - return app('fireflyconfig')->get('slack_webhook_url', '')->data; + return $res; } - - return app('preferences')->getForUser($this, 'slack_webhook_url', '')->data; + $pref = app('preferences')->getForUser($this, 'slack_webhook_url', '')->data; + if (is_array($pref)) { + return ''; + } + return (string)$pref; } /** diff --git a/app/Validation/Account/DepositValidation.php b/app/Validation/Account/DepositValidation.php index 4e306e18f5..fb1911c0a7 100644 --- a/app/Validation/Account/DepositValidation.php +++ b/app/Validation/Account/DepositValidation.php @@ -25,7 +25,6 @@ namespace FireflyIII\Validation\Account; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use Illuminate\Support\Facades\Log; /** * Trait DepositValidation @@ -44,7 +43,7 @@ trait DepositValidation $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; - Log::debug('Now in validateDepositDestination', $array); + app('log')->debug('Now in validateDepositDestination', $array); // source can be any of the following types. $validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? []; @@ -52,12 +51,12 @@ trait DepositValidation // if both values are NULL we return false, // because the destination of a deposit can't be created. $this->destError = (string)trans('validation.deposit_dest_need_data'); - Log::error('Both values are NULL, cant create deposit destination.'); + app('log')->error('Both values are NULL, cant create deposit destination.'); $result = false; } // if the account can be created anyway we don't need to search. if (null === $result && true === $this->canCreateTypes($validTypes)) { - Log::debug('Can create some of these types, so return true.'); + app('log')->debug('Can create some of these types, so return true.'); $result = true; } @@ -65,17 +64,17 @@ trait DepositValidation // otherwise try to find the account: $search = $this->findExistingAccount($validTypes, $array); if (null === $search) { - Log::debug('findExistingAccount() returned NULL, so the result is false.'); + app('log')->debug('findExistingAccount() returned NULL, so the result is false.'); $this->destError = (string)trans('validation.deposit_dest_bad_data', ['id' => $accountId, 'name' => $accountName]); $result = false; } if (null !== $search) { - Log::debug(sprintf('findExistingAccount() returned #%d ("%s"), so the result is true.', $search->id, $search->name)); + app('log')->debug(sprintf('findExistingAccount() returned #%d ("%s"), so the result is true.', $search->id, $search->name)); $this->setDestination($search); $result = true; } } - Log::debug(sprintf('validateDepositDestination will return %s', var_export($result, true))); + app('log')->debug(sprintf('validateDepositDestination will return %s', var_export($result, true))); return $result; } @@ -106,7 +105,7 @@ trait DepositValidation $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; $accountNumber = array_key_exists('number', $array) ? $array['number'] : null; - Log::debug('Now in validateDepositSource', $array); + app('log')->debug('Now in validateDepositSource', $array); // null = we found nothing at all or didn't even search // false = invalid results @@ -128,7 +127,7 @@ trait DepositValidation // if there is an iban, it can only be in use by a valid source type, or we will fail. if (null !== $accountIban && '' !== $accountIban) { - app('log')->debug('Check if there is not already an account with this IBAN'); + app('log')->debug('Check if there is not already another account with this IBAN'); $existing = $this->findExistingAccount($validTypes, ['iban' => $accountIban], true); if (null !== $existing) { $this->sourceError = (string)trans('validation.deposit_src_iban_exists'); @@ -141,11 +140,11 @@ trait DepositValidation if (null !== $accountId) { $search = $this->getRepository()->find($accountId); if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { - Log::debug(sprintf('User submitted an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type)); - Log::debug(sprintf('Firefly III accepts ID #%d as valid account data.', $accountId)); + app('log')->debug(sprintf('User submitted an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type)); + app('log')->debug(sprintf('Firefly III accepts ID #%d as valid account data.', $accountId)); } if (null !== $search && in_array($search->accountType->type, $validTypes, true)) { - Log::debug('ID result is not null and seems valid, save as source account.'); + app('log')->debug('ID result is not null and seems valid, save as source account.'); $this->setSource($search); $result = true; } @@ -155,11 +154,11 @@ trait DepositValidation if (null !== $accountIban) { $search = $this->getRepository()->findByIbanNull($accountIban, $validTypes); if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { - Log::debug(sprintf('User submitted IBAN ("%s"), which is a "%s", so this is not a valid source.', $accountIban, $search->accountType->type)); + app('log')->debug(sprintf('User submitted IBAN ("%s"), which is a "%s", so this is not a valid source.', $accountIban, $search->accountType->type)); $result = false; } if (null !== $search && in_array($search->accountType->type, $validTypes, true)) { - Log::debug('IBAN result is not null and seems valid, save as source account.'); + app('log')->debug('IBAN result is not null and seems valid, save as source account.'); $this->setSource($search); $result = true; } @@ -169,13 +168,13 @@ trait DepositValidation if (null !== $accountNumber && '' !== $accountNumber) { $search = $this->getRepository()->findByAccountNumber($accountNumber, $validTypes); if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { - Log::debug( + app('log')->debug( sprintf('User submitted number ("%s"), which is a "%s", so this is not a valid source.', $accountNumber, $search->accountType->type) ); $result = false; } if (null !== $search && in_array($search->accountType->type, $validTypes, true)) { - Log::debug('Number result is not null and seems valid, save as source account.'); + app('log')->debug('Number result is not null and seems valid, save as source account.'); $this->setSource($search); $result = true; } diff --git a/app/Validation/Account/LiabilityValidation.php b/app/Validation/Account/LiabilityValidation.php index b9a94d3dce..f1920d13ee 100644 --- a/app/Validation/Account/LiabilityValidation.php +++ b/app/Validation/Account/LiabilityValidation.php @@ -26,7 +26,6 @@ namespace FireflyIII\Validation\Account; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use Illuminate\Support\Facades\Log; /** * Trait LiabilityValidation @@ -40,7 +39,7 @@ trait LiabilityValidation */ protected function validateLCDestination(array $array): bool { - Log::debug('Now in validateLCDestination', $array); + app('log')->debug('Now in validateLCDestination', $array); $result = null; $accountId = array_key_exists('id', $array) ? $array['id'] : null; $accountName = array_key_exists('name', $array) ? $array['name'] : null; @@ -49,23 +48,23 @@ trait LiabilityValidation // if the ID is not null the source account should be a dummy account of the type liability credit. // the ID of the destination must belong to a liability. if (null !== $accountId) { - if (AccountType::LIABILITY_CREDIT !== $this?->source?->accountType?->type) { - Log::error('Source account is not a liability.'); + if (AccountType::LIABILITY_CREDIT !== $this->source?->accountType?->type) { + app('log')->error('Source account is not a liability.'); return false; } $result = $this->findExistingAccount($validTypes, $array); if (null === $result) { - Log::error('Destination account is not a liability.'); + app('log')->error('Destination account is not a liability.'); return false; } return true; } if (null !== $accountName && '' !== $accountName) { - Log::debug('Destination ID is null, now we can assume the destination is a (new) liability credit account.'); + app('log')->debug('Destination ID is null, now we can assume the destination is a (new) liability credit account.'); return true; } - Log::error('Destination ID is null, but destination name is also NULL.'); + app('log')->error('Destination ID is null, but destination name is also NULL.'); return false; } @@ -78,19 +77,19 @@ trait LiabilityValidation */ protected function validateLCSource(array $array): bool { - Log::debug('Now in validateLCSource', $array); + app('log')->debug('Now in validateLCSource', $array); // if the array has an ID and ID is not null, try to find it and check type. // this account must be a liability $accountId = array_key_exists('id', $array) ? $array['id'] : null; if (null !== $accountId) { - Log::debug('Source ID is not null, assume were looking for a liability.'); + app('log')->debug('Source ID is not null, assume were looking for a liability.'); // find liability credit: $result = $this->findExistingAccount(config('firefly.valid_liabilities'), $array); if (null === $result) { - Log::error('Did not find a liability account, return false.'); + app('log')->error('Did not find a liability account, return false.'); return false; } - Log::debug(sprintf('Return true, found #%d ("%s")', $result->id, $result->name)); + app('log')->debug(sprintf('Return true, found #%d ("%s")', $result->id, $result->name)); $this->setSource($result); return true; } @@ -100,11 +99,11 @@ trait LiabilityValidation $result = true; if ('' === $accountName || null === $accountName) { - Log::error('Array must have a name, is not the case, return false.'); + app('log')->error('Array must have a name, is not the case, return false.'); $result = false; } if (true === $result) { - Log::error('Array has a name, return true.'); + app('log')->error('Array has a name, return true.'); // set the source to be a (dummy) revenue account. $account = new Account(); $accountType = AccountType::whereType(AccountType::LIABILITY_CREDIT)->first(); diff --git a/app/Validation/Account/OBValidation.php b/app/Validation/Account/OBValidation.php index c77d65d4da..f419d8058a 100644 --- a/app/Validation/Account/OBValidation.php +++ b/app/Validation/Account/OBValidation.php @@ -26,7 +26,6 @@ namespace FireflyIII\Validation\Account; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use Illuminate\Support\Facades\Log; /** * Trait OBValidation @@ -43,20 +42,20 @@ trait OBValidation $result = null; $accountId = array_key_exists('id', $array) ? $array['id'] : null; $accountName = array_key_exists('name', $array) ? $array['name'] : null; - Log::debug('Now in validateOBDestination', $array); + app('log')->debug('Now in validateOBDestination', $array); // source can be any of the following types. - $validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? []; + $validTypes = $this->combinations[$this->transactionType][$this->source?->accountType->type] ?? []; if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) { // if both values are NULL we return false, // because the destination of a deposit can't be created. $this->destError = (string)trans('validation.ob_dest_need_data'); - Log::error('Both values are NULL, cant create OB destination.'); + app('log')->error('Both values are NULL, cant create OB destination.'); $result = false; } // if the account can be created anyway we don't need to search. if (null === $result && true === $this->canCreateTypes($validTypes)) { - Log::debug('Can create some of these types, so return true.'); + app('log')->debug('Can create some of these types, so return true.'); $result = true; } @@ -64,17 +63,17 @@ trait OBValidation // otherwise try to find the account: $search = $this->findExistingAccount($validTypes, $array); if (null === $search) { - Log::debug('findExistingAccount() returned NULL, so the result is false.', $validTypes); + app('log')->debug('findExistingAccount() returned NULL, so the result is false.', $validTypes); $this->destError = (string)trans('validation.ob_dest_bad_data', ['id' => $accountId, 'name' => $accountName]); $result = false; } if (null !== $search) { - Log::debug(sprintf('findExistingAccount() returned #%d ("%s"), so the result is true.', $search->id, $search->name)); + app('log')->debug(sprintf('findExistingAccount() returned #%d ("%s"), so the result is true.', $search->id, $search->name)); $this->setDestination($search); $result = true; } } - Log::debug(sprintf('validateOBDestination(%d, "%s") will return %s', $accountId, $accountName, var_export($result, true))); + app('log')->debug(sprintf('validateOBDestination(%d, "%s") will return %s', $accountId, $accountName, var_export($result, true))); return $result; } @@ -98,7 +97,7 @@ trait OBValidation { $accountId = array_key_exists('id', $array) ? $array['id'] : null; $accountName = array_key_exists('name', $array) ? $array['name'] : null; - Log::debug('Now in validateOBSource', $array); + app('log')->debug('Now in validateOBSource', $array); $result = null; // source can be any of the following types. $validTypes = array_keys($this->combinations[$this->transactionType]); @@ -114,19 +113,19 @@ trait OBValidation // if the user submits an ID only but that ID is not of the correct type, // return false. if (null !== $accountId && null === $accountName) { - Log::debug('Source ID is not null, but name is null.'); + app('log')->debug('Source ID is not null, but name is null.'); $search = $this->getRepository()->find($accountId); // the source resulted in an account, but it's not of a valid type. if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { $message = sprintf('User submitted only an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type); - Log::debug($message); + app('log')->debug($message); $this->sourceError = $message; $result = false; } // the source resulted in an account, AND it's of a valid type. if (null !== $search && in_array($search->accountType->type, $validTypes, true)) { - Log::debug(sprintf('Found account of correct type: #%d, "%s"', $search->id, $search->name)); + app('log')->debug(sprintf('Found account of correct type: #%d, "%s"', $search->id, $search->name)); $this->setSource($search); $result = true; } @@ -134,11 +133,12 @@ trait OBValidation // if the account can be created anyway we don't need to search. if (null === $result && true === $this->canCreateTypes($validTypes)) { - Log::debug('Result is still null.'); + app('log')->debug('Result is still null.'); $result = true; // set the source to be a (dummy) initial balance account. - $account = new Account(); + $account = new Account(); + /** @var AccountType $accountType */ $accountType = AccountType::whereType(AccountType::INITIAL_BALANCE)->first(); $account->accountType = $accountType; $this->setSource($account); diff --git a/app/Validation/Account/ReconciliationValidation.php b/app/Validation/Account/ReconciliationValidation.php index f3e3e1071a..f55f725eef 100644 --- a/app/Validation/Account/ReconciliationValidation.php +++ b/app/Validation/Account/ReconciliationValidation.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Validation\Account; use FireflyIII\Models\Account; -use Illuminate\Support\Facades\Log; /** * Trait ReconciliationValidation @@ -53,7 +52,7 @@ trait ReconciliationValidation } // after that, search for it expecting an asset account or a liability. - Log::debug('Now in validateReconciliationDestination', $array); + app('log')->debug('Now in validateReconciliationDestination', $array); // source can be any of the following types. $validTypes = array_keys($this->combinations[$this->transactionType]); @@ -65,7 +64,7 @@ trait ReconciliationValidation return false; } $this->setSource($search); - Log::debug('Valid source account!'); + app('log')->debug('Valid source account!'); return true; } @@ -85,13 +84,13 @@ trait ReconciliationValidation // is expected to be "positive", i.e. the money flows from the // source to the asset account that is the destination. if (null === $accountId && null === $accountName) { - Log::debug('The source is valid because ID and name are NULL.'); + app('log')->debug('The source is valid because ID and name are NULL.'); $this->setSource(new Account()); return true; } // after that, search for it expecting an asset account or a liability. - Log::debug('Now in validateReconciliationSource', $array); + app('log')->debug('Now in validateReconciliationSource', $array); // source can be any of the following types. $validTypes = array_keys($this->combinations[$this->transactionType]); @@ -103,7 +102,7 @@ trait ReconciliationValidation return false; } $this->setSource($search); - Log::debug('Valid source account!'); + app('log')->debug('Valid source account!'); return true; } diff --git a/app/Validation/Account/TransferValidation.php b/app/Validation/Account/TransferValidation.php index b7227d6db0..5c8146d899 100644 --- a/app/Validation/Account/TransferValidation.php +++ b/app/Validation/Account/TransferValidation.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Validation\Account; use FireflyIII\Models\Account; -use Illuminate\Support\Facades\Log; /** * Trait TransferValidation @@ -41,14 +40,14 @@ trait TransferValidation $accountId = array_key_exists('id', $array) ? $array['id'] : null; $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; - Log::debug('Now in validateTransferDestination', $array); + app('log')->debug('Now in validateTransferDestination', $array); // source can be any of the following types. $validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? []; if (null === $accountId && null === $accountName && null === $accountIban && false === $this->canCreateTypes($validTypes)) { // if both values are NULL we return false, // because the destination of a transfer can't be created. $this->destError = (string)trans('validation.transfer_dest_need_data'); - Log::error('Both values are NULL, cant create transfer destination.'); + app('log')->error('Both values are NULL, cant create transfer destination.'); return false; } @@ -99,7 +98,7 @@ trait TransferValidation $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; $accountNumber = array_key_exists('number', $array) ? $array['number'] : null; - Log::debug('Now in validateTransferSource', $array); + app('log')->debug('Now in validateTransferSource', $array); // source can be any of the following types. $validTypes = array_keys($this->combinations[$this->transactionType]); if (null === $accountId && null === $accountName @@ -122,7 +121,7 @@ trait TransferValidation return false; } $this->setSource($search); - Log::debug('Valid source!'); + app('log')->debug('Valid source!'); return true; } diff --git a/app/Validation/Account/WithdrawalValidation.php b/app/Validation/Account/WithdrawalValidation.php index e7ad03e8e5..2683fa14e8 100644 --- a/app/Validation/Account/WithdrawalValidation.php +++ b/app/Validation/Account/WithdrawalValidation.php @@ -25,7 +25,6 @@ namespace FireflyIII\Validation\Account; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use Illuminate\Support\Facades\Log; /** * Trait WithdrawalValidation @@ -42,7 +41,7 @@ trait WithdrawalValidation $accountId = array_key_exists('id', $array) ? $array['id'] : null; $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; - Log::debug('Now in validateGenericSource', $array); + app('log')->debug('Now in validateGenericSource', $array); // source can be any of the following types. $validTypes = [AccountType::ASSET, AccountType::REVENUE, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; if (null === $accountId && null === $accountName && null === $accountIban && false === $this->canCreateTypes($validTypes)) { @@ -63,7 +62,7 @@ trait WithdrawalValidation return false; } $this->setSource($search); - Log::debug('Valid source account!'); + app('log')->debug('Valid source account!'); return true; } @@ -94,7 +93,7 @@ trait WithdrawalValidation $accountName = array_key_exists('name', $array) ? $array['name'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; $accountNumber = array_key_exists('number', $array) ? $array['number'] : null; - Log::debug('Now in validateWithdrawalDestination()', $array); + app('log')->debug('Now in validateWithdrawalDestination()', $array); // source can be any of the following types. $validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? []; app('log')->debug('Source type can be: ', $validTypes); @@ -149,7 +148,7 @@ trait WithdrawalValidation $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; $accountNumber = array_key_exists('number', $array) ? $array['number'] : null; - Log::debug('Now in validateWithdrawalSource', $array); + app('log')->debug('Now in validateWithdrawalSource', $array); // source can be any of the following types. $validTypes = array_keys($this->combinations[$this->transactionType]); if (null === $accountId && null === $accountName && null === $accountNumber && null === $accountIban && false === $this->canCreateTypes($validTypes)) { @@ -170,7 +169,7 @@ trait WithdrawalValidation return false; } $this->setSource($search); - Log::debug('Valid source account!'); + app('log')->debug('Valid source account!'); return true; } diff --git a/app/Validation/AccountValidator.php b/app/Validation/AccountValidator.php index 080665e403..d8fc17fb07 100644 --- a/app/Validation/AccountValidator.php +++ b/app/Validation/AccountValidator.php @@ -36,19 +36,18 @@ use FireflyIII\Validation\Account\OBValidation; use FireflyIII\Validation\Account\ReconciliationValidation; use FireflyIII\Validation\Account\TransferValidation; use FireflyIII\Validation\Account\WithdrawalValidation; -use Illuminate\Support\Facades\Log; /** * Class AccountValidator */ class AccountValidator { - use WithdrawalValidation; use DepositValidation; - use TransferValidation; - use ReconciliationValidation; - use OBValidation; use LiabilityValidation; + use OBValidation; + use ReconciliationValidation; + use TransferValidation; + use WithdrawalValidation; public bool $createMode; public string $destError; @@ -59,8 +58,6 @@ class AccountValidator private array $combinations; private string $transactionType; private bool $useUserGroupRepository = false; - private User $user; - private UserGroup $userGroup; private UserGroupAccountRepositoryInterface $userGroupAccountRepository; /** @@ -92,10 +89,10 @@ class AccountValidator public function setSource(?Account $account): void { if (null === $account) { - Log::debug('AccountValidator source is set to NULL'); + app('log')->debug('AccountValidator source is set to NULL'); } if (null !== $account) { - Log::debug(sprintf('AccountValidator source is set to #%d: "%s" (%s)', $account->id, $account->name, $account->accountType->type)); + app('log')->debug(sprintf('AccountValidator source is set to #%d: "%s" (%s)', $account->id, $account->name, $account->accountType->type)); } $this->source = $account; } @@ -106,10 +103,10 @@ class AccountValidator public function setDestination(?Account $account): void { if (null === $account) { - Log::debug('AccountValidator destination is set to NULL'); + app('log')->debug('AccountValidator destination is set to NULL'); } if (null !== $account) { - Log::debug(sprintf('AccountValidator destination is set to #%d: "%s" (%s)', $account->id, $account->name, $account->accountType->type)); + app('log')->debug(sprintf('AccountValidator destination is set to #%d: "%s" (%s)', $account->id, $account->name, $account->accountType->type)); } $this->destination = $account; } @@ -119,7 +116,7 @@ class AccountValidator */ public function setTransactionType(string $transactionType): void { - Log::debug(sprintf('Transaction type for validator is now "%s".', ucfirst($transactionType))); + app('log')->debug(sprintf('Transaction type for validator is now "%s".', ucfirst($transactionType))); $this->transactionType = ucfirst($transactionType); } @@ -128,7 +125,6 @@ class AccountValidator */ public function setUser(User $user): void { - $this->user = $user; $this->accountRepository->setUser($user); $this->useUserGroupRepository = false; } @@ -140,7 +136,6 @@ class AccountValidator */ public function setUserGroup(UserGroup $userGroup): void { - $this->userGroup = $userGroup; $this->userGroupAccountRepository->setUserGroup($userGroup); $this->useUserGroupRepository = true; } @@ -152,9 +147,9 @@ class AccountValidator */ public function validateDestination(array $array): bool { - Log::debug('Now in AccountValidator::validateDestination()', $array); + app('log')->debug('Now in AccountValidator::validateDestination()', $array); if (null === $this->source) { - Log::error('Source is NULL, always FALSE.'); + app('log')->error('Source is NULL, always FALSE.'); $this->destError = 'No source account validation has taken place yet. Please do this first or overrule the object.'; return false; @@ -162,7 +157,7 @@ class AccountValidator switch ($this->transactionType) { default: $this->destError = sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType); - Log::error(sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType)); + app('log')->error(sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType)); $result = false; break; @@ -197,10 +192,10 @@ class AccountValidator */ public function validateSource(array $array): bool { - Log::debug('Now in AccountValidator::validateSource()', $array); + app('log')->debug('Now in AccountValidator::validateSource()', $array); switch ($this->transactionType) { default: - Log::error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will do a generic check.', $this->transactionType)); + app('log')->error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will do a generic check.', $this->transactionType)); $result = $this->validateGenericSource($array); break; case TransactionType::WITHDRAWAL: @@ -220,7 +215,7 @@ class AccountValidator break; case TransactionType::RECONCILIATION: - Log::debug('Calling validateReconciliationSource'); + app('log')->debug('Calling validateReconciliationSource'); $result = $this->validateReconciliationSource($array); break; } @@ -235,16 +230,16 @@ class AccountValidator */ protected function canCreateTypes(array $accountTypes): bool { - Log::debug('Can we create any of these types?', $accountTypes); + app('log')->debug('Can we create any of these types?', $accountTypes); /** @var string $accountType */ foreach ($accountTypes as $accountType) { if ($this->canCreateType($accountType)) { - Log::debug(sprintf('YES, we can create a %s', $accountType)); + app('log')->debug(sprintf('YES, we can create a %s', $accountType)); return true; } } - Log::debug('NO, we cant create any of those.'); + app('log')->debug('NO, we cant create any of those.'); return false; } @@ -270,10 +265,11 @@ class AccountValidator * @param bool $inverse * * @return Account|null + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) */ protected function findExistingAccount(array $validTypes, array $data, bool $inverse = false): ?Account { - Log::debug('Now in findExistingAccount', $data); + app('log')->debug('Now in findExistingAccount', $data); app('log')->debug('The search will be reversed!'); $accountId = array_key_exists('id', $data) ? $data['id'] : null; $accountIban = array_key_exists('iban', $data) ? $data['iban'] : null; diff --git a/app/Validation/Administration/ValidatesAdministrationAccess.php b/app/Validation/Administration/ValidatesAdministrationAccess.php deleted file mode 100644 index ecd88233ec..0000000000 --- a/app/Validation/Administration/ValidatesAdministrationAccess.php +++ /dev/null @@ -1,107 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Validation\Administration; - -use FireflyIII\Enums\UserRoleEnum; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Repositories\User\UserRepositoryInterface; -use FireflyIII\User; -use Illuminate\Auth\AuthenticationException; -use Illuminate\Support\Facades\Log; -use Illuminate\Validation\Validator; - -/** - * @deprecated - * Trait ValidatesAdministrationAccess - */ -trait ValidatesAdministrationAccess -{ - /** - * @param Validator $validator - * @param array $allowedRoles - * - * @return void - * @throws AuthenticationException - * @throws FireflyException - * @deprecated - */ - protected function validateAdministration(Validator $validator, array $allowedRoles): void - { - throw new FireflyException('deprecated method, must be done through user.'); - Log::debug('Now in validateAdministration()'); - if (!auth()->check()) { - Log::error('User is not authenticated.'); - throw new AuthenticationException('No access to validateAdministration() method.'); - } - /** @var User $user */ - $user = auth()->user(); - // get data from request: - $data = $validator->getData(); - // check if user is part of this administration - $administrationId = (int)($data['administration_id'] ?? $user->getAdministrationId()); - // safety catch: - if (0 === $administrationId) { - Log::error('validateAdministration ran into empty administration ID.'); - throw new AuthenticationException('Cannot validate administration.'); - } - // grab the group: - $repository = app(UserRepositoryInterface::class); - - // collect the user's roles in this group: - $array = $repository->getRolesInGroup($user, $administrationId); - if (0 === count($array)) { - Log::error(sprintf('User #%d ("%s") has no membership in group #%d.', $user->id, $user->email, $administrationId)); - $validator->errors()->add('administration', (string)trans('validation.no_access_user_group')); - return; - } - if (in_array(UserRoleEnum::OWNER->value, $array, true)) { - Log::debug('User is owner of this administration.'); - return; - } - if (in_array(UserRoleEnum::OWNER->value, $array, true)) { - Log::debug('User has full access to this administration.'); - return; - } - $access = true; - foreach ($allowedRoles as $allowedRole) { - if (!in_array($allowedRole, $array, true)) { - $access = false; - } - } - if (false === $access) { - Log::error( - sprintf( - 'User #%d has memberships [%s] to group #%d but needs [%s].', - $user->id, - join(', ', $array), - $administrationId, - join(', ', $allowedRoles) - ) - ); - $validator->errors()->add('administration', (string)trans('validation.no_access_user_group')); - } - } -} diff --git a/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php b/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php index e641f9a406..c7abe0fdbc 100644 --- a/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php +++ b/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php @@ -54,9 +54,6 @@ trait ValidatesAutoBudgetRequest return; } - if ('' === $amount) { - $validator->errors()->add('auto_budget_amount', (string)trans('validation.amount_required_for_auto_budget')); - } if (1 !== bccomp((string)$amount, '0')) { $validator->errors()->add('auto_budget_amount', (string)trans('validation.auto_budget_amount_positive')); } diff --git a/app/Validation/CurrencyValidation.php b/app/Validation/CurrencyValidation.php index ea963a6ff8..9571b1027a 100644 --- a/app/Validation/CurrencyValidation.php +++ b/app/Validation/CurrencyValidation.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -34,7 +33,7 @@ use Illuminate\Validation\Validator; */ trait CurrencyValidation { - public const TEST = 'Test'; + public const string TEST = 'Test'; /** * If the transactions contain foreign amounts, there must also be foreign currency information. @@ -46,7 +45,7 @@ trait CurrencyValidation if ($validator->errors()->count() > 0) { return; } - Log::debug('Now in validateForeignCurrencyInformation()'); + app('log')->debug('Now in validateForeignCurrencyInformation()'); $transactions = $this->getTransactionsArray($validator); foreach ($transactions as $index => $transaction) { diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 02e6b4ef5f..396a1d32ce 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -36,22 +36,19 @@ use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Services\Password\Verifier; -use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\ParseDateString; use FireflyIII\TransactionRules\Triggers\TriggerInterface; use FireflyIII\User; use Google2FA; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException; use PragmaRX\Google2FA\Exceptions\InvalidCharactersException; use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException; use ValueError; -use function is_string; - /** * Class FireflyValidator. + * TODO all of these validations must become separate classes. */ class FireflyValidator extends Validator { @@ -63,6 +60,7 @@ class FireflyValidator extends Validator * @throws IncompatibleWithGoogleAuthenticatorException * @throws InvalidCharactersException * @throws SecretKeyTooShortException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validate2faCode($attribute, $value): bool { @@ -71,19 +69,23 @@ class FireflyValidator extends Validator } $user = auth()->user(); if (null === $user) { - Log::error('No user during validate2faCode'); + app('log')->error('No user during validate2faCode'); return false; } - $secretPreference = Preferences::get('temp-mfa-secret'); + $secretPreference = app('preferences')->get('temp-mfa-secret'); $secret = $secretPreference?->data ?? ''; + if (is_array($secret)) { + $secret = ''; + } - return Google2FA::verifyKey($secret, $value); + return (bool)Google2FA::verifyKey((string)$secret, $value); } /** * @param mixed $attribute * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -102,6 +104,7 @@ class FireflyValidator extends Validator /** * @param mixed $attribute * @param mixed $value + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -122,6 +125,7 @@ class FireflyValidator extends Validator /** * @param mixed $attribute * @param mixed $value + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -186,7 +190,7 @@ class FireflyValidator extends Validator $value = strtoupper($value); // replace characters outside of ASCI range. - $value = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value); + $value = (string)iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value); $search = [' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; $replace = [ '', @@ -223,15 +227,15 @@ class FireflyValidator extends Validator $last = substr($value, 4); $iban = $last . $first; $iban = trim(str_replace($search, $replace, $iban)); - if (0 === strlen($iban)) { + if ('' === $iban) { return false; } try { $checksum = bcmod($iban, '97'); - } catch (ValueError $e) { + } catch (ValueError $e) { // @phpstan-ignore-line $message = sprintf('Could not validate IBAN check value "%s" (IBAN "%s")', $iban, $value); - Log::error($message); - Log::error($e->getTraceAsString()); + app('log')->error($message); + app('log')->error($e->getTraceAsString()); return false; } @@ -243,6 +247,7 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -258,6 +263,7 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -273,6 +279,7 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -298,7 +305,7 @@ class FireflyValidator extends Validator public function validateRuleActionValue(string $attribute, string $value = null): bool { // first, get the index from this string: - $value = $value ?? ''; + $value ??= ''; $parts = explode('.', $attribute); $index = (int)($parts[1] ?? '0'); @@ -432,7 +439,7 @@ class FireflyValidator extends Validator try { $parser->parseDate($value); } catch (FireflyException $e) { - Log::error($e->getMessage()); + app('log')->error($e->getMessage()); return false; } @@ -444,6 +451,7 @@ class FireflyValidator extends Validator /** * @param mixed $attribute * @param mixed $value + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -467,6 +475,7 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -474,33 +483,33 @@ class FireflyValidator extends Validator { // because a user does not have to be logged in (tests and what-not). if (!auth()->check()) { - Log::debug('validateUniqueAccountForUser::anon'); + app('log')->debug('validateUniqueAccountForUser::anon'); return $this->validateAccountAnonymously(); } if (array_key_exists('objectType', $this->data)) { - Log::debug('validateUniqueAccountForUser::typeString'); + app('log')->debug('validateUniqueAccountForUser::typeString'); return $this->validateByAccountTypeString($value, $parameters, $this->data['objectType']); } if (array_key_exists('type', $this->data)) { - Log::debug('validateUniqueAccountForUser::typeString'); + app('log')->debug('validateUniqueAccountForUser::typeString'); return $this->validateByAccountTypeString($value, $parameters, (string)$this->data['type']); } if (array_key_exists('account_type_id', $this->data)) { - Log::debug('validateUniqueAccountForUser::typeId'); + app('log')->debug('validateUniqueAccountForUser::typeId'); return $this->validateByAccountTypeId($value, $parameters); } $parameterId = $parameters[0] ?? null; if (null !== $parameterId) { - Log::debug('validateUniqueAccountForUser::paramId'); + app('log')->debug('validateUniqueAccountForUser::paramId'); return $this->validateByParameterId((int)$parameterId, $value); } if (array_key_exists('id', $this->data)) { - Log::debug('validateUniqueAccountForUser::accountId'); + app('log')->debug('validateUniqueAccountForUser::accountId'); return $this->validateByAccountId($value); } // without type, just try to validate the name. - Log::debug('validateUniqueAccountForUser::accountName'); + app('log')->debug('validateUniqueAccountForUser::accountName'); return $this->validateByAccountName($value); } @@ -513,6 +522,7 @@ class FireflyValidator extends Validator return false; } + /** @var User $user */ $user = User::find($this->data['user_id']); $type = AccountType::find($this->data['account_type_id'])->first(); $value = $this->data['name']; @@ -623,6 +633,7 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -657,28 +668,29 @@ class FireflyValidator extends Validator app('log')->warning(sprintf('Account number "%s" is not unique and account type "%s" cannot share its account number.', $value, $type)); return false; } - Log::debug(sprintf('Account number "%s" is not unique but account type "%s" may share its account number.', $value, $type)); + app('log')->debug(sprintf('Account number "%s" is not unique but account type "%s" may share its account number.', $value, $type)); // one other account with this account number. /** @var AccountMeta $entry */ foreach ($set as $entry) { $otherAccount = $entry->account; $otherType = (string)config(sprintf('firefly.shortNamesByFullName.%s', $otherAccount->accountType->type)); if (('expense' === $otherType || 'revenue' === $otherType) && $otherType !== $type) { - Log::debug(sprintf('The other account with this account number is a "%s" so return true.', $otherType)); + app('log')->debug(sprintf('The other account with this account number is a "%s" so return true.', $otherType)); return true; } - Log::debug(sprintf('The other account with this account number is a "%s" so return false.', $otherType)); + app('log')->debug(sprintf('The other account with this account number is a "%s" so return false.', $otherType)); } return false; } /** - * @param $attribute - * @param $value + * @param string|null $attribute + * @param string|null $value + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ - public function validateUniqueCurrencyCode($attribute, $value): bool + public function validateUniqueCurrencyCode(string | null $attribute, string | null $value): bool { return $this->validateUniqueCurrency('code', (string)$attribute, (string)$value); } @@ -687,6 +699,7 @@ class FireflyValidator extends Validator * @param string $field * @param string $attribute * @param string $value + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -696,23 +709,23 @@ class FireflyValidator extends Validator } /** - * @param $attribute - * @param $value + * @param string|null $attribute + * @param string|null $value * * @return bool */ - public function validateUniqueCurrencyName($attribute, $value): bool + public function validateUniqueCurrencyName(string | null $attribute, string | null $value): bool { return $this->validateUniqueCurrency('name', (string)$attribute, (string)$value); } /** - * @param $attribute - * @param $value + * @param string|null $attribute + * @param string|null $value * * @return bool */ - public function validateUniqueCurrencySymbol($attribute, $value): bool + public function validateUniqueCurrencySymbol(string | null $attribute, string | null $value): bool { return $this->validateUniqueCurrency('symbol', (string)$attribute, (string)$value); } @@ -721,6 +734,7 @@ class FireflyValidator extends Validator * @param mixed $value * @param mixed $parameters * @param mixed $something + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -776,6 +790,7 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -808,6 +823,7 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -829,6 +845,7 @@ class FireflyValidator extends Validator * @param mixed $attribute * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ @@ -848,6 +865,7 @@ class FireflyValidator extends Validator /** * @param mixed $value * @param mixed $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * * @return bool */ diff --git a/app/Validation/GroupValidation.php b/app/Validation/GroupValidation.php index 29fb2511f3..4b5a2b2138 100644 --- a/app/Validation/GroupValidation.php +++ b/app/Validation/GroupValidation.php @@ -27,7 +27,6 @@ namespace FireflyIII\Validation; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionGroup; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -60,7 +59,7 @@ trait GroupValidation 'source_number', 'destination_number', ]; - /** @var array $transaction */ + /** @var array|null $transaction */ foreach ($transactions as $index => $transaction) { if (!is_array($transaction)) { throw new FireflyException('Invalid data submitted: transaction is not array.'); @@ -107,7 +106,7 @@ trait GroupValidation $count = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id') ->leftJoin('transaction_groups', 'transaction_groups.id', 'transaction_journals.transaction_group_id') ->where('transaction_journals.transaction_group_id', $transactionGroup->id) - ->where('transactions.reconciled', 1)->where('transactions.amount', '<', 0)->count(['transactions.id']); + ->where('transactions.reconciled', 1)->where('transactions.amount', '<', 0)->count('transactions.id'); if (0 === $count) { app('log')->debug(sprintf('Transaction is not reconciled, done with %s', __METHOD__)); return; @@ -142,7 +141,7 @@ trait GroupValidation if ($validator->errors()->count() > 0) { return; } - Log::debug('Now in GroupValidation::validateDescriptions()'); + app('log')->debug('Now in GroupValidation::validateDescriptions()'); $transactions = $this->getTransactionsArray($validator); $validDescriptions = 0; foreach ($transactions as $transaction) { @@ -168,7 +167,7 @@ trait GroupValidation if ($validator->errors()->count() > 0) { return; } - Log::debug('Now in validateGroupDescription()'); + app('log')->debug('Now in validateGroupDescription()'); $data = $validator->getData(); $transactions = $this->getTransactionsArray($validator); @@ -188,12 +187,12 @@ trait GroupValidation */ protected function validateJournalIds(Validator $validator, TransactionGroup $transactionGroup): void { - Log::debug(sprintf('Now in GroupValidation::validateJournalIds(%d)', $transactionGroup->id)); + app('log')->debug(sprintf('Now in GroupValidation::validateJournalIds(%d)', $transactionGroup->id)); $transactions = $this->getTransactionsArray($validator); if (count($transactions) < 2) { // no need for validation. - Log::debug(sprintf('%d transaction(s) in submission, can skip this check.', count($transactions))); + app('log')->debug(sprintf('%d transaction(s) in submission, can skip this check.', count($transactions))); return; } @@ -222,15 +221,15 @@ trait GroupValidation if (array_key_exists('transaction_journal_id', $transaction)) { $journalId = $transaction['transaction_journal_id']; } - Log::debug(sprintf('Now in validateJournalId(%d, %d)', $index, $journalId)); + app('log')->debug(sprintf('Now in validateJournalId(%d, %d)', $index, $journalId)); if (0 === $journalId || '' === $journalId || '0' === $journalId) { - Log::debug('Submitted 0, will accept to be used in a new transaction.'); + app('log')->debug('Submitted 0, will accept to be used in a new transaction.'); return; } $journalId = (int)$journalId; $count = $transactionGroup->transactionJournals()->where('transaction_journals.id', $journalId)->count(); - if (null === $journalId || 0 === $count) { + if (0 === $journalId || 0 === $count) { app('log')->warning(sprintf('Transaction group #%d has %d journals with ID %d', $transactionGroup->id, $count, $journalId)); app('log')->warning('Invalid submission: Each split must have transaction_journal_id (either valid ID or 0).'); $validator->errors()->add(sprintf('transactions.%d.source_name', $index), (string)trans('validation.need_id_in_edit')); diff --git a/app/Validation/RecurrenceValidation.php b/app/Validation/RecurrenceValidation.php index 6dabcbf60d..f318050e5a 100644 --- a/app/Validation/RecurrenceValidation.php +++ b/app/Validation/RecurrenceValidation.php @@ -26,7 +26,6 @@ namespace FireflyIII\Validation; use Carbon\Carbon; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceTransaction; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; use InvalidArgumentException; @@ -53,17 +52,17 @@ trait RecurrenceValidation // grab model from parameter and try to set the transaction type from it if ('invalid' === $transactionType) { - Log::debug('Type is invalid but we will search for it.'); - /** @var Recurrence $recurrence */ - $recurrence = $this->route()->parameter('recurrence'); + app('log')->debug('Type is invalid but we will search for it.'); + /** @var Recurrence|null $recurrence */ + $recurrence = $this->route()?->parameter('recurrence'); if (null !== $recurrence) { - Log::debug('There is a recurrence in the route.'); + app('log')->debug('There is a recurrence in the route.'); // ok so we have a recurrence should be able to extract type somehow. /** @var RecurrenceTransaction|null $first */ $first = $recurrence->recurrenceTransactions()->first(); if (null !== $first) { - $transactionType = $first->transactionType ? $first->transactionType->type : 'withdrawal'; - Log::debug(sprintf('Determined type to be %s.', $transactionType)); + $transactionType = null !== $first->transactionType ? $first->transactionType->type : 'withdrawal'; + app('log')->debug(sprintf('Determined type to be %s.', $transactionType)); } if (null === $first) { app('log')->warning('Just going to assume type is a withdrawal.'); @@ -77,7 +76,7 @@ trait RecurrenceValidation /** @var AccountValidator $accountValidator */ $accountValidator = app(AccountValidator::class); - Log::debug(sprintf('Going to loop %d transaction(s)', count($transactions))); + app('log')->debug(sprintf('Going to loop %d transaction(s)', count($transactions))); foreach ($transactions as $index => $transaction) { $transactionType = $transaction['type'] ?? $transactionType; $accountValidator->setTransactionType($transactionType); @@ -213,7 +212,6 @@ trait RecurrenceValidation if (null === $repetition['moment']) { $repetition['moment'] = ''; } - $repetition['moment'] = $repetition['moment'] ?? 'invalid'; switch ($repetition['type'] ?? 'empty') { default: @@ -320,8 +318,8 @@ trait RecurrenceValidation { try { Carbon::createFromFormat('Y-m-d', $moment); - } catch (InvalidArgumentException $e) { - Log::debug(sprintf('Invalid argument for Carbon: %s', $e->getMessage())); + } catch (InvalidArgumentException $e) { // @phpstan-ignore-line + app('log')->debug(sprintf('Invalid argument for Carbon: %s', $e->getMessage())); $validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string)trans('validation.valid_recurrence_rep_moment')); } } @@ -334,18 +332,12 @@ trait RecurrenceValidation */ protected function validateTransactionId(Recurrence $recurrence, Validator $validator): void { - Log::debug('Now in validateTransactionId'); + app('log')->debug('Now in validateTransactionId'); $transactions = $this->getTransactionData(); $submittedTrCount = count($transactions); - //$recurrence = $validator->get - if (null === $transactions) { - Log::warning('[a] User submitted no transactions.'); - $validator->errors()->add('transactions', (string)trans('validation.at_least_one_transaction')); - return; - } if (0 === $submittedTrCount) { - Log::warning('[b] User submitted no transactions.'); + app('log')->warning('[b] User submitted no transactions.'); $validator->errors()->add('transactions', (string)trans('validation.at_least_one_transaction')); return; } @@ -353,28 +345,28 @@ trait RecurrenceValidation if (1 === $submittedTrCount && 1 === $originalTrCount) { $first = $transactions[0]; // can safely assume index 0. if (!array_key_exists('id', $first)) { - Log::debug('Single count and no ID, done.'); + app('log')->debug('Single count and no ID, done.'); return; // home safe! } $id = $first['id']; if ('' === (string)$id) { - Log::debug('Single count and empty ID, done.'); + app('log')->debug('Single count and empty ID, done.'); return; // home safe! } $integer = (int)$id; $secondCount = $recurrence->recurrenceTransactions()->where('recurrences_transactions.id', $integer)->count(); - Log::debug(sprintf('Result of ID count: %d', $secondCount)); + app('log')->debug(sprintf('Result of ID count: %d', $secondCount)); if (0 === $secondCount) { $validator->errors()->add('transactions.0.id', (string)trans('validation.id_does_not_match', ['id' => $integer])); } - Log::debug('Single ID validation done.'); + app('log')->debug('Single ID validation done.'); return; } - Log::debug('Multi ID validation.'); + app('log')->debug('Multi ID validation.'); $idsMandatory = false; if ($submittedTrCount < $originalTrCount) { - Log::debug(sprintf('User submits %d transaction, recurrence has %d transactions. All entries must have ID.', $submittedTrCount, $originalTrCount)); + app('log')->debug(sprintf('User submits %d transaction, recurrence has %d transactions. All entries must have ID.', $submittedTrCount, $originalTrCount)); $idsMandatory = true; } /** @@ -391,38 +383,38 @@ trait RecurrenceValidation $unmatchedIds = 0; foreach ($transactions as $index => $transaction) { - Log::debug(sprintf('Now at %d/%d', $index + 1, $submittedTrCount)); + app('log')->debug(sprintf('Now at %d/%d', $index + 1, $submittedTrCount)); if (!is_array($transaction)) { - Log::warning('Not an array. Give error.'); + app('log')->warning('Not an array. Give error.'); $validator->errors()->add(sprintf('transactions.%d.id', $index), (string)trans('validation.at_least_one_transaction')); return; } if (!array_key_exists('id', $transaction) && $idsMandatory) { - Log::warning('ID is mandatory but array has no ID.'); + app('log')->warning('ID is mandatory but array has no ID.'); $validator->errors()->add(sprintf('transactions.%d.id', $index), (string)trans('validation.need_id_to_match')); return; } if (array_key_exists('id', $transaction)) { // don't matter if $idsMandatory - Log::debug('Array has ID.'); + app('log')->debug('Array has ID.'); $idCount = $recurrence->recurrenceTransactions()->where('recurrences_transactions.id', (int)$transaction['id'])->count(); if (0 === $idCount) { - Log::debug('ID does not exist or no match. Count another unmatched ID.'); + app('log')->debug('ID does not exist or no match. Count another unmatched ID.'); $unmatchedIds++; } } if (!array_key_exists('id', $transaction) && !$idsMandatory) { - Log::debug('Array has no ID but was not mandatory at this point.'); + app('log')->debug('Array has no ID but was not mandatory at this point.'); $unmatchedIds++; } } // if too many don't match, but you haven't submitted more than already present: $maxUnmatched = max(1, $submittedTrCount - $originalTrCount); - Log::debug(sprintf('Submitted: %d. Original: %d. User can submit %d unmatched transactions.', $submittedTrCount, $originalTrCount, $maxUnmatched)); + app('log')->debug(sprintf('Submitted: %d. Original: %d. User can submit %d unmatched transactions.', $submittedTrCount, $originalTrCount, $maxUnmatched)); if ($unmatchedIds > $maxUnmatched) { - Log::warning(sprintf('Too many unmatched transactions (%d).', $unmatchedIds)); + app('log')->warning(sprintf('Too many unmatched transactions (%d).', $unmatchedIds)); $validator->errors()->add('transactions.0.id', (string)trans('validation.too_many_unmatched')); return; } - Log::debug('Done with ID validation.'); + app('log')->debug('Done with ID validation.'); } } diff --git a/app/Validation/TransactionValidation.php b/app/Validation/TransactionValidation.php index aa7283de10..871b33816f 100644 --- a/app/Validation/TransactionValidation.php +++ b/app/Validation/TransactionValidation.php @@ -33,7 +33,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\User; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Validator; /** @@ -55,15 +54,15 @@ trait TransactionValidation if ($validator->errors()->count() > 0) { return; } - Log::debug('Now in validateAccountInformation (TransactionValidation) ()'); + app('log')->debug('Now in validateAccountInformation (TransactionValidation) ()'); $transactions = $this->getTransactionsArray($validator); $data = $validator->getData(); $transactionType = $data['type'] ?? 'invalid'; - Log::debug(sprintf('Going to loop %d transaction(s)', count($transactions))); + app('log')->debug(sprintf('Going to loop %d transaction(s)', count($transactions))); /** - * @var int $index - * @var array $transaction + * @var int|null $index + * @var array $transaction */ foreach ($transactions as $index => $transaction) { $transaction['user'] = $user; @@ -82,22 +81,16 @@ trait TransactionValidation */ protected function getTransactionsArray(Validator $validator): array { - Log::debug('Now in getTransactionsArray'); + app('log')->debug('Now in getTransactionsArray'); $data = $validator->getData(); $transactions = []; - if (is_array($data) && array_key_exists('transactions', $data) && is_array($data['transactions'])) { - Log::debug('Transactions key exists and is array.'); + if (array_key_exists('transactions', $data) && is_array($data['transactions'])) { + app('log')->debug('Transactions key exists and is array.'); $transactions = $data['transactions']; } - if (is_array($data) && array_key_exists('transactions', $data) && !is_array($data['transactions'])) { - Log::debug(sprintf('Transactions key exists but is NOT array, its a %s', gettype($data['transactions']))); + if (array_key_exists('transactions', $data) && !is_array($data['transactions'])) { + app('log')->debug(sprintf('Transactions key exists but is NOT array, its a %s', gettype($data['transactions']))); } - // should be impossible to hit this: - if (!is_countable($transactions)) { - Log::error(sprintf('Transactions array is not countable, because its a %s', gettype($transactions))); - return []; - } - //Log::debug('Returning transactions.', $transactions); return $transactions; } @@ -176,14 +169,15 @@ trait TransactionValidation * @param array $destination * * @return void + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ protected function sanityCheckReconciliation(Validator $validator, string $transactionType, int $index, array $source, array $destination): void { - Log::debug('Now in sanityCheckReconciliation'); + app('log')->debug('Now in sanityCheckReconciliation'); if (TransactionType::RECONCILIATION === ucfirst($transactionType) && null === $source['id'] && null === $source['name'] && null === $destination['id'] && null === $destination['name'] ) { - Log::debug('Both are NULL, error!'); + app('log')->debug('Both are NULL, error!'); $validator->errors()->add(sprintf('transactions.%d.source_id', $index), trans('validation.reconciliation_either_account')); $validator->errors()->add(sprintf('transactions.%d.source_name', $index), trans('validation.reconciliation_either_account')); $validator->errors()->add(sprintf('transactions.%d.destination_id', $index), trans('validation.reconciliation_either_account')); @@ -193,7 +187,7 @@ trait TransactionValidation if (TransactionType::RECONCILIATION === $transactionType && (null !== $source['id'] || null !== $source['name']) && (null !== $destination['id'] || null !== $destination['name'])) { - Log::debug('Both are not NULL, error!'); + app('log')->debug('Both are not NULL, error!'); $validator->errors()->add(sprintf('transactions.%d.source_id', $index), trans('validation.reconciliation_either_account')); $validator->errors()->add(sprintf('transactions.%d.source_name', $index), trans('validation.reconciliation_either_account')); $validator->errors()->add(sprintf('transactions.%d.destination_id', $index), trans('validation.reconciliation_either_account')); @@ -211,6 +205,7 @@ trait TransactionValidation * @param int $index * * @return void + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ private function sanityCheckForeignCurrency( Validator $validator, @@ -218,28 +213,29 @@ trait TransactionValidation array $transaction, string $transactionType, int $index - ): void { - Log::debug('Now in sanityCheckForeignCurrency()'); + ): void + { + app('log')->debug('Now in sanityCheckForeignCurrency()'); if (0 !== $validator->errors()->count()) { - Log::debug('Already have errors, return'); + app('log')->debug('Already have errors, return'); return; } if (null === $accountValidator->source) { - Log::debug('No source, return'); + app('log')->debug('No source, return'); return; } if (null === $accountValidator->destination) { - Log::debug('No destination, return'); + app('log')->debug('No destination, return'); return; } $source = $accountValidator->source; $destination = $accountValidator->destination; - Log::debug(sprintf('Source: #%d "%s (%s)"', $source->id, $source->name, $source->accountType->type)); - Log::debug(sprintf('Destination: #%d "%s" (%s)', $destination->id, $destination->name, $source->accountType->type)); + app('log')->debug(sprintf('Source: #%d "%s (%s)"', $source->id, $source->name, $source->accountType->type)); + app('log')->debug(sprintf('Destination: #%d "%s" (%s)', $destination->id, $destination->name, $source->accountType->type)); if (!$this->isLiabilityOrAsset($source) || !$this->isLiabilityOrAsset($destination)) { - Log::debug('Any account must be liability or asset account to continue.'); + app('log')->debug('Any account must be liability or asset account to continue.'); return; } @@ -251,16 +247,16 @@ trait TransactionValidation $destinationCurrency = $accountRepository->getAccountCurrency($destination) ?? $defaultCurrency; // if both accounts have the same currency, continue. if ($sourceCurrency->code === $destinationCurrency->code) { - Log::debug('Both accounts have the same currency, continue.'); + app('log')->debug('Both accounts have the same currency, continue.'); return; } - Log::debug(sprintf('Source account expects %s', $sourceCurrency->code)); - Log::debug(sprintf('Destination account expects %s', $destinationCurrency->code)); + app('log')->debug(sprintf('Source account expects %s', $sourceCurrency->code)); + app('log')->debug(sprintf('Destination account expects %s', $destinationCurrency->code)); - Log::debug(sprintf('Amount is %s', $transaction['amount'])); + app('log')->debug(sprintf('Amount is %s', $transaction['amount'])); if (TransactionType::DEPOSIT === ucfirst($transactionType)) { - Log::debug(sprintf('Processing as a "%s"', $transactionType)); + app('log')->debug(sprintf('Processing as a "%s"', $transactionType)); // use case: deposit from liability account to an asset account // the foreign amount must be in the currency of the source // the amount must be in the currency of the destination @@ -274,14 +270,14 @@ trait TransactionValidation // wrong currency information is present $foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false; $foreignCurrencyId = (int)($transaction['foreign_currency_id'] ?? 0); - Log::debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction); - if ($foreignCurrencyCode !== $sourceCurrency->code && $foreignCurrencyId !== (int)$sourceCurrency->id) { + app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction); + if ($foreignCurrencyCode !== $sourceCurrency->code && $foreignCurrencyId !== $sourceCurrency->id) { $validator->errors()->add(sprintf('transactions.%d.foreign_currency_code', $index), (string)trans('validation.require_foreign_src')); return; } } if (TransactionType::TRANSFER === ucfirst($transactionType) || TransactionType::WITHDRAWAL === ucfirst($transactionType)) { - Log::debug(sprintf('Processing as a "%s"', $transactionType)); + app('log')->debug(sprintf('Processing as a "%s"', $transactionType)); // use case: withdrawal from asset account to a liability account. // the foreign amount must be in the currency of the destination // the amount must be in the currency of the source @@ -299,10 +295,10 @@ trait TransactionValidation // wrong currency information is present $foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false; $foreignCurrencyId = (int)($transaction['foreign_currency_id'] ?? 0); - Log::debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction); - if ($foreignCurrencyCode !== $destinationCurrency->code && $foreignCurrencyId !== (int)$destinationCurrency->id) { - Log::debug(sprintf('No match on code, "%s" vs "%s"', $foreignCurrencyCode, $destinationCurrency->code)); - Log::debug(sprintf('No match on ID, #%d vs #%d', $foreignCurrencyId, $destinationCurrency->id)); + app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction); + if ($foreignCurrencyCode !== $destinationCurrency->code && $foreignCurrencyId !== $destinationCurrency->id) { + app('log')->debug(sprintf('No match on code, "%s" vs "%s"', $foreignCurrencyCode, $destinationCurrency->code)); + app('log')->debug(sprintf('No match on ID, #%d vs #%d', $foreignCurrencyId, $destinationCurrency->id)); $validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_dest')); } } @@ -325,7 +321,7 @@ trait TransactionValidation */ private function isLiability(Account $account): bool { - $type = $account->accountType?->type; + $type = $account->accountType->type; if (in_array($type, config('firefly.valid_liabilities'), true)) { return true; } @@ -339,7 +335,7 @@ trait TransactionValidation */ private function isAsset(Account $account): bool { - $type = $account->accountType?->type; + $type = $account->accountType->type; return $type === AccountType::ASSET; } @@ -375,17 +371,17 @@ trait TransactionValidation */ public function validateAccountInformationUpdate(Validator $validator, TransactionGroup $transactionGroup): void { - Log::debug('Now in validateAccountInformationUpdate()'); + app('log')->debug('Now in validateAccountInformationUpdate()'); if ($validator->errors()->count() > 0) { - Log::debug('Validator already has errors, so return.'); + app('log')->debug('Validator already has errors, so return.'); return; } $transactions = $this->getTransactionsArray($validator); /** - * @var int $index - * @var array $transaction + * @var int|null $index + * @var array $transaction */ foreach ($transactions as $index => $transaction) { if (!is_int($index)) { @@ -403,7 +399,7 @@ trait TransactionValidation */ protected function validateSingleUpdate(Validator $validator, int $index, array $transaction, TransactionGroup $transactionGroup): void { - Log::debug('Now validating single account update in validateSingleUpdate()'); + app('log')->debug('Now validating single account update in validateSingleUpdate()'); // if no account types are given, just skip the check. if ( @@ -411,7 +407,7 @@ trait TransactionValidation && !array_key_exists('source_name', $transaction) && !array_key_exists('destination_id', $transaction) && !array_key_exists('destination_name', $transaction)) { - Log::debug('No account data has been submitted so will not validating account info.'); + app('log')->debug('No account data has been submitted so will not validating account info.'); return; } @@ -429,7 +425,7 @@ trait TransactionValidation array_key_exists('source_iban', $transaction) || array_key_exists('source_number', $transaction) ) { - Log::debug('Will try to validate source account information.'); + app('log')->debug('Will try to validate source account information.'); $sourceId = (int)($transaction['source_id'] ?? 0); $sourceName = $transaction['source_name'] ?? null; $sourceIban = $transaction['source_iban'] ?? null; @@ -448,7 +444,7 @@ trait TransactionValidation return; } - Log::debug('Source account info is valid.'); + app('log')->debug('Source account info is valid.'); } if ( @@ -458,15 +454,15 @@ trait TransactionValidation array_key_exists('destination_number', $transaction) ) { - Log::debug('Will try to validate destination account information.'); + app('log')->debug('Will try to validate destination account information.'); // at this point the validator may not have a source account, because it was never submitted for validation. // must add it ourselves or the validator can never check if the destination is correct. // the $transaction array must have a journal id or it's just one, this was validated before. if (null === $accountValidator->source) { - Log::debug('Account validator has no source account, must find it.'); + app('log')->debug('Account validator has no source account, must find it.'); $source = $this->getOriginalSource($transaction, $transactionGroup); if (null !== $source) { - Log::debug('Found a source!'); + app('log')->debug('Found a source!'); $accountValidator->source = $source; } } @@ -482,9 +478,9 @@ trait TransactionValidation $validator->errors()->add(sprintf('transactions.%d.destination_id', $index), $accountValidator->destError); $validator->errors()->add(sprintf('transactions.%d.destination_name', $index), $accountValidator->destError); } - Log::debug('Destination account info is valid.'); + app('log')->debug('Destination account info is valid.'); } - Log::debug('Done with validateSingleUpdate().'); + app('log')->debug('Done with validateSingleUpdate().'); } /** @@ -495,7 +491,7 @@ trait TransactionValidation */ private function getTransactionType(TransactionGroup $group, array $transactions): string { - return $transactions[0]['type'] ?? strtolower($group->transactionJournals()->first()->transactionType->type); + return $transactions[0]['type'] ?? strtolower((string)$group->transactionJournals()->first()?->transactionType->type); } /** @@ -509,13 +505,13 @@ trait TransactionValidation if (1 === $transactionGroup->transactionJournals->count()) { $journal = $transactionGroup->transactionJournals->first(); - return $journal->transactions()->where('amount', '<', 0)->first()->account; + return $journal?->transactions()->where('amount', '<', 0)->first()?->account; } /** @var TransactionJournal $journal */ foreach ($transactionGroup->transactionJournals as $journal) { $journalId = (int)($transaction['transaction_journal_id'] ?? 0); - if ((int)$journal->id === $journalId) { - return $journal->transactions()->where('amount', '<', 0)->first()->account; + if ($journal->id === $journalId) { + return $journal->transactions()->where('amount', '<', 0)->first()?->account; } } @@ -529,7 +525,7 @@ trait TransactionValidation */ public function validateOneRecurrenceTransaction(Validator $validator): void { - Log::debug('Now in validateOneRecurrenceTransaction()'); + app('log')->debug('Now in validateOneRecurrenceTransaction()'); $transactions = $this->getTransactionsArray($validator); // need at least one transaction @@ -545,20 +541,20 @@ trait TransactionValidation */ public function validateOneTransaction(Validator $validator): void { - Log::debug('Now in validateOneTransaction'); + app('log')->debug('Now in validateOneTransaction'); if ($validator->errors()->count() > 0) { - Log::debug('Validator already has errors, so return.'); + app('log')->debug('Validator already has errors, so return.'); return; } $transactions = $this->getTransactionsArray($validator); // need at least one transaction if (0 === count($transactions)) { $validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction')); - Log::debug('Added error: at_least_one_transaction.'); + app('log')->debug('Added error: at_least_one_transaction.'); return; } - Log::debug('Added NO errors.'); + app('log')->debug('Added NO errors.'); } /** @@ -570,10 +566,10 @@ trait TransactionValidation return; } $transactions = $this->getTransactionsArray($validator); - foreach ($transactions as $key => $value) { + foreach (array_keys($transactions) as $key) { if (!is_int($key)) { $validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction')); - Log::debug('Added error: at_least_one_transaction.'); + app('log')->debug('Added error: at_least_one_transaction.'); return; } @@ -590,7 +586,7 @@ trait TransactionValidation if ($validator->errors()->count() > 0) { return; } - Log::debug('Now in validateTransactionTypes()'); + app('log')->debug('Now in validateTransactionTypes()'); $transactions = $this->getTransactionsArray($validator); $types = []; @@ -616,7 +612,7 @@ trait TransactionValidation */ public function validateTransactionTypesForUpdate(Validator $validator): void { - Log::debug('Now in validateTransactionTypesForUpdate()'); + app('log')->debug('Now in validateTransactionTypesForUpdate()'); $transactions = $this->getTransactionsArray($validator); $types = []; foreach ($transactions as $transaction) { @@ -631,7 +627,7 @@ trait TransactionValidation return; } - Log::debug('No errors in validateTransactionTypesForUpdate()'); + app('log')->debug('No errors in validateTransactionTypesForUpdate()'); } /** @@ -661,7 +657,7 @@ trait TransactionValidation if ($validator->errors()->count() > 0) { return; } - Log::debug('Now in validateEqualAccounts()'); + app('log')->debug('Now in validateEqualAccounts()'); $transactions = $this->getTransactionsArray($validator); // needs to be split @@ -705,15 +701,15 @@ trait TransactionValidation private function validateEqualAccountsForUpdate(Validator $validator, TransactionGroup $transactionGroup): void { if ($validator->errors()->count() > 0) { - Log::debug('Validator already has errors, so return.'); + app('log')->debug('Validator already has errors, so return.'); return; } - Log::debug('Now in validateEqualAccountsForUpdate()'); + app('log')->debug('Now in validateEqualAccountsForUpdate()'); $transactions = $this->getTransactionsArray($validator); if (2 !== count($transactions)) { - Log::debug('Less than 2 transactions, do nothing.'); + app('log')->debug('Less than 2 transactions, do nothing.'); return; } @@ -741,7 +737,7 @@ trait TransactionValidation return; } - Log::debug('No errors found in validateEqualAccountsForUpdate'); + app('log')->debug('No errors found in validateEqualAccountsForUpdate'); } /** @@ -784,15 +780,15 @@ trait TransactionValidation if (0 === $journalId) { return $return; } - /** @var Transaction $source */ + /** @var Transaction|null $source */ $source = Transaction::where('transaction_journal_id', $journalId)->where('amount', '<', 0)->with(['account'])->first(); if (null !== $source) { $return['source_id'] = $source->account_id; $return['source_name'] = $source->account->name; } - /** @var Transaction $destination */ + /** @var Transaction|null $destination */ $destination = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->with(['account'])->first(); - if (null !== $source) { + if (null !== $destination) { $return['destination_id'] = $destination->account_id; $return['destination_name'] = $destination->account->name; } diff --git a/bootstrap/app.php b/bootstrap/app.php index c6dadd7ee1..f17a7fe025 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -37,14 +37,14 @@ bcscale(12); if (!function_exists('envNonEmpty')) { /** * @param string $key - * @param null $default + * @param string|int|bool|null $default * * @return mixed|null */ - function envNonEmpty(string $key, $default = null) + function envNonEmpty(string $key, string|int|bool|null $default = null) { $result = env($key, $default); - if (is_string($result) && '' === $result) { + if ('' === $result) { $result = $default; } @@ -66,7 +66,7 @@ if (!function_exists('stringIsEqual')) { } $app = new Illuminate\Foundation\Application( - realpath(__DIR__ . '/../') + (string)realpath(__DIR__ . '/../') ); /* diff --git a/changelog.md b/changelog.md index f93a900746..ca30f51ace 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,40 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 6.1.0 - 2023-12-xx + +> This release required **PHP 8.3.0** and will not work on earlier releases of PHP + +### Added + +- [Issue 7571](https://github.com/firefly-iii/firefly-iii/issues/7571) More tag search options +- [Issue 7781](https://github.com/firefly-iii/firefly-iii/issues/7781) Nice wrapper script for artisan commands +- UI also supports time for transactions + +### Changed + +- Requires PHP8.3 +- [Issue 8148](https://github.com/firefly-iii/firefly-iii/issues/8148) Slovenian language updates +- [Issue 8023](https://github.com/firefly-iii/firefly-iii/issues/8023) Top bar is now fixed in place + +### Deprecated + +- Support for PHP 8.2 + +### Fixed + +- [Issue 8106](https://github.com/firefly-iii/firefly-iii/issues/8106) [issue 8195](https://github.com/firefly-iii/firefly-iii/issues/8195) [issue 8163](https://github.com/firefly-iii/firefly-iii/issues/8163) Various changes and fixes to bill date calculation +- [Issue 8137](https://github.com/firefly-iii/firefly-iii/issues/8137) Fix uneven amount error from cron job +- [Issue 8192](https://github.com/firefly-iii/firefly-iii/issues/8192) No matching transactions found.Rule with trigger NOT Transaction is reconciled returns +- [Issue 8207](https://github.com/firefly-iii/firefly-iii/issues/8207) Broken links, thanks @Maxco10! +- [Issue 8138](https://github.com/firefly-iii/firefly-iii/issues/8138) Reconciled transactions can't be "store(d) as new" +- [Issue 7716](https://github.com/firefly-iii/firefly-iii/issues/7716) Removed bar in budget overview + +### API + +- [Issue 8022](https://github.com/firefly-iii/firefly-iii/issues/8022) API chart expansions +- [Issue 8106](https://github.com/firefly-iii/firefly-iii/issues/8106) API reports empty string instead of NULL + ## 6.0.30 - 2023-10-29 ### Fixed @@ -508,10 +542,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). This is release -* - -*6.0.0 -** of Firefly III. +**6.0.0** of Firefly III. ### Warnings diff --git a/composer.json b/composer.json index 7819851728..cb56304420 100644 --- a/composer.json +++ b/composer.json @@ -1,193 +1,197 @@ { - "name": "grumpydictator/firefly-iii", - "description": "Firefly III: a personal finances manager.", - "keywords": [ - "finance", - "finances", - "manager", - "management", - "euro", - "dollar", - "laravel", - "money", - "currency", - "financials", - "financial", - "budgets", - "administration", - "tool", - "tooling", - "help", - "helper", - "assistant", - "planning", - "organizing", - "bills", - "personal finance", - "budgets", - "budgeting", - "budgeting tool", - "budgeting application", - "transactions", - "self hosted", - "self-hosted", - "transfers", - "management" - ], - "license": "AGPL-3.0-or-later", - "homepage": "https://github.com/firefly-iii/firefly-iii", - "type": "project", - "readme": "readme.md", - "authors": [ - { - "name": "James Cole", - "email": "james@firefly-iii.org", - "homepage": "https://github.com/firefly-iii", - "role": "Developer" - } - ], - "support": { - "email": "james@firefly-iii.org", - "issues": "https://github.com/firefly-iii/firefly-iii/issues", - "forum": "https://gitter.im/firefly-iii/firefly-iii", - "wiki": "https://github.com/firefly-iii/help/wiki", - "source": "https://github.com/firefly-iii/firefly-iii", - "docs": "https://docs.firefly-iii.org/" - }, - "funding": [ - { - "type": "patreon", - "url": "https://www.patreon.com/JC5" + "name": "grumpydictator/firefly-iii", + "description": "Firefly III: a personal finances manager.", + "keywords": [ + "finance", + "finances", + "manager", + "management", + "euro", + "dollar", + "laravel", + "money", + "currency", + "financials", + "financial", + "budgets", + "administration", + "tool", + "tooling", + "help", + "helper", + "assistant", + "planning", + "organizing", + "bills", + "personal finance", + "budgets", + "budgeting", + "budgeting tool", + "budgeting application", + "transactions", + "self hosted", + "self-hosted", + "transfers", + "management" + ], + "license": "AGPL-3.0-or-later", + "homepage": "https://github.com/firefly-iii/firefly-iii", + "type": "project", + "readme": "readme.md", + "authors": [ + { + "name": "James Cole", + "email": "james@firefly-iii.org", + "homepage": "https://github.com/firefly-iii", + "role": "Developer" + } + ], + "support": { + "email": "james@firefly-iii.org", + "issues": "https://github.com/firefly-iii/firefly-iii/issues", + "forum": "https://gitter.im/firefly-iii/firefly-iii", + "wiki": "https://github.com/firefly-iii/help/wiki", + "source": "https://github.com/firefly-iii/firefly-iii", + "docs": "https://docs.firefly-iii.org/" }, - { - "type": "github", - "url": "https://github.com/sponsors/JC5" - } - ], - "require": { - "php": ">=8.2", - "ext-bcmath": "*", - "ext-curl": "*", - "ext-fileinfo": "*", - "ext-iconv": "*", - "ext-intl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "ext-openssl": "*", - "ext-pdo": "*", - "ext-session": "*", - "ext-simplexml": "*", - "ext-sodium": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "bacon/bacon-qr-code": "2.*", - "diglactic/laravel-breadcrumbs": "^8.1", - "doctrine/dbal": "3.*", - "gdbots/query-parser": "^3.0", - "guzzlehttp/guzzle": "^7.8", - "jc5/google2fa-laravel": "^2.0", - "jc5/recovery": "^2", - "laravel/framework": "^10", - "laravel/passport": "11.*", - "laravel/sanctum": "^3.3", - "laravel/slack-notification-channel": "^3.0", - "laravel/ui": "^4.2", - "league/commonmark": "2.*", - "league/csv": "^9.10", - "league/fractal": "0.*", - "nunomaduro/collision": "^7.7", - "pragmarx/google2fa": "^8.0", - "predis/predis": "^2.2", - "psr/log": "<4", - "ramsey/uuid": "^4.7", - "rcrowe/twigbridge": "^0.14", - "spatie/laravel-html": "^3.2", - "spatie/laravel-ignition": "^2", - "spatie/period": "^2.4", - "symfony/http-client": "^6.3", - "symfony/mailgun-mailer": "^6.3", - "therobfonz/laravel-mandrill-driver": "^5.0" - }, - "require-dev": { - "barryvdh/laravel-ide-helper": "2.*", - "ergebnis/phpstan-rules": "^2.1", - "fakerphp/faker": "1.*", - "filp/whoops": "2.*", - "mockery/mockery": "1.*", - "nunomaduro/larastan": "^2.6", - "phpstan/phpstan": "^1.10", - "phpstan/phpstan-deprecation-rules": "^1.1", - "phpstan/phpstan-strict-rules": "^1.4", - "phpunit/phpunit": "^10", - "thecodingmachine/phpstan-strict-rules": "^1.0" - }, - "suggest": { - }, - "autoload": { - "psr-4": { - "FireflyIII\\": "app/", - "Domain\\": "domain/", - "Database\\Factories\\": "database/factories/", - "Database\\Seeders\\": "database/seeders/" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests/" - } - }, - "extra": { - "laravel": { - "dont-discover": [] - } - }, - "scripts": { - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/JC5" + }, + { + "type": "github", + "url": "https://github.com/sponsors/JC5" + } ], - "post-create-project-cmd": [ - "@php artisan key:generate" - ], - "post-autoload-dump": [ - "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump" - ], - "post-update-cmd": [ - "@php artisan config:clear", - "@php artisan route:clear", - "@php artisan twig:clean", - "@php artisan view:clear", - "@php artisan clear-compiled", - "@php artisan cache:clear", - "@php artisan firefly-iii:upgrade-database", - "@php artisan firefly-iii:correct-database", - "@php artisan firefly-iii:report-integrity", - "@php artisan passport:install", - "@php artisan firefly:instructions update" - ], - "post-install-cmd": [ - "@php artisan firefly:instructions install", - "@php artisan firefly-iii:verify-security-alerts" - ], - "unit-test": [ - "@php vendor/bin/phpunit -c phpunit.xml --testsuite unit --no-coverage" - ], - "integration-test": [ - "@php vendor/bin/phpunit -c phpunit.xml --testsuite integration --no-coverage" - ], - "coverage": [ - "@php vendor/bin/phpunit -c phpunit.xml" - ] - }, - "config": { - "platform": { - "php": "8.2" + "require": { + "php": ">=8.3", + "ext-bcmath": "*", + "ext-curl": "*", + "ext-fileinfo": "*", + "ext-iconv": "*", + "ext-intl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "ext-session": "*", + "ext-simplexml": "*", + "ext-sodium": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "bacon/bacon-qr-code": "2.*", + "diglactic/laravel-breadcrumbs": "^8.1", + "doctrine/dbal": "3.*", + "gdbots/query-parser": "^3.0", + "guzzlehttp/guzzle": "^7.8", + "jc5/google2fa-laravel": "^2.0", + "jc5/recovery": "^2", + "laravel/framework": "^10", + "laravel/passport": "11.*", + "laravel/sanctum": "^3.3", + "laravel/slack-notification-channel": "^3.0", + "laravel/ui": "^4.2", + "league/commonmark": "2.*", + "league/csv": "^9.10", + "league/fractal": "0.*", + "nunomaduro/collision": "^7.7", + "pragmarx/google2fa": "^8.0", + "predis/predis": "^2.2", + "psr/log": "<4", + "ramsey/uuid": "^4.7", + "rcrowe/twigbridge": "^0.14", + "spatie/laravel-html": "^3.2", + "spatie/laravel-ignition": "^2", + "spatie/period": "^2.4", + "symfony/http-client": "^7.0", + "symfony/mailgun-mailer": "^7.0", + "therobfonz/laravel-mandrill-driver": "^5.0" }, - "preferred-install": "dist", - "sort-packages": true, - "optimize-autoloader": true, - "allow-plugins": { - "composer/package-versions-deprecated": true + "require-dev": { + "barryvdh/laravel-ide-helper": "2.*", + "ergebnis/phpstan-rules": "^2.1", + "fakerphp/faker": "1.*", + "filp/whoops": "2.*", + "mockery/mockery": "1.*", + "larastan/larastan": "^2", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^10", + "thecodingmachine/phpstan-strict-rules": "^1.0" + }, + "suggest": { + }, + "autoload": { + "psr-4": { + "FireflyIII\\": "app/", + "Domain\\": "domain/", + "Database\\Factories\\": "database/factories/", + "Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "dont-discover": [] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "@php artisan key:generate" + ], + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump" + ], + "post-update-cmd": [ + "@php artisan config:clear", + "@php artisan route:clear", + "@php artisan twig:clean", + "@php artisan view:clear", + "@php artisan clear-compiled", + "@php artisan cache:clear", + "@php artisan firefly-iii:upgrade-database", + "@php artisan firefly-iii:correct-database", + "@php artisan firefly-iii:report-integrity", + "@php artisan passport:install", + "@php artisan firefly:instructions update" + ], + "post-install-cmd": [ + "@php artisan firefly:instructions install", + "@php artisan firefly-iii:verify-security-alerts" + ], + "unit-test": [ + "@php vendor/bin/phpunit -c phpunit.xml --testsuite unit --no-coverage" + ], + "integration-test": [ + "@php vendor/bin/phpunit -c phpunit.xml --testsuite integration --no-coverage" + ], + "coverage": [ + "@php vendor/bin/phpunit -c phpunit.xml" + ] + }, + "config": { + "preferred-install": "dist", + "sort-packages": true, + "optimize-autoloader": true, + "allow-plugins": { + "composer/package-versions-deprecated": true, + "phpstan/extension-installer": true + } } - } } diff --git a/composer.lock b/composer.lock index b026c9c95e..234f3934bc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "639b971ea13ea3e6ed2f57f862a195b8", + "content-hash": "84061ba3494e12bf5871a59c5a4744d4", "packages": [ { "name": "bacon/bacon-qr-code", @@ -115,6 +115,75 @@ ], "time": "2023-01-15T23:15:59+00:00" }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "67a77972b9f398ae7068dabacc39c08aeee170d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/67a77972b9f398ae7068dabacc39c08aeee170d5", + "reference": "67a77972b9f398ae7068dabacc39c08aeee170d5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.7.0 || >=4.0.0" + }, + "require-dev": { + "doctrine/dbal": "^3.7.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2023-10-01T14:29:01+00:00" + }, { "name": "dasprid/enum", "version": "1.0.5", @@ -473,16 +542,16 @@ }, { "name": "doctrine/dbal", - "version": "3.7.1", + "version": "3.7.2", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "5b7bd66c9ff58c04c5474ab85edce442f8081cb2" + "reference": "0ac3c270590e54910715e9a1a044cc368df282b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/5b7bd66c9ff58c04c5474ab85edce442f8081cb2", - "reference": "5b7bd66c9ff58c04c5474ab85edce442f8081cb2", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/0ac3c270590e54910715e9a1a044cc368df282b2", + "reference": "0ac3c270590e54910715e9a1a044cc368df282b2", "shasum": "" }, "require": { @@ -498,7 +567,7 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.35", + "phpstan/phpstan": "1.10.42", "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpunit": "9.6.13", "psalm/plugin-phpunit": "0.18.4", @@ -566,7 +635,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.7.1" + "source": "https://github.com/doctrine/dbal/tree/3.7.2" }, "funding": [ { @@ -582,7 +651,7 @@ "type": "tidelift" } ], - "time": "2023-10-06T05:06:20+00:00" + "time": "2023-11-19T08:06:58+00:00" }, { "name": "doctrine/deprecations", @@ -1073,16 +1142,16 @@ }, { "name": "filp/whoops", - "version": "2.15.3", + "version": "2.15.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "c83e88a30524f9360b11f585f71e6b17313b7187" + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187", - "reference": "c83e88a30524f9360b11f585f71e6b17313b7187", + "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", + "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", "shasum": "" }, "require": { @@ -1132,7 +1201,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.3" + "source": "https://github.com/filp/whoops/tree/2.15.4" }, "funding": [ { @@ -1140,20 +1209,20 @@ "type": "github" } ], - "time": "2023-07-13T12:00:00+00:00" + "time": "2023-11-03T12:00:00+00:00" }, { "name": "firebase/php-jwt", - "version": "v6.9.0", + "version": "v6.10.0", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "f03270e63eaccf3019ef0f32849c497385774e11" + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/f03270e63eaccf3019ef0f32849c497385774e11", - "reference": "f03270e63eaccf3019ef0f32849c497385774e11", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/a49db6f0a5033aef5143295342f1c95521b075ff", + "reference": "a49db6f0a5033aef5143295342f1c95521b075ff", "shasum": "" }, "require": { @@ -1201,9 +1270,9 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.9.0" + "source": "https://github.com/firebase/php-jwt/tree/v6.10.0" }, - "time": "2023-10-05T00:24:42+00:00" + "time": "2023-12-01T16:26:39+00:00" }, { "name": "fruitcake/php-cors", @@ -1317,24 +1386,24 @@ }, { "name": "graham-campbell/result-type", - "version": "v1.1.1", + "version": "v1.1.2", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831" + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", - "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.1" + "phpoption/phpoption": "^1.9.2" }, "require-dev": { - "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "type": "library", "autoload": { @@ -1363,7 +1432,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.1" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" }, "funding": [ { @@ -1375,20 +1444,20 @@ "type": "tidelift" } ], - "time": "2023-02-25T20:23:15+00:00" + "time": "2023-11-12T22:16:48+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.8.0", + "version": "7.8.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", - "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", + "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", "shasum": "" }, "require": { @@ -1403,11 +1472,11 @@ "psr/http-client-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", + "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1485,7 +1554,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.0" + "source": "https://github.com/guzzle/guzzle/tree/7.8.1" }, "funding": [ { @@ -1501,28 +1570,28 @@ "type": "tidelift" } ], - "time": "2023-08-27T10:20:53+00:00" + "time": "2023-12-03T20:35:24+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", - "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", + "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", + "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15" }, "type": "library", "extra": { @@ -1568,7 +1637,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.1" + "source": "https://github.com/guzzle/promises/tree/2.0.2" }, "funding": [ { @@ -1584,20 +1653,20 @@ "type": "tidelift" } ], - "time": "2023-08-03T15:11:55+00:00" + "time": "2023-12-03T20:19:20+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.1", + "version": "2.6.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", - "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", + "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", "shasum": "" }, "require": { @@ -1611,9 +1680,9 @@ "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", + "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" + "phpunit/phpunit": "^8.5.36 || ^9.6.15" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1684,7 +1753,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.1" + "source": "https://github.com/guzzle/psr7/tree/2.6.2" }, "funding": [ { @@ -1700,32 +1769,38 @@ "type": "tidelift" } ], - "time": "2023-08-27T10:13:57+00:00" + "time": "2023-12-03T20:05:35+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.2", + "version": "v1.0.3", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d" + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/61bf437fc2197f587f6857d3ff903a24f1731b5d", - "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "symfony/polyfill-php80": "^1.17" + "symfony/polyfill-php80": "^1.24" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.19 || ^9.5.8", + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", "uri-template/tests": "1.0.0" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, "autoload": { "psr-4": { "GuzzleHttp\\UriTemplate\\": "src" @@ -1764,7 +1839,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.2" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" }, "funding": [ { @@ -1780,7 +1855,7 @@ "type": "tidelift" } ], - "time": "2023-08-27T10:19:19+00:00" + "time": "2023-12-03T19:50:20+00:00" }, { "name": "jc5/google2fa-laravel", @@ -1939,16 +2014,16 @@ }, { "name": "laravel/framework", - "version": "v10.29.0", + "version": "v10.35.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "2d002849a16ad131110a50cbea4d64dbb78515a3" + "reference": "91ec2d92d2f6007e9084fe06438b99c91845da69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/2d002849a16ad131110a50cbea4d64dbb78515a3", - "reference": "2d002849a16ad131110a50cbea4d64dbb78515a3", + "url": "https://api.github.com/repos/laravel/framework/zipball/91ec2d92d2f6007e9084fe06438b99c91845da69", + "reference": "91ec2d92d2f6007e9084fe06438b99c91845da69", "shasum": "" }, "require": { @@ -1981,7 +2056,7 @@ "symfony/console": "^6.2", "symfony/error-handler": "^6.2", "symfony/finder": "^6.2", - "symfony/http-foundation": "^6.3", + "symfony/http-foundation": "^6.4", "symfony/http-kernel": "^6.2", "symfony/mailer": "^6.2", "symfony/mime": "^6.2", @@ -2049,7 +2124,7 @@ "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.5.1", "nyholm/psr7": "^1.2", - "orchestra/testbench-core": "^8.12", + "orchestra/testbench-core": "^8.15.1", "pda/pheanstalk": "^4.0", "phpstan/phpstan": "^1.4.7", "phpunit/phpunit": "^10.0.7", @@ -2137,20 +2212,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-10-24T13:48:53+00:00" + "time": "2023-12-05T14:50:33+00:00" }, { "name": "laravel/passport", - "version": "v11.9.2", + "version": "v11.10.0", "source": { "type": "git", "url": "https://github.com/laravel/passport.git", - "reference": "cece4efda933219002d95f3d5d03509e9ed29a14" + "reference": "966bc8e477d08c86a11dc4c5a86f85fa0abdb89b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/passport/zipball/cece4efda933219002d95f3d5d03509e9ed29a14", - "reference": "cece4efda933219002d95f3d5d03509e9ed29a14", + "url": "https://api.github.com/repos/laravel/passport/zipball/966bc8e477d08c86a11dc4c5a86f85fa0abdb89b", + "reference": "966bc8e477d08c86a11dc4c5a86f85fa0abdb89b", "shasum": "" }, "require": { @@ -2215,20 +2290,20 @@ "issues": "https://github.com/laravel/passport/issues", "source": "https://github.com/laravel/passport" }, - "time": "2023-10-16T20:04:42+00:00" + "time": "2023-11-02T17:16:12+00:00" }, { "name": "laravel/prompts", - "version": "v0.1.12", + "version": "v0.1.13", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "b35f249028c22016e45e48626e19e5d42fd827ff" + "reference": "e1379d8ead15edd6cc4369c22274345982edc95a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/b35f249028c22016e45e48626e19e5d42fd827ff", - "reference": "b35f249028c22016e45e48626e19e5d42fd827ff", + "url": "https://api.github.com/repos/laravel/prompts/zipball/e1379d8ead15edd6cc4369c22274345982edc95a", + "reference": "e1379d8ead15edd6cc4369c22274345982edc95a", "shasum": "" }, "require": { @@ -2270,22 +2345,22 @@ ], "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.1.12" + "source": "https://github.com/laravel/prompts/tree/v0.1.13" }, - "time": "2023-10-18T14:18:57+00:00" + "time": "2023-10-27T13:53:59+00:00" }, { "name": "laravel/sanctum", - "version": "v3.3.1", + "version": "v3.3.2", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "338f633e6487e76b255470d3373fbc29228aa971" + "reference": "e1a272893bec13cf135627f7e156030b3afe1e60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/338f633e6487e76b255470d3373fbc29228aa971", - "reference": "338f633e6487e76b255470d3373fbc29228aa971", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/e1a272893bec13cf135627f7e156030b3afe1e60", + "reference": "e1a272893bec13cf135627f7e156030b3afe1e60", "shasum": "" }, "require": { @@ -2338,20 +2413,20 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2023-09-07T15:46:33+00:00" + "time": "2023-11-03T13:42:14+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.2", + "version": "v1.3.3", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c" + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/076fe2cf128bd54b4341cdc6d49b95b34e101e4c", - "reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754", + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754", "shasum": "" }, "require": { @@ -2398,20 +2473,20 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2023-10-17T13:38:16+00:00" + "time": "2023-11-08T14:08:06+00:00" }, { "name": "laravel/slack-notification-channel", - "version": "v3.0.1", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/laravel/slack-notification-channel.git", - "reference": "dc5742f91f10a5ec21d32541ceb509f1e8e4c94f" + "reference": "c74265319b1d0ca710771d6c708558553972e1d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/dc5742f91f10a5ec21d32541ceb509f1e8e4c94f", - "reference": "dc5742f91f10a5ec21d32541ceb509f1e8e4c94f", + "url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/c74265319b1d0ca710771d6c708558553972e1d6", + "reference": "c74265319b1d0ca710771d6c708558553972e1d6", "shasum": "" }, "require": { @@ -2461,22 +2536,22 @@ ], "support": { "issues": "https://github.com/laravel/slack-notification-channel/issues", - "source": "https://github.com/laravel/slack-notification-channel/tree/v3.0.1" + "source": "https://github.com/laravel/slack-notification-channel/tree/v3.1.0" }, - "time": "2023-07-25T21:12:45+00:00" + "time": "2023-10-30T17:52:13+00:00" }, { "name": "laravel/ui", - "version": "v4.2.2", + "version": "v4.2.3", "source": { "type": "git", "url": "https://github.com/laravel/ui.git", - "reference": "a58ec468db4a340b33f3426c778784717a2c144b" + "reference": "eb532ea096ca1c0298c87c19233daf011fda743a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/ui/zipball/a58ec468db4a340b33f3426c778784717a2c144b", - "reference": "a58ec468db4a340b33f3426c778784717a2c144b", + "url": "https://api.github.com/repos/laravel/ui/zipball/eb532ea096ca1c0298c87c19233daf011fda743a", + "reference": "eb532ea096ca1c0298c87c19233daf011fda743a", "shasum": "" }, "require": { @@ -2523,40 +2598,40 @@ "ui" ], "support": { - "source": "https://github.com/laravel/ui/tree/v4.2.2" + "source": "https://github.com/laravel/ui/tree/v4.2.3" }, - "time": "2023-05-09T19:47:28+00:00" + "time": "2023-11-23T14:44:22+00:00" }, { "name": "lcobucci/clock", - "version": "3.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/lcobucci/clock.git", - "reference": "30a854ceb22bd87d83a7a4563b3f6312453945fc" + "reference": "6f28b826ea01306b07980cb8320ab30b966cd715" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/30a854ceb22bd87d83a7a4563b3f6312453945fc", - "reference": "30a854ceb22bd87d83a7a4563b3f6312453945fc", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/6f28b826ea01306b07980cb8320ab30b966cd715", + "reference": "6f28b826ea01306b07980cb8320ab30b966cd715", "shasum": "" }, "require": { - "php": "~8.2.0", + "php": "~8.2.0 || ~8.3.0", "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" }, "require-dev": { - "infection/infection": "^0.26", - "lcobucci/coding-standard": "^10.0.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.10.7", + "infection/infection": "^0.27", + "lcobucci/coding-standard": "^11.0.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.10.25", "phpstan/phpstan-deprecation-rules": "^1.1.3", - "phpstan/phpstan-phpunit": "^1.3.10", - "phpstan/phpstan-strict-rules": "^1.5.0", - "phpunit/phpunit": "^10.0.17" + "phpstan/phpstan-phpunit": "^1.3.13", + "phpstan/phpstan-strict-rules": "^1.5.1", + "phpunit/phpunit": "^10.2.3" }, "type": "library", "autoload": { @@ -2577,7 +2652,7 @@ "description": "Yet another clock abstraction", "support": { "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/3.1.0" + "source": "https://github.com/lcobucci/clock/tree/3.2.0" }, "funding": [ { @@ -2589,41 +2664,39 @@ "type": "patreon" } ], - "time": "2023-03-20T19:12:25+00:00" + "time": "2023-11-17T17:00:27+00:00" }, { "name": "lcobucci/jwt", - "version": "5.0.0", + "version": "5.2.0", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "47bdb0e0b5d00c2f89ebe33e7e384c77e84e7c34" + "reference": "0ba88aed12c04bd2ed9924f500673f32b67a6211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/47bdb0e0b5d00c2f89ebe33e7e384c77e84e7c34", - "reference": "47bdb0e0b5d00c2f89ebe33e7e384c77e84e7c34", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/0ba88aed12c04bd2ed9924f500673f32b67a6211", + "reference": "0ba88aed12c04bd2ed9924f500673f32b67a6211", "shasum": "" }, "require": { - "ext-hash": "*", - "ext-json": "*", "ext-openssl": "*", "ext-sodium": "*", - "php": "~8.1.0 || ~8.2.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "psr/clock": "^1.0" }, "require-dev": { - "infection/infection": "^0.26.19", + "infection/infection": "^0.27.0", "lcobucci/clock": "^3.0", - "lcobucci/coding-standard": "^9.0", - "phpbench/phpbench": "^1.2.8", + "lcobucci/coding-standard": "^11.0", + "phpbench/phpbench": "^1.2.9", "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.10.3", - "phpstan/phpstan-deprecation-rules": "^1.1.2", - "phpstan/phpstan-phpunit": "^1.3.8", + "phpstan/phpstan": "^1.10.7", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.10", "phpstan/phpstan-strict-rules": "^1.5.0", - "phpunit/phpunit": "^10.0.12" + "phpunit/phpunit": "^10.2.6" }, "suggest": { "lcobucci/clock": ">= 3.0" @@ -2652,7 +2725,7 @@ ], "support": { "issues": "https://github.com/lcobucci/jwt/issues", - "source": "https://github.com/lcobucci/jwt/tree/5.0.0" + "source": "https://github.com/lcobucci/jwt/tree/5.2.0" }, "funding": [ { @@ -2664,7 +2737,7 @@ "type": "patreon" } ], - "time": "2023-02-25T21:35:16+00:00" + "time": "2023-11-20T21:17:42+00:00" }, { "name": "league/commonmark", @@ -2856,35 +2929,36 @@ }, { "name": "league/csv", - "version": "9.11.0", + "version": "9.12.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "33149c4bea4949aa4fa3d03fb11ed28682168b39" + "reference": "c1dc31e23eb3cd0f7b537ee1a669d5f58d5cfe21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/33149c4bea4949aa4fa3d03fb11ed28682168b39", - "reference": "33149c4bea4949aa4fa3d03fb11ed28682168b39", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/c1dc31e23eb3cd0f7b537ee1a669d5f58d5cfe21", + "reference": "c1dc31e23eb3cd0f7b537ee1a669d5f58d5cfe21", "shasum": "" }, "require": { + "ext-filter": "*", "ext-json": "*", "ext-mbstring": "*", "php": "^8.1.2" }, "require-dev": { - "doctrine/collections": "^2.1.3", + "doctrine/collections": "^2.1.4", "ext-dom": "*", "ext-xdebug": "*", "friendsofphp/php-cs-fixer": "^v3.22.0", - "phpbench/phpbench": "^1.2.14", - "phpstan/phpstan": "^1.10.26", - "phpstan/phpstan-deprecation-rules": "^1.1.3", - "phpstan/phpstan-phpunit": "^1.3.13", - "phpstan/phpstan-strict-rules": "^1.5.1", - "phpunit/phpunit": "^10.3.1", - "symfony/var-dumper": "^6.3.3" + "phpbench/phpbench": "^1.2.15", + "phpstan/phpstan": "^1.10.46", + "phpstan/phpstan-deprecation-rules": "^1.1.4", + "phpstan/phpstan-phpunit": "^1.3.15", + "phpstan/phpstan-strict-rules": "^1.5.2", + "phpunit/phpunit": "^10.4.2", + "symfony/var-dumper": "^6.4.0" }, "suggest": { "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", @@ -2940,7 +3014,7 @@ "type": "github" } ], - "time": "2023-09-23T10:09:54+00:00" + "time": "2023-12-01T17:54:07+00:00" }, { "name": "league/event", @@ -2998,16 +3072,16 @@ }, { "name": "league/flysystem", - "version": "3.18.0", + "version": "3.23.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "015633a05aee22490495159237a5944091d8281e" + "reference": "d4ad81e2b67396e33dc9d7e54ec74ccf73151dcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/015633a05aee22490495159237a5944091d8281e", - "reference": "015633a05aee22490495159237a5944091d8281e", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/d4ad81e2b67396e33dc9d7e54ec74ccf73151dcc", + "reference": "d4ad81e2b67396e33dc9d7e54ec74ccf73151dcc", "shasum": "" }, "require": { @@ -3035,7 +3109,7 @@ "friendsofphp/php-cs-fixer": "^3.5", "google/cloud-storage": "^1.23", "microsoft/azure-storage-blob": "^1.1", - "phpseclib/phpseclib": "^3.0.14", + "phpseclib/phpseclib": "^3.0.34", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.3.1" @@ -3072,7 +3146,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.18.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.23.0" }, "funding": [ { @@ -3084,20 +3158,20 @@ "type": "github" } ], - "time": "2023-10-20T17:59:40+00:00" + "time": "2023-12-04T10:16:17+00:00" }, { "name": "league/flysystem-local", - "version": "3.18.0", + "version": "3.23.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "e7381ef7643f658b87efb7dbe98fe538fb1bbf32" + "reference": "5cf046ba5f059460e86a997c504dd781a39a109b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e7381ef7643f658b87efb7dbe98fe538fb1bbf32", - "reference": "e7381ef7643f658b87efb7dbe98fe538fb1bbf32", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/5cf046ba5f059460e86a997c504dd781a39a109b", + "reference": "5cf046ba5f059460e86a997c504dd781a39a109b", "shasum": "" }, "require": { @@ -3132,7 +3206,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem-local/issues", - "source": "https://github.com/thephpleague/flysystem-local/tree/3.18.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.23.0" }, "funding": [ { @@ -3144,7 +3218,7 @@ "type": "github" } ], - "time": "2023-10-19T20:07:13+00:00" + "time": "2023-12-04T10:14:46+00:00" }, { "name": "league/fractal", @@ -3362,16 +3436,16 @@ }, { "name": "league/uri", - "version": "7.3.0", + "version": "7.4.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "36743c3961bb82bf93da91917b6bced0358a8d45" + "reference": "bf414ba956d902f5d98bf9385fcf63954f09dce5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/36743c3961bb82bf93da91917b6bced0358a8d45", - "reference": "36743c3961bb82bf93da91917b6bced0358a8d45", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/bf414ba956d902f5d98bf9385fcf63954f09dce5", + "reference": "bf414ba956d902f5d98bf9385fcf63954f09dce5", "shasum": "" }, "require": { @@ -3440,7 +3514,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.3.0" + "source": "https://github.com/thephpleague/uri/tree/7.4.0" }, "funding": [ { @@ -3448,20 +3522,20 @@ "type": "github" } ], - "time": "2023-09-09T17:21:43+00:00" + "time": "2023-12-01T06:24:25+00:00" }, { "name": "league/uri-interfaces", - "version": "7.3.0", + "version": "7.4.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "c409b60ed2245ff94c965a8c798a60166db53361" + "reference": "bd8c487ec236930f7bbc42b8d374fa882fbba0f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/c409b60ed2245ff94c965a8c798a60166db53361", - "reference": "c409b60ed2245ff94c965a8c798a60166db53361", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/bd8c487ec236930f7bbc42b8d374fa882fbba0f3", + "reference": "bd8c487ec236930f7bbc42b8d374fa882fbba0f3", "shasum": "" }, "require": { @@ -3524,7 +3598,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.3.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.0" }, "funding": [ { @@ -3532,7 +3606,7 @@ "type": "github" } ], - "time": "2023-09-09T17:21:43+00:00" + "time": "2023-11-24T15:40:42+00:00" }, { "name": "mailchimp/transactional", @@ -3691,19 +3765,20 @@ }, { "name": "nesbot/carbon", - "version": "2.71.0", + "version": "2.72.1", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "98276233188583f2ff845a0f992a235472d9466a" + "reference": "2b3b3db0a2d0556a177392ff1a3bf5608fa09f78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/98276233188583f2ff845a0f992a235472d9466a", - "reference": "98276233188583f2ff845a0f992a235472d9466a", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2b3b3db0a2d0556a177392ff1a3bf5608fa09f78", + "reference": "2b3b3db0a2d0556a177392ff1a3bf5608fa09f78", "shasum": "" }, "require": { + "carbonphp/carbon-doctrine-types": "*", "ext-json": "*", "php": "^7.1.8 || ^8.0", "psr/clock": "^1.0", @@ -3715,8 +3790,8 @@ "psr/clock-implementation": "1.0" }, "require-dev": { - "doctrine/dbal": "^2.0 || ^3.1.4", - "doctrine/orm": "^2.7", + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", "friendsofphp/php-cs-fixer": "^3.0", "kylekatarnls/multi-tester": "^2.0", "ondrejmirtes/better-reflection": "*", @@ -3793,7 +3868,7 @@ "type": "tidelift" } ], - "time": "2023-09-25T11:31:05+00:00" + "time": "2023-12-08T23:47:49+00:00" }, { "name": "nette/schema", @@ -3859,16 +3934,16 @@ }, { "name": "nette/utils", - "version": "v4.0.2", + "version": "v4.0.3", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "cead6637226456b35e1175cc53797dd585d85545" + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/cead6637226456b35e1175cc53797dd585d85545", - "reference": "cead6637226456b35e1175cc53797dd585d85545", + "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", "shasum": "" }, "require": { @@ -3939,9 +4014,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.2" + "source": "https://github.com/nette/utils/tree/v4.0.3" }, - "time": "2023-09-19T11:58:07+00:00" + "time": "2023-10-29T21:02:13+00:00" }, { "name": "nunomaduro/collision", @@ -4127,16 +4202,16 @@ }, { "name": "nyholm/psr7", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "3cb4d163b58589e47b35103e8e5e6a6a475b47be" + "reference": "aa5fc277a4f5508013d571341ade0c3886d4d00e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/3cb4d163b58589e47b35103e8e5e6a6a475b47be", - "reference": "3cb4d163b58589e47b35103e8e5e6a6a475b47be", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/aa5fc277a4f5508013d571341ade0c3886d4d00e", + "reference": "aa5fc277a4f5508013d571341ade0c3886d4d00e", "shasum": "" }, "require": { @@ -4189,7 +4264,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.8.0" + "source": "https://github.com/Nyholm/psr7/tree/1.8.1" }, "funding": [ { @@ -4201,7 +4276,7 @@ "type": "github" } ], - "time": "2023-05-02T11:26:24+00:00" + "time": "2023-11-13T09:31:12+00:00" }, { "name": "paragonie/constant_time_encoding", @@ -4322,16 +4397,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.1", + "version": "1.9.2", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", "shasum": "" }, "require": { @@ -4339,7 +4414,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "type": "library", "extra": { @@ -4381,7 +4456,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.1" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" }, "funding": [ { @@ -4393,7 +4468,7 @@ "type": "tidelift" } ], - "time": "2023-02-25T19:38:58+00:00" + "time": "2023-11-12T21:59:55+00:00" }, { "name": "phpseclib/phpseclib", @@ -5343,16 +5418,16 @@ }, { "name": "ramsey/uuid", - "version": "4.7.4", + "version": "4.7.5", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "60a4c63ab724854332900504274f6150ff26d286" + "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/60a4c63ab724854332900504274f6150ff26d286", - "reference": "60a4c63ab724854332900504274f6150ff26d286", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e", + "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e", "shasum": "" }, "require": { @@ -5419,7 +5494,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.4" + "source": "https://github.com/ramsey/uuid/tree/4.7.5" }, "funding": [ { @@ -5431,7 +5506,7 @@ "type": "tidelift" } ], - "time": "2023-04-15T23:01:58+00:00" + "time": "2023-11-08T05:53:05+00:00" }, { "name": "rcrowe/twigbridge", @@ -5950,16 +6025,16 @@ }, { "name": "symfony/console", - "version": "v6.3.4", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" + "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", - "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", + "url": "https://api.github.com/repos/symfony/console/zipball/a550a7c99daeedef3f9d23fb82e3531525ff11fd", + "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd", "shasum": "" }, "require": { @@ -5967,7 +6042,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0" + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { "symfony/dependency-injection": "<5.4", @@ -5981,12 +6056,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6020,7 +6099,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.4" + "source": "https://github.com/symfony/console/tree/v6.4.1" }, "funding": [ { @@ -6036,24 +6115,24 @@ "type": "tidelift" } ], - "time": "2023-08-16T10:10:12+00:00" + "time": "2023-11-30T10:54:28+00:00" }, { "name": "symfony/css-selector", - "version": "v6.3.2", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "883d961421ab1709877c10ac99451632a3d6fa57" + "reference": "bb51d46e53ef8d50d523f0c5faedba056a27943e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", - "reference": "883d961421ab1709877c10ac99451632a3d6fa57", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/bb51d46e53ef8d50d523f0c5faedba056a27943e", + "reference": "bb51d46e53ef8d50d523f0c5faedba056a27943e", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -6085,7 +6164,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.3.2" + "source": "https://github.com/symfony/css-selector/tree/v7.0.0" }, "funding": [ { @@ -6101,11 +6180,11 @@ "type": "tidelift" } ], - "time": "2023-07-12T16:00:22+00:00" + "time": "2023-10-31T17:59:56+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -6152,7 +6231,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.3.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" }, "funding": [ { @@ -6172,30 +6251,31 @@ }, { "name": "symfony/error-handler", - "version": "v6.3.5", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "1f69476b64fb47105c06beef757766c376b548c4" + "reference": "c873490a1c97b3a0a4838afc36ff36c112d02788" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/1f69476b64fb47105c06beef757766c376b548c4", - "reference": "1f69476b64fb47105c06beef757766c376b548c4", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c873490a1c97b3a0a4838afc36ff36c112d02788", + "reference": "c873490a1c97b3a0a4838afc36ff36c112d02788", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "conflict": { - "symfony/deprecation-contracts": "<2.5" + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" }, "require-dev": { "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0" + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -6226,7 +6306,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.3.5" + "source": "https://github.com/symfony/error-handler/tree/v6.4.0" }, "funding": [ { @@ -6242,28 +6322,28 @@ "type": "tidelift" } ], - "time": "2023-09-12T06:57:20+00:00" + "time": "2023-10-18T09:43:34+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.3.2", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e" + "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e", - "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c459b40ffe67c49af6fd392aac374c9edf8a027e", + "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4", + "symfony/dependency-injection": "<6.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -6272,13 +6352,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/error-handler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0" + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -6306,7 +6386,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.0" }, "funding": [ { @@ -6322,11 +6402,11 @@ "type": "tidelift" } ], - "time": "2023-07-06T06:56:43+00:00" + "time": "2023-07-27T16:29:09+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", @@ -6382,7 +6462,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" }, "funding": [ { @@ -6402,23 +6482,23 @@ }, { "name": "symfony/finder", - "version": "v6.3.5", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4" + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a1b31d88c0e998168ca7792f222cbecee47428c4", - "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4", + "url": "https://api.github.com/repos/symfony/finder/zipball/11d736e97f116ac375a81f96e662911a34cd50ce", + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "symfony/filesystem": "^6.0" + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -6446,7 +6526,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.5" + "source": "https://github.com/symfony/finder/tree/v6.4.0" }, "funding": [ { @@ -6462,32 +6542,31 @@ "type": "tidelift" } ], - "time": "2023-09-26T12:56:25+00:00" + "time": "2023-10-31T17:30:12+00:00" }, { "name": "symfony/http-client", - "version": "v6.3.6", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "ab8446f997efb9913627e9da10fa784d2182fe92" + "reference": "c3e90d09b3c45a5d47170e81a712d51c352cbc68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/ab8446f997efb9913627e9da10fa784d2182fe92", - "reference": "ab8446f997efb9913627e9da10fa784d2182fe92", + "url": "https://api.github.com/repos/symfony/http-client/zipball/c3e90d09b3c45a5d47170e81a712d51c352cbc68", + "reference": "c3e90d09b3c45a5d47170e81a712d51c352cbc68", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "^3", "symfony/service-contracts": "^2.5|^3" }, "conflict": { "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.3" + "symfony/http-foundation": "<6.4" }, "provide": { "php-http/async-client-implementation": "*", @@ -6504,10 +6583,11 @@ "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/stopwatch": "^5.4|^6.0" + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -6538,7 +6618,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.3.6" + "source": "https://github.com/symfony/http-client/tree/v7.0.0" }, "funding": [ { @@ -6554,20 +6634,20 @@ "type": "tidelift" } ], - "time": "2023-10-06T10:08:56+00:00" + "time": "2023-11-29T08:40:23+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "3b66325d0176b4ec826bffab57c9037d759c31fb" + "reference": "1ee70e699b41909c209a0c930f11034b93578654" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/3b66325d0176b4ec826bffab57c9037d759c31fb", - "reference": "3b66325d0176b4ec826bffab57c9037d759c31fb", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1ee70e699b41909c209a0c930f11034b93578654", + "reference": "1ee70e699b41909c209a0c930f11034b93578654", "shasum": "" }, "require": { @@ -6616,7 +6696,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.4.0" }, "funding": [ { @@ -6632,20 +6712,20 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-07-30T20:28:31+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.3.6", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "c186627f52febe09c6d5270b04f8462687a250a6" + "reference": "44a6d39a9cc11e154547d882d5aac1e014440771" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c186627f52febe09c6d5270b04f8462687a250a6", - "reference": "c186627f52febe09c6d5270b04f8462687a250a6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/44a6d39a9cc11e154547d882d5aac1e014440771", + "reference": "44a6d39a9cc11e154547d882d5aac1e014440771", "shasum": "" }, "require": { @@ -6660,12 +6740,12 @@ "require-dev": { "doctrine/dbal": "^2.13.1|^3|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.3", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^5.4|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" + "symfony/cache": "^6.3|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6693,7 +6773,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.3.6" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.0" }, "funding": [ { @@ -6709,29 +6789,29 @@ "type": "tidelift" } ], - "time": "2023-10-17T11:32:53+00:00" + "time": "2023-11-20T16:41:16+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.3.6", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "4945f5001b06ff9080cd3d8f1f9f069094c0d156" + "reference": "2953274c16a229b3933ef73a6898e18388e12e1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4945f5001b06ff9080cd3d8f1f9f069094c0d156", - "reference": "4945f5001b06ff9080cd3d8f1f9f069094c0d156", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2953274c16a229b3933ef73a6898e18388e12e1b", + "reference": "2953274c16a229b3933ef73a6898e18388e12e1b", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.3", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^6.3.4", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -6739,7 +6819,7 @@ "symfony/cache": "<5.4", "symfony/config": "<6.1", "symfony/console": "<5.4", - "symfony/dependency-injection": "<6.3.4", + "symfony/dependency-injection": "<6.4", "symfony/doctrine-bridge": "<5.4", "symfony/form": "<5.4", "symfony/http-client": "<5.4", @@ -6749,7 +6829,7 @@ "symfony/translation": "<5.4", "symfony/translation-contracts": "<2.5", "symfony/twig-bridge": "<5.4", - "symfony/validator": "<5.4", + "symfony/validator": "<6.4", "symfony/var-dumper": "<6.3", "twig/twig": "<2.13" }, @@ -6758,26 +6838,26 @@ }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/clock": "^6.2", - "symfony/config": "^6.1", - "symfony/console": "^5.4|^6.0", - "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.3.4", - "symfony/dom-crawler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^5.4|^6.0", - "symfony/property-access": "^5.4.5|^6.0.5", - "symfony/routing": "^5.4|^6.0", - "symfony/serializer": "^6.3", - "symfony/stopwatch": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.3|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^5.4|^6.0", - "symfony/validator": "^6.3", - "symfony/var-exporter": "^6.2", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", "twig/twig": "^2.13|^3.0.4" }, "type": "library", @@ -6806,7 +6886,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.3.6" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.1" }, "funding": [ { @@ -6822,20 +6902,20 @@ "type": "tidelift" } ], - "time": "2023-10-21T13:12:51+00:00" + "time": "2023-12-01T17:02:02+00:00" }, { "name": "symfony/mailer", - "version": "v6.3.5", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "d89611a7830d51b5e118bca38e390dea92f9ea06" + "reference": "ca8dcf8892cdc5b4358ecf2528429bb5e706f7ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/d89611a7830d51b5e118bca38e390dea92f9ea06", - "reference": "d89611a7830d51b5e118bca38e390dea92f9ea06", + "url": "https://api.github.com/repos/symfony/mailer/zipball/ca8dcf8892cdc5b4358ecf2528429bb5e706f7ba", + "reference": "ca8dcf8892cdc5b4358ecf2528429bb5e706f7ba", "shasum": "" }, "require": { @@ -6843,8 +6923,8 @@ "php": ">=8.1", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/mime": "^6.2", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -6855,10 +6935,10 @@ "symfony/twig-bridge": "<6.2.1" }, "require-dev": { - "symfony/console": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/messenger": "^6.2", - "symfony/twig-bridge": "^6.2" + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" }, "type": "library", "autoload": { @@ -6886,7 +6966,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.3.5" + "source": "https://github.com/symfony/mailer/tree/v6.4.0" }, "funding": [ { @@ -6902,32 +6982,32 @@ "type": "tidelift" } ], - "time": "2023-09-06T09:47:15+00:00" + "time": "2023-11-12T18:02:22+00:00" }, { "name": "symfony/mailgun-mailer", - "version": "v6.3.6", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/mailgun-mailer.git", - "reference": "8d9741467c53750dc8ccda23a1cdb91cda732571" + "reference": "3c1dea66d086deea11e10cbcd45a78b44f556cee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/8d9741467c53750dc8ccda23a1cdb91cda732571", - "reference": "8d9741467c53750dc8ccda23a1cdb91cda732571", + "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/3c1dea66d086deea11e10cbcd45a78b44f556cee", + "reference": "3c1dea66d086deea11e10cbcd45a78b44f556cee", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/mailer": "^5.4.21|^6.2.7" + "php": ">=8.2", + "symfony/mailer": "^6.4|^7.0" }, "conflict": { - "symfony/http-foundation": "<6.2" + "symfony/http-foundation": "<6.4" }, "require-dev": { - "symfony/http-client": "^5.4|^6.0", - "symfony/webhook": "^6.3" + "symfony/http-client": "^6.4|^7.0", + "symfony/webhook": "^6.4|^7.0" }, "type": "symfony-mailer-bridge", "autoload": { @@ -6955,7 +7035,7 @@ "description": "Symfony Mailgun Mailer Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailgun-mailer/tree/v6.3.6" + "source": "https://github.com/symfony/mailgun-mailer/tree/v7.0.0" }, "funding": [ { @@ -6971,20 +7051,20 @@ "type": "tidelift" } ], - "time": "2023-10-12T13:32:47+00:00" + "time": "2023-11-07T15:10:37+00:00" }, { "name": "symfony/mime", - "version": "v6.3.5", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e" + "reference": "ca4f58b2ef4baa8f6cecbeca2573f88cd577d205" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/d5179eedf1cb2946dbd760475ebf05c251ef6a6e", - "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e", + "url": "https://api.github.com/repos/symfony/mime/zipball/ca4f58b2ef4baa8f6cecbeca2573f88cd577d205", + "reference": "ca4f58b2ef4baa8f6cecbeca2573f88cd577d205", "shasum": "" }, "require": { @@ -6998,16 +7078,16 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/mailer": "<5.4", - "symfony/serializer": "<6.2.13|>=6.3,<6.3.2" + "symfony/serializer": "<6.3.2" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/property-access": "^5.4|^6.0", - "symfony/property-info": "^5.4|^6.0", - "symfony/serializer": "~6.2.13|^6.3.2" + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.3.2|^7.0" }, "type": "library", "autoload": { @@ -7039,7 +7119,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.3.5" + "source": "https://github.com/symfony/mime/tree/v6.4.0" }, "funding": [ { @@ -7055,7 +7135,7 @@ "type": "tidelift" } ], - "time": "2023-09-29T06:59:36+00:00" + "time": "2023-10-17T11:49:05+00:00" }, { "name": "symfony/polyfill-ctype", @@ -7797,16 +7877,16 @@ }, { "name": "symfony/process", - "version": "v6.3.4", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" + "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", - "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", + "url": "https://api.github.com/repos/symfony/process/zipball/191703b1566d97a5425dc969e4350d32b8ef17aa", + "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa", "shasum": "" }, "require": { @@ -7838,7 +7918,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.4" + "source": "https://github.com/symfony/process/tree/v6.4.0" }, "funding": [ { @@ -7854,7 +7934,7 @@ "type": "tidelift" } ], - "time": "2023-08-07T10:39:22+00:00" + "time": "2023-11-17T21:06:49+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -7947,16 +8027,16 @@ }, { "name": "symfony/routing", - "version": "v6.3.5", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "82616e59acd3e3d9c916bba798326cb7796d7d31" + "reference": "0c95c164fdba18b12523b75e64199ca3503e6d40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/82616e59acd3e3d9c916bba798326cb7796d7d31", - "reference": "82616e59acd3e3d9c916bba798326cb7796d7d31", + "url": "https://api.github.com/repos/symfony/routing/zipball/0c95c164fdba18b12523b75e64199ca3503e6d40", + "reference": "0c95c164fdba18b12523b75e64199ca3503e6d40", "shasum": "" }, "require": { @@ -7972,11 +8052,11 @@ "require-dev": { "doctrine/annotations": "^1.12|^2", "psr/log": "^1|^2|^3", - "symfony/config": "^6.2", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -8010,7 +8090,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.3.5" + "source": "https://github.com/symfony/routing/tree/v6.4.1" }, "funding": [ { @@ -8026,20 +8106,20 @@ "type": "tidelift" } ], - "time": "2023-09-20T16:05:51+00:00" + "time": "2023-12-01T14:54:37+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", "shasum": "" }, "require": { @@ -8092,7 +8172,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" }, "funding": [ { @@ -8108,24 +8188,24 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-07-30T20:28:31+00:00" }, { "name": "symfony/string", - "version": "v6.3.5", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339" + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339", - "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339", + "url": "https://api.github.com/repos/symfony/string/zipball/92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -8135,11 +8215,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -8178,7 +8258,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.5" + "source": "https://github.com/symfony/string/tree/v7.0.0" }, "funding": [ { @@ -8194,20 +8274,20 @@ "type": "tidelift" } ], - "time": "2023-09-18T10:38:32+00:00" + "time": "2023-11-29T08:40:23+00:00" }, { "name": "symfony/translation", - "version": "v6.3.6", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "869b26c7a9d4b8a48afdd77ab36031909c87e3a2" + "reference": "b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/869b26c7a9d4b8a48afdd77ab36031909c87e3a2", - "reference": "869b26c7a9d4b8a48afdd77ab36031909c87e3a2", + "url": "https://api.github.com/repos/symfony/translation/zipball/b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37", + "reference": "b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37", "shasum": "" }, "require": { @@ -8232,17 +8312,17 @@ "require-dev": { "nikic/php-parser": "^4.13", "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/intl": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^5.4|^6.0" + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -8273,7 +8353,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.3.6" + "source": "https://github.com/symfony/translation/tree/v6.4.0" }, "funding": [ { @@ -8289,20 +8369,20 @@ "type": "tidelift" } ], - "time": "2023-10-17T11:32:53+00:00" + "time": "2023-11-29T08:14:36+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86" + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/02c24deb352fb0d79db5486c0c79905a85e37e86", - "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/dee0c6e5b4c07ce851b462530088e64b255ac9c5", + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5", "shasum": "" }, "require": { @@ -8351,7 +8431,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.0" }, "funding": [ { @@ -8367,20 +8447,20 @@ "type": "tidelift" } ], - "time": "2023-05-30T17:17:10+00:00" + "time": "2023-07-25T15:08:44+00:00" }, { "name": "symfony/uid", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384" + "reference": "8092dd1b1a41372110d06374f99ee62f7f0b9a92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/01b0f20b1351d997711c56f1638f7a8c3061e384", - "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384", + "url": "https://api.github.com/repos/symfony/uid/zipball/8092dd1b1a41372110d06374f99ee62f7f0b9a92", + "reference": "8092dd1b1a41372110d06374f99ee62f7f0b9a92", "shasum": "" }, "require": { @@ -8388,7 +8468,7 @@ "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^5.4|^6.0" + "symfony/console": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -8425,7 +8505,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.3.0" + "source": "https://github.com/symfony/uid/tree/v6.4.0" }, "funding": [ { @@ -8441,20 +8521,20 @@ "type": "tidelift" } ], - "time": "2023-04-08T07:25:02+00:00" + "time": "2023-10-31T08:18:17+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.3.6", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97" + "reference": "c40f7d17e91d8b407582ed51a2bbf83c52c367f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/999ede244507c32b8e43aebaa10e9fce20de7c97", - "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c40f7d17e91d8b407582ed51a2bbf83c52c367f6", + "reference": "c40f7d17e91d8b407582ed51a2bbf83c52c367f6", "shasum": "" }, "require": { @@ -8467,10 +8547,11 @@ }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/uid": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", "twig/twig": "^2.13|^3.0.4" }, "bin": [ @@ -8509,7 +8590,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.6" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.0" }, "funding": [ { @@ -8525,7 +8606,7 @@ "type": "tidelift" } ], - "time": "2023-10-12T18:45:56+00:00" + "time": "2023-11-09T08:28:32+00:00" }, { "name": "therobfonz/laravel-mandrill-driver", @@ -8591,23 +8672,23 @@ }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "2.2.6", + "version": "v2.2.7", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c" + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/c42125b83a4fa63b187fdf29f9c93cb7733da30c", - "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb", + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "php": "^5.5 || ^7.0 || ^8.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" @@ -8638,32 +8719,33 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.6" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.2.7" }, - "time": "2023-01-03T09:29:04+00:00" + "time": "2023-12-08T13:03:43+00:00" }, { "name": "twig/twig", - "version": "v3.7.1", + "version": "v3.8.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", - "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" }, "require-dev": { "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3" + "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" }, "type": "library", "autoload": { @@ -8699,7 +8781,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.7.1" + "source": "https://github.com/twigphp/Twig/tree/v3.8.0" }, "funding": [ { @@ -8711,35 +8793,35 @@ "type": "tidelift" } ], - "time": "2023-08-28T11:09:02+00:00" + "time": "2023-11-21T18:54:41+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.5.0", + "version": "v5.6.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", - "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.0.2", - "php": "^7.1.3 || ^8.0", - "phpoption/phpoption": "^1.8", - "symfony/polyfill-ctype": "^1.23", - "symfony/polyfill-mbstring": "^1.23.1", - "symfony/polyfill-php80": "^1.23.1" + "graham-campbell/result-type": "^1.1.2", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.2", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", + "bamarni/composer-bin-plugin": "^1.8.2", "ext-filter": "*", - "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "suggest": { "ext-filter": "Required to use the boolean validator." @@ -8751,7 +8833,7 @@ "forward-command": true }, "branch-alias": { - "dev-master": "5.5-dev" + "dev-master": "5.6-dev" } }, "autoload": { @@ -8783,7 +8865,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" }, "funding": [ { @@ -8795,7 +8877,7 @@ "type": "tidelift" } ], - "time": "2022-10-16T01:01:54+00:00" + "time": "2023-11-12T22:43:29+00:00" }, { "name": "voku/portable-ascii", @@ -9413,17 +9495,118 @@ "time": "2020-07-09T08:09:16+00:00" }, { - "name": "mockery/mockery", - "version": "1.6.6", + "name": "larastan/larastan", + "version": "v2.7.0", "source": { "type": "git", - "url": "https://github.com/mockery/mockery.git", - "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e" + "url": "https://github.com/larastan/larastan.git", + "reference": "a2610d46b9999cf558d9900ccb641962d1442f55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/b8e0bb7d8c604046539c1115994632c74dcb361e", - "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e", + "url": "https://api.github.com/repos/larastan/larastan/zipball/a2610d46b9999cf558d9900ccb641962d1442f55", + "reference": "a2610d46b9999cf558d9900ccb641962d1442f55", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^9.52.16 || ^10.28.0", + "illuminate/container": "^9.52.16 || ^10.28.0", + "illuminate/contracts": "^9.52.16 || ^10.28.0", + "illuminate/database": "^9.52.16 || ^10.28.0", + "illuminate/http": "^9.52.16 || ^10.28.0", + "illuminate/pipeline": "^9.52.16 || ^10.28.0", + "illuminate/support": "^9.52.16 || ^10.28.0", + "php": "^8.0.2", + "phpmyadmin/sql-parser": "^5.8.2", + "phpstan/phpstan": "^1.10.41" + }, + "require-dev": { + "nikic/php-parser": "^4.17.1", + "orchestra/canvas": "^7.11.1 || ^8.11.0", + "orchestra/testbench": "^7.33.0 || ^8.13.0", + "phpunit/phpunit": "^9.6.13" + }, + "suggest": { + "orchestra/testbench": "Using Larastan for analysing a package needs Testbench" + }, + "type": "phpstan-extension", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Larastan\\Larastan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Can Vural", + "email": "can9119@gmail.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel", + "keywords": [ + "PHPStan", + "code analyse", + "code analysis", + "larastan", + "laravel", + "package", + "php", + "static analysis" + ], + "support": { + "issues": "https://github.com/larastan/larastan/issues", + "source": "https://github.com/larastan/larastan/tree/v2.7.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/canvural", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2023-12-04T19:21:38+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.7", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", + "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", "shasum": "" }, "require": { @@ -9436,9 +9619,7 @@ }, "require-dev": { "phpunit/phpunit": "^8.5 || ^9.6.10", - "psalm/plugin-phpunit": "^0.18.4", - "symplify/easy-coding-standard": "^11.5.0", - "vimeo/psalm": "^4.30" + "symplify/easy-coding-standard": "^12.0.8" }, "type": "library", "autoload": { @@ -9495,7 +9676,7 @@ "security": "https://github.com/mockery/mockery/security/advisories", "source": "https://github.com/mockery/mockery" }, - "time": "2023-08-09T00:03:52+00:00" + "time": "2023-12-10T02:24:34+00:00" }, { "name": "myclabs/deep-copy", @@ -9612,102 +9793,6 @@ }, "time": "2023-08-13T19:53:39+00:00" }, - { - "name": "nunomaduro/larastan", - "version": "v2.6.4", - "source": { - "type": "git", - "url": "https://github.com/nunomaduro/larastan.git", - "reference": "6c5e8820f3db6397546f3ce48520af9d312aed27" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/larastan/zipball/6c5e8820f3db6397546f3ce48520af9d312aed27", - "reference": "6c5e8820f3db6397546f3ce48520af9d312aed27", - "shasum": "" - }, - "require": { - "ext-json": "*", - "illuminate/console": "^9.47.0 || ^10.0.0", - "illuminate/container": "^9.47.0 || ^10.0.0", - "illuminate/contracts": "^9.47.0 || ^10.0.0", - "illuminate/database": "^9.47.0 || ^10.0.0", - "illuminate/http": "^9.47.0 || ^10.0.0", - "illuminate/pipeline": "^9.47.0 || ^10.0.0", - "illuminate/support": "^9.47.0 || ^10.0.0", - "php": "^8.0.2", - "phpmyadmin/sql-parser": "^5.6.0", - "phpstan/phpstan": "~1.10.6" - }, - "require-dev": { - "nikic/php-parser": "^4.15.2", - "orchestra/testbench": "^7.19.0 || ^8.0.0", - "phpunit/phpunit": "^9.5.27" - }, - "suggest": { - "orchestra/testbench": "Using Larastan for analysing a package needs Testbench" - }, - "type": "phpstan-extension", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - }, - "phpstan": { - "includes": [ - "extension.neon" - ] - } - }, - "autoload": { - "psr-4": { - "NunoMaduro\\Larastan\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel", - "keywords": [ - "PHPStan", - "code analyse", - "code analysis", - "larastan", - "laravel", - "package", - "php", - "static analysis" - ], - "support": { - "issues": "https://github.com/nunomaduro/larastan/issues", - "source": "https://github.com/nunomaduro/larastan/tree/v2.6.4" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/canvural", - "type": "github" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" - } - ], - "time": "2023-07-29T12:13:13+00:00" - }, { "name": "phar-io/manifest", "version": "2.0.3", @@ -10018,17 +10103,61 @@ "time": "2023-09-19T12:34:29+00:00" }, { - "name": "phpstan/phpdoc-parser", - "version": "1.24.2", + "name": "phpstan/extension-installer", + "version": "1.3.1", "source": { "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "bcad8d995980440892759db0c32acae7c8e79442" + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", - "reference": "bcad8d995980440892759db0c32acae7c8e79442", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a", + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.3.1" + }, + "time": "2023-05-24T08:59:17+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.24.4", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6bd0c26f3786cd9b7c359675cb789e35a8e07496", + "reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496", "shasum": "" }, "require": { @@ -10060,22 +10189,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.4" }, - "time": "2023-09-26T12:28:12+00:00" + "time": "2023-11-26T18:29:22+00:00" }, { "name": "phpstan/phpstan", - "version": "1.10.39", + "version": "1.10.48", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "d9dedb0413f678b4d03cbc2279a48f91592c97c4" + "reference": "087ed4b5f4a7a6e8f3bbdfbfe98ce5c181380bc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d9dedb0413f678b4d03cbc2279a48f91592c97c4", - "reference": "d9dedb0413f678b4d03cbc2279a48f91592c97c4", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/087ed4b5f4a7a6e8f3bbdfbfe98ce5c181380bc6", + "reference": "087ed4b5f4a7a6e8f3bbdfbfe98ce5c181380bc6", "shasum": "" }, "require": { @@ -10124,7 +10253,7 @@ "type": "tidelift" } ], - "time": "2023-10-17T15:46:26+00:00" + "time": "2023-12-08T14:34:28+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -10176,21 +10305,21 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "1.5.1", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6" + "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b21c03d4f6f3a446e4311155f4be9d65048218e6", - "reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7a50e9662ee9f3942e4aaaf3d603653f60282542", + "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542", "shasum": "" }, "require": { "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^1.10.34" }, "require-dev": { "nikic/php-parser": "^4.13.0", @@ -10219,22 +10348,22 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.1" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.2" }, - "time": "2023-03-29T14:47:40+00:00" + "time": "2023-10-30T14:35:06+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.7", + "version": "10.1.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "355324ca4980b8916c18b9db29f3ef484078f26e" + "reference": "a56a9ab2f680246adcf3db43f38ddf1765774735" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e", - "reference": "355324ca4980b8916c18b9db29f3ef484078f26e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/a56a9ab2f680246adcf3db43f38ddf1765774735", + "reference": "a56a9ab2f680246adcf3db43f38ddf1765774735", "shasum": "" }, "require": { @@ -10291,7 +10420,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.9" }, "funding": [ { @@ -10299,7 +10428,7 @@ "type": "github" } ], - "time": "2023-10-04T15:34:17+00:00" + "time": "2023-11-23T12:23:20+00:00" }, { "name": "phpunit/php-file-iterator", @@ -10546,16 +10675,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.4.2", + "version": "10.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1" + "reference": "5aedff46afba98dddecaa12349ec044d9103d4fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", - "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5aedff46afba98dddecaa12349ec044d9103d4fe", + "reference": "5aedff46afba98dddecaa12349ec044d9103d4fe", "shasum": "" }, "require": { @@ -10595,7 +10724,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.4-dev" + "dev-main": "10.5-dev" } }, "autoload": { @@ -10627,7 +10756,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.2" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.2" }, "funding": [ { @@ -10643,7 +10772,7 @@ "type": "tidelift" } ], - "time": "2023-10-26T07:21:45+00:00" + "time": "2023-12-05T14:54:33+00:00" }, { "name": "sebastian/cli-parser", @@ -11617,16 +11746,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { @@ -11655,7 +11784,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { @@ -11663,7 +11792,7 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2023-11-20T00:12:19+00:00" } ], "aliases": [], @@ -11672,7 +11801,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.2", + "php": ">=8.3", "ext-bcmath": "*", "ext-curl": "*", "ext-fileinfo": "*", @@ -11690,8 +11819,5 @@ "ext-xmlwriter": "*" }, "platform-dev": [], - "platform-overrides": { - "php": "8.2" - }, "plugin-api-version": "2.6.0" } diff --git a/config/broadcasting.php b/config/broadcasting.php index 9f892e15a0..abc48f2e99 100644 --- a/config/broadcasting.php +++ b/config/broadcasting.php @@ -57,7 +57,7 @@ return [ 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => env('PUSHER_APP_CLUSTER'), - 'host' => env('PUSHER_HOST') ?: 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com', + 'host' => null !== env('PUSHER_HOST') ? env('PUSHER_HOST') : 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com', 'port' => env('PUSHER_PORT', 443), 'scheme' => env('PUSHER_SCHEME', 'https'), 'encrypted' => true, diff --git a/config/database.php b/config/database.php index cb181adbd7..3a36cb9e72 100644 --- a/config/database.php +++ b/config/database.php @@ -51,7 +51,7 @@ $mysql_ssl_verify = envNonEmpty('MYSQL_SSL_VERIFY_SERVER_CERT', null); $mySqlSSLOptions = []; $useSSL = envNonEmpty('MYSQL_USE_SSL', false); -if (false !== $useSSL && null !== $useSSL) { +if (false !== $useSSL && null !== $useSSL && '' !== $useSSL) { if (null !== $mysql_ssl_ca_dir) { $mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CAPATH] = $mysql_ssl_ca_dir; } @@ -138,7 +138,7 @@ return [ 'client' => env('REDIS_CLIENT', 'predis'), 'options' => [ 'cluster' => env('REDIS_CLUSTER', 'predis'), - 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'), + //'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'), ], 'default' => [ 'scheme' => envNonEmpty('REDIS_SCHEME', 'tcp'), diff --git a/config/firefly.php b/config/firefly.php index 26ba1bd4d3..1cce3cfab2 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -86,8 +86,10 @@ use FireflyIII\TransactionRules\Actions\SetBudget; use FireflyIII\TransactionRules\Actions\SetCategory; use FireflyIII\TransactionRules\Actions\SetDescription; use FireflyIII\TransactionRules\Actions\SetDestinationAccount; +use FireflyIII\TransactionRules\Actions\SetDestinationToCashAccount; use FireflyIII\TransactionRules\Actions\SetNotes; use FireflyIII\TransactionRules\Actions\SetSourceAccount; +use FireflyIII\TransactionRules\Actions\SetSourceToCashAccount; use FireflyIII\TransactionRules\Actions\SwitchAccounts; use FireflyIII\TransactionRules\Actions\UpdatePiggybank; use FireflyIII\User; @@ -112,9 +114,9 @@ return [ 'handle_debts' => true, // see cer.php for exchange rates feature flag. ], - 'version' => '6.0.30', - 'api_version' => '2.0.11', - 'db_version' => 21, + 'version' => '6.1.0-alpha.1', + 'api_version' => '2.0.12', + 'db_version' => 22, // generic settings 'maxUploadSize' => 1073741824, // 1 GB @@ -516,6 +518,8 @@ return [ 'append_notes_to_descr' => AppendNotesToDescription::class, 'move_descr_to_notes' => MoveDescriptionToNotes::class, 'move_notes_to_descr' => MoveNotesToDescription::class, + 'set_source_to_cash' => SetSourceToCashAccount::class, + 'set_destination_to_cash' => SetDestinationToCashAccount::class, ], 'context-rule-actions' => [ 'set_category', @@ -908,4 +912,7 @@ return [ // only used in v1 'allowed_sort_parameters' => ['order', 'name', 'iban'], + + // preselected account lists possibilities: + 'preselected_accounts' => ['all','assets','liabilities'], ]; diff --git a/config/search.php b/config/search.php index 6e065e4f07..ec80652cff 100644 --- a/config/search.php +++ b/config/search.php @@ -34,6 +34,9 @@ return [ 'tag_is' => ['alias' => false, 'needs_context' => true,], 'tag_is_not' => ['alias' => false, 'needs_context' => true,], 'tag' => ['alias' => true, 'alias_for' => 'tag_is', 'needs_context' => true,], + 'tag_contains' => ['alias' => false, 'needs_context' => true,], + 'tag_ends' => ['alias' => false, 'needs_context' => true,], + 'tag_starts' => ['alias' => false, 'needs_context' => true,], 'description_is' => ['alias' => false, 'needs_context' => true,], 'description' => ['alias' => true, 'alias_for' => 'description_is', 'needs_context' => true,], 'description_contains' => ['alias' => false, 'needs_context' => true,], diff --git a/config/upgrade.php b/config/upgrade.php index 423dbfc272..ce8f5477c2 100644 --- a/config/upgrade.php +++ b/config/upgrade.php @@ -35,6 +35,7 @@ return [ '4.8.0' => 'This is a huge upgrade for Firefly III. Please expect bugs and errors, and bear with me as I fix them. I tested a lot of things but pretty sure I missed some. Thanks for understanding.', '4.8.1' => 'This version of Firefly III requires PHP7.3.', '5.3.0' => 'This version of Firefly III requires PHP7.4.', + '6.1' => 'This version of Firefly III requires PHP8.3.', ], 'install' => [ '4.3' => 'Welcome to Firefly! Make sure you follow the installation guide. If you need more help, please check Github or the Firefly III website. The installation guide has a FAQ which you should check out as well.', @@ -48,6 +49,7 @@ return [ '4.8.0' => 'This is a huge upgrade for Firefly III. Please expect bugs and errors, and bear with me as I fix them. I tested a lot of things but pretty sure I missed some. Thanks for understanding.', '4.8.1' => 'This version of Firefly III requires PHP7.3.', '5.3.0' => 'This version of Firefly III requires PHP7.4.', + '6.1' => 'This version of Firefly III requires PHP8.3.', ], ], ]; diff --git a/database/migrations/2016_06_16_000000_create_support_tables.php b/database/migrations/2016_06_16_000000_create_support_tables.php index 14d5c53c2f..37faf813de 100644 --- a/database/migrations/2016_06_16_000000_create_support_tables.php +++ b/database/migrations/2016_06_16_000000_create_support_tables.php @@ -54,7 +54,7 @@ class CreateSupportTables extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2016_06_16_000001_create_users_table.php b/database/migrations/2016_06_16_000001_create_users_table.php index 1e0aece1d0..2b7f8c9682 100644 --- a/database/migrations/2016_06_16_000001_create_users_table.php +++ b/database/migrations/2016_06_16_000001_create_users_table.php @@ -45,6 +45,7 @@ class CreateUsersTable extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) * */ public function up(): void diff --git a/database/migrations/2016_06_16_000002_create_main_tables.php b/database/migrations/2016_06_16_000002_create_main_tables.php index 65c8d18219..6c85d671c0 100644 --- a/database/migrations/2016_06_16_000002_create_main_tables.php +++ b/database/migrations/2016_06_16_000002_create_main_tables.php @@ -72,6 +72,7 @@ class CreateMainTables extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) * */ public function up(): void diff --git a/database/migrations/2016_08_25_091522_changes_for_3101.php b/database/migrations/2016_08_25_091522_changes_for_3101.php index 5b32bada7f..f1aa6f3e42 100644 --- a/database/migrations/2016_08_25_091522_changes_for_3101.php +++ b/database/migrations/2016_08_25_091522_changes_for_3101.php @@ -33,15 +33,12 @@ class ChangesFor3101 extends Migration /** * Reverse the migrations. */ - public function down(): void - { - } + public function down(): void {} /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) * */ - public function up(): void - { - } + public function up(): void {} } diff --git a/database/migrations/2016_09_12_121359_fix_nullables.php b/database/migrations/2016_09_12_121359_fix_nullables.php index fbb3a63012..0394d5b810 100644 --- a/database/migrations/2016_09_12_121359_fix_nullables.php +++ b/database/migrations/2016_09_12_121359_fix_nullables.php @@ -38,12 +38,11 @@ class FixNullables extends Migration /** * Reverse the migrations. */ - public function down(): void - { - } + public function down(): void {} /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) * */ public function up(): void diff --git a/database/migrations/2016_10_09_150037_expand_transactions_table.php b/database/migrations/2016_10_09_150037_expand_transactions_table.php index 256a3bb5ca..d89351ab75 100644 --- a/database/migrations/2016_10_09_150037_expand_transactions_table.php +++ b/database/migrations/2016_10_09_150037_expand_transactions_table.php @@ -55,6 +55,7 @@ class ExpandTransactionsTable extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) * */ public function up(): void diff --git a/database/migrations/2016_10_22_075804_changes_for_v410.php b/database/migrations/2016_10_22_075804_changes_for_v410.php index 7faabd92e2..54f9d108e2 100644 --- a/database/migrations/2016_10_22_075804_changes_for_v410.php +++ b/database/migrations/2016_10_22_075804_changes_for_v410.php @@ -42,7 +42,7 @@ class ChangesForV410 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2016_11_24_210552_changes_for_v420.php b/database/migrations/2016_11_24_210552_changes_for_v420.php index 3283c06112..a2e9afe412 100644 --- a/database/migrations/2016_11_24_210552_changes_for_v420.php +++ b/database/migrations/2016_11_24_210552_changes_for_v420.php @@ -54,6 +54,7 @@ class ChangesForV420 extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) * */ public function up(): void diff --git a/database/migrations/2016_12_22_150431_changes_for_v430.php b/database/migrations/2016_12_22_150431_changes_for_v430.php index 272d3b2a63..b265485128 100644 --- a/database/migrations/2016_12_22_150431_changes_for_v430.php +++ b/database/migrations/2016_12_22_150431_changes_for_v430.php @@ -42,7 +42,7 @@ class ChangesForV430 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2016_12_28_203205_changes_for_v431.php b/database/migrations/2016_12_28_203205_changes_for_v431.php index 7da1ee7968..c177e7317e 100644 --- a/database/migrations/2016_12_28_203205_changes_for_v431.php +++ b/database/migrations/2016_12_28_203205_changes_for_v431.php @@ -112,7 +112,7 @@ class ChangesForV431 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2017_04_13_163623_changes_for_v440.php b/database/migrations/2017_04_13_163623_changes_for_v440.php index 0caad0e92b..5252a8042a 100644 --- a/database/migrations/2017_04_13_163623_changes_for_v440.php +++ b/database/migrations/2017_04_13_163623_changes_for_v440.php @@ -60,7 +60,7 @@ class ChangesForV440 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2017_06_02_105232_changes_for_v450.php b/database/migrations/2017_06_02_105232_changes_for_v450.php index d9b025e8d7..e0db079ec9 100644 --- a/database/migrations/2017_06_02_105232_changes_for_v450.php +++ b/database/migrations/2017_06_02_105232_changes_for_v450.php @@ -84,7 +84,7 @@ class ChangesForV450 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2017_08_20_062014_changes_for_v470.php b/database/migrations/2017_08_20_062014_changes_for_v470.php index a6a4a52f72..4ab5e7d0c1 100644 --- a/database/migrations/2017_08_20_062014_changes_for_v470.php +++ b/database/migrations/2017_08_20_062014_changes_for_v470.php @@ -44,7 +44,7 @@ class ChangesForV470 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2017_11_04_170844_changes_for_v470a.php b/database/migrations/2017_11_04_170844_changes_for_v470a.php index 10f2bb0aa9..e9ffe8c980 100644 --- a/database/migrations/2017_11_04_170844_changes_for_v470a.php +++ b/database/migrations/2017_11_04_170844_changes_for_v470a.php @@ -56,7 +56,7 @@ class ChangesForV470a extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2018_01_01_000001_create_oauth_auth_codes_table.php b/database/migrations/2018_01_01_000001_create_oauth_auth_codes_table.php index 75cd6c65dc..e04833531e 100644 --- a/database/migrations/2018_01_01_000001_create_oauth_auth_codes_table.php +++ b/database/migrations/2018_01_01_000001_create_oauth_auth_codes_table.php @@ -44,6 +44,7 @@ class CreateOauthAuthCodesTable extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2018_01_01_000002_create_oauth_access_tokens_table.php b/database/migrations/2018_01_01_000002_create_oauth_access_tokens_table.php index 3b2950b92c..bb9f1cdd93 100644 --- a/database/migrations/2018_01_01_000002_create_oauth_access_tokens_table.php +++ b/database/migrations/2018_01_01_000002_create_oauth_access_tokens_table.php @@ -44,6 +44,7 @@ class CreateOauthAccessTokensTable extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2018_01_01_000003_create_oauth_refresh_tokens_table.php b/database/migrations/2018_01_01_000003_create_oauth_refresh_tokens_table.php index 5108372473..324f0e213d 100644 --- a/database/migrations/2018_01_01_000003_create_oauth_refresh_tokens_table.php +++ b/database/migrations/2018_01_01_000003_create_oauth_refresh_tokens_table.php @@ -44,6 +44,7 @@ class CreateOauthRefreshTokensTable extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2018_01_01_000004_create_oauth_clients_table.php b/database/migrations/2018_01_01_000004_create_oauth_clients_table.php index c95a1f8eb6..16ef069c02 100644 --- a/database/migrations/2018_01_01_000004_create_oauth_clients_table.php +++ b/database/migrations/2018_01_01_000004_create_oauth_clients_table.php @@ -44,6 +44,7 @@ class CreateOauthClientsTable extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2018_01_01_000005_create_oauth_personal_access_clients_table.php b/database/migrations/2018_01_01_000005_create_oauth_personal_access_clients_table.php index a66f2f5387..4dd78aea8c 100644 --- a/database/migrations/2018_01_01_000005_create_oauth_personal_access_clients_table.php +++ b/database/migrations/2018_01_01_000005_create_oauth_personal_access_clients_table.php @@ -44,6 +44,7 @@ class CreateOauthPersonalAccessClientsTable extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2018_03_19_141348_changes_for_v472.php b/database/migrations/2018_03_19_141348_changes_for_v472.php index 888cdb9f91..3ee040ca42 100644 --- a/database/migrations/2018_03_19_141348_changes_for_v472.php +++ b/database/migrations/2018_03_19_141348_changes_for_v472.php @@ -74,6 +74,7 @@ class ChangesForV472 extends Migration * Run the migrations. * * @return void + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { diff --git a/database/migrations/2018_04_07_210913_changes_for_v473.php b/database/migrations/2018_04_07_210913_changes_for_v473.php index 5d59bc094f..e52f0ef5a2 100644 --- a/database/migrations/2018_04_07_210913_changes_for_v473.php +++ b/database/migrations/2018_04_07_210913_changes_for_v473.php @@ -77,7 +77,7 @@ class ChangesForV473 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2018_04_29_174524_changes_for_v474.php b/database/migrations/2018_04_29_174524_changes_for_v474.php index f7d5ce28dc..719f01d4f8 100644 --- a/database/migrations/2018_04_29_174524_changes_for_v474.php +++ b/database/migrations/2018_04_29_174524_changes_for_v474.php @@ -36,16 +36,13 @@ class ChangesForV474 extends Migration * * @return void */ - public function down(): void - { - } + public function down(): void {} /** * Run the migrations. * * @return void + * @SuppressWarnings(PHPMD.ShortMethodName) */ - public function up(): void - { - } + public function up(): void {} } diff --git a/database/migrations/2018_06_08_200526_changes_for_v475.php b/database/migrations/2018_06_08_200526_changes_for_v475.php index 96f58a0eb2..2426126da4 100644 --- a/database/migrations/2018_06_08_200526_changes_for_v475.php +++ b/database/migrations/2018_06_08_200526_changes_for_v475.php @@ -49,7 +49,7 @@ class ChangesForV475 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2018_09_05_195147_changes_for_v477.php b/database/migrations/2018_09_05_195147_changes_for_v477.php index 8ba83d4dd5..e93b73cea1 100644 --- a/database/migrations/2018_09_05_195147_changes_for_v477.php +++ b/database/migrations/2018_09_05_195147_changes_for_v477.php @@ -63,7 +63,7 @@ class ChangesForV477 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2018_11_06_172532_changes_for_v479.php b/database/migrations/2018_11_06_172532_changes_for_v479.php index 1d48a17273..31269943b2 100644 --- a/database/migrations/2018_11_06_172532_changes_for_v479.php +++ b/database/migrations/2018_11_06_172532_changes_for_v479.php @@ -58,7 +58,7 @@ class ChangesForV479 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2019_01_28_193833_changes_for_v4710.php b/database/migrations/2019_01_28_193833_changes_for_v4710.php index 9f2352be76..118b7848e4 100644 --- a/database/migrations/2019_01_28_193833_changes_for_v4710.php +++ b/database/migrations/2019_01_28_193833_changes_for_v4710.php @@ -47,7 +47,7 @@ class ChangesForV4710 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2019_02_05_055516_changes_for_v4711.php b/database/migrations/2019_02_05_055516_changes_for_v4711.php index f410dad84d..bbdb40e8c7 100644 --- a/database/migrations/2019_02_05_055516_changes_for_v4711.php +++ b/database/migrations/2019_02_05_055516_changes_for_v4711.php @@ -46,7 +46,7 @@ class ChangesForV4711 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2019_02_11_170529_changes_for_v4712.php b/database/migrations/2019_02_11_170529_changes_for_v4712.php index c0ce9bf969..c93ba77ff3 100644 --- a/database/migrations/2019_02_11_170529_changes_for_v4712.php +++ b/database/migrations/2019_02_11_170529_changes_for_v4712.php @@ -45,7 +45,7 @@ class ChangesForV4712 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2019_03_11_223700_fix_ldap_configuration.php b/database/migrations/2019_03_11_223700_fix_ldap_configuration.php index 617d58dfbb..1a896d7f1a 100644 --- a/database/migrations/2019_03_11_223700_fix_ldap_configuration.php +++ b/database/migrations/2019_03_11_223700_fix_ldap_configuration.php @@ -58,7 +58,7 @@ class FixLdapConfiguration extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2019_03_22_183214_changes_for_v480.php b/database/migrations/2019_03_22_183214_changes_for_v480.php index ae51522ab7..de803caf48 100644 --- a/database/migrations/2019_03_22_183214_changes_for_v480.php +++ b/database/migrations/2019_03_22_183214_changes_for_v480.php @@ -113,7 +113,7 @@ class ChangesForV480 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2019_12_28_191351_make_locations_table.php b/database/migrations/2019_12_28_191351_make_locations_table.php index 921f3ad92d..7b3443b06b 100644 --- a/database/migrations/2019_12_28_191351_make_locations_table.php +++ b/database/migrations/2019_12_28_191351_make_locations_table.php @@ -46,7 +46,7 @@ class MakeLocationsTable extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2020_03_13_201950_changes_for_v520.php b/database/migrations/2020_03_13_201950_changes_for_v520.php index 98a9df31a6..18d254418c 100644 --- a/database/migrations/2020_03_13_201950_changes_for_v520.php +++ b/database/migrations/2020_03_13_201950_changes_for_v520.php @@ -45,7 +45,7 @@ class ChangesForV520 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2020_06_07_063612_changes_for_v530.php b/database/migrations/2020_06_07_063612_changes_for_v530.php index 270aadd088..b5615f6b24 100644 --- a/database/migrations/2020_06_07_063612_changes_for_v530.php +++ b/database/migrations/2020_06_07_063612_changes_for_v530.php @@ -46,7 +46,7 @@ class ChangesForV530 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2020_06_30_202620_changes_for_v530a.php b/database/migrations/2020_06_30_202620_changes_for_v530a.php index df4691b311..2dd5834058 100644 --- a/database/migrations/2020_06_30_202620_changes_for_v530a.php +++ b/database/migrations/2020_06_30_202620_changes_for_v530a.php @@ -59,7 +59,7 @@ class ChangesForV530a extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2020_07_24_162820_changes_for_v540.php b/database/migrations/2020_07_24_162820_changes_for_v540.php index 55f025a56c..a3acb2f8de 100644 --- a/database/migrations/2020_07_24_162820_changes_for_v540.php +++ b/database/migrations/2020_07_24_162820_changes_for_v540.php @@ -100,7 +100,7 @@ class ChangesForV540 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void @@ -152,7 +152,7 @@ class ChangesForV540 extends Migration try { Schema::table( 'oauth_clients', - function (Blueprint $table) { + static function (Blueprint $table) { $table->string('secret', 100)->nullable()->change(); } ); diff --git a/database/migrations/2020_11_12_070604_changes_for_v550.php b/database/migrations/2020_11_12_070604_changes_for_v550.php index 0e4292a1ec..a08a2c9987 100644 --- a/database/migrations/2020_11_12_070604_changes_for_v550.php +++ b/database/migrations/2020_11_12_070604_changes_for_v550.php @@ -71,7 +71,7 @@ class ChangesForV550 extends Migration try { Schema::table( 'budget_transaction_journal', - function (Blueprint $table) { + static function (Blueprint $table) { if ('sqlite' !== config('database.default')) { $table->dropForeign('budget_id_foreign'); } @@ -124,7 +124,7 @@ class ChangesForV550 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void @@ -136,7 +136,7 @@ class ChangesForV550 extends Migration try { Schema::create( 'jobs', - function (Blueprint $table) { + static function (Blueprint $table) { $table->bigIncrements('id'); $table->string('queue')->index(); $table->longText('payload'); @@ -159,7 +159,7 @@ class ChangesForV550 extends Migration try { Schema::create( 'failed_jobs', - function (Blueprint $table) { + static function (Blueprint $table) { $table->bigIncrements('id'); $table->string('uuid')->unique(); $table->text('connection'); @@ -180,7 +180,7 @@ class ChangesForV550 extends Migration try { Schema::table( 'budget_transaction_journal', - function (Blueprint $table) { + static function (Blueprint $table) { if (!Schema::hasColumn('budget_transaction_journal', 'budget_limit_id')) { $table->integer('budget_limit_id', false, true)->nullable()->default(null)->after('budget_id'); $table->foreign('budget_limit_id', 'budget_id_foreign')->references('id')->on('budget_limits')->onDelete('set null'); diff --git a/database/migrations/2021_03_12_061213_changes_for_v550b2.php b/database/migrations/2021_03_12_061213_changes_for_v550b2.php index d3364a7b84..ac009e6309 100644 --- a/database/migrations/2021_03_12_061213_changes_for_v550b2.php +++ b/database/migrations/2021_03_12_061213_changes_for_v550b2.php @@ -44,7 +44,7 @@ class ChangesForV550b2 extends Migration try { Schema::table( 'recurrences_transactions', - function (Blueprint $table) { + static function (Blueprint $table) { if ('sqlite' !== config('database.default')) { $table->dropForeign('type_foreign'); } @@ -62,7 +62,7 @@ class ChangesForV550b2 extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void @@ -72,7 +72,7 @@ class ChangesForV550b2 extends Migration try { Schema::table( 'recurrences_transactions', - function (Blueprint $table) { + static function (Blueprint $table) { if (!Schema::hasColumn('recurrences_transactions', 'transaction_type_id')) { $table->integer('transaction_type_id', false, true)->nullable()->after('transaction_currency_id'); $table->foreign('transaction_type_id', 'type_foreign')->references('id')->on('transaction_types')->onDelete('set null'); diff --git a/database/migrations/2021_05_09_064644_add_ldap_columns_to_users_table.php b/database/migrations/2021_05_09_064644_add_ldap_columns_to_users_table.php index 330f76bf5f..e02c16a61c 100644 --- a/database/migrations/2021_05_09_064644_add_ldap_columns_to_users_table.php +++ b/database/migrations/2021_05_09_064644_add_ldap_columns_to_users_table.php @@ -39,7 +39,7 @@ class AddLdapColumnsToUsersTable extends Migration try { Schema::table( 'users', - function (Blueprint $table) { + static function (Blueprint $table) { $table->dropColumn(['domain']); } ); @@ -52,6 +52,7 @@ class AddLdapColumnsToUsersTable extends Migration /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { @@ -59,7 +60,7 @@ class AddLdapColumnsToUsersTable extends Migration try { Schema::table( 'users', - function (Blueprint $table) { + static function (Blueprint $table) { $table->string('domain')->nullable(); } ); diff --git a/database/migrations/2021_05_13_053836_extend_currency_info.php b/database/migrations/2021_05_13_053836_extend_currency_info.php index 962016e894..32ac1506c4 100644 --- a/database/migrations/2021_05_13_053836_extend_currency_info.php +++ b/database/migrations/2021_05_13_053836_extend_currency_info.php @@ -44,7 +44,7 @@ class ExtendCurrencyInfo extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void @@ -52,7 +52,7 @@ class ExtendCurrencyInfo extends Migration try { Schema::table( 'transaction_currencies', - function (Blueprint $table) { + static function (Blueprint $table) { $table->string('code', 51)->change(); $table->string('symbol', 51)->change(); } diff --git a/database/migrations/2021_07_05_193044_drop_tele_table.php b/database/migrations/2021_07_05_193044_drop_tele_table.php index 31016bdff2..746c40211d 100644 --- a/database/migrations/2021_07_05_193044_drop_tele_table.php +++ b/database/migrations/2021_07_05_193044_drop_tele_table.php @@ -42,7 +42,7 @@ class DropTeleTable extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void diff --git a/database/migrations/2021_08_28_073733_user_groups.php b/database/migrations/2021_08_28_073733_user_groups.php index 761eac84c0..76de049742 100644 --- a/database/migrations/2021_08_28_073733_user_groups.php +++ b/database/migrations/2021_08_28_073733_user_groups.php @@ -65,7 +65,7 @@ class UserGroups extends Migration try { Schema::table( $tableName, - function (Blueprint $table) use ($tableName) { + static function (Blueprint $table) use ($tableName) { if ('sqlite' !== config('database.default')) { $table->dropForeign(sprintf('%s_to_ugi', $tableName)); } @@ -85,7 +85,7 @@ class UserGroups extends Migration try { Schema::table( 'users', - function (Blueprint $table) { + static function (Blueprint $table) { if ('sqlite' !== config('database.default')) { $table->dropForeign('type_user_group_id'); } @@ -107,7 +107,7 @@ class UserGroups extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void @@ -178,7 +178,7 @@ class UserGroups extends Migration try { Schema::table( 'users', - function (Blueprint $table) { + static function (Blueprint $table) { if (!Schema::hasColumn('users', 'user_group_id')) { $table->bigInteger('user_group_id', false, true)->nullable(); $table->foreign('user_group_id', 'type_user_group_id')->references('id')->on('user_groups')->onDelete('set null')->onUpdate( @@ -197,7 +197,7 @@ class UserGroups extends Migration try { Schema::table( $tableName, - function (Blueprint $table) use ($tableName) { + static function (Blueprint $table) use ($tableName) { if (!Schema::hasColumn($tableName, 'user_group_id')) { $table->bigInteger('user_group_id', false, true)->nullable()->after('user_id'); $table->foreign('user_group_id', sprintf('%s_to_ugi', $tableName))->references('id')->on('user_groups')->onDelete( diff --git a/database/migrations/2021_12_27_000001_create_local_personal_access_tokens_table.php b/database/migrations/2021_12_27_000001_create_local_personal_access_tokens_table.php index 2e012562f8..7e438f922e 100644 --- a/database/migrations/2021_12_27_000001_create_local_personal_access_tokens_table.php +++ b/database/migrations/2021_12_27_000001_create_local_personal_access_tokens_table.php @@ -44,14 +44,14 @@ class CreateLocalPersonalAccessTokensTable extends Migration /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void { if (!Schema::hasTable('personal_access_tokens')) { try { - Schema::create('personal_access_tokens', function (Blueprint $table) { + Schema::create('personal_access_tokens', static function (Blueprint $table) { $table->bigIncrements('id'); $table->morphs('tokenable'); $table->string('name'); diff --git a/database/migrations/2022_08_21_104626_add_user_groups.php b/database/migrations/2022_08_21_104626_add_user_groups.php index 236db80847..222467d847 100644 --- a/database/migrations/2022_08_21_104626_add_user_groups.php +++ b/database/migrations/2022_08_21_104626_add_user_groups.php @@ -34,7 +34,7 @@ use Illuminate\Support\Facades\Schema; return new class () extends Migration { /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void @@ -42,7 +42,7 @@ return new class () extends Migration { try { Schema::table( 'currency_exchange_rates', - function (Blueprint $table) { + static function (Blueprint $table) { if (!Schema::hasColumn('currency_exchange_rates', 'user_group_id')) { $table->bigInteger('user_group_id', false, true)->nullable()->after('user_id'); $table->foreign('user_group_id', 'cer_to_ugi')->references('id')->on('user_groups')->onDelete('set null')->onUpdate('cascade'); @@ -65,7 +65,7 @@ return new class () extends Migration { try { Schema::table( 'currency_exchange_rates', - function (Blueprint $table) { + static function (Blueprint $table) { if ('sqlite' !== config('database.default')) { $table->dropForeign('cer_to_ugi'); } diff --git a/database/migrations/2022_09_18_123911_create_notifications_table.php b/database/migrations/2022_09_18_123911_create_notifications_table.php index c5a4fd1599..09e4963fa9 100644 --- a/database/migrations/2022_09_18_123911_create_notifications_table.php +++ b/database/migrations/2022_09_18_123911_create_notifications_table.php @@ -30,14 +30,14 @@ use Illuminate\Support\Facades\Schema; return new class () extends Migration { /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void { if (!Schema::hasTable('notifications')) { try { - Schema::create('notifications', function (Blueprint $table) { + Schema::create('notifications', static function (Blueprint $table) { $table->uuid('id')->primary(); $table->string('type'); $table->morphs('notifiable'); diff --git a/database/migrations/2022_10_01_074908_invited_users.php b/database/migrations/2022_10_01_074908_invited_users.php index 4a6246d4e5..c2c0de8cc8 100644 --- a/database/migrations/2022_10_01_074908_invited_users.php +++ b/database/migrations/2022_10_01_074908_invited_users.php @@ -30,14 +30,14 @@ use Illuminate\Support\Facades\Schema; return new class () extends Migration { /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void { if (!Schema::hasTable('invited_users')) { try { - Schema::create('invited_users', function (Blueprint $table) { + Schema::create('invited_users', static function (Blueprint $table) { $table->id(); $table->timestamps(); $table->integer('user_id', false, true); diff --git a/database/migrations/2022_10_01_210238_audit_log_entries.php b/database/migrations/2022_10_01_210238_audit_log_entries.php index 98d8170e72..fd64d2915c 100644 --- a/database/migrations/2022_10_01_210238_audit_log_entries.php +++ b/database/migrations/2022_10_01_210238_audit_log_entries.php @@ -30,14 +30,14 @@ use Illuminate\Support\Facades\Schema; return new class () extends Migration { /** * Run the migrations. - * + * @SuppressWarnings(PHPMD.ShortMethodName) * @return void */ public function up(): void { if (!Schema::hasTable('audit_log_entries')) { try { - Schema::create('audit_log_entries', function (Blueprint $table) { + Schema::create('audit_log_entries', static function (Blueprint $table) { $table->id(); $table->timestamps(); $table->softDeletes(); diff --git a/database/migrations/2023_08_11_192521_upgrade_og_table.php b/database/migrations/2023_08_11_192521_upgrade_og_table.php index 1285607771..1a01ed8f10 100644 --- a/database/migrations/2023_08_11_192521_upgrade_og_table.php +++ b/database/migrations/2023_08_11_192521_upgrade_og_table.php @@ -35,13 +35,14 @@ use Illuminate\Support\Facades\Schema; return new class () extends Migration { /** * Run the migrations. + * @SuppressWarnings(PHPMD.ShortMethodName) */ public function up(): void { try { Schema::table( 'object_groups', - function (Blueprint $table) { + static function (Blueprint $table) { if (!Schema::hasColumn('object_groups', 'user_group_id')) { $table->bigInteger('user_group_id', false, true)->nullable()->after('user_id'); $table->foreign('user_group_id', sprintf('%s_to_ugi', 'object_groups'))->references('id')->on('user_groups')->onDelete( @@ -64,7 +65,7 @@ return new class () extends Migration { try { Schema::table( 'object_groups', - function (Blueprint $table) { + static function (Blueprint $table) { if ('sqlite' !== config('database.default')) { $table->dropForeign(sprintf('%s_to_ugi', 'object_groups')); } diff --git a/database/migrations/2023_10_21_113213_add_currency_pivot_tables.php b/database/migrations/2023_10_21_113213_add_currency_pivot_tables.php index 213a714df6..6845c3f0f4 100644 --- a/database/migrations/2023_10_21_113213_add_currency_pivot_tables.php +++ b/database/migrations/2023_10_21_113213_add_currency_pivot_tables.php @@ -24,11 +24,13 @@ declare(strict_types=1); use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\QueryException; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class () extends Migration { /** + * @SuppressWarnings(PHPMD.ShortMethodName) * Run the migrations. */ public function up(): void @@ -36,7 +38,7 @@ return new class () extends Migration { // transaction_currency_user if (!Schema::hasTable('transaction_currency_user')) { try { - Schema::create('transaction_currency_user', function (Blueprint $table) { + Schema::create('transaction_currency_user', static function (Blueprint $table) { $table->id(); $table->timestamps(); $table->integer('user_id', false, true); @@ -55,7 +57,7 @@ return new class () extends Migration { // transaction_currency_user_group if (!Schema::hasTable('transaction_currency_user_group')) { try { - Schema::create('transaction_currency_user_group', function (Blueprint $table) { + Schema::create('transaction_currency_user_group', static function (Blueprint $table) { $table->id(); $table->timestamps(); $table->bigInteger('user_group_id', false, true); diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 8c18621566..bdce19f553 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -33,7 +33,7 @@ class DatabaseSeeder extends Seeder /** * Run the database seeds. */ - public function run() + public function run(): void { $this->call(AccountTypeSeeder::class); $this->call(TransactionCurrencySeeder::class); diff --git a/database/seeders/ExchangeRateSeeder.php b/database/seeders/ExchangeRateSeeder.php index b5f3ff7350..164473461e 100644 --- a/database/seeders/ExchangeRateSeeder.php +++ b/database/seeders/ExchangeRateSeeder.php @@ -28,15 +28,12 @@ use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\TransactionCurrency; use FireflyIII\User; use Illuminate\Database\Seeder; -use Illuminate\Support\Collection; /** * Class ExchangeRateSeeder */ class ExchangeRateSeeder extends Seeder { - private Collection $users; - /** * @return void */ @@ -111,6 +108,7 @@ class ExchangeRateSeeder extends Seeder * @param float $rate * * @return void + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ private function addRate(User $user, TransactionCurrency $from, TransactionCurrency $to, string $date, float $rate): void { diff --git a/database/seeders/LinkTypeSeeder.php b/database/seeders/LinkTypeSeeder.php index 45a68d1149..e115bb2ad8 100644 --- a/database/seeders/LinkTypeSeeder.php +++ b/database/seeders/LinkTypeSeeder.php @@ -32,7 +32,10 @@ use PDOException; */ class LinkTypeSeeder extends Seeder { - public function run() + /** + * @return void + */ + public function run(): void { $types = [ [ diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php index 019abbaa48..29ff77d72f 100644 --- a/database/seeders/PermissionSeeder.php +++ b/database/seeders/PermissionSeeder.php @@ -32,7 +32,7 @@ use PDOException; */ class PermissionSeeder extends Seeder { - public function run() + public function run(): void { $roles = [ [ diff --git a/database/seeders/TransactionCurrencySeeder.php b/database/seeders/TransactionCurrencySeeder.php index 21a691f48d..b13752fad8 100644 --- a/database/seeders/TransactionCurrencySeeder.php +++ b/database/seeders/TransactionCurrencySeeder.php @@ -34,7 +34,7 @@ class TransactionCurrencySeeder extends Seeder { /** */ - public function run() + public function run(): void { $currencies = []; // european currencies diff --git a/database/seeders/TransactionTypeSeeder.php b/database/seeders/TransactionTypeSeeder.php index 517b5d15ed..da0db936fd 100644 --- a/database/seeders/TransactionTypeSeeder.php +++ b/database/seeders/TransactionTypeSeeder.php @@ -32,7 +32,7 @@ use PDOException; */ class TransactionTypeSeeder extends Seeder { - public function run() + public function run(): void { $types = [ TransactionType::WITHDRAWAL, diff --git a/package-lock.json b/package-lock.json index 5bb3265feb..947ae4e8d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.4.0", "@popperjs/core": "^2.11.8", - "alpinejs": "^3.13.2", + "alpinejs": "^3.13.3", "bootstrap": "^5.3.0", "bootstrap5-autocomplete": "^1.1.22", "chart.js": "^4.4.0", @@ -18,7 +18,7 @@ "store": "^2.0.12" }, "devDependencies": { - "axios": "^1.5.1", + "axios": "^1.6.1", "laravel-vite-plugin": "^0.8.1", "sass": "^1.69.4", "vite": "^4.5.1", @@ -26,9 +26,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz", + "integrity": "sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -389,9 +389,9 @@ } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz", - "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.1.tgz", + "integrity": "sha512-CNy5vSwN3fsUStPRLX7fUYojyuzoEMSXPl7zSLJ8TgtRfjv24LOnOWKT2zYwaHZCJGkdyRnTmstR0P+Ah503Gw==", "hasInstallScript": true, "engines": { "node": ">=6" @@ -425,9 +425,9 @@ "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==" }, "node_modules/alpinejs": { - "version": "3.13.2", - "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.2.tgz", - "integrity": "sha512-WzojeeN082kLZznGI1HAuP8yFJSWqJ1fGdz2mUjj45H4y0XwToE7fFqtI3mCPRR+BpcSbxT/NL+FyPnYAWSltw==", + "version": "3.13.3", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.3.tgz", + "integrity": "sha512-WZ6WQjkAOl+WdW/jukzNHq9zHFDNKmkk/x6WF7WdyNDD6woinrfXCVsZXm0galjbco+pEpYmJLtwlZwcOfIVdg==", "dependencies": { "@vue/reactivity": "~3.1.1" } @@ -452,9 +452,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dev": true, "dependencies": { "follow-redirects": "^1.15.0", @@ -498,9 +498,9 @@ } }, "node_modules/bootstrap5-autocomplete": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/bootstrap5-autocomplete/-/bootstrap5-autocomplete-1.1.23.tgz", - "integrity": "sha512-jmqk7muFampKGPNzp6ABCAZWB38rfRpUyqx1vqn4Wssj88X1xODQGI+hnPAPGfePIjMLEK0Xhd1Is4kva1PUrg==" + "version": "1.1.25", + "resolved": "https://registry.npmjs.org/bootstrap5-autocomplete/-/bootstrap5-autocomplete-1.1.25.tgz", + "integrity": "sha512-6Z7vzlVBJduPUi7U1MPRBzoXmJf8ob9tGcUNxxos6qU1bbjPX7Li30r1Dhtk55hSBfPmVuN7p6zahF7G38xtWA==" }, "node_modules/braces": { "version": "3.0.2", @@ -515,9 +515,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", - "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz", + "integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -820,9 +820,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, "funding": [ { @@ -865,9 +865,9 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", + "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", "dev": true, "funding": [ { @@ -884,7 +884,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -1033,16 +1033,13 @@ } }, "node_modules/vite-plugin-full-reload": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.0.5.tgz", - "integrity": "sha512-kVZFDFWr0DxiHn6MuDVTQf7gnWIdETGlZh0hvTiMXzRN80vgF4PKbONSq8U1d0WtHsKaFODTQgJeakLacoPZEQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.1.0.tgz", + "integrity": "sha512-3cObNDzX6DdfhD9E7kf6w2mNunFpD7drxyNgHLw+XwIYAgb+Xt16SEXo0Up4VH+TMf3n+DSVJZtW2POBGcBYAA==", "dev": true, "dependencies": { "picocolors": "^1.0.0", "picomatch": "^2.3.1" - }, - "peerDependencies": { - "vite": "^2 || ^3 || ^4" } }, "node_modules/vite-plugin-manifest-sri": { diff --git a/package.json b/package.json index 4ba15d5773..5a098f9429 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "build": "vite build" }, "devDependencies": { - "axios": "^1.5.1", + "axios": "^1.6.1", "laravel-vite-plugin": "^0.8.1", "sass": "^1.69.4", "vite": "^4.5.1", @@ -15,7 +15,7 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.4.0", "@popperjs/core": "^2.11.8", - "alpinejs": "^3.13.2", + "alpinejs": "^3.13.3", "bootstrap": "^5.3.0", "bootstrap5-autocomplete": "^1.1.22", "chart.js": "^4.4.0", diff --git a/phpunit.xml b/phpunit.xml index d5008da502..5979cacc3f 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,23 +1,4 @@ - - - - - + diff --git a/public/build/assets/app-28a195fd.css b/public/build/assets/app-fb7b26ec.css similarity index 75% rename from public/build/assets/app-28a195fd.css rename to public/build/assets/app-fb7b26ec.css index a68b491e8d..64cd198444 100644 --- a/public/build/assets/app-28a195fd.css +++ b/public/build/assets/app-fb7b26ec.css @@ -16,8 +16,8 @@ * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . - */.fa{font-family:var(--fa-style-family, "Font Awesome 6 Free");font-weight:var(--fa-style, 900)}.fa,.fa-classic,.fa-sharp,.fas,.fa-solid,.far,.fa-regular,.fab,.fa-brands{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display, inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fas,.fa-classic,.fa-solid,.far,.fa-regular{font-family:"Font Awesome 6 Free"}.fab,.fa-brands{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.0833333337em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.0714285718em;vertical-align:.0535714295em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.0416666682em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin, 2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em) * -1);position:absolute;text-align:center;width:var(--fa-li-width, 2em);line-height:inherit}.fa-border{border-color:var(--fa-border-color, #eee);border-radius:var(--fa-border-radius, .1em);border-style:var(--fa-border-style, solid);border-width:var(--fa-border-width, .08em);padding:var(--fa-border-padding, .2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin, .3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin, .3em)}.fa-beat{animation-name:fa-beat;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, ease-in-out)}.fa-bounce{animation-name:fa-bounce;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, cubic-bezier(.28, .84, .42, 1))}.fa-fade{animation-name:fa-fade;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, cubic-bezier(.4, 0, .6, 1))}.fa-beat-fade{animation-name:fa-beat-fade;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, cubic-bezier(.4, 0, .6, 1))}.fa-flip{animation-name:fa-flip;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, ease-in-out)}.fa-shake{animation-name:fa-shake;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, linear)}.fa-spin{animation-name:fa-spin;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 2s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, linear)}.fa-spin-reverse{--fa-animation-direction: reverse}.fa-pulse,.fa-spin-pulse{animation-name:fa-spin;animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, steps(8))}@media (prefers-reduced-motion: reduce){.fa-beat,.fa-bounce,.fa-fade,.fa-beat-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{animation-delay:-1ms;animation-duration:1ms;animation-iteration-count:1;transition-delay:0s;transition-duration:0s}}@keyframes fa-beat{0%,90%{transform:scale(1)}45%{transform:scale(var(--fa-beat-scale, 1.25))}}@keyframes fa-bounce{0%{transform:scale(1) translateY(0)}10%{transform:scale(var(--fa-bounce-start-scale-x, 1.1),var(--fa-bounce-start-scale-y, .9)) translateY(0)}30%{transform:scale(var(--fa-bounce-jump-scale-x, .9),var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -.5em))}50%{transform:scale(var(--fa-bounce-land-scale-x, 1.05),var(--fa-bounce-land-scale-y, .95)) translateY(0)}57%{transform:scale(1) translateY(var(--fa-bounce-rebound, -.125em))}64%{transform:scale(1) translateY(0)}to{transform:scale(1) translateY(0)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity, .4)}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity, .4);transform:scale(1)}50%{opacity:1;transform:scale(var(--fa-beat-fade-scale, 1.125))}}@keyframes fa-flip{50%{transform:rotate3d(var(--fa-flip-x, 0),var(--fa-flip-y, 1),var(--fa-flip-z, 0),var(--fa-flip-angle, -180deg))}}@keyframes fa-shake{0%{transform:rotate(-15deg)}4%{transform:rotate(15deg)}8%,24%{transform:rotate(-18deg)}12%,28%{transform:rotate(18deg)}16%{transform:rotate(-22deg)}20%{transform:rotate(22deg)}32%{transform:rotate(-12deg)}36%{transform:rotate(12deg)}40%,to{transform:rotate(0)}}@keyframes fa-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.fa-rotate-90{transform:rotate(90deg)}.fa-rotate-180{transform:rotate(180deg)}.fa-rotate-270{transform:rotate(270deg)}.fa-flip-horizontal{transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}.fa-rotate-by{transform:rotate(var(--fa-rotate-angle, none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index, auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse, #fff)}.fa-0:before{content:"0"}.fa-1:before{content:"1"}.fa-2:before{content:"2"}.fa-3:before{content:"3"}.fa-4:before{content:"4"}.fa-5:before{content:"5"}.fa-6:before{content:"6"}.fa-7:before{content:"7"}.fa-8:before{content:"8"}.fa-9:before{content:"9"}.fa-fill-drip:before{content:""}.fa-arrows-to-circle:before{content:""}.fa-circle-chevron-right:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-at:before{content:"@"}.fa-trash-can:before{content:""}.fa-trash-alt:before{content:""}.fa-text-height:before{content:""}.fa-user-xmark:before{content:""}.fa-user-times:before{content:""}.fa-stethoscope:before{content:""}.fa-message:before{content:""}.fa-comment-alt:before{content:""}.fa-info:before{content:""}.fa-down-left-and-up-right-to-center:before{content:""}.fa-compress-alt:before{content:""}.fa-explosion:before{content:""}.fa-file-lines:before{content:""}.fa-file-alt:before{content:""}.fa-file-text:before{content:""}.fa-wave-square:before{content:""}.fa-ring:before{content:""}.fa-building-un:before{content:""}.fa-dice-three:before{content:""}.fa-calendar-days:before{content:""}.fa-calendar-alt:before{content:""}.fa-anchor-circle-check:before{content:""}.fa-building-circle-arrow-right:before{content:""}.fa-volleyball:before{content:""}.fa-volleyball-ball:before{content:""}.fa-arrows-up-to-line:before{content:""}.fa-sort-down:before{content:""}.fa-sort-desc:before{content:""}.fa-circle-minus:before{content:""}.fa-minus-circle:before{content:""}.fa-door-open:before{content:""}.fa-right-from-bracket:before{content:""}.fa-sign-out-alt:before{content:""}.fa-atom:before{content:""}.fa-soap:before{content:""}.fa-icons:before{content:""}.fa-heart-music-camera-bolt:before{content:""}.fa-microphone-lines-slash:before{content:""}.fa-microphone-alt-slash:before{content:""}.fa-bridge-circle-check:before{content:""}.fa-pump-medical:before{content:""}.fa-fingerprint:before{content:""}.fa-hand-point-right:before{content:""}.fa-magnifying-glass-location:before{content:""}.fa-search-location:before{content:""}.fa-forward-step:before{content:""}.fa-step-forward:before{content:""}.fa-face-smile-beam:before{content:""}.fa-smile-beam:before{content:""}.fa-flag-checkered:before{content:""}.fa-football:before{content:""}.fa-football-ball:before{content:""}.fa-school-circle-exclamation:before{content:""}.fa-crop:before{content:""}.fa-angles-down:before{content:""}.fa-angle-double-down:before{content:""}.fa-users-rectangle:before{content:""}.fa-people-roof:before{content:""}.fa-people-line:before{content:""}.fa-beer-mug-empty:before{content:""}.fa-beer:before{content:""}.fa-diagram-predecessor:before{content:""}.fa-arrow-up-long:before{content:""}.fa-long-arrow-up:before{content:""}.fa-fire-flame-simple:before{content:""}.fa-burn:before{content:""}.fa-person:before{content:""}.fa-male:before{content:""}.fa-laptop:before{content:""}.fa-file-csv:before{content:""}.fa-menorah:before{content:""}.fa-truck-plane:before{content:""}.fa-record-vinyl:before{content:""}.fa-face-grin-stars:before{content:""}.fa-grin-stars:before{content:""}.fa-bong:before{content:""}.fa-spaghetti-monster-flying:before{content:""}.fa-pastafarianism:before{content:""}.fa-arrow-down-up-across-line:before{content:""}.fa-spoon:before{content:""}.fa-utensil-spoon:before{content:""}.fa-jar-wheat:before{content:""}.fa-envelopes-bulk:before{content:""}.fa-mail-bulk:before{content:""}.fa-file-circle-exclamation:before{content:""}.fa-circle-h:before{content:""}.fa-hospital-symbol:before{content:""}.fa-pager:before{content:""}.fa-address-book:before{content:""}.fa-contact-book:before{content:""}.fa-strikethrough:before{content:""}.fa-k:before{content:"K"}.fa-landmark-flag:before{content:""}.fa-pencil:before{content:""}.fa-pencil-alt:before{content:""}.fa-backward:before{content:""}.fa-caret-right:before{content:""}.fa-comments:before{content:""}.fa-paste:before{content:""}.fa-file-clipboard:before{content:""}.fa-code-pull-request:before{content:""}.fa-clipboard-list:before{content:""}.fa-truck-ramp-box:before{content:""}.fa-truck-loading:before{content:""}.fa-user-check:before{content:""}.fa-vial-virus:before{content:""}.fa-sheet-plastic:before{content:""}.fa-blog:before{content:""}.fa-user-ninja:before{content:""}.fa-person-arrow-up-from-line:before{content:""}.fa-scroll-torah:before{content:""}.fa-torah:before{content:""}.fa-broom-ball:before{content:""}.fa-quidditch:before{content:""}.fa-quidditch-broom-ball:before{content:""}.fa-toggle-off:before{content:""}.fa-box-archive:before{content:""}.fa-archive:before{content:""}.fa-person-drowning:before{content:""}.fa-arrow-down-9-1:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-sort-numeric-down-alt:before{content:""}.fa-face-grin-tongue-squint:before{content:""}.fa-grin-tongue-squint:before{content:""}.fa-spray-can:before{content:""}.fa-truck-monster:before{content:""}.fa-w:before{content:"W"}.fa-earth-africa:before{content:""}.fa-globe-africa:before{content:""}.fa-rainbow:before{content:""}.fa-circle-notch:before{content:""}.fa-tablet-screen-button:before{content:""}.fa-tablet-alt:before{content:""}.fa-paw:before{content:""}.fa-cloud:before{content:""}.fa-trowel-bricks:before{content:""}.fa-face-flushed:before{content:""}.fa-flushed:before{content:""}.fa-hospital-user:before{content:""}.fa-tent-arrow-left-right:before{content:""}.fa-gavel:before{content:""}.fa-legal:before{content:""}.fa-binoculars:before{content:""}.fa-microphone-slash:before{content:""}.fa-box-tissue:before{content:""}.fa-motorcycle:before{content:""}.fa-bell-concierge:before{content:""}.fa-concierge-bell:before{content:""}.fa-pen-ruler:before{content:""}.fa-pencil-ruler:before{content:""}.fa-people-arrows:before{content:""}.fa-people-arrows-left-right:before{content:""}.fa-mars-and-venus-burst:before{content:""}.fa-square-caret-right:before{content:""}.fa-caret-square-right:before{content:""}.fa-scissors:before{content:""}.fa-cut:before{content:""}.fa-sun-plant-wilt:before{content:""}.fa-toilets-portable:before{content:""}.fa-hockey-puck:before{content:""}.fa-table:before{content:""}.fa-magnifying-glass-arrow-right:before{content:""}.fa-tachograph-digital:before{content:""}.fa-digital-tachograph:before{content:""}.fa-users-slash:before{content:""}.fa-clover:before{content:""}.fa-reply:before{content:""}.fa-mail-reply:before{content:""}.fa-star-and-crescent:before{content:""}.fa-house-fire:before{content:""}.fa-square-minus:before{content:""}.fa-minus-square:before{content:""}.fa-helicopter:before{content:""}.fa-compass:before{content:""}.fa-square-caret-down:before{content:""}.fa-caret-square-down:before{content:""}.fa-file-circle-question:before{content:""}.fa-laptop-code:before{content:""}.fa-swatchbook:before{content:""}.fa-prescription-bottle:before{content:""}.fa-bars:before{content:""}.fa-navicon:before{content:""}.fa-people-group:before{content:""}.fa-hourglass-end:before{content:""}.fa-hourglass-3:before{content:""}.fa-heart-crack:before{content:""}.fa-heart-broken:before{content:""}.fa-square-up-right:before{content:""}.fa-external-link-square-alt:before{content:""}.fa-face-kiss-beam:before{content:""}.fa-kiss-beam:before{content:""}.fa-film:before{content:""}.fa-ruler-horizontal:before{content:""}.fa-people-robbery:before{content:""}.fa-lightbulb:before{content:""}.fa-caret-left:before{content:""}.fa-circle-exclamation:before{content:""}.fa-exclamation-circle:before{content:""}.fa-school-circle-xmark:before{content:""}.fa-arrow-right-from-bracket:before{content:""}.fa-sign-out:before{content:""}.fa-circle-chevron-down:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-unlock-keyhole:before{content:""}.fa-unlock-alt:before{content:""}.fa-cloud-showers-heavy:before{content:""}.fa-headphones-simple:before{content:""}.fa-headphones-alt:before{content:""}.fa-sitemap:before{content:""}.fa-circle-dollar-to-slot:before{content:""}.fa-donate:before{content:""}.fa-memory:before{content:""}.fa-road-spikes:before{content:""}.fa-fire-burner:before{content:""}.fa-flag:before{content:""}.fa-hanukiah:before{content:""}.fa-feather:before{content:""}.fa-volume-low:before{content:""}.fa-volume-down:before{content:""}.fa-comment-slash:before{content:""}.fa-cloud-sun-rain:before{content:""}.fa-compress:before{content:""}.fa-wheat-awn:before{content:""}.fa-wheat-alt:before{content:""}.fa-ankh:before{content:""}.fa-hands-holding-child:before{content:""}.fa-asterisk:before{content:"*"}.fa-square-check:before{content:""}.fa-check-square:before{content:""}.fa-peseta-sign:before{content:""}.fa-heading:before{content:""}.fa-header:before{content:""}.fa-ghost:before{content:""}.fa-list:before{content:""}.fa-list-squares:before{content:""}.fa-square-phone-flip:before{content:""}.fa-phone-square-alt:before{content:""}.fa-cart-plus:before{content:""}.fa-gamepad:before{content:""}.fa-circle-dot:before{content:""}.fa-dot-circle:before{content:""}.fa-face-dizzy:before{content:""}.fa-dizzy:before{content:""}.fa-egg:before{content:""}.fa-house-medical-circle-xmark:before{content:""}.fa-campground:before{content:""}.fa-folder-plus:before{content:""}.fa-futbol:before{content:""}.fa-futbol-ball:before{content:""}.fa-soccer-ball:before{content:""}.fa-paintbrush:before{content:""}.fa-paint-brush:before{content:""}.fa-lock:before{content:""}.fa-gas-pump:before{content:""}.fa-hot-tub-person:before{content:""}.fa-hot-tub:before{content:""}.fa-map-location:before{content:""}.fa-map-marked:before{content:""}.fa-house-flood-water:before{content:""}.fa-tree:before{content:""}.fa-bridge-lock:before{content:""}.fa-sack-dollar:before{content:""}.fa-pen-to-square:before{content:""}.fa-edit:before{content:""}.fa-car-side:before{content:""}.fa-share-nodes:before{content:""}.fa-share-alt:before{content:""}.fa-heart-circle-minus:before{content:""}.fa-hourglass-half:before{content:""}.fa-hourglass-2:before{content:""}.fa-microscope:before{content:""}.fa-sink:before{content:""}.fa-bag-shopping:before{content:""}.fa-shopping-bag:before{content:""}.fa-arrow-down-z-a:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-alpha-down-alt:before{content:""}.fa-mitten:before{content:""}.fa-person-rays:before{content:""}.fa-users:before{content:""}.fa-eye-slash:before{content:""}.fa-flask-vial:before{content:""}.fa-hand:before{content:""}.fa-hand-paper:before{content:""}.fa-om:before{content:""}.fa-worm:before{content:""}.fa-house-circle-xmark:before{content:""}.fa-plug:before{content:""}.fa-chevron-up:before{content:""}.fa-hand-spock:before{content:""}.fa-stopwatch:before{content:""}.fa-face-kiss:before{content:""}.fa-kiss:before{content:""}.fa-bridge-circle-xmark:before{content:""}.fa-face-grin-tongue:before{content:""}.fa-grin-tongue:before{content:""}.fa-chess-bishop:before{content:""}.fa-face-grin-wink:before{content:""}.fa-grin-wink:before{content:""}.fa-ear-deaf:before{content:""}.fa-deaf:before{content:""}.fa-deafness:before{content:""}.fa-hard-of-hearing:before{content:""}.fa-road-circle-check:before{content:""}.fa-dice-five:before{content:""}.fa-square-rss:before{content:""}.fa-rss-square:before{content:""}.fa-land-mine-on:before{content:""}.fa-i-cursor:before{content:""}.fa-stamp:before{content:""}.fa-stairs:before{content:""}.fa-i:before{content:"I"}.fa-hryvnia-sign:before{content:""}.fa-hryvnia:before{content:""}.fa-pills:before{content:""}.fa-face-grin-wide:before{content:""}.fa-grin-alt:before{content:""}.fa-tooth:before{content:""}.fa-v:before{content:"V"}.fa-bangladeshi-taka-sign:before{content:""}.fa-bicycle:before{content:""}.fa-staff-snake:before{content:""}.fa-rod-asclepius:before{content:""}.fa-rod-snake:before{content:""}.fa-staff-aesculapius:before{content:""}.fa-head-side-cough-slash:before{content:""}.fa-truck-medical:before{content:""}.fa-ambulance:before{content:""}.fa-wheat-awn-circle-exclamation:before{content:""}.fa-snowman:before{content:""}.fa-mortar-pestle:before{content:""}.fa-road-barrier:before{content:""}.fa-school:before{content:""}.fa-igloo:before{content:""}.fa-joint:before{content:""}.fa-angle-right:before{content:""}.fa-horse:before{content:""}.fa-q:before{content:"Q"}.fa-g:before{content:"G"}.fa-notes-medical:before{content:""}.fa-temperature-half:before{content:""}.fa-temperature-2:before{content:""}.fa-thermometer-2:before{content:""}.fa-thermometer-half:before{content:""}.fa-dong-sign:before{content:""}.fa-capsules:before{content:""}.fa-poo-storm:before{content:""}.fa-poo-bolt:before{content:""}.fa-face-frown-open:before{content:""}.fa-frown-open:before{content:""}.fa-hand-point-up:before{content:""}.fa-money-bill:before{content:""}.fa-bookmark:before{content:""}.fa-align-justify:before{content:""}.fa-umbrella-beach:before{content:""}.fa-helmet-un:before{content:""}.fa-bullseye:before{content:""}.fa-bacon:before{content:""}.fa-hand-point-down:before{content:""}.fa-arrow-up-from-bracket:before{content:""}.fa-folder:before{content:""}.fa-folder-blank:before{content:""}.fa-file-waveform:before{content:""}.fa-file-medical-alt:before{content:""}.fa-radiation:before{content:""}.fa-chart-simple:before{content:""}.fa-mars-stroke:before{content:""}.fa-vial:before{content:""}.fa-gauge:before{content:""}.fa-dashboard:before{content:""}.fa-gauge-med:before{content:""}.fa-tachometer-alt-average:before{content:""}.fa-wand-magic-sparkles:before{content:""}.fa-magic-wand-sparkles:before{content:""}.fa-e:before{content:"E"}.fa-pen-clip:before{content:""}.fa-pen-alt:before{content:""}.fa-bridge-circle-exclamation:before{content:""}.fa-user:before{content:""}.fa-school-circle-check:before{content:""}.fa-dumpster:before{content:""}.fa-van-shuttle:before{content:""}.fa-shuttle-van:before{content:""}.fa-building-user:before{content:""}.fa-square-caret-left:before{content:""}.fa-caret-square-left:before{content:""}.fa-highlighter:before{content:""}.fa-key:before{content:""}.fa-bullhorn:before{content:""}.fa-globe:before{content:""}.fa-synagogue:before{content:""}.fa-person-half-dress:before{content:""}.fa-road-bridge:before{content:""}.fa-location-arrow:before{content:""}.fa-c:before{content:"C"}.fa-tablet-button:before{content:""}.fa-building-lock:before{content:""}.fa-pizza-slice:before{content:""}.fa-money-bill-wave:before{content:""}.fa-chart-area:before{content:""}.fa-area-chart:before{content:""}.fa-house-flag:before{content:""}.fa-person-circle-minus:before{content:""}.fa-ban:before{content:""}.fa-cancel:before{content:""}.fa-camera-rotate:before{content:""}.fa-spray-can-sparkles:before{content:""}.fa-air-freshener:before{content:""}.fa-star:before{content:""}.fa-repeat:before{content:""}.fa-cross:before{content:""}.fa-box:before{content:""}.fa-venus-mars:before{content:""}.fa-arrow-pointer:before{content:""}.fa-mouse-pointer:before{content:""}.fa-maximize:before{content:""}.fa-expand-arrows-alt:before{content:""}.fa-charging-station:before{content:""}.fa-shapes:before{content:""}.fa-triangle-circle-square:before{content:""}.fa-shuffle:before{content:""}.fa-random:before{content:""}.fa-person-running:before{content:""}.fa-running:before{content:""}.fa-mobile-retro:before{content:""}.fa-grip-lines-vertical:before{content:""}.fa-spider:before{content:""}.fa-hands-bound:before{content:""}.fa-file-invoice-dollar:before{content:""}.fa-plane-circle-exclamation:before{content:""}.fa-x-ray:before{content:""}.fa-spell-check:before{content:""}.fa-slash:before{content:""}.fa-computer-mouse:before{content:""}.fa-mouse:before{content:""}.fa-arrow-right-to-bracket:before{content:""}.fa-sign-in:before{content:""}.fa-shop-slash:before{content:""}.fa-store-alt-slash:before{content:""}.fa-server:before{content:""}.fa-virus-covid-slash:before{content:""}.fa-shop-lock:before{content:""}.fa-hourglass-start:before{content:""}.fa-hourglass-1:before{content:""}.fa-blender-phone:before{content:""}.fa-building-wheat:before{content:""}.fa-person-breastfeeding:before{content:""}.fa-right-to-bracket:before{content:""}.fa-sign-in-alt:before{content:""}.fa-venus:before{content:""}.fa-passport:before{content:""}.fa-heart-pulse:before{content:""}.fa-heartbeat:before{content:""}.fa-people-carry-box:before{content:""}.fa-people-carry:before{content:""}.fa-temperature-high:before{content:""}.fa-microchip:before{content:""}.fa-crown:before{content:""}.fa-weight-hanging:before{content:""}.fa-xmarks-lines:before{content:""}.fa-file-prescription:before{content:""}.fa-weight-scale:before{content:""}.fa-weight:before{content:""}.fa-user-group:before{content:""}.fa-user-friends:before{content:""}.fa-arrow-up-a-z:before{content:""}.fa-sort-alpha-up:before{content:""}.fa-chess-knight:before{content:""}.fa-face-laugh-squint:before{content:""}.fa-laugh-squint:before{content:""}.fa-wheelchair:before{content:""}.fa-circle-arrow-up:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-toggle-on:before{content:""}.fa-person-walking:before{content:""}.fa-walking:before{content:""}.fa-l:before{content:"L"}.fa-fire:before{content:""}.fa-bed-pulse:before{content:""}.fa-procedures:before{content:""}.fa-shuttle-space:before{content:""}.fa-space-shuttle:before{content:""}.fa-face-laugh:before{content:""}.fa-laugh:before{content:""}.fa-folder-open:before{content:""}.fa-heart-circle-plus:before{content:""}.fa-code-fork:before{content:""}.fa-city:before{content:""}.fa-microphone-lines:before{content:""}.fa-microphone-alt:before{content:""}.fa-pepper-hot:before{content:""}.fa-unlock:before{content:""}.fa-colon-sign:before{content:""}.fa-headset:before{content:""}.fa-store-slash:before{content:""}.fa-road-circle-xmark:before{content:""}.fa-user-minus:before{content:""}.fa-mars-stroke-up:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-champagne-glasses:before{content:""}.fa-glass-cheers:before{content:""}.fa-clipboard:before{content:""}.fa-house-circle-exclamation:before{content:""}.fa-file-arrow-up:before{content:""}.fa-file-upload:before{content:""}.fa-wifi:before{content:""}.fa-wifi-3:before{content:""}.fa-wifi-strong:before{content:""}.fa-bath:before{content:""}.fa-bathtub:before{content:""}.fa-underline:before{content:""}.fa-user-pen:before{content:""}.fa-user-edit:before{content:""}.fa-signature:before{content:""}.fa-stroopwafel:before{content:""}.fa-bold:before{content:""}.fa-anchor-lock:before{content:""}.fa-building-ngo:before{content:""}.fa-manat-sign:before{content:""}.fa-not-equal:before{content:""}.fa-border-top-left:before{content:""}.fa-border-style:before{content:""}.fa-map-location-dot:before{content:""}.fa-map-marked-alt:before{content:""}.fa-jedi:before{content:""}.fa-square-poll-vertical:before{content:""}.fa-poll:before{content:""}.fa-mug-hot:before{content:""}.fa-car-battery:before{content:""}.fa-battery-car:before{content:""}.fa-gift:before{content:""}.fa-dice-two:before{content:""}.fa-chess-queen:before{content:""}.fa-glasses:before{content:""}.fa-chess-board:before{content:""}.fa-building-circle-check:before{content:""}.fa-person-chalkboard:before{content:""}.fa-mars-stroke-right:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-hand-back-fist:before{content:""}.fa-hand-rock:before{content:""}.fa-square-caret-up:before{content:""}.fa-caret-square-up:before{content:""}.fa-cloud-showers-water:before{content:""}.fa-chart-bar:before{content:""}.fa-bar-chart:before{content:""}.fa-hands-bubbles:before{content:""}.fa-hands-wash:before{content:""}.fa-less-than-equal:before{content:""}.fa-train:before{content:""}.fa-eye-low-vision:before{content:""}.fa-low-vision:before{content:""}.fa-crow:before{content:""}.fa-sailboat:before{content:""}.fa-window-restore:before{content:""}.fa-square-plus:before{content:""}.fa-plus-square:before{content:""}.fa-torii-gate:before{content:""}.fa-frog:before{content:""}.fa-bucket:before{content:""}.fa-image:before{content:""}.fa-microphone:before{content:""}.fa-cow:before{content:""}.fa-caret-up:before{content:""}.fa-screwdriver:before{content:""}.fa-folder-closed:before{content:""}.fa-house-tsunami:before{content:""}.fa-square-nfi:before{content:""}.fa-arrow-up-from-ground-water:before{content:""}.fa-martini-glass:before{content:""}.fa-glass-martini-alt:before{content:""}.fa-rotate-left:before{content:""}.fa-rotate-back:before{content:""}.fa-rotate-backward:before{content:""}.fa-undo-alt:before{content:""}.fa-table-columns:before{content:""}.fa-columns:before{content:""}.fa-lemon:before{content:""}.fa-head-side-mask:before{content:""}.fa-handshake:before{content:""}.fa-gem:before{content:""}.fa-dolly:before{content:""}.fa-dolly-box:before{content:""}.fa-smoking:before{content:""}.fa-minimize:before{content:""}.fa-compress-arrows-alt:before{content:""}.fa-monument:before{content:""}.fa-snowplow:before{content:""}.fa-angles-right:before{content:""}.fa-angle-double-right:before{content:""}.fa-cannabis:before{content:""}.fa-circle-play:before{content:""}.fa-play-circle:before{content:""}.fa-tablets:before{content:""}.fa-ethernet:before{content:""}.fa-euro-sign:before{content:""}.fa-eur:before{content:""}.fa-euro:before{content:""}.fa-chair:before{content:""}.fa-circle-check:before{content:""}.fa-check-circle:before{content:""}.fa-circle-stop:before{content:""}.fa-stop-circle:before{content:""}.fa-compass-drafting:before{content:""}.fa-drafting-compass:before{content:""}.fa-plate-wheat:before{content:""}.fa-icicles:before{content:""}.fa-person-shelter:before{content:""}.fa-neuter:before{content:""}.fa-id-badge:before{content:""}.fa-marker:before{content:""}.fa-face-laugh-beam:before{content:""}.fa-laugh-beam:before{content:""}.fa-helicopter-symbol:before{content:""}.fa-universal-access:before{content:""}.fa-circle-chevron-up:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-lari-sign:before{content:""}.fa-volcano:before{content:""}.fa-person-walking-dashed-line-arrow-right:before{content:""}.fa-sterling-sign:before{content:""}.fa-gbp:before{content:""}.fa-pound-sign:before{content:""}.fa-viruses:before{content:""}.fa-square-person-confined:before{content:""}.fa-user-tie:before{content:""}.fa-arrow-down-long:before{content:""}.fa-long-arrow-down:before{content:""}.fa-tent-arrow-down-to-line:before{content:""}.fa-certificate:before{content:""}.fa-reply-all:before{content:""}.fa-mail-reply-all:before{content:""}.fa-suitcase:before{content:""}.fa-person-skating:before{content:""}.fa-skating:before{content:""}.fa-filter-circle-dollar:before{content:""}.fa-funnel-dollar:before{content:""}.fa-camera-retro:before{content:""}.fa-circle-arrow-down:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-file-import:before{content:""}.fa-arrow-right-to-file:before{content:""}.fa-square-arrow-up-right:before{content:""}.fa-external-link-square:before{content:""}.fa-box-open:before{content:""}.fa-scroll:before{content:""}.fa-spa:before{content:""}.fa-location-pin-lock:before{content:""}.fa-pause:before{content:""}.fa-hill-avalanche:before{content:""}.fa-temperature-empty:before{content:""}.fa-temperature-0:before{content:""}.fa-thermometer-0:before{content:""}.fa-thermometer-empty:before{content:""}.fa-bomb:before{content:""}.fa-registered:before{content:""}.fa-address-card:before{content:""}.fa-contact-card:before{content:""}.fa-vcard:before{content:""}.fa-scale-unbalanced-flip:before{content:""}.fa-balance-scale-right:before{content:""}.fa-subscript:before{content:""}.fa-diamond-turn-right:before{content:""}.fa-directions:before{content:""}.fa-burst:before{content:""}.fa-house-laptop:before{content:""}.fa-laptop-house:before{content:""}.fa-face-tired:before{content:""}.fa-tired:before{content:""}.fa-money-bills:before{content:""}.fa-smog:before{content:""}.fa-crutch:before{content:""}.fa-cloud-arrow-up:before{content:""}.fa-cloud-upload:before{content:""}.fa-cloud-upload-alt:before{content:""}.fa-palette:before{content:""}.fa-arrows-turn-right:before{content:""}.fa-vest:before{content:""}.fa-ferry:before{content:""}.fa-arrows-down-to-people:before{content:""}.fa-seedling:before{content:""}.fa-sprout:before{content:""}.fa-left-right:before{content:""}.fa-arrows-alt-h:before{content:""}.fa-boxes-packing:before{content:""}.fa-circle-arrow-left:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-group-arrows-rotate:before{content:""}.fa-bowl-food:before{content:""}.fa-candy-cane:before{content:""}.fa-arrow-down-wide-short:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-down:before{content:""}.fa-cloud-bolt:before{content:""}.fa-thunderstorm:before{content:""}.fa-text-slash:before{content:""}.fa-remove-format:before{content:""}.fa-face-smile-wink:before{content:""}.fa-smile-wink:before{content:""}.fa-file-word:before{content:""}.fa-file-powerpoint:before{content:""}.fa-arrows-left-right:before{content:""}.fa-arrows-h:before{content:""}.fa-house-lock:before{content:""}.fa-cloud-arrow-down:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-download-alt:before{content:""}.fa-children:before{content:""}.fa-chalkboard:before{content:""}.fa-blackboard:before{content:""}.fa-user-large-slash:before{content:""}.fa-user-alt-slash:before{content:""}.fa-envelope-open:before{content:""}.fa-handshake-simple-slash:before{content:""}.fa-handshake-alt-slash:before{content:""}.fa-mattress-pillow:before{content:""}.fa-guarani-sign:before{content:""}.fa-arrows-rotate:before{content:""}.fa-refresh:before{content:""}.fa-sync:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-cruzeiro-sign:before{content:""}.fa-greater-than-equal:before{content:""}.fa-shield-halved:before{content:""}.fa-shield-alt:before{content:""}.fa-book-atlas:before{content:""}.fa-atlas:before{content:""}.fa-virus:before{content:""}.fa-envelope-circle-check:before{content:""}.fa-layer-group:before{content:""}.fa-arrows-to-dot:before{content:""}.fa-archway:before{content:""}.fa-heart-circle-check:before{content:""}.fa-house-chimney-crack:before{content:""}.fa-house-damage:before{content:""}.fa-file-zipper:before{content:""}.fa-file-archive:before{content:""}.fa-square:before{content:""}.fa-martini-glass-empty:before{content:""}.fa-glass-martini:before{content:""}.fa-couch:before{content:""}.fa-cedi-sign:before{content:""}.fa-italic:before{content:""}.fa-church:before{content:""}.fa-comments-dollar:before{content:""}.fa-democrat:before{content:""}.fa-z:before{content:"Z"}.fa-person-skiing:before{content:""}.fa-skiing:before{content:""}.fa-road-lock:before{content:""}.fa-a:before{content:"A"}.fa-temperature-arrow-down:before{content:""}.fa-temperature-down:before{content:""}.fa-feather-pointed:before{content:""}.fa-feather-alt:before{content:""}.fa-p:before{content:"P"}.fa-snowflake:before{content:""}.fa-newspaper:before{content:""}.fa-rectangle-ad:before{content:""}.fa-ad:before{content:""}.fa-circle-arrow-right:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-filter-circle-xmark:before{content:""}.fa-locust:before{content:""}.fa-sort:before{content:""}.fa-unsorted:before{content:""}.fa-list-ol:before{content:""}.fa-list-1-2:before{content:""}.fa-list-numeric:before{content:""}.fa-person-dress-burst:before{content:""}.fa-money-check-dollar:before{content:""}.fa-money-check-alt:before{content:""}.fa-vector-square:before{content:""}.fa-bread-slice:before{content:""}.fa-language:before{content:""}.fa-face-kiss-wink-heart:before{content:""}.fa-kiss-wink-heart:before{content:""}.fa-filter:before{content:""}.fa-question:before{content:"?"}.fa-file-signature:before{content:""}.fa-up-down-left-right:before{content:""}.fa-arrows-alt:before{content:""}.fa-house-chimney-user:before{content:""}.fa-hand-holding-heart:before{content:""}.fa-puzzle-piece:before{content:""}.fa-money-check:before{content:""}.fa-star-half-stroke:before{content:""}.fa-star-half-alt:before{content:""}.fa-code:before{content:""}.fa-whiskey-glass:before{content:""}.fa-glass-whiskey:before{content:""}.fa-building-circle-exclamation:before{content:""}.fa-magnifying-glass-chart:before{content:""}.fa-arrow-up-right-from-square:before{content:""}.fa-external-link:before{content:""}.fa-cubes-stacked:before{content:""}.fa-won-sign:before{content:""}.fa-krw:before{content:""}.fa-won:before{content:""}.fa-virus-covid:before{content:""}.fa-austral-sign:before{content:""}.fa-f:before{content:"F"}.fa-leaf:before{content:""}.fa-road:before{content:""}.fa-taxi:before{content:""}.fa-cab:before{content:""}.fa-person-circle-plus:before{content:""}.fa-chart-pie:before{content:""}.fa-pie-chart:before{content:""}.fa-bolt-lightning:before{content:""}.fa-sack-xmark:before{content:""}.fa-file-excel:before{content:""}.fa-file-contract:before{content:""}.fa-fish-fins:before{content:""}.fa-building-flag:before{content:""}.fa-face-grin-beam:before{content:""}.fa-grin-beam:before{content:""}.fa-object-ungroup:before{content:""}.fa-poop:before{content:""}.fa-location-pin:before{content:""}.fa-map-marker:before{content:""}.fa-kaaba:before{content:""}.fa-toilet-paper:before{content:""}.fa-helmet-safety:before{content:""}.fa-hard-hat:before{content:""}.fa-hat-hard:before{content:""}.fa-eject:before{content:""}.fa-circle-right:before{content:""}.fa-arrow-alt-circle-right:before{content:""}.fa-plane-circle-check:before{content:""}.fa-face-rolling-eyes:before{content:""}.fa-meh-rolling-eyes:before{content:""}.fa-object-group:before{content:""}.fa-chart-line:before{content:""}.fa-line-chart:before{content:""}.fa-mask-ventilator:before{content:""}.fa-arrow-right:before{content:""}.fa-signs-post:before{content:""}.fa-map-signs:before{content:""}.fa-cash-register:before{content:""}.fa-person-circle-question:before{content:""}.fa-h:before{content:"H"}.fa-tarp:before{content:""}.fa-screwdriver-wrench:before{content:""}.fa-tools:before{content:""}.fa-arrows-to-eye:before{content:""}.fa-plug-circle-bolt:before{content:""}.fa-heart:before{content:""}.fa-mars-and-venus:before{content:""}.fa-house-user:before{content:""}.fa-home-user:before{content:""}.fa-dumpster-fire:before{content:""}.fa-house-crack:before{content:""}.fa-martini-glass-citrus:before{content:""}.fa-cocktail:before{content:""}.fa-face-surprise:before{content:""}.fa-surprise:before{content:""}.fa-bottle-water:before{content:""}.fa-circle-pause:before{content:""}.fa-pause-circle:before{content:""}.fa-toilet-paper-slash:before{content:""}.fa-apple-whole:before{content:""}.fa-apple-alt:before{content:""}.fa-kitchen-set:before{content:""}.fa-r:before{content:"R"}.fa-temperature-quarter:before{content:""}.fa-temperature-1:before{content:""}.fa-thermometer-1:before{content:""}.fa-thermometer-quarter:before{content:""}.fa-cube:before{content:""}.fa-bitcoin-sign:before{content:""}.fa-shield-dog:before{content:""}.fa-solar-panel:before{content:""}.fa-lock-open:before{content:""}.fa-elevator:before{content:""}.fa-money-bill-transfer:before{content:""}.fa-money-bill-trend-up:before{content:""}.fa-house-flood-water-circle-arrow-right:before{content:""}.fa-square-poll-horizontal:before{content:""}.fa-poll-h:before{content:""}.fa-circle:before{content:""}.fa-backward-fast:before{content:""}.fa-fast-backward:before{content:""}.fa-recycle:before{content:""}.fa-user-astronaut:before{content:""}.fa-plane-slash:before{content:""}.fa-trademark:before{content:""}.fa-basketball:before{content:""}.fa-basketball-ball:before{content:""}.fa-satellite-dish:before{content:""}.fa-circle-up:before{content:""}.fa-arrow-alt-circle-up:before{content:""}.fa-mobile-screen-button:before{content:""}.fa-mobile-alt:before{content:""}.fa-volume-high:before{content:""}.fa-volume-up:before{content:""}.fa-users-rays:before{content:""}.fa-wallet:before{content:""}.fa-clipboard-check:before{content:""}.fa-file-audio:before{content:""}.fa-burger:before{content:""}.fa-hamburger:before{content:""}.fa-wrench:before{content:""}.fa-bugs:before{content:""}.fa-rupee-sign:before{content:""}.fa-rupee:before{content:""}.fa-file-image:before{content:""}.fa-circle-question:before{content:""}.fa-question-circle:before{content:""}.fa-plane-departure:before{content:""}.fa-handshake-slash:before{content:""}.fa-book-bookmark:before{content:""}.fa-code-branch:before{content:""}.fa-hat-cowboy:before{content:""}.fa-bridge:before{content:""}.fa-phone-flip:before{content:""}.fa-phone-alt:before{content:""}.fa-truck-front:before{content:""}.fa-cat:before{content:""}.fa-anchor-circle-exclamation:before{content:""}.fa-truck-field:before{content:""}.fa-route:before{content:""}.fa-clipboard-question:before{content:""}.fa-panorama:before{content:""}.fa-comment-medical:before{content:""}.fa-teeth-open:before{content:""}.fa-file-circle-minus:before{content:""}.fa-tags:before{content:""}.fa-wine-glass:before{content:""}.fa-forward-fast:before{content:""}.fa-fast-forward:before{content:""}.fa-face-meh-blank:before{content:""}.fa-meh-blank:before{content:""}.fa-square-parking:before{content:""}.fa-parking:before{content:""}.fa-house-signal:before{content:""}.fa-bars-progress:before{content:""}.fa-tasks-alt:before{content:""}.fa-faucet-drip:before{content:""}.fa-cart-flatbed:before{content:""}.fa-dolly-flatbed:before{content:""}.fa-ban-smoking:before{content:""}.fa-smoking-ban:before{content:""}.fa-terminal:before{content:""}.fa-mobile-button:before{content:""}.fa-house-medical-flag:before{content:""}.fa-basket-shopping:before{content:""}.fa-shopping-basket:before{content:""}.fa-tape:before{content:""}.fa-bus-simple:before{content:""}.fa-bus-alt:before{content:""}.fa-eye:before{content:""}.fa-face-sad-cry:before{content:""}.fa-sad-cry:before{content:""}.fa-audio-description:before{content:""}.fa-person-military-to-person:before{content:""}.fa-file-shield:before{content:""}.fa-user-slash:before{content:""}.fa-pen:before{content:""}.fa-tower-observation:before{content:""}.fa-file-code:before{content:""}.fa-signal:before{content:""}.fa-signal-5:before{content:""}.fa-signal-perfect:before{content:""}.fa-bus:before{content:""}.fa-heart-circle-xmark:before{content:""}.fa-house-chimney:before{content:""}.fa-home-lg:before{content:""}.fa-window-maximize:before{content:""}.fa-face-frown:before{content:""}.fa-frown:before{content:""}.fa-prescription:before{content:""}.fa-shop:before{content:""}.fa-store-alt:before{content:""}.fa-floppy-disk:before{content:""}.fa-save:before{content:""}.fa-vihara:before{content:""}.fa-scale-unbalanced:before{content:""}.fa-balance-scale-left:before{content:""}.fa-sort-up:before{content:""}.fa-sort-asc:before{content:""}.fa-comment-dots:before{content:""}.fa-commenting:before{content:""}.fa-plant-wilt:before{content:""}.fa-diamond:before{content:""}.fa-face-grin-squint:before{content:""}.fa-grin-squint:before{content:""}.fa-hand-holding-dollar:before{content:""}.fa-hand-holding-usd:before{content:""}.fa-bacterium:before{content:""}.fa-hand-pointer:before{content:""}.fa-drum-steelpan:before{content:""}.fa-hand-scissors:before{content:""}.fa-hands-praying:before{content:""}.fa-praying-hands:before{content:""}.fa-arrow-rotate-right:before{content:""}.fa-arrow-right-rotate:before{content:""}.fa-arrow-rotate-forward:before{content:""}.fa-redo:before{content:""}.fa-biohazard:before{content:""}.fa-location-crosshairs:before{content:""}.fa-location:before{content:""}.fa-mars-double:before{content:""}.fa-child-dress:before{content:""}.fa-users-between-lines:before{content:""}.fa-lungs-virus:before{content:""}.fa-face-grin-tears:before{content:""}.fa-grin-tears:before{content:""}.fa-phone:before{content:""}.fa-calendar-xmark:before{content:""}.fa-calendar-times:before{content:""}.fa-child-reaching:before{content:""}.fa-head-side-virus:before{content:""}.fa-user-gear:before{content:""}.fa-user-cog:before{content:""}.fa-arrow-up-1-9:before{content:""}.fa-sort-numeric-up:before{content:""}.fa-door-closed:before{content:""}.fa-shield-virus:before{content:""}.fa-dice-six:before{content:""}.fa-mosquito-net:before{content:""}.fa-bridge-water:before{content:""}.fa-person-booth:before{content:""}.fa-text-width:before{content:""}.fa-hat-wizard:before{content:""}.fa-pen-fancy:before{content:""}.fa-person-digging:before{content:""}.fa-digging:before{content:""}.fa-trash:before{content:""}.fa-gauge-simple:before{content:""}.fa-gauge-simple-med:before{content:""}.fa-tachometer-average:before{content:""}.fa-book-medical:before{content:""}.fa-poo:before{content:""}.fa-quote-right:before{content:""}.fa-quote-right-alt:before{content:""}.fa-shirt:before{content:""}.fa-t-shirt:before{content:""}.fa-tshirt:before{content:""}.fa-cubes:before{content:""}.fa-divide:before{content:""}.fa-tenge-sign:before{content:""}.fa-tenge:before{content:""}.fa-headphones:before{content:""}.fa-hands-holding:before{content:""}.fa-hands-clapping:before{content:""}.fa-republican:before{content:""}.fa-arrow-left:before{content:""}.fa-person-circle-xmark:before{content:""}.fa-ruler:before{content:""}.fa-align-left:before{content:""}.fa-dice-d6:before{content:""}.fa-restroom:before{content:""}.fa-j:before{content:"J"}.fa-users-viewfinder:before{content:""}.fa-file-video:before{content:""}.fa-up-right-from-square:before{content:""}.fa-external-link-alt:before{content:""}.fa-table-cells:before{content:""}.fa-th:before{content:""}.fa-file-pdf:before{content:""}.fa-book-bible:before{content:""}.fa-bible:before{content:""}.fa-o:before{content:"O"}.fa-suitcase-medical:before{content:""}.fa-medkit:before{content:""}.fa-user-secret:before{content:""}.fa-otter:before{content:""}.fa-person-dress:before{content:""}.fa-female:before{content:""}.fa-comment-dollar:before{content:""}.fa-business-time:before{content:""}.fa-briefcase-clock:before{content:""}.fa-table-cells-large:before{content:""}.fa-th-large:before{content:""}.fa-book-tanakh:before{content:""}.fa-tanakh:before{content:""}.fa-phone-volume:before{content:""}.fa-volume-control-phone:before{content:""}.fa-hat-cowboy-side:before{content:""}.fa-clipboard-user:before{content:""}.fa-child:before{content:""}.fa-lira-sign:before{content:""}.fa-satellite:before{content:""}.fa-plane-lock:before{content:""}.fa-tag:before{content:""}.fa-comment:before{content:""}.fa-cake-candles:before{content:""}.fa-birthday-cake:before{content:""}.fa-cake:before{content:""}.fa-envelope:before{content:""}.fa-angles-up:before{content:""}.fa-angle-double-up:before{content:""}.fa-paperclip:before{content:""}.fa-arrow-right-to-city:before{content:""}.fa-ribbon:before{content:""}.fa-lungs:before{content:""}.fa-arrow-up-9-1:before{content:""}.fa-sort-numeric-up-alt:before{content:""}.fa-litecoin-sign:before{content:""}.fa-border-none:before{content:""}.fa-circle-nodes:before{content:""}.fa-parachute-box:before{content:""}.fa-indent:before{content:""}.fa-truck-field-un:before{content:""}.fa-hourglass:before{content:""}.fa-hourglass-empty:before{content:""}.fa-mountain:before{content:""}.fa-user-doctor:before{content:""}.fa-user-md:before{content:""}.fa-circle-info:before{content:""}.fa-info-circle:before{content:""}.fa-cloud-meatball:before{content:""}.fa-camera:before{content:""}.fa-camera-alt:before{content:""}.fa-square-virus:before{content:""}.fa-meteor:before{content:""}.fa-car-on:before{content:""}.fa-sleigh:before{content:""}.fa-arrow-down-1-9:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-down:before{content:""}.fa-hand-holding-droplet:before{content:""}.fa-hand-holding-water:before{content:""}.fa-water:before{content:""}.fa-calendar-check:before{content:""}.fa-braille:before{content:""}.fa-prescription-bottle-medical:before{content:""}.fa-prescription-bottle-alt:before{content:""}.fa-landmark:before{content:""}.fa-truck:before{content:""}.fa-crosshairs:before{content:""}.fa-person-cane:before{content:""}.fa-tent:before{content:""}.fa-vest-patches:before{content:""}.fa-check-double:before{content:""}.fa-arrow-down-a-z:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-down:before{content:""}.fa-money-bill-wheat:before{content:""}.fa-cookie:before{content:""}.fa-arrow-rotate-left:before{content:""}.fa-arrow-left-rotate:before{content:""}.fa-arrow-rotate-back:before{content:""}.fa-arrow-rotate-backward:before{content:""}.fa-undo:before{content:""}.fa-hard-drive:before{content:""}.fa-hdd:before{content:""}.fa-face-grin-squint-tears:before{content:""}.fa-grin-squint-tears:before{content:""}.fa-dumbbell:before{content:""}.fa-rectangle-list:before{content:""}.fa-list-alt:before{content:""}.fa-tarp-droplet:before{content:""}.fa-house-medical-circle-check:before{content:""}.fa-person-skiing-nordic:before{content:""}.fa-skiing-nordic:before{content:""}.fa-calendar-plus:before{content:""}.fa-plane-arrival:before{content:""}.fa-circle-left:before{content:""}.fa-arrow-alt-circle-left:before{content:""}.fa-train-subway:before{content:""}.fa-subway:before{content:""}.fa-chart-gantt:before{content:""}.fa-indian-rupee-sign:before{content:""}.fa-indian-rupee:before{content:""}.fa-inr:before{content:""}.fa-crop-simple:before{content:""}.fa-crop-alt:before{content:""}.fa-money-bill-1:before{content:""}.fa-money-bill-alt:before{content:""}.fa-left-long:before{content:""}.fa-long-arrow-alt-left:before{content:""}.fa-dna:before{content:""}.fa-virus-slash:before{content:""}.fa-minus:before{content:""}.fa-subtract:before{content:""}.fa-chess:before{content:""}.fa-arrow-left-long:before{content:""}.fa-long-arrow-left:before{content:""}.fa-plug-circle-check:before{content:""}.fa-street-view:before{content:""}.fa-franc-sign:before{content:""}.fa-volume-off:before{content:""}.fa-hands-asl-interpreting:before{content:""}.fa-american-sign-language-interpreting:before{content:""}.fa-asl-interpreting:before{content:""}.fa-hands-american-sign-language-interpreting:before{content:""}.fa-gear:before{content:""}.fa-cog:before{content:""}.fa-droplet-slash:before{content:""}.fa-tint-slash:before{content:""}.fa-mosque:before{content:""}.fa-mosquito:before{content:""}.fa-star-of-david:before{content:""}.fa-person-military-rifle:before{content:""}.fa-cart-shopping:before{content:""}.fa-shopping-cart:before{content:""}.fa-vials:before{content:""}.fa-plug-circle-plus:before{content:""}.fa-place-of-worship:before{content:""}.fa-grip-vertical:before{content:""}.fa-arrow-turn-up:before{content:""}.fa-level-up:before{content:""}.fa-u:before{content:"U"}.fa-square-root-variable:before{content:""}.fa-square-root-alt:before{content:""}.fa-clock:before{content:""}.fa-clock-four:before{content:""}.fa-backward-step:before{content:""}.fa-step-backward:before{content:""}.fa-pallet:before{content:""}.fa-faucet:before{content:""}.fa-baseball-bat-ball:before{content:""}.fa-s:before{content:"S"}.fa-timeline:before{content:""}.fa-keyboard:before{content:""}.fa-caret-down:before{content:""}.fa-house-chimney-medical:before{content:""}.fa-clinic-medical:before{content:""}.fa-temperature-three-quarters:before{content:""}.fa-temperature-3:before{content:""}.fa-thermometer-3:before{content:""}.fa-thermometer-three-quarters:before{content:""}.fa-mobile-screen:before{content:""}.fa-mobile-android-alt:before{content:""}.fa-plane-up:before{content:""}.fa-piggy-bank:before{content:""}.fa-battery-half:before{content:""}.fa-battery-3:before{content:""}.fa-mountain-city:before{content:""}.fa-coins:before{content:""}.fa-khanda:before{content:""}.fa-sliders:before{content:""}.fa-sliders-h:before{content:""}.fa-folder-tree:before{content:""}.fa-network-wired:before{content:""}.fa-map-pin:before{content:""}.fa-hamsa:before{content:""}.fa-cent-sign:before{content:""}.fa-flask:before{content:""}.fa-person-pregnant:before{content:""}.fa-wand-sparkles:before{content:""}.fa-ellipsis-vertical:before{content:""}.fa-ellipsis-v:before{content:""}.fa-ticket:before{content:""}.fa-power-off:before{content:""}.fa-right-long:before{content:""}.fa-long-arrow-alt-right:before{content:""}.fa-flag-usa:before{content:""}.fa-laptop-file:before{content:""}.fa-tty:before{content:""}.fa-teletype:before{content:""}.fa-diagram-next:before{content:""}.fa-person-rifle:before{content:""}.fa-house-medical-circle-exclamation:before{content:""}.fa-closed-captioning:before{content:""}.fa-person-hiking:before{content:""}.fa-hiking:before{content:""}.fa-venus-double:before{content:""}.fa-images:before{content:""}.fa-calculator:before{content:""}.fa-people-pulling:before{content:""}.fa-n:before{content:"N"}.fa-cable-car:before{content:""}.fa-tram:before{content:""}.fa-cloud-rain:before{content:""}.fa-building-circle-xmark:before{content:""}.fa-ship:before{content:""}.fa-arrows-down-to-line:before{content:""}.fa-download:before{content:""}.fa-face-grin:before{content:""}.fa-grin:before{content:""}.fa-delete-left:before{content:""}.fa-backspace:before{content:""}.fa-eye-dropper:before{content:""}.fa-eye-dropper-empty:before{content:""}.fa-eyedropper:before{content:""}.fa-file-circle-check:before{content:""}.fa-forward:before{content:""}.fa-mobile:before{content:""}.fa-mobile-android:before{content:""}.fa-mobile-phone:before{content:""}.fa-face-meh:before{content:""}.fa-meh:before{content:""}.fa-align-center:before{content:""}.fa-book-skull:before{content:""}.fa-book-dead:before{content:""}.fa-id-card:before{content:""}.fa-drivers-license:before{content:""}.fa-outdent:before{content:""}.fa-dedent:before{content:""}.fa-heart-circle-exclamation:before{content:""}.fa-house:before{content:""}.fa-home:before{content:""}.fa-home-alt:before{content:""}.fa-home-lg-alt:before{content:""}.fa-calendar-week:before{content:""}.fa-laptop-medical:before{content:""}.fa-b:before{content:"B"}.fa-file-medical:before{content:""}.fa-dice-one:before{content:""}.fa-kiwi-bird:before{content:""}.fa-arrow-right-arrow-left:before{content:""}.fa-exchange:before{content:""}.fa-rotate-right:before{content:""}.fa-redo-alt:before{content:""}.fa-rotate-forward:before{content:""}.fa-utensils:before{content:""}.fa-cutlery:before{content:""}.fa-arrow-up-wide-short:before{content:""}.fa-sort-amount-up:before{content:""}.fa-mill-sign:before{content:""}.fa-bowl-rice:before{content:""}.fa-skull:before{content:""}.fa-tower-broadcast:before{content:""}.fa-broadcast-tower:before{content:""}.fa-truck-pickup:before{content:""}.fa-up-long:before{content:""}.fa-long-arrow-alt-up:before{content:""}.fa-stop:before{content:""}.fa-code-merge:before{content:""}.fa-upload:before{content:""}.fa-hurricane:before{content:""}.fa-mound:before{content:""}.fa-toilet-portable:before{content:""}.fa-compact-disc:before{content:""}.fa-file-arrow-down:before{content:""}.fa-file-download:before{content:""}.fa-caravan:before{content:""}.fa-shield-cat:before{content:""}.fa-bolt:before{content:""}.fa-zap:before{content:""}.fa-glass-water:before{content:""}.fa-oil-well:before{content:""}.fa-vault:before{content:""}.fa-mars:before{content:""}.fa-toilet:before{content:""}.fa-plane-circle-xmark:before{content:""}.fa-yen-sign:before{content:""}.fa-cny:before{content:""}.fa-jpy:before{content:""}.fa-rmb:before{content:""}.fa-yen:before{content:""}.fa-ruble-sign:before{content:""}.fa-rouble:before{content:""}.fa-rub:before{content:""}.fa-ruble:before{content:""}.fa-sun:before{content:""}.fa-guitar:before{content:""}.fa-face-laugh-wink:before{content:""}.fa-laugh-wink:before{content:""}.fa-horse-head:before{content:""}.fa-bore-hole:before{content:""}.fa-industry:before{content:""}.fa-circle-down:before{content:""}.fa-arrow-alt-circle-down:before{content:""}.fa-arrows-turn-to-dots:before{content:""}.fa-florin-sign:before{content:""}.fa-arrow-down-short-wide:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-amount-down-alt:before{content:""}.fa-less-than:before{content:"<"}.fa-angle-down:before{content:""}.fa-car-tunnel:before{content:""}.fa-head-side-cough:before{content:""}.fa-grip-lines:before{content:""}.fa-thumbs-down:before{content:""}.fa-user-lock:before{content:""}.fa-arrow-right-long:before{content:""}.fa-long-arrow-right:before{content:""}.fa-anchor-circle-xmark:before{content:""}.fa-ellipsis:before{content:""}.fa-ellipsis-h:before{content:""}.fa-chess-pawn:before{content:""}.fa-kit-medical:before{content:""}.fa-first-aid:before{content:""}.fa-person-through-window:before{content:""}.fa-toolbox:before{content:""}.fa-hands-holding-circle:before{content:""}.fa-bug:before{content:""}.fa-credit-card:before{content:""}.fa-credit-card-alt:before{content:""}.fa-car:before{content:""}.fa-automobile:before{content:""}.fa-hand-holding-hand:before{content:""}.fa-book-open-reader:before{content:""}.fa-book-reader:before{content:""}.fa-mountain-sun:before{content:""}.fa-arrows-left-right-to-line:before{content:""}.fa-dice-d20:before{content:""}.fa-truck-droplet:before{content:""}.fa-file-circle-xmark:before{content:""}.fa-temperature-arrow-up:before{content:""}.fa-temperature-up:before{content:""}.fa-medal:before{content:""}.fa-bed:before{content:""}.fa-square-h:before{content:""}.fa-h-square:before{content:""}.fa-podcast:before{content:""}.fa-temperature-full:before{content:""}.fa-temperature-4:before{content:""}.fa-thermometer-4:before{content:""}.fa-thermometer-full:before{content:""}.fa-bell:before{content:""}.fa-superscript:before{content:""}.fa-plug-circle-xmark:before{content:""}.fa-star-of-life:before{content:""}.fa-phone-slash:before{content:""}.fa-paint-roller:before{content:""}.fa-handshake-angle:before{content:""}.fa-hands-helping:before{content:""}.fa-location-dot:before{content:""}.fa-map-marker-alt:before{content:""}.fa-file:before{content:""}.fa-greater-than:before{content:">"}.fa-person-swimming:before{content:""}.fa-swimmer:before{content:""}.fa-arrow-down:before{content:""}.fa-droplet:before{content:""}.fa-tint:before{content:""}.fa-eraser:before{content:""}.fa-earth-americas:before{content:""}.fa-earth:before{content:""}.fa-earth-america:before{content:""}.fa-globe-americas:before{content:""}.fa-person-burst:before{content:""}.fa-dove:before{content:""}.fa-battery-empty:before{content:""}.fa-battery-0:before{content:""}.fa-socks:before{content:""}.fa-inbox:before{content:""}.fa-section:before{content:""}.fa-gauge-high:before{content:""}.fa-tachometer-alt:before{content:""}.fa-tachometer-alt-fast:before{content:""}.fa-envelope-open-text:before{content:""}.fa-hospital:before{content:""}.fa-hospital-alt:before{content:""}.fa-hospital-wide:before{content:""}.fa-wine-bottle:before{content:""}.fa-chess-rook:before{content:""}.fa-bars-staggered:before{content:""}.fa-reorder:before{content:""}.fa-stream:before{content:""}.fa-dharmachakra:before{content:""}.fa-hotdog:before{content:""}.fa-person-walking-with-cane:before{content:""}.fa-blind:before{content:""}.fa-drum:before{content:""}.fa-ice-cream:before{content:""}.fa-heart-circle-bolt:before{content:""}.fa-fax:before{content:""}.fa-paragraph:before{content:""}.fa-check-to-slot:before{content:""}.fa-vote-yea:before{content:""}.fa-star-half:before{content:""}.fa-boxes-stacked:before{content:""}.fa-boxes:before{content:""}.fa-boxes-alt:before{content:""}.fa-link:before{content:""}.fa-chain:before{content:""}.fa-ear-listen:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-tree-city:before{content:""}.fa-play:before{content:""}.fa-font:before{content:""}.fa-rupiah-sign:before{content:""}.fa-magnifying-glass:before{content:""}.fa-search:before{content:""}.fa-table-tennis-paddle-ball:before{content:""}.fa-ping-pong-paddle-ball:before{content:""}.fa-table-tennis:before{content:""}.fa-person-dots-from-line:before{content:""}.fa-diagnoses:before{content:""}.fa-trash-can-arrow-up:before{content:""}.fa-trash-restore-alt:before{content:""}.fa-naira-sign:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-walkie-talkie:before{content:""}.fa-file-pen:before{content:""}.fa-file-edit:before{content:""}.fa-receipt:before{content:""}.fa-square-pen:before{content:""}.fa-pen-square:before{content:""}.fa-pencil-square:before{content:""}.fa-suitcase-rolling:before{content:""}.fa-person-circle-exclamation:before{content:""}.fa-chevron-down:before{content:""}.fa-battery-full:before{content:""}.fa-battery:before{content:""}.fa-battery-5:before{content:""}.fa-skull-crossbones:before{content:""}.fa-code-compare:before{content:""}.fa-list-ul:before{content:""}.fa-list-dots:before{content:""}.fa-school-lock:before{content:""}.fa-tower-cell:before{content:""}.fa-down-long:before{content:""}.fa-long-arrow-alt-down:before{content:""}.fa-ranking-star:before{content:""}.fa-chess-king:before{content:""}.fa-person-harassing:before{content:""}.fa-brazilian-real-sign:before{content:""}.fa-landmark-dome:before{content:""}.fa-landmark-alt:before{content:""}.fa-arrow-up:before{content:""}.fa-tv:before{content:""}.fa-television:before{content:""}.fa-tv-alt:before{content:""}.fa-shrimp:before{content:""}.fa-list-check:before{content:""}.fa-tasks:before{content:""}.fa-jug-detergent:before{content:""}.fa-circle-user:before{content:""}.fa-user-circle:before{content:""}.fa-user-shield:before{content:""}.fa-wind:before{content:""}.fa-car-burst:before{content:""}.fa-car-crash:before{content:""}.fa-y:before{content:"Y"}.fa-person-snowboarding:before{content:""}.fa-snowboarding:before{content:""}.fa-truck-fast:before{content:""}.fa-shipping-fast:before{content:""}.fa-fish:before{content:""}.fa-user-graduate:before{content:""}.fa-circle-half-stroke:before{content:""}.fa-adjust:before{content:""}.fa-clapperboard:before{content:""}.fa-circle-radiation:before{content:""}.fa-radiation-alt:before{content:""}.fa-baseball:before{content:""}.fa-baseball-ball:before{content:""}.fa-jet-fighter-up:before{content:""}.fa-diagram-project:before{content:""}.fa-project-diagram:before{content:""}.fa-copy:before{content:""}.fa-volume-xmark:before{content:""}.fa-volume-mute:before{content:""}.fa-volume-times:before{content:""}.fa-hand-sparkles:before{content:""}.fa-grip:before{content:""}.fa-grip-horizontal:before{content:""}.fa-share-from-square:before{content:""}.fa-share-square:before{content:""}.fa-child-combatant:before{content:""}.fa-child-rifle:before{content:""}.fa-gun:before{content:""}.fa-square-phone:before{content:""}.fa-phone-square:before{content:""}.fa-plus:before{content:"+"}.fa-add:before{content:"+"}.fa-expand:before{content:""}.fa-computer:before{content:""}.fa-xmark:before{content:""}.fa-close:before{content:""}.fa-multiply:before{content:""}.fa-remove:before{content:""}.fa-times:before{content:""}.fa-arrows-up-down-left-right:before{content:""}.fa-arrows:before{content:""}.fa-chalkboard-user:before{content:""}.fa-chalkboard-teacher:before{content:""}.fa-peso-sign:before{content:""}.fa-building-shield:before{content:""}.fa-baby:before{content:""}.fa-users-line:before{content:""}.fa-quote-left:before{content:""}.fa-quote-left-alt:before{content:""}.fa-tractor:before{content:""}.fa-trash-arrow-up:before{content:""}.fa-trash-restore:before{content:""}.fa-arrow-down-up-lock:before{content:""}.fa-lines-leaning:before{content:""}.fa-ruler-combined:before{content:""}.fa-copyright:before{content:""}.fa-equals:before{content:"="}.fa-blender:before{content:""}.fa-teeth:before{content:""}.fa-shekel-sign:before{content:""}.fa-ils:before{content:""}.fa-shekel:before{content:""}.fa-sheqel:before{content:""}.fa-sheqel-sign:before{content:""}.fa-map:before{content:""}.fa-rocket:before{content:""}.fa-photo-film:before{content:""}.fa-photo-video:before{content:""}.fa-folder-minus:before{content:""}.fa-store:before{content:""}.fa-arrow-trend-up:before{content:""}.fa-plug-circle-minus:before{content:""}.fa-sign-hanging:before{content:""}.fa-sign:before{content:""}.fa-bezier-curve:before{content:""}.fa-bell-slash:before{content:""}.fa-tablet:before{content:""}.fa-tablet-android:before{content:""}.fa-school-flag:before{content:""}.fa-fill:before{content:""}.fa-angle-up:before{content:""}.fa-drumstick-bite:before{content:""}.fa-holly-berry:before{content:""}.fa-chevron-left:before{content:""}.fa-bacteria:before{content:""}.fa-hand-lizard:before{content:""}.fa-notdef:before{content:""}.fa-disease:before{content:""}.fa-briefcase-medical:before{content:""}.fa-genderless:before{content:""}.fa-chevron-right:before{content:""}.fa-retweet:before{content:""}.fa-car-rear:before{content:""}.fa-car-alt:before{content:""}.fa-pump-soap:before{content:""}.fa-video-slash:before{content:""}.fa-battery-quarter:before{content:""}.fa-battery-2:before{content:""}.fa-radio:before{content:""}.fa-baby-carriage:before{content:""}.fa-carriage-baby:before{content:""}.fa-traffic-light:before{content:""}.fa-thermometer:before{content:""}.fa-vr-cardboard:before{content:""}.fa-hand-middle-finger:before{content:""}.fa-percent:before{content:"%"}.fa-percentage:before{content:"%"}.fa-truck-moving:before{content:""}.fa-glass-water-droplet:before{content:""}.fa-display:before{content:""}.fa-face-smile:before{content:""}.fa-smile:before{content:""}.fa-thumbtack:before{content:""}.fa-thumb-tack:before{content:""}.fa-trophy:before{content:""}.fa-person-praying:before{content:""}.fa-pray:before{content:""}.fa-hammer:before{content:""}.fa-hand-peace:before{content:""}.fa-rotate:before{content:""}.fa-sync-alt:before{content:""}.fa-spinner:before{content:""}.fa-robot:before{content:""}.fa-peace:before{content:""}.fa-gears:before{content:""}.fa-cogs:before{content:""}.fa-warehouse:before{content:""}.fa-arrow-up-right-dots:before{content:""}.fa-splotch:before{content:""}.fa-face-grin-hearts:before{content:""}.fa-grin-hearts:before{content:""}.fa-dice-four:before{content:""}.fa-sim-card:before{content:""}.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-mercury:before{content:""}.fa-arrow-turn-down:before{content:""}.fa-level-down:before{content:""}.fa-person-falling-burst:before{content:""}.fa-award:before{content:""}.fa-ticket-simple:before{content:""}.fa-ticket-alt:before{content:""}.fa-building:before{content:""}.fa-angles-left:before{content:""}.fa-angle-double-left:before{content:""}.fa-qrcode:before{content:""}.fa-clock-rotate-left:before{content:""}.fa-history:before{content:""}.fa-face-grin-beam-sweat:before{content:""}.fa-grin-beam-sweat:before{content:""}.fa-file-export:before{content:""}.fa-arrow-right-from-file:before{content:""}.fa-shield:before{content:""}.fa-shield-blank:before{content:""}.fa-arrow-up-short-wide:before{content:""}.fa-sort-amount-up-alt:before{content:""}.fa-house-medical:before{content:""}.fa-golf-ball-tee:before{content:""}.fa-golf-ball:before{content:""}.fa-circle-chevron-left:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-house-chimney-window:before{content:""}.fa-pen-nib:before{content:""}.fa-tent-arrow-turn-left:before{content:""}.fa-tents:before{content:""}.fa-wand-magic:before{content:""}.fa-magic:before{content:""}.fa-dog:before{content:""}.fa-carrot:before{content:""}.fa-moon:before{content:""}.fa-wine-glass-empty:before{content:""}.fa-wine-glass-alt:before{content:""}.fa-cheese:before{content:""}.fa-yin-yang:before{content:""}.fa-music:before{content:""}.fa-code-commit:before{content:""}.fa-temperature-low:before{content:""}.fa-person-biking:before{content:""}.fa-biking:before{content:""}.fa-broom:before{content:""}.fa-shield-heart:before{content:""}.fa-gopuram:before{content:""}.fa-earth-oceania:before{content:""}.fa-globe-oceania:before{content:""}.fa-square-xmark:before{content:""}.fa-times-square:before{content:""}.fa-xmark-square:before{content:""}.fa-hashtag:before{content:"#"}.fa-up-right-and-down-left-from-center:before{content:""}.fa-expand-alt:before{content:""}.fa-oil-can:before{content:""}.fa-t:before{content:"T"}.fa-hippo:before{content:""}.fa-chart-column:before{content:""}.fa-infinity:before{content:""}.fa-vial-circle-check:before{content:""}.fa-person-arrow-down-to-line:before{content:""}.fa-voicemail:before{content:""}.fa-fan:before{content:""}.fa-person-walking-luggage:before{content:""}.fa-up-down:before{content:""}.fa-arrows-alt-v:before{content:""}.fa-cloud-moon-rain:before{content:""}.fa-calendar:before{content:""}.fa-trailer:before{content:""}.fa-bahai:before{content:""}.fa-haykal:before{content:""}.fa-sd-card:before{content:""}.fa-dragon:before{content:""}.fa-shoe-prints:before{content:""}.fa-circle-plus:before{content:""}.fa-plus-circle:before{content:""}.fa-face-grin-tongue-wink:before{content:""}.fa-grin-tongue-wink:before{content:""}.fa-hand-holding:before{content:""}.fa-plug-circle-exclamation:before{content:""}.fa-link-slash:before{content:""}.fa-chain-broken:before{content:""}.fa-chain-slash:before{content:""}.fa-unlink:before{content:""}.fa-clone:before{content:""}.fa-person-walking-arrow-loop-left:before{content:""}.fa-arrow-up-z-a:before{content:""}.fa-sort-alpha-up-alt:before{content:""}.fa-fire-flame-curved:before{content:""}.fa-fire-alt:before{content:""}.fa-tornado:before{content:""}.fa-file-circle-plus:before{content:""}.fa-book-quran:before{content:""}.fa-quran:before{content:""}.fa-anchor:before{content:""}.fa-border-all:before{content:""}.fa-face-angry:before{content:""}.fa-angry:before{content:""}.fa-cookie-bite:before{content:""}.fa-arrow-trend-down:before{content:""}.fa-rss:before{content:""}.fa-feed:before{content:""}.fa-draw-polygon:before{content:""}.fa-scale-balanced:before{content:""}.fa-balance-scale:before{content:""}.fa-gauge-simple-high:before{content:""}.fa-tachometer:before{content:""}.fa-tachometer-fast:before{content:""}.fa-shower:before{content:""}.fa-desktop:before{content:""}.fa-desktop-alt:before{content:""}.fa-m:before{content:"M"}.fa-table-list:before{content:""}.fa-th-list:before{content:""}.fa-comment-sms:before{content:""}.fa-sms:before{content:""}.fa-book:before{content:""}.fa-user-plus:before{content:""}.fa-check:before{content:""}.fa-battery-three-quarters:before{content:""}.fa-battery-4:before{content:""}.fa-house-circle-check:before{content:""}.fa-angle-left:before{content:""}.fa-diagram-successor:before{content:""}.fa-truck-arrow-right:before{content:""}.fa-arrows-split-up-and-left:before{content:""}.fa-hand-fist:before{content:""}.fa-fist-raised:before{content:""}.fa-cloud-moon:before{content:""}.fa-briefcase:before{content:""}.fa-person-falling:before{content:""}.fa-image-portrait:before{content:""}.fa-portrait:before{content:""}.fa-user-tag:before{content:""}.fa-rug:before{content:""}.fa-earth-europe:before{content:""}.fa-globe-europe:before{content:""}.fa-cart-flatbed-suitcase:before{content:""}.fa-luggage-cart:before{content:""}.fa-rectangle-xmark:before{content:""}.fa-rectangle-times:before{content:""}.fa-times-rectangle:before{content:""}.fa-window-close:before{content:""}.fa-baht-sign:before{content:""}.fa-book-open:before{content:""}.fa-book-journal-whills:before{content:""}.fa-journal-whills:before{content:""}.fa-handcuffs:before{content:""}.fa-triangle-exclamation:before{content:""}.fa-exclamation-triangle:before{content:""}.fa-warning:before{content:""}.fa-database:before{content:""}.fa-share:before{content:""}.fa-arrow-turn-right:before{content:""}.fa-mail-forward:before{content:""}.fa-bottle-droplet:before{content:""}.fa-mask-face:before{content:""}.fa-hill-rockslide:before{content:""}.fa-right-left:before{content:""}.fa-exchange-alt:before{content:""}.fa-paper-plane:before{content:""}.fa-road-circle-exclamation:before{content:""}.fa-dungeon:before{content:""}.fa-align-right:before{content:""}.fa-money-bill-1-wave:before{content:""}.fa-money-bill-wave-alt:before{content:""}.fa-life-ring:before{content:""}.fa-hands:before{content:""}.fa-sign-language:before{content:""}.fa-signing:before{content:""}.fa-calendar-day:before{content:""}.fa-water-ladder:before{content:""}.fa-ladder-water:before{content:""}.fa-swimming-pool:before{content:""}.fa-arrows-up-down:before{content:""}.fa-arrows-v:before{content:""}.fa-face-grimace:before{content:""}.fa-grimace:before{content:""}.fa-wheelchair-move:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-turn-down:before{content:""}.fa-level-down-alt:before{content:""}.fa-person-walking-arrow-right:before{content:""}.fa-square-envelope:before{content:""}.fa-envelope-square:before{content:""}.fa-dice:before{content:""}.fa-bowling-ball:before{content:""}.fa-brain:before{content:""}.fa-bandage:before{content:""}.fa-band-aid:before{content:""}.fa-calendar-minus:before{content:""}.fa-circle-xmark:before{content:""}.fa-times-circle:before{content:""}.fa-xmark-circle:before{content:""}.fa-gifts:before{content:""}.fa-hotel:before{content:""}.fa-earth-asia:before{content:""}.fa-globe-asia:before{content:""}.fa-id-card-clip:before{content:""}.fa-id-card-alt:before{content:""}.fa-magnifying-glass-plus:before{content:""}.fa-search-plus:before{content:""}.fa-thumbs-up:before{content:""}.fa-user-clock:before{content:""}.fa-hand-dots:before{content:""}.fa-allergies:before{content:""}.fa-file-invoice:before{content:""}.fa-window-minimize:before{content:""}.fa-mug-saucer:before{content:""}.fa-coffee:before{content:""}.fa-brush:before{content:""}.fa-mask:before{content:""}.fa-magnifying-glass-minus:before{content:""}.fa-search-minus:before{content:""}.fa-ruler-vertical:before{content:""}.fa-user-large:before{content:""}.fa-user-alt:before{content:""}.fa-train-tram:before{content:""}.fa-user-nurse:before{content:""}.fa-syringe:before{content:""}.fa-cloud-sun:before{content:""}.fa-stopwatch-20:before{content:""}.fa-square-full:before{content:""}.fa-magnet:before{content:""}.fa-jar:before{content:""}.fa-note-sticky:before{content:""}.fa-sticky-note:before{content:""}.fa-bug-slash:before{content:""}.fa-arrow-up-from-water-pump:before{content:""}.fa-bone:before{content:""}.fa-user-injured:before{content:""}.fa-face-sad-tear:before{content:""}.fa-sad-tear:before{content:""}.fa-plane:before{content:""}.fa-tent-arrows-down:before{content:""}.fa-exclamation:before{content:"!"}.fa-arrows-spin:before{content:""}.fa-print:before{content:""}.fa-turkish-lira-sign:before{content:""}.fa-try:before{content:""}.fa-turkish-lira:before{content:""}.fa-dollar-sign:before{content:"$"}.fa-dollar:before{content:"$"}.fa-usd:before{content:"$"}.fa-x:before{content:"X"}.fa-magnifying-glass-dollar:before{content:""}.fa-search-dollar:before{content:""}.fa-users-gear:before{content:""}.fa-users-cog:before{content:""}.fa-person-military-pointing:before{content:""}.fa-building-columns:before{content:""}.fa-bank:before{content:""}.fa-institution:before{content:""}.fa-museum:before{content:""}.fa-university:before{content:""}.fa-umbrella:before{content:""}.fa-trowel:before{content:""}.fa-d:before{content:"D"}.fa-stapler:before{content:""}.fa-masks-theater:before{content:""}.fa-theater-masks:before{content:""}.fa-kip-sign:before{content:""}.fa-hand-point-left:before{content:""}.fa-handshake-simple:before{content:""}.fa-handshake-alt:before{content:""}.fa-jet-fighter:before{content:""}.fa-fighter-jet:before{content:""}.fa-square-share-nodes:before{content:""}.fa-share-alt-square:before{content:""}.fa-barcode:before{content:""}.fa-plus-minus:before{content:""}.fa-video:before{content:""}.fa-video-camera:before{content:""}.fa-graduation-cap:before{content:""}.fa-mortar-board:before{content:""}.fa-hand-holding-medical:before{content:""}.fa-person-circle-check:before{content:""}.fa-turn-up:before{content:""}.fa-level-up-alt:before{content:""}.sr-only,.fa-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.sr-only-focusable:not(:focus),.fa-sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:root,:host{--fa-style-family-classic: "Font Awesome 6 Free";--fa-font-solid: normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(/build/assets/fa-solid-900-886c8611.woff2) format("woff2"),url(/build/assets/fa-solid-900-cea79b34.ttf) format("truetype")}.fas,.fa-solid{font-weight:900}:root,:host{--fa-style-family-brands: "Font Awesome 6 Brands";--fa-font-brands: normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(/build/assets/fa-brands-400-faae6fc0.woff2) format("woff2"),url(/build/assets/fa-brands-400-003f1154.ttf) format("truetype")}.fab,.fa-brands{font-weight:400}.fa-monero:before{content:""}.fa-hooli:before{content:""}.fa-yelp:before{content:""}.fa-cc-visa:before{content:""}.fa-lastfm:before{content:""}.fa-shopware:before{content:""}.fa-creative-commons-nc:before{content:""}.fa-aws:before{content:""}.fa-redhat:before{content:""}.fa-yoast:before{content:""}.fa-cloudflare:before{content:""}.fa-ups:before{content:""}.fa-wpexplorer:before{content:""}.fa-dyalog:before{content:""}.fa-bity:before{content:""}.fa-stackpath:before{content:""}.fa-buysellads:before{content:""}.fa-first-order:before{content:""}.fa-modx:before{content:""}.fa-guilded:before{content:""}.fa-vnv:before{content:""}.fa-square-js:before{content:""}.fa-js-square:before{content:""}.fa-microsoft:before{content:""}.fa-qq:before{content:""}.fa-orcid:before{content:""}.fa-java:before{content:""}.fa-invision:before{content:""}.fa-creative-commons-pd-alt:before{content:""}.fa-centercode:before{content:""}.fa-glide-g:before{content:""}.fa-drupal:before{content:""}.fa-hire-a-helper:before{content:""}.fa-creative-commons-by:before{content:""}.fa-unity:before{content:""}.fa-whmcs:before{content:""}.fa-rocketchat:before{content:""}.fa-vk:before{content:""}.fa-untappd:before{content:""}.fa-mailchimp:before{content:""}.fa-css3-alt:before{content:""}.fa-square-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-vimeo-v:before{content:""}.fa-contao:before{content:""}.fa-square-font-awesome:before{content:""}.fa-deskpro:before{content:""}.fa-sistrix:before{content:""}.fa-square-instagram:before{content:""}.fa-instagram-square:before{content:""}.fa-battle-net:before{content:""}.fa-the-red-yeti:before{content:""}.fa-square-hacker-news:before{content:""}.fa-hacker-news-square:before{content:""}.fa-edge:before{content:""}.fa-threads:before{content:""}.fa-napster:before{content:""}.fa-square-snapchat:before{content:""}.fa-snapchat-square:before{content:""}.fa-google-plus-g:before{content:""}.fa-artstation:before{content:""}.fa-markdown:before{content:""}.fa-sourcetree:before{content:""}.fa-google-plus:before{content:""}.fa-diaspora:before{content:""}.fa-foursquare:before{content:""}.fa-stack-overflow:before{content:""}.fa-github-alt:before{content:""}.fa-phoenix-squadron:before{content:""}.fa-pagelines:before{content:""}.fa-algolia:before{content:""}.fa-red-river:before{content:""}.fa-creative-commons-sa:before{content:""}.fa-safari:before{content:""}.fa-google:before{content:""}.fa-square-font-awesome-stroke:before{content:""}.fa-font-awesome-alt:before{content:""}.fa-atlassian:before{content:""}.fa-linkedin-in:before{content:""}.fa-digital-ocean:before{content:""}.fa-nimblr:before{content:""}.fa-chromecast:before{content:""}.fa-evernote:before{content:""}.fa-hacker-news:before{content:""}.fa-creative-commons-sampling:before{content:""}.fa-adversal:before{content:""}.fa-creative-commons:before{content:""}.fa-watchman-monitoring:before{content:""}.fa-fonticons:before{content:""}.fa-weixin:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-codepen:before{content:""}.fa-git-alt:before{content:""}.fa-lyft:before{content:""}.fa-rev:before{content:""}.fa-windows:before{content:""}.fa-wizards-of-the-coast:before{content:""}.fa-square-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-meetup:before{content:""}.fa-centos:before{content:""}.fa-adn:before{content:""}.fa-cloudsmith:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-square-dribbble:before{content:""}.fa-dribbble-square:before{content:""}.fa-codiepie:before{content:""}.fa-node:before{content:""}.fa-mix:before{content:""}.fa-steam:before{content:""}.fa-cc-apple-pay:before{content:""}.fa-scribd:before{content:""}.fa-debian:before{content:""}.fa-openid:before{content:""}.fa-instalod:before{content:""}.fa-expeditedssl:before{content:""}.fa-sellcast:before{content:""}.fa-square-twitter:before{content:""}.fa-twitter-square:before{content:""}.fa-r-project:before{content:""}.fa-delicious:before{content:""}.fa-freebsd:before{content:""}.fa-vuejs:before{content:""}.fa-accusoft:before{content:""}.fa-ioxhost:before{content:""}.fa-fonticons-fi:before{content:""}.fa-app-store:before{content:""}.fa-cc-mastercard:before{content:""}.fa-itunes-note:before{content:""}.fa-golang:before{content:""}.fa-kickstarter:before{content:""}.fa-grav:before{content:""}.fa-weibo:before{content:""}.fa-uncharted:before{content:""}.fa-firstdraft:before{content:""}.fa-square-youtube:before{content:""}.fa-youtube-square:before{content:""}.fa-wikipedia-w:before{content:""}.fa-wpressr:before{content:""}.fa-rendact:before{content:""}.fa-angellist:before{content:""}.fa-galactic-republic:before{content:""}.fa-nfc-directional:before{content:""}.fa-skype:before{content:""}.fa-joget:before{content:""}.fa-fedora:before{content:""}.fa-stripe-s:before{content:""}.fa-meta:before{content:""}.fa-laravel:before{content:""}.fa-hotjar:before{content:""}.fa-bluetooth-b:before{content:""}.fa-sticker-mule:before{content:""}.fa-creative-commons-zero:before{content:""}.fa-hips:before{content:""}.fa-behance:before{content:""}.fa-reddit:before{content:""}.fa-discord:before{content:""}.fa-chrome:before{content:""}.fa-app-store-ios:before{content:""}.fa-cc-discover:before{content:""}.fa-wpbeginner:before{content:""}.fa-confluence:before{content:""}.fa-mdb:before{content:""}.fa-dochub:before{content:""}.fa-accessible-icon:before{content:""}.fa-ebay:before{content:""}.fa-amazon:before{content:""}.fa-unsplash:before{content:""}.fa-yarn:before{content:""}.fa-square-steam:before{content:""}.fa-steam-square:before{content:""}.fa-500px:before{content:""}.fa-square-vimeo:before{content:""}.fa-vimeo-square:before{content:""}.fa-asymmetrik:before{content:""}.fa-font-awesome:before{content:""}.fa-font-awesome-flag:before{content:""}.fa-font-awesome-logo-full:before{content:""}.fa-gratipay:before{content:""}.fa-apple:before{content:""}.fa-hive:before{content:""}.fa-gitkraken:before{content:""}.fa-keybase:before{content:""}.fa-apple-pay:before{content:""}.fa-padlet:before{content:""}.fa-amazon-pay:before{content:""}.fa-square-github:before{content:""}.fa-github-square:before{content:""}.fa-stumbleupon:before{content:""}.fa-fedex:before{content:""}.fa-phoenix-framework:before{content:""}.fa-shopify:before{content:""}.fa-neos:before{content:""}.fa-square-threads:before{content:""}.fa-hackerrank:before{content:""}.fa-researchgate:before{content:""}.fa-swift:before{content:""}.fa-angular:before{content:""}.fa-speakap:before{content:""}.fa-angrycreative:before{content:""}.fa-y-combinator:before{content:""}.fa-empire:before{content:""}.fa-envira:before{content:""}.fa-square-gitlab:before{content:""}.fa-gitlab-square:before{content:""}.fa-studiovinari:before{content:""}.fa-pied-piper:before{content:""}.fa-wordpress:before{content:""}.fa-product-hunt:before{content:""}.fa-firefox:before{content:""}.fa-linode:before{content:""}.fa-goodreads:before{content:""}.fa-square-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-jsfiddle:before{content:""}.fa-sith:before{content:""}.fa-themeisle:before{content:""}.fa-page4:before{content:""}.fa-hashnode:before{content:""}.fa-react:before{content:""}.fa-cc-paypal:before{content:""}.fa-squarespace:before{content:""}.fa-cc-stripe:before{content:""}.fa-creative-commons-share:before{content:""}.fa-bitcoin:before{content:""}.fa-keycdn:before{content:""}.fa-opera:before{content:""}.fa-itch-io:before{content:""}.fa-umbraco:before{content:""}.fa-galactic-senate:before{content:""}.fa-ubuntu:before{content:""}.fa-draft2digital:before{content:""}.fa-stripe:before{content:""}.fa-houzz:before{content:""}.fa-gg:before{content:""}.fa-dhl:before{content:""}.fa-square-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-xing:before{content:""}.fa-blackberry:before{content:""}.fa-creative-commons-pd:before{content:""}.fa-playstation:before{content:""}.fa-quinscape:before{content:""}.fa-less:before{content:""}.fa-blogger-b:before{content:""}.fa-opencart:before{content:""}.fa-vine:before{content:""}.fa-paypal:before{content:""}.fa-gitlab:before{content:""}.fa-typo3:before{content:""}.fa-reddit-alien:before{content:""}.fa-yahoo:before{content:""}.fa-dailymotion:before{content:""}.fa-affiliatetheme:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-bootstrap:before{content:""}.fa-odnoklassniki:before{content:""}.fa-nfc-symbol:before{content:""}.fa-ethereum:before{content:""}.fa-speaker-deck:before{content:""}.fa-creative-commons-nc-eu:before{content:""}.fa-patreon:before{content:""}.fa-avianex:before{content:""}.fa-ello:before{content:""}.fa-gofore:before{content:""}.fa-bimobject:before{content:""}.fa-facebook-f:before{content:""}.fa-square-google-plus:before{content:""}.fa-google-plus-square:before{content:""}.fa-mandalorian:before{content:""}.fa-first-order-alt:before{content:""}.fa-osi:before{content:""}.fa-google-wallet:before{content:""}.fa-d-and-d-beyond:before{content:""}.fa-periscope:before{content:""}.fa-fulcrum:before{content:""}.fa-cloudscale:before{content:""}.fa-forumbee:before{content:""}.fa-mizuni:before{content:""}.fa-schlix:before{content:""}.fa-square-xing:before{content:""}.fa-xing-square:before{content:""}.fa-bandcamp:before{content:""}.fa-wpforms:before{content:""}.fa-cloudversify:before{content:""}.fa-usps:before{content:""}.fa-megaport:before{content:""}.fa-magento:before{content:""}.fa-spotify:before{content:""}.fa-optin-monster:before{content:""}.fa-fly:before{content:""}.fa-aviato:before{content:""}.fa-itunes:before{content:""}.fa-cuttlefish:before{content:""}.fa-blogger:before{content:""}.fa-flickr:before{content:""}.fa-viber:before{content:""}.fa-soundcloud:before{content:""}.fa-digg:before{content:""}.fa-tencent-weibo:before{content:""}.fa-symfony:before{content:""}.fa-maxcdn:before{content:""}.fa-etsy:before{content:""}.fa-facebook-messenger:before{content:""}.fa-audible:before{content:""}.fa-think-peaks:before{content:""}.fa-bilibili:before{content:""}.fa-erlang:before{content:""}.fa-x-twitter:before{content:""}.fa-cotton-bureau:before{content:""}.fa-dashcube:before{content:""}.fa-42-group:before{content:""}.fa-innosoft:before{content:""}.fa-stack-exchange:before{content:""}.fa-elementor:before{content:""}.fa-square-pied-piper:before{content:""}.fa-pied-piper-square:before{content:""}.fa-creative-commons-nd:before{content:""}.fa-palfed:before{content:""}.fa-superpowers:before{content:""}.fa-resolving:before{content:""}.fa-xbox:before{content:""}.fa-searchengin:before{content:""}.fa-tiktok:before{content:""}.fa-square-facebook:before{content:""}.fa-facebook-square:before{content:""}.fa-renren:before{content:""}.fa-linux:before{content:""}.fa-glide:before{content:""}.fa-linkedin:before{content:""}.fa-hubspot:before{content:""}.fa-deploydog:before{content:""}.fa-twitch:before{content:""}.fa-ravelry:before{content:""}.fa-mixer:before{content:""}.fa-square-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-vimeo:before{content:""}.fa-mendeley:before{content:""}.fa-uniregistry:before{content:""}.fa-figma:before{content:""}.fa-creative-commons-remix:before{content:""}.fa-cc-amazon-pay:before{content:""}.fa-dropbox:before{content:""}.fa-instagram:before{content:""}.fa-cmplid:before{content:""}.fa-facebook:before{content:""}.fa-gripfire:before{content:""}.fa-jedi-order:before{content:""}.fa-uikit:before{content:""}.fa-fort-awesome-alt:before{content:""}.fa-phabricator:before{content:""}.fa-ussunnah:before{content:""}.fa-earlybirds:before{content:""}.fa-trade-federation:before{content:""}.fa-autoprefixer:before{content:""}.fa-whatsapp:before{content:""}.fa-slideshare:before{content:""}.fa-google-play:before{content:""}.fa-viadeo:before{content:""}.fa-line:before{content:""}.fa-google-drive:before{content:""}.fa-servicestack:before{content:""}.fa-simplybuilt:before{content:""}.fa-bitbucket:before{content:""}.fa-imdb:before{content:""}.fa-deezer:before{content:""}.fa-raspberry-pi:before{content:""}.fa-jira:before{content:""}.fa-docker:before{content:""}.fa-screenpal:before{content:""}.fa-bluetooth:before{content:""}.fa-gitter:before{content:""}.fa-d-and-d:before{content:""}.fa-microblog:before{content:""}.fa-cc-diners-club:before{content:""}.fa-gg-circle:before{content:""}.fa-pied-piper-hat:before{content:""}.fa-kickstarter-k:before{content:""}.fa-yandex:before{content:""}.fa-readme:before{content:""}.fa-html5:before{content:""}.fa-sellsy:before{content:""}.fa-sass:before{content:""}.fa-wirsindhandwerk:before{content:""}.fa-wsh:before{content:""}.fa-buromobelexperte:before{content:""}.fa-salesforce:before{content:""}.fa-octopus-deploy:before{content:""}.fa-medapps:before{content:""}.fa-ns8:before{content:""}.fa-pinterest-p:before{content:""}.fa-apper:before{content:""}.fa-fort-awesome:before{content:""}.fa-waze:before{content:""}.fa-cc-jcb:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-fantasy-flight-games:before{content:""}.fa-rust:before{content:""}.fa-wix:before{content:""}.fa-square-behance:before{content:""}.fa-behance-square:before{content:""}.fa-supple:before{content:""}.fa-rebel:before{content:""}.fa-css3:before{content:""}.fa-staylinked:before{content:""}.fa-kaggle:before{content:""}.fa-space-awesome:before{content:""}.fa-deviantart:before{content:""}.fa-cpanel:before{content:""}.fa-goodreads-g:before{content:""}.fa-square-git:before{content:""}.fa-git-square:before{content:""}.fa-square-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-trello:before{content:""}.fa-creative-commons-nc-jp:before{content:""}.fa-get-pocket:before{content:""}.fa-perbyte:before{content:""}.fa-grunt:before{content:""}.fa-weebly:before{content:""}.fa-connectdevelop:before{content:""}.fa-leanpub:before{content:""}.fa-black-tie:before{content:""}.fa-themeco:before{content:""}.fa-python:before{content:""}.fa-android:before{content:""}.fa-bots:before{content:""}.fa-free-code-camp:before{content:""}.fa-hornbill:before{content:""}.fa-js:before{content:""}.fa-ideal:before{content:""}.fa-git:before{content:""}.fa-dev:before{content:""}.fa-sketch:before{content:""}.fa-yandex-international:before{content:""}.fa-cc-amex:before{content:""}.fa-uber:before{content:""}.fa-github:before{content:""}.fa-php:before{content:""}.fa-alipay:before{content:""}.fa-youtube:before{content:""}.fa-skyatlas:before{content:""}.fa-firefox-browser:before{content:""}.fa-replyd:before{content:""}.fa-suse:before{content:""}.fa-jenkins:before{content:""}.fa-twitter:before{content:""}.fa-rockrms:before{content:""}.fa-pinterest:before{content:""}.fa-buffer:before{content:""}.fa-npm:before{content:""}.fa-yammer:before{content:""}.fa-btc:before{content:""}.fa-dribbble:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-internet-explorer:before{content:""}.fa-stubber:before{content:""}.fa-telegram:before{content:""}.fa-telegram-plane:before{content:""}.fa-old-republic:before{content:""}.fa-odysee:before{content:""}.fa-square-whatsapp:before{content:""}.fa-whatsapp-square:before{content:""}.fa-node-js:before{content:""}.fa-edge-legacy:before{content:""}.fa-slack:before{content:""}.fa-slack-hash:before{content:""}.fa-medrt:before{content:""}.fa-usb:before{content:""}.fa-tumblr:before{content:""}.fa-vaadin:before{content:""}.fa-quora:before{content:""}.fa-square-x-twitter:before{content:""}.fa-reacteurope:before{content:""}.fa-medium:before{content:""}.fa-medium-m:before{content:""}.fa-amilia:before{content:""}.fa-mixcloud:before{content:""}.fa-flipboard:before{content:""}.fa-viacoin:before{content:""}.fa-critical-role:before{content:""}.fa-sitrox:before{content:""}.fa-discourse:before{content:""}.fa-joomla:before{content:""}.fa-mastodon:before{content:""}.fa-airbnb:before{content:""}.fa-wolf-pack-battalion:before{content:""}.fa-buy-n-large:before{content:""}.fa-gulp:before{content:""}.fa-creative-commons-sampling-plus:before{content:""}.fa-strava:before{content:""}.fa-ember:before{content:""}.fa-canadian-maple-leaf:before{content:""}.fa-teamspeak:before{content:""}.fa-pushed:before{content:""}.fa-wordpress-simple:before{content:""}.fa-nutritionix:before{content:""}.fa-wodu:before{content:""}.fa-google-pay:before{content:""}.fa-intercom:before{content:""}.fa-zhihu:before{content:""}.fa-korvue:before{content:""}.fa-pix:before{content:""}.fa-steam-symbol:before{content:""}/*! - * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com + */.fa{font-family:var(--fa-style-family, "Font Awesome 6 Free");font-weight:var(--fa-style, 900)}.fa,.fa-classic,.fa-sharp,.fas,.fa-solid,.far,.fa-regular,.fab,.fa-brands{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display, inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fas,.fa-classic,.fa-solid,.far,.fa-regular{font-family:"Font Awesome 6 Free"}.fab,.fa-brands{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.0833333337em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.0714285718em;vertical-align:.0535714295em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.0416666682em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin, 2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em) * -1);position:absolute;text-align:center;width:var(--fa-li-width, 2em);line-height:inherit}.fa-border{border-color:var(--fa-border-color, #eee);border-radius:var(--fa-border-radius, .1em);border-style:var(--fa-border-style, solid);border-width:var(--fa-border-width, .08em);padding:var(--fa-border-padding, .2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin, .3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin, .3em)}.fa-beat{animation-name:fa-beat;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, ease-in-out)}.fa-bounce{animation-name:fa-bounce;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, cubic-bezier(.28, .84, .42, 1))}.fa-fade{animation-name:fa-fade;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, cubic-bezier(.4, 0, .6, 1))}.fa-beat-fade{animation-name:fa-beat-fade;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, cubic-bezier(.4, 0, .6, 1))}.fa-flip{animation-name:fa-flip;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, ease-in-out)}.fa-shake{animation-name:fa-shake;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, linear)}.fa-spin{animation-name:fa-spin;animation-delay:var(--fa-animation-delay, 0s);animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 2s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, linear)}.fa-spin-reverse{--fa-animation-direction: reverse}.fa-pulse,.fa-spin-pulse{animation-name:fa-spin;animation-direction:var(--fa-animation-direction, normal);animation-duration:var(--fa-animation-duration, 1s);animation-iteration-count:var(--fa-animation-iteration-count, infinite);animation-timing-function:var(--fa-animation-timing, steps(8))}@media (prefers-reduced-motion: reduce){.fa-beat,.fa-bounce,.fa-fade,.fa-beat-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{animation-delay:-1ms;animation-duration:1ms;animation-iteration-count:1;transition-delay:0s;transition-duration:0s}}@keyframes fa-beat{0%,90%{transform:scale(1)}45%{transform:scale(var(--fa-beat-scale, 1.25))}}@keyframes fa-bounce{0%{transform:scale(1) translateY(0)}10%{transform:scale(var(--fa-bounce-start-scale-x, 1.1),var(--fa-bounce-start-scale-y, .9)) translateY(0)}30%{transform:scale(var(--fa-bounce-jump-scale-x, .9),var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -.5em))}50%{transform:scale(var(--fa-bounce-land-scale-x, 1.05),var(--fa-bounce-land-scale-y, .95)) translateY(0)}57%{transform:scale(1) translateY(var(--fa-bounce-rebound, -.125em))}64%{transform:scale(1) translateY(0)}to{transform:scale(1) translateY(0)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity, .4)}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity, .4);transform:scale(1)}50%{opacity:1;transform:scale(var(--fa-beat-fade-scale, 1.125))}}@keyframes fa-flip{50%{transform:rotate3d(var(--fa-flip-x, 0),var(--fa-flip-y, 1),var(--fa-flip-z, 0),var(--fa-flip-angle, -180deg))}}@keyframes fa-shake{0%{transform:rotate(-15deg)}4%{transform:rotate(15deg)}8%,24%{transform:rotate(-18deg)}12%,28%{transform:rotate(18deg)}16%{transform:rotate(-22deg)}20%{transform:rotate(22deg)}32%{transform:rotate(-12deg)}36%{transform:rotate(12deg)}40%,to{transform:rotate(0)}}@keyframes fa-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.fa-rotate-90{transform:rotate(90deg)}.fa-rotate-180{transform:rotate(180deg)}.fa-rotate-270{transform:rotate(270deg)}.fa-flip-horizontal{transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}.fa-rotate-by{transform:rotate(var(--fa-rotate-angle, none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index, auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse, #fff)}.fa-0:before{content:"0"}.fa-1:before{content:"1"}.fa-2:before{content:"2"}.fa-3:before{content:"3"}.fa-4:before{content:"4"}.fa-5:before{content:"5"}.fa-6:before{content:"6"}.fa-7:before{content:"7"}.fa-8:before{content:"8"}.fa-9:before{content:"9"}.fa-fill-drip:before{content:""}.fa-arrows-to-circle:before{content:""}.fa-circle-chevron-right:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-at:before{content:"@"}.fa-trash-can:before{content:""}.fa-trash-alt:before{content:""}.fa-text-height:before{content:""}.fa-user-xmark:before{content:""}.fa-user-times:before{content:""}.fa-stethoscope:before{content:""}.fa-message:before{content:""}.fa-comment-alt:before{content:""}.fa-info:before{content:""}.fa-down-left-and-up-right-to-center:before{content:""}.fa-compress-alt:before{content:""}.fa-explosion:before{content:""}.fa-file-lines:before{content:""}.fa-file-alt:before{content:""}.fa-file-text:before{content:""}.fa-wave-square:before{content:""}.fa-ring:before{content:""}.fa-building-un:before{content:""}.fa-dice-three:before{content:""}.fa-calendar-days:before{content:""}.fa-calendar-alt:before{content:""}.fa-anchor-circle-check:before{content:""}.fa-building-circle-arrow-right:before{content:""}.fa-volleyball:before{content:""}.fa-volleyball-ball:before{content:""}.fa-arrows-up-to-line:before{content:""}.fa-sort-down:before{content:""}.fa-sort-desc:before{content:""}.fa-circle-minus:before{content:""}.fa-minus-circle:before{content:""}.fa-door-open:before{content:""}.fa-right-from-bracket:before{content:""}.fa-sign-out-alt:before{content:""}.fa-atom:before{content:""}.fa-soap:before{content:""}.fa-icons:before{content:""}.fa-heart-music-camera-bolt:before{content:""}.fa-microphone-lines-slash:before{content:""}.fa-microphone-alt-slash:before{content:""}.fa-bridge-circle-check:before{content:""}.fa-pump-medical:before{content:""}.fa-fingerprint:before{content:""}.fa-hand-point-right:before{content:""}.fa-magnifying-glass-location:before{content:""}.fa-search-location:before{content:""}.fa-forward-step:before{content:""}.fa-step-forward:before{content:""}.fa-face-smile-beam:before{content:""}.fa-smile-beam:before{content:""}.fa-flag-checkered:before{content:""}.fa-football:before{content:""}.fa-football-ball:before{content:""}.fa-school-circle-exclamation:before{content:""}.fa-crop:before{content:""}.fa-angles-down:before{content:""}.fa-angle-double-down:before{content:""}.fa-users-rectangle:before{content:""}.fa-people-roof:before{content:""}.fa-people-line:before{content:""}.fa-beer-mug-empty:before{content:""}.fa-beer:before{content:""}.fa-diagram-predecessor:before{content:""}.fa-arrow-up-long:before{content:""}.fa-long-arrow-up:before{content:""}.fa-fire-flame-simple:before{content:""}.fa-burn:before{content:""}.fa-person:before{content:""}.fa-male:before{content:""}.fa-laptop:before{content:""}.fa-file-csv:before{content:""}.fa-menorah:before{content:""}.fa-truck-plane:before{content:""}.fa-record-vinyl:before{content:""}.fa-face-grin-stars:before{content:""}.fa-grin-stars:before{content:""}.fa-bong:before{content:""}.fa-spaghetti-monster-flying:before{content:""}.fa-pastafarianism:before{content:""}.fa-arrow-down-up-across-line:before{content:""}.fa-spoon:before{content:""}.fa-utensil-spoon:before{content:""}.fa-jar-wheat:before{content:""}.fa-envelopes-bulk:before{content:""}.fa-mail-bulk:before{content:""}.fa-file-circle-exclamation:before{content:""}.fa-circle-h:before{content:""}.fa-hospital-symbol:before{content:""}.fa-pager:before{content:""}.fa-address-book:before{content:""}.fa-contact-book:before{content:""}.fa-strikethrough:before{content:""}.fa-k:before{content:"K"}.fa-landmark-flag:before{content:""}.fa-pencil:before{content:""}.fa-pencil-alt:before{content:""}.fa-backward:before{content:""}.fa-caret-right:before{content:""}.fa-comments:before{content:""}.fa-paste:before{content:""}.fa-file-clipboard:before{content:""}.fa-code-pull-request:before{content:""}.fa-clipboard-list:before{content:""}.fa-truck-ramp-box:before{content:""}.fa-truck-loading:before{content:""}.fa-user-check:before{content:""}.fa-vial-virus:before{content:""}.fa-sheet-plastic:before{content:""}.fa-blog:before{content:""}.fa-user-ninja:before{content:""}.fa-person-arrow-up-from-line:before{content:""}.fa-scroll-torah:before{content:""}.fa-torah:before{content:""}.fa-broom-ball:before{content:""}.fa-quidditch:before{content:""}.fa-quidditch-broom-ball:before{content:""}.fa-toggle-off:before{content:""}.fa-box-archive:before{content:""}.fa-archive:before{content:""}.fa-person-drowning:before{content:""}.fa-arrow-down-9-1:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-sort-numeric-down-alt:before{content:""}.fa-face-grin-tongue-squint:before{content:""}.fa-grin-tongue-squint:before{content:""}.fa-spray-can:before{content:""}.fa-truck-monster:before{content:""}.fa-w:before{content:"W"}.fa-earth-africa:before{content:""}.fa-globe-africa:before{content:""}.fa-rainbow:before{content:""}.fa-circle-notch:before{content:""}.fa-tablet-screen-button:before{content:""}.fa-tablet-alt:before{content:""}.fa-paw:before{content:""}.fa-cloud:before{content:""}.fa-trowel-bricks:before{content:""}.fa-face-flushed:before{content:""}.fa-flushed:before{content:""}.fa-hospital-user:before{content:""}.fa-tent-arrow-left-right:before{content:""}.fa-gavel:before{content:""}.fa-legal:before{content:""}.fa-binoculars:before{content:""}.fa-microphone-slash:before{content:""}.fa-box-tissue:before{content:""}.fa-motorcycle:before{content:""}.fa-bell-concierge:before{content:""}.fa-concierge-bell:before{content:""}.fa-pen-ruler:before{content:""}.fa-pencil-ruler:before{content:""}.fa-people-arrows:before{content:""}.fa-people-arrows-left-right:before{content:""}.fa-mars-and-venus-burst:before{content:""}.fa-square-caret-right:before{content:""}.fa-caret-square-right:before{content:""}.fa-scissors:before{content:""}.fa-cut:before{content:""}.fa-sun-plant-wilt:before{content:""}.fa-toilets-portable:before{content:""}.fa-hockey-puck:before{content:""}.fa-table:before{content:""}.fa-magnifying-glass-arrow-right:before{content:""}.fa-tachograph-digital:before{content:""}.fa-digital-tachograph:before{content:""}.fa-users-slash:before{content:""}.fa-clover:before{content:""}.fa-reply:before{content:""}.fa-mail-reply:before{content:""}.fa-star-and-crescent:before{content:""}.fa-house-fire:before{content:""}.fa-square-minus:before{content:""}.fa-minus-square:before{content:""}.fa-helicopter:before{content:""}.fa-compass:before{content:""}.fa-square-caret-down:before{content:""}.fa-caret-square-down:before{content:""}.fa-file-circle-question:before{content:""}.fa-laptop-code:before{content:""}.fa-swatchbook:before{content:""}.fa-prescription-bottle:before{content:""}.fa-bars:before{content:""}.fa-navicon:before{content:""}.fa-people-group:before{content:""}.fa-hourglass-end:before{content:""}.fa-hourglass-3:before{content:""}.fa-heart-crack:before{content:""}.fa-heart-broken:before{content:""}.fa-square-up-right:before{content:""}.fa-external-link-square-alt:before{content:""}.fa-face-kiss-beam:before{content:""}.fa-kiss-beam:before{content:""}.fa-film:before{content:""}.fa-ruler-horizontal:before{content:""}.fa-people-robbery:before{content:""}.fa-lightbulb:before{content:""}.fa-caret-left:before{content:""}.fa-circle-exclamation:before{content:""}.fa-exclamation-circle:before{content:""}.fa-school-circle-xmark:before{content:""}.fa-arrow-right-from-bracket:before{content:""}.fa-sign-out:before{content:""}.fa-circle-chevron-down:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-unlock-keyhole:before{content:""}.fa-unlock-alt:before{content:""}.fa-cloud-showers-heavy:before{content:""}.fa-headphones-simple:before{content:""}.fa-headphones-alt:before{content:""}.fa-sitemap:before{content:""}.fa-circle-dollar-to-slot:before{content:""}.fa-donate:before{content:""}.fa-memory:before{content:""}.fa-road-spikes:before{content:""}.fa-fire-burner:before{content:""}.fa-flag:before{content:""}.fa-hanukiah:before{content:""}.fa-feather:before{content:""}.fa-volume-low:before{content:""}.fa-volume-down:before{content:""}.fa-comment-slash:before{content:""}.fa-cloud-sun-rain:before{content:""}.fa-compress:before{content:""}.fa-wheat-awn:before{content:""}.fa-wheat-alt:before{content:""}.fa-ankh:before{content:""}.fa-hands-holding-child:before{content:""}.fa-asterisk:before{content:"*"}.fa-square-check:before{content:""}.fa-check-square:before{content:""}.fa-peseta-sign:before{content:""}.fa-heading:before{content:""}.fa-header:before{content:""}.fa-ghost:before{content:""}.fa-list:before{content:""}.fa-list-squares:before{content:""}.fa-square-phone-flip:before{content:""}.fa-phone-square-alt:before{content:""}.fa-cart-plus:before{content:""}.fa-gamepad:before{content:""}.fa-circle-dot:before{content:""}.fa-dot-circle:before{content:""}.fa-face-dizzy:before{content:""}.fa-dizzy:before{content:""}.fa-egg:before{content:""}.fa-house-medical-circle-xmark:before{content:""}.fa-campground:before{content:""}.fa-folder-plus:before{content:""}.fa-futbol:before{content:""}.fa-futbol-ball:before{content:""}.fa-soccer-ball:before{content:""}.fa-paintbrush:before{content:""}.fa-paint-brush:before{content:""}.fa-lock:before{content:""}.fa-gas-pump:before{content:""}.fa-hot-tub-person:before{content:""}.fa-hot-tub:before{content:""}.fa-map-location:before{content:""}.fa-map-marked:before{content:""}.fa-house-flood-water:before{content:""}.fa-tree:before{content:""}.fa-bridge-lock:before{content:""}.fa-sack-dollar:before{content:""}.fa-pen-to-square:before{content:""}.fa-edit:before{content:""}.fa-car-side:before{content:""}.fa-share-nodes:before{content:""}.fa-share-alt:before{content:""}.fa-heart-circle-minus:before{content:""}.fa-hourglass-half:before{content:""}.fa-hourglass-2:before{content:""}.fa-microscope:before{content:""}.fa-sink:before{content:""}.fa-bag-shopping:before{content:""}.fa-shopping-bag:before{content:""}.fa-arrow-down-z-a:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-alpha-down-alt:before{content:""}.fa-mitten:before{content:""}.fa-person-rays:before{content:""}.fa-users:before{content:""}.fa-eye-slash:before{content:""}.fa-flask-vial:before{content:""}.fa-hand:before{content:""}.fa-hand-paper:before{content:""}.fa-om:before{content:""}.fa-worm:before{content:""}.fa-house-circle-xmark:before{content:""}.fa-plug:before{content:""}.fa-chevron-up:before{content:""}.fa-hand-spock:before{content:""}.fa-stopwatch:before{content:""}.fa-face-kiss:before{content:""}.fa-kiss:before{content:""}.fa-bridge-circle-xmark:before{content:""}.fa-face-grin-tongue:before{content:""}.fa-grin-tongue:before{content:""}.fa-chess-bishop:before{content:""}.fa-face-grin-wink:before{content:""}.fa-grin-wink:before{content:""}.fa-ear-deaf:before{content:""}.fa-deaf:before{content:""}.fa-deafness:before{content:""}.fa-hard-of-hearing:before{content:""}.fa-road-circle-check:before{content:""}.fa-dice-five:before{content:""}.fa-square-rss:before{content:""}.fa-rss-square:before{content:""}.fa-land-mine-on:before{content:""}.fa-i-cursor:before{content:""}.fa-stamp:before{content:""}.fa-stairs:before{content:""}.fa-i:before{content:"I"}.fa-hryvnia-sign:before{content:""}.fa-hryvnia:before{content:""}.fa-pills:before{content:""}.fa-face-grin-wide:before{content:""}.fa-grin-alt:before{content:""}.fa-tooth:before{content:""}.fa-v:before{content:"V"}.fa-bangladeshi-taka-sign:before{content:""}.fa-bicycle:before{content:""}.fa-staff-snake:before{content:""}.fa-rod-asclepius:before{content:""}.fa-rod-snake:before{content:""}.fa-staff-aesculapius:before{content:""}.fa-head-side-cough-slash:before{content:""}.fa-truck-medical:before{content:""}.fa-ambulance:before{content:""}.fa-wheat-awn-circle-exclamation:before{content:""}.fa-snowman:before{content:""}.fa-mortar-pestle:before{content:""}.fa-road-barrier:before{content:""}.fa-school:before{content:""}.fa-igloo:before{content:""}.fa-joint:before{content:""}.fa-angle-right:before{content:""}.fa-horse:before{content:""}.fa-q:before{content:"Q"}.fa-g:before{content:"G"}.fa-notes-medical:before{content:""}.fa-temperature-half:before{content:""}.fa-temperature-2:before{content:""}.fa-thermometer-2:before{content:""}.fa-thermometer-half:before{content:""}.fa-dong-sign:before{content:""}.fa-capsules:before{content:""}.fa-poo-storm:before{content:""}.fa-poo-bolt:before{content:""}.fa-face-frown-open:before{content:""}.fa-frown-open:before{content:""}.fa-hand-point-up:before{content:""}.fa-money-bill:before{content:""}.fa-bookmark:before{content:""}.fa-align-justify:before{content:""}.fa-umbrella-beach:before{content:""}.fa-helmet-un:before{content:""}.fa-bullseye:before{content:""}.fa-bacon:before{content:""}.fa-hand-point-down:before{content:""}.fa-arrow-up-from-bracket:before{content:""}.fa-folder:before{content:""}.fa-folder-blank:before{content:""}.fa-file-waveform:before{content:""}.fa-file-medical-alt:before{content:""}.fa-radiation:before{content:""}.fa-chart-simple:before{content:""}.fa-mars-stroke:before{content:""}.fa-vial:before{content:""}.fa-gauge:before{content:""}.fa-dashboard:before{content:""}.fa-gauge-med:before{content:""}.fa-tachometer-alt-average:before{content:""}.fa-wand-magic-sparkles:before{content:""}.fa-magic-wand-sparkles:before{content:""}.fa-e:before{content:"E"}.fa-pen-clip:before{content:""}.fa-pen-alt:before{content:""}.fa-bridge-circle-exclamation:before{content:""}.fa-user:before{content:""}.fa-school-circle-check:before{content:""}.fa-dumpster:before{content:""}.fa-van-shuttle:before{content:""}.fa-shuttle-van:before{content:""}.fa-building-user:before{content:""}.fa-square-caret-left:before{content:""}.fa-caret-square-left:before{content:""}.fa-highlighter:before{content:""}.fa-key:before{content:""}.fa-bullhorn:before{content:""}.fa-globe:before{content:""}.fa-synagogue:before{content:""}.fa-person-half-dress:before{content:""}.fa-road-bridge:before{content:""}.fa-location-arrow:before{content:""}.fa-c:before{content:"C"}.fa-tablet-button:before{content:""}.fa-building-lock:before{content:""}.fa-pizza-slice:before{content:""}.fa-money-bill-wave:before{content:""}.fa-chart-area:before{content:""}.fa-area-chart:before{content:""}.fa-house-flag:before{content:""}.fa-person-circle-minus:before{content:""}.fa-ban:before{content:""}.fa-cancel:before{content:""}.fa-camera-rotate:before{content:""}.fa-spray-can-sparkles:before{content:""}.fa-air-freshener:before{content:""}.fa-star:before{content:""}.fa-repeat:before{content:""}.fa-cross:before{content:""}.fa-box:before{content:""}.fa-venus-mars:before{content:""}.fa-arrow-pointer:before{content:""}.fa-mouse-pointer:before{content:""}.fa-maximize:before{content:""}.fa-expand-arrows-alt:before{content:""}.fa-charging-station:before{content:""}.fa-shapes:before{content:""}.fa-triangle-circle-square:before{content:""}.fa-shuffle:before{content:""}.fa-random:before{content:""}.fa-person-running:before{content:""}.fa-running:before{content:""}.fa-mobile-retro:before{content:""}.fa-grip-lines-vertical:before{content:""}.fa-spider:before{content:""}.fa-hands-bound:before{content:""}.fa-file-invoice-dollar:before{content:""}.fa-plane-circle-exclamation:before{content:""}.fa-x-ray:before{content:""}.fa-spell-check:before{content:""}.fa-slash:before{content:""}.fa-computer-mouse:before{content:""}.fa-mouse:before{content:""}.fa-arrow-right-to-bracket:before{content:""}.fa-sign-in:before{content:""}.fa-shop-slash:before{content:""}.fa-store-alt-slash:before{content:""}.fa-server:before{content:""}.fa-virus-covid-slash:before{content:""}.fa-shop-lock:before{content:""}.fa-hourglass-start:before{content:""}.fa-hourglass-1:before{content:""}.fa-blender-phone:before{content:""}.fa-building-wheat:before{content:""}.fa-person-breastfeeding:before{content:""}.fa-right-to-bracket:before{content:""}.fa-sign-in-alt:before{content:""}.fa-venus:before{content:""}.fa-passport:before{content:""}.fa-heart-pulse:before{content:""}.fa-heartbeat:before{content:""}.fa-people-carry-box:before{content:""}.fa-people-carry:before{content:""}.fa-temperature-high:before{content:""}.fa-microchip:before{content:""}.fa-crown:before{content:""}.fa-weight-hanging:before{content:""}.fa-xmarks-lines:before{content:""}.fa-file-prescription:before{content:""}.fa-weight-scale:before{content:""}.fa-weight:before{content:""}.fa-user-group:before{content:""}.fa-user-friends:before{content:""}.fa-arrow-up-a-z:before{content:""}.fa-sort-alpha-up:before{content:""}.fa-chess-knight:before{content:""}.fa-face-laugh-squint:before{content:""}.fa-laugh-squint:before{content:""}.fa-wheelchair:before{content:""}.fa-circle-arrow-up:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-toggle-on:before{content:""}.fa-person-walking:before{content:""}.fa-walking:before{content:""}.fa-l:before{content:"L"}.fa-fire:before{content:""}.fa-bed-pulse:before{content:""}.fa-procedures:before{content:""}.fa-shuttle-space:before{content:""}.fa-space-shuttle:before{content:""}.fa-face-laugh:before{content:""}.fa-laugh:before{content:""}.fa-folder-open:before{content:""}.fa-heart-circle-plus:before{content:""}.fa-code-fork:before{content:""}.fa-city:before{content:""}.fa-microphone-lines:before{content:""}.fa-microphone-alt:before{content:""}.fa-pepper-hot:before{content:""}.fa-unlock:before{content:""}.fa-colon-sign:before{content:""}.fa-headset:before{content:""}.fa-store-slash:before{content:""}.fa-road-circle-xmark:before{content:""}.fa-user-minus:before{content:""}.fa-mars-stroke-up:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-champagne-glasses:before{content:""}.fa-glass-cheers:before{content:""}.fa-clipboard:before{content:""}.fa-house-circle-exclamation:before{content:""}.fa-file-arrow-up:before{content:""}.fa-file-upload:before{content:""}.fa-wifi:before{content:""}.fa-wifi-3:before{content:""}.fa-wifi-strong:before{content:""}.fa-bath:before{content:""}.fa-bathtub:before{content:""}.fa-underline:before{content:""}.fa-user-pen:before{content:""}.fa-user-edit:before{content:""}.fa-signature:before{content:""}.fa-stroopwafel:before{content:""}.fa-bold:before{content:""}.fa-anchor-lock:before{content:""}.fa-building-ngo:before{content:""}.fa-manat-sign:before{content:""}.fa-not-equal:before{content:""}.fa-border-top-left:before{content:""}.fa-border-style:before{content:""}.fa-map-location-dot:before{content:""}.fa-map-marked-alt:before{content:""}.fa-jedi:before{content:""}.fa-square-poll-vertical:before{content:""}.fa-poll:before{content:""}.fa-mug-hot:before{content:""}.fa-car-battery:before{content:""}.fa-battery-car:before{content:""}.fa-gift:before{content:""}.fa-dice-two:before{content:""}.fa-chess-queen:before{content:""}.fa-glasses:before{content:""}.fa-chess-board:before{content:""}.fa-building-circle-check:before{content:""}.fa-person-chalkboard:before{content:""}.fa-mars-stroke-right:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-hand-back-fist:before{content:""}.fa-hand-rock:before{content:""}.fa-square-caret-up:before{content:""}.fa-caret-square-up:before{content:""}.fa-cloud-showers-water:before{content:""}.fa-chart-bar:before{content:""}.fa-bar-chart:before{content:""}.fa-hands-bubbles:before{content:""}.fa-hands-wash:before{content:""}.fa-less-than-equal:before{content:""}.fa-train:before{content:""}.fa-eye-low-vision:before{content:""}.fa-low-vision:before{content:""}.fa-crow:before{content:""}.fa-sailboat:before{content:""}.fa-window-restore:before{content:""}.fa-square-plus:before{content:""}.fa-plus-square:before{content:""}.fa-torii-gate:before{content:""}.fa-frog:before{content:""}.fa-bucket:before{content:""}.fa-image:before{content:""}.fa-microphone:before{content:""}.fa-cow:before{content:""}.fa-caret-up:before{content:""}.fa-screwdriver:before{content:""}.fa-folder-closed:before{content:""}.fa-house-tsunami:before{content:""}.fa-square-nfi:before{content:""}.fa-arrow-up-from-ground-water:before{content:""}.fa-martini-glass:before{content:""}.fa-glass-martini-alt:before{content:""}.fa-rotate-left:before{content:""}.fa-rotate-back:before{content:""}.fa-rotate-backward:before{content:""}.fa-undo-alt:before{content:""}.fa-table-columns:before{content:""}.fa-columns:before{content:""}.fa-lemon:before{content:""}.fa-head-side-mask:before{content:""}.fa-handshake:before{content:""}.fa-gem:before{content:""}.fa-dolly:before{content:""}.fa-dolly-box:before{content:""}.fa-smoking:before{content:""}.fa-minimize:before{content:""}.fa-compress-arrows-alt:before{content:""}.fa-monument:before{content:""}.fa-snowplow:before{content:""}.fa-angles-right:before{content:""}.fa-angle-double-right:before{content:""}.fa-cannabis:before{content:""}.fa-circle-play:before{content:""}.fa-play-circle:before{content:""}.fa-tablets:before{content:""}.fa-ethernet:before{content:""}.fa-euro-sign:before{content:""}.fa-eur:before{content:""}.fa-euro:before{content:""}.fa-chair:before{content:""}.fa-circle-check:before{content:""}.fa-check-circle:before{content:""}.fa-circle-stop:before{content:""}.fa-stop-circle:before{content:""}.fa-compass-drafting:before{content:""}.fa-drafting-compass:before{content:""}.fa-plate-wheat:before{content:""}.fa-icicles:before{content:""}.fa-person-shelter:before{content:""}.fa-neuter:before{content:""}.fa-id-badge:before{content:""}.fa-marker:before{content:""}.fa-face-laugh-beam:before{content:""}.fa-laugh-beam:before{content:""}.fa-helicopter-symbol:before{content:""}.fa-universal-access:before{content:""}.fa-circle-chevron-up:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-lari-sign:before{content:""}.fa-volcano:before{content:""}.fa-person-walking-dashed-line-arrow-right:before{content:""}.fa-sterling-sign:before{content:""}.fa-gbp:before{content:""}.fa-pound-sign:before{content:""}.fa-viruses:before{content:""}.fa-square-person-confined:before{content:""}.fa-user-tie:before{content:""}.fa-arrow-down-long:before{content:""}.fa-long-arrow-down:before{content:""}.fa-tent-arrow-down-to-line:before{content:""}.fa-certificate:before{content:""}.fa-reply-all:before{content:""}.fa-mail-reply-all:before{content:""}.fa-suitcase:before{content:""}.fa-person-skating:before{content:""}.fa-skating:before{content:""}.fa-filter-circle-dollar:before{content:""}.fa-funnel-dollar:before{content:""}.fa-camera-retro:before{content:""}.fa-circle-arrow-down:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-file-import:before{content:""}.fa-arrow-right-to-file:before{content:""}.fa-square-arrow-up-right:before{content:""}.fa-external-link-square:before{content:""}.fa-box-open:before{content:""}.fa-scroll:before{content:""}.fa-spa:before{content:""}.fa-location-pin-lock:before{content:""}.fa-pause:before{content:""}.fa-hill-avalanche:before{content:""}.fa-temperature-empty:before{content:""}.fa-temperature-0:before{content:""}.fa-thermometer-0:before{content:""}.fa-thermometer-empty:before{content:""}.fa-bomb:before{content:""}.fa-registered:before{content:""}.fa-address-card:before{content:""}.fa-contact-card:before{content:""}.fa-vcard:before{content:""}.fa-scale-unbalanced-flip:before{content:""}.fa-balance-scale-right:before{content:""}.fa-subscript:before{content:""}.fa-diamond-turn-right:before{content:""}.fa-directions:before{content:""}.fa-burst:before{content:""}.fa-house-laptop:before{content:""}.fa-laptop-house:before{content:""}.fa-face-tired:before{content:""}.fa-tired:before{content:""}.fa-money-bills:before{content:""}.fa-smog:before{content:""}.fa-crutch:before{content:""}.fa-cloud-arrow-up:before{content:""}.fa-cloud-upload:before{content:""}.fa-cloud-upload-alt:before{content:""}.fa-palette:before{content:""}.fa-arrows-turn-right:before{content:""}.fa-vest:before{content:""}.fa-ferry:before{content:""}.fa-arrows-down-to-people:before{content:""}.fa-seedling:before{content:""}.fa-sprout:before{content:""}.fa-left-right:before{content:""}.fa-arrows-alt-h:before{content:""}.fa-boxes-packing:before{content:""}.fa-circle-arrow-left:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-group-arrows-rotate:before{content:""}.fa-bowl-food:before{content:""}.fa-candy-cane:before{content:""}.fa-arrow-down-wide-short:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-down:before{content:""}.fa-cloud-bolt:before{content:""}.fa-thunderstorm:before{content:""}.fa-text-slash:before{content:""}.fa-remove-format:before{content:""}.fa-face-smile-wink:before{content:""}.fa-smile-wink:before{content:""}.fa-file-word:before{content:""}.fa-file-powerpoint:before{content:""}.fa-arrows-left-right:before{content:""}.fa-arrows-h:before{content:""}.fa-house-lock:before{content:""}.fa-cloud-arrow-down:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-download-alt:before{content:""}.fa-children:before{content:""}.fa-chalkboard:before{content:""}.fa-blackboard:before{content:""}.fa-user-large-slash:before{content:""}.fa-user-alt-slash:before{content:""}.fa-envelope-open:before{content:""}.fa-handshake-simple-slash:before{content:""}.fa-handshake-alt-slash:before{content:""}.fa-mattress-pillow:before{content:""}.fa-guarani-sign:before{content:""}.fa-arrows-rotate:before{content:""}.fa-refresh:before{content:""}.fa-sync:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-cruzeiro-sign:before{content:""}.fa-greater-than-equal:before{content:""}.fa-shield-halved:before{content:""}.fa-shield-alt:before{content:""}.fa-book-atlas:before{content:""}.fa-atlas:before{content:""}.fa-virus:before{content:""}.fa-envelope-circle-check:before{content:""}.fa-layer-group:before{content:""}.fa-arrows-to-dot:before{content:""}.fa-archway:before{content:""}.fa-heart-circle-check:before{content:""}.fa-house-chimney-crack:before{content:""}.fa-house-damage:before{content:""}.fa-file-zipper:before{content:""}.fa-file-archive:before{content:""}.fa-square:before{content:""}.fa-martini-glass-empty:before{content:""}.fa-glass-martini:before{content:""}.fa-couch:before{content:""}.fa-cedi-sign:before{content:""}.fa-italic:before{content:""}.fa-church:before{content:""}.fa-comments-dollar:before{content:""}.fa-democrat:before{content:""}.fa-z:before{content:"Z"}.fa-person-skiing:before{content:""}.fa-skiing:before{content:""}.fa-road-lock:before{content:""}.fa-a:before{content:"A"}.fa-temperature-arrow-down:before{content:""}.fa-temperature-down:before{content:""}.fa-feather-pointed:before{content:""}.fa-feather-alt:before{content:""}.fa-p:before{content:"P"}.fa-snowflake:before{content:""}.fa-newspaper:before{content:""}.fa-rectangle-ad:before{content:""}.fa-ad:before{content:""}.fa-circle-arrow-right:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-filter-circle-xmark:before{content:""}.fa-locust:before{content:""}.fa-sort:before{content:""}.fa-unsorted:before{content:""}.fa-list-ol:before{content:""}.fa-list-1-2:before{content:""}.fa-list-numeric:before{content:""}.fa-person-dress-burst:before{content:""}.fa-money-check-dollar:before{content:""}.fa-money-check-alt:before{content:""}.fa-vector-square:before{content:""}.fa-bread-slice:before{content:""}.fa-language:before{content:""}.fa-face-kiss-wink-heart:before{content:""}.fa-kiss-wink-heart:before{content:""}.fa-filter:before{content:""}.fa-question:before{content:"?"}.fa-file-signature:before{content:""}.fa-up-down-left-right:before{content:""}.fa-arrows-alt:before{content:""}.fa-house-chimney-user:before{content:""}.fa-hand-holding-heart:before{content:""}.fa-puzzle-piece:before{content:""}.fa-money-check:before{content:""}.fa-star-half-stroke:before{content:""}.fa-star-half-alt:before{content:""}.fa-code:before{content:""}.fa-whiskey-glass:before{content:""}.fa-glass-whiskey:before{content:""}.fa-building-circle-exclamation:before{content:""}.fa-magnifying-glass-chart:before{content:""}.fa-arrow-up-right-from-square:before{content:""}.fa-external-link:before{content:""}.fa-cubes-stacked:before{content:""}.fa-won-sign:before{content:""}.fa-krw:before{content:""}.fa-won:before{content:""}.fa-virus-covid:before{content:""}.fa-austral-sign:before{content:""}.fa-f:before{content:"F"}.fa-leaf:before{content:""}.fa-road:before{content:""}.fa-taxi:before{content:""}.fa-cab:before{content:""}.fa-person-circle-plus:before{content:""}.fa-chart-pie:before{content:""}.fa-pie-chart:before{content:""}.fa-bolt-lightning:before{content:""}.fa-sack-xmark:before{content:""}.fa-file-excel:before{content:""}.fa-file-contract:before{content:""}.fa-fish-fins:before{content:""}.fa-building-flag:before{content:""}.fa-face-grin-beam:before{content:""}.fa-grin-beam:before{content:""}.fa-object-ungroup:before{content:""}.fa-poop:before{content:""}.fa-location-pin:before{content:""}.fa-map-marker:before{content:""}.fa-kaaba:before{content:""}.fa-toilet-paper:before{content:""}.fa-helmet-safety:before{content:""}.fa-hard-hat:before{content:""}.fa-hat-hard:before{content:""}.fa-eject:before{content:""}.fa-circle-right:before{content:""}.fa-arrow-alt-circle-right:before{content:""}.fa-plane-circle-check:before{content:""}.fa-face-rolling-eyes:before{content:""}.fa-meh-rolling-eyes:before{content:""}.fa-object-group:before{content:""}.fa-chart-line:before{content:""}.fa-line-chart:before{content:""}.fa-mask-ventilator:before{content:""}.fa-arrow-right:before{content:""}.fa-signs-post:before{content:""}.fa-map-signs:before{content:""}.fa-cash-register:before{content:""}.fa-person-circle-question:before{content:""}.fa-h:before{content:"H"}.fa-tarp:before{content:""}.fa-screwdriver-wrench:before{content:""}.fa-tools:before{content:""}.fa-arrows-to-eye:before{content:""}.fa-plug-circle-bolt:before{content:""}.fa-heart:before{content:""}.fa-mars-and-venus:before{content:""}.fa-house-user:before{content:""}.fa-home-user:before{content:""}.fa-dumpster-fire:before{content:""}.fa-house-crack:before{content:""}.fa-martini-glass-citrus:before{content:""}.fa-cocktail:before{content:""}.fa-face-surprise:before{content:""}.fa-surprise:before{content:""}.fa-bottle-water:before{content:""}.fa-circle-pause:before{content:""}.fa-pause-circle:before{content:""}.fa-toilet-paper-slash:before{content:""}.fa-apple-whole:before{content:""}.fa-apple-alt:before{content:""}.fa-kitchen-set:before{content:""}.fa-r:before{content:"R"}.fa-temperature-quarter:before{content:""}.fa-temperature-1:before{content:""}.fa-thermometer-1:before{content:""}.fa-thermometer-quarter:before{content:""}.fa-cube:before{content:""}.fa-bitcoin-sign:before{content:""}.fa-shield-dog:before{content:""}.fa-solar-panel:before{content:""}.fa-lock-open:before{content:""}.fa-elevator:before{content:""}.fa-money-bill-transfer:before{content:""}.fa-money-bill-trend-up:before{content:""}.fa-house-flood-water-circle-arrow-right:before{content:""}.fa-square-poll-horizontal:before{content:""}.fa-poll-h:before{content:""}.fa-circle:before{content:""}.fa-backward-fast:before{content:""}.fa-fast-backward:before{content:""}.fa-recycle:before{content:""}.fa-user-astronaut:before{content:""}.fa-plane-slash:before{content:""}.fa-trademark:before{content:""}.fa-basketball:before{content:""}.fa-basketball-ball:before{content:""}.fa-satellite-dish:before{content:""}.fa-circle-up:before{content:""}.fa-arrow-alt-circle-up:before{content:""}.fa-mobile-screen-button:before{content:""}.fa-mobile-alt:before{content:""}.fa-volume-high:before{content:""}.fa-volume-up:before{content:""}.fa-users-rays:before{content:""}.fa-wallet:before{content:""}.fa-clipboard-check:before{content:""}.fa-file-audio:before{content:""}.fa-burger:before{content:""}.fa-hamburger:before{content:""}.fa-wrench:before{content:""}.fa-bugs:before{content:""}.fa-rupee-sign:before{content:""}.fa-rupee:before{content:""}.fa-file-image:before{content:""}.fa-circle-question:before{content:""}.fa-question-circle:before{content:""}.fa-plane-departure:before{content:""}.fa-handshake-slash:before{content:""}.fa-book-bookmark:before{content:""}.fa-code-branch:before{content:""}.fa-hat-cowboy:before{content:""}.fa-bridge:before{content:""}.fa-phone-flip:before{content:""}.fa-phone-alt:before{content:""}.fa-truck-front:before{content:""}.fa-cat:before{content:""}.fa-anchor-circle-exclamation:before{content:""}.fa-truck-field:before{content:""}.fa-route:before{content:""}.fa-clipboard-question:before{content:""}.fa-panorama:before{content:""}.fa-comment-medical:before{content:""}.fa-teeth-open:before{content:""}.fa-file-circle-minus:before{content:""}.fa-tags:before{content:""}.fa-wine-glass:before{content:""}.fa-forward-fast:before{content:""}.fa-fast-forward:before{content:""}.fa-face-meh-blank:before{content:""}.fa-meh-blank:before{content:""}.fa-square-parking:before{content:""}.fa-parking:before{content:""}.fa-house-signal:before{content:""}.fa-bars-progress:before{content:""}.fa-tasks-alt:before{content:""}.fa-faucet-drip:before{content:""}.fa-cart-flatbed:before{content:""}.fa-dolly-flatbed:before{content:""}.fa-ban-smoking:before{content:""}.fa-smoking-ban:before{content:""}.fa-terminal:before{content:""}.fa-mobile-button:before{content:""}.fa-house-medical-flag:before{content:""}.fa-basket-shopping:before{content:""}.fa-shopping-basket:before{content:""}.fa-tape:before{content:""}.fa-bus-simple:before{content:""}.fa-bus-alt:before{content:""}.fa-eye:before{content:""}.fa-face-sad-cry:before{content:""}.fa-sad-cry:before{content:""}.fa-audio-description:before{content:""}.fa-person-military-to-person:before{content:""}.fa-file-shield:before{content:""}.fa-user-slash:before{content:""}.fa-pen:before{content:""}.fa-tower-observation:before{content:""}.fa-file-code:before{content:""}.fa-signal:before{content:""}.fa-signal-5:before{content:""}.fa-signal-perfect:before{content:""}.fa-bus:before{content:""}.fa-heart-circle-xmark:before{content:""}.fa-house-chimney:before{content:""}.fa-home-lg:before{content:""}.fa-window-maximize:before{content:""}.fa-face-frown:before{content:""}.fa-frown:before{content:""}.fa-prescription:before{content:""}.fa-shop:before{content:""}.fa-store-alt:before{content:""}.fa-floppy-disk:before{content:""}.fa-save:before{content:""}.fa-vihara:before{content:""}.fa-scale-unbalanced:before{content:""}.fa-balance-scale-left:before{content:""}.fa-sort-up:before{content:""}.fa-sort-asc:before{content:""}.fa-comment-dots:before{content:""}.fa-commenting:before{content:""}.fa-plant-wilt:before{content:""}.fa-diamond:before{content:""}.fa-face-grin-squint:before{content:""}.fa-grin-squint:before{content:""}.fa-hand-holding-dollar:before{content:""}.fa-hand-holding-usd:before{content:""}.fa-bacterium:before{content:""}.fa-hand-pointer:before{content:""}.fa-drum-steelpan:before{content:""}.fa-hand-scissors:before{content:""}.fa-hands-praying:before{content:""}.fa-praying-hands:before{content:""}.fa-arrow-rotate-right:before{content:""}.fa-arrow-right-rotate:before{content:""}.fa-arrow-rotate-forward:before{content:""}.fa-redo:before{content:""}.fa-biohazard:before{content:""}.fa-location-crosshairs:before{content:""}.fa-location:before{content:""}.fa-mars-double:before{content:""}.fa-child-dress:before{content:""}.fa-users-between-lines:before{content:""}.fa-lungs-virus:before{content:""}.fa-face-grin-tears:before{content:""}.fa-grin-tears:before{content:""}.fa-phone:before{content:""}.fa-calendar-xmark:before{content:""}.fa-calendar-times:before{content:""}.fa-child-reaching:before{content:""}.fa-head-side-virus:before{content:""}.fa-user-gear:before{content:""}.fa-user-cog:before{content:""}.fa-arrow-up-1-9:before{content:""}.fa-sort-numeric-up:before{content:""}.fa-door-closed:before{content:""}.fa-shield-virus:before{content:""}.fa-dice-six:before{content:""}.fa-mosquito-net:before{content:""}.fa-bridge-water:before{content:""}.fa-person-booth:before{content:""}.fa-text-width:before{content:""}.fa-hat-wizard:before{content:""}.fa-pen-fancy:before{content:""}.fa-person-digging:before{content:""}.fa-digging:before{content:""}.fa-trash:before{content:""}.fa-gauge-simple:before{content:""}.fa-gauge-simple-med:before{content:""}.fa-tachometer-average:before{content:""}.fa-book-medical:before{content:""}.fa-poo:before{content:""}.fa-quote-right:before{content:""}.fa-quote-right-alt:before{content:""}.fa-shirt:before{content:""}.fa-t-shirt:before{content:""}.fa-tshirt:before{content:""}.fa-cubes:before{content:""}.fa-divide:before{content:""}.fa-tenge-sign:before{content:""}.fa-tenge:before{content:""}.fa-headphones:before{content:""}.fa-hands-holding:before{content:""}.fa-hands-clapping:before{content:""}.fa-republican:before{content:""}.fa-arrow-left:before{content:""}.fa-person-circle-xmark:before{content:""}.fa-ruler:before{content:""}.fa-align-left:before{content:""}.fa-dice-d6:before{content:""}.fa-restroom:before{content:""}.fa-j:before{content:"J"}.fa-users-viewfinder:before{content:""}.fa-file-video:before{content:""}.fa-up-right-from-square:before{content:""}.fa-external-link-alt:before{content:""}.fa-table-cells:before{content:""}.fa-th:before{content:""}.fa-file-pdf:before{content:""}.fa-book-bible:before{content:""}.fa-bible:before{content:""}.fa-o:before{content:"O"}.fa-suitcase-medical:before{content:""}.fa-medkit:before{content:""}.fa-user-secret:before{content:""}.fa-otter:before{content:""}.fa-person-dress:before{content:""}.fa-female:before{content:""}.fa-comment-dollar:before{content:""}.fa-business-time:before{content:""}.fa-briefcase-clock:before{content:""}.fa-table-cells-large:before{content:""}.fa-th-large:before{content:""}.fa-book-tanakh:before{content:""}.fa-tanakh:before{content:""}.fa-phone-volume:before{content:""}.fa-volume-control-phone:before{content:""}.fa-hat-cowboy-side:before{content:""}.fa-clipboard-user:before{content:""}.fa-child:before{content:""}.fa-lira-sign:before{content:""}.fa-satellite:before{content:""}.fa-plane-lock:before{content:""}.fa-tag:before{content:""}.fa-comment:before{content:""}.fa-cake-candles:before{content:""}.fa-birthday-cake:before{content:""}.fa-cake:before{content:""}.fa-envelope:before{content:""}.fa-angles-up:before{content:""}.fa-angle-double-up:before{content:""}.fa-paperclip:before{content:""}.fa-arrow-right-to-city:before{content:""}.fa-ribbon:before{content:""}.fa-lungs:before{content:""}.fa-arrow-up-9-1:before{content:""}.fa-sort-numeric-up-alt:before{content:""}.fa-litecoin-sign:before{content:""}.fa-border-none:before{content:""}.fa-circle-nodes:before{content:""}.fa-parachute-box:before{content:""}.fa-indent:before{content:""}.fa-truck-field-un:before{content:""}.fa-hourglass:before{content:""}.fa-hourglass-empty:before{content:""}.fa-mountain:before{content:""}.fa-user-doctor:before{content:""}.fa-user-md:before{content:""}.fa-circle-info:before{content:""}.fa-info-circle:before{content:""}.fa-cloud-meatball:before{content:""}.fa-camera:before{content:""}.fa-camera-alt:before{content:""}.fa-square-virus:before{content:""}.fa-meteor:before{content:""}.fa-car-on:before{content:""}.fa-sleigh:before{content:""}.fa-arrow-down-1-9:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-down:before{content:""}.fa-hand-holding-droplet:before{content:""}.fa-hand-holding-water:before{content:""}.fa-water:before{content:""}.fa-calendar-check:before{content:""}.fa-braille:before{content:""}.fa-prescription-bottle-medical:before{content:""}.fa-prescription-bottle-alt:before{content:""}.fa-landmark:before{content:""}.fa-truck:before{content:""}.fa-crosshairs:before{content:""}.fa-person-cane:before{content:""}.fa-tent:before{content:""}.fa-vest-patches:before{content:""}.fa-check-double:before{content:""}.fa-arrow-down-a-z:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-down:before{content:""}.fa-money-bill-wheat:before{content:""}.fa-cookie:before{content:""}.fa-arrow-rotate-left:before{content:""}.fa-arrow-left-rotate:before{content:""}.fa-arrow-rotate-back:before{content:""}.fa-arrow-rotate-backward:before{content:""}.fa-undo:before{content:""}.fa-hard-drive:before{content:""}.fa-hdd:before{content:""}.fa-face-grin-squint-tears:before{content:""}.fa-grin-squint-tears:before{content:""}.fa-dumbbell:before{content:""}.fa-rectangle-list:before{content:""}.fa-list-alt:before{content:""}.fa-tarp-droplet:before{content:""}.fa-house-medical-circle-check:before{content:""}.fa-person-skiing-nordic:before{content:""}.fa-skiing-nordic:before{content:""}.fa-calendar-plus:before{content:""}.fa-plane-arrival:before{content:""}.fa-circle-left:before{content:""}.fa-arrow-alt-circle-left:before{content:""}.fa-train-subway:before{content:""}.fa-subway:before{content:""}.fa-chart-gantt:before{content:""}.fa-indian-rupee-sign:before{content:""}.fa-indian-rupee:before{content:""}.fa-inr:before{content:""}.fa-crop-simple:before{content:""}.fa-crop-alt:before{content:""}.fa-money-bill-1:before{content:""}.fa-money-bill-alt:before{content:""}.fa-left-long:before{content:""}.fa-long-arrow-alt-left:before{content:""}.fa-dna:before{content:""}.fa-virus-slash:before{content:""}.fa-minus:before{content:""}.fa-subtract:before{content:""}.fa-chess:before{content:""}.fa-arrow-left-long:before{content:""}.fa-long-arrow-left:before{content:""}.fa-plug-circle-check:before{content:""}.fa-street-view:before{content:""}.fa-franc-sign:before{content:""}.fa-volume-off:before{content:""}.fa-hands-asl-interpreting:before{content:""}.fa-american-sign-language-interpreting:before{content:""}.fa-asl-interpreting:before{content:""}.fa-hands-american-sign-language-interpreting:before{content:""}.fa-gear:before{content:""}.fa-cog:before{content:""}.fa-droplet-slash:before{content:""}.fa-tint-slash:before{content:""}.fa-mosque:before{content:""}.fa-mosquito:before{content:""}.fa-star-of-david:before{content:""}.fa-person-military-rifle:before{content:""}.fa-cart-shopping:before{content:""}.fa-shopping-cart:before{content:""}.fa-vials:before{content:""}.fa-plug-circle-plus:before{content:""}.fa-place-of-worship:before{content:""}.fa-grip-vertical:before{content:""}.fa-arrow-turn-up:before{content:""}.fa-level-up:before{content:""}.fa-u:before{content:"U"}.fa-square-root-variable:before{content:""}.fa-square-root-alt:before{content:""}.fa-clock:before{content:""}.fa-clock-four:before{content:""}.fa-backward-step:before{content:""}.fa-step-backward:before{content:""}.fa-pallet:before{content:""}.fa-faucet:before{content:""}.fa-baseball-bat-ball:before{content:""}.fa-s:before{content:"S"}.fa-timeline:before{content:""}.fa-keyboard:before{content:""}.fa-caret-down:before{content:""}.fa-house-chimney-medical:before{content:""}.fa-clinic-medical:before{content:""}.fa-temperature-three-quarters:before{content:""}.fa-temperature-3:before{content:""}.fa-thermometer-3:before{content:""}.fa-thermometer-three-quarters:before{content:""}.fa-mobile-screen:before{content:""}.fa-mobile-android-alt:before{content:""}.fa-plane-up:before{content:""}.fa-piggy-bank:before{content:""}.fa-battery-half:before{content:""}.fa-battery-3:before{content:""}.fa-mountain-city:before{content:""}.fa-coins:before{content:""}.fa-khanda:before{content:""}.fa-sliders:before{content:""}.fa-sliders-h:before{content:""}.fa-folder-tree:before{content:""}.fa-network-wired:before{content:""}.fa-map-pin:before{content:""}.fa-hamsa:before{content:""}.fa-cent-sign:before{content:""}.fa-flask:before{content:""}.fa-person-pregnant:before{content:""}.fa-wand-sparkles:before{content:""}.fa-ellipsis-vertical:before{content:""}.fa-ellipsis-v:before{content:""}.fa-ticket:before{content:""}.fa-power-off:before{content:""}.fa-right-long:before{content:""}.fa-long-arrow-alt-right:before{content:""}.fa-flag-usa:before{content:""}.fa-laptop-file:before{content:""}.fa-tty:before{content:""}.fa-teletype:before{content:""}.fa-diagram-next:before{content:""}.fa-person-rifle:before{content:""}.fa-house-medical-circle-exclamation:before{content:""}.fa-closed-captioning:before{content:""}.fa-person-hiking:before{content:""}.fa-hiking:before{content:""}.fa-venus-double:before{content:""}.fa-images:before{content:""}.fa-calculator:before{content:""}.fa-people-pulling:before{content:""}.fa-n:before{content:"N"}.fa-cable-car:before{content:""}.fa-tram:before{content:""}.fa-cloud-rain:before{content:""}.fa-building-circle-xmark:before{content:""}.fa-ship:before{content:""}.fa-arrows-down-to-line:before{content:""}.fa-download:before{content:""}.fa-face-grin:before{content:""}.fa-grin:before{content:""}.fa-delete-left:before{content:""}.fa-backspace:before{content:""}.fa-eye-dropper:before{content:""}.fa-eye-dropper-empty:before{content:""}.fa-eyedropper:before{content:""}.fa-file-circle-check:before{content:""}.fa-forward:before{content:""}.fa-mobile:before{content:""}.fa-mobile-android:before{content:""}.fa-mobile-phone:before{content:""}.fa-face-meh:before{content:""}.fa-meh:before{content:""}.fa-align-center:before{content:""}.fa-book-skull:before{content:""}.fa-book-dead:before{content:""}.fa-id-card:before{content:""}.fa-drivers-license:before{content:""}.fa-outdent:before{content:""}.fa-dedent:before{content:""}.fa-heart-circle-exclamation:before{content:""}.fa-house:before{content:""}.fa-home:before{content:""}.fa-home-alt:before{content:""}.fa-home-lg-alt:before{content:""}.fa-calendar-week:before{content:""}.fa-laptop-medical:before{content:""}.fa-b:before{content:"B"}.fa-file-medical:before{content:""}.fa-dice-one:before{content:""}.fa-kiwi-bird:before{content:""}.fa-arrow-right-arrow-left:before{content:""}.fa-exchange:before{content:""}.fa-rotate-right:before{content:""}.fa-redo-alt:before{content:""}.fa-rotate-forward:before{content:""}.fa-utensils:before{content:""}.fa-cutlery:before{content:""}.fa-arrow-up-wide-short:before{content:""}.fa-sort-amount-up:before{content:""}.fa-mill-sign:before{content:""}.fa-bowl-rice:before{content:""}.fa-skull:before{content:""}.fa-tower-broadcast:before{content:""}.fa-broadcast-tower:before{content:""}.fa-truck-pickup:before{content:""}.fa-up-long:before{content:""}.fa-long-arrow-alt-up:before{content:""}.fa-stop:before{content:""}.fa-code-merge:before{content:""}.fa-upload:before{content:""}.fa-hurricane:before{content:""}.fa-mound:before{content:""}.fa-toilet-portable:before{content:""}.fa-compact-disc:before{content:""}.fa-file-arrow-down:before{content:""}.fa-file-download:before{content:""}.fa-caravan:before{content:""}.fa-shield-cat:before{content:""}.fa-bolt:before{content:""}.fa-zap:before{content:""}.fa-glass-water:before{content:""}.fa-oil-well:before{content:""}.fa-vault:before{content:""}.fa-mars:before{content:""}.fa-toilet:before{content:""}.fa-plane-circle-xmark:before{content:""}.fa-yen-sign:before{content:""}.fa-cny:before{content:""}.fa-jpy:before{content:""}.fa-rmb:before{content:""}.fa-yen:before{content:""}.fa-ruble-sign:before{content:""}.fa-rouble:before{content:""}.fa-rub:before{content:""}.fa-ruble:before{content:""}.fa-sun:before{content:""}.fa-guitar:before{content:""}.fa-face-laugh-wink:before{content:""}.fa-laugh-wink:before{content:""}.fa-horse-head:before{content:""}.fa-bore-hole:before{content:""}.fa-industry:before{content:""}.fa-circle-down:before{content:""}.fa-arrow-alt-circle-down:before{content:""}.fa-arrows-turn-to-dots:before{content:""}.fa-florin-sign:before{content:""}.fa-arrow-down-short-wide:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-amount-down-alt:before{content:""}.fa-less-than:before{content:"<"}.fa-angle-down:before{content:""}.fa-car-tunnel:before{content:""}.fa-head-side-cough:before{content:""}.fa-grip-lines:before{content:""}.fa-thumbs-down:before{content:""}.fa-user-lock:before{content:""}.fa-arrow-right-long:before{content:""}.fa-long-arrow-right:before{content:""}.fa-anchor-circle-xmark:before{content:""}.fa-ellipsis:before{content:""}.fa-ellipsis-h:before{content:""}.fa-chess-pawn:before{content:""}.fa-kit-medical:before{content:""}.fa-first-aid:before{content:""}.fa-person-through-window:before{content:""}.fa-toolbox:before{content:""}.fa-hands-holding-circle:before{content:""}.fa-bug:before{content:""}.fa-credit-card:before{content:""}.fa-credit-card-alt:before{content:""}.fa-car:before{content:""}.fa-automobile:before{content:""}.fa-hand-holding-hand:before{content:""}.fa-book-open-reader:before{content:""}.fa-book-reader:before{content:""}.fa-mountain-sun:before{content:""}.fa-arrows-left-right-to-line:before{content:""}.fa-dice-d20:before{content:""}.fa-truck-droplet:before{content:""}.fa-file-circle-xmark:before{content:""}.fa-temperature-arrow-up:before{content:""}.fa-temperature-up:before{content:""}.fa-medal:before{content:""}.fa-bed:before{content:""}.fa-square-h:before{content:""}.fa-h-square:before{content:""}.fa-podcast:before{content:""}.fa-temperature-full:before{content:""}.fa-temperature-4:before{content:""}.fa-thermometer-4:before{content:""}.fa-thermometer-full:before{content:""}.fa-bell:before{content:""}.fa-superscript:before{content:""}.fa-plug-circle-xmark:before{content:""}.fa-star-of-life:before{content:""}.fa-phone-slash:before{content:""}.fa-paint-roller:before{content:""}.fa-handshake-angle:before{content:""}.fa-hands-helping:before{content:""}.fa-location-dot:before{content:""}.fa-map-marker-alt:before{content:""}.fa-file:before{content:""}.fa-greater-than:before{content:">"}.fa-person-swimming:before{content:""}.fa-swimmer:before{content:""}.fa-arrow-down:before{content:""}.fa-droplet:before{content:""}.fa-tint:before{content:""}.fa-eraser:before{content:""}.fa-earth-americas:before{content:""}.fa-earth:before{content:""}.fa-earth-america:before{content:""}.fa-globe-americas:before{content:""}.fa-person-burst:before{content:""}.fa-dove:before{content:""}.fa-battery-empty:before{content:""}.fa-battery-0:before{content:""}.fa-socks:before{content:""}.fa-inbox:before{content:""}.fa-section:before{content:""}.fa-gauge-high:before{content:""}.fa-tachometer-alt:before{content:""}.fa-tachometer-alt-fast:before{content:""}.fa-envelope-open-text:before{content:""}.fa-hospital:before{content:""}.fa-hospital-alt:before{content:""}.fa-hospital-wide:before{content:""}.fa-wine-bottle:before{content:""}.fa-chess-rook:before{content:""}.fa-bars-staggered:before{content:""}.fa-reorder:before{content:""}.fa-stream:before{content:""}.fa-dharmachakra:before{content:""}.fa-hotdog:before{content:""}.fa-person-walking-with-cane:before{content:""}.fa-blind:before{content:""}.fa-drum:before{content:""}.fa-ice-cream:before{content:""}.fa-heart-circle-bolt:before{content:""}.fa-fax:before{content:""}.fa-paragraph:before{content:""}.fa-check-to-slot:before{content:""}.fa-vote-yea:before{content:""}.fa-star-half:before{content:""}.fa-boxes-stacked:before{content:""}.fa-boxes:before{content:""}.fa-boxes-alt:before{content:""}.fa-link:before{content:""}.fa-chain:before{content:""}.fa-ear-listen:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-tree-city:before{content:""}.fa-play:before{content:""}.fa-font:before{content:""}.fa-rupiah-sign:before{content:""}.fa-magnifying-glass:before{content:""}.fa-search:before{content:""}.fa-table-tennis-paddle-ball:before{content:""}.fa-ping-pong-paddle-ball:before{content:""}.fa-table-tennis:before{content:""}.fa-person-dots-from-line:before{content:""}.fa-diagnoses:before{content:""}.fa-trash-can-arrow-up:before{content:""}.fa-trash-restore-alt:before{content:""}.fa-naira-sign:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-walkie-talkie:before{content:""}.fa-file-pen:before{content:""}.fa-file-edit:before{content:""}.fa-receipt:before{content:""}.fa-square-pen:before{content:""}.fa-pen-square:before{content:""}.fa-pencil-square:before{content:""}.fa-suitcase-rolling:before{content:""}.fa-person-circle-exclamation:before{content:""}.fa-chevron-down:before{content:""}.fa-battery-full:before{content:""}.fa-battery:before{content:""}.fa-battery-5:before{content:""}.fa-skull-crossbones:before{content:""}.fa-code-compare:before{content:""}.fa-list-ul:before{content:""}.fa-list-dots:before{content:""}.fa-school-lock:before{content:""}.fa-tower-cell:before{content:""}.fa-down-long:before{content:""}.fa-long-arrow-alt-down:before{content:""}.fa-ranking-star:before{content:""}.fa-chess-king:before{content:""}.fa-person-harassing:before{content:""}.fa-brazilian-real-sign:before{content:""}.fa-landmark-dome:before{content:""}.fa-landmark-alt:before{content:""}.fa-arrow-up:before{content:""}.fa-tv:before{content:""}.fa-television:before{content:""}.fa-tv-alt:before{content:""}.fa-shrimp:before{content:""}.fa-list-check:before{content:""}.fa-tasks:before{content:""}.fa-jug-detergent:before{content:""}.fa-circle-user:before{content:""}.fa-user-circle:before{content:""}.fa-user-shield:before{content:""}.fa-wind:before{content:""}.fa-car-burst:before{content:""}.fa-car-crash:before{content:""}.fa-y:before{content:"Y"}.fa-person-snowboarding:before{content:""}.fa-snowboarding:before{content:""}.fa-truck-fast:before{content:""}.fa-shipping-fast:before{content:""}.fa-fish:before{content:""}.fa-user-graduate:before{content:""}.fa-circle-half-stroke:before{content:""}.fa-adjust:before{content:""}.fa-clapperboard:before{content:""}.fa-circle-radiation:before{content:""}.fa-radiation-alt:before{content:""}.fa-baseball:before{content:""}.fa-baseball-ball:before{content:""}.fa-jet-fighter-up:before{content:""}.fa-diagram-project:before{content:""}.fa-project-diagram:before{content:""}.fa-copy:before{content:""}.fa-volume-xmark:before{content:""}.fa-volume-mute:before{content:""}.fa-volume-times:before{content:""}.fa-hand-sparkles:before{content:""}.fa-grip:before{content:""}.fa-grip-horizontal:before{content:""}.fa-share-from-square:before{content:""}.fa-share-square:before{content:""}.fa-child-combatant:before{content:""}.fa-child-rifle:before{content:""}.fa-gun:before{content:""}.fa-square-phone:before{content:""}.fa-phone-square:before{content:""}.fa-plus:before{content:"+"}.fa-add:before{content:"+"}.fa-expand:before{content:""}.fa-computer:before{content:""}.fa-xmark:before{content:""}.fa-close:before{content:""}.fa-multiply:before{content:""}.fa-remove:before{content:""}.fa-times:before{content:""}.fa-arrows-up-down-left-right:before{content:""}.fa-arrows:before{content:""}.fa-chalkboard-user:before{content:""}.fa-chalkboard-teacher:before{content:""}.fa-peso-sign:before{content:""}.fa-building-shield:before{content:""}.fa-baby:before{content:""}.fa-users-line:before{content:""}.fa-quote-left:before{content:""}.fa-quote-left-alt:before{content:""}.fa-tractor:before{content:""}.fa-trash-arrow-up:before{content:""}.fa-trash-restore:before{content:""}.fa-arrow-down-up-lock:before{content:""}.fa-lines-leaning:before{content:""}.fa-ruler-combined:before{content:""}.fa-copyright:before{content:""}.fa-equals:before{content:"="}.fa-blender:before{content:""}.fa-teeth:before{content:""}.fa-shekel-sign:before{content:""}.fa-ils:before{content:""}.fa-shekel:before{content:""}.fa-sheqel:before{content:""}.fa-sheqel-sign:before{content:""}.fa-map:before{content:""}.fa-rocket:before{content:""}.fa-photo-film:before{content:""}.fa-photo-video:before{content:""}.fa-folder-minus:before{content:""}.fa-store:before{content:""}.fa-arrow-trend-up:before{content:""}.fa-plug-circle-minus:before{content:""}.fa-sign-hanging:before{content:""}.fa-sign:before{content:""}.fa-bezier-curve:before{content:""}.fa-bell-slash:before{content:""}.fa-tablet:before{content:""}.fa-tablet-android:before{content:""}.fa-school-flag:before{content:""}.fa-fill:before{content:""}.fa-angle-up:before{content:""}.fa-drumstick-bite:before{content:""}.fa-holly-berry:before{content:""}.fa-chevron-left:before{content:""}.fa-bacteria:before{content:""}.fa-hand-lizard:before{content:""}.fa-notdef:before{content:""}.fa-disease:before{content:""}.fa-briefcase-medical:before{content:""}.fa-genderless:before{content:""}.fa-chevron-right:before{content:""}.fa-retweet:before{content:""}.fa-car-rear:before{content:""}.fa-car-alt:before{content:""}.fa-pump-soap:before{content:""}.fa-video-slash:before{content:""}.fa-battery-quarter:before{content:""}.fa-battery-2:before{content:""}.fa-radio:before{content:""}.fa-baby-carriage:before{content:""}.fa-carriage-baby:before{content:""}.fa-traffic-light:before{content:""}.fa-thermometer:before{content:""}.fa-vr-cardboard:before{content:""}.fa-hand-middle-finger:before{content:""}.fa-percent:before{content:"%"}.fa-percentage:before{content:"%"}.fa-truck-moving:before{content:""}.fa-glass-water-droplet:before{content:""}.fa-display:before{content:""}.fa-face-smile:before{content:""}.fa-smile:before{content:""}.fa-thumbtack:before{content:""}.fa-thumb-tack:before{content:""}.fa-trophy:before{content:""}.fa-person-praying:before{content:""}.fa-pray:before{content:""}.fa-hammer:before{content:""}.fa-hand-peace:before{content:""}.fa-rotate:before{content:""}.fa-sync-alt:before{content:""}.fa-spinner:before{content:""}.fa-robot:before{content:""}.fa-peace:before{content:""}.fa-gears:before{content:""}.fa-cogs:before{content:""}.fa-warehouse:before{content:""}.fa-arrow-up-right-dots:before{content:""}.fa-splotch:before{content:""}.fa-face-grin-hearts:before{content:""}.fa-grin-hearts:before{content:""}.fa-dice-four:before{content:""}.fa-sim-card:before{content:""}.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-mercury:before{content:""}.fa-arrow-turn-down:before{content:""}.fa-level-down:before{content:""}.fa-person-falling-burst:before{content:""}.fa-award:before{content:""}.fa-ticket-simple:before{content:""}.fa-ticket-alt:before{content:""}.fa-building:before{content:""}.fa-angles-left:before{content:""}.fa-angle-double-left:before{content:""}.fa-qrcode:before{content:""}.fa-clock-rotate-left:before{content:""}.fa-history:before{content:""}.fa-face-grin-beam-sweat:before{content:""}.fa-grin-beam-sweat:before{content:""}.fa-file-export:before{content:""}.fa-arrow-right-from-file:before{content:""}.fa-shield:before{content:""}.fa-shield-blank:before{content:""}.fa-arrow-up-short-wide:before{content:""}.fa-sort-amount-up-alt:before{content:""}.fa-house-medical:before{content:""}.fa-golf-ball-tee:before{content:""}.fa-golf-ball:before{content:""}.fa-circle-chevron-left:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-house-chimney-window:before{content:""}.fa-pen-nib:before{content:""}.fa-tent-arrow-turn-left:before{content:""}.fa-tents:before{content:""}.fa-wand-magic:before{content:""}.fa-magic:before{content:""}.fa-dog:before{content:""}.fa-carrot:before{content:""}.fa-moon:before{content:""}.fa-wine-glass-empty:before{content:""}.fa-wine-glass-alt:before{content:""}.fa-cheese:before{content:""}.fa-yin-yang:before{content:""}.fa-music:before{content:""}.fa-code-commit:before{content:""}.fa-temperature-low:before{content:""}.fa-person-biking:before{content:""}.fa-biking:before{content:""}.fa-broom:before{content:""}.fa-shield-heart:before{content:""}.fa-gopuram:before{content:""}.fa-earth-oceania:before{content:""}.fa-globe-oceania:before{content:""}.fa-square-xmark:before{content:""}.fa-times-square:before{content:""}.fa-xmark-square:before{content:""}.fa-hashtag:before{content:"#"}.fa-up-right-and-down-left-from-center:before{content:""}.fa-expand-alt:before{content:""}.fa-oil-can:before{content:""}.fa-t:before{content:"T"}.fa-hippo:before{content:""}.fa-chart-column:before{content:""}.fa-infinity:before{content:""}.fa-vial-circle-check:before{content:""}.fa-person-arrow-down-to-line:before{content:""}.fa-voicemail:before{content:""}.fa-fan:before{content:""}.fa-person-walking-luggage:before{content:""}.fa-up-down:before{content:""}.fa-arrows-alt-v:before{content:""}.fa-cloud-moon-rain:before{content:""}.fa-calendar:before{content:""}.fa-trailer:before{content:""}.fa-bahai:before{content:""}.fa-haykal:before{content:""}.fa-sd-card:before{content:""}.fa-dragon:before{content:""}.fa-shoe-prints:before{content:""}.fa-circle-plus:before{content:""}.fa-plus-circle:before{content:""}.fa-face-grin-tongue-wink:before{content:""}.fa-grin-tongue-wink:before{content:""}.fa-hand-holding:before{content:""}.fa-plug-circle-exclamation:before{content:""}.fa-link-slash:before{content:""}.fa-chain-broken:before{content:""}.fa-chain-slash:before{content:""}.fa-unlink:before{content:""}.fa-clone:before{content:""}.fa-person-walking-arrow-loop-left:before{content:""}.fa-arrow-up-z-a:before{content:""}.fa-sort-alpha-up-alt:before{content:""}.fa-fire-flame-curved:before{content:""}.fa-fire-alt:before{content:""}.fa-tornado:before{content:""}.fa-file-circle-plus:before{content:""}.fa-book-quran:before{content:""}.fa-quran:before{content:""}.fa-anchor:before{content:""}.fa-border-all:before{content:""}.fa-face-angry:before{content:""}.fa-angry:before{content:""}.fa-cookie-bite:before{content:""}.fa-arrow-trend-down:before{content:""}.fa-rss:before{content:""}.fa-feed:before{content:""}.fa-draw-polygon:before{content:""}.fa-scale-balanced:before{content:""}.fa-balance-scale:before{content:""}.fa-gauge-simple-high:before{content:""}.fa-tachometer:before{content:""}.fa-tachometer-fast:before{content:""}.fa-shower:before{content:""}.fa-desktop:before{content:""}.fa-desktop-alt:before{content:""}.fa-m:before{content:"M"}.fa-table-list:before{content:""}.fa-th-list:before{content:""}.fa-comment-sms:before{content:""}.fa-sms:before{content:""}.fa-book:before{content:""}.fa-user-plus:before{content:""}.fa-check:before{content:""}.fa-battery-three-quarters:before{content:""}.fa-battery-4:before{content:""}.fa-house-circle-check:before{content:""}.fa-angle-left:before{content:""}.fa-diagram-successor:before{content:""}.fa-truck-arrow-right:before{content:""}.fa-arrows-split-up-and-left:before{content:""}.fa-hand-fist:before{content:""}.fa-fist-raised:before{content:""}.fa-cloud-moon:before{content:""}.fa-briefcase:before{content:""}.fa-person-falling:before{content:""}.fa-image-portrait:before{content:""}.fa-portrait:before{content:""}.fa-user-tag:before{content:""}.fa-rug:before{content:""}.fa-earth-europe:before{content:""}.fa-globe-europe:before{content:""}.fa-cart-flatbed-suitcase:before{content:""}.fa-luggage-cart:before{content:""}.fa-rectangle-xmark:before{content:""}.fa-rectangle-times:before{content:""}.fa-times-rectangle:before{content:""}.fa-window-close:before{content:""}.fa-baht-sign:before{content:""}.fa-book-open:before{content:""}.fa-book-journal-whills:before{content:""}.fa-journal-whills:before{content:""}.fa-handcuffs:before{content:""}.fa-triangle-exclamation:before{content:""}.fa-exclamation-triangle:before{content:""}.fa-warning:before{content:""}.fa-database:before{content:""}.fa-share:before{content:""}.fa-mail-forward:before{content:""}.fa-bottle-droplet:before{content:""}.fa-mask-face:before{content:""}.fa-hill-rockslide:before{content:""}.fa-right-left:before{content:""}.fa-exchange-alt:before{content:""}.fa-paper-plane:before{content:""}.fa-road-circle-exclamation:before{content:""}.fa-dungeon:before{content:""}.fa-align-right:before{content:""}.fa-money-bill-1-wave:before{content:""}.fa-money-bill-wave-alt:before{content:""}.fa-life-ring:before{content:""}.fa-hands:before{content:""}.fa-sign-language:before{content:""}.fa-signing:before{content:""}.fa-calendar-day:before{content:""}.fa-water-ladder:before{content:""}.fa-ladder-water:before{content:""}.fa-swimming-pool:before{content:""}.fa-arrows-up-down:before{content:""}.fa-arrows-v:before{content:""}.fa-face-grimace:before{content:""}.fa-grimace:before{content:""}.fa-wheelchair-move:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-turn-down:before{content:""}.fa-level-down-alt:before{content:""}.fa-person-walking-arrow-right:before{content:""}.fa-square-envelope:before{content:""}.fa-envelope-square:before{content:""}.fa-dice:before{content:""}.fa-bowling-ball:before{content:""}.fa-brain:before{content:""}.fa-bandage:before{content:""}.fa-band-aid:before{content:""}.fa-calendar-minus:before{content:""}.fa-circle-xmark:before{content:""}.fa-times-circle:before{content:""}.fa-xmark-circle:before{content:""}.fa-gifts:before{content:""}.fa-hotel:before{content:""}.fa-earth-asia:before{content:""}.fa-globe-asia:before{content:""}.fa-id-card-clip:before{content:""}.fa-id-card-alt:before{content:""}.fa-magnifying-glass-plus:before{content:""}.fa-search-plus:before{content:""}.fa-thumbs-up:before{content:""}.fa-user-clock:before{content:""}.fa-hand-dots:before{content:""}.fa-allergies:before{content:""}.fa-file-invoice:before{content:""}.fa-window-minimize:before{content:""}.fa-mug-saucer:before{content:""}.fa-coffee:before{content:""}.fa-brush:before{content:""}.fa-mask:before{content:""}.fa-magnifying-glass-minus:before{content:""}.fa-search-minus:before{content:""}.fa-ruler-vertical:before{content:""}.fa-user-large:before{content:""}.fa-user-alt:before{content:""}.fa-train-tram:before{content:""}.fa-user-nurse:before{content:""}.fa-syringe:before{content:""}.fa-cloud-sun:before{content:""}.fa-stopwatch-20:before{content:""}.fa-square-full:before{content:""}.fa-magnet:before{content:""}.fa-jar:before{content:""}.fa-note-sticky:before{content:""}.fa-sticky-note:before{content:""}.fa-bug-slash:before{content:""}.fa-arrow-up-from-water-pump:before{content:""}.fa-bone:before{content:""}.fa-user-injured:before{content:""}.fa-face-sad-tear:before{content:""}.fa-sad-tear:before{content:""}.fa-plane:before{content:""}.fa-tent-arrows-down:before{content:""}.fa-exclamation:before{content:"!"}.fa-arrows-spin:before{content:""}.fa-print:before{content:""}.fa-turkish-lira-sign:before{content:""}.fa-try:before{content:""}.fa-turkish-lira:before{content:""}.fa-dollar-sign:before{content:"$"}.fa-dollar:before{content:"$"}.fa-usd:before{content:"$"}.fa-x:before{content:"X"}.fa-magnifying-glass-dollar:before{content:""}.fa-search-dollar:before{content:""}.fa-users-gear:before{content:""}.fa-users-cog:before{content:""}.fa-person-military-pointing:before{content:""}.fa-building-columns:before{content:""}.fa-bank:before{content:""}.fa-institution:before{content:""}.fa-museum:before{content:""}.fa-university:before{content:""}.fa-umbrella:before{content:""}.fa-trowel:before{content:""}.fa-d:before{content:"D"}.fa-stapler:before{content:""}.fa-masks-theater:before{content:""}.fa-theater-masks:before{content:""}.fa-kip-sign:before{content:""}.fa-hand-point-left:before{content:""}.fa-handshake-simple:before{content:""}.fa-handshake-alt:before{content:""}.fa-jet-fighter:before{content:""}.fa-fighter-jet:before{content:""}.fa-square-share-nodes:before{content:""}.fa-share-alt-square:before{content:""}.fa-barcode:before{content:""}.fa-plus-minus:before{content:""}.fa-video:before{content:""}.fa-video-camera:before{content:""}.fa-graduation-cap:before{content:""}.fa-mortar-board:before{content:""}.fa-hand-holding-medical:before{content:""}.fa-person-circle-check:before{content:""}.fa-turn-up:before{content:""}.fa-level-up-alt:before{content:""}.sr-only,.fa-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.sr-only-focusable:not(:focus),.fa-sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:root,:host{--fa-style-family-classic: "Font Awesome 6 Free";--fa-font-solid: normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(/build/assets/fa-solid-900-9fc85f3a.woff2) format("woff2"),url(/build/assets/fa-solid-900-fbbf06d7.ttf) format("truetype")}.fas,.fa-solid{font-weight:900}:root,:host{--fa-style-family-brands: "Font Awesome 6 Brands";--fa-font-brands: normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(/build/assets/fa-brands-400-3a8924cd.woff2) format("woff2"),url(/build/assets/fa-brands-400-5656d596.ttf) format("truetype")}.fab,.fa-brands{font-weight:400}.fa-monero:before{content:""}.fa-hooli:before{content:""}.fa-yelp:before{content:""}.fa-cc-visa:before{content:""}.fa-lastfm:before{content:""}.fa-shopware:before{content:""}.fa-creative-commons-nc:before{content:""}.fa-aws:before{content:""}.fa-redhat:before{content:""}.fa-yoast:before{content:""}.fa-cloudflare:before{content:""}.fa-ups:before{content:""}.fa-pixiv:before{content:""}.fa-wpexplorer:before{content:""}.fa-dyalog:before{content:""}.fa-bity:before{content:""}.fa-stackpath:before{content:""}.fa-buysellads:before{content:""}.fa-first-order:before{content:""}.fa-modx:before{content:""}.fa-guilded:before{content:""}.fa-vnv:before{content:""}.fa-square-js:before{content:""}.fa-js-square:before{content:""}.fa-microsoft:before{content:""}.fa-qq:before{content:""}.fa-orcid:before{content:""}.fa-java:before{content:""}.fa-invision:before{content:""}.fa-creative-commons-pd-alt:before{content:""}.fa-centercode:before{content:""}.fa-glide-g:before{content:""}.fa-drupal:before{content:""}.fa-hire-a-helper:before{content:""}.fa-creative-commons-by:before{content:""}.fa-unity:before{content:""}.fa-whmcs:before{content:""}.fa-rocketchat:before{content:""}.fa-vk:before{content:""}.fa-untappd:before{content:""}.fa-mailchimp:before{content:""}.fa-css3-alt:before{content:""}.fa-square-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-vimeo-v:before{content:""}.fa-contao:before{content:""}.fa-square-font-awesome:before{content:""}.fa-deskpro:before{content:""}.fa-brave:before{content:""}.fa-sistrix:before{content:""}.fa-square-instagram:before{content:""}.fa-instagram-square:before{content:""}.fa-battle-net:before{content:""}.fa-the-red-yeti:before{content:""}.fa-square-hacker-news:before{content:""}.fa-hacker-news-square:before{content:""}.fa-edge:before{content:""}.fa-threads:before{content:""}.fa-napster:before{content:""}.fa-square-snapchat:before{content:""}.fa-snapchat-square:before{content:""}.fa-google-plus-g:before{content:""}.fa-artstation:before{content:""}.fa-markdown:before{content:""}.fa-sourcetree:before{content:""}.fa-google-plus:before{content:""}.fa-diaspora:before{content:""}.fa-foursquare:before{content:""}.fa-stack-overflow:before{content:""}.fa-github-alt:before{content:""}.fa-phoenix-squadron:before{content:""}.fa-pagelines:before{content:""}.fa-algolia:before{content:""}.fa-red-river:before{content:""}.fa-creative-commons-sa:before{content:""}.fa-safari:before{content:""}.fa-google:before{content:""}.fa-square-font-awesome-stroke:before{content:""}.fa-font-awesome-alt:before{content:""}.fa-atlassian:before{content:""}.fa-linkedin-in:before{content:""}.fa-digital-ocean:before{content:""}.fa-nimblr:before{content:""}.fa-chromecast:before{content:""}.fa-evernote:before{content:""}.fa-hacker-news:before{content:""}.fa-creative-commons-sampling:before{content:""}.fa-adversal:before{content:""}.fa-creative-commons:before{content:""}.fa-watchman-monitoring:before{content:""}.fa-fonticons:before{content:""}.fa-weixin:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-codepen:before{content:""}.fa-git-alt:before{content:""}.fa-lyft:before{content:""}.fa-rev:before{content:""}.fa-windows:before{content:""}.fa-wizards-of-the-coast:before{content:""}.fa-square-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-meetup:before{content:""}.fa-centos:before{content:""}.fa-adn:before{content:""}.fa-cloudsmith:before{content:""}.fa-opensuse:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-square-dribbble:before{content:""}.fa-dribbble-square:before{content:""}.fa-codiepie:before{content:""}.fa-node:before{content:""}.fa-mix:before{content:""}.fa-steam:before{content:""}.fa-cc-apple-pay:before{content:""}.fa-scribd:before{content:""}.fa-debian:before{content:""}.fa-openid:before{content:""}.fa-instalod:before{content:""}.fa-expeditedssl:before{content:""}.fa-sellcast:before{content:""}.fa-square-twitter:before{content:""}.fa-twitter-square:before{content:""}.fa-r-project:before{content:""}.fa-delicious:before{content:""}.fa-freebsd:before{content:""}.fa-vuejs:before{content:""}.fa-accusoft:before{content:""}.fa-ioxhost:before{content:""}.fa-fonticons-fi:before{content:""}.fa-app-store:before{content:""}.fa-cc-mastercard:before{content:""}.fa-itunes-note:before{content:""}.fa-golang:before{content:""}.fa-kickstarter:before{content:""}.fa-grav:before{content:""}.fa-weibo:before{content:""}.fa-uncharted:before{content:""}.fa-firstdraft:before{content:""}.fa-square-youtube:before{content:""}.fa-youtube-square:before{content:""}.fa-wikipedia-w:before{content:""}.fa-wpressr:before{content:""}.fa-rendact:before{content:""}.fa-angellist:before{content:""}.fa-galactic-republic:before{content:""}.fa-nfc-directional:before{content:""}.fa-skype:before{content:""}.fa-joget:before{content:""}.fa-fedora:before{content:""}.fa-stripe-s:before{content:""}.fa-meta:before{content:""}.fa-laravel:before{content:""}.fa-hotjar:before{content:""}.fa-bluetooth-b:before{content:""}.fa-square-letterboxd:before{content:""}.fa-sticker-mule:before{content:""}.fa-creative-commons-zero:before{content:""}.fa-hips:before{content:""}.fa-behance:before{content:""}.fa-reddit:before{content:""}.fa-discord:before{content:""}.fa-chrome:before{content:""}.fa-app-store-ios:before{content:""}.fa-cc-discover:before{content:""}.fa-wpbeginner:before{content:""}.fa-confluence:before{content:""}.fa-shoelace:before{content:""}.fa-mdb:before{content:""}.fa-dochub:before{content:""}.fa-accessible-icon:before{content:""}.fa-ebay:before{content:""}.fa-amazon:before{content:""}.fa-unsplash:before{content:""}.fa-yarn:before{content:""}.fa-square-steam:before{content:""}.fa-steam-square:before{content:""}.fa-500px:before{content:""}.fa-square-vimeo:before{content:""}.fa-vimeo-square:before{content:""}.fa-asymmetrik:before{content:""}.fa-font-awesome:before{content:""}.fa-font-awesome-flag:before{content:""}.fa-font-awesome-logo-full:before{content:""}.fa-gratipay:before{content:""}.fa-apple:before{content:""}.fa-hive:before{content:""}.fa-gitkraken:before{content:""}.fa-keybase:before{content:""}.fa-apple-pay:before{content:""}.fa-padlet:before{content:""}.fa-amazon-pay:before{content:""}.fa-square-github:before{content:""}.fa-github-square:before{content:""}.fa-stumbleupon:before{content:""}.fa-fedex:before{content:""}.fa-phoenix-framework:before{content:""}.fa-shopify:before{content:""}.fa-neos:before{content:""}.fa-square-threads:before{content:""}.fa-hackerrank:before{content:""}.fa-researchgate:before{content:""}.fa-swift:before{content:""}.fa-angular:before{content:""}.fa-speakap:before{content:""}.fa-angrycreative:before{content:""}.fa-y-combinator:before{content:""}.fa-empire:before{content:""}.fa-envira:before{content:""}.fa-google-scholar:before{content:""}.fa-square-gitlab:before{content:""}.fa-gitlab-square:before{content:""}.fa-studiovinari:before{content:""}.fa-pied-piper:before{content:""}.fa-wordpress:before{content:""}.fa-product-hunt:before{content:""}.fa-firefox:before{content:""}.fa-linode:before{content:""}.fa-goodreads:before{content:""}.fa-square-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-jsfiddle:before{content:""}.fa-sith:before{content:""}.fa-themeisle:before{content:""}.fa-page4:before{content:""}.fa-hashnode:before{content:""}.fa-react:before{content:""}.fa-cc-paypal:before{content:""}.fa-squarespace:before{content:""}.fa-cc-stripe:before{content:""}.fa-creative-commons-share:before{content:""}.fa-bitcoin:before{content:""}.fa-keycdn:before{content:""}.fa-opera:before{content:""}.fa-itch-io:before{content:""}.fa-umbraco:before{content:""}.fa-galactic-senate:before{content:""}.fa-ubuntu:before{content:""}.fa-draft2digital:before{content:""}.fa-stripe:before{content:""}.fa-houzz:before{content:""}.fa-gg:before{content:""}.fa-dhl:before{content:""}.fa-square-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-xing:before{content:""}.fa-blackberry:before{content:""}.fa-creative-commons-pd:before{content:""}.fa-playstation:before{content:""}.fa-quinscape:before{content:""}.fa-less:before{content:""}.fa-blogger-b:before{content:""}.fa-opencart:before{content:""}.fa-vine:before{content:""}.fa-signal-messenger:before{content:""}.fa-paypal:before{content:""}.fa-gitlab:before{content:""}.fa-typo3:before{content:""}.fa-reddit-alien:before{content:""}.fa-yahoo:before{content:""}.fa-dailymotion:before{content:""}.fa-affiliatetheme:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-bootstrap:before{content:""}.fa-odnoklassniki:before{content:""}.fa-nfc-symbol:before{content:""}.fa-mintbit:before{content:""}.fa-ethereum:before{content:""}.fa-speaker-deck:before{content:""}.fa-creative-commons-nc-eu:before{content:""}.fa-patreon:before{content:""}.fa-avianex:before{content:""}.fa-ello:before{content:""}.fa-gofore:before{content:""}.fa-bimobject:before{content:""}.fa-brave-reverse:before{content:""}.fa-facebook-f:before{content:""}.fa-square-google-plus:before{content:""}.fa-google-plus-square:before{content:""}.fa-mandalorian:before{content:""}.fa-first-order-alt:before{content:""}.fa-osi:before{content:""}.fa-google-wallet:before{content:""}.fa-d-and-d-beyond:before{content:""}.fa-periscope:before{content:""}.fa-fulcrum:before{content:""}.fa-cloudscale:before{content:""}.fa-forumbee:before{content:""}.fa-mizuni:before{content:""}.fa-schlix:before{content:""}.fa-square-xing:before{content:""}.fa-xing-square:before{content:""}.fa-bandcamp:before{content:""}.fa-wpforms:before{content:""}.fa-cloudversify:before{content:""}.fa-usps:before{content:""}.fa-megaport:before{content:""}.fa-magento:before{content:""}.fa-spotify:before{content:""}.fa-optin-monster:before{content:""}.fa-fly:before{content:""}.fa-aviato:before{content:""}.fa-itunes:before{content:""}.fa-cuttlefish:before{content:""}.fa-blogger:before{content:""}.fa-flickr:before{content:""}.fa-viber:before{content:""}.fa-soundcloud:before{content:""}.fa-digg:before{content:""}.fa-tencent-weibo:before{content:""}.fa-letterboxd:before{content:""}.fa-symfony:before{content:""}.fa-maxcdn:before{content:""}.fa-etsy:before{content:""}.fa-facebook-messenger:before{content:""}.fa-audible:before{content:""}.fa-think-peaks:before{content:""}.fa-bilibili:before{content:""}.fa-erlang:before{content:""}.fa-x-twitter:before{content:""}.fa-cotton-bureau:before{content:""}.fa-dashcube:before{content:""}.fa-42-group:before{content:""}.fa-innosoft:before{content:""}.fa-stack-exchange:before{content:""}.fa-elementor:before{content:""}.fa-square-pied-piper:before{content:""}.fa-pied-piper-square:before{content:""}.fa-creative-commons-nd:before{content:""}.fa-palfed:before{content:""}.fa-superpowers:before{content:""}.fa-resolving:before{content:""}.fa-xbox:before{content:""}.fa-searchengin:before{content:""}.fa-tiktok:before{content:""}.fa-square-facebook:before{content:""}.fa-facebook-square:before{content:""}.fa-renren:before{content:""}.fa-linux:before{content:""}.fa-glide:before{content:""}.fa-linkedin:before{content:""}.fa-hubspot:before{content:""}.fa-deploydog:before{content:""}.fa-twitch:before{content:""}.fa-ravelry:before{content:""}.fa-mixer:before{content:""}.fa-square-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-vimeo:before{content:""}.fa-mendeley:before{content:""}.fa-uniregistry:before{content:""}.fa-figma:before{content:""}.fa-creative-commons-remix:before{content:""}.fa-cc-amazon-pay:before{content:""}.fa-dropbox:before{content:""}.fa-instagram:before{content:""}.fa-cmplid:before{content:""}.fa-upwork:before{content:""}.fa-facebook:before{content:""}.fa-gripfire:before{content:""}.fa-jedi-order:before{content:""}.fa-uikit:before{content:""}.fa-fort-awesome-alt:before{content:""}.fa-phabricator:before{content:""}.fa-ussunnah:before{content:""}.fa-earlybirds:before{content:""}.fa-trade-federation:before{content:""}.fa-autoprefixer:before{content:""}.fa-whatsapp:before{content:""}.fa-slideshare:before{content:""}.fa-google-play:before{content:""}.fa-viadeo:before{content:""}.fa-line:before{content:""}.fa-google-drive:before{content:""}.fa-servicestack:before{content:""}.fa-simplybuilt:before{content:""}.fa-bitbucket:before{content:""}.fa-imdb:before{content:""}.fa-deezer:before{content:""}.fa-raspberry-pi:before{content:""}.fa-jira:before{content:""}.fa-docker:before{content:""}.fa-screenpal:before{content:""}.fa-bluetooth:before{content:""}.fa-gitter:before{content:""}.fa-d-and-d:before{content:""}.fa-microblog:before{content:""}.fa-cc-diners-club:before{content:""}.fa-gg-circle:before{content:""}.fa-pied-piper-hat:before{content:""}.fa-kickstarter-k:before{content:""}.fa-yandex:before{content:""}.fa-readme:before{content:""}.fa-html5:before{content:""}.fa-sellsy:before{content:""}.fa-sass:before{content:""}.fa-wirsindhandwerk:before{content:""}.fa-wsh:before{content:""}.fa-buromobelexperte:before{content:""}.fa-salesforce:before{content:""}.fa-octopus-deploy:before{content:""}.fa-medapps:before{content:""}.fa-ns8:before{content:""}.fa-pinterest-p:before{content:""}.fa-apper:before{content:""}.fa-fort-awesome:before{content:""}.fa-waze:before{content:""}.fa-cc-jcb:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-fantasy-flight-games:before{content:""}.fa-rust:before{content:""}.fa-wix:before{content:""}.fa-square-behance:before{content:""}.fa-behance-square:before{content:""}.fa-supple:before{content:""}.fa-webflow:before{content:""}.fa-rebel:before{content:""}.fa-css3:before{content:""}.fa-staylinked:before{content:""}.fa-kaggle:before{content:""}.fa-space-awesome:before{content:""}.fa-deviantart:before{content:""}.fa-cpanel:before{content:""}.fa-goodreads-g:before{content:""}.fa-square-git:before{content:""}.fa-git-square:before{content:""}.fa-square-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-trello:before{content:""}.fa-creative-commons-nc-jp:before{content:""}.fa-get-pocket:before{content:""}.fa-perbyte:before{content:""}.fa-grunt:before{content:""}.fa-weebly:before{content:""}.fa-connectdevelop:before{content:""}.fa-leanpub:before{content:""}.fa-black-tie:before{content:""}.fa-themeco:before{content:""}.fa-python:before{content:""}.fa-android:before{content:""}.fa-bots:before{content:""}.fa-free-code-camp:before{content:""}.fa-hornbill:before{content:""}.fa-js:before{content:""}.fa-ideal:before{content:""}.fa-git:before{content:""}.fa-dev:before{content:""}.fa-sketch:before{content:""}.fa-yandex-international:before{content:""}.fa-cc-amex:before{content:""}.fa-uber:before{content:""}.fa-github:before{content:""}.fa-php:before{content:""}.fa-alipay:before{content:""}.fa-youtube:before{content:""}.fa-skyatlas:before{content:""}.fa-firefox-browser:before{content:""}.fa-replyd:before{content:""}.fa-suse:before{content:""}.fa-jenkins:before{content:""}.fa-twitter:before{content:""}.fa-rockrms:before{content:""}.fa-pinterest:before{content:""}.fa-buffer:before{content:""}.fa-npm:before{content:""}.fa-yammer:before{content:""}.fa-btc:before{content:""}.fa-dribbble:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-internet-explorer:before{content:""}.fa-stubber:before{content:""}.fa-telegram:before{content:""}.fa-telegram-plane:before{content:""}.fa-old-republic:before{content:""}.fa-odysee:before{content:""}.fa-square-whatsapp:before{content:""}.fa-whatsapp-square:before{content:""}.fa-node-js:before{content:""}.fa-edge-legacy:before{content:""}.fa-slack:before{content:""}.fa-slack-hash:before{content:""}.fa-medrt:before{content:""}.fa-usb:before{content:""}.fa-tumblr:before{content:""}.fa-vaadin:before{content:""}.fa-quora:before{content:""}.fa-square-x-twitter:before{content:""}.fa-reacteurope:before{content:""}.fa-medium:before{content:""}.fa-medium-m:before{content:""}.fa-amilia:before{content:""}.fa-mixcloud:before{content:""}.fa-flipboard:before{content:""}.fa-viacoin:before{content:""}.fa-critical-role:before{content:""}.fa-sitrox:before{content:""}.fa-discourse:before{content:""}.fa-joomla:before{content:""}.fa-mastodon:before{content:""}.fa-airbnb:before{content:""}.fa-wolf-pack-battalion:before{content:""}.fa-buy-n-large:before{content:""}.fa-gulp:before{content:""}.fa-creative-commons-sampling-plus:before{content:""}.fa-strava:before{content:""}.fa-ember:before{content:""}.fa-canadian-maple-leaf:before{content:""}.fa-teamspeak:before{content:""}.fa-pushed:before{content:""}.fa-wordpress-simple:before{content:""}.fa-nutritionix:before{content:""}.fa-wodu:before{content:""}.fa-google-pay:before{content:""}.fa-intercom:before{content:""}.fa-zhihu:before{content:""}.fa-korvue:before{content:""}.fa-pix:before{content:""}.fa-steam-symbol:before{content:""}/*! + * Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) * Copyright 2023 Fonticons, Inc. - */:root,:host{--fa-style-family-classic: "Font Awesome 6 Free";--fa-font-regular: normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(/build/assets/fa-regular-400-9169d8be.woff2) format("woff2"),url(/build/assets/fa-regular-400-7d81a1a7.ttf) format("truetype")}.far,.fa-regular{font-weight:400} + */:root,:host{--fa-style-family-classic: "Font Awesome 6 Free";--fa-font-regular: normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(/build/assets/fa-regular-400-2bccecf0.woff2) format("woff2"),url(/build/assets/fa-regular-400-5d02dc9b.ttf) format("truetype")}.far,.fa-regular{font-weight:400} diff --git a/public/build/assets/create-1321af8a.js b/public/build/assets/create-1321af8a.js deleted file mode 100644 index 5b6df91c7a..0000000000 --- a/public/build/assets/create-1321af8a.js +++ /dev/null @@ -1 +0,0 @@ -var q=Object.defineProperty;var H=(n,e,t)=>e in n?q(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var C=(n,e,t)=>(H(n,typeof e!="symbol"?e+"":e,t),t);import{x as P,w as M,J as j,z as R,I as z,A as $,y as V}from"./load-translations-87b32220.js";function A(){return{id:"",name:""}}function B(){let e=P(new Date,"yyyy-MM-dd HH:mm");return{description:"",amount:"",currency_code:"EUR",source_account:A(),destination_account:A(),date:e}}function W(n,e){let t=[];for(let s in n)if(n.hasOwnProperty(s)){const i=n[s];let o={};o.description=i.description,o.source_name=i.source_account.name,o.destination_name=i.destination_account.name,o.amount=i.amount,o.date=i.date,o.currency_code=i.currency_code,i.source_account.id.toString()!==""&&(o.source_id=i.source_account.id),i.destination_account.id.toString()!==""&&(o.destination_id=i.destination_account.id),o.type=e,t.push(o)}return t}const T={showAllSuggestions:!1,suggestionsThreshold:1,maximumItems:0,autoselectFirst:!0,ignoreEnter:!1,updateOnSelect:!1,highlightTyped:!1,highlightClass:"",fullWidth:!1,fixed:!1,fuzzy:!1,startsWith:!1,preventBrowserAutocomplete:!1,itemClass:"",activeClasses:["bg-primary","text-white"],labelField:"label",valueField:"value",searchFields:["label"],queryParam:"query",items:[],source:null,hiddenInput:!1,hiddenValue:"",clearControl:"",datalist:"",server:"",serverMethod:"GET",serverParams:{},serverDataKey:"data",fetchOptions:{},liveServer:!1,noCache:!0,debounceTime:300,notFoundMessage:"",onRenderItem:(n,e,t)=>e,onSelectItem:(n,e)=>{},onServerResponse:(n,e)=>n.json(),onChange:(n,e)=>{},onBeforeFetch:n=>{},onAfterFetch:n=>{}},x="is-loading",L="is-active",p="show",_="next",b="prev",g=new WeakMap;let k=0,v=0;function U(n,e=300){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>{n.apply(this,s)},e)}}function G(n){return n.normalize("NFD").replace(/[\u0300-\u036f]/g,"")}function y(n){return n?G(n.toString()).toLowerCase():""}function K(n,e){if(n.indexOf(e)>=0)return!0;let t=0;for(let s=0;se+"‍").join("")}class I{constructor(e,t={}){C(this,"handleEvent",e=>{["scroll","resize"].includes(e.type)?(this._timer&&window.cancelAnimationFrame(this._timer),this._timer=window.requestAnimationFrame(()=>{this[`on${e.type}`](e)})):this[`on${e.type}`](e)});if(!(e instanceof HTMLElement)){console.error("Invalid element",e);return}g.set(e,this),k++,v++,this._searchInput=e,this._configure(t),this._preventInput=!1,this._keyboardNavigation=!1,this._searchFunc=U(()=>{this._loadFromServer(!0)},this._config.debounceTime),this._configureSearchInput(),this._configureDropElement(),this._config.fixed&&(document.addEventListener("scroll",this,!0),window.addEventListener("resize",this));const s=this._getClearControl();s&&s.addEventListener("click",this),["focus","change","blur","input","keydown"].forEach(i=>{this._searchInput.addEventListener(i,this)}),["mousemove","mouseleave"].forEach(i=>{this._dropElement.addEventListener(i,this)}),this._fetchData()}static init(e="input.autocomplete",t={}){document.querySelectorAll(e).forEach(i=>{this.getOrCreateInstance(i,t)})}static getInstance(e){return g.has(e)?g.get(e):null}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,t)}dispose(){v--,["focus","change","blur","input","keydown"].forEach(t=>{this._searchInput.removeEventListener(t,this)}),["mousemove","mouseleave"].forEach(t=>{this._dropElement.removeEventListener(t,this)});const e=this._getClearControl();e&&e.removeEventListener("click",this),this._config.fixed&&v<=0&&(document.removeEventListener("scroll",this,!0),window.removeEventListener("resize",this)),this._dropElement.parentElement.removeChild(this._dropElement),g.delete(this._searchInput)}_getClearControl(){if(this._config.clearControl)return document.querySelector(this._config.clearControl)}_configure(e={}){this._config=Object.assign({},T);const t={...e,...this._searchInput.dataset},s=i=>["true","false","1","0",!0,!1].includes(i)&&!!JSON.parse(i);for(const[i,o]of Object.entries(T)){if(t[i]===void 0)continue;const a=t[i];switch(typeof o){case"number":this._config[i]=parseInt(a);break;case"boolean":this._config[i]=s(a);break;case"string":this._config[i]=a.toString();break;case"object":if(Array.isArray(o))if(typeof a=="string"){const r=a.includes("|")?"|":",";this._config[i]=a.split(r)}else this._config[i]=a;else this._config[i]=typeof a=="string"?JSON.parse(a):a;break;case"function":this._config[i]=typeof a=="string"?window[a]:a;break;default:this._config[i]=a;break}}}_configureSearchInput(){if(this._searchInput.autocomplete="off",this._searchInput.spellcheck=!1,w(this._searchInput,{"aria-autocomplete":"list","aria-haspopup":"menu","aria-expanded":"false",role:"combobox"}),this._searchInput.id&&this._config.preventBrowserAutocomplete){const e=document.querySelector(`[for="${this._searchInput.id}"]`);e&&X(e)}this._hiddenInput=null,this._config.hiddenInput&&(this._hiddenInput=document.createElement("input"),this._hiddenInput.type="hidden",this._hiddenInput.value=this._config.hiddenValue,this._hiddenInput.name=this._searchInput.name,this._searchInput.name="_"+this._searchInput.name,D(this._searchInput,this._hiddenInput))}_configureDropElement(){this._dropElement=document.createElement("ul"),this._dropElement.id="ac-menu-"+k,this._dropElement.classList.add("dropdown-menu","autocomplete-menu","p-0"),this._dropElement.style.maxHeight="280px",this._config.fullWidth||(this._dropElement.style.maxWidth="360px"),this._config.fixed&&(this._dropElement.style.position="fixed"),this._dropElement.style.overflowY="auto",this._dropElement.style.overscrollBehavior="contain",this._dropElement.style.textAlign="unset",D(this._searchInput,this._dropElement),this._searchInput.setAttribute("aria-controls",this._dropElement.id)}onclick(e){e.target.matches(this._config.clearControl)&&this.clear()}oninput(e){this._preventInput||(this._hiddenInput&&(this._hiddenInput.value=null),this.showOrSearch())}onchange(e){const t=this._searchInput.value,s=Object.values(this._items).find(i=>i.label===t);this._config.onChange(s,this)}onblur(e){this.hideSuggestions()}onfocus(e){this.showOrSearch()}onkeydown(e){switch(e.keyCode||e.key){case 13:case"Enter":if(this.isDropdownVisible()){const s=this.getSelection();s&&s.click(),(s||!this._config.ignoreEnter)&&e.preventDefault()}break;case 38:case"ArrowUp":e.preventDefault(),this._keyboardNavigation=!0,this._moveSelection(b);break;case 40:case"ArrowDown":e.preventDefault(),this._keyboardNavigation=!0,this.isDropdownVisible()?this._moveSelection(_):this.showOrSearch(!1);break;case 27:case"Escape":this.isDropdownVisible()&&(this._searchInput.focus(),this.hideSuggestions());break}}onmousemove(e){this._keyboardNavigation=!1}onmouseleave(e){this.removeSelection()}onscroll(e){this._positionMenu()}onresize(e){this._positionMenu()}getConfig(e=null){return e!==null?this._config[e]:this._config}setConfig(e,t){this._config[e]=t}setData(e){this._items={},this._addItems(e)}enable(){this._searchInput.setAttribute("disabled","")}disable(){this._searchInput.removeAttribute("disabled")}isDisabled(){return this._searchInput.hasAttribute("disabled")||this._searchInput.disabled||this._searchInput.hasAttribute("readonly")}isDropdownVisible(){return this._dropElement.classList.contains(p)}clear(){this._searchInput.value="",this._hiddenInput&&(this._hiddenInput.value="")}getSelection(){return this._dropElement.querySelector("a."+L)}removeSelection(){const e=this.getSelection();e&&e.classList.remove(...this._activeClasses())}_activeClasses(){return[...this._config.activeClasses,L]}_isItemEnabled(e){if(e.style.display==="none")return!1;const t=e.firstElementChild;return t.tagName==="A"&&!t.classList.contains("disabled")}_moveSelection(e=_,t=null){const s=this.getSelection();if(s){const i=e===_?"nextSibling":"previousSibling";t=s.parentNode;do t=t[i];while(t&&!this._isItemEnabled(t));t?(s.classList.remove(...this._activeClasses()),e===b?t.parentNode.scrollTop=t.offsetTop-t.parentNode.offsetTop:t.offsetTop>t.parentNode.offsetHeight-t.offsetHeight&&(t.parentNode.scrollTop+=t.offsetHeight)):s&&(t=s.parentElement)}else{if(e===b)return t;if(!t)for(t=this._dropElement.firstChild;t&&!this._isItemEnabled(t);)t=t.nextSibling}if(t){const i=t.querySelector("a");i.classList.add(...this._activeClasses()),this._searchInput.setAttribute("aria-activedescendant",i.id),this._config.updateOnSelect&&(this._searchInput.value=i.dataset.label)}else this._searchInput.setAttribute("aria-activedescendant","");return t}_shouldShow(){return this.isDisabled()?!1:this._searchInput.value.length>=this._config.suggestionsThreshold}showOrSearch(e=!0){if(e&&!this._shouldShow()){this.hideSuggestions();return}this._config.liveServer?this._searchFunc():this._config.source?this._config.source(this._searchInput.value,t=>{this.setData(t),this._showSuggestions()}):this._showSuggestions()}_createGroup(e){const t=this._createLi(),s=document.createElement("span");return t.append(s),s.classList.add("dropdown-header","text-truncate"),s.innerHTML=e,t}_createItem(e,t){let s=t.label;if(this._config.highlightTyped){const a=y(s).indexOf(e);a!==-1&&(s=s.substring(0,a)+`${s.substring(a,a+e.length)}`+s.substring(a+e.length,s.length))}s=this._config.onRenderItem(t,s,this);const i=this._createLi(),o=document.createElement("a");if(i.append(o),o.id=this._dropElement.id+"-"+this._dropElement.children.length,o.classList.add("dropdown-item","text-truncate"),this._config.itemClass&&o.classList.add(...this._config.itemClass.split(" ")),o.setAttribute("data-value",t.value),o.setAttribute("data-label",t.label),o.setAttribute("tabindex","-1"),o.setAttribute("role","menuitem"),o.setAttribute("href","#"),o.innerHTML=s,t.data)for(const[a,r]of Object.entries(t.data))o.dataset[a]=r;return o.addEventListener("mouseenter",a=>{this._keyboardNavigation||(this.removeSelection(),i.querySelector("a").classList.add(...this._activeClasses()))}),o.addEventListener("mousedown",a=>{a.preventDefault()}),o.addEventListener("click",a=>{a.preventDefault(),this._preventInput=!0,this._searchInput.value=J(t.label),this._hiddenInput&&(this._hiddenInput.value=t.value),this._config.onSelectItem(t,this),this.hideSuggestions(),this._preventInput=!1}),i}_showSuggestions(){if(document.activeElement!=this._searchInput)return;const e=y(this._searchInput.value);this._dropElement.innerHTML="";const t=Object.keys(this._items);let s=0,i=null;const o=[];for(let a=0;a0&&this._config.searchFields.forEach(d=>{const f=y(c[d]);let m=!1;if(this._config.fuzzy)m=K(f,e);else{const E=f.indexOf(e);m=this._config.startsWith?E===0:E>=0}m&&(l=!0)});const N=l||e.length===0;if(h||l){if(s++,c.group&&!o.includes(c.group)){const f=this._createGroup(c.group);this._dropElement.appendChild(f),o.push(c.group)}const d=this._createItem(e,c);if(!i&&N&&(i=d),this._dropElement.appendChild(d),this._config.maximumItems>0&&s>=this._config.maximumItems)break}}if(i&&this._config.autoselectFirst&&(this.removeSelection(),this._moveSelection(_,i)),s===0)if(this._config.notFoundMessage){const a=this._createLi();a.innerHTML=`${this._config.notFoundMessage}`,this._dropElement.appendChild(a),this._showDropdown()}else this.hideSuggestions();else this._showDropdown()}_createLi(){const e=document.createElement("li");return e.setAttribute("role","presentation"),e}_showDropdown(){this._dropElement.classList.add(p),this._dropElement.setAttribute("role","menu"),w(this._searchInput,{"aria-expanded":"true"}),this._positionMenu()}toggleSuggestions(e=!0){this._dropElement.classList.contains(p)?this.hideSuggestions():this.showOrSearch(e)}hideSuggestions(){this._dropElement.classList.remove(p),w(this._searchInput,{"aria-expanded":"false"}),this.removeSelection()}getInput(){return this._searchInput}getDropMenu(){return this._dropElement}_positionMenu(){const e=window.getComputedStyle(this._searchInput),t=this._searchInput.getBoundingClientRect(),s=e.direction==="rtl",i=this._config.fullWidth,o=this._config.fixed;let a=null,r=null;o&&(a=t.x,r=t.y+t.height,s&&!i&&(a-=this._dropElement.offsetWidth-t.width)),this._dropElement.style.transform="unset",i&&(this._dropElement.style.width=this._searchInput.offsetWidth+"px"),a!==null&&(this._dropElement.style.left=a+"px"),r!==null&&(this._dropElement.style.top=r+"px");const c=this._dropElement.getBoundingClientRect(),h=window.innerHeight;if(c.y+c.height>h){const l=i?t.height+4:t.height;this._dropElement.style.transform="translateY(calc(-100.1% - "+l+"px))"}}_fetchData(){this._items={},this._addItems(this._config.items);const e=this._config.datalist;if(e){const t=document.querySelector(`#${e}`);if(t){const s=Array.from(t.children).map(i=>{const o=i.getAttribute("value")??i.innerHTML.toLowerCase(),a=i.innerHTML;return{value:o,label:a}});this._addItems(s)}else console.error(`Datalist not found ${e}`)}this._setHiddenVal(),this._config.server&&!this._config.liveServer&&this._loadFromServer()}_setHiddenVal(){if(this._config.hiddenInput&&!this._config.hiddenValue)for(const[e,t]of Object.entries(this._items))t.label==this._searchInput.value&&(this._hiddenInput.value=e)}_addItems(e){const t=Object.keys(e);for(let s=0;sc.group=o.group),this._addItems(o.items);continue}const a=typeof o=="string"?o:o.label,r=typeof o!="object"?{}:o;r.label=o[this._config.labelField]??a,r.value=o[this._config.valueField]??i,r.label&&(this._items[r.value]=r)}}_loadFromServer(e=!1){this._abortController&&this._abortController.abort(),this._abortController=new AbortController;let t=this._searchInput.dataset.serverParams||{};typeof t=="string"&&(t=JSON.parse(t));const s=Object.assign({},this._config.serverParams,t);if(s[this._config.queryParam]=this._searchInput.value,this._config.noCache&&(s.t=Date.now()),s.related){const r=document.getElementById(s.related);if(r){s.related=r.value;const c=r.getAttribute("name");c&&(s[c]=r.value)}}const i=new URLSearchParams(s);let o=this._config.server,a=Object.assign(this._config.fetchOptions,{method:this._config.serverMethod||"GET",signal:this._abortController.signal});a.method==="POST"?a.body=i:o+="?"+i.toString(),this._searchInput.classList.add(x),this._config.onBeforeFetch(this),fetch(o,a).then(r=>this._config.onServerResponse(r,this)).then(r=>{const c=r[this._config.serverDataKey]||r;this.setData(c),this._setHiddenVal(),this._abortController=null,e&&this._showSuggestions()}).catch(r=>{r.name==="AbortError"||this._abortController.signal.aborted||console.error(r)}).finally(r=>{this._searchInput.classList.remove(x),this._config.onAfterFetch(this)})}}class Y{post(e){let t="/api/v2/transactions";return M.post(t,e)}}class Q{list(e){return M.get("/api/v2/currencies",{params:e})}}let u;const S={description:"/api/v2/autocomplete/transaction-descriptions",account:"/api/v2/autocomplete/accounts"};let Z=function(){return{count:0,totalAmount:0,transactionType:"unknown",showSuccessMessage:!1,showErrorMessage:!1,entries:[],loadingCurrencies:!0,defaultCurrency:{},enabledCurrencies:[],nativeCurrencies:[],foreignCurrencies:[],filters:{source:[],destination:[]},errorMessageText:"",detectTransactionType(){const n=this.entries[0].source_account.type??"unknown",e=this.entries[0].destination_account.type??"unknown";if(n==="unknown"&&e==="unknown"){this.transactionType="unknown",console.warn("Cannot infer transaction type from two unknown accounts.");return}if(n===e&&["Asset account","Loan","Debt","Mortgage"].includes(n)){this.transactionType="transfer",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}if(n==="Asset account"&&["Expense account","Debt","Loan","Mortgage"].includes(e)){this.transactionType="withdrawal",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}if(n==="Asset account"&&e==="unknown"){this.transactionType="withdrawal",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}if(["Debt","Loan","Mortgage"].includes(n)&&e==="Expense account"){this.transactionType="withdrawal",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}if(n==="Revenue account"&&["Asset account","Debt","Loan","Mortgage"].includes(e)){this.transactionType="deposit",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}if(["Debt","Loan","Mortgage"].includes(n)&&e==="Asset account"){this.transactionType="deposit",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}console.warn('Unknown account combination between "'+n+'" and "'+e+'".')},selectSourceAccount(n,e){const t=parseInt(e._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account={id:n.id,name:n.name,type:n.type},console.log("Changed source account into a known "+n.type.toLowerCase())},changedAmount(n){const e=parseInt(n.target.dataset.index);this.entries[e].amount=parseFloat(n.target.value),this.totalAmount=0;for(let t in this.entries)this.entries.hasOwnProperty(t)&&(this.totalAmount=this.totalAmount+parseFloat(this.entries[t].amount));console.log("Changed amount to "+this.totalAmount)},selectDestAccount(n,e){const t=parseInt(e._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account={id:n.id,name:n.name,type:n.type},console.log("Changed destination account into a known "+n.type.toLowerCase())},loadCurrencies(){console.log("Loading user currencies."),new Q().list({}).then(e=>{for(let t in e.data.data)if(e.data.data.hasOwnProperty(t)){let s=e.data.data[t];if(s.attributes.enabled){let i={id:s.id,name:s.attributes.name,code:s.attributes.code,default:s.attributes.default,symbol:s.attributes.symbol,decimal_places:s.attributes.decimal_places};i.default&&(this.defaultCurrency=i),this.enabledCurrencies.push(i)}}this.loadingCurrencies=!1,console.log(this.enabledCurrencies)})},changeSourceAccount(n,e){if(typeof n>"u"){const t=parseInt(e._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account.name===e._searchInput.value){console.warn('Ignore hallucinated source account name change to "'+e._searchInput.value+'"');return}document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account={name:e._searchInput.value},console.log('Changed source account into a unknown account called "'+e._searchInput.value+'"')}},changeDestAccount(n,e){if(typeof n>"u"){const t=parseInt(e._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account.name===e._searchInput.value){console.warn('Ignore hallucinated destination account name change to "'+e._searchInput.value+'"');return}document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account={name:e._searchInput.value},console.log('Changed destination account into a unknown account called "'+e._searchInput.value+'"')}},showError:!1,showSuccess:!1,addedSplit(){console.log("addedSplit"),I.init("input.ac-source",{server:S.account,serverParams:{types:this.filters.source},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,preventBrowserAutocomplete:!0,highlightTyped:!0,liveServer:!0,onChange:this.changeSourceAccount,onSelectItem:this.selectSourceAccount,onRenderItem:function(n,e,t){return n.name_with_balance+'
'+u.t("firefly.account_type_"+n.type)+""}}),I.init("input.ac-dest",{server:S.account,serverParams:{types:this.filters.destination},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,preventBrowserAutocomplete:!0,liveServer:!0,highlightTyped:!0,onSelectItem:this.selectDestAccount,onChange:this.changeDestAccount,onRenderItem:function(n,e,t){return n.name_with_balance+'
'+u.t("firefly.account_type_"+n.type)+""}}),this.filters.destination=[],I.init("input.ac-description",{server:S.description,fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},valueField:"id",labelField:"description",highlightTyped:!0,onSelectItem:console.log})},init(){Promise.all([R("language","en_US")]).then(n=>{u=new z;const e=n[0].replace("-","_");u.locale=e,$(u,e).then(()=>{this.addSplit()})}),this.loadCurrencies(),this.filters.source=["Asset account","Loan","Debt","Mortgage","Revenue account"],this.filters.destination=["Expense account","Loan","Debt","Mortgage","Asset account"]},submitTransaction(){this.detectTransactionType();let n=W(this.entries,this.transactionType),e={group_title:null,fire_webhooks:!1,apply_rules:!1,transactions:n};n.length>1&&(e.group_title=n[0].description);let t=new Y;console.log(e),t.post(e).then(s=>{this.showSuccessMessage=!0,console.log(s),window.location="transactions/show/"+s.data.data.id+"?transaction_group_id="+s.data.data.id+"&message=created"}).catch(s=>{this.showErrorMessage=!0,this.errorMessageText=s.response.data.message})},addSplit(){this.entries.push(B())},removeSplit(n){this.entries.splice(n,1),document.querySelector("#split-0-tab").click()},formattedTotalAmount(){return V(this.totalAmount,"EUR")}}},O={transactions:Z,dates:j};function F(){Object.keys(O).forEach(n=>{console.log(`Loading page component "${n}"`);let e=O[n]();Alpine.data(n,()=>e)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),F()});window.bootstrapped&&(console.log("Loaded through window variable."),F()); diff --git a/public/build/assets/create-46a6e026.js b/public/build/assets/create-46a6e026.js new file mode 100644 index 0000000000..8935fd736f --- /dev/null +++ b/public/build/assets/create-46a6e026.js @@ -0,0 +1,3 @@ +var q=Object.defineProperty;var H=(n,e,t)=>e in n?q(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var C=(n,e,t)=>(H(n,typeof e!="symbol"?e+"":e,t),t);import{x as P,w as M,J as j,z as R,I as z,A as $,y as V}from"./load-translations-85b093de.js";function T(){return{id:"",name:"",alpine_name:""}}function B(){let e=P(new Date,"yyyy-MM-dd HH:mm");return{description:"",amount:"",currency_code:"EUR",source_account:T(),destination_account:T(),date:e}}function W(n,e){let t=[];for(let s in n)if(n.hasOwnProperty(s)){const i=n[s];let r={};r.description=i.description,r.source_name=i.source_account.name,r.destination_name=i.destination_account.name,r.amount=i.amount,r.date=i.date,r.currency_code=i.currency_code,typeof i.source_account.id<"u"&&i.source_account.id.toString()!==""&&(r.source_id=i.source_account.id),typeof i.destination_account.id<"u"&&i.destination_account.id.toString()!==""&&(r.destination_id=i.destination_account.id),r.type=e,t.push(r)}return t}const x={showAllSuggestions:!1,suggestionsThreshold:1,maximumItems:0,autoselectFirst:!0,ignoreEnter:!1,updateOnSelect:!1,highlightTyped:!1,highlightClass:"",fullWidth:!1,fixed:!1,fuzzy:!1,startsWith:!1,fillIn:!1,preventBrowserAutocomplete:!1,itemClass:"",activeClasses:["bg-primary","text-white"],labelField:"label",valueField:"value",searchFields:["label"],queryParam:"query",items:[],source:null,hiddenInput:!1,hiddenValue:"",clearControl:"",datalist:"",server:"",serverMethod:"GET",serverParams:{},serverDataKey:"data",fetchOptions:{},liveServer:!1,noCache:!0,debounceTime:300,notFoundMessage:"",onRenderItem:(n,e,t)=>e,onSelectItem:(n,e)=>{},onServerResponse:(n,e)=>n.json(),onChange:(n,e)=>{},onBeforeFetch:n=>{},onAfterFetch:n=>{}},A="is-loading",k="is-active",p="show",_="next",y="prev",g=new WeakMap;let L=0,v=0;function U(n,e=300){let t;return(...s)=>{clearTimeout(t),t=setTimeout(()=>{n.apply(this,s)},e)}}function G(n){return n.normalize("NFD").replace(/[\u0300-\u036f]/g,"")}function b(n){return n?G(n.toString()).toLowerCase():""}function K(n,e){if(n.indexOf(e)>=0)return!0;let t=0;for(let s=0;se+"‍").join("")}function Y(n,e="window"){return n.split(".").reduce((t,s)=>t[s],e)}class S{constructor(e,t={}){C(this,"handleEvent",e=>{["scroll","resize"].includes(e.type)?(this._timer&&window.cancelAnimationFrame(this._timer),this._timer=window.requestAnimationFrame(()=>{this[`on${e.type}`](e)})):this[`on${e.type}`](e)});if(!(e instanceof HTMLElement)){console.error("Invalid element",e);return}g.set(e,this),L++,v++,this._searchInput=e,this._configure(t),this._preventInput=!1,this._keyboardNavigation=!1,this._searchFunc=U(()=>{this._loadFromServer(!0)},this._config.debounceTime),this._configureSearchInput(),this._configureDropElement(),this._config.fixed&&(document.addEventListener("scroll",this,!0),window.addEventListener("resize",this));const s=this._getClearControl();s&&s.addEventListener("click",this),["focus","change","blur","input","keydown"].forEach(i=>{this._searchInput.addEventListener(i,this)}),["mousemove","mouseleave"].forEach(i=>{this._dropElement.addEventListener(i,this)}),this._fetchData()}static init(e="input.autocomplete",t={}){document.querySelectorAll(e).forEach(i=>{this.getOrCreateInstance(i,t)})}static getInstance(e){return g.has(e)?g.get(e):null}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,t)}dispose(){v--,["focus","change","blur","input","keydown"].forEach(t=>{this._searchInput.removeEventListener(t,this)}),["mousemove","mouseleave"].forEach(t=>{this._dropElement.removeEventListener(t,this)});const e=this._getClearControl();e&&e.removeEventListener("click",this),this._config.fixed&&v<=0&&(document.removeEventListener("scroll",this,!0),window.removeEventListener("resize",this)),this._dropElement.parentElement.removeChild(this._dropElement),g.delete(this._searchInput)}_getClearControl(){if(this._config.clearControl)return document.querySelector(this._config.clearControl)}_configure(e={}){this._config=Object.assign({},x);const t={...e,...this._searchInput.dataset},s=i=>["true","false","1","0",!0,!1].includes(i)&&!!JSON.parse(i);for(const[i,r]of Object.entries(x)){if(t[i]===void 0)continue;const o=t[i];switch(typeof r){case"number":this._config[i]=parseInt(o);break;case"boolean":this._config[i]=s(o);break;case"string":this._config[i]=o.toString();break;case"object":if(Array.isArray(r))if(typeof o=="string"){const a=o.includes("|")?"|":",";this._config[i]=o.split(a)}else this._config[i]=o;else this._config[i]=typeof o=="string"?JSON.parse(o):o;break;case"function":this._config[i]=typeof o=="string"?window[o]:o;break;default:this._config[i]=o;break}}}_configureSearchInput(){if(this._searchInput.autocomplete="off",this._searchInput.spellcheck=!1,w(this._searchInput,{"aria-autocomplete":"list","aria-haspopup":"menu","aria-expanded":"false",role:"combobox"}),this._searchInput.id&&this._config.preventBrowserAutocomplete){const e=document.querySelector(`[for="${this._searchInput.id}"]`);e&&X(e)}this._hiddenInput=null,this._config.hiddenInput&&(this._hiddenInput=document.createElement("input"),this._hiddenInput.type="hidden",this._hiddenInput.value=this._config.hiddenValue,this._hiddenInput.name=this._searchInput.name,this._searchInput.name="_"+this._searchInput.name,O(this._searchInput,this._hiddenInput))}_configureDropElement(){this._dropElement=document.createElement("ul"),this._dropElement.id="ac-menu-"+L,this._dropElement.classList.add("dropdown-menu","autocomplete-menu","p-0"),this._dropElement.style.maxHeight="280px",this._config.fullWidth||(this._dropElement.style.maxWidth="360px"),this._config.fixed&&(this._dropElement.style.position="fixed"),this._dropElement.style.overflowY="auto",this._dropElement.style.overscrollBehavior="contain",this._dropElement.style.textAlign="unset",O(this._searchInput,this._dropElement),this._searchInput.setAttribute("aria-controls",this._dropElement.id)}onclick(e){e.target.matches(this._config.clearControl)&&this.clear()}oninput(e){this._preventInput||(this._hiddenInput&&(this._hiddenInput.value=null),this.showOrSearch())}onchange(e){const t=this._searchInput.value,s=Object.values(this._items).find(i=>i.label===t);this._config.onChange(s,this)}onblur(e){if(e.relatedTarget&&e.relatedTarget.classList.contains("modal")){this._searchInput.focus();return}setTimeout(()=>{this.hideSuggestions()},100)}onfocus(e){this.showOrSearch()}onkeydown(e){switch(e.keyCode||e.key){case 13:case"Enter":if(this.isDropdownVisible()){const s=this.getSelection();s&&s.click(),(s||!this._config.ignoreEnter)&&e.preventDefault()}break;case 38:case"ArrowUp":e.preventDefault(),this._keyboardNavigation=!0,this._moveSelection(y);break;case 40:case"ArrowDown":e.preventDefault(),this._keyboardNavigation=!0,this.isDropdownVisible()?this._moveSelection(_):this.showOrSearch(!1);break;case 27:case"Escape":this.isDropdownVisible()&&(this._searchInput.focus(),this.hideSuggestions());break}}onmousemove(e){this._keyboardNavigation=!1}onmouseleave(e){this.removeSelection()}onscroll(e){this._positionMenu()}onresize(e){this._positionMenu()}getConfig(e=null){return e!==null?this._config[e]:this._config}setConfig(e,t){this._config[e]=t}setData(e){this._items={},this._addItems(e)}enable(){this._searchInput.setAttribute("disabled","")}disable(){this._searchInput.removeAttribute("disabled")}isDisabled(){return this._searchInput.hasAttribute("disabled")||this._searchInput.disabled||this._searchInput.hasAttribute("readonly")}isDropdownVisible(){return this._dropElement.classList.contains(p)}clear(){this._searchInput.value="",this._hiddenInput&&(this._hiddenInput.value="")}getSelection(){return this._dropElement.querySelector("a."+k)}removeSelection(){const e=this.getSelection();e&&e.classList.remove(...this._activeClasses())}_activeClasses(){return[...this._config.activeClasses,k]}_isItemEnabled(e){if(e.style.display==="none")return!1;const t=e.firstElementChild;return t.tagName==="A"&&!t.classList.contains("disabled")}_moveSelection(e=_,t=null){const s=this.getSelection();if(s){const i=e===_?"nextSibling":"previousSibling";t=s.parentNode;do t=t[i];while(t&&!this._isItemEnabled(t));t?(s.classList.remove(...this._activeClasses()),e===y?t.parentNode.scrollTop=t.offsetTop-t.parentNode.offsetTop:t.offsetTop>t.parentNode.offsetHeight-t.offsetHeight&&(t.parentNode.scrollTop+=t.offsetHeight)):s&&(t=s.parentElement)}else{if(e===y)return t;if(!t)for(t=this._dropElement.firstChild;t&&!this._isItemEnabled(t);)t=t.nextSibling}if(t){const i=t.querySelector("a");i.classList.add(...this._activeClasses()),this._searchInput.setAttribute("aria-activedescendant",i.id),this._config.updateOnSelect&&(this._searchInput.value=i.dataset.label)}else this._searchInput.setAttribute("aria-activedescendant","");return t}_shouldShow(){return this.isDisabled()?!1:this._searchInput.value.length>=this._config.suggestionsThreshold}showOrSearch(e=!0){if(e&&!this._shouldShow()){this.hideSuggestions();return}this._config.liveServer?this._searchFunc():this._config.source?this._config.source(this._searchInput.value,t=>{this.setData(t),this._showSuggestions()}):this._showSuggestions()}_createGroup(e){const t=this._createLi(),s=document.createElement("span");return t.append(s),s.classList.add("dropdown-header","text-truncate"),s.innerHTML=e,t}_createItem(e,t){let s=t.label;if(this._config.highlightTyped){const o=b(s).indexOf(e);o!==-1&&(s=s.substring(0,o)+`${s.substring(o,o+e.length)}`+s.substring(o+e.length,s.length))}s=this._config.onRenderItem(t,s,this);const i=this._createLi(),r=document.createElement("a");if(i.append(r),r.id=this._dropElement.id+"-"+this._dropElement.children.length,r.classList.add("dropdown-item","text-truncate"),this._config.itemClass&&r.classList.add(...this._config.itemClass.split(" ")),r.setAttribute("data-value",t.value),r.setAttribute("data-label",t.label),r.setAttribute("tabindex","-1"),r.setAttribute("role","menuitem"),r.setAttribute("href","#"),r.innerHTML=s,t.data)for(const[o,a]of Object.entries(t.data))r.dataset[o]=a;if(this._config.fillIn){const o=document.createElement("button");o.type="button",o.classList.add("btn","btn-link","border-0"),o.innerHTML=` + + `,i.append(o),i.classList.add("d-flex","justify-content-between"),o.addEventListener("click",a=>{this._searchInput.value=t.label,this._searchInput.focus()})}return r.addEventListener("mouseenter",o=>{this._keyboardNavigation||(this.removeSelection(),i.querySelector("a").classList.add(...this._activeClasses()))}),r.addEventListener("mousedown",o=>{o.preventDefault()}),r.addEventListener("click",o=>{o.preventDefault(),this._preventInput=!0,this._searchInput.value=J(t.label),this._hiddenInput&&(this._hiddenInput.value=t.value),this._config.onSelectItem(t,this),this.hideSuggestions(),this._preventInput=!1}),i}_showSuggestions(){if(document.activeElement!=this._searchInput)return;const e=b(this._searchInput.value);this._dropElement.innerHTML="";const t=Object.keys(this._items);let s=0,i=null;const r=[];for(let o=0;o0&&this._config.searchFields.forEach(d=>{const f=b(c[d]);let m=!1;if(this._config.fuzzy)m=K(f,e);else{const E=f.indexOf(e);m=this._config.startsWith?E===0:E>=0}m&&(l=!0)});const N=l||e.length===0;if(h||l){if(s++,c.group&&!r.includes(c.group)){const f=this._createGroup(c.group);this._dropElement.appendChild(f),r.push(c.group)}const d=this._createItem(e,c);if(!i&&N&&(i=d),this._dropElement.appendChild(d),this._config.maximumItems>0&&s>=this._config.maximumItems)break}}if(i&&this._config.autoselectFirst&&(this.removeSelection(),this._moveSelection(_,i)),s===0)if(this._config.notFoundMessage){const o=this._createLi();o.innerHTML=`${this._config.notFoundMessage}`,this._dropElement.appendChild(o),this._showDropdown()}else this.hideSuggestions();else this._showDropdown()}_createLi(){const e=document.createElement("li");return e.setAttribute("role","presentation"),e}_showDropdown(){this._dropElement.classList.add(p),this._dropElement.setAttribute("role","menu"),w(this._searchInput,{"aria-expanded":"true"}),this._positionMenu()}toggleSuggestions(e=!0){this._dropElement.classList.contains(p)?this.hideSuggestions():this.showOrSearch(e)}hideSuggestions(){this._dropElement.classList.remove(p),w(this._searchInput,{"aria-expanded":"false"}),this.removeSelection()}getInput(){return this._searchInput}getDropMenu(){return this._dropElement}_positionMenu(){const e=window.getComputedStyle(this._searchInput),t=this._searchInput.getBoundingClientRect(),s=e.direction==="rtl",i=this._config.fullWidth,r=this._config.fixed;let o=null,a=null;r&&(o=t.x,a=t.y+t.height,s&&!i&&(o-=this._dropElement.offsetWidth-t.width)),this._dropElement.style.transform="unset",i&&(this._dropElement.style.width=this._searchInput.offsetWidth+"px"),o!==null&&(this._dropElement.style.left=o+"px"),a!==null&&(this._dropElement.style.top=a+"px");const c=this._dropElement.getBoundingClientRect(),h=window.innerHeight;if(c.y+c.height>h){const l=i?t.height+4:t.height;this._dropElement.style.transform="translateY(calc(-100.1% - "+l+"px))"}}_fetchData(){this._items={},this._addItems(this._config.items);const e=this._config.datalist;if(e){const t=document.querySelector(`#${e}`);if(t){const s=Array.from(t.children).map(i=>{const r=i.getAttribute("value")??i.innerHTML.toLowerCase(),o=i.innerHTML;return{value:r,label:o}});this._addItems(s)}else console.error(`Datalist not found ${e}`)}this._setHiddenVal(),this._config.server&&!this._config.liveServer&&this._loadFromServer()}_setHiddenVal(){if(this._config.hiddenInput&&!this._config.hiddenValue)for(const[e,t]of Object.entries(this._items))t.label==this._searchInput.value&&(this._hiddenInput.value=e)}_addItems(e){const t=Object.keys(e);for(let s=0;sc.group=r.group),this._addItems(r.items);continue}const o=typeof r=="string"?r:r.label,a=typeof r!="object"?{}:r;a.label=r[this._config.labelField]??o,a.value=r[this._config.valueField]??i,a.label&&(this._items[a.value]=a)}}_loadFromServer(e=!1){this._abortController&&this._abortController.abort(),this._abortController=new AbortController;let t=this._searchInput.dataset.serverParams||{};typeof t=="string"&&(t=JSON.parse(t));const s=Object.assign({},this._config.serverParams,t);if(s[this._config.queryParam]=this._searchInput.value,this._config.noCache&&(s.t=Date.now()),s.related){const a=document.getElementById(s.related);if(a){s.related=a.value;const c=a.getAttribute("name");c&&(s[c]=a.value)}}const i=new URLSearchParams(s);let r=this._config.server,o=Object.assign(this._config.fetchOptions,{method:this._config.serverMethod||"GET",signal:this._abortController.signal});o.method==="POST"?o.body=i:r+="?"+i.toString(),this._searchInput.classList.add(A),this._config.onBeforeFetch(this),fetch(r,o).then(a=>this._config.onServerResponse(a,this)).then(a=>{const c=Y(this._config.serverDataKey,a)||a;this.setData(c),this._setHiddenVal(),this._abortController=null,e&&this._showSuggestions()}).catch(a=>{a.name==="AbortError"||this._abortController.signal.aborted||console.error(a)}).finally(a=>{this._searchInput.classList.remove(A),this._config.onAfterFetch(this)})}}class Q{post(e){let t="/api/v2/transactions";return M.post(t,e)}}class Z{list(e){return M.get("/api/v2/currencies",{params:e})}}let u;const I={description:"/api/v2/autocomplete/transaction-descriptions",account:"/api/v2/autocomplete/accounts"};let ee=function(){return{count:0,totalAmount:0,transactionType:"unknown",showSuccessMessage:!1,showErrorMessage:!1,entries:[],loadingCurrencies:!0,defaultCurrency:{},enabledCurrencies:[],nativeCurrencies:[],foreignCurrencies:[],filters:{source:[],destination:[]},errorMessageText:"",detectTransactionType(){const n=this.entries[0].source_account.type??"unknown",e=this.entries[0].destination_account.type??"unknown";if(n==="unknown"&&e==="unknown"){this.transactionType="unknown",console.warn("Cannot infer transaction type from two unknown accounts.");return}if(n===e&&["Asset account","Loan","Debt","Mortgage"].includes(n)){this.transactionType="transfer",console.log('Transaction type is detected to be "'+this.transactionType+'".'),console.log("filter down currencies for transfer.");return}if(n==="Asset account"&&["Expense account","Debt","Loan","Mortgage"].includes(e)){this.transactionType="withdrawal",console.log('[a] Transaction type is detected to be "'+this.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(n==="Asset account"&&e==="unknown"){this.transactionType="withdrawal",console.log('[b] Transaction type is detected to be "'+this.transactionType+'".'),console.log(this.entries[0].source_account),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(["Debt","Loan","Mortgage"].includes(n)&&e==="Expense account"){this.transactionType="withdrawal",console.log('[c] Transaction type is detected to be "'+this.transactionType+'".'),this.filterNativeCurrencies(this.entries[0].source_account.currency_code);return}if(n==="Revenue account"&&["Asset account","Debt","Loan","Mortgage"].includes(e)){this.transactionType="deposit",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}if(["Debt","Loan","Mortgage"].includes(n)&&e==="Asset account"){this.transactionType="deposit",console.log('Transaction type is detected to be "'+this.transactionType+'".');return}console.warn('Unknown account combination between "'+n+'" and "'+e+'".')},selectSourceAccount(n,e){const t=parseInt(e._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account={id:n.id,name:n.name,alpine_name:n.name,type:n.type,currency_code:n.currency_code},console.log("Changed source account into a known "+n.type.toLowerCase()),document.querySelector("#form")._x_dataStack[0].detectTransactionType()},filterNativeCurrencies(n){console.log('filterNativeCurrencies("'+n+'")');let e=[],t;for(let s in this.enabledCurrencies)if(this.enabledCurrencies.hasOwnProperty(s)){let i=this.enabledCurrencies[s];i.code===n&&(t=i)}e.push(t),this.nativeCurrencies=e;for(let s in this.entries)this.entries.hasOwnProperty(s)&&(this.entries[s].currency_code=n)},changedAmount(n){const e=parseInt(n.target.dataset.index);this.entries[e].amount=parseFloat(n.target.value),this.totalAmount=0;for(let t in this.entries)this.entries.hasOwnProperty(t)&&(this.totalAmount=this.totalAmount+parseFloat(this.entries[t].amount));console.log("Changed amount to "+this.totalAmount)},selectDestAccount(n,e){const t=parseInt(e._searchInput.attributes["data-index"].value);document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account={id:n.id,name:n.name,alpine_name:n.name,type:n.type,currency_code:n.currency_code},console.log("Changed destination account into a known "+n.type.toLowerCase()),document.querySelector("#form")._x_dataStack[0].detectTransactionType()},loadCurrencies(){console.log("Loading user currencies."),new Z().list({}).then(e=>{for(let t in e.data.data)if(e.data.data.hasOwnProperty(t)){let s=e.data.data[t];if(s.attributes.enabled){let i={id:s.id,name:s.attributes.name,code:s.attributes.code,default:s.attributes.default,symbol:s.attributes.symbol,decimal_places:s.attributes.decimal_places};i.default&&(this.defaultCurrency=i),this.enabledCurrencies.push(i),this.nativeCurrencies.push(i)}}this.loadingCurrencies=!1,console.log(this.enabledCurrencies)})},changeSourceAccount(n,e){if(console.log("changeSourceAccount"),typeof n>"u"){const t=parseInt(e._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account.name===e._searchInput.value){console.warn('Ignore hallucinated source account name change to "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType();return}document.querySelector("#form")._x_dataStack[0].$data.entries[t].source_account={name:e._searchInput.value,alpine_name:e._searchInput.value},console.log('Changed source account into a unknown account called "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType()}},changeDestAccount(n,e){if(document.querySelector("#form")._x_dataStack[0].$data.entries[0].destination_account,typeof n>"u"){const t=parseInt(e._searchInput.attributes["data-index"].value);if(document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account.name===e._searchInput.value){console.warn('Ignore hallucinated destination account name change to "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType();return}document.querySelector("#form")._x_dataStack[0].$data.entries[t].destination_account={name:e._searchInput.value,alpine_name:e._searchInput.value},console.log('Changed destination account into a unknown account called "'+e._searchInput.value+'"'),document.querySelector("#form")._x_dataStack[0].detectTransactionType()}},showError:!1,showSuccess:!1,addedSplit(){console.log("addedSplit"),S.init("input.ac-source",{server:I.account,serverParams:{types:this.filters.source},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,preventBrowserAutocomplete:!0,highlightTyped:!0,liveServer:!0,onChange:this.changeSourceAccount,onSelectItem:this.selectSourceAccount,onRenderItem:function(n,e,t){return n.name_with_balance+'
'+u.t("firefly.account_type_"+n.type)+""}}),S.init("input.ac-dest",{server:I.account,serverParams:{types:this.filters.destination},fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},hiddenInput:!0,preventBrowserAutocomplete:!0,liveServer:!0,highlightTyped:!0,onSelectItem:this.selectDestAccount,onChange:this.changeDestAccount,onRenderItem:function(n,e,t){return n.name_with_balance+'
'+u.t("firefly.account_type_"+n.type)+""}}),this.filters.destination=[],S.init("input.ac-description",{server:I.description,fetchOptions:{headers:{"X-CSRF-TOKEN":document.head.querySelector('meta[name="csrf-token"]').content}},valueField:"id",labelField:"description",highlightTyped:!0,onSelectItem:console.log})},init(){Promise.all([R("language","en_US")]).then(n=>{u=new z;const e=n[0].replace("-","_");u.locale=e,$(u,e).then(()=>{this.addSplit()})}),this.loadCurrencies(),this.filters.source=["Asset account","Loan","Debt","Mortgage","Revenue account"],this.filters.destination=["Expense account","Loan","Debt","Mortgage","Asset account"]},submitTransaction(){this.detectTransactionType();let n=W(this.entries,this.transactionType),e={group_title:null,fire_webhooks:!1,apply_rules:!1,transactions:n};n.length>1&&(e.group_title=n[0].description);let t=new Q;console.log(e),t.post(e).then(s=>{this.showSuccessMessage=!0,console.log(s),window.location="transactions/show/"+s.data.data.id+"?transaction_group_id="+s.data.data.id+"&message=created"}).catch(s=>{this.showErrorMessage=!0,this.errorMessageText=s.response.data.message})},addSplit(){this.entries.push(B())},removeSplit(n){this.entries.splice(n,1),document.querySelector("#split-0-tab").click()},formattedTotalAmount(){return V(this.totalAmount,"EUR")}}},D={transactions:ee,dates:j};function F(){Object.keys(D).forEach(n=>{console.log(`Loading page component "${n}"`);let e=D[n]();Alpine.data(n,()=>e)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),F()});window.bootstrapped&&(console.log("Loaded through window variable."),F()); diff --git a/public/build/assets/dashboard-42661760.js b/public/build/assets/dashboard-bfc72cdb.js similarity index 57% rename from public/build/assets/dashboard-42661760.js rename to public/build/assets/dashboard-bfc72cdb.js index f275765ec7..a3b0bbe79b 100644 --- a/public/build/assets/dashboard-42661760.js +++ b/public/build/assets/dashboard-bfc72cdb.js @@ -1,30 +1,30 @@ -var Sa=Object.defineProperty;var Oa=(n,t,e)=>t in n?Sa(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var k=(n,t,e)=>(Oa(n,typeof t!="symbol"?t+"":t,e),e);import{r as z,a as ai,t as wt,s as Yi,g as ji,b as ls,c as mr,d as j,e as br,f as yr,_ as an,h as Aa,i as Ui,j as _r,k as La,l as Ra,m as xr,n as Fa,o as Os,p as Ia,q as As,u as Ea,v as za,w as vt,x as J,y as V,z as bt,P as Ba,I as li,A as ci,B as Na,C as Wa,D as Ha,E as Va,F as Ls,G as Ya,H as ja,J as Ua}from"./load-translations-87b32220.js";var $a=36e5;function qa(n,t){z(2,arguments);var e=wt(t);return ai(n,e*$a)}var Xa=864e5;function Ka(n,t){z(2,arguments);var e=Yi(n),i=Yi(t),s=e.getTime()-ji(e),o=i.getTime()-ji(i);return Math.round((s-o)/Xa)}var Ga=6e4;function Qa(n,t){z(2,arguments);var e=wt(t);return ai(n,e*Ga)}function Za(n,t){z(2,arguments);var e=wt(t),i=e*3;return ls(n,i)}function Ja(n,t){z(2,arguments);var e=wt(t);return ai(n,e*1e3)}function tl(n,t){z(2,arguments);var e=wt(t),i=e*7;return mr(n,i)}function el(n,t){z(2,arguments);var e=wt(t);return ls(n,e*12)}function tn(n,t){z(2,arguments);var e=j(n),i=j(t),s=e.getTime()-i.getTime();return s<0?-1:s>0?1:s}var ui=6e4,hi=36e5,nl=1e3;function il(n,t){z(2,arguments);var e=j(n),i=j(t),s=e.getFullYear()-i.getFullYear(),o=e.getMonth()-i.getMonth();return s*12+o}function sl(n,t){z(2,arguments);var e=j(n),i=j(t);return e.getFullYear()-i.getFullYear()}function Rs(n,t){var e=n.getFullYear()-t.getFullYear()||n.getMonth()-t.getMonth()||n.getDate()-t.getDate()||n.getHours()-t.getHours()||n.getMinutes()-t.getMinutes()||n.getSeconds()-t.getSeconds()||n.getMilliseconds()-t.getMilliseconds();return e<0?-1:e>0?1:e}function vr(n,t){z(2,arguments);var e=j(n),i=j(t),s=Rs(e,i),o=Math.abs(Ka(e,i));e.setDate(e.getDate()-s*o);var r=+(Rs(e,i)===-s),a=s*(o-r);return a===0?0:a}function di(n,t){return z(2,arguments),j(n).getTime()-j(t).getTime()}var Fs={ceil:Math.ceil,round:Math.round,floor:Math.floor,trunc:function(t){return t<0?Math.ceil(t):Math.floor(t)}},ol="trunc";function bn(n){return n?Fs[n]:Fs[ol]}function rl(n,t,e){z(2,arguments);var i=di(n,t)/hi;return bn(e==null?void 0:e.roundingMethod)(i)}function al(n,t,e){z(2,arguments);var i=di(n,t)/ui;return bn(e==null?void 0:e.roundingMethod)(i)}function ll(n){z(1,arguments);var t=j(n);return br(t).getTime()===yr(t).getTime()}function wr(n,t){z(2,arguments);var e=j(n),i=j(t),s=tn(e,i),o=Math.abs(il(e,i)),r;if(o<1)r=0;else{e.getMonth()===1&&e.getDate()>27&&e.setDate(30),e.setMonth(e.getMonth()-s*o);var a=tn(e,i)===-s;ll(j(n))&&o===1&&tn(n,i)===1&&(a=!1),r=s*(o-Number(a))}return r===0?0:r}function cl(n,t,e){z(2,arguments);var i=wr(n,t)/3;return bn(e==null?void 0:e.roundingMethod)(i)}function ul(n,t,e){z(2,arguments);var i=di(n,t)/1e3;return bn(e==null?void 0:e.roundingMethod)(i)}function hl(n,t,e){z(2,arguments);var i=vr(n,t)/7;return bn(e==null?void 0:e.roundingMethod)(i)}function dl(n,t){z(2,arguments);var e=j(n),i=j(t),s=tn(e,i),o=Math.abs(sl(e,i));e.setFullYear(1584),i.setFullYear(1584);var r=tn(e,i)===-s,a=s*(o-Number(r));return a===0?0:a}function fl(n){z(1,arguments);var t=j(n);return t.setSeconds(0,0),t}function gl(n){z(1,arguments);var t=j(n),e=t.getFullYear();return t.setFullYear(e+1,0,0),t.setHours(23,59,59,999),t}function pl(n){z(1,arguments);var t=j(n);return t.setMinutes(59,59,999),t}function ml(n){z(1,arguments);var t=j(n);return t.setSeconds(59,999),t}function bl(n){z(1,arguments);var t=j(n);return t.setMilliseconds(999),t}function yl(n,t){if(n==null)throw new TypeError("assign requires that input parameter not be null or undefined");for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&(n[e]=t[e]);return n}function Is(n,t){(t==null||t>n.length)&&(t=n.length);for(var e=0,i=new Array(t);e=n.length?{done:!0}:{done:!1,value:n[i++]}},e:function(c){throw c},f:s}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. -In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var o=!0,r=!1,a;return{s:function(){e=e.call(n)},n:function(){var c=e.next();return o=c.done,c},e:function(c){r=!0,a=c},f:function(){try{!o&&e.return!=null&&e.return()}finally{if(r)throw a}}}}function D(n){if(n===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return n}function $i(n,t){return $i=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(i,s){return i.__proto__=s,i},$i(n,t)}function B(n,t){if(typeof t!="function"&&t!==null)throw new TypeError("Super expression must either be null or a function");n.prototype=Object.create(t&&t.prototype,{constructor:{value:n,writable:!0,configurable:!0}}),Object.defineProperty(n,"prototype",{writable:!1}),t&&$i(n,t)}function Gn(n){return Gn=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},Gn(n)}function xl(){if(typeof Reflect>"u"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch{return!1}}function vl(n,t){if(t&&(an(t)==="object"||typeof t=="function"))return t;if(t!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return D(n)}function N(n){var t=xl();return function(){var i=Gn(n),s;if(t){var o=Gn(this).constructor;s=Reflect.construct(i,arguments,o)}else s=i.apply(this,arguments);return vl(this,s)}}function I(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}function wl(n,t){if(an(n)!=="object"||n===null)return n;var e=n[Symbol.toPrimitive];if(e!==void 0){var i=e.call(n,t||"default");if(an(i)!=="object")return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(n)}function Mr(n){var t=wl(n,"string");return an(t)==="symbol"?t:String(t)}function zs(n,t){for(var e=0;e0,i=e?t:1-t,s;if(i<=50)s=n||100;else{var o=i+50,r=Math.floor(o/100)*100,a=n>=o%100;s=n+r-(a?100:0)}return e?s:1-s}function Dr(n){return n%400===0||n%4===0&&n%100!==0}var Dl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r0}},{key:"set",value:function(s,o,r){var a=s.getUTCFullYear();if(r.isTwoDigitYear){var l=Pr(r.year,a);return s.setUTCFullYear(l,0,1),s.setUTCHours(0,0,0,0),s}var c=!("era"in o)||o.era===1?r.year:1-r.year;return s.setUTCFullYear(c,0,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Tl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r0}},{key:"set",value:function(s,o,r,a){var l=Aa(s,a);if(r.isTwoDigitYear){var c=Pr(r.year,l);return s.setUTCFullYear(c,0,a.firstWeekContainsDate),s.setUTCHours(0,0,0,0),Ui(s,a)}var u=!("era"in o)||o.era===1?r.year:1-r.year;return s.setUTCFullYear(u,0,a.firstWeekContainsDate),s.setUTCHours(0,0,0,0),Ui(s,a)}}]),e}(Y),Sl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=4}},{key:"set",value:function(s,o,r){return s.setUTCMonth((r-1)*3,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Ll=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=4}},{key:"set",value:function(s,o,r){return s.setUTCMonth((r-1)*3,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Rl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=11}},{key:"set",value:function(s,o,r){return s.setUTCMonth(r,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Fl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=11}},{key:"set",value:function(s,o,r){return s.setUTCMonth(r,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y);function Il(n,t,e){z(2,arguments);var i=j(n),s=wt(t),o=La(i,e)-s;return i.setUTCDate(i.getUTCDate()-o*7),i}var El=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=53}},{key:"set",value:function(s,o,r,a){return Ui(Il(s,r,a),a)}}]),e}(Y);function zl(n,t){z(2,arguments);var e=j(n),i=wt(t),s=Ra(e)-i;return e.setUTCDate(e.getUTCDate()-s*7),e}var Bl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=53}},{key:"set",value:function(s,o,r){return _r(zl(s,r))}}]),e}(Y),Nl=[31,28,31,30,31,30,31,31,30,31,30,31],Wl=[31,29,31,30,31,30,31,31,30,31,30,31],Hl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=Wl[l]:o>=1&&o<=Nl[l]}},{key:"set",value:function(s,o,r){return s.setUTCDate(r),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Vl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=366:o>=1&&o<=365}},{key:"set",value:function(s,o,r){return s.setUTCMonth(0,r),s.setUTCHours(0,0,0,0),s}}]),e}(Y);function us(n,t,e){var i,s,o,r,a,l,c,u;z(2,arguments);var h=xr(),d=wt((i=(s=(o=(r=e==null?void 0:e.weekStartsOn)!==null&&r!==void 0?r:e==null||(a=e.locale)===null||a===void 0||(l=a.options)===null||l===void 0?void 0:l.weekStartsOn)!==null&&o!==void 0?o:h.weekStartsOn)!==null&&s!==void 0?s:(c=h.locale)===null||c===void 0||(u=c.options)===null||u===void 0?void 0:u.weekStartsOn)!==null&&i!==void 0?i:0);if(!(d>=0&&d<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");var f=j(n),g=wt(t),p=f.getUTCDay(),m=g%7,b=(m+7)%7,y=(b=0&&o<=6}},{key:"set",value:function(s,o,r,a){return s=us(s,r,a),s.setUTCHours(0,0,0,0),s}}]),e}(Y),jl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=6}},{key:"set",value:function(s,o,r,a){return s=us(s,r,a),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Ul=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=6}},{key:"set",value:function(s,o,r,a){return s=us(s,r,a),s.setUTCHours(0,0,0,0),s}}]),e}(Y);function $l(n,t){z(2,arguments);var e=wt(t);e%7===0&&(e=e-7);var i=1,s=j(n),o=s.getUTCDay(),r=e%7,a=(r+7)%7,l=(a=1&&o<=7}},{key:"set",value:function(s,o,r){return s=$l(s,r),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Xl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=12}},{key:"set",value:function(s,o,r){var a=s.getUTCHours()>=12;return a&&r<12?s.setUTCHours(r+12,0,0,0):!a&&r===12?s.setUTCHours(0,0,0,0):s.setUTCHours(r,0,0,0),s}}]),e}(Y),Zl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=23}},{key:"set",value:function(s,o,r){return s.setUTCHours(r,0,0,0),s}}]),e}(Y),Jl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=11}},{key:"set",value:function(s,o,r){var a=s.getUTCHours()>=12;return a&&r<12?s.setUTCHours(r+12,0,0,0):s.setUTCHours(r,0,0,0),s}}]),e}(Y),tc=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=24}},{key:"set",value:function(s,o,r){var a=r<=24?r%24:r;return s.setUTCHours(a,0,0,0),s}}]),e}(Y),ec=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=59}},{key:"set",value:function(s,o,r){return s.setUTCMinutes(r,0,0),s}}]),e}(Y),nc=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=59}},{key:"set",value:function(s,o,r){return s.setUTCSeconds(r,0),s}}]),e}(Y),ic=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&A<=7))throw new RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var O=wt((g=(p=(m=(b=i==null?void 0:i.weekStartsOn)!==null&&b!==void 0?b:i==null||(y=i.locale)===null||y===void 0||(x=y.options)===null||x===void 0?void 0:x.weekStartsOn)!==null&&m!==void 0?m:M.weekStartsOn)!==null&&p!==void 0?p:(v=M.locale)===null||v===void 0||(_=v.options)===null||_===void 0?void 0:_.weekStartsOn)!==null&&g!==void 0?g:0);if(!(O>=0&&O<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");if(C==="")return w===""?j(e):new Date(NaN);var R={firstWeekContainsDate:A,weekStartsOn:O,locale:T},st=[new Cl],gt=C.match(uc).map(function(ct){var q=ct[0];if(q in Os){var St=Os[q];return St(ct,T.formatLong)}return ct}).join("").match(cc),W=[],U=Es(gt),Q;try{var Mt=function(){var q=Q.value;!(i!=null&&i.useAdditionalWeekYearTokens)&&Ia(q)&&As(q,C,n),!(i!=null&&i.useAdditionalDayOfYearTokens)&&Ea(q)&&As(q,C,n);var St=q[0],wn=lc[St];if(wn){var Ds=wn.incompatibleTokens;if(Array.isArray(Ds)){var Ts=W.find(function(Ss){return Ds.includes(Ss.token)||Ss.token===St});if(Ts)throw new RangeError("The format string mustn't contain `".concat(Ts.fullToken,"` and `").concat(q,"` at the same time"))}else if(wn.incompatibleTokens==="*"&&W.length>0)throw new RangeError("The format string mustn't contain `".concat(q,"` and any other token at the same time"));W.push({token:St,fullToken:q});var xi=wn.run(w,q,T.match,R);if(!xi)return{v:new Date(NaN)};st.push(xi.setter),w=xi.rest}else{if(St.match(gc))throw new RangeError("Format string contains an unescaped latin alphabet character `"+St+"`");if(q==="''"?q="'":St==="'"&&(q=mc(q)),w.indexOf(q)===0)w=w.slice(q.length);else return{v:new Date(NaN)}}};for(U.s();!(Q=U.n()).done;){var lt=Mt();if(an(lt)==="object")return lt.v}}catch(ct){U.e(ct)}finally{U.f()}if(w.length>0&&fc.test(w))return new Date(NaN);var Kt=st.map(function(ct){return ct.priority}).sort(function(ct,q){return q-ct}).filter(function(ct,q,St){return St.indexOf(ct)===q}).map(function(ct){return st.filter(function(q){return q.priority===ct}).sort(function(q,St){return St.subPriority-q.subPriority})}).map(function(ct){return ct[0]}),Gt=j(e);if(isNaN(Gt.getTime()))return new Date(NaN);var Tt=za(Gt,ji(Gt)),Qt={},Rt=Es(Kt),Zt;try{for(Rt.s();!(Zt=Rt.n()).done;){var Jt=Zt.value;if(!Jt.validate(Tt,R))return new Date(NaN);var vn=Jt.set(Tt,Qt,R);Array.isArray(vn)?(Tt=vn[0],yl(Qt,vn[1])):Tt=vn}}catch(ct){Rt.e(ct)}finally{Rt.f()}return Tt}function mc(n){return n.match(hc)[1].replace(dc,"'")}function bc(n){z(1,arguments);var t=j(n);return t.setMinutes(0,0,0),t}function yc(n){z(1,arguments);var t=j(n);return t.setMilliseconds(0),t}function _c(n,t){var e;z(1,arguments);var i=wt((e=t==null?void 0:t.additionalDigits)!==null&&e!==void 0?e:2);if(i!==2&&i!==1&&i!==0)throw new RangeError("additionalDigits must be 0, 1 or 2");if(!(typeof n=="string"||Object.prototype.toString.call(n)==="[object String]"))return new Date(NaN);var s=Mc(n),o;if(s.date){var r=kc(s.date,i);o=Cc(r.restDateString,r.year)}if(!o||isNaN(o.getTime()))return new Date(NaN);var a=o.getTime(),l=0,c;if(s.time&&(l=Pc(s.time),isNaN(l)))return new Date(NaN);if(s.timezone){if(c=Dc(s.timezone),isNaN(c))return new Date(NaN)}else{var u=new Date(a+l),h=new Date(0);return h.setFullYear(u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()),h.setHours(u.getUTCHours(),u.getUTCMinutes(),u.getUTCSeconds(),u.getUTCMilliseconds()),h}return new Date(a+l+c)}var Mn={dateTimeDelimiter:/[T ]/,timeZoneDelimiter:/[Z ]/i,timezone:/([Z+-].*)$/},xc=/^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/,vc=/^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/,wc=/^([+-])(\d{2})(?::?(\d{2}))?$/;function Mc(n){var t={},e=n.split(Mn.dateTimeDelimiter),i;if(e.length>2)return t;if(/:/.test(e[0])?i=e[0]:(t.date=e[0],i=e[1],Mn.timeZoneDelimiter.test(t.date)&&(t.date=n.split(Mn.timeZoneDelimiter)[0],i=n.substr(t.date.length,n.length))),i){var s=Mn.timezone.exec(i);s?(t.time=i.replace(s[1],""),t.timezone=s[1]):t.time=i}return t}function kc(n,t){var e=new RegExp("^(?:(\\d{4}|[+-]\\d{"+(4+t)+"})|(\\d{2}|[+-]\\d{"+(2+t)+"})$)"),i=n.match(e);if(!i)return{year:NaN,restDateString:""};var s=i[1]?parseInt(i[1]):null,o=i[2]?parseInt(i[2]):null;return{year:o===null?s:o*100,restDateString:n.slice((i[1]||i[2]).length)}}function Cc(n,t){if(t===null)return new Date(NaN);var e=n.match(xc);if(!e)return new Date(NaN);var i=!!e[4],s=Be(e[1]),o=Be(e[2])-1,r=Be(e[3]),a=Be(e[4]),l=Be(e[5])-1;if(i)return Lc(t,a,l)?Tc(t,a,l):new Date(NaN);var c=new Date(0);return!Oc(t,o,r)||!Ac(t,s)?new Date(NaN):(c.setUTCFullYear(t,o,Math.max(s,r)),c)}function Be(n){return n?parseInt(n):1}function Pc(n){var t=n.match(vc);if(!t)return NaN;var e=vi(t[1]),i=vi(t[2]),s=vi(t[3]);return Rc(e,i,s)?e*hi+i*ui+s*1e3:NaN}function vi(n){return n&&parseFloat(n.replace(",","."))||0}function Dc(n){if(n==="Z")return 0;var t=n.match(wc);if(!t)return 0;var e=t[1]==="+"?-1:1,i=parseInt(t[2]),s=t[3]&&parseInt(t[3])||0;return Fc(i,s)?e*(i*hi+s*ui):NaN}function Tc(n,t,e){var i=new Date(0);i.setUTCFullYear(n,0,4);var s=i.getUTCDay()||7,o=(t-1)*7+e+1-s;return i.setUTCDate(i.getUTCDate()+o),i}var Sc=[31,null,31,30,31,30,31,31,30,31,30,31];function Tr(n){return n%400===0||n%4===0&&n%100!==0}function Oc(n,t,e){return t>=0&&t<=11&&e>=1&&e<=(Sc[t]||(Tr(n)?29:28))}function Ac(n,t){return t>=1&&t<=(Tr(n)?366:365)}function Lc(n,t,e){return t>=1&&t<=53&&e>=0&&e<=6}function Rc(n,t,e){return n===24?t===0&&e===0:e>=0&&e<60&&t>=0&&t<60&&n>=0&&n<25}function Fc(n,t){return t>=0&&t<=59}class Ic{get(t,e,i){return vt.get("/api/v2/summary/basic",{params:{start:t,end:e,code:i}})}}function Nt(n,t,e){const i=J(t,"y-MM-dd")+"_"+J(e,"y-MM-dd")+"_"+n;return console.log("getCacheKey: "+i),String(i)}let wi=!1;const Ec=()=>({balanceBox:{amounts:[],subtitles:[]},billBox:{paid:[],unpaid:[]},leftBox:{left:[],perDay:[]},netBox:{net:[]},autoConversion:!1,loading:!1,boxData:null,boxOptions:null,getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Nt("dashboard-boxes-data",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){this.boxData=s,this.generateOptions(this.boxData);return}new Ic().get(J(n,"yyyy-MM-dd"),J(t,"yyyy-MM-dd"),null).then(r=>{this.boxData=r.data,window.store.set(e,r.data),this.generateOptions(this.boxData)})},generateOptions(n){this.balanceBox={amounts:[],subtitles:[]},this.billBox={paid:[],unpaid:[]},this.leftBox={left:[],perDay:[]},this.netBox={net:[]};let t={};for(const e in n)if(n.hasOwnProperty(e)){const i=n[e];if(!i.hasOwnProperty("key"))continue;let s=i.key;if(this.autoConversion){if(s.startsWith("balance-in-native")){this.balanceBox.amounts.push(V(i.value,i.currency_code)),t.hasOwnProperty(i.currency_code)||(t[i.currency_code]="");continue}if(s.startsWith("spent-in-native")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=t[i.currency_code]+V(i.value,i.currency_code);continue}if(s.startsWith("earned-in-native")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=V(i.value,i.currency_code)+" + "+t[i.currency_code];continue}if(s.startsWith("bills-unpaid-in-native")){this.billBox.unpaid.push(V(i.value,i.currency_code));continue}if(s.startsWith("bills-paid-in-native")){this.billBox.paid.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-to-spend-in-native")){this.leftBox.left.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-per-day-to-spend-in-native")){this.leftBox.perDay.push(V(i.value,i.currency_code));continue}if(s.startsWith("net-worth-in-native")){this.netBox.net.push(V(i.value,i.currency_code));continue}}if(!this.autoConversion&&!s.endsWith("native")){if(s.startsWith("balance-in-")){this.balanceBox.amounts.push(V(i.value,i.currency_code));continue}if(s.startsWith("spent-in-")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=t[i.currency_code]+V(i.value,i.currency_code);continue}if(s.startsWith("earned-in-")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=V(i.value,i.currency_code)+" + "+t[i.currency_code];continue}if(s.startsWith("bills-unpaid-in-")){this.billBox.unpaid.push(V(i.value,i.currency_code));continue}if(s.startsWith("bills-paid-in-")){this.billBox.paid.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-to-spend-in-")){this.leftBox.left.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-per-day-to-spend-in-")){this.leftBox.perDay.push(V(i.value,i.currency_code));continue}s.startsWith("net-worth-in-")&&this.netBox.net.push(V(i.value,i.currency_code))}}for(let e in t)t.hasOwnProperty(e)&&this.balanceBox.subtitles.push(t[e]);this.loading=!1},loadBoxes(){if(this.loading!==!0){if(this.loading=!0,this.boxData===null){this.getFreshData();return}this.generateOptions(this.boxData),this.loading=!1}},init(){Promise.all([bt("viewRange"),bt("autoConversion",!1)]).then(n=>{wi=!0,this.autoConversion=n[1],this.loadBoxes()}),window.store.observe("end",()=>{wi&&(this.boxData=null,this.loadBoxes())}),window.store.observe("autoConversion",n=>{wi&&(this.autoConversion=n,this.loadBoxes())})}});class zc{put(t,e){let i="/api/v1/preferences/"+t;return vt.put(i,{data:e})}}function Bc(n,t=null){window.store.set(n,t),new zc().put(n,t).then(i=>{}).catch(()=>{new Ba().post(n,t).then(s=>{})})}let Nc=class{dashboard(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/account/dashboard",{params:{start:i,end:s}})}expense(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/account/expense-dashboard",{params:{start:i,end:s}})}},Bs=class{get(t,e){let i={date:J(e,"y-MM-dd").slice(0,10)};return e?vt.get("/api/v2/accounts/"+t,{params:i}):vt.get("/api/v2/accounts/"+t)}transactions(t,e){const i={page:e.page??1};return e.hasOwnProperty("start")&&(i.start=J(e.start,"y-MM-dd")),e.hasOwnProperty("end")&&(i.end=J(e.end,"y-MM-dd")),vt.get("/api/v2/accounts/"+t+"/transactions",{params:i})}};/*! +var Sa=Object.defineProperty;var Oa=(n,t,e)=>t in n?Sa(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var k=(n,t,e)=>(Oa(n,typeof t!="symbol"?t+"":t,e),e);import{r as z,a as ai,t as wt,s as Yi,g as ji,b as ls,c as br,d as j,e as yr,f as _r,_ as an,h as Aa,i as Ui,j as xr,k as La,l as Ra,m as vr,n as Fa,o as As,p as Ia,q as Ls,u as Ea,v as za,w as vt,x as J,y as V,z as bt,P as Ba,I as li,A as ci,B as Na,C as Wa,D as Ha,E as Va,F as Rs,G as Ya,H as ja,J as Ua}from"./load-translations-85b093de.js";var $a=36e5;function qa(n,t){z(2,arguments);var e=wt(t);return ai(n,e*$a)}var Xa=864e5;function Ka(n,t){z(2,arguments);var e=Yi(n),i=Yi(t),s=e.getTime()-ji(e),o=i.getTime()-ji(i);return Math.round((s-o)/Xa)}var Ga=6e4;function Qa(n,t){z(2,arguments);var e=wt(t);return ai(n,e*Ga)}function Za(n,t){z(2,arguments);var e=wt(t),i=e*3;return ls(n,i)}function Ja(n,t){z(2,arguments);var e=wt(t);return ai(n,e*1e3)}function tl(n,t){z(2,arguments);var e=wt(t),i=e*7;return br(n,i)}function el(n,t){z(2,arguments);var e=wt(t);return ls(n,e*12)}function tn(n,t){z(2,arguments);var e=j(n),i=j(t),s=e.getTime()-i.getTime();return s<0?-1:s>0?1:s}var ui=6e4,hi=36e5,nl=1e3;function il(n,t){z(2,arguments);var e=j(n),i=j(t),s=e.getFullYear()-i.getFullYear(),o=e.getMonth()-i.getMonth();return s*12+o}function sl(n,t){z(2,arguments);var e=j(n),i=j(t);return e.getFullYear()-i.getFullYear()}function Fs(n,t){var e=n.getFullYear()-t.getFullYear()||n.getMonth()-t.getMonth()||n.getDate()-t.getDate()||n.getHours()-t.getHours()||n.getMinutes()-t.getMinutes()||n.getSeconds()-t.getSeconds()||n.getMilliseconds()-t.getMilliseconds();return e<0?-1:e>0?1:e}function wr(n,t){z(2,arguments);var e=j(n),i=j(t),s=Fs(e,i),o=Math.abs(Ka(e,i));e.setDate(e.getDate()-s*o);var r=+(Fs(e,i)===-s),a=s*(o-r);return a===0?0:a}function di(n,t){return z(2,arguments),j(n).getTime()-j(t).getTime()}var Is={ceil:Math.ceil,round:Math.round,floor:Math.floor,trunc:function(t){return t<0?Math.ceil(t):Math.floor(t)}},ol="trunc";function bn(n){return n?Is[n]:Is[ol]}function rl(n,t,e){z(2,arguments);var i=di(n,t)/hi;return bn(e==null?void 0:e.roundingMethod)(i)}function al(n,t,e){z(2,arguments);var i=di(n,t)/ui;return bn(e==null?void 0:e.roundingMethod)(i)}function ll(n){z(1,arguments);var t=j(n);return yr(t).getTime()===_r(t).getTime()}function Mr(n,t){z(2,arguments);var e=j(n),i=j(t),s=tn(e,i),o=Math.abs(il(e,i)),r;if(o<1)r=0;else{e.getMonth()===1&&e.getDate()>27&&e.setDate(30),e.setMonth(e.getMonth()-s*o);var a=tn(e,i)===-s;ll(j(n))&&o===1&&tn(n,i)===1&&(a=!1),r=s*(o-Number(a))}return r===0?0:r}function cl(n,t,e){z(2,arguments);var i=Mr(n,t)/3;return bn(e==null?void 0:e.roundingMethod)(i)}function ul(n,t,e){z(2,arguments);var i=di(n,t)/1e3;return bn(e==null?void 0:e.roundingMethod)(i)}function hl(n,t,e){z(2,arguments);var i=wr(n,t)/7;return bn(e==null?void 0:e.roundingMethod)(i)}function dl(n,t){z(2,arguments);var e=j(n),i=j(t),s=tn(e,i),o=Math.abs(sl(e,i));e.setFullYear(1584),i.setFullYear(1584);var r=tn(e,i)===-s,a=s*(o-Number(r));return a===0?0:a}function fl(n){z(1,arguments);var t=j(n);return t.setSeconds(0,0),t}function gl(n){z(1,arguments);var t=j(n),e=t.getFullYear();return t.setFullYear(e+1,0,0),t.setHours(23,59,59,999),t}function pl(n){z(1,arguments);var t=j(n);return t.setMinutes(59,59,999),t}function ml(n){z(1,arguments);var t=j(n);return t.setSeconds(59,999),t}function bl(n){z(1,arguments);var t=j(n);return t.setMilliseconds(999),t}function yl(n,t){if(n==null)throw new TypeError("assign requires that input parameter not be null or undefined");for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&(n[e]=t[e]);return n}function Es(n,t){(t==null||t>n.length)&&(t=n.length);for(var e=0,i=new Array(t);e=n.length?{done:!0}:{done:!1,value:n[i++]}},e:function(c){throw c},f:s}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var o=!0,r=!1,a;return{s:function(){e=e.call(n)},n:function(){var c=e.next();return o=c.done,c},e:function(c){r=!0,a=c},f:function(){try{!o&&e.return!=null&&e.return()}finally{if(r)throw a}}}}function D(n){if(n===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return n}function $i(n,t){return $i=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(i,s){return i.__proto__=s,i},$i(n,t)}function B(n,t){if(typeof t!="function"&&t!==null)throw new TypeError("Super expression must either be null or a function");n.prototype=Object.create(t&&t.prototype,{constructor:{value:n,writable:!0,configurable:!0}}),Object.defineProperty(n,"prototype",{writable:!1}),t&&$i(n,t)}function Gn(n){return Gn=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},Gn(n)}function xl(){if(typeof Reflect>"u"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch{return!1}}function vl(n,t){if(t&&(an(t)==="object"||typeof t=="function"))return t;if(t!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return D(n)}function N(n){var t=xl();return function(){var i=Gn(n),s;if(t){var o=Gn(this).constructor;s=Reflect.construct(i,arguments,o)}else s=i.apply(this,arguments);return vl(this,s)}}function I(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}function wl(n,t){if(an(n)!=="object"||n===null)return n;var e=n[Symbol.toPrimitive];if(e!==void 0){var i=e.call(n,t||"default");if(an(i)!=="object")return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(n)}function kr(n){var t=wl(n,"string");return an(t)==="symbol"?t:String(t)}function Bs(n,t){for(var e=0;e0,i=e?t:1-t,s;if(i<=50)s=n||100;else{var o=i+50,r=Math.floor(o/100)*100,a=n>=o%100;s=n+r-(a?100:0)}return e?s:1-s}function Tr(n){return n%400===0||n%4===0&&n%100!==0}var Dl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r0}},{key:"set",value:function(s,o,r){var a=s.getUTCFullYear();if(r.isTwoDigitYear){var l=Dr(r.year,a);return s.setUTCFullYear(l,0,1),s.setUTCHours(0,0,0,0),s}var c=!("era"in o)||o.era===1?r.year:1-r.year;return s.setUTCFullYear(c,0,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Tl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r0}},{key:"set",value:function(s,o,r,a){var l=Aa(s,a);if(r.isTwoDigitYear){var c=Dr(r.year,l);return s.setUTCFullYear(c,0,a.firstWeekContainsDate),s.setUTCHours(0,0,0,0),Ui(s,a)}var u=!("era"in o)||o.era===1?r.year:1-r.year;return s.setUTCFullYear(u,0,a.firstWeekContainsDate),s.setUTCHours(0,0,0,0),Ui(s,a)}}]),e}(Y),Sl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=4}},{key:"set",value:function(s,o,r){return s.setUTCMonth((r-1)*3,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Ll=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=4}},{key:"set",value:function(s,o,r){return s.setUTCMonth((r-1)*3,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Rl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=11}},{key:"set",value:function(s,o,r){return s.setUTCMonth(r,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Fl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=11}},{key:"set",value:function(s,o,r){return s.setUTCMonth(r,1),s.setUTCHours(0,0,0,0),s}}]),e}(Y);function Il(n,t,e){z(2,arguments);var i=j(n),s=wt(t),o=La(i,e)-s;return i.setUTCDate(i.getUTCDate()-o*7),i}var El=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=53}},{key:"set",value:function(s,o,r,a){return Ui(Il(s,r,a),a)}}]),e}(Y);function zl(n,t){z(2,arguments);var e=j(n),i=wt(t),s=Ra(e)-i;return e.setUTCDate(e.getUTCDate()-s*7),e}var Bl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=53}},{key:"set",value:function(s,o,r){return xr(zl(s,r))}}]),e}(Y),Nl=[31,28,31,30,31,30,31,31,30,31,30,31],Wl=[31,29,31,30,31,30,31,31,30,31,30,31],Hl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=Wl[l]:o>=1&&o<=Nl[l]}},{key:"set",value:function(s,o,r){return s.setUTCDate(r),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Vl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=366:o>=1&&o<=365}},{key:"set",value:function(s,o,r){return s.setUTCMonth(0,r),s.setUTCHours(0,0,0,0),s}}]),e}(Y);function us(n,t,e){var i,s,o,r,a,l,c,u;z(2,arguments);var h=vr(),d=wt((i=(s=(o=(r=e==null?void 0:e.weekStartsOn)!==null&&r!==void 0?r:e==null||(a=e.locale)===null||a===void 0||(l=a.options)===null||l===void 0?void 0:l.weekStartsOn)!==null&&o!==void 0?o:h.weekStartsOn)!==null&&s!==void 0?s:(c=h.locale)===null||c===void 0||(u=c.options)===null||u===void 0?void 0:u.weekStartsOn)!==null&&i!==void 0?i:0);if(!(d>=0&&d<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");var f=j(n),g=wt(t),p=f.getUTCDay(),m=g%7,b=(m+7)%7,y=(b=0&&o<=6}},{key:"set",value:function(s,o,r,a){return s=us(s,r,a),s.setUTCHours(0,0,0,0),s}}]),e}(Y),jl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=6}},{key:"set",value:function(s,o,r,a){return s=us(s,r,a),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Ul=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=6}},{key:"set",value:function(s,o,r,a){return s=us(s,r,a),s.setUTCHours(0,0,0,0),s}}]),e}(Y);function $l(n,t){z(2,arguments);var e=wt(t);e%7===0&&(e=e-7);var i=1,s=j(n),o=s.getUTCDay(),r=e%7,a=(r+7)%7,l=(a=1&&o<=7}},{key:"set",value:function(s,o,r){return s=$l(s,r),s.setUTCHours(0,0,0,0),s}}]),e}(Y),Xl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=12}},{key:"set",value:function(s,o,r){var a=s.getUTCHours()>=12;return a&&r<12?s.setUTCHours(r+12,0,0,0):!a&&r===12?s.setUTCHours(0,0,0,0):s.setUTCHours(r,0,0,0),s}}]),e}(Y),Zl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=23}},{key:"set",value:function(s,o,r){return s.setUTCHours(r,0,0,0),s}}]),e}(Y),Jl=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=11}},{key:"set",value:function(s,o,r){var a=s.getUTCHours()>=12;return a&&r<12?s.setUTCHours(r+12,0,0,0):s.setUTCHours(r,0,0,0),s}}]),e}(Y),tc=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&o<=24}},{key:"set",value:function(s,o,r){var a=r<=24?r%24:r;return s.setUTCHours(a,0,0,0),s}}]),e}(Y),ec=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=59}},{key:"set",value:function(s,o,r){return s.setUTCMinutes(r,0,0),s}}]),e}(Y),nc=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=0&&o<=59}},{key:"set",value:function(s,o,r){return s.setUTCSeconds(r,0),s}}]),e}(Y),ic=function(n){B(e,n);var t=N(e);function e(){var i;I(this,e);for(var s=arguments.length,o=new Array(s),r=0;r=1&&A<=7))throw new RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var O=wt((g=(p=(m=(b=i==null?void 0:i.weekStartsOn)!==null&&b!==void 0?b:i==null||(y=i.locale)===null||y===void 0||(x=y.options)===null||x===void 0?void 0:x.weekStartsOn)!==null&&m!==void 0?m:M.weekStartsOn)!==null&&p!==void 0?p:(v=M.locale)===null||v===void 0||(_=v.options)===null||_===void 0?void 0:_.weekStartsOn)!==null&&g!==void 0?g:0);if(!(O>=0&&O<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");if(C==="")return w===""?j(e):new Date(NaN);var R={firstWeekContainsDate:A,weekStartsOn:O,locale:T},st=[new Cl],gt=C.match(uc).map(function(ct){var q=ct[0];if(q in As){var Ot=As[q];return Ot(ct,T.formatLong)}return ct}).join("").match(cc),W=[],U=zs(gt),Q;try{var Mt=function(){var q=Q.value;!(i!=null&&i.useAdditionalWeekYearTokens)&&Ia(q)&&Ls(q,C,n),!(i!=null&&i.useAdditionalDayOfYearTokens)&&Ea(q)&&Ls(q,C,n);var Ot=q[0],wn=lc[Ot];if(wn){var Ts=wn.incompatibleTokens;if(Array.isArray(Ts)){var Ss=W.find(function(Os){return Ts.includes(Os.token)||Os.token===Ot});if(Ss)throw new RangeError("The format string mustn't contain `".concat(Ss.fullToken,"` and `").concat(q,"` at the same time"))}else if(wn.incompatibleTokens==="*"&&W.length>0)throw new RangeError("The format string mustn't contain `".concat(q,"` and any other token at the same time"));W.push({token:Ot,fullToken:q});var xi=wn.run(w,q,T.match,R);if(!xi)return{v:new Date(NaN)};st.push(xi.setter),w=xi.rest}else{if(Ot.match(gc))throw new RangeError("Format string contains an unescaped latin alphabet character `"+Ot+"`");if(q==="''"?q="'":Ot==="'"&&(q=mc(q)),w.indexOf(q)===0)w=w.slice(q.length);else return{v:new Date(NaN)}}};for(U.s();!(Q=U.n()).done;){var lt=Mt();if(an(lt)==="object")return lt.v}}catch(ct){U.e(ct)}finally{U.f()}if(w.length>0&&fc.test(w))return new Date(NaN);var Gt=st.map(function(ct){return ct.priority}).sort(function(ct,q){return q-ct}).filter(function(ct,q,Ot){return Ot.indexOf(ct)===q}).map(function(ct){return st.filter(function(q){return q.priority===ct}).sort(function(q,Ot){return Ot.subPriority-q.subPriority})}).map(function(ct){return ct[0]}),Qt=j(e);if(isNaN(Qt.getTime()))return new Date(NaN);var Tt=za(Qt,ji(Qt)),Zt={},St=zs(Gt),Jt;try{for(St.s();!(Jt=St.n()).done;){var Ft=Jt.value;if(!Ft.validate(Tt,R))return new Date(NaN);var vn=Ft.set(Tt,Zt,R);Array.isArray(vn)?(Tt=vn[0],yl(Zt,vn[1])):Tt=vn}}catch(ct){St.e(ct)}finally{St.f()}return Tt}function mc(n){return n.match(hc)[1].replace(dc,"'")}function bc(n){z(1,arguments);var t=j(n);return t.setMinutes(0,0,0),t}function yc(n){z(1,arguments);var t=j(n);return t.setMilliseconds(0),t}function _c(n,t){var e;z(1,arguments);var i=wt((e=t==null?void 0:t.additionalDigits)!==null&&e!==void 0?e:2);if(i!==2&&i!==1&&i!==0)throw new RangeError("additionalDigits must be 0, 1 or 2");if(!(typeof n=="string"||Object.prototype.toString.call(n)==="[object String]"))return new Date(NaN);var s=Mc(n),o;if(s.date){var r=kc(s.date,i);o=Cc(r.restDateString,r.year)}if(!o||isNaN(o.getTime()))return new Date(NaN);var a=o.getTime(),l=0,c;if(s.time&&(l=Pc(s.time),isNaN(l)))return new Date(NaN);if(s.timezone){if(c=Dc(s.timezone),isNaN(c))return new Date(NaN)}else{var u=new Date(a+l),h=new Date(0);return h.setFullYear(u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()),h.setHours(u.getUTCHours(),u.getUTCMinutes(),u.getUTCSeconds(),u.getUTCMilliseconds()),h}return new Date(a+l+c)}var Mn={dateTimeDelimiter:/[T ]/,timeZoneDelimiter:/[Z ]/i,timezone:/([Z+-].*)$/},xc=/^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/,vc=/^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/,wc=/^([+-])(\d{2})(?::?(\d{2}))?$/;function Mc(n){var t={},e=n.split(Mn.dateTimeDelimiter),i;if(e.length>2)return t;if(/:/.test(e[0])?i=e[0]:(t.date=e[0],i=e[1],Mn.timeZoneDelimiter.test(t.date)&&(t.date=n.split(Mn.timeZoneDelimiter)[0],i=n.substr(t.date.length,n.length))),i){var s=Mn.timezone.exec(i);s?(t.time=i.replace(s[1],""),t.timezone=s[1]):t.time=i}return t}function kc(n,t){var e=new RegExp("^(?:(\\d{4}|[+-]\\d{"+(4+t)+"})|(\\d{2}|[+-]\\d{"+(2+t)+"})$)"),i=n.match(e);if(!i)return{year:NaN,restDateString:""};var s=i[1]?parseInt(i[1]):null,o=i[2]?parseInt(i[2]):null;return{year:o===null?s:o*100,restDateString:n.slice((i[1]||i[2]).length)}}function Cc(n,t){if(t===null)return new Date(NaN);var e=n.match(xc);if(!e)return new Date(NaN);var i=!!e[4],s=Be(e[1]),o=Be(e[2])-1,r=Be(e[3]),a=Be(e[4]),l=Be(e[5])-1;if(i)return Lc(t,a,l)?Tc(t,a,l):new Date(NaN);var c=new Date(0);return!Oc(t,o,r)||!Ac(t,s)?new Date(NaN):(c.setUTCFullYear(t,o,Math.max(s,r)),c)}function Be(n){return n?parseInt(n):1}function Pc(n){var t=n.match(vc);if(!t)return NaN;var e=vi(t[1]),i=vi(t[2]),s=vi(t[3]);return Rc(e,i,s)?e*hi+i*ui+s*1e3:NaN}function vi(n){return n&&parseFloat(n.replace(",","."))||0}function Dc(n){if(n==="Z")return 0;var t=n.match(wc);if(!t)return 0;var e=t[1]==="+"?-1:1,i=parseInt(t[2]),s=t[3]&&parseInt(t[3])||0;return Fc(i,s)?e*(i*hi+s*ui):NaN}function Tc(n,t,e){var i=new Date(0);i.setUTCFullYear(n,0,4);var s=i.getUTCDay()||7,o=(t-1)*7+e+1-s;return i.setUTCDate(i.getUTCDate()+o),i}var Sc=[31,null,31,30,31,30,31,31,30,31,30,31];function Sr(n){return n%400===0||n%4===0&&n%100!==0}function Oc(n,t,e){return t>=0&&t<=11&&e>=1&&e<=(Sc[t]||(Sr(n)?29:28))}function Ac(n,t){return t>=1&&t<=(Sr(n)?366:365)}function Lc(n,t,e){return t>=1&&t<=53&&e>=0&&e<=6}function Rc(n,t,e){return n===24?t===0&&e===0:e>=0&&e<60&&t>=0&&t<60&&n>=0&&n<25}function Fc(n,t){return t>=0&&t<=59}class Ic{get(t,e,i){return vt.get("/api/v2/summary/basic",{params:{start:t,end:e,code:i}})}}function Wt(n,t,e){const i=J(t,"y-MM-dd")+"_"+J(e,"y-MM-dd")+"_"+n;return console.log("getCacheKey: "+i),String(i)}let wi=!1;const Ec=()=>({balanceBox:{amounts:[],subtitles:[]},billBox:{paid:[],unpaid:[]},leftBox:{left:[],perDay:[]},netBox:{net:[]},autoConversion:!1,loading:!1,boxData:null,boxOptions:null,getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-boxes-data",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){this.boxData=s,this.generateOptions(this.boxData);return}new Ic().get(J(n,"yyyy-MM-dd"),J(t,"yyyy-MM-dd"),null).then(r=>{this.boxData=r.data,window.store.set(e,r.data),this.generateOptions(this.boxData)})},generateOptions(n){this.balanceBox={amounts:[],subtitles:[]},this.billBox={paid:[],unpaid:[]},this.leftBox={left:[],perDay:[]},this.netBox={net:[]};let t={};for(const e in n)if(n.hasOwnProperty(e)){const i=n[e];if(!i.hasOwnProperty("key"))continue;let s=i.key;if(this.autoConversion){if(s.startsWith("balance-in-native")){this.balanceBox.amounts.push(V(i.value,i.currency_code)),t.hasOwnProperty(i.currency_code)||(t[i.currency_code]="");continue}if(s.startsWith("spent-in-native")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=t[i.currency_code]+V(i.value,i.currency_code);continue}if(s.startsWith("earned-in-native")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=V(i.value,i.currency_code)+" + "+t[i.currency_code];continue}if(s.startsWith("bills-unpaid-in-native")){this.billBox.unpaid.push(V(i.value,i.currency_code));continue}if(s.startsWith("bills-paid-in-native")){this.billBox.paid.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-to-spend-in-native")){this.leftBox.left.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-per-day-to-spend-in-native")){this.leftBox.perDay.push(V(i.value,i.currency_code));continue}if(s.startsWith("net-worth-in-native")){this.netBox.net.push(V(i.value,i.currency_code));continue}}if(!this.autoConversion&&!s.endsWith("native")){if(s.startsWith("balance-in-")){this.balanceBox.amounts.push(V(i.value,i.currency_code));continue}if(s.startsWith("spent-in-")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=t[i.currency_code]+V(i.value,i.currency_code);continue}if(s.startsWith("earned-in-")){t.hasOwnProperty(i.currency_code)||(t[i.currency_code]=""),t[i.currency_code]=V(i.value,i.currency_code)+" + "+t[i.currency_code];continue}if(s.startsWith("bills-unpaid-in-")){this.billBox.unpaid.push(V(i.value,i.currency_code));continue}if(s.startsWith("bills-paid-in-")){this.billBox.paid.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-to-spend-in-")){this.leftBox.left.push(V(i.value,i.currency_code));continue}if(s.startsWith("left-per-day-to-spend-in-")){this.leftBox.perDay.push(V(i.value,i.currency_code));continue}s.startsWith("net-worth-in-")&&this.netBox.net.push(V(i.value,i.currency_code))}}for(let e in t)t.hasOwnProperty(e)&&this.balanceBox.subtitles.push(t[e]);this.loading=!1},loadBoxes(){if(this.loading!==!0){if(this.loading=!0,this.boxData===null){this.getFreshData();return}this.generateOptions(this.boxData),this.loading=!1}},init(){Promise.all([bt("viewRange"),bt("autoConversion",!1)]).then(n=>{wi=!0,this.autoConversion=n[1],this.loadBoxes()}),window.store.observe("end",()=>{wi&&(this.boxData=null,this.loadBoxes())}),window.store.observe("autoConversion",n=>{wi&&(this.autoConversion=n,this.loadBoxes())})}});class zc{put(t,e){let i="/api/v1/preferences/"+t;return vt.put(i,{data:e})}}function Bc(n,t=null){window.store.set(n,t),new zc().put(n,t).then(i=>{}).catch(()=>{new Ba().post(n,t).then(s=>{})})}let Nc=class{dashboard(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/account/dashboard",{params:{start:i,end:s}})}expense(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/account/expense-dashboard",{params:{start:i,end:s}})}},Ns=class{get(t,e){let i={date:J(e,"y-MM-dd").slice(0,10)};return e?vt.get("/api/v2/accounts/"+t,{params:i}):vt.get("/api/v2/accounts/"+t)}transactions(t,e){const i={page:e.page??1};return e.hasOwnProperty("start")&&(i.start=J(e.start,"y-MM-dd")),e.hasOwnProperty("end")&&(i.end=J(e.end,"y-MM-dd")),vt.get("/api/v2/accounts/"+t+"/transactions",{params:i})}};/*! * @kurkle/color v0.3.2 * https://github.com/kurkle/color#readme * (c) 2023 Jukka Kurkela * Released under the MIT License - */function yn(n){return n+.5|0}const ne=(n,t,e)=>Math.max(Math.min(n,e),t);function Ge(n){return ne(yn(n*2.55),0,255)}function le(n){return ne(yn(n*255),0,255)}function Ut(n){return ne(yn(n/2.55)/100,0,1)}function Ns(n){return ne(yn(n*100),0,100)}const Ot={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},qi=[..."0123456789ABCDEF"],Wc=n=>qi[n&15],Hc=n=>qi[(n&240)>>4]+qi[n&15],kn=n=>(n&240)>>4===(n&15),Vc=n=>kn(n.r)&&kn(n.g)&&kn(n.b)&&kn(n.a);function Yc(n){var t=n.length,e;return n[0]==="#"&&(t===4||t===5?e={r:255&Ot[n[1]]*17,g:255&Ot[n[2]]*17,b:255&Ot[n[3]]*17,a:t===5?Ot[n[4]]*17:255}:(t===7||t===9)&&(e={r:Ot[n[1]]<<4|Ot[n[2]],g:Ot[n[3]]<<4|Ot[n[4]],b:Ot[n[5]]<<4|Ot[n[6]],a:t===9?Ot[n[7]]<<4|Ot[n[8]]:255})),e}const jc=(n,t)=>n<255?t(n):"";function Uc(n){var t=Vc(n)?Wc:Hc;return n?"#"+t(n.r)+t(n.g)+t(n.b)+jc(n.a,t):void 0}const $c=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Sr(n,t,e){const i=t*Math.min(e,1-e),s=(o,r=(o+n/30)%12)=>e-i*Math.max(Math.min(r-3,9-r,1),-1);return[s(0),s(8),s(4)]}function qc(n,t,e){const i=(s,o=(s+n/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[i(5),i(3),i(1)]}function Xc(n,t,e){const i=Sr(n,1,.5);let s;for(t+e>1&&(s=1/(t+e),t*=s,e*=s),s=0;s<3;s++)i[s]*=1-t-e,i[s]+=t;return i}function Kc(n,t,e,i,s){return n===s?(t-e)/i+(t.5?u/(2-o-r):u/(o+r),l=Kc(e,i,s,u,o),l=l*60+.5),[l|0,c||0,a]}function ds(n,t,e,i){return(Array.isArray(t)?n(t[0],t[1],t[2]):n(t,e,i)).map(le)}function fs(n,t,e){return ds(Sr,n,t,e)}function Gc(n,t,e){return ds(Xc,n,t,e)}function Qc(n,t,e){return ds(qc,n,t,e)}function Or(n){return(n%360+360)%360}function Zc(n){const t=$c.exec(n);let e=255,i;if(!t)return;t[5]!==i&&(e=t[6]?Ge(+t[5]):le(+t[5]));const s=Or(+t[2]),o=+t[3]/100,r=+t[4]/100;return t[1]==="hwb"?i=Gc(s,o,r):t[1]==="hsv"?i=Qc(s,o,r):i=fs(s,o,r),{r:i[0],g:i[1],b:i[2],a:e}}function Jc(n,t){var e=hs(n);e[0]=Or(e[0]+t),e=fs(e),n.r=e[0],n.g=e[1],n.b=e[2]}function tu(n){if(!n)return;const t=hs(n),e=t[0],i=Ns(t[1]),s=Ns(t[2]);return n.a<255?`hsla(${e}, ${i}%, ${s}%, ${Ut(n.a)})`:`hsl(${e}, ${i}%, ${s}%)`}const Ws={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Hs={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function eu(){const n={},t=Object.keys(Hs),e=Object.keys(Ws);let i,s,o,r,a;for(i=0;i>16&255,o>>8&255,o&255]}return n}let Cn;function nu(n){Cn||(Cn=eu(),Cn.transparent=[0,0,0,0]);const t=Cn[n.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const iu=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function su(n){const t=iu.exec(n);let e=255,i,s,o;if(t){if(t[7]!==i){const r=+t[7];e=t[8]?Ge(r):ne(r*255,0,255)}return i=+t[1],s=+t[3],o=+t[5],i=255&(t[2]?Ge(i):ne(i,0,255)),s=255&(t[4]?Ge(s):ne(s,0,255)),o=255&(t[6]?Ge(o):ne(o,0,255)),{r:i,g:s,b:o,a:e}}}function ou(n){return n&&(n.a<255?`rgba(${n.r}, ${n.g}, ${n.b}, ${Ut(n.a)})`:`rgb(${n.r}, ${n.g}, ${n.b})`)}const Mi=n=>n<=.0031308?n*12.92:Math.pow(n,1/2.4)*1.055-.055,Ce=n=>n<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4);function ru(n,t,e){const i=Ce(Ut(n.r)),s=Ce(Ut(n.g)),o=Ce(Ut(n.b));return{r:le(Mi(i+e*(Ce(Ut(t.r))-i))),g:le(Mi(s+e*(Ce(Ut(t.g))-s))),b:le(Mi(o+e*(Ce(Ut(t.b))-o))),a:n.a+e*(t.a-n.a)}}function Pn(n,t,e){if(n){let i=hs(n);i[t]=Math.max(0,Math.min(i[t]+i[t]*e,t===0?360:1)),i=fs(i),n.r=i[0],n.g=i[1],n.b=i[2]}}function Ar(n,t){return n&&Object.assign(t||{},n)}function Vs(n){var t={r:0,g:0,b:0,a:255};return Array.isArray(n)?n.length>=3&&(t={r:n[0],g:n[1],b:n[2],a:255},n.length>3&&(t.a=le(n[3]))):(t=Ar(n,{r:0,g:0,b:0,a:1}),t.a=le(t.a)),t}function au(n){return n.charAt(0)==="r"?su(n):Zc(n)}class dt{constructor(t){if(t instanceof dt)return t;const e=typeof t;let i;e==="object"?i=Vs(t):e==="string"&&(i=Yc(t)||nu(t)||au(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=Ar(this._rgb);return t&&(t.a=Ut(t.a)),t}set rgb(t){this._rgb=Vs(t)}rgbString(){return this._valid?ou(this._rgb):void 0}hexString(){return this._valid?Uc(this._rgb):void 0}hslString(){return this._valid?tu(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let o;const r=e===o?.5:e,a=2*r-1,l=i.a-s.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,i.r=255&c*i.r+o*s.r+.5,i.g=255&c*i.g+o*s.g+.5,i.b=255&c*i.b+o*s.b+.5,i.a=r*i.a+(1-r)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=ru(this._rgb,t._rgb,e)),this}clone(){return new dt(this.rgb)}alpha(t){return this._rgb.a=le(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=yn(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Pn(this._rgb,2,t),this}darken(t){return Pn(this._rgb,2,-t),this}saturate(t){return Pn(this._rgb,1,t),this}desaturate(t){return Pn(this._rgb,1,-t),this}rotate(t){return Jc(this._rgb,t),this}}/*! - * Chart.js v4.4.0 + */function yn(n){return n+.5|0}const ne=(n,t,e)=>Math.max(Math.min(n,e),t);function Ge(n){return ne(yn(n*2.55),0,255)}function le(n){return ne(yn(n*255),0,255)}function $t(n){return ne(yn(n/2.55)/100,0,1)}function Ws(n){return ne(yn(n*100),0,100)}const At={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},qi=[..."0123456789ABCDEF"],Wc=n=>qi[n&15],Hc=n=>qi[(n&240)>>4]+qi[n&15],kn=n=>(n&240)>>4===(n&15),Vc=n=>kn(n.r)&&kn(n.g)&&kn(n.b)&&kn(n.a);function Yc(n){var t=n.length,e;return n[0]==="#"&&(t===4||t===5?e={r:255&At[n[1]]*17,g:255&At[n[2]]*17,b:255&At[n[3]]*17,a:t===5?At[n[4]]*17:255}:(t===7||t===9)&&(e={r:At[n[1]]<<4|At[n[2]],g:At[n[3]]<<4|At[n[4]],b:At[n[5]]<<4|At[n[6]],a:t===9?At[n[7]]<<4|At[n[8]]:255})),e}const jc=(n,t)=>n<255?t(n):"";function Uc(n){var t=Vc(n)?Wc:Hc;return n?"#"+t(n.r)+t(n.g)+t(n.b)+jc(n.a,t):void 0}const $c=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Or(n,t,e){const i=t*Math.min(e,1-e),s=(o,r=(o+n/30)%12)=>e-i*Math.max(Math.min(r-3,9-r,1),-1);return[s(0),s(8),s(4)]}function qc(n,t,e){const i=(s,o=(s+n/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[i(5),i(3),i(1)]}function Xc(n,t,e){const i=Or(n,1,.5);let s;for(t+e>1&&(s=1/(t+e),t*=s,e*=s),s=0;s<3;s++)i[s]*=1-t-e,i[s]+=t;return i}function Kc(n,t,e,i,s){return n===s?(t-e)/i+(t.5?u/(2-o-r):u/(o+r),l=Kc(e,i,s,u,o),l=l*60+.5),[l|0,c||0,a]}function ds(n,t,e,i){return(Array.isArray(t)?n(t[0],t[1],t[2]):n(t,e,i)).map(le)}function fs(n,t,e){return ds(Or,n,t,e)}function Gc(n,t,e){return ds(Xc,n,t,e)}function Qc(n,t,e){return ds(qc,n,t,e)}function Ar(n){return(n%360+360)%360}function Zc(n){const t=$c.exec(n);let e=255,i;if(!t)return;t[5]!==i&&(e=t[6]?Ge(+t[5]):le(+t[5]));const s=Ar(+t[2]),o=+t[3]/100,r=+t[4]/100;return t[1]==="hwb"?i=Gc(s,o,r):t[1]==="hsv"?i=Qc(s,o,r):i=fs(s,o,r),{r:i[0],g:i[1],b:i[2],a:e}}function Jc(n,t){var e=hs(n);e[0]=Ar(e[0]+t),e=fs(e),n.r=e[0],n.g=e[1],n.b=e[2]}function tu(n){if(!n)return;const t=hs(n),e=t[0],i=Ws(t[1]),s=Ws(t[2]);return n.a<255?`hsla(${e}, ${i}%, ${s}%, ${$t(n.a)})`:`hsl(${e}, ${i}%, ${s}%)`}const Hs={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Vs={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function eu(){const n={},t=Object.keys(Vs),e=Object.keys(Hs);let i,s,o,r,a;for(i=0;i>16&255,o>>8&255,o&255]}return n}let Cn;function nu(n){Cn||(Cn=eu(),Cn.transparent=[0,0,0,0]);const t=Cn[n.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const iu=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function su(n){const t=iu.exec(n);let e=255,i,s,o;if(t){if(t[7]!==i){const r=+t[7];e=t[8]?Ge(r):ne(r*255,0,255)}return i=+t[1],s=+t[3],o=+t[5],i=255&(t[2]?Ge(i):ne(i,0,255)),s=255&(t[4]?Ge(s):ne(s,0,255)),o=255&(t[6]?Ge(o):ne(o,0,255)),{r:i,g:s,b:o,a:e}}}function ou(n){return n&&(n.a<255?`rgba(${n.r}, ${n.g}, ${n.b}, ${$t(n.a)})`:`rgb(${n.r}, ${n.g}, ${n.b})`)}const Mi=n=>n<=.0031308?n*12.92:Math.pow(n,1/2.4)*1.055-.055,Ce=n=>n<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4);function ru(n,t,e){const i=Ce($t(n.r)),s=Ce($t(n.g)),o=Ce($t(n.b));return{r:le(Mi(i+e*(Ce($t(t.r))-i))),g:le(Mi(s+e*(Ce($t(t.g))-s))),b:le(Mi(o+e*(Ce($t(t.b))-o))),a:n.a+e*(t.a-n.a)}}function Pn(n,t,e){if(n){let i=hs(n);i[t]=Math.max(0,Math.min(i[t]+i[t]*e,t===0?360:1)),i=fs(i),n.r=i[0],n.g=i[1],n.b=i[2]}}function Lr(n,t){return n&&Object.assign(t||{},n)}function Ys(n){var t={r:0,g:0,b:0,a:255};return Array.isArray(n)?n.length>=3&&(t={r:n[0],g:n[1],b:n[2],a:255},n.length>3&&(t.a=le(n[3]))):(t=Lr(n,{r:0,g:0,b:0,a:1}),t.a=le(t.a)),t}function au(n){return n.charAt(0)==="r"?su(n):Zc(n)}class dt{constructor(t){if(t instanceof dt)return t;const e=typeof t;let i;e==="object"?i=Ys(t):e==="string"&&(i=Yc(t)||nu(t)||au(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=Lr(this._rgb);return t&&(t.a=$t(t.a)),t}set rgb(t){this._rgb=Ys(t)}rgbString(){return this._valid?ou(this._rgb):void 0}hexString(){return this._valid?Uc(this._rgb):void 0}hslString(){return this._valid?tu(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let o;const r=e===o?.5:e,a=2*r-1,l=i.a-s.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,i.r=255&c*i.r+o*s.r+.5,i.g=255&c*i.g+o*s.g+.5,i.b=255&c*i.b+o*s.b+.5,i.a=r*i.a+(1-r)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=ru(this._rgb,t._rgb,e)),this}clone(){return new dt(this.rgb)}alpha(t){return this._rgb.a=le(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=yn(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Pn(this._rgb,2,t),this}darken(t){return Pn(this._rgb,2,-t),this}saturate(t){return Pn(this._rgb,1,t),this}desaturate(t){return Pn(this._rgb,1,-t),this}rotate(t){return Jc(this._rgb,t),this}}/*! + * Chart.js v4.4.1 * https://www.chartjs.org * (c) 2023 Chart.js Contributors * Released under the MIT License - */function Wt(){}const lu=(()=>{let n=0;return()=>n++})();function H(n){return n===null||typeof n>"u"}function K(n){if(Array.isArray&&Array.isArray(n))return!0;const t=Object.prototype.toString.call(n);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function F(n){return n!==null&&Object.prototype.toString.call(n)==="[object Object]"}function et(n){return(typeof n=="number"||n instanceof Number)&&isFinite(+n)}function Pt(n,t){return et(n)?n:t}function S(n,t){return typeof n>"u"?t:n}const cu=(n,t)=>typeof n=="string"&&n.endsWith("%")?parseFloat(n)/100:+n/t,Lr=(n,t)=>typeof n=="string"&&n.endsWith("%")?parseFloat(n)/100*t:+n;function X(n,t,e){if(n&&typeof n.call=="function")return n.apply(e,t)}function $(n,t,e,i){let s,o,r;if(K(n))if(o=n.length,i)for(s=o-1;s>=0;s--)t.call(e,n[s],s);else for(s=0;sn,x:n=>n.x,y:n=>n.y};function du(n){const t=n.split("."),e=[];let i="";for(const s of t)i+=s,i.endsWith("\\")?i=i.slice(0,-1)+".":(e.push(i),i="");return e}function fu(n){const t=du(n);return e=>{for(const i of t){if(i==="")break;e=e&&e[i]}return e}}function ce(n,t){return(Ys[t]||(Ys[t]=fu(t)))(n)}function gs(n){return n.charAt(0).toUpperCase()+n.slice(1)}const cn=n=>typeof n<"u",ue=n=>typeof n=="function",js=(n,t)=>{if(n.size!==t.size)return!1;for(const e of n)if(!t.has(e))return!1;return!0};function gu(n){return n.type==="mouseup"||n.type==="click"||n.type==="contextmenu"}const tt=Math.PI,G=2*tt,pu=G+tt,ti=Number.POSITIVE_INFINITY,mu=tt/180,at=tt/2,de=tt/4,Us=tt*2/3,ie=Math.log10,Bt=Math.sign;function nn(n,t,e){return Math.abs(n-t)s-o).pop(),t}function un(n){return!isNaN(parseFloat(n))&&isFinite(n)}function yu(n,t){const e=Math.round(n);return e-t<=n&&e+t>=n}function Fr(n,t,e){let i,s,o;for(i=0,s=n.length;il&&c=Math.min(t,e)-i&&n<=Math.max(t,e)+i}function ms(n,t,e){e=e||(r=>n[r]1;)o=s+i>>1,e(o)?s=o:i=o;return{lo:s,hi:i}}const xe=(n,t,e,i)=>ms(n,e,i?s=>{const o=n[s][t];return on[s][t]ms(n,e,i=>n[i][t]>=e);function wu(n,t,e){let i=0,s=n.length;for(;ii&&n[s-1]>e;)s--;return i>0||s{const i="_onData"+gs(e),s=n[e];Object.defineProperty(n,e,{configurable:!0,enumerable:!1,value(...o){const r=s.apply(this,o);return n._chartjs.listeners.forEach(a=>{typeof a[i]=="function"&&a[i](...o)}),r}})})}function Xs(n,t){const e=n._chartjs;if(!e)return;const i=e.listeners,s=i.indexOf(t);s!==-1&&i.splice(s,1),!(i.length>0)&&(Er.forEach(o=>{delete n[o]}),delete n._chartjs)}function zr(n){const t=new Set(n);return t.size===n.length?n:Array.from(t)}const Br=function(){return typeof window>"u"?function(n){return n()}:window.requestAnimationFrame}();function Nr(n,t){let e=[],i=!1;return function(...s){e=s,i||(i=!0,Br.call(window,()=>{i=!1,n.apply(t,e)}))}}function ku(n,t){let e;return function(...i){return t?(clearTimeout(e),e=setTimeout(n,t,i)):n.apply(this,i),t}}const Wr=n=>n==="start"?"left":n==="end"?"right":"center",kt=(n,t,e)=>n==="start"?t:n==="end"?e:(t+e)/2,Cu=(n,t,e,i)=>n===(i?"left":"right")?e:n==="center"?(t+e)/2:t;function Pu(n,t,e){const i=t.length;let s=0,o=i;if(n._sorted){const{iScale:r,_parsed:a}=n,l=r.axis,{min:c,max:u,minDefined:h,maxDefined:d}=r.getUserBounds();h&&(s=ft(Math.min(xe(a,l,c).lo,e?i:xe(t,l,r.getPixelForValue(c)).lo),0,i-1)),d?o=ft(Math.max(xe(a,r.axis,u,!0).hi+1,e?0:xe(t,l,r.getPixelForValue(u),!0).hi+1),s,i)-s:o=i-s}return{start:s,count:o}}function Du(n){const{xScale:t,yScale:e,_scaleRanges:i}=n,s={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!i)return n._scaleRanges=s,!0;const o=i.xmin!==t.min||i.xmax!==t.max||i.ymin!==e.min||i.ymax!==e.max;return Object.assign(i,s),o}const Dn=n=>n===0||n===1,Ks=(n,t,e)=>-(Math.pow(2,10*(n-=1))*Math.sin((n-t)*G/e)),Gs=(n,t,e)=>Math.pow(2,-10*n)*Math.sin((n-t)*G/e)+1,sn={linear:n=>n,easeInQuad:n=>n*n,easeOutQuad:n=>-n*(n-2),easeInOutQuad:n=>(n/=.5)<1?.5*n*n:-.5*(--n*(n-2)-1),easeInCubic:n=>n*n*n,easeOutCubic:n=>(n-=1)*n*n+1,easeInOutCubic:n=>(n/=.5)<1?.5*n*n*n:.5*((n-=2)*n*n+2),easeInQuart:n=>n*n*n*n,easeOutQuart:n=>-((n-=1)*n*n*n-1),easeInOutQuart:n=>(n/=.5)<1?.5*n*n*n*n:-.5*((n-=2)*n*n*n-2),easeInQuint:n=>n*n*n*n*n,easeOutQuint:n=>(n-=1)*n*n*n*n+1,easeInOutQuint:n=>(n/=.5)<1?.5*n*n*n*n*n:.5*((n-=2)*n*n*n*n+2),easeInSine:n=>-Math.cos(n*at)+1,easeOutSine:n=>Math.sin(n*at),easeInOutSine:n=>-.5*(Math.cos(tt*n)-1),easeInExpo:n=>n===0?0:Math.pow(2,10*(n-1)),easeOutExpo:n=>n===1?1:-Math.pow(2,-10*n)+1,easeInOutExpo:n=>Dn(n)?n:n<.5?.5*Math.pow(2,10*(n*2-1)):.5*(-Math.pow(2,-10*(n*2-1))+2),easeInCirc:n=>n>=1?n:-(Math.sqrt(1-n*n)-1),easeOutCirc:n=>Math.sqrt(1-(n-=1)*n),easeInOutCirc:n=>(n/=.5)<1?-.5*(Math.sqrt(1-n*n)-1):.5*(Math.sqrt(1-(n-=2)*n)+1),easeInElastic:n=>Dn(n)?n:Ks(n,.075,.3),easeOutElastic:n=>Dn(n)?n:Gs(n,.075,.3),easeInOutElastic(n){return Dn(n)?n:n<.5?.5*Ks(n*2,.1125,.45):.5+.5*Gs(n*2-1,.1125,.45)},easeInBack(n){return n*n*((1.70158+1)*n-1.70158)},easeOutBack(n){return(n-=1)*n*((1.70158+1)*n+1.70158)+1},easeInOutBack(n){let t=1.70158;return(n/=.5)<1?.5*(n*n*(((t*=1.525)+1)*n-t)):.5*((n-=2)*n*(((t*=1.525)+1)*n+t)+2)},easeInBounce:n=>1-sn.easeOutBounce(1-n),easeOutBounce(n){return n<1/2.75?7.5625*n*n:n<2/2.75?7.5625*(n-=1.5/2.75)*n+.75:n<2.5/2.75?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375},easeInOutBounce:n=>n<.5?sn.easeInBounce(n*2)*.5:sn.easeOutBounce(n*2-1)*.5+.5};function bs(n){if(n&&typeof n=="object"){const t=n.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function Ae(n){return bs(n)?n:new dt(n)}function on(n){return bs(n)?n:new dt(n).saturate(.5).darken(.1).hexString()}const Tu=["x","y","borderWidth","radius","tension"],Su=["color","borderColor","backgroundColor"];function Ou(n){n.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),n.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),n.set("animations",{colors:{type:"color",properties:Su},numbers:{type:"number",properties:Tu}}),n.describe("animations",{_fallback:"animation"}),n.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>t|0}}}})}function Au(n){n.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const Qs=new Map;function Lu(n,t){t=t||{};const e=n+JSON.stringify(t);let i=Qs.get(e);return i||(i=new Intl.NumberFormat(n,t),Qs.set(e,i)),i}function _n(n,t,e){return Lu(t,e).format(n)}const Hr={values(n){return K(n)?n:""+n},numeric(n,t,e){if(n===0)return"0";const i=this.chart.options.locale;let s,o=n;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(s="scientific"),o=Ru(n,e)}const r=ie(Math.abs(o)),a=isNaN(r)?1:Math.max(Math.min(-1*Math.floor(r),20),0),l={notation:s,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),_n(n,i,l)},logarithmic(n,t,e){if(n===0)return"0";const i=e[t].significand||n/Math.pow(10,Math.floor(ie(n)));return[1,2,3,5,10,15].includes(i)||t>.8*e.length?Hr.numeric.call(this,n,t,e):""}};function Ru(n,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&n!==Math.floor(n)&&(e=n-Math.floor(n)),e}var fi={formatters:Hr};function Fu(n){n.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:fi.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),n.route("scale.ticks","color","","color"),n.route("scale.grid","color","","borderColor"),n.route("scale.border","color","","borderColor"),n.route("scale.title","color","","color"),n.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),n.describe("scales",{_fallback:"scale"}),n.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const Me=Object.create(null),Ki=Object.create(null);function rn(n,t){if(!t)return n;const e=t.split(".");for(let i=0,s=e.length;ii.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(i,s)=>on(s.backgroundColor),this.hoverBorderColor=(i,s)=>on(s.borderColor),this.hoverColor=(i,s)=>on(s.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ki(this,t,e)}get(t){return rn(this,t)}describe(t,e){return ki(Ki,t,e)}override(t,e){return ki(Me,t,e)}route(t,e,i,s){const o=rn(this,t),r=rn(this,i),a="_"+e;Object.defineProperties(o,{[a]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[a],c=r[s];return F(l)?Object.assign({},c,l):S(l,c)},set(l){this[a]=l}}})}apply(t){t.forEach(e=>e(this))}}var nt=new Iu({_scriptable:n=>!n.startsWith("on"),_indexable:n=>n!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[Ou,Au,Fu]);function Eu(n){return!n||H(n.size)||H(n.family)?null:(n.style?n.style+" ":"")+(n.weight?n.weight+" ":"")+n.size+"px "+n.family}function ei(n,t,e,i,s){let o=t[s];return o||(o=t[s]=n.measureText(s).width,e.push(s)),o>i&&(i=o),i}function zu(n,t,e,i){i=i||{};let s=i.data=i.data||{},o=i.garbageCollect=i.garbageCollect||[];i.font!==t&&(s=i.data={},o=i.garbageCollect=[],i.font=t),n.save(),n.font=t;let r=0;const a=e.length;let l,c,u,h,d;for(l=0;le.length){for(l=0;l0&&n.stroke()}}function qt(n,t,e){return e=e||.5,!t||n&&n.x>t.left-e&&n.xt.top-e&&n.y0&&o.strokeColor!=="";let l,c;for(n.save(),n.font=s.string,Wu(n,o),l=0;l+n||0;function ys(n,t){const e={},i=F(t),s=i?Object.keys(t):t,o=F(n)?i?r=>S(n[r],n[t[r]]):r=>n[r]:()=>n;for(const r of s)e[r]=$u(o(r));return e}function Yr(n){return ys(n,{top:"y",right:"x",bottom:"y",left:"x"})}function ve(n){return ys(n,["topLeft","topRight","bottomLeft","bottomRight"])}function yt(n){const t=Yr(n);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function ut(n,t){n=n||{},t=t||nt.font;let e=S(n.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let i=S(n.style,t.style);i&&!(""+i).match(ju)&&(console.warn('Invalid font style specified: "'+i+'"'),i=void 0);const s={family:S(n.family,t.family),lineHeight:Uu(S(n.lineHeight,t.lineHeight),e),size:e,style:i,weight:S(n.weight,t.weight),string:""};return s.string=Eu(s),s}function Tn(n,t,e,i){let s=!0,o,r,a;for(o=0,r=n.length;oe&&a===0?0:a+l;return{min:r(i,-Math.abs(o)),max:r(s,o)}}function he(n,t){return Object.assign(Object.create(n),t)}function _s(n,t=[""],e,i,s=()=>n[0]){const o=e||n;typeof i>"u"&&(i=qr("_fallback",n));const r={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:n,_rootScopes:o,_fallback:i,_getTarget:s,override:a=>_s([a,...n],t,o,i)};return new Proxy(r,{deleteProperty(a,l){return delete a[l],delete a._keys,delete n[0][l],!0},get(a,l){return Ur(a,l,()=>eh(l,t,n,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(n[0])},has(a,l){return to(a).includes(l)},ownKeys(a){return to(a)},set(a,l,c){const u=a._storage||(a._storage=s());return a[l]=u[l]=c,delete a._keys,!0}})}function Ee(n,t,e,i){const s={_cacheable:!1,_proxy:n,_context:t,_subProxy:e,_stack:new Set,_descriptors:jr(n,i),setContext:o=>Ee(n,o,e,i),override:o=>Ee(n.override(o),t,e,i)};return new Proxy(s,{deleteProperty(o,r){return delete o[r],delete n[r],!0},get(o,r,a){return Ur(o,r,()=>Ku(o,r,a))},getOwnPropertyDescriptor(o,r){return o._descriptors.allKeys?Reflect.has(n,r)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(n,r)},getPrototypeOf(){return Reflect.getPrototypeOf(n)},has(o,r){return Reflect.has(n,r)},ownKeys(){return Reflect.ownKeys(n)},set(o,r,a){return n[r]=a,delete o[r],!0}})}function jr(n,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:i=t.indexable,_allKeys:s=t.allKeys}=n;return{allKeys:s,scriptable:e,indexable:i,isScriptable:ue(e)?e:()=>e,isIndexable:ue(i)?i:()=>i}}const Xu=(n,t)=>n?n+gs(t):t,xs=(n,t)=>F(t)&&n!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function Ur(n,t,e){if(Object.prototype.hasOwnProperty.call(n,t))return n[t];const i=e();return n[t]=i,i}function Ku(n,t,e){const{_proxy:i,_context:s,_subProxy:o,_descriptors:r}=n;let a=i[t];return ue(a)&&r.isScriptable(t)&&(a=Gu(t,a,n,e)),K(a)&&a.length&&(a=Qu(t,a,n,r.isIndexable)),xs(t,a)&&(a=Ee(a,s,o&&o[t],r)),a}function Gu(n,t,e,i){const{_proxy:s,_context:o,_subProxy:r,_stack:a}=e;if(a.has(n))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+n);a.add(n);let l=t(o,r||i);return a.delete(n),xs(n,l)&&(l=vs(s._scopes,s,n,l)),l}function Qu(n,t,e,i){const{_proxy:s,_context:o,_subProxy:r,_descriptors:a}=e;if(typeof o.index<"u"&&i(n))return t[o.index%t.length];if(F(t[0])){const l=t,c=s._scopes.filter(u=>u!==l);t=[];for(const u of l){const h=vs(c,s,n,u);t.push(Ee(h,o,r&&r[n],a))}}return t}function $r(n,t,e){return ue(n)?n(t,e):n}const Zu=(n,t)=>n===!0?t:typeof n=="string"?ce(t,n):void 0;function Ju(n,t,e,i,s){for(const o of t){const r=Zu(e,o);if(r){n.add(r);const a=$r(r._fallback,e,s);if(typeof a<"u"&&a!==e&&a!==i)return a}else if(r===!1&&typeof i<"u"&&e!==i)return null}return!1}function vs(n,t,e,i){const s=t._rootScopes,o=$r(t._fallback,e,i),r=[...n,...s],a=new Set;a.add(i);let l=Js(a,r,e,o||e,i);return l===null||typeof o<"u"&&o!==e&&(l=Js(a,r,o,l,i),l===null)?!1:_s(Array.from(a),[""],s,o,()=>th(t,e,i))}function Js(n,t,e,i,s){for(;e;)e=Ju(n,t,e,i,s);return e}function th(n,t,e){const i=n._getTarget();t in i||(i[t]={});const s=i[t];return K(s)&&F(e)?e:s||{}}function eh(n,t,e,i){let s;for(const o of t)if(s=qr(Xu(o,n),e),typeof s<"u")return xs(n,s)?vs(e,i,n,s):s}function qr(n,t){for(const e of t){if(!e)continue;const i=e[n];if(typeof i<"u")return i}}function to(n){let t=n._keys;return t||(t=n._keys=nh(n._scopes)),t}function nh(n){const t=new Set;for(const e of n)for(const i of Object.keys(e).filter(s=>!s.startsWith("_")))t.add(i);return Array.from(t)}function ih(n,t,e,i){const{iScale:s}=n,{key:o="r"}=this._parsing,r=new Array(i);let a,l,c,u;for(a=0,l=i;atn==="x"?"y":"x";function oh(n,t,e,i){const s=n.skip?t:n,o=t,r=e.skip?t:e,a=Xi(o,s),l=Xi(r,o);let c=a/(a+l),u=l/(a+l);c=isNaN(c)?0:c,u=isNaN(u)?0:u;const h=i*c,d=i*u;return{previous:{x:o.x-h*(r.x-s.x),y:o.y-h*(r.y-s.y)},next:{x:o.x+d*(r.x-s.x),y:o.y+d*(r.y-s.y)}}}function rh(n,t,e){const i=n.length;let s,o,r,a,l,c=ze(n,0);for(let u=0;u!c.skip)),t.cubicInterpolationMode==="monotone")lh(n,s);else{let c=i?n[n.length-1]:n[0];for(o=0,r=n.length;on.ownerDocument.defaultView.getComputedStyle(n,null);function hh(n,t){return mi(n).getPropertyValue(t)}const dh=["top","right","bottom","left"];function we(n,t,e){const i={};e=e?"-"+e:"";for(let s=0;s<4;s++){const o=dh[s];i[o]=parseFloat(n[t+"-"+o+e])||0}return i.width=i.left+i.right,i.height=i.top+i.bottom,i}const fh=(n,t,e)=>(n>0||t>0)&&(!e||!e.shadowRoot);function gh(n,t){const e=n.touches,i=e&&e.length?e[0]:n,{offsetX:s,offsetY:o}=i;let r=!1,a,l;if(fh(s,o,n.target))a=s,l=o;else{const c=t.getBoundingClientRect();a=i.clientX-c.left,l=i.clientY-c.top,r=!0}return{x:a,y:l,box:r}}function be(n,t){if("native"in n)return n;const{canvas:e,currentDevicePixelRatio:i}=t,s=mi(e),o=s.boxSizing==="border-box",r=we(s,"padding"),a=we(s,"border","width"),{x:l,y:c,box:u}=gh(n,e),h=r.left+(u&&a.left),d=r.top+(u&&a.top);let{width:f,height:g}=t;return o&&(f-=r.width+a.width,g-=r.height+a.height),{x:Math.round((l-h)/f*e.width/i),y:Math.round((c-d)/g*e.height/i)}}function ph(n,t,e){let i,s;if(t===void 0||e===void 0){const o=ws(n);if(!o)t=n.clientWidth,e=n.clientHeight;else{const r=o.getBoundingClientRect(),a=mi(o),l=we(a,"border","width"),c=we(a,"padding");t=r.width-c.width-l.width,e=r.height-c.height-l.height,i=ni(a.maxWidth,o,"clientWidth"),s=ni(a.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:i||ti,maxHeight:s||ti}}const On=n=>Math.round(n*10)/10;function mh(n,t,e,i){const s=mi(n),o=we(s,"margin"),r=ni(s.maxWidth,n,"clientWidth")||ti,a=ni(s.maxHeight,n,"clientHeight")||ti,l=ph(n,t,e);let{width:c,height:u}=l;if(s.boxSizing==="content-box"){const d=we(s,"border","width"),f=we(s,"padding");c-=f.width+d.width,u-=f.height+d.height}return c=Math.max(0,c-o.width),u=Math.max(0,i?c/i:u-o.height),c=On(Math.min(c,r,l.maxWidth)),u=On(Math.min(u,a,l.maxHeight)),c&&!u&&(u=On(c/2)),(t!==void 0||e!==void 0)&&i&&l.height&&u>l.height&&(u=l.height,c=On(Math.floor(u*i))),{width:c,height:u}}function eo(n,t,e){const i=t||1,s=Math.floor(n.height*i),o=Math.floor(n.width*i);n.height=Math.floor(n.height),n.width=Math.floor(n.width);const r=n.canvas;return r.style&&(e||!r.style.height&&!r.style.width)&&(r.style.height=`${n.height}px`,r.style.width=`${n.width}px`),n.currentDevicePixelRatio!==i||r.height!==s||r.width!==o?(n.currentDevicePixelRatio=i,r.height=s,r.width=o,n.ctx.setTransform(i,0,0,i,0,0),!0):!1}const bh=function(){let n=!1;try{const t={get passive(){return n=!0,!1}};window.addEventListener("test",null,t),window.removeEventListener("test",null,t)}catch{}return n}();function no(n,t){const e=hh(n,t),i=e&&e.match(/^(\d+)(\.\d+)?px$/);return i?+i[1]:void 0}function ye(n,t,e,i){return{x:n.x+e*(t.x-n.x),y:n.y+e*(t.y-n.y)}}function yh(n,t,e,i){return{x:n.x+e*(t.x-n.x),y:i==="middle"?e<.5?n.y:t.y:i==="after"?e<1?n.y:t.y:e>0?t.y:n.y}}function _h(n,t,e,i){const s={x:n.cp2x,y:n.cp2y},o={x:t.cp1x,y:t.cp1y},r=ye(n,s,e),a=ye(s,o,e),l=ye(o,t,e),c=ye(r,a,e),u=ye(a,l,e);return ye(c,u,e)}const xh=function(n,t){return{x(e){return n+n+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,i){return e-i},leftForLtr(e,i){return e-i}}},vh=function(){return{x(n){return n},setWidth(n){},textAlign(n){return n},xPlus(n,t){return n+t},leftForLtr(n,t){return n}}};function Fe(n,t,e){return n?xh(t,e):vh()}function Gr(n,t){let e,i;(t==="ltr"||t==="rtl")&&(e=n.canvas.style,i=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),n.prevTextDirection=i)}function Qr(n,t){t!==void 0&&(delete n.prevTextDirection,n.canvas.style.setProperty("direction",t[0],t[1]))}function Zr(n){return n==="angle"?{between:hn,compare:_u,normalize:Dt}:{between:$t,compare:(t,e)=>t-e,normalize:t=>t}}function io({start:n,end:t,count:e,loop:i,style:s}){return{start:n%e,end:t%e,loop:i&&(t-n+1)%e===0,style:s}}function wh(n,t,e){const{property:i,start:s,end:o}=e,{between:r,normalize:a}=Zr(i),l=t.length;let{start:c,end:u,loop:h}=n,d,f;if(h){for(c+=l,u+=l,d=0,f=l;dl(s,x,b)&&a(s,x)!==0,_=()=>a(o,b)===0||l(o,x,b),w=()=>p||v(),C=()=>!p||_();for(let M=u,T=u;M<=h;++M)y=t[M%r],!y.skip&&(b=c(y[i]),b!==x&&(p=l(b,s,o),m===null&&w()&&(m=a(b,s)===0?M:T),m!==null&&C()&&(g.push(io({start:m,end:M,loop:d,count:r,style:f})),m=null),T=M,x=b));return m!==null&&g.push(io({start:m,end:h,loop:d,count:r,style:f})),g}function ta(n,t){const e=[],i=n.segments;for(let s=0;ss&&n[o%t].skip;)o--;return o%=t,{start:s,end:o}}function kh(n,t,e,i){const s=n.length,o=[];let r=t,a=n[t],l;for(l=t+1;l<=e;++l){const c=n[l%s];c.skip||c.stop?a.skip||(i=!1,o.push({start:t%s,end:(l-1)%s,loop:i}),t=r=c.stop?l:null):(r=l,a.skip&&(t=l)),a=c}return r!==null&&o.push({start:t%s,end:r%s,loop:i}),o}function Ch(n,t){const e=n.points,i=n.options.spanGaps,s=e.length;if(!s)return[];const o=!!n._loop,{start:r,end:a}=Mh(e,s,o,i);if(i===!0)return so(n,[{start:r,end:a,loop:o}],e,t);const l=a{let n=0;return()=>n++})();function H(n){return n===null||typeof n>"u"}function K(n){if(Array.isArray&&Array.isArray(n))return!0;const t=Object.prototype.toString.call(n);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function F(n){return n!==null&&Object.prototype.toString.call(n)==="[object Object]"}function et(n){return(typeof n=="number"||n instanceof Number)&&isFinite(+n)}function Pt(n,t){return et(n)?n:t}function S(n,t){return typeof n>"u"?t:n}const cu=(n,t)=>typeof n=="string"&&n.endsWith("%")?parseFloat(n)/100:+n/t,Rr=(n,t)=>typeof n=="string"&&n.endsWith("%")?parseFloat(n)/100*t:+n;function X(n,t,e){if(n&&typeof n.call=="function")return n.apply(e,t)}function $(n,t,e,i){let s,o,r;if(K(n))if(o=n.length,i)for(s=o-1;s>=0;s--)t.call(e,n[s],s);else for(s=0;sn,x:n=>n.x,y:n=>n.y};function du(n){const t=n.split("."),e=[];let i="";for(const s of t)i+=s,i.endsWith("\\")?i=i.slice(0,-1)+".":(e.push(i),i="");return e}function fu(n){const t=du(n);return e=>{for(const i of t){if(i==="")break;e=e&&e[i]}return e}}function ce(n,t){return(js[t]||(js[t]=fu(t)))(n)}function gs(n){return n.charAt(0).toUpperCase()+n.slice(1)}const cn=n=>typeof n<"u",ue=n=>typeof n=="function",Us=(n,t)=>{if(n.size!==t.size)return!1;for(const e of n)if(!t.has(e))return!1;return!0};function gu(n){return n.type==="mouseup"||n.type==="click"||n.type==="contextmenu"}const tt=Math.PI,G=2*tt,pu=G+tt,ti=Number.POSITIVE_INFINITY,mu=tt/180,at=tt/2,de=tt/4,$s=tt*2/3,ie=Math.log10,Nt=Math.sign;function nn(n,t,e){return Math.abs(n-t)s-o).pop(),t}function un(n){return!isNaN(parseFloat(n))&&isFinite(n)}function yu(n,t){const e=Math.round(n);return e-t<=n&&e+t>=n}function Ir(n,t,e){let i,s,o;for(i=0,s=n.length;il&&c=Math.min(t,e)-i&&n<=Math.max(t,e)+i}function ms(n,t,e){e=e||(r=>n[r]1;)o=s+i>>1,e(o)?s=o:i=o;return{lo:s,hi:i}}const xe=(n,t,e,i)=>ms(n,e,i?s=>{const o=n[s][t];return on[s][t]ms(n,e,i=>n[i][t]>=e);function wu(n,t,e){let i=0,s=n.length;for(;ii&&n[s-1]>e;)s--;return i>0||s{const i="_onData"+gs(e),s=n[e];Object.defineProperty(n,e,{configurable:!0,enumerable:!1,value(...o){const r=s.apply(this,o);return n._chartjs.listeners.forEach(a=>{typeof a[i]=="function"&&a[i](...o)}),r}})})}function Ks(n,t){const e=n._chartjs;if(!e)return;const i=e.listeners,s=i.indexOf(t);s!==-1&&i.splice(s,1),!(i.length>0)&&(zr.forEach(o=>{delete n[o]}),delete n._chartjs)}function Br(n){const t=new Set(n);return t.size===n.length?n:Array.from(t)}const Nr=function(){return typeof window>"u"?function(n){return n()}:window.requestAnimationFrame}();function Wr(n,t){let e=[],i=!1;return function(...s){e=s,i||(i=!0,Nr.call(window,()=>{i=!1,n.apply(t,e)}))}}function ku(n,t){let e;return function(...i){return t?(clearTimeout(e),e=setTimeout(n,t,i)):n.apply(this,i),t}}const Hr=n=>n==="start"?"left":n==="end"?"right":"center",kt=(n,t,e)=>n==="start"?t:n==="end"?e:(t+e)/2,Cu=(n,t,e,i)=>n===(i?"left":"right")?e:n==="center"?(t+e)/2:t;function Pu(n,t,e){const i=t.length;let s=0,o=i;if(n._sorted){const{iScale:r,_parsed:a}=n,l=r.axis,{min:c,max:u,minDefined:h,maxDefined:d}=r.getUserBounds();h&&(s=ft(Math.min(xe(a,l,c).lo,e?i:xe(t,l,r.getPixelForValue(c)).lo),0,i-1)),d?o=ft(Math.max(xe(a,r.axis,u,!0).hi+1,e?0:xe(t,l,r.getPixelForValue(u),!0).hi+1),s,i)-s:o=i-s}return{start:s,count:o}}function Du(n){const{xScale:t,yScale:e,_scaleRanges:i}=n,s={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!i)return n._scaleRanges=s,!0;const o=i.xmin!==t.min||i.xmax!==t.max||i.ymin!==e.min||i.ymax!==e.max;return Object.assign(i,s),o}const Dn=n=>n===0||n===1,Gs=(n,t,e)=>-(Math.pow(2,10*(n-=1))*Math.sin((n-t)*G/e)),Qs=(n,t,e)=>Math.pow(2,-10*n)*Math.sin((n-t)*G/e)+1,sn={linear:n=>n,easeInQuad:n=>n*n,easeOutQuad:n=>-n*(n-2),easeInOutQuad:n=>(n/=.5)<1?.5*n*n:-.5*(--n*(n-2)-1),easeInCubic:n=>n*n*n,easeOutCubic:n=>(n-=1)*n*n+1,easeInOutCubic:n=>(n/=.5)<1?.5*n*n*n:.5*((n-=2)*n*n+2),easeInQuart:n=>n*n*n*n,easeOutQuart:n=>-((n-=1)*n*n*n-1),easeInOutQuart:n=>(n/=.5)<1?.5*n*n*n*n:-.5*((n-=2)*n*n*n-2),easeInQuint:n=>n*n*n*n*n,easeOutQuint:n=>(n-=1)*n*n*n*n+1,easeInOutQuint:n=>(n/=.5)<1?.5*n*n*n*n*n:.5*((n-=2)*n*n*n*n+2),easeInSine:n=>-Math.cos(n*at)+1,easeOutSine:n=>Math.sin(n*at),easeInOutSine:n=>-.5*(Math.cos(tt*n)-1),easeInExpo:n=>n===0?0:Math.pow(2,10*(n-1)),easeOutExpo:n=>n===1?1:-Math.pow(2,-10*n)+1,easeInOutExpo:n=>Dn(n)?n:n<.5?.5*Math.pow(2,10*(n*2-1)):.5*(-Math.pow(2,-10*(n*2-1))+2),easeInCirc:n=>n>=1?n:-(Math.sqrt(1-n*n)-1),easeOutCirc:n=>Math.sqrt(1-(n-=1)*n),easeInOutCirc:n=>(n/=.5)<1?-.5*(Math.sqrt(1-n*n)-1):.5*(Math.sqrt(1-(n-=2)*n)+1),easeInElastic:n=>Dn(n)?n:Gs(n,.075,.3),easeOutElastic:n=>Dn(n)?n:Qs(n,.075,.3),easeInOutElastic(n){return Dn(n)?n:n<.5?.5*Gs(n*2,.1125,.45):.5+.5*Qs(n*2-1,.1125,.45)},easeInBack(n){return n*n*((1.70158+1)*n-1.70158)},easeOutBack(n){return(n-=1)*n*((1.70158+1)*n+1.70158)+1},easeInOutBack(n){let t=1.70158;return(n/=.5)<1?.5*(n*n*(((t*=1.525)+1)*n-t)):.5*((n-=2)*n*(((t*=1.525)+1)*n+t)+2)},easeInBounce:n=>1-sn.easeOutBounce(1-n),easeOutBounce(n){return n<1/2.75?7.5625*n*n:n<2/2.75?7.5625*(n-=1.5/2.75)*n+.75:n<2.5/2.75?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375},easeInOutBounce:n=>n<.5?sn.easeInBounce(n*2)*.5:sn.easeOutBounce(n*2-1)*.5+.5};function bs(n){if(n&&typeof n=="object"){const t=n.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function Ae(n){return bs(n)?n:new dt(n)}function on(n){return bs(n)?n:new dt(n).saturate(.5).darken(.1).hexString()}const Tu=["x","y","borderWidth","radius","tension"],Su=["color","borderColor","backgroundColor"];function Ou(n){n.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),n.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),n.set("animations",{colors:{type:"color",properties:Su},numbers:{type:"number",properties:Tu}}),n.describe("animations",{_fallback:"animation"}),n.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>t|0}}}})}function Au(n){n.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const Zs=new Map;function Lu(n,t){t=t||{};const e=n+JSON.stringify(t);let i=Zs.get(e);return i||(i=new Intl.NumberFormat(n,t),Zs.set(e,i)),i}function _n(n,t,e){return Lu(t,e).format(n)}const Vr={values(n){return K(n)?n:""+n},numeric(n,t,e){if(n===0)return"0";const i=this.chart.options.locale;let s,o=n;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(s="scientific"),o=Ru(n,e)}const r=ie(Math.abs(o)),a=isNaN(r)?1:Math.max(Math.min(-1*Math.floor(r),20),0),l={notation:s,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),_n(n,i,l)},logarithmic(n,t,e){if(n===0)return"0";const i=e[t].significand||n/Math.pow(10,Math.floor(ie(n)));return[1,2,3,5,10,15].includes(i)||t>.8*e.length?Vr.numeric.call(this,n,t,e):""}};function Ru(n,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&n!==Math.floor(n)&&(e=n-Math.floor(n)),e}var fi={formatters:Vr};function Fu(n){n.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:fi.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),n.route("scale.ticks","color","","color"),n.route("scale.grid","color","","borderColor"),n.route("scale.border","color","","borderColor"),n.route("scale.title","color","","color"),n.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),n.describe("scales",{_fallback:"scale"}),n.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const Me=Object.create(null),Ki=Object.create(null);function rn(n,t){if(!t)return n;const e=t.split(".");for(let i=0,s=e.length;ii.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(i,s)=>on(s.backgroundColor),this.hoverBorderColor=(i,s)=>on(s.borderColor),this.hoverColor=(i,s)=>on(s.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ki(this,t,e)}get(t){return rn(this,t)}describe(t,e){return ki(Ki,t,e)}override(t,e){return ki(Me,t,e)}route(t,e,i,s){const o=rn(this,t),r=rn(this,i),a="_"+e;Object.defineProperties(o,{[a]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[a],c=r[s];return F(l)?Object.assign({},c,l):S(l,c)},set(l){this[a]=l}}})}apply(t){t.forEach(e=>e(this))}}var nt=new Iu({_scriptable:n=>!n.startsWith("on"),_indexable:n=>n!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[Ou,Au,Fu]);function Eu(n){return!n||H(n.size)||H(n.family)?null:(n.style?n.style+" ":"")+(n.weight?n.weight+" ":"")+n.size+"px "+n.family}function ei(n,t,e,i,s){let o=t[s];return o||(o=t[s]=n.measureText(s).width,e.push(s)),o>i&&(i=o),i}function zu(n,t,e,i){i=i||{};let s=i.data=i.data||{},o=i.garbageCollect=i.garbageCollect||[];i.font!==t&&(s=i.data={},o=i.garbageCollect=[],i.font=t),n.save(),n.font=t;let r=0;const a=e.length;let l,c,u,h,d;for(l=0;le.length){for(l=0;l0&&n.stroke()}}function Xt(n,t,e){return e=e||.5,!t||n&&n.x>t.left-e&&n.xt.top-e&&n.y0&&o.strokeColor!=="";let l,c;for(n.save(),n.font=s.string,Wu(n,o),l=0;l+n||0;function ys(n,t){const e={},i=F(t),s=i?Object.keys(t):t,o=F(n)?i?r=>S(n[r],n[t[r]]):r=>n[r]:()=>n;for(const r of s)e[r]=$u(o(r));return e}function jr(n){return ys(n,{top:"y",right:"x",bottom:"y",left:"x"})}function ve(n){return ys(n,["topLeft","topRight","bottomLeft","bottomRight"])}function yt(n){const t=jr(n);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function ut(n,t){n=n||{},t=t||nt.font;let e=S(n.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let i=S(n.style,t.style);i&&!(""+i).match(ju)&&(console.warn('Invalid font style specified: "'+i+'"'),i=void 0);const s={family:S(n.family,t.family),lineHeight:Uu(S(n.lineHeight,t.lineHeight),e),size:e,style:i,weight:S(n.weight,t.weight),string:""};return s.string=Eu(s),s}function Tn(n,t,e,i){let s=!0,o,r,a;for(o=0,r=n.length;oe&&a===0?0:a+l;return{min:r(i,-Math.abs(o)),max:r(s,o)}}function he(n,t){return Object.assign(Object.create(n),t)}function _s(n,t=[""],e,i,s=()=>n[0]){const o=e||n;typeof i>"u"&&(i=Xr("_fallback",n));const r={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:n,_rootScopes:o,_fallback:i,_getTarget:s,override:a=>_s([a,...n],t,o,i)};return new Proxy(r,{deleteProperty(a,l){return delete a[l],delete a._keys,delete n[0][l],!0},get(a,l){return $r(a,l,()=>eh(l,t,n,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(n[0])},has(a,l){return eo(a).includes(l)},ownKeys(a){return eo(a)},set(a,l,c){const u=a._storage||(a._storage=s());return a[l]=u[l]=c,delete a._keys,!0}})}function Ee(n,t,e,i){const s={_cacheable:!1,_proxy:n,_context:t,_subProxy:e,_stack:new Set,_descriptors:Ur(n,i),setContext:o=>Ee(n,o,e,i),override:o=>Ee(n.override(o),t,e,i)};return new Proxy(s,{deleteProperty(o,r){return delete o[r],delete n[r],!0},get(o,r,a){return $r(o,r,()=>Ku(o,r,a))},getOwnPropertyDescriptor(o,r){return o._descriptors.allKeys?Reflect.has(n,r)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(n,r)},getPrototypeOf(){return Reflect.getPrototypeOf(n)},has(o,r){return Reflect.has(n,r)},ownKeys(){return Reflect.ownKeys(n)},set(o,r,a){return n[r]=a,delete o[r],!0}})}function Ur(n,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:i=t.indexable,_allKeys:s=t.allKeys}=n;return{allKeys:s,scriptable:e,indexable:i,isScriptable:ue(e)?e:()=>e,isIndexable:ue(i)?i:()=>i}}const Xu=(n,t)=>n?n+gs(t):t,xs=(n,t)=>F(t)&&n!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function $r(n,t,e){if(Object.prototype.hasOwnProperty.call(n,t))return n[t];const i=e();return n[t]=i,i}function Ku(n,t,e){const{_proxy:i,_context:s,_subProxy:o,_descriptors:r}=n;let a=i[t];return ue(a)&&r.isScriptable(t)&&(a=Gu(t,a,n,e)),K(a)&&a.length&&(a=Qu(t,a,n,r.isIndexable)),xs(t,a)&&(a=Ee(a,s,o&&o[t],r)),a}function Gu(n,t,e,i){const{_proxy:s,_context:o,_subProxy:r,_stack:a}=e;if(a.has(n))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+n);a.add(n);let l=t(o,r||i);return a.delete(n),xs(n,l)&&(l=vs(s._scopes,s,n,l)),l}function Qu(n,t,e,i){const{_proxy:s,_context:o,_subProxy:r,_descriptors:a}=e;if(typeof o.index<"u"&&i(n))return t[o.index%t.length];if(F(t[0])){const l=t,c=s._scopes.filter(u=>u!==l);t=[];for(const u of l){const h=vs(c,s,n,u);t.push(Ee(h,o,r&&r[n],a))}}return t}function qr(n,t,e){return ue(n)?n(t,e):n}const Zu=(n,t)=>n===!0?t:typeof n=="string"?ce(t,n):void 0;function Ju(n,t,e,i,s){for(const o of t){const r=Zu(e,o);if(r){n.add(r);const a=qr(r._fallback,e,s);if(typeof a<"u"&&a!==e&&a!==i)return a}else if(r===!1&&typeof i<"u"&&e!==i)return null}return!1}function vs(n,t,e,i){const s=t._rootScopes,o=qr(t._fallback,e,i),r=[...n,...s],a=new Set;a.add(i);let l=to(a,r,e,o||e,i);return l===null||typeof o<"u"&&o!==e&&(l=to(a,r,o,l,i),l===null)?!1:_s(Array.from(a),[""],s,o,()=>th(t,e,i))}function to(n,t,e,i,s){for(;e;)e=Ju(n,t,e,i,s);return e}function th(n,t,e){const i=n._getTarget();t in i||(i[t]={});const s=i[t];return K(s)&&F(e)?e:s||{}}function eh(n,t,e,i){let s;for(const o of t)if(s=Xr(Xu(o,n),e),typeof s<"u")return xs(n,s)?vs(e,i,n,s):s}function Xr(n,t){for(const e of t){if(!e)continue;const i=e[n];if(typeof i<"u")return i}}function eo(n){let t=n._keys;return t||(t=n._keys=nh(n._scopes)),t}function nh(n){const t=new Set;for(const e of n)for(const i of Object.keys(e).filter(s=>!s.startsWith("_")))t.add(i);return Array.from(t)}function ih(n,t,e,i){const{iScale:s}=n,{key:o="r"}=this._parsing,r=new Array(i);let a,l,c,u;for(a=0,l=i;atn==="x"?"y":"x";function oh(n,t,e,i){const s=n.skip?t:n,o=t,r=e.skip?t:e,a=Xi(o,s),l=Xi(r,o);let c=a/(a+l),u=l/(a+l);c=isNaN(c)?0:c,u=isNaN(u)?0:u;const h=i*c,d=i*u;return{previous:{x:o.x-h*(r.x-s.x),y:o.y-h*(r.y-s.y)},next:{x:o.x+d*(r.x-s.x),y:o.y+d*(r.y-s.y)}}}function rh(n,t,e){const i=n.length;let s,o,r,a,l,c=ze(n,0);for(let u=0;u!c.skip)),t.cubicInterpolationMode==="monotone")lh(n,s);else{let c=i?n[n.length-1]:n[0];for(o=0,r=n.length;on.ownerDocument.defaultView.getComputedStyle(n,null);function hh(n,t){return mi(n).getPropertyValue(t)}const dh=["top","right","bottom","left"];function we(n,t,e){const i={};e=e?"-"+e:"";for(let s=0;s<4;s++){const o=dh[s];i[o]=parseFloat(n[t+"-"+o+e])||0}return i.width=i.left+i.right,i.height=i.top+i.bottom,i}const fh=(n,t,e)=>(n>0||t>0)&&(!e||!e.shadowRoot);function gh(n,t){const e=n.touches,i=e&&e.length?e[0]:n,{offsetX:s,offsetY:o}=i;let r=!1,a,l;if(fh(s,o,n.target))a=s,l=o;else{const c=t.getBoundingClientRect();a=i.clientX-c.left,l=i.clientY-c.top,r=!0}return{x:a,y:l,box:r}}function be(n,t){if("native"in n)return n;const{canvas:e,currentDevicePixelRatio:i}=t,s=mi(e),o=s.boxSizing==="border-box",r=we(s,"padding"),a=we(s,"border","width"),{x:l,y:c,box:u}=gh(n,e),h=r.left+(u&&a.left),d=r.top+(u&&a.top);let{width:f,height:g}=t;return o&&(f-=r.width+a.width,g-=r.height+a.height),{x:Math.round((l-h)/f*e.width/i),y:Math.round((c-d)/g*e.height/i)}}function ph(n,t,e){let i,s;if(t===void 0||e===void 0){const o=Ms(n);if(!o)t=n.clientWidth,e=n.clientHeight;else{const r=o.getBoundingClientRect(),a=mi(o),l=we(a,"border","width"),c=we(a,"padding");t=r.width-c.width-l.width,e=r.height-c.height-l.height,i=ni(a.maxWidth,o,"clientWidth"),s=ni(a.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:i||ti,maxHeight:s||ti}}const On=n=>Math.round(n*10)/10;function mh(n,t,e,i){const s=mi(n),o=we(s,"margin"),r=ni(s.maxWidth,n,"clientWidth")||ti,a=ni(s.maxHeight,n,"clientHeight")||ti,l=ph(n,t,e);let{width:c,height:u}=l;if(s.boxSizing==="content-box"){const d=we(s,"border","width"),f=we(s,"padding");c-=f.width+d.width,u-=f.height+d.height}return c=Math.max(0,c-o.width),u=Math.max(0,i?c/i:u-o.height),c=On(Math.min(c,r,l.maxWidth)),u=On(Math.min(u,a,l.maxHeight)),c&&!u&&(u=On(c/2)),(t!==void 0||e!==void 0)&&i&&l.height&&u>l.height&&(u=l.height,c=On(Math.floor(u*i))),{width:c,height:u}}function no(n,t,e){const i=t||1,s=Math.floor(n.height*i),o=Math.floor(n.width*i);n.height=Math.floor(n.height),n.width=Math.floor(n.width);const r=n.canvas;return r.style&&(e||!r.style.height&&!r.style.width)&&(r.style.height=`${n.height}px`,r.style.width=`${n.width}px`),n.currentDevicePixelRatio!==i||r.height!==s||r.width!==o?(n.currentDevicePixelRatio=i,r.height=s,r.width=o,n.ctx.setTransform(i,0,0,i,0,0),!0):!1}const bh=function(){let n=!1;try{const t={get passive(){return n=!0,!1}};ws()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return n}();function io(n,t){const e=hh(n,t),i=e&&e.match(/^(\d+)(\.\d+)?px$/);return i?+i[1]:void 0}function ye(n,t,e,i){return{x:n.x+e*(t.x-n.x),y:n.y+e*(t.y-n.y)}}function yh(n,t,e,i){return{x:n.x+e*(t.x-n.x),y:i==="middle"?e<.5?n.y:t.y:i==="after"?e<1?n.y:t.y:e>0?t.y:n.y}}function _h(n,t,e,i){const s={x:n.cp2x,y:n.cp2y},o={x:t.cp1x,y:t.cp1y},r=ye(n,s,e),a=ye(s,o,e),l=ye(o,t,e),c=ye(r,a,e),u=ye(a,l,e);return ye(c,u,e)}const xh=function(n,t){return{x(e){return n+n+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,i){return e-i},leftForLtr(e,i){return e-i}}},vh=function(){return{x(n){return n},setWidth(n){},textAlign(n){return n},xPlus(n,t){return n+t},leftForLtr(n,t){return n}}};function Fe(n,t,e){return n?xh(t,e):vh()}function Gr(n,t){let e,i;(t==="ltr"||t==="rtl")&&(e=n.canvas.style,i=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),n.prevTextDirection=i)}function Qr(n,t){t!==void 0&&(delete n.prevTextDirection,n.canvas.style.setProperty("direction",t[0],t[1]))}function Zr(n){return n==="angle"?{between:hn,compare:_u,normalize:Dt}:{between:qt,compare:(t,e)=>t-e,normalize:t=>t}}function so({start:n,end:t,count:e,loop:i,style:s}){return{start:n%e,end:t%e,loop:i&&(t-n+1)%e===0,style:s}}function wh(n,t,e){const{property:i,start:s,end:o}=e,{between:r,normalize:a}=Zr(i),l=t.length;let{start:c,end:u,loop:h}=n,d,f;if(h){for(c+=l,u+=l,d=0,f=l;dl(s,x,b)&&a(s,x)!==0,_=()=>a(o,b)===0||l(o,x,b),w=()=>p||v(),C=()=>!p||_();for(let M=u,T=u;M<=h;++M)y=t[M%r],!y.skip&&(b=c(y[i]),b!==x&&(p=l(b,s,o),m===null&&w()&&(m=a(b,s)===0?M:T),m!==null&&C()&&(g.push(so({start:m,end:M,loop:d,count:r,style:f})),m=null),T=M,x=b));return m!==null&&g.push(so({start:m,end:h,loop:d,count:r,style:f})),g}function ta(n,t){const e=[],i=n.segments;for(let s=0;ss&&n[o%t].skip;)o--;return o%=t,{start:s,end:o}}function kh(n,t,e,i){const s=n.length,o=[];let r=t,a=n[t],l;for(l=t+1;l<=e;++l){const c=n[l%s];c.skip||c.stop?a.skip||(i=!1,o.push({start:t%s,end:(l-1)%s,loop:i}),t=r=c.stop?l:null):(r=l,a.skip&&(t=l)),a=c}return r!==null&&o.push({start:t%s,end:r%s,loop:i}),o}function Ch(n,t){const e=n.points,i=n.options.spanGaps,s=e.length;if(!s)return[];const o=!!n._loop,{start:r,end:a}=Mh(e,s,o,i);if(i===!0)return oo(n,[{start:r,end:a,loop:o}],e,t);const l=aa({chart:t,initial:e.initial,numSteps:r,currentStep:Math.min(i-e.start,r)}))}_refresh(){this._request||(this._running=!0,this._request=Br.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((i,s)=>{if(!i.running||!i.items.length)return;const o=i.items;let r=o.length-1,a=!1,l;for(;r>=0;--r)l=o[r],l._active?(l._total>i.duration&&(i.duration=l._total),l.tick(t),a=!0):(o[r]=o[o.length-1],o.pop());a&&(s.draw(),this._notify(s,i,t,"progress")),o.length||(i.running=!1,this._notify(s,i,t,"complete"),i.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((i,s)=>Math.max(i,s._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var Yt=new Th;const ro="transparent",Sh={boolean(n,t,e){return e>.5?t:n},color(n,t,e){const i=Ae(n||ro),s=i.valid&&Ae(t||ro);return s&&s.valid?s.mix(i,e).hexString():t},number(n,t,e){return n+(t-n)*e}};class Oh{constructor(t,e,i,s){const o=e[i];s=Tn([t.to,s,o,t.from]);const r=Tn([t.from,o,s]);this._active=!0,this._fn=t.fn||Sh[t.type||typeof r],this._easing=sn[t.easing]||sn.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=r,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],o=i-this._start,r=this._duration-o;this._start=i,this._duration=Math.floor(Math.max(r,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=Tn([t.to,e,s,t.from]),this._from=Tn([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,o=this._from,r=this._loop,a=this._to;let l;if(this._active=o!==a&&(r||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[s]=this._fn(o,a,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,i)=>{t.push({res:e,rej:i})})}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let s=0;s{const o=t[s];if(!F(o))return;const r={};for(const a of e)r[a]=o[a];(K(o.properties)&&o.properties||[s]).forEach(a=>{(a===s||!i.has(a))&&i.set(a,r)})})}_animateOptions(t,e){const i=e.options,s=Lh(t,i);if(!s)return[];const o=this._createAnimations(s,i);return i.$shared&&Ah(t.options.$animations,i).then(()=>{t.options=i},()=>{}),o}_createAnimations(t,e){const i=this._properties,s=[],o=t.$animations||(t.$animations={}),r=Object.keys(e),a=Date.now();let l;for(l=r.length-1;l>=0;--l){const c=r[l];if(c.charAt(0)==="$")continue;if(c==="options"){s.push(...this._animateOptions(t,e));continue}const u=e[c];let h=o[c];const d=i.get(c);if(h)if(d&&h.active()){h.update(d,u,a);continue}else h.cancel();if(!d||!d.duration){t[c]=u;continue}o[c]=h=new Oh(d,t,c,u),s.push(h)}return s}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const i=this._createAnimations(t,e);if(i.length)return Yt.add(this._chart,i),!0}}function Ah(n,t){const e=[],i=Object.keys(t);for(let s=0;s0||!e&&o<0)return s.index}return null}function ho(n,t){const{chart:e,_cachedMeta:i}=n,s=e._stacks||(e._stacks={}),{iScale:o,vScale:r,index:a}=i,l=o.axis,c=r.axis,u=Eh(o,r,i),h=t.length;let d;for(let f=0;fe[i].axis===t).shift()}function Nh(n,t){return he(n,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function Wh(n,t,e){return he(n,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Ne(n,t){const e=n.controller.index,i=n.vScale&&n.vScale.axis;if(i){t=t||n._parsed;for(const s of t){const o=s._stacks;if(!o||o[i]===void 0||o[i][e]===void 0)return;delete o[i][e],o[i]._visualValues!==void 0&&o[i]._visualValues[e]!==void 0&&delete o[i]._visualValues[e]}}}const Pi=n=>n==="reset"||n==="none",fo=(n,t)=>t?n:Object.assign({},n),Hh=(n,t,e)=>n&&!t.hidden&&t._stacked&&{keys:na(e,!0),values:null};class Xt{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=co(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ne(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(h,d,f,g)=>h==="x"?d:h==="r"?g:f,o=e.xAxisID=S(i.xAxisID,Ci(t,"x")),r=e.yAxisID=S(i.yAxisID,Ci(t,"y")),a=e.rAxisID=S(i.rAxisID,Ci(t,"r")),l=e.indexAxis,c=e.iAxisID=s(l,o,r,a),u=e.vAxisID=s(l,r,o,a);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(r),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(u)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&Xs(this._data,this),t._stacked&&Ne(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(F(e))this._data=Ih(e);else if(i!==e){if(i){Xs(i,this);const s=this._cachedMeta;Ne(s),s._parsed=[]}e&&Object.isExtensible(e)&&Mu(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,i=this.getDataset();let s=!1;this._dataCheck();const o=e._stacked;e._stacked=co(e.vScale,e),e.stack!==i.stack&&(s=!0,Ne(e),e.stack=i.stack),this._resyncElements(t),(s||o!==e._stacked)&&ho(this,e._parsed)}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),i=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(i,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:i,_data:s}=this,{iScale:o,_stacked:r}=i,a=o.axis;let l=t===0&&e===s.length?!0:i._sorted,c=t>0&&i._parsed[t-1],u,h,d;if(this._parsing===!1)i._parsed=s,i._sorted=!0,d=s;else{K(s[t])?d=this.parseArrayData(i,s,t,e):F(s[t])?d=this.parseObjectData(i,s,t,e):d=this.parsePrimitiveData(i,s,t,e);const f=()=>h[a]===null||c&&h[a]p||h=0;--d)if(!g()){this.updateRangeFromParsed(c,t,f,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,o,r;for(s=0,o=e.length;s=0&&tthis.getContext(i,s,e),p=c.resolveNamedOptions(d,f,g,h);return p.$shared&&(p.$shared=l,o[r]=Object.freeze(fo(p,l))),p}_resolveAnimations(t,e,i){const s=this.chart,o=this._cachedDataOpts,r=`animation-${e}`,a=o[r];if(a)return a;let l;if(s.options.animation!==!1){const u=this.chart.config,h=u.datasetAnimationScopeKeys(this._type,e),d=u.getOptionScopes(this.getDataset(),h);l=u.createResolver(d,this.getContext(t,i,e))}const c=new ea(s,l&&l.animations);return l&&l._cacheable&&(o[r]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||Pi(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,o=this.getSharedOptions(i),r=this.includeOptions(e,o)||o!==s;return this.updateSharedOptions(o,e,i),{sharedOptions:o,includeOptions:r}}updateElement(t,e,i,s){Pi(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!Pi(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const o=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const s=i.length,o=e.length,r=Math.min(o,s);r&&this.parse(0,r),o>s?this._insertElements(s,o-s,t):o{for(c.length+=e,a=c.length-1;a>=r;a--)c[a]=c[a-e]};for(l(o),a=t;as-o))}return n._cache.$bar}function Yh(n){const t=n.iScale,e=Vh(t,n.type);let i=t._length,s,o,r,a;const l=()=>{r===32767||r===-32768||(cn(a)&&(i=Math.min(i,Math.abs(r-a)||i)),a=r)};for(s=0,o=e.length;s0?s[n-1]:null,a=nMath.abs(a)&&(l=a,c=r),t[e.axis]=c,t._custom={barStart:l,barEnd:c,start:s,end:o,min:r,max:a}}function ia(n,t,e,i){return K(n)?$h(n,t,e,i):t[e.axis]=e.parse(n,i),t}function go(n,t,e,i){const s=n.iScale,o=n.vScale,r=s.getLabels(),a=s===o,l=[];let c,u,h,d;for(c=e,u=e+i;c=e?1:-1)}function Xh(n){let t,e,i,s,o;return n.horizontal?(t=n.base>n.x,e="left",i="right"):(t=n.basel.controller.options.grouped),o=i.options.stacked,r=[],a=l=>{const c=l.controller.getParsed(e),u=c&&c[l.vScale.axis];if(H(u)||isNaN(u))return!0};for(const l of s)if(!(e!==void 0&&a(l))&&((o===!1||r.indexOf(l.stack)===-1||o===void 0&&l.stack===void 0)&&r.push(l.stack),l.index===t))break;return r.length||r.push(void 0),r}_getStackCount(t){return this._getStacks(void 0,t).length}_getStackIndex(t,e,i){const s=this._getStacks(t,i),o=e!==void 0?s.indexOf(e):-1;return o===-1?s.length-1:o}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let o,r;for(o=0,r=e.data.length;ohn(x,a,l,!0)?1:Math.max(v,v*e,_,_*e),g=(x,v,_)=>hn(x,a,l,!0)?-1:Math.min(v,v*e,_,_*e),p=f(0,c,h),m=f(at,u,d),b=g(tt,c,h),y=g(tt+at,u,d);i=(p-b)/2,s=(m-y)/2,o=-(p+b)/2,r=-(m+y)/2}return{ratioX:i,ratioY:s,offsetX:o,offsetY:r}}class Le extends Xt{constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(this._parsing===!1)s._parsed=i;else{let o=l=>+i[l];if(F(i[t])){const{key:l="value"}=this._parsing;o=c=>+ce(i[c],l)}let r,a;for(r=t,a=t+e;r0&&!isNaN(t)?G*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],o=_n(e._parsed[t],i.options.locale);return{label:s[t]||"",value:o}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,o,r,a,l;if(!t){for(s=0,o=i.data.datasets.length;st!=="spacing",_indexable:t=>t!=="spacing"&&!t.startsWith("borderDash")&&!t.startsWith("hoverBorderDash")}),k(Le,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map((o,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:o,fillStyle:l.backgroundColor,strokeStyle:l.borderColor,fontColor:s,lineWidth:l.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(r),index:r}})}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}});class jn extends Xt{initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:o}=e,r=this.chart._animationsDisabled;let{start:a,count:l}=Pu(e,s,r);this._drawStart=a,this._drawCount=l,Du(e)&&(a=0,l=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!o._decimated,i.points=s;const c=this.resolveDatasetElementOptions(t);this.options.showLine||(c.borderWidth=0),c.segment=this.options.segment,this.updateElement(i,void 0,{animated:!r,options:c},t),this.updateElements(s,a,l,t)}updateElements(t,e,i,s){const o=s==="reset",{iScale:r,vScale:a,_stacked:l,_dataset:c}=this._cachedMeta,{sharedOptions:u,includeOptions:h}=this._getSharedOptions(e,s),d=r.axis,f=a.axis,{spanGaps:g,segment:p}=this.options,m=un(g)?g:Number.POSITIVE_INFINITY,b=this.chart._animationsDisabled||o||s==="none",y=e+i,x=t.length;let v=e>0&&this.getParsed(e-1);for(let _=0;_=y){C.skip=!0;continue}const M=this.getParsed(_),T=H(M[f]),A=C[d]=r.getPixelForValue(M[d],_),O=C[f]=o||T?a.getBasePixel():a.getPixelForValue(l?this.applyStack(a,M,l):M[f],_);C.skip=isNaN(A)||isNaN(O)||T,C.stop=_>0&&Math.abs(M[d]-v[d])>m,p&&(C.parsed=M,C.raw=c.data[_]),h&&(C.options=u||this.resolveDataElementOptions(_,w.active?"active":s)),b||this.updateElement(w,_,C,s),v=M}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const o=s[0].size(this.resolveDataElementOptions(0)),r=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,o,r)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}k(jn,"id","line"),k(jn,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),k(jn,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});class Un extends Xt{constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],o=_n(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:o}}parseObjectData(t,e,i,s){return ih.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach((i,s)=>{const o=this.getParsed(s).r;!isNaN(o)&&this.chart.getDataVisibility(s)&&(oe.max&&(e.max=o))}),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),o=Math.max(s/2,0),r=Math.max(i.cutoutPercentage?o/100*i.cutoutPercentage:1,0),a=(o-r)/t.getVisibleDatasetCount();this.outerRadius=o-a*this.index,this.innerRadius=this.outerRadius-a}updateElements(t,e,i,s){const o=s==="reset",r=this.chart,l=r.options.animation,c=this._cachedMeta.rScale,u=c.xCenter,h=c.yCenter,d=c.getIndexAngle(0)-.5*tt;let f=d,g;const p=360/this.countVisibleElements();for(g=0;g{!isNaN(this.getParsed(s).r)&&this.chart.getDataVisibility(s)&&e++}),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?At(this.resolveDataElementOptions(t,e).angle||i):0}}k(Un,"id","polarArea"),k(Un,"defaults",{dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0}),k(Un,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map((o,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:o,fillStyle:l.backgroundColor,strokeStyle:l.borderColor,fontColor:s,lineWidth:l.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(r),index:r}})}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}});class Qi extends Le{}k(Qi,"id","pie"),k(Qi,"defaults",{cutout:0,rotation:0,circumference:360,radius:"100%"});function ge(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class Ms{constructor(t){k(this,"options");this.options=t||{}}static override(t){Object.assign(Ms.prototype,t)}init(){}formats(){return ge()}parse(){return ge()}format(){return ge()}add(){return ge()}diff(){return ge()}startOf(){return ge()}endOf(){return ge()}}var sa={_date:Ms};function Jh(n,t,e,i){const{controller:s,data:o,_sorted:r}=n,a=s._cachedMeta.iScale;if(a&&t===a.axis&&t!=="r"&&r&&o.length){const l=a._reversePixels?vu:xe;if(i){if(s._sharedOptions){const c=o[0],u=typeof c.getRange=="function"&&c.getRange(t);if(u){const h=l(o,t,e-u),d=l(o,t,e+u);return{lo:h.lo,hi:d.hi}}}}else return l(o,t,e)}return{lo:0,hi:o.length-1}}function xn(n,t,e,i,s){const o=n.getSortedVisibleDatasetMetas(),r=e[t];for(let a=0,l=o.length;a{l[r](t[e],s)&&(o.push({element:l,datasetIndex:c,index:u}),a=a||l.inRange(t.x,t.y,s))}),i&&!a?[]:o}var id={evaluateInteractionItems:xn,modes:{index(n,t,e,i){const s=be(t,n),o=e.axis||"x",r=e.includeInvisible||!1,a=e.intersect?Ti(n,s,o,i,r):Si(n,s,o,!1,i,r),l=[];return a.length?(n.getSortedVisibleDatasetMetas().forEach(c=>{const u=a[0].index,h=c.data[u];h&&!h.skip&&l.push({element:h,datasetIndex:c.index,index:u})}),l):[]},dataset(n,t,e,i){const s=be(t,n),o=e.axis||"xy",r=e.includeInvisible||!1;let a=e.intersect?Ti(n,s,o,i,r):Si(n,s,o,!1,i,r);if(a.length>0){const l=a[0].datasetIndex,c=n.getDatasetMeta(l).data;a=[];for(let u=0;ue.pos===t)}function yo(n,t){return n.filter(e=>oa.indexOf(e.pos)===-1&&e.box.axis===t)}function He(n,t){return n.sort((e,i)=>{const s=t?i:e,o=t?e:i;return s.weight===o.weight?s.index-o.index:s.weight-o.weight})}function sd(n){const t=[];let e,i,s,o,r,a;for(e=0,i=(n||[]).length;ec.box.fullSize),!0),i=He(We(t,"left"),!0),s=He(We(t,"right")),o=He(We(t,"top"),!0),r=He(We(t,"bottom")),a=yo(t,"x"),l=yo(t,"y");return{fullSize:e,leftAndTop:i.concat(o),rightAndBottom:s.concat(l).concat(r).concat(a),chartArea:We(t,"chartArea"),vertical:i.concat(s).concat(l),horizontal:o.concat(r).concat(a)}}function _o(n,t,e,i){return Math.max(n[e],t[e])+Math.max(n[i],t[i])}function ra(n,t){n.top=Math.max(n.top,t.top),n.left=Math.max(n.left,t.left),n.bottom=Math.max(n.bottom,t.bottom),n.right=Math.max(n.right,t.right)}function ld(n,t,e,i){const{pos:s,box:o}=e,r=n.maxPadding;if(!F(s)){e.size&&(n[s]-=e.size);const h=i[e.stack]||{size:0,count:1};h.size=Math.max(h.size,e.horizontal?o.height:o.width),e.size=h.size/h.count,n[s]+=e.size}o.getPadding&&ra(r,o.getPadding());const a=Math.max(0,t.outerWidth-_o(r,n,"left","right")),l=Math.max(0,t.outerHeight-_o(r,n,"top","bottom")),c=a!==n.w,u=l!==n.h;return n.w=a,n.h=l,e.horizontal?{same:c,other:u}:{same:u,other:c}}function cd(n){const t=n.maxPadding;function e(i){const s=Math.max(t[i]-n[i],0);return n[i]+=s,s}n.y+=e("top"),n.x+=e("left"),e("right"),e("bottom")}function ud(n,t){const e=t.maxPadding;function i(s){const o={left:0,top:0,right:0,bottom:0};return s.forEach(r=>{o[r]=Math.max(t[r],e[r])}),o}return i(n?["left","right"]:["top","bottom"])}function Qe(n,t,e,i){const s=[];let o,r,a,l,c,u;for(o=0,r=n.length,c=0;o{typeof p.beforeLayout=="function"&&p.beforeLayout()});const u=l.reduce((p,m)=>m.box.options&&m.box.options.display===!1?p:p+1,0)||1,h=Object.freeze({outerWidth:t,outerHeight:e,padding:s,availableWidth:o,availableHeight:r,vBoxMaxWidth:o/2/u,hBoxMaxHeight:r/2}),d=Object.assign({},s);ra(d,yt(i));const f=Object.assign({maxPadding:d,w:o,h:r,x:s.left,y:s.top},s),g=rd(l.concat(c),h);Qe(a.fullSize,f,h,g),Qe(l,f,h,g),Qe(c,f,h,g)&&Qe(l,f,h,g),cd(f),xo(a.leftAndTop,f,h,g),f.x+=f.w,f.y+=f.h,xo(a.rightAndBottom,f,h,g),n.chartArea={left:f.left,top:f.top,right:f.left+f.w,bottom:f.top+f.h,height:f.h,width:f.w},$(a.chartArea,p=>{const m=p.box;Object.assign(m,n.chartArea),m.update(f.w,f.h,{left:0,top:0,right:0,bottom:0})})}};class aa{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class hd extends aa{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const $n="$chartjs",dd={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},vo=n=>n===null||n==="";function fd(n,t){const e=n.style,i=n.getAttribute("height"),s=n.getAttribute("width");if(n[$n]={initial:{height:i,width:s,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",vo(s)){const o=no(n,"width");o!==void 0&&(n.width=o)}if(vo(i))if(n.style.height==="")n.height=n.width/(t||2);else{const o=no(n,"height");o!==void 0&&(n.height=o)}return n}const la=bh?{passive:!0}:!1;function gd(n,t,e){n.addEventListener(t,e,la)}function pd(n,t,e){n.canvas.removeEventListener(t,e,la)}function md(n,t){const e=dd[n.type]||n.type,{x:i,y:s}=be(n,t);return{type:e,chart:t,native:n,x:i!==void 0?i:null,y:s!==void 0?s:null}}function ii(n,t){for(const e of n)if(e===t||e.contains(t))return!0}function bd(n,t,e){const i=n.canvas,s=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ii(a.addedNodes,i),r=r&&!ii(a.removedNodes,i);r&&e()});return s.observe(document,{childList:!0,subtree:!0}),s}function yd(n,t,e){const i=n.canvas,s=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ii(a.removedNodes,i),r=r&&!ii(a.addedNodes,i);r&&e()});return s.observe(document,{childList:!0,subtree:!0}),s}const fn=new Map;let wo=0;function ca(){const n=window.devicePixelRatio;n!==wo&&(wo=n,fn.forEach((t,e)=>{e.currentDevicePixelRatio!==n&&t()}))}function _d(n,t){fn.size||window.addEventListener("resize",ca),fn.set(n,t)}function xd(n){fn.delete(n),fn.size||window.removeEventListener("resize",ca)}function vd(n,t,e){const i=n.canvas,s=i&&ws(i);if(!s)return;const o=Nr((a,l)=>{const c=s.clientWidth;e(a,l),c{const l=a[0],c=l.contentRect.width,u=l.contentRect.height;c===0&&u===0||o(c,u)});return r.observe(s),_d(n,o),r}function Oi(n,t,e){e&&e.disconnect(),t==="resize"&&xd(n)}function wd(n,t,e){const i=n.canvas,s=Nr(o=>{n.ctx!==null&&e(md(o,n))},n);return gd(i,t,s),s}class Md extends aa{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(fd(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e[$n])return!1;const i=e[$n].initial;["height","width"].forEach(o=>{const r=i[o];H(r)?e.removeAttribute(o):e.setAttribute(o,r)});const s=i.style||{};return Object.keys(s).forEach(o=>{e.style[o]=s[o]}),e.width=e.width,delete e[$n],!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),r={attach:bd,detach:yd,resize:vd}[e]||wd;s[e]=r(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:Oi,detach:Oi,resize:Oi}[e]||pd)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return mh(t,e,i,s)}isAttached(t){const e=ws(t);return!!(e&&e.isConnected)}}function kd(n){return!Kr()||typeof OffscreenCanvas<"u"&&n instanceof OffscreenCanvas?hd:Md}class Lt{constructor(){k(this,"x");k(this,"y");k(this,"active",!1);k(this,"options");k(this,"$animations")}tooltipPosition(t){const{x:e,y:i}=this.getProps(["x","y"],t);return{x:e,y:i}}hasValue(){return un(this.x)&&un(this.y)}getProps(t,e){const i=this.$animations;if(!e||!i)return this;const s={};return t.forEach(o=>{s[o]=i[o]&&i[o].active()?i[o]._to:this[o]}),s}}k(Lt,"defaults",{}),k(Lt,"defaultRoutes");function Cd(n,t){const e=n.options.ticks,i=Pd(n),s=Math.min(e.maxTicksLimit||i,i),o=e.major.enabled?Td(t):[],r=o.length,a=o[0],l=o[r-1],c=[];if(r>s)return Sd(t,c,o,r/s),c;const u=Dd(o,t,s);if(r>0){let h,d;const f=r>1?Math.round((l-a)/(r-1)):null;for(Ln(t,c,u,H(f)?0:a-f,a),h=0,d=r-1;hs)return l}return Math.max(s,1)}function Td(n){const t=[];let e,i;for(e=0,i=n.length;en==="left"?"right":n==="right"?"left":n,Mo=(n,t,e)=>t==="top"||t==="left"?n[t]+e:n[t]-e,ko=(n,t)=>Math.min(t||n,n);function Co(n,t){const e=[],i=n.length/t,s=n.length;let o=0;for(;or+a)))return l}function Rd(n,t){$(n,e=>{const i=e.gc,s=i.length/2;let o;if(s>t){for(o=0;oi?i:e,i=s&&e>i?e:i,{min:Pt(e,Pt(i,e)),max:Pt(i,Pt(e,i))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){X(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:o,ticks:r}=this.options,a=r.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=qu(this,o,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||i<=1||!this.isHorizontal()){this.labelRotation=s;return}const u=this._getLabelSizes(),h=u.widest.width,d=u.highest.height,f=ft(this.chart.width-h,0,this.maxWidth);a=t.offset?this.maxWidth/i:f/(i-1),h+6>a&&(a=f/(i-(t.offset?.5:1)),l=this.maxHeight-Ve(t.grid)-e.padding-Po(t.title,this.chart.options.font),c=Math.sqrt(h*h+d*d),r=ps(Math.min(Math.asin(ft((u.highest.height+6)/a,-1,1)),Math.asin(ft(l/c,-1,1))-Math.asin(ft(d/c,-1,1)))),r=Math.max(s,Math.min(o,r))),this.labelRotation=r}afterCalculateLabelRotation(){X(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){X(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:o}}=this,r=this._isVisible(),a=this.isHorizontal();if(r){const l=Po(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Ve(o)+l):(t.height=this.maxHeight,t.width=Ve(o)+l),i.display&&this.ticks.length){const{first:c,last:u,widest:h,highest:d}=this._getLabelSizes(),f=i.padding*2,g=At(this.labelRotation),p=Math.cos(g),m=Math.sin(g);if(a){const b=i.mirror?0:m*h.width+p*d.height;t.height=Math.min(this.maxHeight,t.height+b+f)}else{const b=i.mirror?0:p*h.width+m*d.height;t.width=Math.min(this.maxWidth,t.width+b+f)}this._calculatePadding(c,u,m,p)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:o,padding:r},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const u=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let d=0,f=0;l?c?(d=s*t.width,f=i*e.height):(d=i*t.height,f=s*e.width):o==="start"?f=e.width:o==="end"?d=t.width:o!=="inner"&&(d=t.width/2,f=e.width/2),this.paddingLeft=Math.max((d-u+r)*this.width/(this.width-u),0),this.paddingRight=Math.max((f-h+r)*this.width/(this.width-h),0)}else{let u=e.height/2,h=t.height/2;o==="start"?(u=0,h=t.height):o==="end"&&(u=e.height,h=0),this.paddingTop=u+r,this.paddingBottom=h+r}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){X(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,i;for(e=0,i=t.length;e({width:r[T]||0,height:a[T]||0});return{first:M(0),last:M(e-1),widest:M(w),highest:M(C),widths:r,heights:a}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return xu(this._alignToPixels?fe(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*s?a/i:l/s:l*s0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:o,position:r,border:a}=s,l=o.offset,c=this.isHorizontal(),h=this.ticks.length+(l?1:0),d=Ve(o),f=[],g=a.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,b=function(U){return fe(i,U,p)};let y,x,v,_,w,C,M,T,A,O,R,st;if(r==="top")y=b(this.bottom),C=this.bottom-d,T=y-m,O=b(t.top)+m,st=t.bottom;else if(r==="bottom")y=b(this.top),O=t.top,st=b(t.bottom)-m,C=y+m,T=this.top+d;else if(r==="left")y=b(this.right),w=this.right-d,M=y-m,A=b(t.left)+m,R=t.right;else if(r==="right")y=b(this.left),A=t.left,R=b(t.right)-m,w=y+m,M=this.left+d;else if(e==="x"){if(r==="center")y=b((t.top+t.bottom)/2+.5);else if(F(r)){const U=Object.keys(r)[0],Q=r[U];y=b(this.chart.scales[U].getPixelForValue(Q))}O=t.top,st=t.bottom,C=y+m,T=C+d}else if(e==="y"){if(r==="center")y=b((t.left+t.right)/2);else if(F(r)){const U=Object.keys(r)[0],Q=r[U];y=b(this.chart.scales[U].getPixelForValue(Q))}w=y-m,M=w-d,A=t.left,R=t.right}const gt=S(s.ticks.maxTicksLimit,h),W=Math.max(1,Math.ceil(h/gt));for(x=0;xo.value===t);return s>=0?e.setContext(this.getContext(s)).lineWidth:0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,r;const a=(l,c,u)=>{!u.width||!u.color||(i.save(),i.lineWidth=u.width,i.strokeStyle=u.color,i.setLineDash(u.borderDash||[]),i.lineDashOffset=u.borderDashOffset,i.beginPath(),i.moveTo(l.x,l.y),i.lineTo(c.x,c.y),i.stroke(),i.restore())};if(e.display)for(o=0,r=s.length;o{this.draw(o)}}]:[{z:i,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let o,r;for(o=0,r=e.length;o{const i=e.split("."),s=i.pop(),o=[n].concat(i).join("."),r=t[e].split("."),a=r.pop(),l=r.join(".");nt.route(o,s,l,a)})}function Wd(n){return"id"in n&&"defaults"in n}class Hd{constructor(){this.controllers=new Rn(Xt,"datasets",!0),this.elements=new Rn(Lt,"elements"),this.plugins=new Rn(Object,"plugins"),this.scales=new Rn(ke,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,i){[...e].forEach(s=>{const o=i||this._getRegistryForType(s);i||o.isForType(s)||o===this.plugins&&s.id?this._exec(t,o,s):$(s,r=>{const a=i||this._getRegistryForType(r);this._exec(t,a,r)})})}_exec(t,e,i){const s=gs(t);X(i["before"+s],[],i),e[t](i),X(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;eo.filter(a=>!r.some(l=>a.plugin.id===l.plugin.id));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function Yd(n){const t={},e=[],i=Object.keys(It.plugins.items);for(let o=0;o1&&Do(n[0].toLowerCase());if(i)return i}throw new Error(`Cannot determine type of '${n}' axis. Please provide 'axis' or 'position' option.`)}function To(n,t,e){if(e[t+"AxisID"]===n)return{axis:t}}function Gd(n,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(i=>i.xAxisID===n||i.yAxisID===n);if(e.length)return To(n,"x",e[0])||To(n,"y",e[0])}return{}}function Qd(n,t){const e=Me[n.type]||{scales:{}},i=t.scales||{},s=Zi(n.type,t),o=Object.create(null);return Object.keys(i).forEach(r=>{const a=i[r];if(!F(a))return console.error(`Invalid scale configuration for scale: ${r}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${r}`);const l=Ji(r,a,Gd(r,n),nt.scales[a.type]),c=Xd(l,s),u=e.scales||{};o[r]=en(Object.create(null),[{axis:l},a,u[l],u[c]])}),n.data.datasets.forEach(r=>{const a=r.type||n.type,l=r.indexAxis||Zi(a,t),u=(Me[a]||{}).scales||{};Object.keys(u).forEach(h=>{const d=qd(h,l),f=r[d+"AxisID"]||d;o[f]=o[f]||Object.create(null),en(o[f],[{axis:d},i[f],u[h]])})}),Object.keys(o).forEach(r=>{const a=o[r];en(a,[nt.scales[a.type],nt.scale])}),o}function ua(n){const t=n.options||(n.options={});t.plugins=S(t.plugins,{}),t.scales=Qd(n,t)}function ha(n){return n=n||{},n.datasets=n.datasets||[],n.labels=n.labels||[],n}function Zd(n){return n=n||{},n.data=ha(n.data),ua(n),n}const So=new Map,da=new Set;function Fn(n,t){let e=So.get(n);return e||(e=t(),So.set(n,e),da.add(e)),e}const Ye=(n,t,e)=>{const i=ce(t,e);i!==void 0&&n.add(i)};class Jd{constructor(t){this._config=Zd(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=ha(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),ua(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Fn(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return Fn(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return Fn(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,i=this.type;return Fn(`${i}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return(!s||e)&&(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:o}=this,r=this._cachedScopes(t,i),a=r.get(e);if(a)return a;const l=new Set;e.forEach(u=>{t&&(l.add(t),u.forEach(h=>Ye(l,t,h))),u.forEach(h=>Ye(l,s,h)),u.forEach(h=>Ye(l,Me[o]||{},h)),u.forEach(h=>Ye(l,nt,h)),u.forEach(h=>Ye(l,Ki,h))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),da.has(e)&&r.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,Me[e]||{},nt.datasets[e]||{},{type:e},nt,Ki]}resolveNamedOptions(t,e,i,s=[""]){const o={$shared:!0},{resolver:r,subPrefixes:a}=Oo(this._resolverCache,t,s);let l=r;if(ef(r,e)){o.$shared=!1,i=ue(i)?i():i;const c=this.createResolver(t,i,a);l=Ee(r,i,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,i=[""],s){const{resolver:o}=Oo(this._resolverCache,t,i);return F(e)?Ee(o,e,void 0,s):o}}function Oo(n,t,e){let i=n.get(t);i||(i=new Map,n.set(t,i));const s=e.join();let o=i.get(s);return o||(o={resolver:_s(t,e),subPrefixes:e.filter(a=>!a.toLowerCase().includes("hover"))},i.set(s,o)),o}const tf=n=>F(n)&&Object.getOwnPropertyNames(n).reduce((t,e)=>t||ue(n[e]),!1);function ef(n,t){const{isScriptable:e,isIndexable:i}=jr(n);for(const s of t){const o=e(s),r=i(s),a=(r||o)&&n[s];if(o&&(ue(a)||tf(a))||r&&K(a))return!0}return!1}var nf="4.4.0";const sf=["top","bottom","left","right","chartArea"];function Ao(n,t){return n==="top"||n==="bottom"||sf.indexOf(n)===-1&&t==="x"}function Lo(n,t){return function(e,i){return e[n]===i[n]?e[t]-i[t]:e[n]-i[n]}}function Ro(n){const t=n.chart,e=t.options.animation;t.notifyPlugins("afterRender"),X(e&&e.onComplete,[n],t)}function of(n){const t=n.chart,e=t.options.animation;X(e&&e.onProgress,[n],t)}function fa(n){return Kr()&&typeof n=="string"?n=document.getElementById(n):n&&n.length&&(n=n[0]),n&&n.canvas&&(n=n.canvas),n}const qn={},Fo=n=>{const t=fa(n);return Object.values(qn).filter(e=>e.canvas===t).pop()};function rf(n,t,e){const i=Object.keys(n);for(const s of i){const o=+s;if(o>=t){const r=n[s];delete n[s],(e>0||o>t)&&(n[o+e]=r)}}}function af(n,t,e,i){return!e||n.type==="mouseout"?null:i?t:n}function In(n,t,e){return n.options.clip?n[e]:t[e]}function lf(n,t){const{xScale:e,yScale:i}=n;return e&&i?{left:In(e,t,"left"),right:In(e,t,"right"),top:In(i,t,"top"),bottom:In(i,t,"bottom")}:t}class mt{static register(...t){It.add(...t),Io()}static unregister(...t){It.remove(...t),Io()}constructor(t,e){const i=this.config=new Jd(e),s=fa(t),o=Fo(s);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const r=i.createResolver(i.chartOptionScopes(),this.getContext());this.platform=new(i.platform||kd(s)),this.platform.updateConfig(i);const a=this.platform.acquireContext(s,r.aspectRatio),l=a&&a.canvas,c=l&&l.height,u=l&&l.width;if(this.id=lu(),this.ctx=a,this.canvas=l,this.width=u,this.height=c,this._options=r,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new Vd,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=ku(h=>this.update(h),r.resizeDelay||0),this._dataChanges=[],qn[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}Yt.listen(this,"complete",Ro),Yt.listen(this,"progress",of),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:s,_aspectRatio:o}=this;return H(t)?e&&o?o:s?i/s:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return It}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():eo(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Zs(this.canvas,this.ctx),this}stop(){return Yt.stop(this),this}resize(t,e){Yt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,o=i.maintainAspectRatio&&this.aspectRatio,r=this.platform.getMaximumSize(s,t,e,o),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=r.width,this.height=r.height,this._aspectRatio=this.aspectRatio,eo(this,a,!0)&&(this.notifyPlugins("resize",{size:r}),X(i.onResize,[this,r],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};$(e,(i,s)=>{i.id=s})}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce((r,a)=>(r[a]=!1,r),{});let o=[];e&&(o=o.concat(Object.keys(e).map(r=>{const a=e[r],l=Ji(r,a),c=l==="r",u=l==="x";return{options:a,dposition:c?"chartArea":u?"bottom":"left",dtype:c?"radialLinear":u?"category":"linear"}}))),$(o,r=>{const a=r.options,l=a.id,c=Ji(l,a),u=S(a.type,r.dtype);(a.position===void 0||Ao(a.position,c)!==Ao(r.dposition))&&(a.position=r.dposition),s[l]=!0;let h=null;if(l in i&&i[l].type===u)h=i[l];else{const d=It.getScale(u);h=new d({id:l,type:u,ctx:this.ctx,chart:this}),i[h.id]=h}h.init(a,t)}),$(s,(r,a)=>{r||delete i[a]}),$(i,r=>{se.configure(this,r,r.options),se.addBox(this,r)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort((s,o)=>s.index-o.index),i>e){for(let s=e;se.length&&delete this._stacks,t.forEach((i,s)=>{e.filter(o=>o===i._dataset).length===0&&this._destroyDatasetMeta(s)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let r=0;for(let c=0,u=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(Lo("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){$(this.scales,t=>{se.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);(!js(e,i)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:o}of e){const r=i==="_removeElements"?-o:o;rf(t,s,r)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=o=>new Set(t.filter(r=>r[0]===o).map((r,a)=>a+","+r.splice(1).join(","))),s=i(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;se.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],$(this.boxes,s=>{i&&s.position==="chartArea"||(s.configure&&s.configure(),this._layers.push(...s._layers()))},this),this._layers.forEach((s,o)=>{s._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,i=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i=t._clip,s=!i.disabled,o=lf(t,this.chartArea),r={meta:t,index:t.index,cancelable:!0};this.notifyPlugins("beforeDatasetDraw",r)!==!1&&(s&&gi(e,{left:i.left===!1?0:o.left-i.left,right:i.right===!1?this.width:o.right+i.right,top:i.top===!1?0:o.top-i.top,bottom:i.bottom===!1?this.height:o.bottom+i.bottom}),t.controller.draw(),s&&pi(e),r.cancelable=!1,this.notifyPlugins("afterDatasetDraw",r))}isPointInArea(t){return qt(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const o=id.modes[e];return typeof o=="function"?o(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter(o=>o&&o._dataset===e).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=he(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return typeof i.hidden=="boolean"?!i.hidden:!e.hidden}setDatasetVisibility(t,e){const i=this.getDatasetMeta(t);i.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",o=this.getDatasetMeta(t),r=o.controller._resolveAnimations(void 0,s);cn(e)?(o.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),r.update(o,{visible:i}),this.update(a=>a.datasetIndex===t?s:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),Yt.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,r),t[o]=r},s=(o,r,a)=>{o.offsetX=r,o.offsetY=a,this._eventHandler(o)};$(this.options.events,o=>i(o,s))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},s=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let r;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",o),i("detach",r)};r=()=>{this.attached=!1,s("resize",o),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():r()}unbindEvents(){$(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},$(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let o,r,a,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+s+"DatasetHoverStyle"]()),a=0,l=t.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[r],index:r}});!Zn(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,i){const s=this.options.hover,o=(l,c)=>l.filter(u=>!c.some(h=>u.datasetIndex===h.datasetIndex&&u.index===h.index)),r=o(e,t),a=i?t:o(t,e);r.length&&this.updateHoverStyle(r,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=r=>(r.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",i,s)===!1)return;const o=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(o||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:o}=this,r=e,a=this._getActiveElements(t,s,i,r),l=gu(t),c=af(t,this._lastEvent,i,l);i&&(this._lastEvent=null,X(o.onHover,[t,a,this],this),l&&X(o.onClick,[t,a,this],this));const u=!Zn(a,s);return(u||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=c,u}_getActiveElements(t,e,i,s){if(t.type==="mouseout")return[];if(!i)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,s)}}k(mt,"defaults",nt),k(mt,"instances",qn),k(mt,"overrides",Me),k(mt,"registry",It),k(mt,"version",nf),k(mt,"getChart",Fo);function Io(){return $(mt.instances,n=>n._plugins.invalidate())}function cf(n,t,e){const{startAngle:i,pixelMargin:s,x:o,y:r,outerRadius:a,innerRadius:l}=t;let c=s/a;n.beginPath(),n.arc(o,r,a,i-c,e+c),l>s?(c=s/l,n.arc(o,r,l,e+c,i-c,!0)):n.arc(o,r,s,e+at,i-at),n.closePath(),n.clip()}function uf(n){return ys(n,["outerStart","outerEnd","innerStart","innerEnd"])}function hf(n,t,e,i){const s=uf(n.options.borderRadius),o=(e-t)/2,r=Math.min(o,i*t/2),a=l=>{const c=(e-Math.min(o,l))*i/2;return ft(l,0,Math.min(o,c))};return{outerStart:a(s.outerStart),outerEnd:a(s.outerEnd),innerStart:ft(s.innerStart,0,r),innerEnd:ft(s.innerEnd,0,r)}}function Pe(n,t,e,i){return{x:e+n*Math.cos(t),y:i+n*Math.sin(t)}}function si(n,t,e,i,s,o){const{x:r,y:a,startAngle:l,pixelMargin:c,innerRadius:u}=t,h=Math.max(t.outerRadius+i+e-c,0),d=u>0?u+i+e+c:0;let f=0;const g=s-l;if(i){const W=u>0?u-i:0,U=h>0?h-i:0,Q=(W+U)/2,Mt=Q!==0?g*Q/(Q+i):g;f=(g-Mt)/2}const p=Math.max(.001,g*h-e/tt)/h,m=(g-p)/2,b=l+m+f,y=s-m-f,{outerStart:x,outerEnd:v,innerStart:_,innerEnd:w}=hf(t,d,h,y-b),C=h-x,M=h-v,T=b+x/C,A=y-v/M,O=d+_,R=d+w,st=b+_/O,gt=y-w/R;if(n.beginPath(),o){const W=(T+A)/2;if(n.arc(r,a,h,T,W),n.arc(r,a,h,W,A),v>0){const lt=Pe(M,A,r,a);n.arc(lt.x,lt.y,v,A,y+at)}const U=Pe(R,y,r,a);if(n.lineTo(U.x,U.y),w>0){const lt=Pe(R,gt,r,a);n.arc(lt.x,lt.y,w,y+at,gt+Math.PI)}const Q=(y-w/d+(b+_/d))/2;if(n.arc(r,a,d,y-w/d,Q,!0),n.arc(r,a,d,Q,b+_/d,!0),_>0){const lt=Pe(O,st,r,a);n.arc(lt.x,lt.y,_,st+Math.PI,b-at)}const Mt=Pe(C,b,r,a);if(n.lineTo(Mt.x,Mt.y),x>0){const lt=Pe(C,T,r,a);n.arc(lt.x,lt.y,x,b-at,T)}}else{n.moveTo(r,a);const W=Math.cos(T)*h+r,U=Math.sin(T)*h+a;n.lineTo(W,U);const Q=Math.cos(A)*h+r,Mt=Math.sin(A)*h+a;n.lineTo(Q,Mt)}n.closePath()}function df(n,t,e,i,s){const{fullCircles:o,startAngle:r,circumference:a}=t;let l=t.endAngle;if(o){si(n,t,e,i,l,s);for(let c=0;c=G||hn(r,l,c),m=$t(a,u+f,h+f);return p&&m}getCenterPoint(e){const{x:i,y:s,startAngle:o,endAngle:r,innerRadius:a,outerRadius:l}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],e),{offset:c,spacing:u}=this.options,h=(o+r)/2,d=(a+l+u+c)/2;return{x:i+Math.cos(h)*d,y:s+Math.sin(h)*d}}tooltipPosition(e){return this.getCenterPoint(e)}draw(e){const{options:i,circumference:s}=this,o=(i.offset||0)/4,r=(i.spacing||0)/2,a=i.circular;if(this.pixelMargin=i.borderAlign==="inner"?.33:0,this.fullCircles=s>G?Math.floor(s/G):0,s===0||this.innerRadius<0||this.outerRadius<0)return;e.save();const l=(this.startAngle+this.endAngle)/2;e.translate(Math.cos(l)*o,Math.sin(l)*o);const c=1-Math.sin(Math.min(tt,s||0)),u=o*c;e.fillStyle=i.backgroundColor,e.strokeStyle=i.borderColor,df(e,this,u,r,a),ff(e,this,u,r,a),e.restore()}}k(Ze,"id","arc"),k(Ze,"defaults",{borderAlign:"center",borderColor:"#fff",borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0}),k(Ze,"defaultRoutes",{backgroundColor:"backgroundColor"}),k(Ze,"descriptors",{_scriptable:!0,_indexable:e=>e!=="borderDash"});function ga(n,t,e=t){n.lineCap=S(e.borderCapStyle,t.borderCapStyle),n.setLineDash(S(e.borderDash,t.borderDash)),n.lineDashOffset=S(e.borderDashOffset,t.borderDashOffset),n.lineJoin=S(e.borderJoinStyle,t.borderJoinStyle),n.lineWidth=S(e.borderWidth,t.borderWidth),n.strokeStyle=S(e.borderColor,t.borderColor)}function gf(n,t,e){n.lineTo(e.x,e.y)}function pf(n){return n.stepped?Bu:n.tension||n.cubicInterpolationMode==="monotone"?Nu:gf}function pa(n,t,e={}){const i=n.length,{start:s=0,end:o=i-1}=e,{start:r,end:a}=t,l=Math.max(s,r),c=Math.min(o,a),u=sa&&o>a;return{count:i,start:l,loop:t.loop,ilen:c(r+(c?a-v:v))%o,x=()=>{p!==m&&(n.lineTo(u,m),n.lineTo(u,p),n.lineTo(u,b))};for(l&&(f=s[y(0)],n.moveTo(f.x,f.y)),d=0;d<=a;++d){if(f=s[y(d)],f.skip)continue;const v=f.x,_=f.y,w=v|0;w===g?(_m&&(m=_),u=(h*u+v)/++h):(x(),n.lineTo(v,_),g=w,h=0,p=m=_),b=_}x()}function ts(n){const t=n.options,e=t.borderDash&&t.borderDash.length;return!n._decimated&&!n._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?bf:mf}function yf(n){return n.stepped?yh:n.tension||n.cubicInterpolationMode==="monotone"?_h:ye}function _f(n,t,e,i){let s=t._path;s||(s=t._path=new Path2D,t.path(s,e,i)&&s.closePath()),ga(n,t.options),n.stroke(s)}function xf(n,t,e,i){const{segments:s,options:o}=t,r=ts(t);for(const a of s)ga(n,o,a.style),n.beginPath(),r(n,t,a,{start:e,end:e+i-1})&&n.closePath(),n.stroke()}const vf=typeof Path2D=="function";function wf(n,t,e,i){vf&&!t.options.segment?_f(n,t,e,i):xf(n,t,e,i)}class oe extends Lt{constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||i.cubicInterpolationMode==="monotone")&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;uh(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=Ch(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],o=this.points,r=ta(this,{property:e,start:s,end:s});if(!r.length)return;const a=[],l=yf(i);let c,u;for(c=0,u=r.length;ct!=="borderDash"&&t!=="fill"});function Eo(n,t,e,i){const s=n.options,{[e]:o}=n.getProps([e],i);return Math.abs(t-o)n.replace("rgb(","rgba(").replace(")",", 0.5)"));function ba(n){return es[n%es.length]}function ya(n){return zo[n%zo.length]}function Tf(n,t){return n.borderColor=ba(t),n.backgroundColor=ya(t),++t}function Sf(n,t){return n.backgroundColor=n.data.map(()=>ba(t++)),t}function Of(n,t){return n.backgroundColor=n.data.map(()=>ya(t++)),t}function Af(n){let t=0;return(e,i)=>{const s=n.getDatasetMeta(i).controller;s instanceof Le?t=Sf(e,t):s instanceof Un?t=Of(e,t):s&&(t=Tf(e,t))}}function Bo(n){let t;for(t in n)if(n[t].borderColor||n[t].backgroundColor)return!0;return!1}function Lf(n){return n&&(n.borderColor||n.backgroundColor)}var Rf={id:"colors",defaults:{enabled:!0,forceOverride:!1},beforeLayout(n,t,e){if(!e.enabled)return;const{data:{datasets:i},options:s}=n.config,{elements:o}=s;if(!e.forceOverride&&(Bo(i)||Lf(s)||o&&Bo(o)))return;const r=Af(n);i.forEach(r)}};function Ff(n,t,e){const i=n.segments,s=n.points,o=t.points,r=[];for(const a of i){let{start:l,end:c}=a;c=ks(l,c,s);const u=ns(e,s[l],s[c],a.loop);if(!t.segments){r.push({source:a,target:u,start:s[l],end:s[c]});continue}const h=ta(t,u);for(const d of h){const f=ns(e,o[d.start],o[d.end],d.loop),g=Jr(a,s,f);for(const p of g)r.push({source:p,target:d,start:{[e]:No(u,f,"start",Math.max)},end:{[e]:No(u,f,"end",Math.min)}})}}return r}function ns(n,t,e,i){if(i)return;let s=t[n],o=e[n];return n==="angle"&&(s=Dt(s),o=Dt(o)),{property:n,start:s,end:o}}function If(n,t){const{x:e=null,y:i=null}=n||{},s=t.points,o=[];return t.segments.forEach(({start:r,end:a})=>{a=ks(r,a,s);const l=s[r],c=s[a];i!==null?(o.push({x:l.x,y:i}),o.push({x:c.x,y:i})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function ks(n,t,e){for(;t>n;t--){const i=e[t];if(!isNaN(i.x)&&!isNaN(i.y))break}return t}function No(n,t,e,i){return n&&t?i(n[e],t[e]):n?n[e]:t?t[e]:0}function _a(n,t){let e=[],i=!1;return K(n)?(i=!0,e=n):e=If(n,t),e.length?new oe({points:e,options:{tension:0},_loop:i,_fullLoop:i}):null}function Wo(n){return n&&n.fill!==!1}function Ef(n,t,e){let s=n[t].fill;const o=[t];let r;if(!e)return s;for(;s!==!1&&o.indexOf(s)===-1;){if(!et(s))return s;if(r=n[s],!r)return!1;if(r.visible)return s;o.push(s),s=r.fill}return!1}function zf(n,t,e){const i=Hf(n);if(F(i))return isNaN(i.value)?!1:i;let s=parseFloat(i);return et(s)&&Math.floor(s)===s?Bf(i[0],t,s,e):["origin","start","end","stack","shape"].indexOf(i)>=0&&i}function Bf(n,t,e,i){return(n==="-"||n==="+")&&(e=t+e),e===t||e<0||e>=i?!1:e}function Nf(n,t){let e=null;return n==="start"?e=t.bottom:n==="end"?e=t.top:F(n)?e=t.getPixelForValue(n.value):t.getBasePixel&&(e=t.getBasePixel()),e}function Wf(n,t,e){let i;return n==="start"?i=e:n==="end"?i=t.options.reverse?t.min:t.max:F(n)?i=n.value:i=t.getBaseValue(),i}function Hf(n){const t=n.options,e=t.fill;let i=S(e&&e.target,e);return i===void 0&&(i=!!t.backgroundColor),i===!1||i===null?!1:i===!0?"origin":i}function Vf(n){const{scale:t,index:e,line:i}=n,s=[],o=i.segments,r=i.points,a=Yf(t,e);a.push(_a({x:null,y:t.bottom},i));for(let l=0;l=0;--r){const a=s[r].$filler;a&&(a.line.updateControlPoints(o,a.axis),i&&a.fill&&Ri(n.ctx,a,o))}},beforeDatasetsDraw(n,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const i=n.getSortedVisibleDatasetMetas();for(let s=i.length-1;s>=0;--s){const o=i[s].$filler;Wo(o)&&Ri(n.ctx,o,n.chartArea)}},beforeDatasetDraw(n,t,e){const i=t.meta.$filler;!Wo(i)||e.drawTime!=="beforeDatasetDraw"||Ri(n.ctx,i,n.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const jo=(n,t)=>{let{boxHeight:e=t,boxWidth:i=t}=n;return n.usePointStyle&&(e=Math.min(e,t),i=n.pointStyleWidth||Math.min(i,t)),{boxWidth:i,boxHeight:e,itemHeight:Math.max(t,e)}},tg=(n,t)=>n!==null&&t!==null&&n.datasetIndex===t.datasetIndex&&n.index===t.index;class Uo extends Lt{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=X(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter(i=>t.filter(i,this.chart.data))),t.sort&&(e=e.sort((i,s)=>t.sort(i,s,this.chart.data))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display){this.width=this.height=0;return}const i=t.labels,s=ut(i.font),o=s.size,r=this._computeTitleHeight(),{boxWidth:a,itemHeight:l}=jo(i,o);let c,u;e.font=s.string,this.isHorizontal()?(c=this.maxWidth,u=this._fitRows(r,o,a,l)+10):(u=this.maxHeight,c=this._fitCols(r,s,a,l)+10),this.width=Math.min(c,t.maxWidth||this.maxWidth),this.height=Math.min(u,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:o,maxWidth:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],u=s+a;let h=t;o.textAlign="left",o.textBaseline="middle";let d=-1,f=-u;return this.legendItems.forEach((g,p)=>{const m=i+e/2+o.measureText(g.text).width;(p===0||c[c.length-1]+m+2*a>r)&&(h+=u,c[c.length-(p>0?0:1)]=0,f+=u,d++),l[p]={left:0,top:f,row:d,width:m,height:s},c[c.length-1]+=m+a}),h}_fitCols(t,e,i,s){const{ctx:o,maxHeight:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],u=r-t;let h=a,d=0,f=0,g=0,p=0;return this.legendItems.forEach((m,b)=>{const{itemWidth:y,itemHeight:x}=eg(i,e,o,m,s);b>0&&f+x+2*a>u&&(h+=d+a,c.push({width:d,height:f}),g+=d+a,p++,d=f=0),l[b]={left:g,top:f,col:p,width:y,height:x},d=Math.max(d,y),f+=x+a}),h+=d,c.push({width:d,height:f}),h}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:o}}=this,r=Fe(o,this.left,this.width);if(this.isHorizontal()){let a=0,l=kt(i,this.left+s,this.right-this.lineWidths[a]);for(const c of e)a!==c.row&&(a=c.row,l=kt(i,this.left+s,this.right-this.lineWidths[a])),c.top+=this.top+t+s,c.left=r.leftForLtr(r.x(l),c.width),l+=c.width+s}else{let a=0,l=kt(i,this.top+t+s,this.bottom-this.columnSizes[a].height);for(const c of e)c.col!==a&&(a=c.col,l=kt(i,this.top+t+s,this.bottom-this.columnSizes[a].height)),c.top=l,c.left+=this.left+s,c.left=r.leftForLtr(r.x(c.left),c.width),l+=c.height+s}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const t=this.ctx;gi(t,this),this._draw(),pi(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:o,labels:r}=t,a=nt.color,l=Fe(t.rtl,this.left,this.width),c=ut(r.font),{padding:u}=r,h=c.size,d=h/2;let f;this.drawTitle(),s.textAlign=l.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=c.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=jo(r,h),b=function(w,C,M){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const T=S(M.lineWidth,1);if(s.fillStyle=S(M.fillStyle,a),s.lineCap=S(M.lineCap,"butt"),s.lineDashOffset=S(M.lineDashOffset,0),s.lineJoin=S(M.lineJoin,"miter"),s.lineWidth=T,s.strokeStyle=S(M.strokeStyle,a),s.setLineDash(S(M.lineDash,[])),r.usePointStyle){const A={radius:p*Math.SQRT2/2,pointStyle:M.pointStyle,rotation:M.rotation,borderWidth:T},O=l.xPlus(w,g/2),R=C+d;Vr(s,A,O,R,r.pointStyleWidth&&g)}else{const A=C+Math.max((h-p)/2,0),O=l.leftForLtr(w,g),R=ve(M.borderRadius);s.beginPath(),Object.values(R).some(st=>st!==0)?dn(s,{x:O,y:A,w:g,h:p,radius:R}):s.rect(O,A,g,p),s.fill(),T!==0&&s.stroke()}s.restore()},y=function(w,C,M){Ie(s,M.text,w,C+m/2,c,{strikethrough:M.hidden,textAlign:l.textAlign(M.textAlign)})},x=this.isHorizontal(),v=this._computeTitleHeight();x?f={x:kt(o,this.left+u,this.right-i[0]),y:this.top+u+v,line:0}:f={x:this.left+u,y:kt(o,this.top+v+u,this.bottom-e[0].height),line:0},Gr(this.ctx,t.textDirection);const _=m+u;this.legendItems.forEach((w,C)=>{s.strokeStyle=w.fontColor,s.fillStyle=w.fontColor;const M=s.measureText(w.text).width,T=l.textAlign(w.textAlign||(w.textAlign=r.textAlign)),A=g+d+M;let O=f.x,R=f.y;l.setWidth(this.width),x?C>0&&O+A+u>this.right&&(R=f.y+=_,f.line++,O=f.x=kt(o,this.left+u,this.right-i[f.line])):C>0&&R+_>this.bottom&&(O=f.x=O+e[f.line].width+u,f.line++,R=f.y=kt(o,this.top+v+u,this.bottom-e[f.line].height));const st=l.x(O);if(b(st,R,w),O=Cu(T,O+g+d,x?O+A:this.right,t.rtl),y(l.x(O),R,w),x)f.x+=A+u;else if(typeof w.text!="string"){const gt=c.lineHeight;f.y+=va(w,gt)+u}else f.y+=_}),Qr(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=ut(e.font),s=yt(e.padding);if(!e.display)return;const o=Fe(t.rtl,this.left,this.width),r=this.ctx,a=e.position,l=i.size/2,c=s.top+l;let u,h=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),u=this.top+c,h=kt(t.align,h,this.right-d);else{const g=this.columnSizes.reduce((p,m)=>Math.max(p,m.height),0);u=c+kt(t.align,this.top,this.bottom-g-t.labels.padding-this._computeTitleHeight())}const f=kt(a,h,h+d);r.textAlign=o.textAlign(Wr(a)),r.textBaseline="middle",r.strokeStyle=e.color,r.fillStyle=e.color,r.font=i.string,Ie(r,e.text,f,u,i)}_computeTitleHeight(){const t=this.options.title,e=ut(t.font),i=yt(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,o;if($t(t,this.left,this.right)&&$t(e,this.top,this.bottom)){for(o=this.legendHitBoxes,i=0;io.length>r.length?o:r)),t+e.size/2+i.measureText(s).width}function ig(n,t,e){let i=n;return typeof t.text!="string"&&(i=va(t,e)),i}function va(n,t){const e=n.text?n.text.length:0;return t*e}function sg(n,t){return!!((n==="mousemove"||n==="mouseout")&&(t.onHover||t.onLeave)||t.onClick&&(n==="click"||n==="mouseup"))}var og={id:"legend",_element:Uo,start(n,t,e){const i=n.legend=new Uo({ctx:n.ctx,options:e,chart:n});se.configure(n,i,e),se.addBox(n,i)},stop(n){se.removeBox(n,n.legend),delete n.legend},beforeUpdate(n,t,e){const i=n.legend;se.configure(n,i,e),i.options=e},afterUpdate(n){const t=n.legend;t.buildLabels(),t.adjustHitBoxes()},afterEvent(n,t){t.replay||n.legend.handleEvent(t.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(n,t,e){const i=t.datasetIndex,s=e.chart;s.isDatasetVisible(i)?(s.hide(i),t.hidden=!0):(s.show(i),t.hidden=!1)},onHover:null,onLeave:null,labels:{color:n=>n.chart.options.color,boxWidth:40,padding:10,generateLabels(n){const t=n.data.datasets,{labels:{usePointStyle:e,pointStyle:i,textAlign:s,color:o,useBorderRadius:r,borderRadius:a}}=n.legend.options;return n._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(e?0:void 0),u=yt(c.borderWidth);return{text:t[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(u.width+u.height)/4,strokeStyle:c.borderColor,pointStyle:i||c.pointStyle,rotation:c.rotation,textAlign:s||c.textAlign,borderRadius:r&&(a||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:n=>n.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:n=>!n.startsWith("on"),labels:{_scriptable:n=>!["generateLabels","filter","sort"].includes(n)}}};const Je={average(n){if(!n.length)return!1;let t,e,i=0,s=0,o=0;for(t=0,e=n.length;ta({chart:t,initial:e.initial,numSteps:r,currentStep:Math.min(i-e.start,r)}))}_refresh(){this._request||(this._running=!0,this._request=Nr.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((i,s)=>{if(!i.running||!i.items.length)return;const o=i.items;let r=o.length-1,a=!1,l;for(;r>=0;--r)l=o[r],l._active?(l._total>i.duration&&(i.duration=l._total),l.tick(t),a=!0):(o[r]=o[o.length-1],o.pop());a&&(s.draw(),this._notify(s,i,t,"progress")),o.length||(i.running=!1,this._notify(s,i,t,"complete"),i.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((i,s)=>Math.max(i,s._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var jt=new Th;const ao="transparent",Sh={boolean(n,t,e){return e>.5?t:n},color(n,t,e){const i=Ae(n||ao),s=i.valid&&Ae(t||ao);return s&&s.valid?s.mix(i,e).hexString():t},number(n,t,e){return n+(t-n)*e}};class Oh{constructor(t,e,i,s){const o=e[i];s=Tn([t.to,s,o,t.from]);const r=Tn([t.from,o,s]);this._active=!0,this._fn=t.fn||Sh[t.type||typeof r],this._easing=sn[t.easing]||sn.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=r,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],o=i-this._start,r=this._duration-o;this._start=i,this._duration=Math.floor(Math.max(r,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=Tn([t.to,e,s,t.from]),this._from=Tn([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,o=this._from,r=this._loop,a=this._to;let l;if(this._active=o!==a&&(r||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[s]=this._fn(o,a,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,i)=>{t.push({res:e,rej:i})})}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let s=0;s{const o=t[s];if(!F(o))return;const r={};for(const a of e)r[a]=o[a];(K(o.properties)&&o.properties||[s]).forEach(a=>{(a===s||!i.has(a))&&i.set(a,r)})})}_animateOptions(t,e){const i=e.options,s=Lh(t,i);if(!s)return[];const o=this._createAnimations(s,i);return i.$shared&&Ah(t.options.$animations,i).then(()=>{t.options=i},()=>{}),o}_createAnimations(t,e){const i=this._properties,s=[],o=t.$animations||(t.$animations={}),r=Object.keys(e),a=Date.now();let l;for(l=r.length-1;l>=0;--l){const c=r[l];if(c.charAt(0)==="$")continue;if(c==="options"){s.push(...this._animateOptions(t,e));continue}const u=e[c];let h=o[c];const d=i.get(c);if(h)if(d&&h.active()){h.update(d,u,a);continue}else h.cancel();if(!d||!d.duration){t[c]=u;continue}o[c]=h=new Oh(d,t,c,u),s.push(h)}return s}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const i=this._createAnimations(t,e);if(i.length)return jt.add(this._chart,i),!0}}function Ah(n,t){const e=[],i=Object.keys(t);for(let s=0;s0||!e&&o<0)return s.index}return null}function fo(n,t){const{chart:e,_cachedMeta:i}=n,s=e._stacks||(e._stacks={}),{iScale:o,vScale:r,index:a}=i,l=o.axis,c=r.axis,u=Eh(o,r,i),h=t.length;let d;for(let f=0;fe[i].axis===t).shift()}function Nh(n,t){return he(n,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function Wh(n,t,e){return he(n,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Ne(n,t){const e=n.controller.index,i=n.vScale&&n.vScale.axis;if(i){t=t||n._parsed;for(const s of t){const o=s._stacks;if(!o||o[i]===void 0||o[i][e]===void 0)return;delete o[i][e],o[i]._visualValues!==void 0&&o[i]._visualValues[e]!==void 0&&delete o[i]._visualValues[e]}}}const Pi=n=>n==="reset"||n==="none",go=(n,t)=>t?n:Object.assign({},n),Hh=(n,t,e)=>n&&!t.hidden&&t._stacked&&{keys:na(e,!0),values:null};class Kt{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=uo(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ne(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(h,d,f,g)=>h==="x"?d:h==="r"?g:f,o=e.xAxisID=S(i.xAxisID,Ci(t,"x")),r=e.yAxisID=S(i.yAxisID,Ci(t,"y")),a=e.rAxisID=S(i.rAxisID,Ci(t,"r")),l=e.indexAxis,c=e.iAxisID=s(l,o,r,a),u=e.vAxisID=s(l,r,o,a);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(r),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(u)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&Ks(this._data,this),t._stacked&&Ne(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(F(e))this._data=Ih(e);else if(i!==e){if(i){Ks(i,this);const s=this._cachedMeta;Ne(s),s._parsed=[]}e&&Object.isExtensible(e)&&Mu(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,i=this.getDataset();let s=!1;this._dataCheck();const o=e._stacked;e._stacked=uo(e.vScale,e),e.stack!==i.stack&&(s=!0,Ne(e),e.stack=i.stack),this._resyncElements(t),(s||o!==e._stacked)&&fo(this,e._parsed)}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),i=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(i,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:i,_data:s}=this,{iScale:o,_stacked:r}=i,a=o.axis;let l=t===0&&e===s.length?!0:i._sorted,c=t>0&&i._parsed[t-1],u,h,d;if(this._parsing===!1)i._parsed=s,i._sorted=!0,d=s;else{K(s[t])?d=this.parseArrayData(i,s,t,e):F(s[t])?d=this.parseObjectData(i,s,t,e):d=this.parsePrimitiveData(i,s,t,e);const f=()=>h[a]===null||c&&h[a]p||h=0;--d)if(!g()){this.updateRangeFromParsed(c,t,f,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,o,r;for(s=0,o=e.length;s=0&&tthis.getContext(i,s,e),p=c.resolveNamedOptions(d,f,g,h);return p.$shared&&(p.$shared=l,o[r]=Object.freeze(go(p,l))),p}_resolveAnimations(t,e,i){const s=this.chart,o=this._cachedDataOpts,r=`animation-${e}`,a=o[r];if(a)return a;let l;if(s.options.animation!==!1){const u=this.chart.config,h=u.datasetAnimationScopeKeys(this._type,e),d=u.getOptionScopes(this.getDataset(),h);l=u.createResolver(d,this.getContext(t,i,e))}const c=new ea(s,l&&l.animations);return l&&l._cacheable&&(o[r]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||Pi(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,o=this.getSharedOptions(i),r=this.includeOptions(e,o)||o!==s;return this.updateSharedOptions(o,e,i),{sharedOptions:o,includeOptions:r}}updateElement(t,e,i,s){Pi(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!Pi(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const o=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const s=i.length,o=e.length,r=Math.min(o,s);r&&this.parse(0,r),o>s?this._insertElements(s,o-s,t):o{for(c.length+=e,a=c.length-1;a>=r;a--)c[a]=c[a-e]};for(l(o),a=t;as-o))}return n._cache.$bar}function Yh(n){const t=n.iScale,e=Vh(t,n.type);let i=t._length,s,o,r,a;const l=()=>{r===32767||r===-32768||(cn(a)&&(i=Math.min(i,Math.abs(r-a)||i)),a=r)};for(s=0,o=e.length;s0?s[n-1]:null,a=nMath.abs(a)&&(l=a,c=r),t[e.axis]=c,t._custom={barStart:l,barEnd:c,start:s,end:o,min:r,max:a}}function ia(n,t,e,i){return K(n)?$h(n,t,e,i):t[e.axis]=e.parse(n,i),t}function po(n,t,e,i){const s=n.iScale,o=n.vScale,r=s.getLabels(),a=s===o,l=[];let c,u,h,d;for(c=e,u=e+i;c=e?1:-1)}function Xh(n){let t,e,i,s,o;return n.horizontal?(t=n.base>n.x,e="left",i="right"):(t=n.basel.controller.options.grouped),o=i.options.stacked,r=[],a=l=>{const c=l.controller.getParsed(e),u=c&&c[l.vScale.axis];if(H(u)||isNaN(u))return!0};for(const l of s)if(!(e!==void 0&&a(l))&&((o===!1||r.indexOf(l.stack)===-1||o===void 0&&l.stack===void 0)&&r.push(l.stack),l.index===t))break;return r.length||r.push(void 0),r}_getStackCount(t){return this._getStacks(void 0,t).length}_getStackIndex(t,e,i){const s=this._getStacks(t,i),o=e!==void 0?s.indexOf(e):-1;return o===-1?s.length-1:o}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let o,r;for(o=0,r=e.data.length;ohn(x,a,l,!0)?1:Math.max(v,v*e,_,_*e),g=(x,v,_)=>hn(x,a,l,!0)?-1:Math.min(v,v*e,_,_*e),p=f(0,c,h),m=f(at,u,d),b=g(tt,c,h),y=g(tt+at,u,d);i=(p-b)/2,s=(m-y)/2,o=-(p+b)/2,r=-(m+y)/2}return{ratioX:i,ratioY:s,offsetX:o,offsetY:r}}class Le extends Kt{constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(this._parsing===!1)s._parsed=i;else{let o=l=>+i[l];if(F(i[t])){const{key:l="value"}=this._parsing;o=c=>+ce(i[c],l)}let r,a;for(r=t,a=t+e;r0&&!isNaN(t)?G*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],o=_n(e._parsed[t],i.options.locale);return{label:s[t]||"",value:o}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,o,r,a,l;if(!t){for(s=0,o=i.data.datasets.length;st!=="spacing",_indexable:t=>t!=="spacing"&&!t.startsWith("borderDash")&&!t.startsWith("hoverBorderDash")}),k(Le,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map((o,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:o,fillStyle:l.backgroundColor,strokeStyle:l.borderColor,fontColor:s,lineWidth:l.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(r),index:r}})}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}});class jn extends Kt{initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:o}=e,r=this.chart._animationsDisabled;let{start:a,count:l}=Pu(e,s,r);this._drawStart=a,this._drawCount=l,Du(e)&&(a=0,l=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!o._decimated,i.points=s;const c=this.resolveDatasetElementOptions(t);this.options.showLine||(c.borderWidth=0),c.segment=this.options.segment,this.updateElement(i,void 0,{animated:!r,options:c},t),this.updateElements(s,a,l,t)}updateElements(t,e,i,s){const o=s==="reset",{iScale:r,vScale:a,_stacked:l,_dataset:c}=this._cachedMeta,{sharedOptions:u,includeOptions:h}=this._getSharedOptions(e,s),d=r.axis,f=a.axis,{spanGaps:g,segment:p}=this.options,m=un(g)?g:Number.POSITIVE_INFINITY,b=this.chart._animationsDisabled||o||s==="none",y=e+i,x=t.length;let v=e>0&&this.getParsed(e-1);for(let _=0;_=y){C.skip=!0;continue}const M=this.getParsed(_),T=H(M[f]),A=C[d]=r.getPixelForValue(M[d],_),O=C[f]=o||T?a.getBasePixel():a.getPixelForValue(l?this.applyStack(a,M,l):M[f],_);C.skip=isNaN(A)||isNaN(O)||T,C.stop=_>0&&Math.abs(M[d]-v[d])>m,p&&(C.parsed=M,C.raw=c.data[_]),h&&(C.options=u||this.resolveDataElementOptions(_,w.active?"active":s)),b||this.updateElement(w,_,C,s),v=M}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const o=s[0].size(this.resolveDataElementOptions(0)),r=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,o,r)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}k(jn,"id","line"),k(jn,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),k(jn,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});class Un extends Kt{constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],o=_n(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:o}}parseObjectData(t,e,i,s){return ih.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach((i,s)=>{const o=this.getParsed(s).r;!isNaN(o)&&this.chart.getDataVisibility(s)&&(oe.max&&(e.max=o))}),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),o=Math.max(s/2,0),r=Math.max(i.cutoutPercentage?o/100*i.cutoutPercentage:1,0),a=(o-r)/t.getVisibleDatasetCount();this.outerRadius=o-a*this.index,this.innerRadius=this.outerRadius-a}updateElements(t,e,i,s){const o=s==="reset",r=this.chart,l=r.options.animation,c=this._cachedMeta.rScale,u=c.xCenter,h=c.yCenter,d=c.getIndexAngle(0)-.5*tt;let f=d,g;const p=360/this.countVisibleElements();for(g=0;g{!isNaN(this.getParsed(s).r)&&this.chart.getDataVisibility(s)&&e++}),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?Lt(this.resolveDataElementOptions(t,e).angle||i):0}}k(Un,"id","polarArea"),k(Un,"defaults",{dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0}),k(Un,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map((o,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:o,fillStyle:l.backgroundColor,strokeStyle:l.borderColor,fontColor:s,lineWidth:l.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(r),index:r}})}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}});class Qi extends Le{}k(Qi,"id","pie"),k(Qi,"defaults",{cutout:0,rotation:0,circumference:360,radius:"100%"});function ge(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class ks{constructor(t){k(this,"options");this.options=t||{}}static override(t){Object.assign(ks.prototype,t)}init(){}formats(){return ge()}parse(){return ge()}format(){return ge()}add(){return ge()}diff(){return ge()}startOf(){return ge()}endOf(){return ge()}}var sa={_date:ks};function Jh(n,t,e,i){const{controller:s,data:o,_sorted:r}=n,a=s._cachedMeta.iScale;if(a&&t===a.axis&&t!=="r"&&r&&o.length){const l=a._reversePixels?vu:xe;if(i){if(s._sharedOptions){const c=o[0],u=typeof c.getRange=="function"&&c.getRange(t);if(u){const h=l(o,t,e-u),d=l(o,t,e+u);return{lo:h.lo,hi:d.hi}}}}else return l(o,t,e)}return{lo:0,hi:o.length-1}}function xn(n,t,e,i,s){const o=n.getSortedVisibleDatasetMetas(),r=e[t];for(let a=0,l=o.length;a{l[r](t[e],s)&&(o.push({element:l,datasetIndex:c,index:u}),a=a||l.inRange(t.x,t.y,s))}),i&&!a?[]:o}var id={evaluateInteractionItems:xn,modes:{index(n,t,e,i){const s=be(t,n),o=e.axis||"x",r=e.includeInvisible||!1,a=e.intersect?Ti(n,s,o,i,r):Si(n,s,o,!1,i,r),l=[];return a.length?(n.getSortedVisibleDatasetMetas().forEach(c=>{const u=a[0].index,h=c.data[u];h&&!h.skip&&l.push({element:h,datasetIndex:c.index,index:u})}),l):[]},dataset(n,t,e,i){const s=be(t,n),o=e.axis||"xy",r=e.includeInvisible||!1;let a=e.intersect?Ti(n,s,o,i,r):Si(n,s,o,!1,i,r);if(a.length>0){const l=a[0].datasetIndex,c=n.getDatasetMeta(l).data;a=[];for(let u=0;ue.pos===t)}function _o(n,t){return n.filter(e=>oa.indexOf(e.pos)===-1&&e.box.axis===t)}function He(n,t){return n.sort((e,i)=>{const s=t?i:e,o=t?e:i;return s.weight===o.weight?s.index-o.index:s.weight-o.weight})}function sd(n){const t=[];let e,i,s,o,r,a;for(e=0,i=(n||[]).length;ec.box.fullSize),!0),i=He(We(t,"left"),!0),s=He(We(t,"right")),o=He(We(t,"top"),!0),r=He(We(t,"bottom")),a=_o(t,"x"),l=_o(t,"y");return{fullSize:e,leftAndTop:i.concat(o),rightAndBottom:s.concat(l).concat(r).concat(a),chartArea:We(t,"chartArea"),vertical:i.concat(s).concat(l),horizontal:o.concat(r).concat(a)}}function xo(n,t,e,i){return Math.max(n[e],t[e])+Math.max(n[i],t[i])}function ra(n,t){n.top=Math.max(n.top,t.top),n.left=Math.max(n.left,t.left),n.bottom=Math.max(n.bottom,t.bottom),n.right=Math.max(n.right,t.right)}function ld(n,t,e,i){const{pos:s,box:o}=e,r=n.maxPadding;if(!F(s)){e.size&&(n[s]-=e.size);const h=i[e.stack]||{size:0,count:1};h.size=Math.max(h.size,e.horizontal?o.height:o.width),e.size=h.size/h.count,n[s]+=e.size}o.getPadding&&ra(r,o.getPadding());const a=Math.max(0,t.outerWidth-xo(r,n,"left","right")),l=Math.max(0,t.outerHeight-xo(r,n,"top","bottom")),c=a!==n.w,u=l!==n.h;return n.w=a,n.h=l,e.horizontal?{same:c,other:u}:{same:u,other:c}}function cd(n){const t=n.maxPadding;function e(i){const s=Math.max(t[i]-n[i],0);return n[i]+=s,s}n.y+=e("top"),n.x+=e("left"),e("right"),e("bottom")}function ud(n,t){const e=t.maxPadding;function i(s){const o={left:0,top:0,right:0,bottom:0};return s.forEach(r=>{o[r]=Math.max(t[r],e[r])}),o}return i(n?["left","right"]:["top","bottom"])}function Qe(n,t,e,i){const s=[];let o,r,a,l,c,u;for(o=0,r=n.length,c=0;o{typeof p.beforeLayout=="function"&&p.beforeLayout()});const u=l.reduce((p,m)=>m.box.options&&m.box.options.display===!1?p:p+1,0)||1,h=Object.freeze({outerWidth:t,outerHeight:e,padding:s,availableWidth:o,availableHeight:r,vBoxMaxWidth:o/2/u,hBoxMaxHeight:r/2}),d=Object.assign({},s);ra(d,yt(i));const f=Object.assign({maxPadding:d,w:o,h:r,x:s.left,y:s.top},s),g=rd(l.concat(c),h);Qe(a.fullSize,f,h,g),Qe(l,f,h,g),Qe(c,f,h,g)&&Qe(l,f,h,g),cd(f),vo(a.leftAndTop,f,h,g),f.x+=f.w,f.y+=f.h,vo(a.rightAndBottom,f,h,g),n.chartArea={left:f.left,top:f.top,right:f.left+f.w,bottom:f.top+f.h,height:f.h,width:f.w},$(a.chartArea,p=>{const m=p.box;Object.assign(m,n.chartArea),m.update(f.w,f.h,{left:0,top:0,right:0,bottom:0})})}};class aa{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class hd extends aa{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const $n="$chartjs",dd={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},wo=n=>n===null||n==="";function fd(n,t){const e=n.style,i=n.getAttribute("height"),s=n.getAttribute("width");if(n[$n]={initial:{height:i,width:s,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",wo(s)){const o=io(n,"width");o!==void 0&&(n.width=o)}if(wo(i))if(n.style.height==="")n.height=n.width/(t||2);else{const o=io(n,"height");o!==void 0&&(n.height=o)}return n}const la=bh?{passive:!0}:!1;function gd(n,t,e){n.addEventListener(t,e,la)}function pd(n,t,e){n.canvas.removeEventListener(t,e,la)}function md(n,t){const e=dd[n.type]||n.type,{x:i,y:s}=be(n,t);return{type:e,chart:t,native:n,x:i!==void 0?i:null,y:s!==void 0?s:null}}function ii(n,t){for(const e of n)if(e===t||e.contains(t))return!0}function bd(n,t,e){const i=n.canvas,s=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ii(a.addedNodes,i),r=r&&!ii(a.removedNodes,i);r&&e()});return s.observe(document,{childList:!0,subtree:!0}),s}function yd(n,t,e){const i=n.canvas,s=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||ii(a.removedNodes,i),r=r&&!ii(a.addedNodes,i);r&&e()});return s.observe(document,{childList:!0,subtree:!0}),s}const fn=new Map;let Mo=0;function ca(){const n=window.devicePixelRatio;n!==Mo&&(Mo=n,fn.forEach((t,e)=>{e.currentDevicePixelRatio!==n&&t()}))}function _d(n,t){fn.size||window.addEventListener("resize",ca),fn.set(n,t)}function xd(n){fn.delete(n),fn.size||window.removeEventListener("resize",ca)}function vd(n,t,e){const i=n.canvas,s=i&&Ms(i);if(!s)return;const o=Wr((a,l)=>{const c=s.clientWidth;e(a,l),c{const l=a[0],c=l.contentRect.width,u=l.contentRect.height;c===0&&u===0||o(c,u)});return r.observe(s),_d(n,o),r}function Oi(n,t,e){e&&e.disconnect(),t==="resize"&&xd(n)}function wd(n,t,e){const i=n.canvas,s=Wr(o=>{n.ctx!==null&&e(md(o,n))},n);return gd(i,t,s),s}class Md extends aa{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(fd(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e[$n])return!1;const i=e[$n].initial;["height","width"].forEach(o=>{const r=i[o];H(r)?e.removeAttribute(o):e.setAttribute(o,r)});const s=i.style||{};return Object.keys(s).forEach(o=>{e.style[o]=s[o]}),e.width=e.width,delete e[$n],!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),r={attach:bd,detach:yd,resize:vd}[e]||wd;s[e]=r(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:Oi,detach:Oi,resize:Oi}[e]||pd)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return mh(t,e,i,s)}isAttached(t){const e=Ms(t);return!!(e&&e.isConnected)}}function kd(n){return!ws()||typeof OffscreenCanvas<"u"&&n instanceof OffscreenCanvas?hd:Md}class Rt{constructor(){k(this,"x");k(this,"y");k(this,"active",!1);k(this,"options");k(this,"$animations")}tooltipPosition(t){const{x:e,y:i}=this.getProps(["x","y"],t);return{x:e,y:i}}hasValue(){return un(this.x)&&un(this.y)}getProps(t,e){const i=this.$animations;if(!e||!i)return this;const s={};return t.forEach(o=>{s[o]=i[o]&&i[o].active()?i[o]._to:this[o]}),s}}k(Rt,"defaults",{}),k(Rt,"defaultRoutes");function Cd(n,t){const e=n.options.ticks,i=Pd(n),s=Math.min(e.maxTicksLimit||i,i),o=e.major.enabled?Td(t):[],r=o.length,a=o[0],l=o[r-1],c=[];if(r>s)return Sd(t,c,o,r/s),c;const u=Dd(o,t,s);if(r>0){let h,d;const f=r>1?Math.round((l-a)/(r-1)):null;for(Ln(t,c,u,H(f)?0:a-f,a),h=0,d=r-1;hs)return l}return Math.max(s,1)}function Td(n){const t=[];let e,i;for(e=0,i=n.length;en==="left"?"right":n==="right"?"left":n,ko=(n,t,e)=>t==="top"||t==="left"?n[t]+e:n[t]-e,Co=(n,t)=>Math.min(t||n,n);function Po(n,t){const e=[],i=n.length/t,s=n.length;let o=0;for(;or+a)))return l}function Rd(n,t){$(n,e=>{const i=e.gc,s=i.length/2;let o;if(s>t){for(o=0;oi?i:e,i=s&&e>i?e:i,{min:Pt(e,Pt(i,e)),max:Pt(i,Pt(e,i))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){X(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:o,ticks:r}=this.options,a=r.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=qu(this,o,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||i<=1||!this.isHorizontal()){this.labelRotation=s;return}const u=this._getLabelSizes(),h=u.widest.width,d=u.highest.height,f=ft(this.chart.width-h,0,this.maxWidth);a=t.offset?this.maxWidth/i:f/(i-1),h+6>a&&(a=f/(i-(t.offset?.5:1)),l=this.maxHeight-Ve(t.grid)-e.padding-Do(t.title,this.chart.options.font),c=Math.sqrt(h*h+d*d),r=ps(Math.min(Math.asin(ft((u.highest.height+6)/a,-1,1)),Math.asin(ft(l/c,-1,1))-Math.asin(ft(d/c,-1,1)))),r=Math.max(s,Math.min(o,r))),this.labelRotation=r}afterCalculateLabelRotation(){X(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){X(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:o}}=this,r=this._isVisible(),a=this.isHorizontal();if(r){const l=Do(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Ve(o)+l):(t.height=this.maxHeight,t.width=Ve(o)+l),i.display&&this.ticks.length){const{first:c,last:u,widest:h,highest:d}=this._getLabelSizes(),f=i.padding*2,g=Lt(this.labelRotation),p=Math.cos(g),m=Math.sin(g);if(a){const b=i.mirror?0:m*h.width+p*d.height;t.height=Math.min(this.maxHeight,t.height+b+f)}else{const b=i.mirror?0:p*h.width+m*d.height;t.width=Math.min(this.maxWidth,t.width+b+f)}this._calculatePadding(c,u,m,p)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:o,padding:r},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const u=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let d=0,f=0;l?c?(d=s*t.width,f=i*e.height):(d=i*t.height,f=s*e.width):o==="start"?f=e.width:o==="end"?d=t.width:o!=="inner"&&(d=t.width/2,f=e.width/2),this.paddingLeft=Math.max((d-u+r)*this.width/(this.width-u),0),this.paddingRight=Math.max((f-h+r)*this.width/(this.width-h),0)}else{let u=e.height/2,h=t.height/2;o==="start"?(u=0,h=t.height):o==="end"&&(u=e.height,h=0),this.paddingTop=u+r,this.paddingBottom=h+r}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){X(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,i;for(e=0,i=t.length;e({width:r[T]||0,height:a[T]||0});return{first:M(0),last:M(e-1),widest:M(w),highest:M(C),widths:r,heights:a}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return xu(this._alignToPixels?fe(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*s?a/i:l/s:l*s0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:o,position:r,border:a}=s,l=o.offset,c=this.isHorizontal(),h=this.ticks.length+(l?1:0),d=Ve(o),f=[],g=a.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,b=function(U){return fe(i,U,p)};let y,x,v,_,w,C,M,T,A,O,R,st;if(r==="top")y=b(this.bottom),C=this.bottom-d,T=y-m,O=b(t.top)+m,st=t.bottom;else if(r==="bottom")y=b(this.top),O=t.top,st=b(t.bottom)-m,C=y+m,T=this.top+d;else if(r==="left")y=b(this.right),w=this.right-d,M=y-m,A=b(t.left)+m,R=t.right;else if(r==="right")y=b(this.left),A=t.left,R=b(t.right)-m,w=y+m,M=this.left+d;else if(e==="x"){if(r==="center")y=b((t.top+t.bottom)/2+.5);else if(F(r)){const U=Object.keys(r)[0],Q=r[U];y=b(this.chart.scales[U].getPixelForValue(Q))}O=t.top,st=t.bottom,C=y+m,T=C+d}else if(e==="y"){if(r==="center")y=b((t.left+t.right)/2);else if(F(r)){const U=Object.keys(r)[0],Q=r[U];y=b(this.chart.scales[U].getPixelForValue(Q))}w=y-m,M=w-d,A=t.left,R=t.right}const gt=S(s.ticks.maxTicksLimit,h),W=Math.max(1,Math.ceil(h/gt));for(x=0;x0&&(Ft-=St/2);break}Qt={left:Ft,top:Jt,width:St+Tt.width,height:Zt+Tt.height,color:W.backdropColor}}m.push({label:v,font:T,textOffset:R,options:{rotation:p,color:Q,strokeColor:Mt,strokeWidth:lt,textAlign:Gt,textBaseline:st,translation:[_,w],backdrop:Qt}})}return m}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-Lt(this.labelRotation))return t==="top"?"left":"right";let s="center";return e.align==="start"?s="left":e.align==="end"?s="right":e.align==="inner"&&(s="inner"),s}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:i,mirror:s,padding:o}}=this.options,r=this._getLabelSizes(),a=t+o,l=r.widest.width;let c,u;return e==="left"?s?(u=this.right+o,i==="near"?c="left":i==="center"?(c="center",u+=l/2):(c="right",u+=l)):(u=this.right-a,i==="near"?c="right":i==="center"?(c="center",u-=l/2):(c="left",u=this.left)):e==="right"?s?(u=this.left+o,i==="near"?c="right":i==="center"?(c="center",u-=l/2):(c="left",u-=l)):(u=this.left+a,i==="near"?c="left":i==="center"?(c="center",u+=l/2):(c="right",u=this.right)):c="right",{textAlign:c,x:u}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;if(e==="left"||e==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(e==="top"||e==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:i,top:s,width:o,height:r}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(i,s,o,r),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const s=this.ticks.findIndex(o=>o.value===t);return s>=0?e.setContext(this.getContext(s)).lineWidth:0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,r;const a=(l,c,u)=>{!u.width||!u.color||(i.save(),i.lineWidth=u.width,i.strokeStyle=u.color,i.setLineDash(u.borderDash||[]),i.lineDashOffset=u.borderDashOffset,i.beginPath(),i.moveTo(l.x,l.y),i.lineTo(c.x,c.y),i.stroke(),i.restore())};if(e.display)for(o=0,r=s.length;o{this.draw(o)}}]:[{z:i,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let o,r;for(o=0,r=e.length;o{const i=e.split("."),s=i.pop(),o=[n].concat(i).join("."),r=t[e].split("."),a=r.pop(),l=r.join(".");nt.route(o,s,l,a)})}function Wd(n){return"id"in n&&"defaults"in n}class Hd{constructor(){this.controllers=new Rn(Kt,"datasets",!0),this.elements=new Rn(Rt,"elements"),this.plugins=new Rn(Object,"plugins"),this.scales=new Rn(ke,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,i){[...e].forEach(s=>{const o=i||this._getRegistryForType(s);i||o.isForType(s)||o===this.plugins&&s.id?this._exec(t,o,s):$(s,r=>{const a=i||this._getRegistryForType(r);this._exec(t,a,r)})})}_exec(t,e,i){const s=gs(t);X(i["before"+s],[],i),e[t](i),X(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;eo.filter(a=>!r.some(l=>a.plugin.id===l.plugin.id));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function Yd(n){const t={},e=[],i=Object.keys(Et.plugins.items);for(let o=0;o1&&To(n[0].toLowerCase());if(i)return i}throw new Error(`Cannot determine type of '${n}' axis. Please provide 'axis' or 'position' option.`)}function So(n,t,e){if(e[t+"AxisID"]===n)return{axis:t}}function Gd(n,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(i=>i.xAxisID===n||i.yAxisID===n);if(e.length)return So(n,"x",e[0])||So(n,"y",e[0])}return{}}function Qd(n,t){const e=Me[n.type]||{scales:{}},i=t.scales||{},s=Zi(n.type,t),o=Object.create(null);return Object.keys(i).forEach(r=>{const a=i[r];if(!F(a))return console.error(`Invalid scale configuration for scale: ${r}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${r}`);const l=Ji(r,a,Gd(r,n),nt.scales[a.type]),c=Xd(l,s),u=e.scales||{};o[r]=en(Object.create(null),[{axis:l},a,u[l],u[c]])}),n.data.datasets.forEach(r=>{const a=r.type||n.type,l=r.indexAxis||Zi(a,t),u=(Me[a]||{}).scales||{};Object.keys(u).forEach(h=>{const d=qd(h,l),f=r[d+"AxisID"]||d;o[f]=o[f]||Object.create(null),en(o[f],[{axis:d},i[f],u[h]])})}),Object.keys(o).forEach(r=>{const a=o[r];en(a,[nt.scales[a.type],nt.scale])}),o}function ua(n){const t=n.options||(n.options={});t.plugins=S(t.plugins,{}),t.scales=Qd(n,t)}function ha(n){return n=n||{},n.datasets=n.datasets||[],n.labels=n.labels||[],n}function Zd(n){return n=n||{},n.data=ha(n.data),ua(n),n}const Oo=new Map,da=new Set;function Fn(n,t){let e=Oo.get(n);return e||(e=t(),Oo.set(n,e),da.add(e)),e}const Ye=(n,t,e)=>{const i=ce(t,e);i!==void 0&&n.add(i)};class Jd{constructor(t){this._config=Zd(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=ha(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),ua(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Fn(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return Fn(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return Fn(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,i=this.type;return Fn(`${i}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return(!s||e)&&(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:o}=this,r=this._cachedScopes(t,i),a=r.get(e);if(a)return a;const l=new Set;e.forEach(u=>{t&&(l.add(t),u.forEach(h=>Ye(l,t,h))),u.forEach(h=>Ye(l,s,h)),u.forEach(h=>Ye(l,Me[o]||{},h)),u.forEach(h=>Ye(l,nt,h)),u.forEach(h=>Ye(l,Ki,h))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),da.has(e)&&r.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,Me[e]||{},nt.datasets[e]||{},{type:e},nt,Ki]}resolveNamedOptions(t,e,i,s=[""]){const o={$shared:!0},{resolver:r,subPrefixes:a}=Ao(this._resolverCache,t,s);let l=r;if(ef(r,e)){o.$shared=!1,i=ue(i)?i():i;const c=this.createResolver(t,i,a);l=Ee(r,i,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,i=[""],s){const{resolver:o}=Ao(this._resolverCache,t,i);return F(e)?Ee(o,e,void 0,s):o}}function Ao(n,t,e){let i=n.get(t);i||(i=new Map,n.set(t,i));const s=e.join();let o=i.get(s);return o||(o={resolver:_s(t,e),subPrefixes:e.filter(a=>!a.toLowerCase().includes("hover"))},i.set(s,o)),o}const tf=n=>F(n)&&Object.getOwnPropertyNames(n).some(t=>ue(n[t]));function ef(n,t){const{isScriptable:e,isIndexable:i}=Ur(n);for(const s of t){const o=e(s),r=i(s),a=(r||o)&&n[s];if(o&&(ue(a)||tf(a))||r&&K(a))return!0}return!1}var nf="4.4.1";const sf=["top","bottom","left","right","chartArea"];function Lo(n,t){return n==="top"||n==="bottom"||sf.indexOf(n)===-1&&t==="x"}function Ro(n,t){return function(e,i){return e[n]===i[n]?e[t]-i[t]:e[n]-i[n]}}function Fo(n){const t=n.chart,e=t.options.animation;t.notifyPlugins("afterRender"),X(e&&e.onComplete,[n],t)}function of(n){const t=n.chart,e=t.options.animation;X(e&&e.onProgress,[n],t)}function fa(n){return ws()&&typeof n=="string"?n=document.getElementById(n):n&&n.length&&(n=n[0]),n&&n.canvas&&(n=n.canvas),n}const qn={},Io=n=>{const t=fa(n);return Object.values(qn).filter(e=>e.canvas===t).pop()};function rf(n,t,e){const i=Object.keys(n);for(const s of i){const o=+s;if(o>=t){const r=n[s];delete n[s],(e>0||o>t)&&(n[o+e]=r)}}}function af(n,t,e,i){return!e||n.type==="mouseout"?null:i?t:n}function In(n,t,e){return n.options.clip?n[e]:t[e]}function lf(n,t){const{xScale:e,yScale:i}=n;return e&&i?{left:In(e,t,"left"),right:In(e,t,"right"),top:In(i,t,"top"),bottom:In(i,t,"bottom")}:t}class mt{static register(...t){Et.add(...t),Eo()}static unregister(...t){Et.remove(...t),Eo()}constructor(t,e){const i=this.config=new Jd(e),s=fa(t),o=Io(s);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const r=i.createResolver(i.chartOptionScopes(),this.getContext());this.platform=new(i.platform||kd(s)),this.platform.updateConfig(i);const a=this.platform.acquireContext(s,r.aspectRatio),l=a&&a.canvas,c=l&&l.height,u=l&&l.width;if(this.id=lu(),this.ctx=a,this.canvas=l,this.width=u,this.height=c,this._options=r,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new Vd,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=ku(h=>this.update(h),r.resizeDelay||0),this._dataChanges=[],qn[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}jt.listen(this,"complete",Fo),jt.listen(this,"progress",of),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:s,_aspectRatio:o}=this;return H(t)?e&&o?o:s?i/s:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return Et}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():no(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Js(this.canvas,this.ctx),this}stop(){return jt.stop(this),this}resize(t,e){jt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,o=i.maintainAspectRatio&&this.aspectRatio,r=this.platform.getMaximumSize(s,t,e,o),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=r.width,this.height=r.height,this._aspectRatio=this.aspectRatio,no(this,a,!0)&&(this.notifyPlugins("resize",{size:r}),X(i.onResize,[this,r],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};$(e,(i,s)=>{i.id=s})}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce((r,a)=>(r[a]=!1,r),{});let o=[];e&&(o=o.concat(Object.keys(e).map(r=>{const a=e[r],l=Ji(r,a),c=l==="r",u=l==="x";return{options:a,dposition:c?"chartArea":u?"bottom":"left",dtype:c?"radialLinear":u?"category":"linear"}}))),$(o,r=>{const a=r.options,l=a.id,c=Ji(l,a),u=S(a.type,r.dtype);(a.position===void 0||Lo(a.position,c)!==Lo(r.dposition))&&(a.position=r.dposition),s[l]=!0;let h=null;if(l in i&&i[l].type===u)h=i[l];else{const d=Et.getScale(u);h=new d({id:l,type:u,ctx:this.ctx,chart:this}),i[h.id]=h}h.init(a,t)}),$(s,(r,a)=>{r||delete i[a]}),$(i,r=>{se.configure(this,r,r.options),se.addBox(this,r)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort((s,o)=>s.index-o.index),i>e){for(let s=e;se.length&&delete this._stacks,t.forEach((i,s)=>{e.filter(o=>o===i._dataset).length===0&&this._destroyDatasetMeta(s)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let r=0;for(let c=0,u=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(Ro("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){$(this.scales,t=>{se.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);(!Us(e,i)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:o}of e){const r=i==="_removeElements"?-o:o;rf(t,s,r)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=o=>new Set(t.filter(r=>r[0]===o).map((r,a)=>a+","+r.splice(1).join(","))),s=i(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;se.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],$(this.boxes,s=>{i&&s.position==="chartArea"||(s.configure&&s.configure(),this._layers.push(...s._layers()))},this),this._layers.forEach((s,o)=>{s._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,i=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i=t._clip,s=!i.disabled,o=lf(t,this.chartArea),r={meta:t,index:t.index,cancelable:!0};this.notifyPlugins("beforeDatasetDraw",r)!==!1&&(s&&gi(e,{left:i.left===!1?0:o.left-i.left,right:i.right===!1?this.width:o.right+i.right,top:i.top===!1?0:o.top-i.top,bottom:i.bottom===!1?this.height:o.bottom+i.bottom}),t.controller.draw(),s&&pi(e),r.cancelable=!1,this.notifyPlugins("afterDatasetDraw",r))}isPointInArea(t){return Xt(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const o=id.modes[e];return typeof o=="function"?o(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter(o=>o&&o._dataset===e).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=he(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return typeof i.hidden=="boolean"?!i.hidden:!e.hidden}setDatasetVisibility(t,e){const i=this.getDatasetMeta(t);i.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",o=this.getDatasetMeta(t),r=o.controller._resolveAnimations(void 0,s);cn(e)?(o.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),r.update(o,{visible:i}),this.update(a=>a.datasetIndex===t?s:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),jt.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,r),t[o]=r},s=(o,r,a)=>{o.offsetX=r,o.offsetY=a,this._eventHandler(o)};$(this.options.events,o=>i(o,s))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},s=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let r;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",o),i("detach",r)};r=()=>{this.attached=!1,s("resize",o),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():r()}unbindEvents(){$(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},$(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let o,r,a,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+s+"DatasetHoverStyle"]()),a=0,l=t.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[r],index:r}});!Zn(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,i){const s=this.options.hover,o=(l,c)=>l.filter(u=>!c.some(h=>u.datasetIndex===h.datasetIndex&&u.index===h.index)),r=o(e,t),a=i?t:o(t,e);r.length&&this.updateHoverStyle(r,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=r=>(r.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",i,s)===!1)return;const o=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(o||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:o}=this,r=e,a=this._getActiveElements(t,s,i,r),l=gu(t),c=af(t,this._lastEvent,i,l);i&&(this._lastEvent=null,X(o.onHover,[t,a,this],this),l&&X(o.onClick,[t,a,this],this));const u=!Zn(a,s);return(u||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=c,u}_getActiveElements(t,e,i,s){if(t.type==="mouseout")return[];if(!i)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,s)}}k(mt,"defaults",nt),k(mt,"instances",qn),k(mt,"overrides",Me),k(mt,"registry",Et),k(mt,"version",nf),k(mt,"getChart",Io);function Eo(){return $(mt.instances,n=>n._plugins.invalidate())}function cf(n,t,e){const{startAngle:i,pixelMargin:s,x:o,y:r,outerRadius:a,innerRadius:l}=t;let c=s/a;n.beginPath(),n.arc(o,r,a,i-c,e+c),l>s?(c=s/l,n.arc(o,r,l,e+c,i-c,!0)):n.arc(o,r,s,e+at,i-at),n.closePath(),n.clip()}function uf(n){return ys(n,["outerStart","outerEnd","innerStart","innerEnd"])}function hf(n,t,e,i){const s=uf(n.options.borderRadius),o=(e-t)/2,r=Math.min(o,i*t/2),a=l=>{const c=(e-Math.min(o,l))*i/2;return ft(l,0,Math.min(o,c))};return{outerStart:a(s.outerStart),outerEnd:a(s.outerEnd),innerStart:ft(s.innerStart,0,r),innerEnd:ft(s.innerEnd,0,r)}}function Pe(n,t,e,i){return{x:e+n*Math.cos(t),y:i+n*Math.sin(t)}}function si(n,t,e,i,s,o){const{x:r,y:a,startAngle:l,pixelMargin:c,innerRadius:u}=t,h=Math.max(t.outerRadius+i+e-c,0),d=u>0?u+i+e+c:0;let f=0;const g=s-l;if(i){const W=u>0?u-i:0,U=h>0?h-i:0,Q=(W+U)/2,Mt=Q!==0?g*Q/(Q+i):g;f=(g-Mt)/2}const p=Math.max(.001,g*h-e/tt)/h,m=(g-p)/2,b=l+m+f,y=s-m-f,{outerStart:x,outerEnd:v,innerStart:_,innerEnd:w}=hf(t,d,h,y-b),C=h-x,M=h-v,T=b+x/C,A=y-v/M,O=d+_,R=d+w,st=b+_/O,gt=y-w/R;if(n.beginPath(),o){const W=(T+A)/2;if(n.arc(r,a,h,T,W),n.arc(r,a,h,W,A),v>0){const lt=Pe(M,A,r,a);n.arc(lt.x,lt.y,v,A,y+at)}const U=Pe(R,y,r,a);if(n.lineTo(U.x,U.y),w>0){const lt=Pe(R,gt,r,a);n.arc(lt.x,lt.y,w,y+at,gt+Math.PI)}const Q=(y-w/d+(b+_/d))/2;if(n.arc(r,a,d,y-w/d,Q,!0),n.arc(r,a,d,Q,b+_/d,!0),_>0){const lt=Pe(O,st,r,a);n.arc(lt.x,lt.y,_,st+Math.PI,b-at)}const Mt=Pe(C,b,r,a);if(n.lineTo(Mt.x,Mt.y),x>0){const lt=Pe(C,T,r,a);n.arc(lt.x,lt.y,x,b-at,T)}}else{n.moveTo(r,a);const W=Math.cos(T)*h+r,U=Math.sin(T)*h+a;n.lineTo(W,U);const Q=Math.cos(A)*h+r,Mt=Math.sin(A)*h+a;n.lineTo(Q,Mt)}n.closePath()}function df(n,t,e,i,s){const{fullCircles:o,startAngle:r,circumference:a}=t;let l=t.endAngle;if(o){si(n,t,e,i,l,s);for(let c=0;c=G||hn(r,l,c),m=qt(a,u+f,h+f);return p&&m}getCenterPoint(e){const{x:i,y:s,startAngle:o,endAngle:r,innerRadius:a,outerRadius:l}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],e),{offset:c,spacing:u}=this.options,h=(o+r)/2,d=(a+l+u+c)/2;return{x:i+Math.cos(h)*d,y:s+Math.sin(h)*d}}tooltipPosition(e){return this.getCenterPoint(e)}draw(e){const{options:i,circumference:s}=this,o=(i.offset||0)/4,r=(i.spacing||0)/2,a=i.circular;if(this.pixelMargin=i.borderAlign==="inner"?.33:0,this.fullCircles=s>G?Math.floor(s/G):0,s===0||this.innerRadius<0||this.outerRadius<0)return;e.save();const l=(this.startAngle+this.endAngle)/2;e.translate(Math.cos(l)*o,Math.sin(l)*o);const c=1-Math.sin(Math.min(tt,s||0)),u=o*c;e.fillStyle=i.backgroundColor,e.strokeStyle=i.borderColor,df(e,this,u,r,a),ff(e,this,u,r,a),e.restore()}}k(Ze,"id","arc"),k(Ze,"defaults",{borderAlign:"center",borderColor:"#fff",borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0}),k(Ze,"defaultRoutes",{backgroundColor:"backgroundColor"}),k(Ze,"descriptors",{_scriptable:!0,_indexable:e=>e!=="borderDash"});function ga(n,t,e=t){n.lineCap=S(e.borderCapStyle,t.borderCapStyle),n.setLineDash(S(e.borderDash,t.borderDash)),n.lineDashOffset=S(e.borderDashOffset,t.borderDashOffset),n.lineJoin=S(e.borderJoinStyle,t.borderJoinStyle),n.lineWidth=S(e.borderWidth,t.borderWidth),n.strokeStyle=S(e.borderColor,t.borderColor)}function gf(n,t,e){n.lineTo(e.x,e.y)}function pf(n){return n.stepped?Bu:n.tension||n.cubicInterpolationMode==="monotone"?Nu:gf}function pa(n,t,e={}){const i=n.length,{start:s=0,end:o=i-1}=e,{start:r,end:a}=t,l=Math.max(s,r),c=Math.min(o,a),u=sa&&o>a;return{count:i,start:l,loop:t.loop,ilen:c(r+(c?a-v:v))%o,x=()=>{p!==m&&(n.lineTo(u,m),n.lineTo(u,p),n.lineTo(u,b))};for(l&&(f=s[y(0)],n.moveTo(f.x,f.y)),d=0;d<=a;++d){if(f=s[y(d)],f.skip)continue;const v=f.x,_=f.y,w=v|0;w===g?(_m&&(m=_),u=(h*u+v)/++h):(x(),n.lineTo(v,_),g=w,h=0,p=m=_),b=_}x()}function ts(n){const t=n.options,e=t.borderDash&&t.borderDash.length;return!n._decimated&&!n._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?bf:mf}function yf(n){return n.stepped?yh:n.tension||n.cubicInterpolationMode==="monotone"?_h:ye}function _f(n,t,e,i){let s=t._path;s||(s=t._path=new Path2D,t.path(s,e,i)&&s.closePath()),ga(n,t.options),n.stroke(s)}function xf(n,t,e,i){const{segments:s,options:o}=t,r=ts(t);for(const a of s)ga(n,o,a.style),n.beginPath(),r(n,t,a,{start:e,end:e+i-1})&&n.closePath(),n.stroke()}const vf=typeof Path2D=="function";function wf(n,t,e,i){vf&&!t.options.segment?_f(n,t,e,i):xf(n,t,e,i)}class oe extends Rt{constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||i.cubicInterpolationMode==="monotone")&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;uh(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=Ch(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],o=this.points,r=ta(this,{property:e,start:s,end:s});if(!r.length)return;const a=[],l=yf(i);let c,u;for(c=0,u=r.length;ct!=="borderDash"&&t!=="fill"});function zo(n,t,e,i){const s=n.options,{[e]:o}=n.getProps([e],i);return Math.abs(t-o)n.replace("rgb(","rgba(").replace(")",", 0.5)"));function ba(n){return es[n%es.length]}function ya(n){return Bo[n%Bo.length]}function Tf(n,t){return n.borderColor=ba(t),n.backgroundColor=ya(t),++t}function Sf(n,t){return n.backgroundColor=n.data.map(()=>ba(t++)),t}function Of(n,t){return n.backgroundColor=n.data.map(()=>ya(t++)),t}function Af(n){let t=0;return(e,i)=>{const s=n.getDatasetMeta(i).controller;s instanceof Le?t=Sf(e,t):s instanceof Un?t=Of(e,t):s&&(t=Tf(e,t))}}function No(n){let t;for(t in n)if(n[t].borderColor||n[t].backgroundColor)return!0;return!1}function Lf(n){return n&&(n.borderColor||n.backgroundColor)}var Rf={id:"colors",defaults:{enabled:!0,forceOverride:!1},beforeLayout(n,t,e){if(!e.enabled)return;const{data:{datasets:i},options:s}=n.config,{elements:o}=s;if(!e.forceOverride&&(No(i)||Lf(s)||o&&No(o)))return;const r=Af(n);i.forEach(r)}};function Ff(n,t,e){const i=n.segments,s=n.points,o=t.points,r=[];for(const a of i){let{start:l,end:c}=a;c=Cs(l,c,s);const u=ns(e,s[l],s[c],a.loop);if(!t.segments){r.push({source:a,target:u,start:s[l],end:s[c]});continue}const h=ta(t,u);for(const d of h){const f=ns(e,o[d.start],o[d.end],d.loop),g=Jr(a,s,f);for(const p of g)r.push({source:p,target:d,start:{[e]:Wo(u,f,"start",Math.max)},end:{[e]:Wo(u,f,"end",Math.min)}})}}return r}function ns(n,t,e,i){if(i)return;let s=t[n],o=e[n];return n==="angle"&&(s=Dt(s),o=Dt(o)),{property:n,start:s,end:o}}function If(n,t){const{x:e=null,y:i=null}=n||{},s=t.points,o=[];return t.segments.forEach(({start:r,end:a})=>{a=Cs(r,a,s);const l=s[r],c=s[a];i!==null?(o.push({x:l.x,y:i}),o.push({x:c.x,y:i})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function Cs(n,t,e){for(;t>n;t--){const i=e[t];if(!isNaN(i.x)&&!isNaN(i.y))break}return t}function Wo(n,t,e,i){return n&&t?i(n[e],t[e]):n?n[e]:t?t[e]:0}function _a(n,t){let e=[],i=!1;return K(n)?(i=!0,e=n):e=If(n,t),e.length?new oe({points:e,options:{tension:0},_loop:i,_fullLoop:i}):null}function Ho(n){return n&&n.fill!==!1}function Ef(n,t,e){let s=n[t].fill;const o=[t];let r;if(!e)return s;for(;s!==!1&&o.indexOf(s)===-1;){if(!et(s))return s;if(r=n[s],!r)return!1;if(r.visible)return s;o.push(s),s=r.fill}return!1}function zf(n,t,e){const i=Hf(n);if(F(i))return isNaN(i.value)?!1:i;let s=parseFloat(i);return et(s)&&Math.floor(s)===s?Bf(i[0],t,s,e):["origin","start","end","stack","shape"].indexOf(i)>=0&&i}function Bf(n,t,e,i){return(n==="-"||n==="+")&&(e=t+e),e===t||e<0||e>=i?!1:e}function Nf(n,t){let e=null;return n==="start"?e=t.bottom:n==="end"?e=t.top:F(n)?e=t.getPixelForValue(n.value):t.getBasePixel&&(e=t.getBasePixel()),e}function Wf(n,t,e){let i;return n==="start"?i=e:n==="end"?i=t.options.reverse?t.min:t.max:F(n)?i=n.value:i=t.getBaseValue(),i}function Hf(n){const t=n.options,e=t.fill;let i=S(e&&e.target,e);return i===void 0&&(i=!!t.backgroundColor),i===!1||i===null?!1:i===!0?"origin":i}function Vf(n){const{scale:t,index:e,line:i}=n,s=[],o=i.segments,r=i.points,a=Yf(t,e);a.push(_a({x:null,y:t.bottom},i));for(let l=0;l=0;--r){const a=s[r].$filler;a&&(a.line.updateControlPoints(o,a.axis),i&&a.fill&&Ri(n.ctx,a,o))}},beforeDatasetsDraw(n,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const i=n.getSortedVisibleDatasetMetas();for(let s=i.length-1;s>=0;--s){const o=i[s].$filler;Ho(o)&&Ri(n.ctx,o,n.chartArea)}},beforeDatasetDraw(n,t,e){const i=t.meta.$filler;!Ho(i)||e.drawTime!=="beforeDatasetDraw"||Ri(n.ctx,i,n.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Uo=(n,t)=>{let{boxHeight:e=t,boxWidth:i=t}=n;return n.usePointStyle&&(e=Math.min(e,t),i=n.pointStyleWidth||Math.min(i,t)),{boxWidth:i,boxHeight:e,itemHeight:Math.max(t,e)}},tg=(n,t)=>n!==null&&t!==null&&n.datasetIndex===t.datasetIndex&&n.index===t.index;class $o extends Rt{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=X(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter(i=>t.filter(i,this.chart.data))),t.sort&&(e=e.sort((i,s)=>t.sort(i,s,this.chart.data))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display){this.width=this.height=0;return}const i=t.labels,s=ut(i.font),o=s.size,r=this._computeTitleHeight(),{boxWidth:a,itemHeight:l}=Uo(i,o);let c,u;e.font=s.string,this.isHorizontal()?(c=this.maxWidth,u=this._fitRows(r,o,a,l)+10):(u=this.maxHeight,c=this._fitCols(r,s,a,l)+10),this.width=Math.min(c,t.maxWidth||this.maxWidth),this.height=Math.min(u,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:o,maxWidth:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],u=s+a;let h=t;o.textAlign="left",o.textBaseline="middle";let d=-1,f=-u;return this.legendItems.forEach((g,p)=>{const m=i+e/2+o.measureText(g.text).width;(p===0||c[c.length-1]+m+2*a>r)&&(h+=u,c[c.length-(p>0?0:1)]=0,f+=u,d++),l[p]={left:0,top:f,row:d,width:m,height:s},c[c.length-1]+=m+a}),h}_fitCols(t,e,i,s){const{ctx:o,maxHeight:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],u=r-t;let h=a,d=0,f=0,g=0,p=0;return this.legendItems.forEach((m,b)=>{const{itemWidth:y,itemHeight:x}=eg(i,e,o,m,s);b>0&&f+x+2*a>u&&(h+=d+a,c.push({width:d,height:f}),g+=d+a,p++,d=f=0),l[b]={left:g,top:f,col:p,width:y,height:x},d=Math.max(d,y),f+=x+a}),h+=d,c.push({width:d,height:f}),h}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:o}}=this,r=Fe(o,this.left,this.width);if(this.isHorizontal()){let a=0,l=kt(i,this.left+s,this.right-this.lineWidths[a]);for(const c of e)a!==c.row&&(a=c.row,l=kt(i,this.left+s,this.right-this.lineWidths[a])),c.top+=this.top+t+s,c.left=r.leftForLtr(r.x(l),c.width),l+=c.width+s}else{let a=0,l=kt(i,this.top+t+s,this.bottom-this.columnSizes[a].height);for(const c of e)c.col!==a&&(a=c.col,l=kt(i,this.top+t+s,this.bottom-this.columnSizes[a].height)),c.top=l,c.left+=this.left+s,c.left=r.leftForLtr(r.x(c.left),c.width),l+=c.height+s}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const t=this.ctx;gi(t,this),this._draw(),pi(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:o,labels:r}=t,a=nt.color,l=Fe(t.rtl,this.left,this.width),c=ut(r.font),{padding:u}=r,h=c.size,d=h/2;let f;this.drawTitle(),s.textAlign=l.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=c.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=Uo(r,h),b=function(w,C,M){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const T=S(M.lineWidth,1);if(s.fillStyle=S(M.fillStyle,a),s.lineCap=S(M.lineCap,"butt"),s.lineDashOffset=S(M.lineDashOffset,0),s.lineJoin=S(M.lineJoin,"miter"),s.lineWidth=T,s.strokeStyle=S(M.strokeStyle,a),s.setLineDash(S(M.lineDash,[])),r.usePointStyle){const A={radius:p*Math.SQRT2/2,pointStyle:M.pointStyle,rotation:M.rotation,borderWidth:T},O=l.xPlus(w,g/2),R=C+d;Yr(s,A,O,R,r.pointStyleWidth&&g)}else{const A=C+Math.max((h-p)/2,0),O=l.leftForLtr(w,g),R=ve(M.borderRadius);s.beginPath(),Object.values(R).some(st=>st!==0)?dn(s,{x:O,y:A,w:g,h:p,radius:R}):s.rect(O,A,g,p),s.fill(),T!==0&&s.stroke()}s.restore()},y=function(w,C,M){Ie(s,M.text,w,C+m/2,c,{strikethrough:M.hidden,textAlign:l.textAlign(M.textAlign)})},x=this.isHorizontal(),v=this._computeTitleHeight();x?f={x:kt(o,this.left+u,this.right-i[0]),y:this.top+u+v,line:0}:f={x:this.left+u,y:kt(o,this.top+v+u,this.bottom-e[0].height),line:0},Gr(this.ctx,t.textDirection);const _=m+u;this.legendItems.forEach((w,C)=>{s.strokeStyle=w.fontColor,s.fillStyle=w.fontColor;const M=s.measureText(w.text).width,T=l.textAlign(w.textAlign||(w.textAlign=r.textAlign)),A=g+d+M;let O=f.x,R=f.y;l.setWidth(this.width),x?C>0&&O+A+u>this.right&&(R=f.y+=_,f.line++,O=f.x=kt(o,this.left+u,this.right-i[f.line])):C>0&&R+_>this.bottom&&(O=f.x=O+e[f.line].width+u,f.line++,R=f.y=kt(o,this.top+v+u,this.bottom-e[f.line].height));const st=l.x(O);if(b(st,R,w),O=Cu(T,O+g+d,x?O+A:this.right,t.rtl),y(l.x(O),R,w),x)f.x+=A+u;else if(typeof w.text!="string"){const gt=c.lineHeight;f.y+=va(w,gt)+u}else f.y+=_}),Qr(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=ut(e.font),s=yt(e.padding);if(!e.display)return;const o=Fe(t.rtl,this.left,this.width),r=this.ctx,a=e.position,l=i.size/2,c=s.top+l;let u,h=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),u=this.top+c,h=kt(t.align,h,this.right-d);else{const g=this.columnSizes.reduce((p,m)=>Math.max(p,m.height),0);u=c+kt(t.align,this.top,this.bottom-g-t.labels.padding-this._computeTitleHeight())}const f=kt(a,h,h+d);r.textAlign=o.textAlign(Hr(a)),r.textBaseline="middle",r.strokeStyle=e.color,r.fillStyle=e.color,r.font=i.string,Ie(r,e.text,f,u,i)}_computeTitleHeight(){const t=this.options.title,e=ut(t.font),i=yt(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,o;if(qt(t,this.left,this.right)&&qt(e,this.top,this.bottom)){for(o=this.legendHitBoxes,i=0;io.length>r.length?o:r)),t+e.size/2+i.measureText(s).width}function ig(n,t,e){let i=n;return typeof t.text!="string"&&(i=va(t,e)),i}function va(n,t){const e=n.text?n.text.length:0;return t*e}function sg(n,t){return!!((n==="mousemove"||n==="mouseout")&&(t.onHover||t.onLeave)||t.onClick&&(n==="click"||n==="mouseup"))}var og={id:"legend",_element:$o,start(n,t,e){const i=n.legend=new $o({ctx:n.ctx,options:e,chart:n});se.configure(n,i,e),se.addBox(n,i)},stop(n){se.removeBox(n,n.legend),delete n.legend},beforeUpdate(n,t,e){const i=n.legend;se.configure(n,i,e),i.options=e},afterUpdate(n){const t=n.legend;t.buildLabels(),t.adjustHitBoxes()},afterEvent(n,t){t.replay||n.legend.handleEvent(t.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(n,t,e){const i=t.datasetIndex,s=e.chart;s.isDatasetVisible(i)?(s.hide(i),t.hidden=!0):(s.show(i),t.hidden=!1)},onHover:null,onLeave:null,labels:{color:n=>n.chart.options.color,boxWidth:40,padding:10,generateLabels(n){const t=n.data.datasets,{labels:{usePointStyle:e,pointStyle:i,textAlign:s,color:o,useBorderRadius:r,borderRadius:a}}=n.legend.options;return n._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(e?0:void 0),u=yt(c.borderWidth);return{text:t[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(u.width+u.height)/4,strokeStyle:c.borderColor,pointStyle:i||c.pointStyle,rotation:c.rotation,textAlign:s||c.textAlign,borderRadius:r&&(a||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:n=>n.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:n=>!n.startsWith("on"),labels:{_scriptable:n=>!["generateLabels","filter","sort"].includes(n)}}};const Je={average(n){if(!n.length)return!1;let t,e,i=0,s=0,o=0;for(t=0,e=n.length;t-1?n.split(` -`):n}function rg(n,t){const{element:e,datasetIndex:i,index:s}=t,o=n.getDatasetMeta(i).controller,{label:r,value:a}=o.getLabelAndValue(s);return{chart:n,label:r,parsed:o.getParsed(s),raw:n.data.datasets[i].data[s],formattedValue:a,dataset:o.getDataset(),dataIndex:s,datasetIndex:i,element:e}}function $o(n,t){const e=n.chart.ctx,{body:i,footer:s,title:o}=n,{boxWidth:r,boxHeight:a}=t,l=ut(t.bodyFont),c=ut(t.titleFont),u=ut(t.footerFont),h=o.length,d=s.length,f=i.length,g=yt(t.padding);let p=g.height,m=0,b=i.reduce((v,_)=>v+_.before.length+_.lines.length+_.after.length,0);if(b+=n.beforeBody.length+n.afterBody.length,h&&(p+=h*c.lineHeight+(h-1)*t.titleSpacing+t.titleMarginBottom),b){const v=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;p+=f*v+(b-f)*l.lineHeight+(b-1)*t.bodySpacing}d&&(p+=t.footerMarginTop+d*u.lineHeight+(d-1)*t.footerSpacing);let y=0;const x=function(v){m=Math.max(m,e.measureText(v).width+y)};return e.save(),e.font=c.string,$(n.title,x),e.font=l.string,$(n.beforeBody.concat(n.afterBody),x),y=t.displayColors?r+2+t.boxPadding:0,$(i,v=>{$(v.before,x),$(v.lines,x),$(v.after,x)}),y=0,e.font=u.string,$(n.footer,x),e.restore(),m+=g.width,{width:m,height:p}}function ag(n,t){const{y:e,height:i}=t;return en.height-i/2?"bottom":"center"}function lg(n,t,e,i){const{x:s,width:o}=i,r=e.caretSize+e.caretPadding;if(n==="left"&&s+o+r>t.width||n==="right"&&s-o-r<0)return!0}function cg(n,t,e,i){const{x:s,width:o}=e,{width:r,chartArea:{left:a,right:l}}=n;let c="center";return i==="center"?c=s<=(a+l)/2?"left":"right":s<=o/2?c="left":s>=r-o/2&&(c="right"),lg(c,n,t,e)&&(c="center"),c}function qo(n,t,e){const i=e.yAlign||t.yAlign||ag(n,e);return{xAlign:e.xAlign||t.xAlign||cg(n,t,e,i),yAlign:i}}function ug(n,t){let{x:e,width:i}=n;return t==="right"?e-=i:t==="center"&&(e-=i/2),e}function hg(n,t,e){let{y:i,height:s}=n;return t==="top"?i+=e:t==="bottom"?i-=s+e:i-=s/2,i}function Xo(n,t,e,i){const{caretSize:s,caretPadding:o,cornerRadius:r}=n,{xAlign:a,yAlign:l}=e,c=s+o,{topLeft:u,topRight:h,bottomLeft:d,bottomRight:f}=ve(r);let g=ug(t,a);const p=hg(t,l,c);return l==="center"?a==="left"?g+=c:a==="right"&&(g-=c):a==="left"?g-=Math.max(u,d)+s:a==="right"&&(g+=Math.max(h,f)+s),{x:ft(g,0,i.width-t.width),y:ft(p,0,i.height-t.height)}}function En(n,t,e){const i=yt(e.padding);return t==="center"?n.x+n.width/2:t==="right"?n.x+n.width-i.right:n.x+i.left}function Ko(n){return Ft([],jt(n))}function dg(n,t,e){return he(n,{tooltip:t,tooltipItems:e,type:"tooltip"})}function Go(n,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?n.override(e):n}const wa={beforeTitle:Wt,title(n){if(n.length>0){const t=n[0],e=t.chart.data.labels,i=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(i>0&&t.dataIndex"u"?wa[t].call(e,i):s}class is extends Lt{constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,o=new ea(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=dg(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:i}=e,s=_t(i,"beforeTitle",this,t),o=_t(i,"title",this,t),r=_t(i,"afterTitle",this,t);let a=[];return a=Ft(a,jt(s)),a=Ft(a,jt(o)),a=Ft(a,jt(r)),a}getBeforeBody(t,e){return Ko(_t(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:i}=e,s=[];return $(t,o=>{const r={before:[],lines:[],after:[]},a=Go(i,o);Ft(r.before,jt(_t(a,"beforeLabel",this,o))),Ft(r.lines,_t(a,"label",this,o)),Ft(r.after,jt(_t(a,"afterLabel",this,o))),s.push(r)}),s}getAfterBody(t,e){return Ko(_t(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:i}=e,s=_t(i,"beforeFooter",this,t),o=_t(i,"footer",this,t),r=_t(i,"afterFooter",this,t);let a=[];return a=Ft(a,jt(s)),a=Ft(a,jt(o)),a=Ft(a,jt(r)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],o=[],r=[];let a=[],l,c;for(l=0,c=e.length;lt.filter(u,h,d,i))),t.itemSort&&(a=a.sort((u,h)=>t.itemSort(u,h,i))),$(a,u=>{const h=Go(t.callbacks,u);s.push(_t(h,"labelColor",this,u)),o.push(_t(h,"labelPointStyle",this,u)),r.push(_t(h,"labelTextColor",this,u))}),this.labelColors=s,this.labelPointStyles=o,this.labelTextColors=r,this.dataPoints=a,a}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let o,r=[];if(!s.length)this.opacity!==0&&(o={opacity:0});else{const a=Je[i.position].call(this,s,this._eventPosition);r=this._createItems(i),this.title=this.getTitle(r,i),this.beforeBody=this.getBeforeBody(r,i),this.body=this.getBody(r,i),this.afterBody=this.getAfterBody(r,i),this.footer=this.getFooter(r,i);const l=this._size=$o(this,i),c=Object.assign({},a,l),u=qo(this.chart,i,c),h=Xo(i,c,u,this.chart);this.xAlign=u.xAlign,this.yAlign=u.yAlign,o={opacity:1,x:h.x,y:h.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=r,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const o=this.getCaretPosition(t,i,s);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:o}=this,{caretSize:r,cornerRadius:a}=i,{topLeft:l,topRight:c,bottomLeft:u,bottomRight:h}=ve(a),{x:d,y:f}=t,{width:g,height:p}=e;let m,b,y,x,v,_;return o==="center"?(v=f+p/2,s==="left"?(m=d,b=m-r,x=v+r,_=v-r):(m=d+g,b=m+r,x=v-r,_=v+r),y=m):(s==="left"?b=d+Math.max(l,u)+r:s==="right"?b=d+g-Math.max(c,h)-r:b=this.caretX,o==="top"?(x=f,v=x-r,m=b-r,y=b+r):(x=f+p,v=x+r,m=b+r,y=b-r),_=x),{x1:m,x2:b,x3:y,y1:x,y2:v,y3:_}}drawTitle(t,e,i){const s=this.title,o=s.length;let r,a,l;if(o){const c=Fe(i.rtl,this.x,this.width);for(t.x=En(this,i.titleAlign,i),e.textAlign=c.textAlign(i.titleAlign),e.textBaseline="middle",r=ut(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=r.string,l=0;ly!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,dn(t,{x:p,y:g,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=r.backgroundColor,t.beginPath(),dn(t,{x:m,y:g+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(p,g,c,l),t.strokeRect(p,g,c,l),t.fillStyle=r.backgroundColor,t.fillRect(m,g+1,c-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:o,bodyAlign:r,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:u}=i,h=ut(i.bodyFont);let d=h.lineHeight,f=0;const g=Fe(i.rtl,this.x,this.width),p=function(M){e.fillText(M,g.x(t.x+f),t.y+d/2),t.y+=d+o},m=g.textAlign(r);let b,y,x,v,_,w,C;for(e.textAlign=r,e.textBaseline="middle",e.font=h.string,t.x=En(this,m,i),e.fillStyle=i.bodyColor,$(this.beforeBody,p),f=a&&m!=="right"?r==="center"?c/2+u:c+2+u:0,v=0,w=s.length;v0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,o=i&&i.y;if(s||o){const r=Je[t.position].call(this,this._active,this._eventPosition);if(!r)return;const a=this._size=$o(this,t),l=Object.assign({},r,this._size),c=qo(e,t,l),u=Xo(t,l,c,e);(s._to!==u.x||o._to!==u.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=r.x,this.caretY=r.y,this._resolveAnimations().update(this,u))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},o={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const r=yt(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(o,t,s,e),Gr(t,e.textDirection),o.y+=r.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),Qr(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!Zn(i,s),r=this._positionChanged(s,e);(o||r)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,o=this._active||[],r=this._getActiveElements(t,o,e,i),a=this._positionChanged(r,t),l=e||!Zn(r,o)||a;return l&&(this._active=r,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,i,s){const o=this.options;if(t.type==="mouseout")return[];if(!s)return e;const r=this.chart.getElementsAtEventForMode(t,o.mode,o,i);return o.reverse&&r.reverse(),r}_positionChanged(t,e){const{caretX:i,caretY:s,options:o}=this,r=Je[o.position].call(this,t,e);return r!==!1&&(i!==r.x||s!==r.y)}}k(is,"positioners",Je);var fg={id:"tooltip",_element:is,positioners:Je,afterInit(n,t,e){e&&(n.tooltip=new is({chart:n,options:e}))},beforeUpdate(n,t,e){n.tooltip&&n.tooltip.initialize(e)},reset(n,t,e){n.tooltip&&n.tooltip.initialize(e)},afterDraw(n){const t=n.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(n.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(n.ctx),n.notifyPlugins("afterTooltipDraw",e)}},afterEvent(n,t){if(n.tooltip){const e=t.replay;n.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(n,t)=>t.bodyFont.size,boxWidth:(n,t)=>t.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:wa},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:n=>n!=="filter"&&n!=="itemSort"&&n!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const gg=(n,t,e,i)=>(typeof t=="string"?(e=n.push(t)-1,i.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function pg(n,t,e,i){const s=n.indexOf(t);if(s===-1)return gg(n,t,e,i);const o=n.lastIndexOf(t);return s!==o?e:s}const mg=(n,t)=>n===null?null:ft(Math.round(n),0,t);function Qo(n){const t=this.getLabels();return n>=0&&ne.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}k(ss,"id","category"),k(ss,"defaults",{ticks:{callback:Qo}});function bg(n,t){const e=[],{bounds:s,step:o,min:r,max:a,precision:l,count:c,maxTicks:u,maxDigits:h,includeBounds:d}=n,f=o||1,g=u-1,{min:p,max:m}=t,b=!H(r),y=!H(a),x=!H(c),v=(m-p)/(h+1);let _=$s((m-p)/g/f)*f,w,C,M,T;if(_<1e-14&&!b&&!y)return[{value:p},{value:m}];T=Math.ceil(m/_)-Math.floor(p/_),T>g&&(_=$s(T*_/g/f)*f),H(l)||(w=Math.pow(10,l),_=Math.ceil(_*w)/w),s==="ticks"?(C=Math.floor(p/_)*_,M=Math.ceil(m/_)*_):(C=p,M=m),b&&y&&o&&yu((a-r)/o,_/1e3)?(T=Math.round(Math.min((a-r)/_,u)),_=(a-r)/T,C=r,M=a):x?(C=b?r:C,M=y?a:M,T=c-1,_=(M-C)/T):(T=(M-C)/_,nn(T,Math.round(T),_/1e3)?T=Math.round(T):T=Math.ceil(T));const A=Math.max(qs(_),qs(C));w=Math.pow(10,H(l)?A:l),C=Math.round(C*w)/w,M=Math.round(M*w)/w;let O=0;for(b&&(d&&C!==r?(e.push({value:r}),Ca)break;e.push({value:R})}return y&&d&&M!==a?e.length&&nn(e[e.length-1].value,a,Zo(a,v,n))?e[e.length-1].value=a:e.push({value:a}):(!y||M===a)&&e.push({value:M}),e}function Zo(n,t,{horizontal:e,minRotation:i}){const s=At(i),o=(e?Math.sin(s):Math.cos(s))||.001,r=.75*t*(""+n).length;return Math.min(t/o,r)}class oi extends ke{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return H(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:o}=this;const r=l=>s=e?s:l,a=l=>o=i?o:l;if(t){const l=Bt(s),c=Bt(o);l<0&&c<0?a(0):l>0&&c>0&&r(0)}if(s===o){let l=o===0?1:Math.abs(o*.05);a(o+l),t||r(s-l)}this.min=s,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:i}=t,s;return i?(s=Math.ceil(this.max/i)-Math.floor(this.min/i)+1,s>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${i} would result generating up to ${s} ticks. Limiting to 1000.`),s=1e3)):(s=this.computeTickLimit(),e=e||11),e&&(s=Math.min(e,s)),s}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const s={maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,r=bg(s,o);return t.bounds==="ticks"&&Fr(r,this,"value"),t.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return _n(t,this.chart.options.locale,this.options.ticks.format)}}class os extends oi{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=et(t)?t:0,this.max=et(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=At(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/s))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}k(os,"id","linear"),k(os,"defaults",{ticks:{callback:fi.formatters.numeric}});const gn=n=>Math.floor(ie(n)),pe=(n,t)=>Math.pow(10,gn(n)+t);function Jo(n){return n/Math.pow(10,gn(n))===1}function tr(n,t,e){const i=Math.pow(10,e),s=Math.floor(n/i);return Math.ceil(t/i)-s}function yg(n,t){const e=t-n;let i=gn(e);for(;tr(n,t,i)>10;)i++;for(;tr(n,t,i)<10;)i--;return Math.min(i,gn(n))}function _g(n,{min:t,max:e}){t=Pt(n.min,t);const i=[],s=gn(t);let o=yg(t,e),r=o<0?Math.pow(10,Math.abs(o)):1;const a=Math.pow(10,o),l=s>o?Math.pow(10,s):0,c=Math.round((t-l)*r)/r,u=Math.floor((t-l)/a/10)*a*10;let h=Math.floor((c-u)/Math.pow(10,o)),d=Pt(n.min,Math.round((l+u+h*Math.pow(10,o))*r)/r);for(;d=10?h=h<15?15:20:h++,h>=20&&(o++,h=2,r=o>=0?1:r),d=Math.round((l+u+h*Math.pow(10,o))*r)/r;const f=Pt(n.max,d);return i.push({value:f,major:Jo(f),significand:h}),i}class er extends ke{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=oi.prototype.parse.apply(this,[t,e]);if(i===0){this._zero=!0;return}return et(i)&&i>0?i:null}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=et(t)?Math.max(0,t):null,this.max=et(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!et(this._userMin)&&(this.min=t===pe(this.min,0)?pe(this.min,-1):pe(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const o=a=>i=t?i:a,r=a=>s=e?s:a;i===s&&(i<=0?(o(1),r(10)):(o(pe(i,-1)),r(pe(s,1)))),i<=0&&o(pe(s,-1)),s<=0&&r(pe(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e={min:this._userMin,max:this._userMax},i=_g(e,this);return t.bounds==="ticks"&&Fr(i,this,"value"),t.reverse?(i.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),i}getLabelForValue(t){return t===void 0?"0":_n(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=ie(t),this._valueRange=ie(this.max)-ie(t)}getPixelForValue(t){return(t===void 0||t===0)&&(t=this.min),t===null||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(ie(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}k(er,"id","logarithmic"),k(er,"defaults",{ticks:{callback:fi.formatters.logarithmic,major:{enabled:!0}}});function rs(n){const t=n.ticks;if(t.display&&n.display){const e=yt(t.backdropPadding);return S(t.font&&t.font.size,nt.font.size)+e.height}return 0}function xg(n,t,e){return e=K(e)?e:[e],{w:zu(n,t.string,e),h:e.length*t.lineHeight}}function nr(n,t,e,i,s){return n===i||n===s?{start:t-e/2,end:t+e/2}:ns?{start:t-e,end:t}:{start:t,end:t+e}}function vg(n){const t={l:n.left+n._padding.left,r:n.right-n._padding.right,t:n.top+n._padding.top,b:n.bottom-n._padding.bottom},e=Object.assign({},t),i=[],s=[],o=n._pointLabels.length,r=n.options.pointLabels,a=r.centerPointLabels?tt/o:0;for(let l=0;lt.r&&(a=(i.end-t.r)/o,n.r=Math.max(n.r,t.r+a)),s.startt.b&&(l=(s.end-t.b)/r,n.b=Math.max(n.b,t.b+l))}function Mg(n,t,e){const i=n.drawingArea,{extra:s,additionalAngle:o,padding:r,size:a}=e,l=n.getPointPosition(t,i+s+r,o),c=Math.round(ps(Dt(l.angle+at))),u=Tg(l.y,a.h,c),h=Pg(c),d=Dg(l.x,a.w,h);return{visible:!0,x:l.x,y:u,textAlign:h,left:d,top:u,right:d+a.w,bottom:u+a.h}}function kg(n,t){if(!t)return!0;const{left:e,top:i,right:s,bottom:o}=n;return!(qt({x:e,y:i},t)||qt({x:e,y:o},t)||qt({x:s,y:i},t)||qt({x:s,y:o},t))}function Cg(n,t,e){const i=[],s=n._pointLabels.length,o=n.options,{centerPointLabels:r,display:a}=o.pointLabels,l={extra:rs(o)/2,additionalAngle:r?tt/s:0};let c;for(let u=0;u270||e<90)&&(n-=t),n}function Sg(n,t,e){const{left:i,top:s,right:o,bottom:r}=e,{backdropColor:a}=t;if(!H(a)){const l=ve(t.borderRadius),c=yt(t.backdropPadding);n.fillStyle=a;const u=i-c.left,h=s-c.top,d=o-i+c.width,f=r-s+c.height;Object.values(l).some(g=>g!==0)?(n.beginPath(),dn(n,{x:u,y:h,w:d,h:f,radius:l}),n.fill()):n.fillRect(u,h,d,f)}}function Og(n,t){const{ctx:e,options:{pointLabels:i}}=n;for(let s=t-1;s>=0;s--){const o=n._pointLabelItems[s];if(!o.visible)continue;const r=i.setContext(n.getPointLabelContext(s));Sg(e,r,o);const a=ut(r.font),{x:l,y:c,textAlign:u}=o;Ie(e,n._pointLabels[s],l,c+a.lineHeight/2,a,{color:r.color,textAlign:u,textBaseline:"middle"})}}function Ma(n,t,e,i){const{ctx:s}=n;if(e)s.arc(n.xCenter,n.yCenter,t,0,G);else{let o=n.getPointPosition(0,t);s.moveTo(o.x,o.y);for(let r=1;r{const s=X(this.options.pointLabels.callback,[e,i],this);return s||s===0?s:""}).filter((e,i)=>this.chart.getDataVisibility(i))}fit(){const t=this.options;t.display&&t.pointLabels.display?vg(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){const e=G/(this._pointLabels.length||1),i=this.options.startAngle||0;return Dt(t*e+At(i))}getDistanceFromCenterForValue(t){if(H(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(H(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t{if(h!==0){l=this.getDistanceFromCenterForValue(u.value);const d=this.getContext(h),f=s.setContext(d),g=o.setContext(d);Ag(this,f,l,r,g)}}),i.display){for(t.save(),a=r-1;a>=0;a--){const u=i.setContext(this.getPointLabelContext(a)),{color:h,lineWidth:d}=u;!d||!h||(t.lineWidth=d,t.strokeStyle=h,t.setLineDash(u.borderDash),t.lineDashOffset=u.borderDashOffset,l=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),c=this.getPointPosition(a,l),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(c.x,c.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let o,r;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach((a,l)=>{if(l===0&&!e.reverse)return;const c=i.setContext(this.getContext(l)),u=ut(c.font);if(o=this.getDistanceFromCenterForValue(this.ticks[l].value),c.showLabelBackdrop){t.font=u.string,r=t.measureText(a.label).width,t.fillStyle=c.backdropColor;const h=yt(c.backdropPadding);t.fillRect(-r/2-h.left,-o-u.size/2-h.top,r+h.width,u.size+h.height)}Ie(t,a.label,0,-o,u,{color:c.color,strokeColor:c.textStrokeColor,strokeWidth:c.textStrokeWidth})}),t.restore()}drawTitle(){}}k(zn,"id","radialLinear"),k(zn,"defaults",{display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:fi.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback(t){return t},padding:5,centerPointLabels:!1}}),k(zn,"defaultRoutes",{"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"}),k(zn,"descriptors",{angleLines:{_fallback:"grid"}});const bi={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},xt=Object.keys(bi);function ir(n,t){return n-t}function sr(n,t){if(H(t))return null;const e=n._adapter,{parser:i,round:s,isoWeekday:o}=n._parseOpts;let r=t;return typeof i=="function"&&(r=i(r)),et(r)||(r=typeof i=="string"?e.parse(r,i):e.parse(r)),r===null?null:(s&&(r=s==="week"&&(un(o)||o===!0)?e.startOf(r,"isoWeek",o):e.startOf(r,s)),+r)}function or(n,t,e,i){const s=xt.length;for(let o=xt.indexOf(n);o=xt.indexOf(e);o--){const r=xt[o];if(bi[r].common&&n._adapter.diff(s,i,r)>=t-1)return r}return xt[e?xt.indexOf(e):0]}function Fg(n){for(let t=xt.indexOf(n)+1,e=xt.length;t=t?e[i]:e[s];n[o]=!0}}function Ig(n,t,e,i){const s=n._adapter,o=+s.startOf(t[0].value,i),r=t[t.length-1].value;let a,l;for(a=o;a<=r;a=+s.add(a,1,i))l=e[a],l>=0&&(t[l].major=!0);return t}function ar(n,t,e){const i=[],s={},o=t.length;let r,a;for(r=0;r+t.value))}initOffsets(t=[]){let e=0,i=0,s,o;this.options.offset&&t.length&&(s=this.getDecimalForValue(t[0]),t.length===1?e=1-s:e=(this.getDecimalForValue(t[1])-s)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?i=o:i=(o-this.getDecimalForValue(t[t.length-2]))/2);const r=t.length<3?.5:.25;e=ft(e,0,r),i=ft(i,0,r),this._offsets={start:e,end:i,factor:1/(e+1+i)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,o=s.time,r=o.unit||or(o.minUnit,e,i,this._getLabelCapacity(e)),a=S(s.ticks.stepSize,1),l=r==="week"?o.isoWeekday:!1,c=un(l)||l===!0,u={};let h=e,d,f;if(c&&(h=+t.startOf(h,"isoWeek",l)),h=+t.startOf(h,c?"day":r),t.diff(i,e,r)>1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+r);const g=s.ticks.source==="data"&&this.getDataTimestamps();for(d=h,f=0;d+p)}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const s=this.options.time.displayFormats,o=this._unit,r=e||s[o];return this._adapter.format(t,r)}_tickFormatFunction(t,e,i,s){const o=this.options,r=o.ticks.callback;if(r)return X(r,[t,e,i],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,u=l&&a[l],h=c&&a[c],d=i[e],f=c&&h&&d&&d.major;return this._adapter.format(t,s||(f?h:u))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e0?a:1}getDataTimestamps(){let t=this._cache.data||[],e,i;if(t.length)return t;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(e=0,i=s.length;e=n[i].pos&&t<=n[s].pos&&({lo:i,hi:s}=xe(n,"pos",t)),{pos:o,time:a}=n[i],{pos:r,time:l}=n[s]):(t>=n[i].time&&t<=n[s].time&&({lo:i,hi:s}=xe(n,"time",t)),{time:o,pos:a}=n[i],{time:r,pos:l}=n[s]);const c=r-o;return c?a+(l-a)*(t-o)/c:a}class lr extends pn{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Bn(e,this.min),this._tableRange=Bn(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],o=[];let r,a,l,c,u;for(r=0,a=t.length;r=e&&c<=i&&s.push(c);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(r=0,a=s.length;rs-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return e.length&&i.length?t=this.normalize(e.concat(i)):t=e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(Bn(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return Bn(this._table,i*this._tableRange+this._minPos,!0)}}k(lr,"id","timeseries"),k(lr,"defaults",pn.defaults);function yi(n){return n==="sankey"?{type:"sankey",data:{datasets:[]},options:{animations:!1}}:n==="pie"?{type:"pie",data:{datasets:[]}}:n==="column"?{type:"bar",data:{labels:[],datasets:[]},options:{plugins:{tooltip:{callbacks:{label:function(t){let e=t.dataset.currency_code;return V(t.raw,e)}}}},maintainAspectRatio:!1,scales:{}}}:n==="line"?{options:{plugins:{tooltip:{callbacks:{label:function(t){let e=t.dataset.currency_code;return V(t.raw,e)}}}},maintainAspectRatio:!1,scales:{x:{type:"time",time:{tooltipFormat:"PP"}}}},type:"line",data:{labels:[],datasets:[]}}:{}}let ri=new dt("#36a2eb"),Re=new dt("#ff6384"),mn=new dt("#4bc0c0"),ka=new dt("#ff9f40"),Eg=new dt("#9966ff"),zg=new dt("#ffcd56"),Bg=new dt("#c9cbcf"),cr=0;window.theme==="dark"&&(Re.darken(.3).desaturate(.3),mn.darken(.3).desaturate(.3),ri.darken(.3).desaturate(.3),ka.darken(.3).desaturate(.3));let Fi=[Re,ka,ri,mn,Eg,zg,Bg,mn];function De(n,t){let e={borderColor:Re.rgbString(),backgroundColor:Re.rgbString()},i;switch(n){default:let o=Math.floor(cr/2)%Fi.length;i=new dt(Fi[o].rgbString()),i.lighten(.38),e={borderColor:Fi[o].hexString(),backgroundColor:i.hexString()};break;case"spent":i=new dt(ri.rgbString()),i.lighten(.38),e={borderColor:ri.rgbString(),backgroundColor:i.rgbString()};break;case"left":i=new dt(mn.rgbString()),i.lighten(.38),e={borderColor:mn.rgbString(),backgroundColor:i.rgbString()};break;case"overspent":i=new dt(Re.rgbString()),i.lighten(.22),e={borderColor:Re.rgbString(),backgroundColor:i.rgbString()};break}return cr++,t==="border"?e.borderColor:t==="background"?e.backgroundColor:"#FF0000"}let me=[],je=null,Ii=null,Ei=!1;const Ng=()=>({loading:!1,loadingAccounts:!1,accountList:[],autoConversion:!1,chartOptions:null,switchAutoConversion(){this.autoConversion=!this.autoConversion,Bc("autoConversion",this.autoConversion)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Nt("dashboard-accounts-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){console.log(s),this.drawChart(this.generateOptions(s)),this.loading=!1;return}new Nc().dashboard(n,t,null).then(r=>{this.chartData=r.data,window.store.set(e,r.data),console.log(r.data),this.drawChart(this.generateOptions(this.chartData)),this.loading=!1})},generateOptions(n){me=[];let t=yi("line");for(let e=0;e0){this.loadingAccounts=!1;return}const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Nt("dashboard-accounts-data",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){this.accountList=s,this.loadingAccounts=!1;return}const o=10;let r=0,a=0,l=[];Promise.all([bt("frontpageAccounts")]).then(c=>{r=c[0].length;for(let u in c[0]){let h=c[0];if(h.hasOwnProperty(u)){let d=h[u];new Bs().get(d,new Date(window.store.get("end"))).then(f=>{let g=f.data.data;const p={page:1,start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))};new Bs().transactions(g.id,p).then(m=>{let b=[];for(let y=0;y=o);y++){let x=m.data.data[y],v={title:x.attributes.group_title===null?"":x.attributes.group_title,id:x.id,transactions:[]};for(let _=0;_y.order-x.order),this.accountList=l,this.loadingAccounts=!1,window.store.set(e,l))})})}}})},init(){Promise.all([bt("viewRange","1M"),bt("autoConversion",!1),bt("language","en_US")]).then(n=>{this.autoConversion=n[1],Ei=!0,this.loadChart(),this.loadAccounts()}),window.store.observe("end",()=>{Ei&&(Ii=null,this.accountList=[],this.loadChart(),this.loadAccounts())}),window.store.observe("autoConversion",()=>{Ei&&(this.loadChart(),this.loadAccounts())})}});let Wg=class{dashboard(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/budget/dashboard",{params:{start:i,end:s}})}},Ue=[],Nn=null,te=null,zi=!1,Te;const Hg=()=>({loading:!1,autoConversion:!1,loadChart(){if(this.loading!==!0){if(this.loading=!0,te!==null){this.drawChart(this.generateOptions(te)),this.loading=!1;return}this.getFreshData()}},drawChart(n){if(Nn!==null){Nn.data.datasets=n.data.datasets,Nn.update();return}Nn=new mt(document.querySelector("#budget-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Nt("dashboard-budgets-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){te=s,this.drawChart(this.generateOptions(te)),this.loading=!1;return}new Wg().dashboard(n,t,null).then(r=>{te=r.data,this.drawChart(this.generateOptions(te)),window.store.set(e,te),this.loading=!1})},generateOptions(n){Ue=[];let t=yi("column");t.options.locale=window.store.get("locale").replace("_","-"),t.options.plugins={tooltip:{callbacks:{title:function(e){return e.label},label:function(e){let i=e.dataset.label||"";return i&&(i+=": "),i+" "+V(e.parsed.y,Ue[e.parsed.x]??"EUR")}}}},t.data={labels:[],datasets:[{label:Te.t("firefly.spent"),data:[],borderWidth:1,stack:1,backgroundColor:De("spent","background"),borderColor:De("spent","border")},{label:Te.t("firefly.left"),data:[],borderWidth:1,stack:1,backgroundColor:De("left","background"),borderColor:De("left","border")},{label:Te.t("firefly.overspent"),data:[],borderWidth:1,stack:1,backgroundColor:De("overspent","background"),borderColor:De("overspent","border")}]};for(const e in n)if(n.hasOwnProperty(e)){let i=n[e],s=i.label+" ("+i.currency_code+")";t.data.labels.push(s),this.autoConversion&&(Ue.push(i.native_currency_code),t.data.datasets[0].data.push(parseFloat(i.native_entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(i.native_entries.left)),t.data.datasets[2].data.push(parseFloat(i.native_entries.overspent))),this.autoConversion||(Ue.push(i.currency_code),t.data.datasets[0].data.push(parseFloat(i.entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(i.entries.left)),t.data.datasets[2].data.push(parseFloat(i.entries.overspent)))}return t.options.scales={y:{ticks:{callback:function(e){return V(e,Ue[0]??"EUR")}}}},t},init(){Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{Te=new li;const t=n[1].replace("-","_");Te.locale=t,ci(Te,t).then(()=>{this.autoConversion=n[0],zi=!0,this.loading===!1&&this.loadChart()})}),window.store.observe("end",()=>{zi&&this.loading===!1&&(te=null,this.loadChart())}),window.store.observe("autoConversion",n=>{zi&&(this.autoConversion=n,this.loading===!1&&this.loadChart())})}});class Vg{dashboard(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/category/dashboard",{params:{start:i,end:s}})}}let ur=[],$e=null,Se=null,Bi=!1;const Yg=()=>({loading:!1,autoConversion:!1,generateOptions(n){ur=[];let t=yi("column"),e={};for(const s in n)if(n.hasOwnProperty(s)){let o=n[s],r=o.currency_code;this.autoConversion&&(r=o.native_currency_code),e.hasOwnProperty(r)||(e[r]={name:r,yAxisID:"",data:{}},ur.push(r))}for(const s in n)if(n.hasOwnProperty(s)){let o=n[s],r=o.currency_code;this.autoConversion&&(r=o.native_currency_code);for(const a in e)if(e.hasOwnProperty(a)){let l=0;r===a&&(l=parseFloat(o.amount),""+o.currency_code,this.autoConversion&&(l=parseFloat(o.native_amount),""+o.native_currency_code)),e[a].data.hasOwnProperty(o.label)&&(e[a].data[o.label]=e[a].data[o.label]+l),e[a].data.hasOwnProperty(o.label)||(e[a].data[o.label]=l)}t.data.labels.includes(o.label)||t.data.labels.push(o.label)}let i=0;for(const s in e){let o="y"+s,r={label:s,currency_code:s,yAxisID:o,data:[]};for(const a in e[s].data)r.data.push(e[s].data[a]);t.data.datasets.push(r),t.options.scales.hasOwnProperty(o)||(t.options.scales[o]={beginAtZero:!0,type:"linear",position:i===1?"right":"left",ticks:{callback:function(a,l,c){return V(a,s)}}},i++)}return t},drawChart(n){if($e!==null){$e.options=n.options,$e.data=n.data,$e.update();return}$e=new mt(document.querySelector("#category-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Nt("dashboard-categories-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Se=s,this.drawChart(this.generateOptions(Se)),this.loading=!1;return}new Vg().dashboard(n,t,null).then(r=>{Se=r.data,this.drawChart(this.generateOptions(r.data)),window.store.set(e,Se),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,Se!==null){this.drawChart(this.generateOptions(Se)),this.loading=!1;return}this.getFreshData()}},init(){Promise.all([bt("autoConversion",!1)]).then(n=>{this.autoConversion=n[0],Bi=!0,this.loadChart()}),window.store.observe("end",()=>{Bi&&(this.chartData=null,this.loadChart())}),window.store.observe("autoConversion",n=>{Bi&&(this.autoConversion=n,this.loadChart())})}});let jg=class{list(t){return vt.get("/api/v2/transactions",{params:t})}};/*! +`):n}function rg(n,t){const{element:e,datasetIndex:i,index:s}=t,o=n.getDatasetMeta(i).controller,{label:r,value:a}=o.getLabelAndValue(s);return{chart:n,label:r,parsed:o.getParsed(s),raw:n.data.datasets[i].data[s],formattedValue:a,dataset:o.getDataset(),dataIndex:s,datasetIndex:i,element:e}}function qo(n,t){const e=n.chart.ctx,{body:i,footer:s,title:o}=n,{boxWidth:r,boxHeight:a}=t,l=ut(t.bodyFont),c=ut(t.titleFont),u=ut(t.footerFont),h=o.length,d=s.length,f=i.length,g=yt(t.padding);let p=g.height,m=0,b=i.reduce((v,_)=>v+_.before.length+_.lines.length+_.after.length,0);if(b+=n.beforeBody.length+n.afterBody.length,h&&(p+=h*c.lineHeight+(h-1)*t.titleSpacing+t.titleMarginBottom),b){const v=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;p+=f*v+(b-f)*l.lineHeight+(b-1)*t.bodySpacing}d&&(p+=t.footerMarginTop+d*u.lineHeight+(d-1)*t.footerSpacing);let y=0;const x=function(v){m=Math.max(m,e.measureText(v).width+y)};return e.save(),e.font=c.string,$(n.title,x),e.font=l.string,$(n.beforeBody.concat(n.afterBody),x),y=t.displayColors?r+2+t.boxPadding:0,$(i,v=>{$(v.before,x),$(v.lines,x),$(v.after,x)}),y=0,e.font=u.string,$(n.footer,x),e.restore(),m+=g.width,{width:m,height:p}}function ag(n,t){const{y:e,height:i}=t;return en.height-i/2?"bottom":"center"}function lg(n,t,e,i){const{x:s,width:o}=i,r=e.caretSize+e.caretPadding;if(n==="left"&&s+o+r>t.width||n==="right"&&s-o-r<0)return!0}function cg(n,t,e,i){const{x:s,width:o}=e,{width:r,chartArea:{left:a,right:l}}=n;let c="center";return i==="center"?c=s<=(a+l)/2?"left":"right":s<=o/2?c="left":s>=r-o/2&&(c="right"),lg(c,n,t,e)&&(c="center"),c}function Xo(n,t,e){const i=e.yAlign||t.yAlign||ag(n,e);return{xAlign:e.xAlign||t.xAlign||cg(n,t,e,i),yAlign:i}}function ug(n,t){let{x:e,width:i}=n;return t==="right"?e-=i:t==="center"&&(e-=i/2),e}function hg(n,t,e){let{y:i,height:s}=n;return t==="top"?i+=e:t==="bottom"?i-=s+e:i-=s/2,i}function Ko(n,t,e,i){const{caretSize:s,caretPadding:o,cornerRadius:r}=n,{xAlign:a,yAlign:l}=e,c=s+o,{topLeft:u,topRight:h,bottomLeft:d,bottomRight:f}=ve(r);let g=ug(t,a);const p=hg(t,l,c);return l==="center"?a==="left"?g+=c:a==="right"&&(g-=c):a==="left"?g-=Math.max(u,d)+s:a==="right"&&(g+=Math.max(h,f)+s),{x:ft(g,0,i.width-t.width),y:ft(p,0,i.height-t.height)}}function En(n,t,e){const i=yt(e.padding);return t==="center"?n.x+n.width/2:t==="right"?n.x+n.width-i.right:n.x+i.left}function Go(n){return It([],Ut(n))}function dg(n,t,e){return he(n,{tooltip:t,tooltipItems:e,type:"tooltip"})}function Qo(n,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?n.override(e):n}const wa={beforeTitle:Ht,title(n){if(n.length>0){const t=n[0],e=t.chart.data.labels,i=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(i>0&&t.dataIndex"u"?wa[t].call(e,i):s}class is extends Rt{constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,o=new ea(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=dg(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:i}=e,s=_t(i,"beforeTitle",this,t),o=_t(i,"title",this,t),r=_t(i,"afterTitle",this,t);let a=[];return a=It(a,Ut(s)),a=It(a,Ut(o)),a=It(a,Ut(r)),a}getBeforeBody(t,e){return Go(_t(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:i}=e,s=[];return $(t,o=>{const r={before:[],lines:[],after:[]},a=Qo(i,o);It(r.before,Ut(_t(a,"beforeLabel",this,o))),It(r.lines,_t(a,"label",this,o)),It(r.after,Ut(_t(a,"afterLabel",this,o))),s.push(r)}),s}getAfterBody(t,e){return Go(_t(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:i}=e,s=_t(i,"beforeFooter",this,t),o=_t(i,"footer",this,t),r=_t(i,"afterFooter",this,t);let a=[];return a=It(a,Ut(s)),a=It(a,Ut(o)),a=It(a,Ut(r)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],o=[],r=[];let a=[],l,c;for(l=0,c=e.length;lt.filter(u,h,d,i))),t.itemSort&&(a=a.sort((u,h)=>t.itemSort(u,h,i))),$(a,u=>{const h=Qo(t.callbacks,u);s.push(_t(h,"labelColor",this,u)),o.push(_t(h,"labelPointStyle",this,u)),r.push(_t(h,"labelTextColor",this,u))}),this.labelColors=s,this.labelPointStyles=o,this.labelTextColors=r,this.dataPoints=a,a}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let o,r=[];if(!s.length)this.opacity!==0&&(o={opacity:0});else{const a=Je[i.position].call(this,s,this._eventPosition);r=this._createItems(i),this.title=this.getTitle(r,i),this.beforeBody=this.getBeforeBody(r,i),this.body=this.getBody(r,i),this.afterBody=this.getAfterBody(r,i),this.footer=this.getFooter(r,i);const l=this._size=qo(this,i),c=Object.assign({},a,l),u=Xo(this.chart,i,c),h=Ko(i,c,u,this.chart);this.xAlign=u.xAlign,this.yAlign=u.yAlign,o={opacity:1,x:h.x,y:h.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=r,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const o=this.getCaretPosition(t,i,s);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:o}=this,{caretSize:r,cornerRadius:a}=i,{topLeft:l,topRight:c,bottomLeft:u,bottomRight:h}=ve(a),{x:d,y:f}=t,{width:g,height:p}=e;let m,b,y,x,v,_;return o==="center"?(v=f+p/2,s==="left"?(m=d,b=m-r,x=v+r,_=v-r):(m=d+g,b=m+r,x=v-r,_=v+r),y=m):(s==="left"?b=d+Math.max(l,u)+r:s==="right"?b=d+g-Math.max(c,h)-r:b=this.caretX,o==="top"?(x=f,v=x-r,m=b-r,y=b+r):(x=f+p,v=x+r,m=b+r,y=b-r),_=x),{x1:m,x2:b,x3:y,y1:x,y2:v,y3:_}}drawTitle(t,e,i){const s=this.title,o=s.length;let r,a,l;if(o){const c=Fe(i.rtl,this.x,this.width);for(t.x=En(this,i.titleAlign,i),e.textAlign=c.textAlign(i.titleAlign),e.textBaseline="middle",r=ut(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=r.string,l=0;ly!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,dn(t,{x:p,y:g,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=r.backgroundColor,t.beginPath(),dn(t,{x:m,y:g+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(p,g,c,l),t.strokeRect(p,g,c,l),t.fillStyle=r.backgroundColor,t.fillRect(m,g+1,c-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:o,bodyAlign:r,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:u}=i,h=ut(i.bodyFont);let d=h.lineHeight,f=0;const g=Fe(i.rtl,this.x,this.width),p=function(M){e.fillText(M,g.x(t.x+f),t.y+d/2),t.y+=d+o},m=g.textAlign(r);let b,y,x,v,_,w,C;for(e.textAlign=r,e.textBaseline="middle",e.font=h.string,t.x=En(this,m,i),e.fillStyle=i.bodyColor,$(this.beforeBody,p),f=a&&m!=="right"?r==="center"?c/2+u:c+2+u:0,v=0,w=s.length;v0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,o=i&&i.y;if(s||o){const r=Je[t.position].call(this,this._active,this._eventPosition);if(!r)return;const a=this._size=qo(this,t),l=Object.assign({},r,this._size),c=Xo(e,t,l),u=Ko(t,l,c,e);(s._to!==u.x||o._to!==u.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=r.x,this.caretY=r.y,this._resolveAnimations().update(this,u))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},o={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const r=yt(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(o,t,s,e),Gr(t,e.textDirection),o.y+=r.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),Qr(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!Zn(i,s),r=this._positionChanged(s,e);(o||r)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,o=this._active||[],r=this._getActiveElements(t,o,e,i),a=this._positionChanged(r,t),l=e||!Zn(r,o)||a;return l&&(this._active=r,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,i,s){const o=this.options;if(t.type==="mouseout")return[];if(!s)return e.filter(a=>this.chart.data.datasets[a.datasetIndex]&&this.chart.getDatasetMeta(a.datasetIndex).controller.getParsed(a.index)!==void 0);const r=this.chart.getElementsAtEventForMode(t,o.mode,o,i);return o.reverse&&r.reverse(),r}_positionChanged(t,e){const{caretX:i,caretY:s,options:o}=this,r=Je[o.position].call(this,t,e);return r!==!1&&(i!==r.x||s!==r.y)}}k(is,"positioners",Je);var fg={id:"tooltip",_element:is,positioners:Je,afterInit(n,t,e){e&&(n.tooltip=new is({chart:n,options:e}))},beforeUpdate(n,t,e){n.tooltip&&n.tooltip.initialize(e)},reset(n,t,e){n.tooltip&&n.tooltip.initialize(e)},afterDraw(n){const t=n.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(n.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(n.ctx),n.notifyPlugins("afterTooltipDraw",e)}},afterEvent(n,t){if(n.tooltip){const e=t.replay;n.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(n,t)=>t.bodyFont.size,boxWidth:(n,t)=>t.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:wa},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:n=>n!=="filter"&&n!=="itemSort"&&n!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const gg=(n,t,e,i)=>(typeof t=="string"?(e=n.push(t)-1,i.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function pg(n,t,e,i){const s=n.indexOf(t);if(s===-1)return gg(n,t,e,i);const o=n.lastIndexOf(t);return s!==o?e:s}const mg=(n,t)=>n===null?null:ft(Math.round(n),0,t);function Zo(n){const t=this.getLabels();return n>=0&&ne.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}k(ss,"id","category"),k(ss,"defaults",{ticks:{callback:Zo}});function bg(n,t){const e=[],{bounds:s,step:o,min:r,max:a,precision:l,count:c,maxTicks:u,maxDigits:h,includeBounds:d}=n,f=o||1,g=u-1,{min:p,max:m}=t,b=!H(r),y=!H(a),x=!H(c),v=(m-p)/(h+1);let _=qs((m-p)/g/f)*f,w,C,M,T;if(_<1e-14&&!b&&!y)return[{value:p},{value:m}];T=Math.ceil(m/_)-Math.floor(p/_),T>g&&(_=qs(T*_/g/f)*f),H(l)||(w=Math.pow(10,l),_=Math.ceil(_*w)/w),s==="ticks"?(C=Math.floor(p/_)*_,M=Math.ceil(m/_)*_):(C=p,M=m),b&&y&&o&&yu((a-r)/o,_/1e3)?(T=Math.round(Math.min((a-r)/_,u)),_=(a-r)/T,C=r,M=a):x?(C=b?r:C,M=y?a:M,T=c-1,_=(M-C)/T):(T=(M-C)/_,nn(T,Math.round(T),_/1e3)?T=Math.round(T):T=Math.ceil(T));const A=Math.max(Xs(_),Xs(C));w=Math.pow(10,H(l)?A:l),C=Math.round(C*w)/w,M=Math.round(M*w)/w;let O=0;for(b&&(d&&C!==r?(e.push({value:r}),Ca)break;e.push({value:R})}return y&&d&&M!==a?e.length&&nn(e[e.length-1].value,a,Jo(a,v,n))?e[e.length-1].value=a:e.push({value:a}):(!y||M===a)&&e.push({value:M}),e}function Jo(n,t,{horizontal:e,minRotation:i}){const s=Lt(i),o=(e?Math.sin(s):Math.cos(s))||.001,r=.75*t*(""+n).length;return Math.min(t/o,r)}class oi extends ke{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return H(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:o}=this;const r=l=>s=e?s:l,a=l=>o=i?o:l;if(t){const l=Nt(s),c=Nt(o);l<0&&c<0?a(0):l>0&&c>0&&r(0)}if(s===o){let l=o===0?1:Math.abs(o*.05);a(o+l),t||r(s-l)}this.min=s,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:i}=t,s;return i?(s=Math.ceil(this.max/i)-Math.floor(this.min/i)+1,s>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${i} would result generating up to ${s} ticks. Limiting to 1000.`),s=1e3)):(s=this.computeTickLimit(),e=e||11),e&&(s=Math.min(e,s)),s}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const s={maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,r=bg(s,o);return t.bounds==="ticks"&&Ir(r,this,"value"),t.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return _n(t,this.chart.options.locale,this.options.ticks.format)}}class os extends oi{determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=et(t)?t:0,this.max=et(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=Lt(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/s))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}k(os,"id","linear"),k(os,"defaults",{ticks:{callback:fi.formatters.numeric}});const gn=n=>Math.floor(ie(n)),pe=(n,t)=>Math.pow(10,gn(n)+t);function tr(n){return n/Math.pow(10,gn(n))===1}function er(n,t,e){const i=Math.pow(10,e),s=Math.floor(n/i);return Math.ceil(t/i)-s}function yg(n,t){const e=t-n;let i=gn(e);for(;er(n,t,i)>10;)i++;for(;er(n,t,i)<10;)i--;return Math.min(i,gn(n))}function _g(n,{min:t,max:e}){t=Pt(n.min,t);const i=[],s=gn(t);let o=yg(t,e),r=o<0?Math.pow(10,Math.abs(o)):1;const a=Math.pow(10,o),l=s>o?Math.pow(10,s):0,c=Math.round((t-l)*r)/r,u=Math.floor((t-l)/a/10)*a*10;let h=Math.floor((c-u)/Math.pow(10,o)),d=Pt(n.min,Math.round((l+u+h*Math.pow(10,o))*r)/r);for(;d=10?h=h<15?15:20:h++,h>=20&&(o++,h=2,r=o>=0?1:r),d=Math.round((l+u+h*Math.pow(10,o))*r)/r;const f=Pt(n.max,d);return i.push({value:f,major:tr(f),significand:h}),i}class nr extends ke{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=oi.prototype.parse.apply(this,[t,e]);if(i===0){this._zero=!0;return}return et(i)&&i>0?i:null}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=et(t)?Math.max(0,t):null,this.max=et(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!et(this._userMin)&&(this.min=t===pe(this.min,0)?pe(this.min,-1):pe(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const o=a=>i=t?i:a,r=a=>s=e?s:a;i===s&&(i<=0?(o(1),r(10)):(o(pe(i,-1)),r(pe(s,1)))),i<=0&&o(pe(s,-1)),s<=0&&r(pe(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e={min:this._userMin,max:this._userMax},i=_g(e,this);return t.bounds==="ticks"&&Ir(i,this,"value"),t.reverse?(i.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),i}getLabelForValue(t){return t===void 0?"0":_n(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=ie(t),this._valueRange=ie(this.max)-ie(t)}getPixelForValue(t){return(t===void 0||t===0)&&(t=this.min),t===null||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(ie(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}k(nr,"id","logarithmic"),k(nr,"defaults",{ticks:{callback:fi.formatters.logarithmic,major:{enabled:!0}}});function rs(n){const t=n.ticks;if(t.display&&n.display){const e=yt(t.backdropPadding);return S(t.font&&t.font.size,nt.font.size)+e.height}return 0}function xg(n,t,e){return e=K(e)?e:[e],{w:zu(n,t.string,e),h:e.length*t.lineHeight}}function ir(n,t,e,i,s){return n===i||n===s?{start:t-e/2,end:t+e/2}:ns?{start:t-e,end:t}:{start:t,end:t+e}}function vg(n){const t={l:n.left+n._padding.left,r:n.right-n._padding.right,t:n.top+n._padding.top,b:n.bottom-n._padding.bottom},e=Object.assign({},t),i=[],s=[],o=n._pointLabels.length,r=n.options.pointLabels,a=r.centerPointLabels?tt/o:0;for(let l=0;lt.r&&(a=(i.end-t.r)/o,n.r=Math.max(n.r,t.r+a)),s.startt.b&&(l=(s.end-t.b)/r,n.b=Math.max(n.b,t.b+l))}function Mg(n,t,e){const i=n.drawingArea,{extra:s,additionalAngle:o,padding:r,size:a}=e,l=n.getPointPosition(t,i+s+r,o),c=Math.round(ps(Dt(l.angle+at))),u=Tg(l.y,a.h,c),h=Pg(c),d=Dg(l.x,a.w,h);return{visible:!0,x:l.x,y:u,textAlign:h,left:d,top:u,right:d+a.w,bottom:u+a.h}}function kg(n,t){if(!t)return!0;const{left:e,top:i,right:s,bottom:o}=n;return!(Xt({x:e,y:i},t)||Xt({x:e,y:o},t)||Xt({x:s,y:i},t)||Xt({x:s,y:o},t))}function Cg(n,t,e){const i=[],s=n._pointLabels.length,o=n.options,{centerPointLabels:r,display:a}=o.pointLabels,l={extra:rs(o)/2,additionalAngle:r?tt/s:0};let c;for(let u=0;u270||e<90)&&(n-=t),n}function Sg(n,t,e){const{left:i,top:s,right:o,bottom:r}=e,{backdropColor:a}=t;if(!H(a)){const l=ve(t.borderRadius),c=yt(t.backdropPadding);n.fillStyle=a;const u=i-c.left,h=s-c.top,d=o-i+c.width,f=r-s+c.height;Object.values(l).some(g=>g!==0)?(n.beginPath(),dn(n,{x:u,y:h,w:d,h:f,radius:l}),n.fill()):n.fillRect(u,h,d,f)}}function Og(n,t){const{ctx:e,options:{pointLabels:i}}=n;for(let s=t-1;s>=0;s--){const o=n._pointLabelItems[s];if(!o.visible)continue;const r=i.setContext(n.getPointLabelContext(s));Sg(e,r,o);const a=ut(r.font),{x:l,y:c,textAlign:u}=o;Ie(e,n._pointLabels[s],l,c+a.lineHeight/2,a,{color:r.color,textAlign:u,textBaseline:"middle"})}}function Ma(n,t,e,i){const{ctx:s}=n;if(e)s.arc(n.xCenter,n.yCenter,t,0,G);else{let o=n.getPointPosition(0,t);s.moveTo(o.x,o.y);for(let r=1;r{const s=X(this.options.pointLabels.callback,[e,i],this);return s||s===0?s:""}).filter((e,i)=>this.chart.getDataVisibility(i))}fit(){const t=this.options;t.display&&t.pointLabels.display?vg(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){const e=G/(this._pointLabels.length||1),i=this.options.startAngle||0;return Dt(t*e+Lt(i))}getDistanceFromCenterForValue(t){if(H(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(H(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t{if(h!==0){l=this.getDistanceFromCenterForValue(u.value);const d=this.getContext(h),f=s.setContext(d),g=o.setContext(d);Ag(this,f,l,r,g)}}),i.display){for(t.save(),a=r-1;a>=0;a--){const u=i.setContext(this.getPointLabelContext(a)),{color:h,lineWidth:d}=u;!d||!h||(t.lineWidth=d,t.strokeStyle=h,t.setLineDash(u.borderDash),t.lineDashOffset=u.borderDashOffset,l=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),c=this.getPointPosition(a,l),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(c.x,c.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let o,r;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach((a,l)=>{if(l===0&&!e.reverse)return;const c=i.setContext(this.getContext(l)),u=ut(c.font);if(o=this.getDistanceFromCenterForValue(this.ticks[l].value),c.showLabelBackdrop){t.font=u.string,r=t.measureText(a.label).width,t.fillStyle=c.backdropColor;const h=yt(c.backdropPadding);t.fillRect(-r/2-h.left,-o-u.size/2-h.top,r+h.width,u.size+h.height)}Ie(t,a.label,0,-o,u,{color:c.color,strokeColor:c.textStrokeColor,strokeWidth:c.textStrokeWidth})}),t.restore()}drawTitle(){}}k(zn,"id","radialLinear"),k(zn,"defaults",{display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:fi.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback(t){return t},padding:5,centerPointLabels:!1}}),k(zn,"defaultRoutes",{"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"}),k(zn,"descriptors",{angleLines:{_fallback:"grid"}});const bi={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},xt=Object.keys(bi);function sr(n,t){return n-t}function or(n,t){if(H(t))return null;const e=n._adapter,{parser:i,round:s,isoWeekday:o}=n._parseOpts;let r=t;return typeof i=="function"&&(r=i(r)),et(r)||(r=typeof i=="string"?e.parse(r,i):e.parse(r)),r===null?null:(s&&(r=s==="week"&&(un(o)||o===!0)?e.startOf(r,"isoWeek",o):e.startOf(r,s)),+r)}function rr(n,t,e,i){const s=xt.length;for(let o=xt.indexOf(n);o=xt.indexOf(e);o--){const r=xt[o];if(bi[r].common&&n._adapter.diff(s,i,r)>=t-1)return r}return xt[e?xt.indexOf(e):0]}function Fg(n){for(let t=xt.indexOf(n)+1,e=xt.length;t=t?e[i]:e[s];n[o]=!0}}function Ig(n,t,e,i){const s=n._adapter,o=+s.startOf(t[0].value,i),r=t[t.length-1].value;let a,l;for(a=o;a<=r;a=+s.add(a,1,i))l=e[a],l>=0&&(t[l].major=!0);return t}function lr(n,t,e){const i=[],s={},o=t.length;let r,a;for(r=0;r+t.value))}initOffsets(t=[]){let e=0,i=0,s,o;this.options.offset&&t.length&&(s=this.getDecimalForValue(t[0]),t.length===1?e=1-s:e=(this.getDecimalForValue(t[1])-s)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?i=o:i=(o-this.getDecimalForValue(t[t.length-2]))/2);const r=t.length<3?.5:.25;e=ft(e,0,r),i=ft(i,0,r),this._offsets={start:e,end:i,factor:1/(e+1+i)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,o=s.time,r=o.unit||rr(o.minUnit,e,i,this._getLabelCapacity(e)),a=S(s.ticks.stepSize,1),l=r==="week"?o.isoWeekday:!1,c=un(l)||l===!0,u={};let h=e,d,f;if(c&&(h=+t.startOf(h,"isoWeek",l)),h=+t.startOf(h,c?"day":r),t.diff(i,e,r)>1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+r);const g=s.ticks.source==="data"&&this.getDataTimestamps();for(d=h,f=0;d+p)}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const s=this.options.time.displayFormats,o=this._unit,r=e||s[o];return this._adapter.format(t,r)}_tickFormatFunction(t,e,i,s){const o=this.options,r=o.ticks.callback;if(r)return X(r,[t,e,i],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,u=l&&a[l],h=c&&a[c],d=i[e],f=c&&h&&d&&d.major;return this._adapter.format(t,s||(f?h:u))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e0?a:1}getDataTimestamps(){let t=this._cache.data||[],e,i;if(t.length)return t;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(e=0,i=s.length;e=n[i].pos&&t<=n[s].pos&&({lo:i,hi:s}=xe(n,"pos",t)),{pos:o,time:a}=n[i],{pos:r,time:l}=n[s]):(t>=n[i].time&&t<=n[s].time&&({lo:i,hi:s}=xe(n,"time",t)),{time:o,pos:a}=n[i],{time:r,pos:l}=n[s]);const c=r-o;return c?a+(l-a)*(t-o)/c:a}class cr extends pn{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=Bn(e,this.min),this._tableRange=Bn(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],o=[];let r,a,l,c,u;for(r=0,a=t.length;r=e&&c<=i&&s.push(c);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(r=0,a=s.length;rs-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return e.length&&i.length?t=this.normalize(e.concat(i)):t=e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(Bn(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return Bn(this._table,i*this._tableRange+this._minPos,!0)}}k(cr,"id","timeseries"),k(cr,"defaults",pn.defaults);function yi(n){return n==="sankey"?{type:"sankey",data:{datasets:[]},options:{animations:!1}}:n==="pie"?{type:"pie",data:{datasets:[]}}:n==="column"?{type:"bar",data:{labels:[],datasets:[]},options:{plugins:{tooltip:{callbacks:{label:function(t){let e=t.dataset.currency_code;return V(t.raw,e)}}}},maintainAspectRatio:!1,scales:{}}}:n==="line"?{options:{plugins:{tooltip:{callbacks:{label:function(t){let e=t.dataset.currency_code;return V(t.raw,e)}}}},maintainAspectRatio:!1,scales:{x:{type:"time",time:{tooltipFormat:"PP"}}}},type:"line",data:{labels:[],datasets:[]}}:{}}let ri=new dt("#36a2eb"),Re=new dt("#ff6384"),mn=new dt("#4bc0c0"),ka=new dt("#ff9f40"),Eg=new dt("#9966ff"),zg=new dt("#ffcd56"),Bg=new dt("#c9cbcf"),ur=0;window.theme==="dark"&&(Re.darken(.3).desaturate(.3),mn.darken(.3).desaturate(.3),ri.darken(.3).desaturate(.3),ka.darken(.3).desaturate(.3));let Fi=[Re,ka,ri,mn,Eg,zg,Bg,mn];function De(n,t){let e={borderColor:Re.rgbString(),backgroundColor:Re.rgbString()},i;switch(n){default:let o=Math.floor(ur/2)%Fi.length;i=new dt(Fi[o].rgbString()),i.lighten(.38),e={borderColor:Fi[o].hexString(),backgroundColor:i.hexString()};break;case"spent":i=new dt(ri.rgbString()),i.lighten(.38),e={borderColor:ri.rgbString(),backgroundColor:i.rgbString()};break;case"left":i=new dt(mn.rgbString()),i.lighten(.38),e={borderColor:mn.rgbString(),backgroundColor:i.rgbString()};break;case"overspent":i=new dt(Re.rgbString()),i.lighten(.22),e={borderColor:Re.rgbString(),backgroundColor:i.rgbString()};break}return ur++,t==="border"?e.borderColor:t==="background"?e.backgroundColor:"#FF0000"}let me=[],je=null,Ii=null,Ei=!1;const Ng=()=>({loading:!1,loadingAccounts:!1,accountList:[],autoConversion:!1,chartOptions:null,switchAutoConversion(){this.autoConversion=!this.autoConversion,Bc("autoConversion",this.autoConversion)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-accounts-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){console.log(s),this.drawChart(this.generateOptions(s)),this.loading=!1;return}new Nc().dashboard(n,t,null).then(r=>{this.chartData=r.data,window.store.set(e,r.data),console.log(r.data),this.drawChart(this.generateOptions(this.chartData)),this.loading=!1})},generateOptions(n){me=[];let t=yi("line");for(let e=0;e0){this.loadingAccounts=!1;return}const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-accounts-data",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){this.accountList=s,this.loadingAccounts=!1;return}const o=10;let r=0,a=0,l=[];Promise.all([bt("frontpageAccounts")]).then(c=>{r=c[0].length;for(let u in c[0]){let h=c[0];if(h.hasOwnProperty(u)){let d=h[u];new Ns().get(d,new Date(window.store.get("end"))).then(f=>{let g=f.data.data;const p={page:1,start:new Date(window.store.get("start")),end:new Date(window.store.get("end"))};new Ns().transactions(g.id,p).then(m=>{let b=[];for(let y=0;y=o);y++){let x=m.data.data[y],v={title:x.attributes.group_title===null?"":x.attributes.group_title,id:x.id,transactions:[]};for(let _=0;_y.order-x.order),this.accountList=l,this.loadingAccounts=!1,window.store.set(e,l))})})}}})},init(){Promise.all([bt("viewRange","1M"),bt("autoConversion",!1),bt("language","en_US")]).then(n=>{this.autoConversion=n[1],Ei=!0,this.loadChart(),this.loadAccounts()}),window.store.observe("end",()=>{Ei&&(Ii=null,this.accountList=[],this.loadChart(),this.loadAccounts())}),window.store.observe("autoConversion",()=>{Ei&&(this.loadChart(),this.loadAccounts())})}});let Wg=class{dashboard(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/budget/dashboard",{params:{start:i,end:s}})}},Ue=[],Nn=null,te=null,zi=!1,Te;const Hg=()=>({loading:!1,autoConversion:!1,loadChart(){if(this.loading!==!0){if(this.loading=!0,te!==null){this.drawChart(this.generateOptions(te)),this.loading=!1;return}this.getFreshData()}},drawChart(n){if(Nn!==null){Nn.data.datasets=n.data.datasets,Nn.update();return}Nn=new mt(document.querySelector("#budget-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-budgets-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){te=s,this.drawChart(this.generateOptions(te)),this.loading=!1;return}new Wg().dashboard(n,t,null).then(r=>{te=r.data,this.drawChart(this.generateOptions(te)),window.store.set(e,te),this.loading=!1})},generateOptions(n){Ue=[];let t=yi("column");t.options.locale=window.store.get("locale").replace("_","-"),t.options.plugins={tooltip:{callbacks:{title:function(e){return e.label},label:function(e){let i=e.dataset.label||"";return i&&(i+=": "),i+" "+V(e.parsed.y,Ue[e.parsed.x]??"EUR")}}}},t.data={labels:[],datasets:[{label:Te.t("firefly.spent"),data:[],borderWidth:1,stack:1,backgroundColor:De("spent","background"),borderColor:De("spent","border")},{label:Te.t("firefly.left"),data:[],borderWidth:1,stack:1,backgroundColor:De("left","background"),borderColor:De("left","border")},{label:Te.t("firefly.overspent"),data:[],borderWidth:1,stack:1,backgroundColor:De("overspent","background"),borderColor:De("overspent","border")}]};for(const e in n)if(n.hasOwnProperty(e)){let i=n[e],s=i.label+" ("+i.currency_code+")";t.data.labels.push(s),this.autoConversion&&(Ue.push(i.native_currency_code),t.data.datasets[0].data.push(parseFloat(i.native_entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(i.native_entries.left)),t.data.datasets[2].data.push(parseFloat(i.native_entries.overspent))),this.autoConversion||(Ue.push(i.currency_code),t.data.datasets[0].data.push(parseFloat(i.entries.spent)*-1),t.data.datasets[1].data.push(parseFloat(i.entries.left)),t.data.datasets[2].data.push(parseFloat(i.entries.overspent)))}return t.options.scales={y:{ticks:{callback:function(e){return V(e,Ue[0]??"EUR")}}}},t},init(){Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{Te=new li;const t=n[1].replace("-","_");Te.locale=t,ci(Te,t).then(()=>{this.autoConversion=n[0],zi=!0,this.loading===!1&&this.loadChart()})}),window.store.observe("end",()=>{zi&&this.loading===!1&&(te=null,this.loadChart())}),window.store.observe("autoConversion",n=>{zi&&(this.autoConversion=n,this.loading===!1&&this.loadChart())})}});class Vg{dashboard(t,e){let i=J(t,"y-MM-dd"),s=J(e,"y-MM-dd");return vt.get("/api/v2/chart/category/dashboard",{params:{start:i,end:s}})}}let hr=[],$e=null,Se=null,Bi=!1;const Yg=()=>({loading:!1,autoConversion:!1,generateOptions(n){hr=[];let t=yi("column"),e={};for(const s in n)if(n.hasOwnProperty(s)){let o=n[s],r=o.currency_code;this.autoConversion&&(r=o.native_currency_code),e.hasOwnProperty(r)||(e[r]={name:r,yAxisID:"",data:{}},hr.push(r))}for(const s in n)if(n.hasOwnProperty(s)){let o=n[s],r=o.currency_code;this.autoConversion&&(r=o.native_currency_code);for(const a in e)if(e.hasOwnProperty(a)){let l=0;r===a&&(l=parseFloat(o.amount),""+o.currency_code,this.autoConversion&&(l=parseFloat(o.native_amount),""+o.native_currency_code)),e[a].data.hasOwnProperty(o.label)&&(e[a].data[o.label]=e[a].data[o.label]+l),e[a].data.hasOwnProperty(o.label)||(e[a].data[o.label]=l)}t.data.labels.includes(o.label)||t.data.labels.push(o.label)}let i=0;for(const s in e){let o="y"+s,r={label:s,currency_code:s,yAxisID:o,data:[]};for(const a in e[s].data)r.data.push(e[s].data[a]);t.data.datasets.push(r),t.options.scales.hasOwnProperty(o)||(t.options.scales[o]={beginAtZero:!0,type:"linear",position:i===1?"right":"left",ticks:{callback:function(a,l,c){return V(a,s)}}},i++)}return t},drawChart(n){if($e!==null){$e.options=n.options,$e.data=n.data,$e.update();return}$e=new mt(document.querySelector("#category-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt("dashboard-categories-chart",n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Se=s,this.drawChart(this.generateOptions(Se)),this.loading=!1;return}new Vg().dashboard(n,t,null).then(r=>{Se=r.data,this.drawChart(this.generateOptions(r.data)),window.store.set(e,Se),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,Se!==null){this.drawChart(this.generateOptions(Se)),this.loading=!1;return}this.getFreshData()}},init(){Promise.all([bt("autoConversion",!1)]).then(n=>{this.autoConversion=n[0],Bi=!0,this.loadChart()}),window.store.observe("end",()=>{Bi&&(this.chartData=null,this.loadChart())}),window.store.observe("autoConversion",n=>{Bi&&(this.autoConversion=n,this.loadChart())})}});let jg=class{list(t){return vt.get("/api/v2/transactions",{params:t})}};/*! * chartjs-chart-sankey v0.12.0 * https://github.com/kurkle/chartjs-chart-sankey#readme * (c) 2022 Jukka Kurkela * Released under the MIT license */function Ug(n){const t=[],e=K(n)?n:H(n)?[]:[n];for(;e.length;){const i=e.pop();typeof i=="string"?t.unshift.apply(t,i.split(` -`)):Array.isArray(i)?e.push.apply(e,i):H(e)||t.unshift(""+i)}return t}function Ni(n){return!n||["min","max"].indexOf(n)===-1?"max":n}const ae=n=>n!==void 0;function $g(n,t){const e=new Set(t.map(r=>r.to)),i=new Set(t.map(r=>r.from)),s=new Set([...n.keys()]);let o=0;for(;s.size;){const r=qg([...s],e);for(const a of r){const l=n.get(a);ae(l.x)||(l.x=o),s.delete(a)}s.size&&(e.clear(),t.filter(a=>s.has(a.from)).forEach(a=>e.add(a.to)),o++)}return[...n.keys()].filter(r=>!i.has(r)).forEach(r=>{const a=n.get(r);a.column||(a.x=o)}),o}function qg(n,t){const e=n.filter(i=>!t.has(i));return e.length?e:n.slice(0,1)}const Xg=(n,t)=>n.x!==t.x?n.x-t.x:n.y-t.y;let Wn=-1;function Kg(){return Wn=Wn<100?Wn+1:0,Wn}function as(n,t,e=Kg()){let i=0;for(const s of n)s.node._visited!==e&&(s.node._visited=e,i+=s.node[t].length+as(s.node[t],t,e));return i}const Ca=n=>(t,e)=>as(t.node[n],n)-as(e.node[n],n)||t.node[n].length-e.node[n].length;function Cs(n,t){n.from.sort(Ca("from"));for(const e of n.from){const i=e.node;ae(i.y)||(i.y=t,Cs(i,t)),t=Math.max(i.y+i.out,t)}return t}function _e(n,t){n.to.sort(Ca("to"));for(const e of n.to){const i=e.node;ae(i.y)||(i.y=t,_e(i,t)),t=Math.max(i.y+i.in,t)}return t}function qe(n,t){return ae(n.y)?n.y:(n.y=t,t)}function Gg(n,t){const e=n.filter(u=>u.x===0),i=n.filter(u=>u.x===t),s=e.filter(u=>!ae(u.y)),o=i.filter(u=>!ae(u.y)),r=n.filter(u=>u.x>0&&u.xMath.max(u,h.y+h.out||0),0),l=i.reduce((u,h)=>Math.max(u,h.y+h.in||0),0),c=0;return a>=l?(s.forEach(u=>{a=qe(u,a),a=Math.max(a+u.out,_e(u,a))}),o.forEach(u=>{l=qe(u,l),l=Math.max(l+u.in,_e(u,l))})):(o.forEach(u=>{l=qe(u,l),l=Math.max(l+u.in,_e(u,l))}),s.forEach(u=>{a=qe(u,a),a=Math.max(a+u.out,_e(u,a))})),r.forEach(u=>{let h=n.filter(d=>d.x===u.x&&ae(d.y)).reduce((d,f)=>Math.max(d,f.y+Math.max(f.in,f.out)),0);h=qe(u,h),h=Math.max(h+u.in,Cs(u,h)),h=Math.max(h+u.out,_e(u,h)),c=Math.max(c,h)}),Math.max(a,l,c)}function Qg(n,t){n.sort((r,a)=>Math.max(a.in,a.out)-Math.max(r.in,r.out));const e=n[0];e.y=0;const i=Cs(e,0),s=_e(e,0),o=Gg(n,t);return Math.max(i,s,o)}function Zg(n,t){let e=0,i=0;for(let s=0;s<=t;s++){let o=i;const r=n.filter(a=>a.x===s).sort((a,l)=>a.priority-l.priority);i=r[0].to.filter(a=>a.node.x>s+1).reduce((a,l)=>a+l.flow,0)||0;for(const a of r)a.y=o,o+=Math.max(a.out,a.in);e=Math.max(o,e)}return e}function Jg(n,t){let e=1,i=0,s=0,o=0;const r=[];n.sort(Xg);for(const a of n){if(a.y){if(a.x===0)r.push(a.y);else{for(i!==a.x&&(i=a.x,s=0),e=s+1;ea.y);e++);s=e}a.y+=e*t,e++}o=Math.max(o,a.y+Math.max(a.in,a.out))}return o}function tp(n,t){n.forEach(e=>{const i=Math[t](e.in||e.out,e.out||e.in),s=il.node.y+l.node.out/2-(c.node.y+c.node.out/2)).forEach((l,c)=>{s?l.addY=c*(i-l.flow)/(a-1):(l.addY=r,r+=l.flow)}),r=0,a=e.to.length,e.to.sort((l,c)=>l.node.y+l.node.in/2-(c.node.y+c.node.in/2)).forEach((l,c)=>{o?l.addY=c*(i-l.flow)/(a-1):(l.addY=r,r+=l.flow)})})}function ep(n,t,e,i){const s=[...n.values()],o=$g(n,t),a=(e?Zg(s,o):Qg(s,o))*.03,l=Jg(s,a);return tp(s,i),{maxX:o,maxY:l}}function np(n){const t=new Map;for(let i=0;is.flow-i.flow;return[...t.values()].forEach(i=>{i.from=i.from.sort(e),i.from.forEach(s=>{s.node=t.get(s.key)}),i.to=i.to.sort(e),i.to.forEach(s=>{s.node=t.get(s.key)})}),t}function hr(n,t,e){for(const i of n)if(i.key===t&&i.index===e)return i.addY;return 0}class _i extends Xt{parseObjectData(t,e,i,s){const{from:o="from",to:r="to",flow:a="flow"}=this.options.parsing,l=e.map(({[o]:y,[r]:x,[a]:v})=>({from:y,to:x,flow:v})),{xScale:c,yScale:u}=t,h=[],d=this._nodes=np(l),{column:f,priority:g,size:p}=this.getDataset();if(g)for(const y of d.values())y.key in g&&(y.priority=g[y.key]);if(f)for(const y of d.values())y.key in f&&(y.column=!0,y.x=f[y.key]);const{maxX:m,maxY:b}=ep(d,l,!!g,Ni(p));this._maxX=m,this._maxY=b;for(let y=0,x=l.length;y1){const d=c-u*l/2+h;for(let f=0;fn.type==="data"?(n.parsed._custom.x-n.parsed.x)*200:void 0,delay:n=>n.type==="data"?n.parsed.x*500+n.dataIndex*20:void 0},colors:{type:"color",properties:["colorFrom","colorTo"]}},transitions:{hide:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],to:"transparent"}}},show:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],from:"transparent"}}}}};_i.overrides={interaction:{mode:"nearest",intersect:!0},datasets:{clip:!1,parsing:!0},plugins:{tooltip:{callbacks:{title(){return""},label(n){const t=n.dataset.data[n.dataIndex];return t.from+" -> "+t.to+": "+t.flow}}},legend:{display:!1}},scales:{x:{type:"linear",bounds:"data",display:!1,min:0,offset:!1},y:{type:"linear",bounds:"data",display:!1,min:0,reverse:!0,offset:!1}},layout:{padding:{top:3,left:3,right:13,bottom:3}}};const dr=(n,t,e,i)=>n({x:n.x+e*(t.x-n.x),y:n.y+e*(t.y-n.y)});function ip(n,{x:t,x2:e,options:i}){let s;i.colorMode==="from"?s=Ae(i.colorFrom).alpha(.5).rgbString():i.colorMode==="to"?s=Ae(i.colorTo).alpha(.5).rgbString():(s=n.createLinearGradient(t,0,e,0),s.addColorStop(0,Ae(i.colorFrom).alpha(.5).rgbString()),s.addColorStop(1,Ae(i.colorTo).alpha(.5).rgbString())),n.fillStyle=s,n.strokeStyle=s,n.lineWidth=.5}class Ps extends Lt{constructor(t){super(),this.options=void 0,this.x=void 0,this.y=void 0,this.x2=void 0,this.y2=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t){const e=this,{x:i,x2:s,y:o,y2:r,height:a,progress:l}=e,{cp1:c,cp2:u}=dr(i,o,s,r);l!==0&&(t.save(),l<1&&(t.beginPath(),t.rect(i,Math.min(o,r),(s-i)*l+1,Math.abs(r-o)+a+1),t.clip()),ip(t,e),t.beginPath(),t.moveTo(i,o),t.bezierCurveTo(c.x,c.y,u.x,u.y,s,r),t.lineTo(s,r+a),t.bezierCurveTo(u.x,u.y+a,c.x,c.y+a,i,o+a),t.lineTo(i,o),t.stroke(),t.closePath(),t.fill(),t.restore())}inRange(t,e,i){const{x:s,y:o,x2:r,y2:a,height:l}=this.getProps(["x","y","x2","y2","height"],i);if(tr)return!1;const{cp1:c,cp2:u}=dr(s,o,r,a),h=(t-s)/(r-s),d={x:s,y:o},f={x:r,y:a},g=Oe(d,c,h),p=Oe(c,u,h),m=Oe(u,f,h),b=Oe(g,p,h),y=Oe(p,m,h),x=Oe(b,y,h).y;return e>=x&&e<=x+l}inXRange(t,e){const{x:i,x2:s}=this.getProps(["x","x2"],e);return t>=i&&t<=s}inYRange(t,e){const{y:i,y2:s,height:o}=this.getProps(["y","y2","height"],e),r=Math.min(i,s),a=Math.max(i,s)+o;return t>=r&&t<=a}getCenterPoint(t){const{x:e,y:i,x2:s,y2:o,height:r}=this.getProps(["x","y","x2","y2","height"],t);return{x:(e+s)/2,y:(i+o+r)/2}}tooltipPosition(t){return this.getCenterPoint(t)}getRange(t){return t==="x"?this.width/2:this.height/2}}Ps.id="flow";Ps.defaults={colorFrom:"red",colorTo:"green",colorMode:"gradient",hoverColorFrom:(n,t)=>on(t.colorFrom),hoverColorTo:(n,t)=>on(t.colorTo)};mt.register({SankeyController:_i,Flow:Ps});const fr="dashboard-sankey-data";let pt,Wi=!1,Hn=null,Ht=[],ht=!1,L={category:null,unknown_category:null,in:null,out:null,unknown_source:null,unknown_dest:null,unknown_account:null,expense_account:null,revenue_account:null,budget:null,unknown_budget:null,all_money:null};const gr=function(n){return n.includes(L.revenue_account)?"forestgreen":n.includes("("+L.in+",")?"green":n.includes(L.budget)||n.includes(L.unknown_budget)?"Orchid":n.includes("("+L.out+",")?"MediumOrchid":n.includes(L.all_money)?"blue":"red"};function Xe(n,t,e,i){if(n==="category"&&t!==null&&e==="in")return L.category+' "'+t+'" ('+L.in+(ht?", "+i+")":")");if(n==="category"&&t===null&&e==="in")return L.unknown_category+" ("+L.in+(ht?", "+i+")":")");if(n==="category"&&t!==null&&e==="out")return L.category+' "'+t+'" ('+L.out+(ht?", "+i+")":")");if(n==="category"&&t===null&&e==="out")return L.unknown_category+" ("+L.out+(ht?", "+i+")":")");if(n==="account"&&t===null&&e==="in")return L.unknown_source+(ht?" ("+i+")":"");if(n==="account"&&t!==null&&e==="in")return L.revenue_account+'"'+t+'"'+(ht?" ("+i+")":"");if(n==="account"&&t===null&&e==="out")return L.unknown_dest+(ht?" ("+i+")":"");if(n==="account"&&t!==null&&e==="out")return L.expense_account+' "'+t+'"'+(ht?" ("+i+")":"");if(n==="budget"&&t!==null)return L.budget+' "'+t+'"'+(ht?" ("+i+")":"");if(n==="budget"&&t===null)return L.unknown_budget+(ht?" ("+i+")":"");console.error('Cannot handle: type:"'+n+'", dir: "'+e+'"')}function Ke(n,t,e){if(n==="category"&&t!==null)return L.category+' "'+t+'"'+(ht?" ("+e+")":"");if(n==="category"&&t===null)return L.unknown_category+(ht?" ("+e+")":"");if(n==="account"&&t===null)return L.unknown_account+(ht?" ("+e+")":"");if(n==="account"&&t!==null)return t+(ht?" ("+e+")":"");if(n==="budget"&&t!==null)return L.budget+' "'+t+'"'+(ht?" ("+e+")":"");if(n==="budget"&&t===null)return L.unknown_budget+(ht?" ("+e+")":"");console.error('Cannot handle: type:"'+n+'"')}const sp=()=>({loading:!1,autoConversion:!1,generateOptions(){let n=yi("sankey"),t={},e={};for(let s in Ht)if(Ht.hasOwnProperty(s)){let o=Ht[s];for(let r in o.attributes.transactions)if(o.attributes.transactions.hasOwnProperty(r)){let a=o.attributes.transactions[r],l=this.autoConversion?a.native_currency_code:a.currency_code,c=this.autoConversion?parseFloat(a.native_amount):parseFloat(a.amount),u;if(a.type==="deposit"){let h=Xe("category",a.category_name,"in",l),d=Xe("account",a.source_name,"in",l);e[h]=Ke("category",a.category_name,l),e[d]=Ke("account",a.source_name,l),u=d+"-"+h+"-"+l,t.hasOwnProperty(u)||(t[u]={from:d,to:h,amount:0}),t[u].amount+=c,u=h+"-"+L.all_money+"-"+l,t.hasOwnProperty(u)||(t[u]={from:h,to:L.all_money+(this.autoConversion?" ("+l+")":""),amount:0}),t[u].amount+=c}if(a.type==="withdrawal"){let h=Xe("budget",a.budget_name,"out",l);e[h]=Ke("budget",a.budget_name,l),u=L.all_money+"-"+h+"-"+l,t.hasOwnProperty(u)||(t[u]={from:L.all_money+(this.autoConversion?" ("+l+")":""),to:h,amount:0}),t[u].amount+=c;let d=Xe("category",a.category_name,"out",l);e[d]=Ke("category",a.category_name,l),u=h+"-"+d+"-"+l,t.hasOwnProperty(u)||(t[u]={from:h,to:d,amount:0}),t[u].amount+=c;let f=Xe("account",a.destination_name,"out",l);e[f]=Ke("account",a.destination_name,l),u=d+"-"+f+"-"+l,t.hasOwnProperty(u)||(t[u]={from:d,to:f,amount:0}),t[u].amount+=c}}}let i={label:"Firefly III dashboard sankey chart",data:[],colorFrom:s=>gr(s.dataset.data[s.dataIndex]?s.dataset.data[s.dataIndex].from:""),colorTo:s=>gr(s.dataset.data[s.dataIndex]?s.dataset.data[s.dataIndex].to:""),colorMode:"gradient",labels:e,size:"min"};for(let s in t)if(t.hasOwnProperty(s)){let o=t[s];i.data.push({from:o.from,to:o.to,flow:o.amount})}return n.data.datasets.push(i),n},drawChart(n){if(Hn!==null){Hn.data.datasets=n.data.datasets,Hn.update();return}Hn=new mt(document.querySelector("#sankey-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Nt(fr,n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Ht=s,this.drawChart(this.generateOptions()),this.loading=!1;return}let o={start:J(n,"y-MM-dd"),end:J(t,"y-MM-dd"),type:"withdrawal,deposit",page:1};this.downloadTransactions(o)},downloadTransactions(n){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),i=Nt(fr,t,e);new jg().list(n).then(o=>{if(Ht=[...Ht,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>n.page){n.page++,this.downloadTransactions(n);return}window.store.set(i,Ht),this.drawChart(this.generateOptions()),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,Ht.length!==0){this.drawChart(this.generateOptions()),this.loading=!1;return}this.getFreshData()}},init(){Ht=[],Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{this.autoConversion=n[0],ht=n[0],pt=new li;const t=n[1].replace("-","_");pt.locale=t,ci(pt,t).then(()=>{L.all_money=pt.t("firefly.all_money"),L.category=pt.t("firefly.category"),L.in=pt.t("firefly.money_flowing_in"),L.out=pt.t("firefly.money_flowing_out"),L.unknown_category=pt.t("firefly.unknown_category_plain"),L.unknown_source=pt.t("firefly.unknown_source_plain"),L.unknown_dest=pt.t("firefly.unknown_dest_plain"),L.unknown_account=pt.t("firefly.unknown_any_plain"),L.unknown_budget=pt.t("firefly.unknown_budget_plain"),L.expense_account=pt.t("firefly.expense_account"),L.revenue_account=pt.t("firefly.revenue_account"),L.budget=pt.t("firefly.budget"),Wi=!0,this.loadChart()})}),window.store.observe("end",()=>{Wi&&(this.transactions=[],this.loadChart())}),window.store.observe("autoConversion",n=>{Wi&&(this.autoConversion=n,this.loadChart())})}});let op=class{list(t){return vt.get("/api/v2/subscriptions",{params:t})}paid(t){return vt.get("/api/v2/subscriptions/sum/paid",{params:t})}unpaid(t){return vt.get("/api/v2/subscriptions/sum/unpaid",{params:t})}},Hi=!1,ee,Ct={};function Pa(n){return new op().list(n).then(e=>{let i=e.data.data;for(let s in i)if(i.hasOwnProperty(s)){let o=i[s];if(o.attributes.active&&o.attributes.pay_dates.length>0){let r=o.attributes.object_group_id===null?0:o.attributes.object_group_id,a=o.attributes.object_group_title===null?ee.t("firefly.default_group_title_name_plain"):o.attributes.object_group_title,l=o.attributes.object_group_order===null?0:o.attributes.object_group_order;Ct.hasOwnProperty(r)||(Ct[r]={id:r,title:a,order:l,payment_info:{},bills:[]});let c={id:o.id,name:o.attributes.name,amount_min:o.attributes.amount_min,amount_max:o.attributes.amount_max,amount:(parseFloat(o.attributes.amount_max)+parseFloat(o.attributes.amount_min))/2,currency_code:o.attributes.currency_code,native_amount_min:o.attributes.native_amount_min,native_amount_max:o.attributes.native_amount_max,native_amount:(parseFloat(o.attributes.native_amount_max)+parseFloat(o.attributes.native_amount_min))/2,native_currency_code:o.attributes.native_currency_code,transactions:[],pay_dates:o.attributes.pay_dates,paid:o.attributes.paid_dates.length>0};c.expected_amount=n.autoConversion?V(c.native_amount,c.native_currency_code):V(c.amount,c.currency_code),c.expected_times=ee.t("firefly.subscr_expected_x_times",{times:o.attributes.pay_dates.length,amount:c.expected_amount});for(let u in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(u)){const h=o.attributes.paid_dates[u];let d=100;n.autoConversion&&(d=Math.round(-100+parseFloat(h.native_amount)*-1/parseFloat(c.native_amount)*100)),n.autoConversion||(d=Math.round(-100+parseFloat(h.amount)*-1/parseFloat(c.amount)*100));let f={amount:n.autoConversion?V(h.native_amount,h.native_currency_code):V(h.amount,h.currency_code),percentage:d,date:J(new Date(h.date),"PP"),foreign_amount:null};h.foreign_currency_code!==null&&(f.foreign_amount=n.autoConversion?h.foreign_native_amount:h.foreign_amount,f.foreign_currency_code=n.autoConversion?h.native_currency_code:h.foreign_currency_code),c.transactions.push(f)}if(Ct[r].bills.push(c),o.attributes.paid_dates.length===0){const u=o.attributes.pay_dates.length*c.amount,h=o.attributes.pay_dates.length*c.native_amount;Ct[r].payment_info.hasOwnProperty(c.currency_code)||(Ct[r].payment_info[c.currency_code]={currency_code:c.currency_code,paid:0,unpaid:0,native_currency_code:c.native_currency_code,native_paid:0,native_unpaid:0}),Ct[r].payment_info[c.currency_code].unpaid+=u,Ct[r].payment_info[c.currency_code].native_unpaid+=h}if(o.attributes.paid_dates.length>0){for(let u in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(u)){let h=o.attributes.paid_dates[u];Ct[r].payment_info.hasOwnProperty(h.currency_code)||(Ct[r].payment_info[h.currency_code]={currency_code:c.currency_code,paid:0,unpaid:0,native_currency_code:c.native_currency_code,native_paid:0,native_unpaid:0});const d=parseFloat(h.amount)*-1,f=parseFloat(h.native_amount)*-1;Ct[r].payment_info[h.currency_code].paid+=d,Ct[r].payment_info[h.currency_code].native_paid+=f}}}}return parseInt(e.data.meta.pagination.total_pages)>n.page?(n.page++,Pa(n)):Promise.resolve()})}const rp=()=>({loading:!1,autoConversion:!1,subscriptions:[],startSubscriptions(){this.loading=!0;let n=new Date(window.store.get("start")),t=new Date(window.store.get("end"));console.log("here we are");const e=window.store.get("cacheValid");let i=window.store.get(Nt("subscriptions-data-dashboard",n,t));e&&typeof i<"u",Ct={},this.subscriptions=[],console.log("cache is invalid, must download");let s={start:J(n,"y-MM-dd"),end:J(t,"y-MM-dd"),autoConversion:this.autoConversion,page:1};Pa(s).then(()=>{console.log("Done with download!");let o=Object.values(Ct);for(let r in o)if(o.hasOwnProperty(r)){let a=o[r];const l=Object.keys(a.payment_info);a.col_size=l.length===1?"col-6 offset-3":"col-6",a.chart_width=l.length===1?"50%":"100%",a.payment_length=l.length,this.subscriptions.push(a)}this.loading=!1})},drawPieChart(n,t,e){let i="#pie_"+n+"_"+e.currency_code;const s=this.autoConversion?e.native_unpaid:e.unpaid,o=this.autoConversion?e.native_paid:e.paid,r=this.autoConversion?e.native_currency_code:e.currency_code,l={type:"doughnut",data:{labels:[ee.t("firefly.paid"),ee.t("firefly.unpaid")],datasets:[{label:ee.t("firefly.subscriptions_in_group",{title:t}),data:[o,s],backgroundColor:["rgb(54, 162, 235)","rgb(255, 99, 132)"],hoverOffset:4}]},options:{plugins:{tooltip:{callbacks:{label:function(u){return u.dataset.label+": "+V(u.raw,r)}}}}}};var c=mt.getChart(document.querySelector(i));typeof c<"u"&&c.destroy(),new mt(document.querySelector(i),l)},init(){console.log("subscriptions init"),Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{console.log("subscriptions after promises"),this.autoConversion=n[0],Hi=!0,ee=new li;const t=n[1].replace("-","_");ee.locale=t,ci(ee,t).then(()=>{this.loading===!1&&this.startSubscriptions()})}),window.store.observe("end",()=>{Hi&&(console.log("subscriptions observe end"),this.loading===!1&&this.startSubscriptions())}),window.store.observe("autoConversion",n=>{Hi&&(console.log("subscriptions observe autoConversion"),this.autoConversion=n,this.loading===!1&&this.startSubscriptions())})}});class ap{list(t){return vt.get("/api/v2/piggy-banks",{params:t})}}let Vt={},Vi=!1,Vn;const pr="dashboard-piggies-data",lp=()=>({loading:!1,autoConversion:!1,sankeyGrouping:"account",piggies:[],getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Nt(pr,n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Vt=s,this.parsePiggies(),this.loading=!1;return}let o={start:J(n,"y-MM-dd"),end:J(t,"y-MM-dd"),page:1};this.downloadPiggyBanks(o)},downloadPiggyBanks(n){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),i=Nt(pr,t,e);new ap().list(n).then(o=>{if(Vt=[...Vt,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>n.page){n.page++,this.downloadPiggyBanks(n);return}window.store.set(i,Vt),this.parsePiggies(),this.loading=!1})},parsePiggies(){let n=[];for(let t in Vt)if(Vt.hasOwnProperty(t)){let e=Vt[t];if(e.attributes.percentage>=100||e.attributes.percentage===0)continue;let i=e.object_group_title??Vn.t("firefly.default_group_title_name_plain");n.hasOwnProperty(i)||(n[i]={id:e.object_group_id??0,title:i,order:e.object_group_order??0,piggies:[]});let s={id:e.id,name:e.attributes.name,percentage:parseInt(e.attributes.percentage),amount:this.autoConversion?e.attributes.native_current_amount:e.attributes.current_amount,left_to_save:this.autoConversion?e.attributes.native_left_to_save:e.attributes.left_to_save,target_amount:this.autoConversion?e.attributes.native_target_amount:e.attributes.target_amount,save_per_month:this.autoConversion?e.attributes.native_save_per_month:e.attributes.save_per_month,currency_code:this.autoConversion?e.attributes.native_currency_code:e.attributes.currency_code};n[i].piggies.push(s)}this.piggies=Object.values(n)},loadPiggyBanks(){if(this.loading!==!0){if(this.loading=!0,this.piggies.length!==0){this.parsePiggies(),this.loading=!1;return}this.getFreshData()}},init(){Vt=[],Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{Vn=new li;const t=n[1].replace("-","_");Vn.locale=t,ci(Vn,t).then(()=>{Vi=!0,this.autoConversion=n[0],this.loadPiggyBanks()})}),window.store.observe("end",()=>{Vi&&(Vt=[],this.loadPiggyBanks())}),window.store.observe("autoConversion",n=>{Vi&&(this.autoConversion=n,this.loadPiggyBanks())})}});/*! +`)):Array.isArray(i)?e.push.apply(e,i):H(e)||t.unshift(""+i)}return t}function Ni(n){return!n||["min","max"].indexOf(n)===-1?"max":n}const ae=n=>n!==void 0;function $g(n,t){const e=new Set(t.map(r=>r.to)),i=new Set(t.map(r=>r.from)),s=new Set([...n.keys()]);let o=0;for(;s.size;){const r=qg([...s],e);for(const a of r){const l=n.get(a);ae(l.x)||(l.x=o),s.delete(a)}s.size&&(e.clear(),t.filter(a=>s.has(a.from)).forEach(a=>e.add(a.to)),o++)}return[...n.keys()].filter(r=>!i.has(r)).forEach(r=>{const a=n.get(r);a.column||(a.x=o)}),o}function qg(n,t){const e=n.filter(i=>!t.has(i));return e.length?e:n.slice(0,1)}const Xg=(n,t)=>n.x!==t.x?n.x-t.x:n.y-t.y;let Wn=-1;function Kg(){return Wn=Wn<100?Wn+1:0,Wn}function as(n,t,e=Kg()){let i=0;for(const s of n)s.node._visited!==e&&(s.node._visited=e,i+=s.node[t].length+as(s.node[t],t,e));return i}const Ca=n=>(t,e)=>as(t.node[n],n)-as(e.node[n],n)||t.node[n].length-e.node[n].length;function Ps(n,t){n.from.sort(Ca("from"));for(const e of n.from){const i=e.node;ae(i.y)||(i.y=t,Ps(i,t)),t=Math.max(i.y+i.out,t)}return t}function _e(n,t){n.to.sort(Ca("to"));for(const e of n.to){const i=e.node;ae(i.y)||(i.y=t,_e(i,t)),t=Math.max(i.y+i.in,t)}return t}function qe(n,t){return ae(n.y)?n.y:(n.y=t,t)}function Gg(n,t){const e=n.filter(u=>u.x===0),i=n.filter(u=>u.x===t),s=e.filter(u=>!ae(u.y)),o=i.filter(u=>!ae(u.y)),r=n.filter(u=>u.x>0&&u.xMath.max(u,h.y+h.out||0),0),l=i.reduce((u,h)=>Math.max(u,h.y+h.in||0),0),c=0;return a>=l?(s.forEach(u=>{a=qe(u,a),a=Math.max(a+u.out,_e(u,a))}),o.forEach(u=>{l=qe(u,l),l=Math.max(l+u.in,_e(u,l))})):(o.forEach(u=>{l=qe(u,l),l=Math.max(l+u.in,_e(u,l))}),s.forEach(u=>{a=qe(u,a),a=Math.max(a+u.out,_e(u,a))})),r.forEach(u=>{let h=n.filter(d=>d.x===u.x&&ae(d.y)).reduce((d,f)=>Math.max(d,f.y+Math.max(f.in,f.out)),0);h=qe(u,h),h=Math.max(h+u.in,Ps(u,h)),h=Math.max(h+u.out,_e(u,h)),c=Math.max(c,h)}),Math.max(a,l,c)}function Qg(n,t){n.sort((r,a)=>Math.max(a.in,a.out)-Math.max(r.in,r.out));const e=n[0];e.y=0;const i=Ps(e,0),s=_e(e,0),o=Gg(n,t);return Math.max(i,s,o)}function Zg(n,t){let e=0,i=0;for(let s=0;s<=t;s++){let o=i;const r=n.filter(a=>a.x===s).sort((a,l)=>a.priority-l.priority);i=r[0].to.filter(a=>a.node.x>s+1).reduce((a,l)=>a+l.flow,0)||0;for(const a of r)a.y=o,o+=Math.max(a.out,a.in);e=Math.max(o,e)}return e}function Jg(n,t){let e=1,i=0,s=0,o=0;const r=[];n.sort(Xg);for(const a of n){if(a.y){if(a.x===0)r.push(a.y);else{for(i!==a.x&&(i=a.x,s=0),e=s+1;ea.y);e++);s=e}a.y+=e*t,e++}o=Math.max(o,a.y+Math.max(a.in,a.out))}return o}function tp(n,t){n.forEach(e=>{const i=Math[t](e.in||e.out,e.out||e.in),s=il.node.y+l.node.out/2-(c.node.y+c.node.out/2)).forEach((l,c)=>{s?l.addY=c*(i-l.flow)/(a-1):(l.addY=r,r+=l.flow)}),r=0,a=e.to.length,e.to.sort((l,c)=>l.node.y+l.node.in/2-(c.node.y+c.node.in/2)).forEach((l,c)=>{o?l.addY=c*(i-l.flow)/(a-1):(l.addY=r,r+=l.flow)})})}function ep(n,t,e,i){const s=[...n.values()],o=$g(n,t),a=(e?Zg(s,o):Qg(s,o))*.03,l=Jg(s,a);return tp(s,i),{maxX:o,maxY:l}}function np(n){const t=new Map;for(let i=0;is.flow-i.flow;return[...t.values()].forEach(i=>{i.from=i.from.sort(e),i.from.forEach(s=>{s.node=t.get(s.key)}),i.to=i.to.sort(e),i.to.forEach(s=>{s.node=t.get(s.key)})}),t}function dr(n,t,e){for(const i of n)if(i.key===t&&i.index===e)return i.addY;return 0}class _i extends Kt{parseObjectData(t,e,i,s){const{from:o="from",to:r="to",flow:a="flow"}=this.options.parsing,l=e.map(({[o]:y,[r]:x,[a]:v})=>({from:y,to:x,flow:v})),{xScale:c,yScale:u}=t,h=[],d=this._nodes=np(l),{column:f,priority:g,size:p}=this.getDataset();if(g)for(const y of d.values())y.key in g&&(y.priority=g[y.key]);if(f)for(const y of d.values())y.key in f&&(y.column=!0,y.x=f[y.key]);const{maxX:m,maxY:b}=ep(d,l,!!g,Ni(p));this._maxX=m,this._maxY=b;for(let y=0,x=l.length;y1){const d=c-u*l/2+h;for(let f=0;fn.type==="data"?(n.parsed._custom.x-n.parsed.x)*200:void 0,delay:n=>n.type==="data"?n.parsed.x*500+n.dataIndex*20:void 0},colors:{type:"color",properties:["colorFrom","colorTo"]}},transitions:{hide:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],to:"transparent"}}},show:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],from:"transparent"}}}}};_i.overrides={interaction:{mode:"nearest",intersect:!0},datasets:{clip:!1,parsing:!0},plugins:{tooltip:{callbacks:{title(){return""},label(n){const t=n.dataset.data[n.dataIndex];return t.from+" -> "+t.to+": "+t.flow}}},legend:{display:!1}},scales:{x:{type:"linear",bounds:"data",display:!1,min:0,offset:!1},y:{type:"linear",bounds:"data",display:!1,min:0,reverse:!0,offset:!1}},layout:{padding:{top:3,left:3,right:13,bottom:3}}};const fr=(n,t,e,i)=>n({x:n.x+e*(t.x-n.x),y:n.y+e*(t.y-n.y)});function ip(n,{x:t,x2:e,options:i}){let s;i.colorMode==="from"?s=Ae(i.colorFrom).alpha(.5).rgbString():i.colorMode==="to"?s=Ae(i.colorTo).alpha(.5).rgbString():(s=n.createLinearGradient(t,0,e,0),s.addColorStop(0,Ae(i.colorFrom).alpha(.5).rgbString()),s.addColorStop(1,Ae(i.colorTo).alpha(.5).rgbString())),n.fillStyle=s,n.strokeStyle=s,n.lineWidth=.5}class Ds extends Rt{constructor(t){super(),this.options=void 0,this.x=void 0,this.y=void 0,this.x2=void 0,this.y2=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t){const e=this,{x:i,x2:s,y:o,y2:r,height:a,progress:l}=e,{cp1:c,cp2:u}=fr(i,o,s,r);l!==0&&(t.save(),l<1&&(t.beginPath(),t.rect(i,Math.min(o,r),(s-i)*l+1,Math.abs(r-o)+a+1),t.clip()),ip(t,e),t.beginPath(),t.moveTo(i,o),t.bezierCurveTo(c.x,c.y,u.x,u.y,s,r),t.lineTo(s,r+a),t.bezierCurveTo(u.x,u.y+a,c.x,c.y+a,i,o+a),t.lineTo(i,o),t.stroke(),t.closePath(),t.fill(),t.restore())}inRange(t,e,i){const{x:s,y:o,x2:r,y2:a,height:l}=this.getProps(["x","y","x2","y2","height"],i);if(tr)return!1;const{cp1:c,cp2:u}=fr(s,o,r,a),h=(t-s)/(r-s),d={x:s,y:o},f={x:r,y:a},g=Oe(d,c,h),p=Oe(c,u,h),m=Oe(u,f,h),b=Oe(g,p,h),y=Oe(p,m,h),x=Oe(b,y,h).y;return e>=x&&e<=x+l}inXRange(t,e){const{x:i,x2:s}=this.getProps(["x","x2"],e);return t>=i&&t<=s}inYRange(t,e){const{y:i,y2:s,height:o}=this.getProps(["y","y2","height"],e),r=Math.min(i,s),a=Math.max(i,s)+o;return t>=r&&t<=a}getCenterPoint(t){const{x:e,y:i,x2:s,y2:o,height:r}=this.getProps(["x","y","x2","y2","height"],t);return{x:(e+s)/2,y:(i+o+r)/2}}tooltipPosition(t){return this.getCenterPoint(t)}getRange(t){return t==="x"?this.width/2:this.height/2}}Ds.id="flow";Ds.defaults={colorFrom:"red",colorTo:"green",colorMode:"gradient",hoverColorFrom:(n,t)=>on(t.colorFrom),hoverColorTo:(n,t)=>on(t.colorTo)};mt.register({SankeyController:_i,Flow:Ds});const gr="dashboard-sankey-data";let pt,Wi=!1,Hn=null,Vt=[],ht=!1,L={category:null,unknown_category:null,in:null,out:null,unknown_source:null,unknown_dest:null,unknown_account:null,expense_account:null,revenue_account:null,budget:null,unknown_budget:null,all_money:null};const pr=function(n){return n.includes(L.revenue_account)?"forestgreen":n.includes("("+L.in+",")?"green":n.includes(L.budget)||n.includes(L.unknown_budget)?"Orchid":n.includes("("+L.out+",")?"MediumOrchid":n.includes(L.all_money)?"blue":"red"};function Xe(n,t,e,i){if(n==="category"&&t!==null&&e==="in")return L.category+' "'+t+'" ('+L.in+(ht?", "+i+")":")");if(n==="category"&&t===null&&e==="in")return L.unknown_category+" ("+L.in+(ht?", "+i+")":")");if(n==="category"&&t!==null&&e==="out")return L.category+' "'+t+'" ('+L.out+(ht?", "+i+")":")");if(n==="category"&&t===null&&e==="out")return L.unknown_category+" ("+L.out+(ht?", "+i+")":")");if(n==="account"&&t===null&&e==="in")return L.unknown_source+(ht?" ("+i+")":"");if(n==="account"&&t!==null&&e==="in")return L.revenue_account+'"'+t+'"'+(ht?" ("+i+")":"");if(n==="account"&&t===null&&e==="out")return L.unknown_dest+(ht?" ("+i+")":"");if(n==="account"&&t!==null&&e==="out")return L.expense_account+' "'+t+'"'+(ht?" ("+i+")":"");if(n==="budget"&&t!==null)return L.budget+' "'+t+'"'+(ht?" ("+i+")":"");if(n==="budget"&&t===null)return L.unknown_budget+(ht?" ("+i+")":"");console.error('Cannot handle: type:"'+n+'", dir: "'+e+'"')}function Ke(n,t,e){if(n==="category"&&t!==null)return L.category+' "'+t+'"'+(ht?" ("+e+")":"");if(n==="category"&&t===null)return L.unknown_category+(ht?" ("+e+")":"");if(n==="account"&&t===null)return L.unknown_account+(ht?" ("+e+")":"");if(n==="account"&&t!==null)return t+(ht?" ("+e+")":"");if(n==="budget"&&t!==null)return L.budget+' "'+t+'"'+(ht?" ("+e+")":"");if(n==="budget"&&t===null)return L.unknown_budget+(ht?" ("+e+")":"");console.error('Cannot handle: type:"'+n+'"')}const sp=()=>({loading:!1,autoConversion:!1,generateOptions(){let n=yi("sankey"),t={},e={};for(let s in Vt)if(Vt.hasOwnProperty(s)){let o=Vt[s];for(let r in o.attributes.transactions)if(o.attributes.transactions.hasOwnProperty(r)){let a=o.attributes.transactions[r],l=this.autoConversion?a.native_currency_code:a.currency_code,c=this.autoConversion?parseFloat(a.native_amount):parseFloat(a.amount),u;if(a.type==="deposit"){let h=Xe("category",a.category_name,"in",l),d=Xe("account",a.source_name,"in",l);e[h]=Ke("category",a.category_name,l),e[d]=Ke("account",a.source_name,l),u=d+"-"+h+"-"+l,t.hasOwnProperty(u)||(t[u]={from:d,to:h,amount:0}),t[u].amount+=c,u=h+"-"+L.all_money+"-"+l,t.hasOwnProperty(u)||(t[u]={from:h,to:L.all_money+(this.autoConversion?" ("+l+")":""),amount:0}),t[u].amount+=c}if(a.type==="withdrawal"){let h=Xe("budget",a.budget_name,"out",l);e[h]=Ke("budget",a.budget_name,l),u=L.all_money+"-"+h+"-"+l,t.hasOwnProperty(u)||(t[u]={from:L.all_money+(this.autoConversion?" ("+l+")":""),to:h,amount:0}),t[u].amount+=c;let d=Xe("category",a.category_name,"out",l);e[d]=Ke("category",a.category_name,l),u=h+"-"+d+"-"+l,t.hasOwnProperty(u)||(t[u]={from:h,to:d,amount:0}),t[u].amount+=c;let f=Xe("account",a.destination_name,"out",l);e[f]=Ke("account",a.destination_name,l),u=d+"-"+f+"-"+l,t.hasOwnProperty(u)||(t[u]={from:d,to:f,amount:0}),t[u].amount+=c}}}let i={label:"Firefly III dashboard sankey chart",data:[],colorFrom:s=>pr(s.dataset.data[s.dataIndex]?s.dataset.data[s.dataIndex].from:""),colorTo:s=>pr(s.dataset.data[s.dataIndex]?s.dataset.data[s.dataIndex].to:""),colorMode:"gradient",labels:e,size:"min"};for(let s in t)if(t.hasOwnProperty(s)){let o=t[s];i.data.push({from:o.from,to:o.to,flow:o.amount})}return n.data.datasets.push(i),n},drawChart(n){if(Hn!==null){Hn.data.datasets=n.data.datasets,Hn.update();return}Hn=new mt(document.querySelector("#sankey-chart"),n)},getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt(gr,n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Vt=s,this.drawChart(this.generateOptions()),this.loading=!1;return}let o={start:J(n,"y-MM-dd"),end:J(t,"y-MM-dd"),type:"withdrawal,deposit",page:1};this.downloadTransactions(o)},downloadTransactions(n){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),i=Wt(gr,t,e);new jg().list(n).then(o=>{if(Vt=[...Vt,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>n.page){n.page++,this.downloadTransactions(n);return}window.store.set(i,Vt),this.drawChart(this.generateOptions()),this.loading=!1})},loadChart(){if(this.loading!==!0){if(this.loading=!0,Vt.length!==0){this.drawChart(this.generateOptions()),this.loading=!1;return}this.getFreshData()}},init(){Vt=[],Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{this.autoConversion=n[0],ht=n[0],pt=new li;const t=n[1].replace("-","_");pt.locale=t,ci(pt,t).then(()=>{L.all_money=pt.t("firefly.all_money"),L.category=pt.t("firefly.category"),L.in=pt.t("firefly.money_flowing_in"),L.out=pt.t("firefly.money_flowing_out"),L.unknown_category=pt.t("firefly.unknown_category_plain"),L.unknown_source=pt.t("firefly.unknown_source_plain"),L.unknown_dest=pt.t("firefly.unknown_dest_plain"),L.unknown_account=pt.t("firefly.unknown_any_plain"),L.unknown_budget=pt.t("firefly.unknown_budget_plain"),L.expense_account=pt.t("firefly.expense_account"),L.revenue_account=pt.t("firefly.revenue_account"),L.budget=pt.t("firefly.budget"),Wi=!0,this.loadChart()})}),window.store.observe("end",()=>{Wi&&(this.transactions=[],this.loadChart())}),window.store.observe("autoConversion",n=>{Wi&&(this.autoConversion=n,this.loadChart())})}});let op=class{list(t){return vt.get("/api/v2/subscriptions",{params:t})}paid(t){return vt.get("/api/v2/subscriptions/sum/paid",{params:t})}unpaid(t){return vt.get("/api/v2/subscriptions/sum/unpaid",{params:t})}},Hi=!1,ee,Ct={};function Pa(n){return new op().list(n).then(e=>{let i=e.data.data;for(let s in i)if(i.hasOwnProperty(s)){let o=i[s];if(o.attributes.active&&o.attributes.pay_dates.length>0){let r=o.attributes.object_group_id===null?0:o.attributes.object_group_id,a=o.attributes.object_group_title===null?ee.t("firefly.default_group_title_name_plain"):o.attributes.object_group_title,l=o.attributes.object_group_order===null?0:o.attributes.object_group_order;Ct.hasOwnProperty(r)||(Ct[r]={id:r,title:a,order:l,payment_info:{},bills:[]});let c={id:o.id,name:o.attributes.name,amount_min:o.attributes.amount_min,amount_max:o.attributes.amount_max,amount:(parseFloat(o.attributes.amount_max)+parseFloat(o.attributes.amount_min))/2,currency_code:o.attributes.currency_code,native_amount_min:o.attributes.native_amount_min,native_amount_max:o.attributes.native_amount_max,native_amount:(parseFloat(o.attributes.native_amount_max)+parseFloat(o.attributes.native_amount_min))/2,native_currency_code:o.attributes.native_currency_code,transactions:[],pay_dates:o.attributes.pay_dates,paid:o.attributes.paid_dates.length>0};c.expected_amount=n.autoConversion?V(c.native_amount,c.native_currency_code):V(c.amount,c.currency_code),c.expected_times=ee.t("firefly.subscr_expected_x_times",{times:o.attributes.pay_dates.length,amount:c.expected_amount});for(let u in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(u)){const h=o.attributes.paid_dates[u];let d=100;n.autoConversion&&(d=Math.round(-100+parseFloat(h.native_amount)*-1/parseFloat(c.native_amount)*100)),n.autoConversion||(d=Math.round(-100+parseFloat(h.amount)*-1/parseFloat(c.amount)*100));let f={amount:n.autoConversion?V(h.native_amount,h.native_currency_code):V(h.amount,h.currency_code),percentage:d,date:J(new Date(h.date),"PP"),foreign_amount:null};h.foreign_currency_code!==null&&(f.foreign_amount=n.autoConversion?h.foreign_native_amount:h.foreign_amount,f.foreign_currency_code=n.autoConversion?h.native_currency_code:h.foreign_currency_code),c.transactions.push(f)}if(Ct[r].bills.push(c),o.attributes.paid_dates.length===0){const u=o.attributes.pay_dates.length*c.amount,h=o.attributes.pay_dates.length*c.native_amount;Ct[r].payment_info.hasOwnProperty(c.currency_code)||(Ct[r].payment_info[c.currency_code]={currency_code:c.currency_code,paid:0,unpaid:0,native_currency_code:c.native_currency_code,native_paid:0,native_unpaid:0}),Ct[r].payment_info[c.currency_code].unpaid+=u,Ct[r].payment_info[c.currency_code].native_unpaid+=h}if(o.attributes.paid_dates.length>0){for(let u in o.attributes.paid_dates)if(o.attributes.paid_dates.hasOwnProperty(u)){let h=o.attributes.paid_dates[u];Ct[r].payment_info.hasOwnProperty(h.currency_code)||(Ct[r].payment_info[h.currency_code]={currency_code:c.currency_code,paid:0,unpaid:0,native_currency_code:c.native_currency_code,native_paid:0,native_unpaid:0});const d=parseFloat(h.amount)*-1,f=parseFloat(h.native_amount)*-1;Ct[r].payment_info[h.currency_code].paid+=d,Ct[r].payment_info[h.currency_code].native_paid+=f}}}}return parseInt(e.data.meta.pagination.total_pages)>n.page?(n.page++,Pa(n)):Promise.resolve()})}const rp=()=>({loading:!1,autoConversion:!1,subscriptions:[],startSubscriptions(){this.loading=!0;let n=new Date(window.store.get("start")),t=new Date(window.store.get("end"));console.log("here we are");const e=window.store.get("cacheValid");let i=window.store.get(Wt("subscriptions-data-dashboard",n,t));e&&typeof i<"u",Ct={},this.subscriptions=[],console.log("cache is invalid, must download");let s={start:J(n,"y-MM-dd"),end:J(t,"y-MM-dd"),autoConversion:this.autoConversion,page:1};Pa(s).then(()=>{console.log("Done with download!");let o=Object.values(Ct);for(let r in o)if(o.hasOwnProperty(r)){let a=o[r];const l=Object.keys(a.payment_info);a.col_size=l.length===1?"col-6 offset-3":"col-6",a.chart_width=l.length===1?"50%":"100%",a.payment_length=l.length,this.subscriptions.push(a)}this.loading=!1})},drawPieChart(n,t,e){let i="#pie_"+n+"_"+e.currency_code;const s=this.autoConversion?e.native_unpaid:e.unpaid,o=this.autoConversion?e.native_paid:e.paid,r=this.autoConversion?e.native_currency_code:e.currency_code,l={type:"doughnut",data:{labels:[ee.t("firefly.paid"),ee.t("firefly.unpaid")],datasets:[{label:ee.t("firefly.subscriptions_in_group",{title:t}),data:[o,s],backgroundColor:["rgb(54, 162, 235)","rgb(255, 99, 132)"],hoverOffset:4}]},options:{plugins:{tooltip:{callbacks:{label:function(u){return u.dataset.label+": "+V(u.raw,r)}}}}}};var c=mt.getChart(document.querySelector(i));typeof c<"u"&&c.destroy(),new mt(document.querySelector(i),l)},init(){console.log("subscriptions init"),Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{console.log("subscriptions after promises"),this.autoConversion=n[0],Hi=!0,ee=new li;const t=n[1].replace("-","_");ee.locale=t,ci(ee,t).then(()=>{this.loading===!1&&this.startSubscriptions()})}),window.store.observe("end",()=>{Hi&&(console.log("subscriptions observe end"),this.loading===!1&&this.startSubscriptions())}),window.store.observe("autoConversion",n=>{Hi&&(console.log("subscriptions observe autoConversion"),this.autoConversion=n,this.loading===!1&&this.startSubscriptions())})}});class ap{list(t){return vt.get("/api/v2/piggy-banks",{params:t})}}let Yt={},Vi=!1,Vn;const mr="dashboard-piggies-data",lp=()=>({loading:!1,autoConversion:!1,sankeyGrouping:"account",piggies:[],getFreshData(){const n=new Date(window.store.get("start")),t=new Date(window.store.get("end")),e=Wt(mr,n,t),i=window.store.get("cacheValid");let s=window.store.get(e);if(i&&typeof s<"u"){Yt=s,this.parsePiggies(),this.loading=!1;return}let o={start:J(n,"y-MM-dd"),end:J(t,"y-MM-dd"),page:1};this.downloadPiggyBanks(o)},downloadPiggyBanks(n){const t=new Date(window.store.get("start")),e=new Date(window.store.get("end")),i=Wt(mr,t,e);new ap().list(n).then(o=>{if(Yt=[...Yt,...o.data.data],parseInt(o.data.meta.pagination.total_pages)>n.page){n.page++,this.downloadPiggyBanks(n);return}window.store.set(i,Yt),this.parsePiggies(),this.loading=!1})},parsePiggies(){let n=[];for(let t in Yt)if(Yt.hasOwnProperty(t)){let e=Yt[t];if(e.attributes.percentage>=100||e.attributes.percentage===0)continue;let i=e.object_group_title??Vn.t("firefly.default_group_title_name_plain");n.hasOwnProperty(i)||(n[i]={id:e.object_group_id??0,title:i,order:e.object_group_order??0,piggies:[]});let s={id:e.id,name:e.attributes.name,percentage:parseInt(e.attributes.percentage),amount:this.autoConversion?e.attributes.native_current_amount:e.attributes.current_amount,left_to_save:this.autoConversion?e.attributes.native_left_to_save:e.attributes.left_to_save,target_amount:this.autoConversion?e.attributes.native_target_amount:e.attributes.target_amount,save_per_month:this.autoConversion?e.attributes.native_save_per_month:e.attributes.save_per_month,currency_code:this.autoConversion?e.attributes.native_currency_code:e.attributes.currency_code};n[i].piggies.push(s)}this.piggies=Object.values(n)},loadPiggyBanks(){if(this.loading!==!0){if(this.loading=!0,this.piggies.length!==0){this.parsePiggies(),this.loading=!1;return}this.getFreshData()}},init(){Yt=[],Promise.all([bt("autoConversion",!1),bt("language","en_US")]).then(n=>{Vn=new li;const t=n[1].replace("-","_");Vn.locale=t,ci(Vn,t).then(()=>{Vi=!0,this.autoConversion=n[0],this.loadPiggyBanks()})}),window.store.observe("end",()=>{Vi&&(Yt=[],this.loadPiggyBanks())}),window.store.observe("autoConversion",n=>{Vi&&(this.autoConversion=n,this.loadPiggyBanks())})}});/*! * chartjs-adapter-date-fns v3.0.0 * https://www.chartjs.org * (c) 2022 chartjs-adapter-date-fns Contributors * Released under the MIT license - */const cp={datetime:"MMM d, yyyy, h:mm:ss aaaa",millisecond:"h:mm:ss.SSS aaaa",second:"h:mm:ss aaaa",minute:"h:mm aaaa",hour:"ha",day:"MMM d",week:"PP",month:"MMM yyyy",quarter:"qqq - yyyy",year:"yyyy"};sa._date.override({_id:"date-fns",formats:function(){return cp},parse:function(n,t){if(n===null||typeof n>"u")return null;const e=typeof n;return e==="number"||n instanceof Date?n=j(n):e==="string"&&(typeof t=="string"?n=pc(n,t,new Date,this.options):n=_c(n,this.options)),Na(n)?n.getTime():null},format:function(n,t){return J(n,t,this.options)},add:function(n,t,e){switch(e){case"millisecond":return ai(n,t);case"second":return Ja(n,t);case"minute":return Qa(n,t);case"hour":return qa(n,t);case"day":return mr(n,t);case"week":return tl(n,t);case"month":return ls(n,t);case"quarter":return Za(n,t);case"year":return el(n,t);default:return n}},diff:function(n,t,e){switch(e){case"millisecond":return di(n,t);case"second":return ul(n,t);case"minute":return al(n,t);case"hour":return rl(n,t);case"day":return vr(n,t);case"week":return hl(n,t);case"month":return wr(n,t);case"quarter":return cl(n,t);case"year":return dl(n,t);default:return 0}},startOf:function(n,t,e){switch(t){case"second":return yc(n);case"minute":return fl(n);case"hour":return bc(n);case"day":return Yi(n);case"week":return Ls(n);case"isoWeek":return Ls(n,{weekStartsOn:+e});case"month":return Va(n);case"quarter":return Ha(n);case"year":return Wa(n);default:return n}},endOf:function(n,t){switch(t){case"second":return bl(n);case"minute":return ml(n);case"hour":return pl(n);case"day":return br(n);case"week":return ja(n);case"month":return yr(n);case"quarter":return Ya(n);case"year":return gl(n);default:return n}}});mt.register({LineController:jn,LineElement:oe,ArcElement:Ze,BarController:Yn,TimeScale:pn,PieController:Qi,BarElement:Kn,Filler:Jf,Colors:Rf,LinearScale:os,CategoryScale:ss,PointElement:Xn,Tooltip:fg,Legend:og});const Da={dates:Ua,boxes:Ec,accounts:Ng,budgets:Hg,categories:Yg,sankey:sp,subscriptions:rp,piggies:lp};function Ta(n){Object.keys(n).forEach(t=>{console.log(`Loading page component "${t}"`);let e=n[t]();Alpine.data(t,()=>e)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{Ta(Da)});window.bootstrapped&&Ta(Da); + */const cp={datetime:"MMM d, yyyy, h:mm:ss aaaa",millisecond:"h:mm:ss.SSS aaaa",second:"h:mm:ss aaaa",minute:"h:mm aaaa",hour:"ha",day:"MMM d",week:"PP",month:"MMM yyyy",quarter:"qqq - yyyy",year:"yyyy"};sa._date.override({_id:"date-fns",formats:function(){return cp},parse:function(n,t){if(n===null||typeof n>"u")return null;const e=typeof n;return e==="number"||n instanceof Date?n=j(n):e==="string"&&(typeof t=="string"?n=pc(n,t,new Date,this.options):n=_c(n,this.options)),Na(n)?n.getTime():null},format:function(n,t){return J(n,t,this.options)},add:function(n,t,e){switch(e){case"millisecond":return ai(n,t);case"second":return Ja(n,t);case"minute":return Qa(n,t);case"hour":return qa(n,t);case"day":return br(n,t);case"week":return tl(n,t);case"month":return ls(n,t);case"quarter":return Za(n,t);case"year":return el(n,t);default:return n}},diff:function(n,t,e){switch(e){case"millisecond":return di(n,t);case"second":return ul(n,t);case"minute":return al(n,t);case"hour":return rl(n,t);case"day":return wr(n,t);case"week":return hl(n,t);case"month":return Mr(n,t);case"quarter":return cl(n,t);case"year":return dl(n,t);default:return 0}},startOf:function(n,t,e){switch(t){case"second":return yc(n);case"minute":return fl(n);case"hour":return bc(n);case"day":return Yi(n);case"week":return Rs(n);case"isoWeek":return Rs(n,{weekStartsOn:+e});case"month":return Va(n);case"quarter":return Ha(n);case"year":return Wa(n);default:return n}},endOf:function(n,t){switch(t){case"second":return bl(n);case"minute":return ml(n);case"hour":return pl(n);case"day":return yr(n);case"week":return ja(n);case"month":return _r(n);case"quarter":return Ya(n);case"year":return gl(n);default:return n}}});mt.register({LineController:jn,LineElement:oe,ArcElement:Ze,BarController:Yn,TimeScale:pn,PieController:Qi,BarElement:Kn,Filler:Jf,Colors:Rf,LinearScale:os,CategoryScale:ss,PointElement:Xn,Tooltip:fg,Legend:og});const Da={dates:Ua,boxes:Ec,accounts:Ng,budgets:Hg,categories:Yg,sankey:sp,subscriptions:rp,piggies:lp};function Ta(n){Object.keys(n).forEach(t=>{console.log(`Loading page component "${t}"`);let e=n[t]();Alpine.data(t,()=>e)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{Ta(Da)});window.bootstrapped&&Ta(Da); diff --git a/public/build/assets/fa-brands-400-003f1154.ttf b/public/build/assets/fa-brands-400-003f1154.ttf deleted file mode 100644 index 30f55b7435..0000000000 Binary files a/public/build/assets/fa-brands-400-003f1154.ttf and /dev/null differ diff --git a/public/build/assets/fa-brands-400-3a8924cd.woff2 b/public/build/assets/fa-brands-400-3a8924cd.woff2 new file mode 100644 index 0000000000..36fbda7d33 Binary files /dev/null and b/public/build/assets/fa-brands-400-3a8924cd.woff2 differ diff --git a/public/build/assets/fa-brands-400-5656d596.ttf b/public/build/assets/fa-brands-400-5656d596.ttf new file mode 100644 index 0000000000..5efb1d4f96 Binary files /dev/null and b/public/build/assets/fa-brands-400-5656d596.ttf differ diff --git a/public/build/assets/fa-brands-400-faae6fc0.woff2 b/public/build/assets/fa-brands-400-faae6fc0.woff2 deleted file mode 100644 index 8a480d9b1f..0000000000 Binary files a/public/build/assets/fa-brands-400-faae6fc0.woff2 and /dev/null differ diff --git a/public/build/assets/fa-regular-400-2bccecf0.woff2 b/public/build/assets/fa-regular-400-2bccecf0.woff2 new file mode 100644 index 0000000000..b6cabbacb6 Binary files /dev/null and b/public/build/assets/fa-regular-400-2bccecf0.woff2 differ diff --git a/public/build/assets/fa-regular-400-5d02dc9b.ttf b/public/build/assets/fa-regular-400-5d02dc9b.ttf new file mode 100644 index 0000000000..838b4e2cfe Binary files /dev/null and b/public/build/assets/fa-regular-400-5d02dc9b.ttf differ diff --git a/public/build/assets/fa-regular-400-7d81a1a7.ttf b/public/build/assets/fa-regular-400-7d81a1a7.ttf deleted file mode 100644 index c79589d83d..0000000000 Binary files a/public/build/assets/fa-regular-400-7d81a1a7.ttf and /dev/null differ diff --git a/public/build/assets/fa-regular-400-9169d8be.woff2 b/public/build/assets/fa-regular-400-9169d8be.woff2 deleted file mode 100644 index 059a94e2fd..0000000000 Binary files a/public/build/assets/fa-regular-400-9169d8be.woff2 and /dev/null differ diff --git a/public/build/assets/fa-solid-900-886c8611.woff2 b/public/build/assets/fa-solid-900-886c8611.woff2 deleted file mode 100644 index 88b0367aae..0000000000 Binary files a/public/build/assets/fa-solid-900-886c8611.woff2 and /dev/null differ diff --git a/public/build/assets/fa-solid-900-9fc85f3a.woff2 b/public/build/assets/fa-solid-900-9fc85f3a.woff2 new file mode 100644 index 0000000000..824d518eb4 Binary files /dev/null and b/public/build/assets/fa-solid-900-9fc85f3a.woff2 differ diff --git a/public/build/assets/fa-solid-900-cea79b34.ttf b/public/build/assets/fa-solid-900-cea79b34.ttf deleted file mode 100644 index e479fb2934..0000000000 Binary files a/public/build/assets/fa-solid-900-cea79b34.ttf and /dev/null differ diff --git a/public/build/assets/fa-solid-900-fbbf06d7.ttf b/public/build/assets/fa-solid-900-fbbf06d7.ttf new file mode 100644 index 0000000000..ec24749db9 Binary files /dev/null and b/public/build/assets/fa-solid-900-fbbf06d7.ttf differ diff --git a/public/build/assets/load-translations-87b32220.js b/public/build/assets/load-translations-85b093de.js similarity index 76% rename from public/build/assets/load-translations-87b32220.js rename to public/build/assets/load-translations-85b093de.js index dc6fdafd0c..f907244c0b 100644 --- a/public/build/assets/load-translations-87b32220.js +++ b/public/build/assets/load-translations-85b093de.js @@ -1,19 +1,19 @@ -function bind$4(t,e){return function(){return t.apply(e,arguments)}}const{toString:toString$7}=Object.prototype,{getPrototypeOf}=Object,kindOf=(t=>e=>{const a=toString$7.call(e);return t[a]||(t[a]=a.slice(8,-1).toLowerCase())})(Object.create(null)),kindOfTest=t=>(t=t.toLowerCase(),e=>kindOf(e)===t),typeOfTest=t=>e=>typeof e===t,{isArray:isArray$c}=Array,isUndefined=typeOfTest("undefined");function isBuffer$3(t){return t!==null&&!isUndefined(t)&&t.constructor!==null&&!isUndefined(t.constructor)&&isFunction$5(t.constructor.isBuffer)&&t.constructor.isBuffer(t)}const isArrayBuffer=kindOfTest("ArrayBuffer");function isArrayBufferView(t){let e;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?e=ArrayBuffer.isView(t):e=t&&t.buffer&&isArrayBuffer(t.buffer),e}const isString$1=typeOfTest("string"),isFunction$5=typeOfTest("function"),isNumber=typeOfTest("number"),isObject$b=t=>t!==null&&typeof t=="object",isBoolean=t=>t===!0||t===!1,isPlainObject=t=>{if(kindOf(t)!=="object")return!1;const e=getPrototypeOf(t);return(e===null||e===Object.prototype||Object.getPrototypeOf(e)===null)&&!(Symbol.toStringTag in t)&&!(Symbol.iterator in t)},isDate$1=kindOfTest("Date"),isFile=kindOfTest("File"),isBlob=kindOfTest("Blob"),isFileList=kindOfTest("FileList"),isStream=t=>isObject$b(t)&&isFunction$5(t.pipe),isFormData=t=>{let e;return t&&(typeof FormData=="function"&&t instanceof FormData||isFunction$5(t.append)&&((e=kindOf(t))==="formdata"||e==="object"&&isFunction$5(t.toString)&&t.toString()==="[object FormData]"))},isURLSearchParams=kindOfTest("URLSearchParams"),trim$2=t=>t.trim?t.trim():t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function forEach(t,e,{allOwnKeys:a=!1}={}){if(t===null||typeof t>"u")return;let r,n;if(typeof t!="object"&&(t=[t]),isArray$c(t))for(r=0,n=t.length;r0;)if(n=a[r],e===n.toLowerCase())return n;return null}const _global=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),isContextDefined=t=>!isUndefined(t)&&t!==_global;function merge(){const{caseless:t}=isContextDefined(this)&&this||{},e={},a=(r,n)=>{const i=t&&findKey$1(e,n)||n;isPlainObject(e[i])&&isPlainObject(r)?e[i]=merge(e[i],r):isPlainObject(r)?e[i]=merge({},r):isArray$c(r)?e[i]=r.slice():e[i]=r};for(let r=0,n=arguments.length;r(forEach(e,(n,i)=>{a&&isFunction$5(n)?t[i]=bind$4(n,a):t[i]=n},{allOwnKeys:r}),t),stripBOM=t=>(t.charCodeAt(0)===65279&&(t=t.slice(1)),t),inherits=(t,e,a,r)=>{t.prototype=Object.create(e.prototype,r),t.prototype.constructor=t,Object.defineProperty(t,"super",{value:e.prototype}),a&&Object.assign(t.prototype,a)},toFlatObject=(t,e,a,r)=>{let n,i,o;const s={};if(e=e||{},t==null)return e;do{for(n=Object.getOwnPropertyNames(t),i=n.length;i-- >0;)o=n[i],(!r||r(o,t,e))&&!s[o]&&(e[o]=t[o],s[o]=!0);t=a!==!1&&getPrototypeOf(t)}while(t&&(!a||a(t,e))&&t!==Object.prototype);return e},endsWith=(t,e,a)=>{t=String(t),(a===void 0||a>t.length)&&(a=t.length),a-=e.length;const r=t.indexOf(e,a);return r!==-1&&r===a},toArray=t=>{if(!t)return null;if(isArray$c(t))return t;let e=t.length;if(!isNumber(e))return null;const a=new Array(e);for(;e-- >0;)a[e]=t[e];return a},isTypedArray$3=(t=>e=>t&&e instanceof t)(typeof Uint8Array<"u"&&getPrototypeOf(Uint8Array)),forEachEntry=(t,e)=>{const r=(t&&t[Symbol.iterator]).call(t);let n;for(;(n=r.next())&&!n.done;){const i=n.value;e.call(t,i[0],i[1])}},matchAll=(t,e)=>{let a;const r=[];for(;(a=t.exec(e))!==null;)r.push(a);return r},isHTMLForm=kindOfTest("HTMLFormElement"),toCamelCase=t=>t.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(a,r,n){return r.toUpperCase()+n}),hasOwnProperty$c=(({hasOwnProperty:t})=>(e,a)=>t.call(e,a))(Object.prototype),isRegExp=kindOfTest("RegExp"),reduceDescriptors=(t,e)=>{const a=Object.getOwnPropertyDescriptors(t),r={};forEach(a,(n,i)=>{let o;(o=e(n,i,t))!==!1&&(r[i]=o||n)}),Object.defineProperties(t,r)},freezeMethods=t=>{reduceDescriptors(t,(e,a)=>{if(isFunction$5(t)&&["arguments","caller","callee"].indexOf(a)!==-1)return!1;const r=t[a];if(isFunction$5(r)){if(e.enumerable=!1,"writable"in e){e.writable=!1;return}e.set||(e.set=()=>{throw Error("Can not rewrite read-only method '"+a+"'")})}})},toObjectSet=(t,e)=>{const a={},r=n=>{n.forEach(i=>{a[i]=!0})};return isArray$c(t)?r(t):r(String(t).split(e)),a},noop$3=()=>{},toFiniteNumber=(t,e)=>(t=+t,Number.isFinite(t)?t:e),ALPHA="abcdefghijklmnopqrstuvwxyz",DIGIT="0123456789",ALPHABET={DIGIT,ALPHA,ALPHA_DIGIT:ALPHA+ALPHA.toUpperCase()+DIGIT},generateString=(t=16,e=ALPHABET.ALPHA_DIGIT)=>{let a="";const{length:r}=e;for(;t--;)a+=e[Math.random()*r|0];return a};function isSpecCompliantForm(t){return!!(t&&isFunction$5(t.append)&&t[Symbol.toStringTag]==="FormData"&&t[Symbol.iterator])}const toJSONObject=t=>{const e=new Array(10),a=(r,n)=>{if(isObject$b(r)){if(e.indexOf(r)>=0)return;if(!("toJSON"in r)){e[n]=r;const i=isArray$c(r)?[]:{};return forEach(r,(o,s)=>{const u=a(o,n+1);!isUndefined(u)&&(i[s]=u)}),e[n]=void 0,i}}return r};return a(t,0)},isAsyncFn=kindOfTest("AsyncFunction"),isThenable=t=>t&&(isObject$b(t)||isFunction$5(t))&&isFunction$5(t.then)&&isFunction$5(t.catch),utils={isArray:isArray$c,isArrayBuffer,isBuffer:isBuffer$3,isFormData,isArrayBufferView,isString:isString$1,isNumber,isBoolean,isObject:isObject$b,isPlainObject,isUndefined,isDate:isDate$1,isFile,isBlob,isRegExp,isFunction:isFunction$5,isStream,isURLSearchParams,isTypedArray:isTypedArray$3,isFileList,forEach,merge,extend,trim:trim$2,stripBOM,inherits,toFlatObject,kindOf,kindOfTest,endsWith,toArray,forEachEntry,matchAll,isHTMLForm,hasOwnProperty:hasOwnProperty$c,hasOwnProp:hasOwnProperty$c,reduceDescriptors,freezeMethods,toObjectSet,toCamelCase,noop:noop$3,toFiniteNumber,findKey:findKey$1,global:_global,isContextDefined,ALPHABET,generateString,isSpecCompliantForm,toJSONObject,isAsyncFn,isThenable};function AxiosError(t,e,a,r,n){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack,this.message=t,this.name="AxiosError",e&&(this.code=e),a&&(this.config=a),r&&(this.request=r),n&&(this.response=n)}utils.inherits(AxiosError,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:utils.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});const prototype$1=AxiosError.prototype,descriptors={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach(t=>{descriptors[t]={value:t}});Object.defineProperties(AxiosError,descriptors);Object.defineProperty(prototype$1,"isAxiosError",{value:!0});AxiosError.from=(t,e,a,r,n,i)=>{const o=Object.create(prototype$1);return utils.toFlatObject(t,o,function(u){return u!==Error.prototype},s=>s!=="isAxiosError"),AxiosError.call(o,t.message,e,a,r,n),o.cause=t,o.name=t.name,i&&Object.assign(o,i),o};const httpAdapter=null;function isVisitable(t){return utils.isPlainObject(t)||utils.isArray(t)}function removeBrackets(t){return utils.endsWith(t,"[]")?t.slice(0,-2):t}function renderKey(t,e,a){return t?t.concat(e).map(function(n,i){return n=removeBrackets(n),!a&&i?"["+n+"]":n}).join(a?".":""):e}function isFlatArray(t){return utils.isArray(t)&&!t.some(isVisitable)}const predicates=utils.toFlatObject(utils,{},null,function(e){return/^is[A-Z]/.test(e)});function toFormData(t,e,a){if(!utils.isObject(t))throw new TypeError("target must be an object");e=e||new FormData,a=utils.toFlatObject(a,{metaTokens:!0,dots:!1,indexes:!1},!1,function(O,M){return!utils.isUndefined(M[O])});const r=a.metaTokens,n=a.visitor||l,i=a.dots,o=a.indexes,u=(a.Blob||typeof Blob<"u"&&Blob)&&utils.isSpecCompliantForm(e);if(!utils.isFunction(n))throw new TypeError("visitor must be a function");function c(T){if(T===null)return"";if(utils.isDate(T))return T.toISOString();if(!u&&utils.isBlob(T))throw new AxiosError("Blob is not supported. Use a Buffer instead.");return utils.isArrayBuffer(T)||utils.isTypedArray(T)?u&&typeof Blob=="function"?new Blob([T]):Buffer.from(T):T}function l(T,O,M){let C=T;if(T&&!M&&typeof T=="object"){if(utils.endsWith(O,"{}"))O=r?O:O.slice(0,-2),T=JSON.stringify(T);else if(utils.isArray(T)&&isFlatArray(T)||(utils.isFileList(T)||utils.endsWith(O,"[]"))&&(C=utils.toArray(T)))return O=removeBrackets(O),C.forEach(function(L,D){!(utils.isUndefined(L)||L===null)&&e.append(o===!0?renderKey([O],D,i):o===null?O:O+"[]",c(L))}),!1}return isVisitable(T)?!0:(e.append(renderKey(M,O,i),c(T)),!1)}const p=[],v=Object.assign(predicates,{defaultVisitor:l,convertValue:c,isVisitable});function S(T,O){if(!utils.isUndefined(T)){if(p.indexOf(T)!==-1)throw Error("Circular reference detected in "+O.join("."));p.push(T),utils.forEach(T,function(C,w){(!(utils.isUndefined(C)||C===null)&&n.call(e,C,utils.isString(w)?w.trim():w,O,v))===!0&&S(C,O?O.concat(w):[w])}),p.pop()}}if(!utils.isObject(t))throw new TypeError("data must be an object");return S(t),e}function encode$1(t){const e={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(t).replace(/[!'()~]|%20|%00/g,function(r){return e[r]})}function AxiosURLSearchParams(t,e){this._pairs=[],t&&toFormData(t,this,e)}const prototype=AxiosURLSearchParams.prototype;prototype.append=function(e,a){this._pairs.push([e,a])};prototype.toString=function(e){const a=e?function(r){return e.call(this,r,encode$1)}:encode$1;return this._pairs.map(function(n){return a(n[0])+"="+a(n[1])},"").join("&")};function encode(t){return encodeURIComponent(t).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function buildURL(t,e,a){if(!e)return t;const r=a&&a.encode||encode,n=a&&a.serialize;let i;if(n?i=n(e,a):i=utils.isURLSearchParams(e)?e.toString():new AxiosURLSearchParams(e,a).toString(r),i){const o=t.indexOf("#");o!==-1&&(t=t.slice(0,o)),t+=(t.indexOf("?")===-1?"?":"&")+i}return t}class InterceptorManager{constructor(){this.handlers=[]}use(e,a,r){return this.handlers.push({fulfilled:e,rejected:a,synchronous:r?r.synchronous:!1,runWhen:r?r.runWhen:null}),this.handlers.length-1}eject(e){this.handlers[e]&&(this.handlers[e]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(e){utils.forEach(this.handlers,function(r){r!==null&&e(r)})}}const InterceptorManager$1=InterceptorManager,transitionalDefaults={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},URLSearchParams$1=typeof URLSearchParams<"u"?URLSearchParams:AxiosURLSearchParams,FormData$1=typeof FormData<"u"?FormData:null,Blob$1=typeof Blob<"u"?Blob:null,isStandardBrowserEnv=(()=>{let t;return typeof navigator<"u"&&((t=navigator.product)==="ReactNative"||t==="NativeScript"||t==="NS")?!1:typeof window<"u"&&typeof document<"u"})(),isStandardBrowserWebWorkerEnv=(()=>typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function")(),platform={isBrowser:!0,classes:{URLSearchParams:URLSearchParams$1,FormData:FormData$1,Blob:Blob$1},isStandardBrowserEnv,isStandardBrowserWebWorkerEnv,protocols:["http","https","file","blob","url","data"]};function toURLEncodedForm(t,e){return toFormData(t,new platform.classes.URLSearchParams,Object.assign({visitor:function(a,r,n,i){return platform.isNode&&utils.isBuffer(a)?(this.append(r,a.toString("base64")),!1):i.defaultVisitor.apply(this,arguments)}},e))}function parsePropPath(t){return utils.matchAll(/\w+|\[(\w*)]/g,t).map(e=>e[0]==="[]"?"":e[1]||e[0])}function arrayToObject(t){const e={},a=Object.keys(t);let r;const n=a.length;let i;for(r=0;r=a.length;return o=!o&&utils.isArray(n)?n.length:o,u?(utils.hasOwnProp(n,o)?n[o]=[n[o],r]:n[o]=r,!s):((!n[o]||!utils.isObject(n[o]))&&(n[o]=[]),e(a,r,n[o],i)&&utils.isArray(n[o])&&(n[o]=arrayToObject(n[o])),!s)}if(utils.isFormData(t)&&utils.isFunction(t.entries)){const a={};return utils.forEachEntry(t,(r,n)=>{e(parsePropPath(r),n,a,0)}),a}return null}function stringifySafely(t,e,a){if(utils.isString(t))try{return(e||JSON.parse)(t),utils.trim(t)}catch(r){if(r.name!=="SyntaxError")throw r}return(a||JSON.stringify)(t)}const defaults={transitional:transitionalDefaults,adapter:["xhr","http"],transformRequest:[function(e,a){const r=a.getContentType()||"",n=r.indexOf("application/json")>-1,i=utils.isObject(e);if(i&&utils.isHTMLForm(e)&&(e=new FormData(e)),utils.isFormData(e))return n&&n?JSON.stringify(formDataToJSON(e)):e;if(utils.isArrayBuffer(e)||utils.isBuffer(e)||utils.isStream(e)||utils.isFile(e)||utils.isBlob(e))return e;if(utils.isArrayBufferView(e))return e.buffer;if(utils.isURLSearchParams(e))return a.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();let s;if(i){if(r.indexOf("application/x-www-form-urlencoded")>-1)return toURLEncodedForm(e,this.formSerializer).toString();if((s=utils.isFileList(e))||r.indexOf("multipart/form-data")>-1){const u=this.env&&this.env.FormData;return toFormData(s?{"files[]":e}:e,u&&new u,this.formSerializer)}}return i||n?(a.setContentType("application/json",!1),stringifySafely(e)):e}],transformResponse:[function(e){const a=this.transitional||defaults.transitional,r=a&&a.forcedJSONParsing,n=this.responseType==="json";if(e&&utils.isString(e)&&(r&&!this.responseType||n)){const o=!(a&&a.silentJSONParsing)&&n;try{return JSON.parse(e)}catch(s){if(o)throw s.name==="SyntaxError"?AxiosError.from(s,AxiosError.ERR_BAD_RESPONSE,this,null,this.response):s}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:platform.classes.FormData,Blob:platform.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};utils.forEach(["delete","get","head","post","put","patch"],t=>{defaults.headers[t]={}});const defaults$1=defaults,ignoreDuplicateOf=utils.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),parseHeaders=t=>{const e={};let a,r,n;return t&&t.split(` -`).forEach(function(o){n=o.indexOf(":"),a=o.substring(0,n).trim().toLowerCase(),r=o.substring(n+1).trim(),!(!a||e[a]&&ignoreDuplicateOf[a])&&(a==="set-cookie"?e[a]?e[a].push(r):e[a]=[r]:e[a]=e[a]?e[a]+", "+r:r)}),e},$internals=Symbol("internals");function normalizeHeader(t){return t&&String(t).trim().toLowerCase()}function normalizeValue(t){return t===!1||t==null?t:utils.isArray(t)?t.map(normalizeValue):String(t)}function parseTokens(t){const e=Object.create(null),a=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let r;for(;r=a.exec(t);)e[r[1]]=r[2];return e}const isValidHeaderName=t=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(t.trim());function matchHeaderValue(t,e,a,r,n){if(utils.isFunction(r))return r.call(this,e,a);if(n&&(e=a),!!utils.isString(e)){if(utils.isString(r))return e.indexOf(r)!==-1;if(utils.isRegExp(r))return r.test(e)}}function formatHeader(t){return t.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,a,r)=>a.toUpperCase()+r)}function buildAccessors(t,e){const a=utils.toCamelCase(" "+e);["get","set","has"].forEach(r=>{Object.defineProperty(t,r+a,{value:function(n,i,o){return this[r].call(this,e,n,i,o)},configurable:!0})})}class AxiosHeaders{constructor(e){e&&this.set(e)}set(e,a,r){const n=this;function i(s,u,c){const l=normalizeHeader(u);if(!l)throw new Error("header name must be a non-empty string");const p=utils.findKey(n,l);(!p||n[p]===void 0||c===!0||c===void 0&&n[p]!==!1)&&(n[p||u]=normalizeValue(s))}const o=(s,u)=>utils.forEach(s,(c,l)=>i(c,l,u));return utils.isPlainObject(e)||e instanceof this.constructor?o(e,a):utils.isString(e)&&(e=e.trim())&&!isValidHeaderName(e)?o(parseHeaders(e),a):e!=null&&i(a,e,r),this}get(e,a){if(e=normalizeHeader(e),e){const r=utils.findKey(this,e);if(r){const n=this[r];if(!a)return n;if(a===!0)return parseTokens(n);if(utils.isFunction(a))return a.call(this,n,r);if(utils.isRegExp(a))return a.exec(n);throw new TypeError("parser must be boolean|regexp|function")}}}has(e,a){if(e=normalizeHeader(e),e){const r=utils.findKey(this,e);return!!(r&&this[r]!==void 0&&(!a||matchHeaderValue(this,this[r],r,a)))}return!1}delete(e,a){const r=this;let n=!1;function i(o){if(o=normalizeHeader(o),o){const s=utils.findKey(r,o);s&&(!a||matchHeaderValue(r,r[s],s,a))&&(delete r[s],n=!0)}}return utils.isArray(e)?e.forEach(i):i(e),n}clear(e){const a=Object.keys(this);let r=a.length,n=!1;for(;r--;){const i=a[r];(!e||matchHeaderValue(this,this[i],i,e,!0))&&(delete this[i],n=!0)}return n}normalize(e){const a=this,r={};return utils.forEach(this,(n,i)=>{const o=utils.findKey(r,i);if(o){a[o]=normalizeValue(n),delete a[i];return}const s=e?formatHeader(i):String(i).trim();s!==i&&delete a[i],a[s]=normalizeValue(n),r[s]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){const a=Object.create(null);return utils.forEach(this,(r,n)=>{r!=null&&r!==!1&&(a[n]=e&&utils.isArray(r)?r.join(", "):r)}),a}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,a])=>e+": "+a).join(` -`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(e){return e instanceof this?e:new this(e)}static concat(e,...a){const r=new this(e);return a.forEach(n=>r.set(n)),r}static accessor(e){const r=(this[$internals]=this[$internals]={accessors:{}}).accessors,n=this.prototype;function i(o){const s=normalizeHeader(o);r[s]||(buildAccessors(n,o),r[s]=!0)}return utils.isArray(e)?e.forEach(i):i(e),this}}AxiosHeaders.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);utils.reduceDescriptors(AxiosHeaders.prototype,({value:t},e)=>{let a=e[0].toUpperCase()+e.slice(1);return{get:()=>t,set(r){this[a]=r}}});utils.freezeMethods(AxiosHeaders);const AxiosHeaders$1=AxiosHeaders;function transformData(t,e){const a=this||defaults$1,r=e||a,n=AxiosHeaders$1.from(r.headers);let i=r.data;return utils.forEach(t,function(s){i=s.call(a,i,n.normalize(),e?e.status:void 0)}),n.normalize(),i}function isCancel(t){return!!(t&&t.__CANCEL__)}function CanceledError(t,e,a){AxiosError.call(this,t??"canceled",AxiosError.ERR_CANCELED,e,a),this.name="CanceledError"}utils.inherits(CanceledError,AxiosError,{__CANCEL__:!0});function settle(t,e,a){const r=a.config.validateStatus;!a.status||!r||r(a.status)?t(a):e(new AxiosError("Request failed with status code "+a.status,[AxiosError.ERR_BAD_REQUEST,AxiosError.ERR_BAD_RESPONSE][Math.floor(a.status/100)-4],a.config,a.request,a))}const cookies=platform.isStandardBrowserEnv?function(){return{write:function(a,r,n,i,o,s){const u=[];u.push(a+"="+encodeURIComponent(r)),utils.isNumber(n)&&u.push("expires="+new Date(n).toGMTString()),utils.isString(i)&&u.push("path="+i),utils.isString(o)&&u.push("domain="+o),s===!0&&u.push("secure"),document.cookie=u.join("; ")},read:function(a){const r=document.cookie.match(new RegExp("(^|;\\s*)("+a+")=([^;]*)"));return r?decodeURIComponent(r[3]):null},remove:function(a){this.write(a,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}();function isAbsoluteURL(t){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(t)}function combineURLs(t,e){return e?t.replace(/\/+$/,"")+"/"+e.replace(/^\/+/,""):t}function buildFullPath(t,e){return t&&!isAbsoluteURL(e)?combineURLs(t,e):e}const isURLSameOrigin=platform.isStandardBrowserEnv?function(){const e=/(msie|trident)/i.test(navigator.userAgent),a=document.createElement("a");let r;function n(i){let o=i;return e&&(a.setAttribute("href",o),o=a.href),a.setAttribute("href",o),{href:a.href,protocol:a.protocol?a.protocol.replace(/:$/,""):"",host:a.host,search:a.search?a.search.replace(/^\?/,""):"",hash:a.hash?a.hash.replace(/^#/,""):"",hostname:a.hostname,port:a.port,pathname:a.pathname.charAt(0)==="/"?a.pathname:"/"+a.pathname}}return r=n(window.location.href),function(o){const s=utils.isString(o)?n(o):o;return s.protocol===r.protocol&&s.host===r.host}}():function(){return function(){return!0}}();function parseProtocol(t){const e=/^([-+\w]{1,25})(:?\/\/|:)/.exec(t);return e&&e[1]||""}function speedometer(t,e){t=t||10;const a=new Array(t),r=new Array(t);let n=0,i=0,o;return e=e!==void 0?e:1e3,function(u){const c=Date.now(),l=r[i];o||(o=c),a[n]=u,r[n]=c;let p=i,v=0;for(;p!==n;)v+=a[p++],p=p%t;if(n=(n+1)%t,n===i&&(i=(i+1)%t),c-o{const i=n.loaded,o=n.lengthComputable?n.total:void 0,s=i-a,u=r(s),c=i<=o;a=i;const l={loaded:i,total:o,progress:o?i/o:void 0,bytes:s,rate:u||void 0,estimated:u&&o&&c?(o-i)/u:void 0,event:n};l[e?"download":"upload"]=!0,t(l)}}const isXHRAdapterSupported=typeof XMLHttpRequest<"u",xhrAdapter=isXHRAdapterSupported&&function(t){return new Promise(function(a,r){let n=t.data;const i=AxiosHeaders$1.from(t.headers).normalize(),o=t.responseType;let s;function u(){t.cancelToken&&t.cancelToken.unsubscribe(s),t.signal&&t.signal.removeEventListener("abort",s)}let c;utils.isFormData(n)&&(platform.isStandardBrowserEnv||platform.isStandardBrowserWebWorkerEnv?i.setContentType(!1):i.getContentType(/^\s*multipart\/form-data/)?utils.isString(c=i.getContentType())&&i.setContentType(c.replace(/^\s*(multipart\/form-data);+/,"$1")):i.setContentType("multipart/form-data"));let l=new XMLHttpRequest;if(t.auth){const T=t.auth.username||"",O=t.auth.password?unescape(encodeURIComponent(t.auth.password)):"";i.set("Authorization","Basic "+btoa(T+":"+O))}const p=buildFullPath(t.baseURL,t.url);l.open(t.method.toUpperCase(),buildURL(p,t.params,t.paramsSerializer),!0),l.timeout=t.timeout;function v(){if(!l)return;const T=AxiosHeaders$1.from("getAllResponseHeaders"in l&&l.getAllResponseHeaders()),M={data:!o||o==="text"||o==="json"?l.responseText:l.response,status:l.status,statusText:l.statusText,headers:T,config:t,request:l};settle(function(w){a(w),u()},function(w){r(w),u()},M),l=null}if("onloadend"in l?l.onloadend=v:l.onreadystatechange=function(){!l||l.readyState!==4||l.status===0&&!(l.responseURL&&l.responseURL.indexOf("file:")===0)||setTimeout(v)},l.onabort=function(){l&&(r(new AxiosError("Request aborted",AxiosError.ECONNABORTED,t,l)),l=null)},l.onerror=function(){r(new AxiosError("Network Error",AxiosError.ERR_NETWORK,t,l)),l=null},l.ontimeout=function(){let O=t.timeout?"timeout of "+t.timeout+"ms exceeded":"timeout exceeded";const M=t.transitional||transitionalDefaults;t.timeoutErrorMessage&&(O=t.timeoutErrorMessage),r(new AxiosError(O,M.clarifyTimeoutError?AxiosError.ETIMEDOUT:AxiosError.ECONNABORTED,t,l)),l=null},platform.isStandardBrowserEnv){const T=isURLSameOrigin(p)&&t.xsrfCookieName&&cookies.read(t.xsrfCookieName);T&&i.set(t.xsrfHeaderName,T)}n===void 0&&i.setContentType(null),"setRequestHeader"in l&&utils.forEach(i.toJSON(),function(O,M){l.setRequestHeader(M,O)}),utils.isUndefined(t.withCredentials)||(l.withCredentials=!!t.withCredentials),o&&o!=="json"&&(l.responseType=t.responseType),typeof t.onDownloadProgress=="function"&&l.addEventListener("progress",progressEventReducer(t.onDownloadProgress,!0)),typeof t.onUploadProgress=="function"&&l.upload&&l.upload.addEventListener("progress",progressEventReducer(t.onUploadProgress)),(t.cancelToken||t.signal)&&(s=T=>{l&&(r(!T||T.type?new CanceledError(null,t,l):T),l.abort(),l=null)},t.cancelToken&&t.cancelToken.subscribe(s),t.signal&&(t.signal.aborted?s():t.signal.addEventListener("abort",s)));const S=parseProtocol(p);if(S&&platform.protocols.indexOf(S)===-1){r(new AxiosError("Unsupported protocol "+S+":",AxiosError.ERR_BAD_REQUEST,t));return}l.send(n||null)})},knownAdapters={http:httpAdapter,xhr:xhrAdapter};utils.forEach(knownAdapters,(t,e)=>{if(t){try{Object.defineProperty(t,"name",{value:e})}catch{}Object.defineProperty(t,"adapterName",{value:e})}});const renderReason=t=>`- ${t}`,isResolvedHandle=t=>utils.isFunction(t)||t===null||t===!1,adapters={getAdapter:t=>{t=utils.isArray(t)?t:[t];const{length:e}=t;let a,r;const n={};for(let i=0;i`adapter ${s} `+(u===!1?"is not supported by the environment":"is not available in the build"));let o=e?i.length>1?`since : +function bind$4(t,e){return function(){return t.apply(e,arguments)}}const{toString:toString$7}=Object.prototype,{getPrototypeOf}=Object,kindOf=(t=>e=>{const a=toString$7.call(e);return t[a]||(t[a]=a.slice(8,-1).toLowerCase())})(Object.create(null)),kindOfTest=t=>(t=t.toLowerCase(),e=>kindOf(e)===t),typeOfTest=t=>e=>typeof e===t,{isArray:isArray$c}=Array,isUndefined=typeOfTest("undefined");function isBuffer$3(t){return t!==null&&!isUndefined(t)&&t.constructor!==null&&!isUndefined(t.constructor)&&isFunction$5(t.constructor.isBuffer)&&t.constructor.isBuffer(t)}const isArrayBuffer=kindOfTest("ArrayBuffer");function isArrayBufferView(t){let e;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?e=ArrayBuffer.isView(t):e=t&&t.buffer&&isArrayBuffer(t.buffer),e}const isString$1=typeOfTest("string"),isFunction$5=typeOfTest("function"),isNumber=typeOfTest("number"),isObject$b=t=>t!==null&&typeof t=="object",isBoolean=t=>t===!0||t===!1,isPlainObject=t=>{if(kindOf(t)!=="object")return!1;const e=getPrototypeOf(t);return(e===null||e===Object.prototype||Object.getPrototypeOf(e)===null)&&!(Symbol.toStringTag in t)&&!(Symbol.iterator in t)},isDate$1=kindOfTest("Date"),isFile=kindOfTest("File"),isBlob=kindOfTest("Blob"),isFileList=kindOfTest("FileList"),isStream=t=>isObject$b(t)&&isFunction$5(t.pipe),isFormData=t=>{let e;return t&&(typeof FormData=="function"&&t instanceof FormData||isFunction$5(t.append)&&((e=kindOf(t))==="formdata"||e==="object"&&isFunction$5(t.toString)&&t.toString()==="[object FormData]"))},isURLSearchParams=kindOfTest("URLSearchParams"),trim$2=t=>t.trim?t.trim():t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function forEach(t,e,{allOwnKeys:a=!1}={}){if(t===null||typeof t>"u")return;let r,n;if(typeof t!="object"&&(t=[t]),isArray$c(t))for(r=0,n=t.length;r0;)if(n=a[r],e===n.toLowerCase())return n;return null}const _global=(()=>typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global)(),isContextDefined=t=>!isUndefined(t)&&t!==_global;function merge(){const{caseless:t}=isContextDefined(this)&&this||{},e={},a=(r,n)=>{const i=t&&findKey$1(e,n)||n;isPlainObject(e[i])&&isPlainObject(r)?e[i]=merge(e[i],r):isPlainObject(r)?e[i]=merge({},r):isArray$c(r)?e[i]=r.slice():e[i]=r};for(let r=0,n=arguments.length;r(forEach(e,(n,i)=>{a&&isFunction$5(n)?t[i]=bind$4(n,a):t[i]=n},{allOwnKeys:r}),t),stripBOM=t=>(t.charCodeAt(0)===65279&&(t=t.slice(1)),t),inherits=(t,e,a,r)=>{t.prototype=Object.create(e.prototype,r),t.prototype.constructor=t,Object.defineProperty(t,"super",{value:e.prototype}),a&&Object.assign(t.prototype,a)},toFlatObject=(t,e,a,r)=>{let n,i,o;const s={};if(e=e||{},t==null)return e;do{for(n=Object.getOwnPropertyNames(t),i=n.length;i-- >0;)o=n[i],(!r||r(o,t,e))&&!s[o]&&(e[o]=t[o],s[o]=!0);t=a!==!1&&getPrototypeOf(t)}while(t&&(!a||a(t,e))&&t!==Object.prototype);return e},endsWith=(t,e,a)=>{t=String(t),(a===void 0||a>t.length)&&(a=t.length),a-=e.length;const r=t.indexOf(e,a);return r!==-1&&r===a},toArray=t=>{if(!t)return null;if(isArray$c(t))return t;let e=t.length;if(!isNumber(e))return null;const a=new Array(e);for(;e-- >0;)a[e]=t[e];return a},isTypedArray$3=(t=>e=>t&&e instanceof t)(typeof Uint8Array<"u"&&getPrototypeOf(Uint8Array)),forEachEntry=(t,e)=>{const r=(t&&t[Symbol.iterator]).call(t);let n;for(;(n=r.next())&&!n.done;){const i=n.value;e.call(t,i[0],i[1])}},matchAll=(t,e)=>{let a;const r=[];for(;(a=t.exec(e))!==null;)r.push(a);return r},isHTMLForm=kindOfTest("HTMLFormElement"),toCamelCase=t=>t.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(a,r,n){return r.toUpperCase()+n}),hasOwnProperty$c=(({hasOwnProperty:t})=>(e,a)=>t.call(e,a))(Object.prototype),isRegExp=kindOfTest("RegExp"),reduceDescriptors=(t,e)=>{const a=Object.getOwnPropertyDescriptors(t),r={};forEach(a,(n,i)=>{let o;(o=e(n,i,t))!==!1&&(r[i]=o||n)}),Object.defineProperties(t,r)},freezeMethods=t=>{reduceDescriptors(t,(e,a)=>{if(isFunction$5(t)&&["arguments","caller","callee"].indexOf(a)!==-1)return!1;const r=t[a];if(isFunction$5(r)){if(e.enumerable=!1,"writable"in e){e.writable=!1;return}e.set||(e.set=()=>{throw Error("Can not rewrite read-only method '"+a+"'")})}})},toObjectSet=(t,e)=>{const a={},r=n=>{n.forEach(i=>{a[i]=!0})};return isArray$c(t)?r(t):r(String(t).split(e)),a},noop$3=()=>{},toFiniteNumber=(t,e)=>(t=+t,Number.isFinite(t)?t:e),ALPHA="abcdefghijklmnopqrstuvwxyz",DIGIT="0123456789",ALPHABET={DIGIT,ALPHA,ALPHA_DIGIT:ALPHA+ALPHA.toUpperCase()+DIGIT},generateString=(t=16,e=ALPHABET.ALPHA_DIGIT)=>{let a="";const{length:r}=e;for(;t--;)a+=e[Math.random()*r|0];return a};function isSpecCompliantForm(t){return!!(t&&isFunction$5(t.append)&&t[Symbol.toStringTag]==="FormData"&&t[Symbol.iterator])}const toJSONObject=t=>{const e=new Array(10),a=(r,n)=>{if(isObject$b(r)){if(e.indexOf(r)>=0)return;if(!("toJSON"in r)){e[n]=r;const i=isArray$c(r)?[]:{};return forEach(r,(o,s)=>{const u=a(o,n+1);!isUndefined(u)&&(i[s]=u)}),e[n]=void 0,i}}return r};return a(t,0)},isAsyncFn=kindOfTest("AsyncFunction"),isThenable=t=>t&&(isObject$b(t)||isFunction$5(t))&&isFunction$5(t.then)&&isFunction$5(t.catch),utils$1={isArray:isArray$c,isArrayBuffer,isBuffer:isBuffer$3,isFormData,isArrayBufferView,isString:isString$1,isNumber,isBoolean,isObject:isObject$b,isPlainObject,isUndefined,isDate:isDate$1,isFile,isBlob,isRegExp,isFunction:isFunction$5,isStream,isURLSearchParams,isTypedArray:isTypedArray$3,isFileList,forEach,merge,extend,trim:trim$2,stripBOM,inherits,toFlatObject,kindOf,kindOfTest,endsWith,toArray,forEachEntry,matchAll,isHTMLForm,hasOwnProperty:hasOwnProperty$c,hasOwnProp:hasOwnProperty$c,reduceDescriptors,freezeMethods,toObjectSet,toCamelCase,noop:noop$3,toFiniteNumber,findKey:findKey$1,global:_global,isContextDefined,ALPHABET,generateString,isSpecCompliantForm,toJSONObject,isAsyncFn,isThenable};function AxiosError(t,e,a,r,n){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack,this.message=t,this.name="AxiosError",e&&(this.code=e),a&&(this.config=a),r&&(this.request=r),n&&(this.response=n)}utils$1.inherits(AxiosError,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:utils$1.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});const prototype$1=AxiosError.prototype,descriptors={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach(t=>{descriptors[t]={value:t}});Object.defineProperties(AxiosError,descriptors);Object.defineProperty(prototype$1,"isAxiosError",{value:!0});AxiosError.from=(t,e,a,r,n,i)=>{const o=Object.create(prototype$1);return utils$1.toFlatObject(t,o,function(u){return u!==Error.prototype},s=>s!=="isAxiosError"),AxiosError.call(o,t.message,e,a,r,n),o.cause=t,o.name=t.name,i&&Object.assign(o,i),o};const httpAdapter=null;function isVisitable(t){return utils$1.isPlainObject(t)||utils$1.isArray(t)}function removeBrackets(t){return utils$1.endsWith(t,"[]")?t.slice(0,-2):t}function renderKey(t,e,a){return t?t.concat(e).map(function(n,i){return n=removeBrackets(n),!a&&i?"["+n+"]":n}).join(a?".":""):e}function isFlatArray(t){return utils$1.isArray(t)&&!t.some(isVisitable)}const predicates=utils$1.toFlatObject(utils$1,{},null,function(e){return/^is[A-Z]/.test(e)});function toFormData(t,e,a){if(!utils$1.isObject(t))throw new TypeError("target must be an object");e=e||new FormData,a=utils$1.toFlatObject(a,{metaTokens:!0,dots:!1,indexes:!1},!1,function(O,M){return!utils$1.isUndefined(M[O])});const r=a.metaTokens,n=a.visitor||d,i=a.dots,o=a.indexes,u=(a.Blob||typeof Blob<"u"&&Blob)&&utils$1.isSpecCompliantForm(e);if(!utils$1.isFunction(n))throw new TypeError("visitor must be a function");function l(A){if(A===null)return"";if(utils$1.isDate(A))return A.toISOString();if(!u&&utils$1.isBlob(A))throw new AxiosError("Blob is not supported. Use a Buffer instead.");return utils$1.isArrayBuffer(A)||utils$1.isTypedArray(A)?u&&typeof Blob=="function"?new Blob([A]):Buffer.from(A):A}function d(A,O,M){let C=A;if(A&&!M&&typeof A=="object"){if(utils$1.endsWith(O,"{}"))O=r?O:O.slice(0,-2),A=JSON.stringify(A);else if(utils$1.isArray(A)&&isFlatArray(A)||(utils$1.isFileList(A)||utils$1.endsWith(O,"[]"))&&(C=utils$1.toArray(A)))return O=removeBrackets(O),C.forEach(function(N,D){!(utils$1.isUndefined(N)||N===null)&&e.append(o===!0?renderKey([O],D,i):o===null?O:O+"[]",l(N))}),!1}return isVisitable(A)?!0:(e.append(renderKey(M,O,i),l(A)),!1)}const h=[],v=Object.assign(predicates,{defaultVisitor:d,convertValue:l,isVisitable});function S(A,O){if(!utils$1.isUndefined(A)){if(h.indexOf(A)!==-1)throw Error("Circular reference detected in "+O.join("."));h.push(A),utils$1.forEach(A,function(C,w){(!(utils$1.isUndefined(C)||C===null)&&n.call(e,C,utils$1.isString(w)?w.trim():w,O,v))===!0&&S(C,O?O.concat(w):[w])}),h.pop()}}if(!utils$1.isObject(t))throw new TypeError("data must be an object");return S(t),e}function encode$1(t){const e={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(t).replace(/[!'()~]|%20|%00/g,function(r){return e[r]})}function AxiosURLSearchParams(t,e){this._pairs=[],t&&toFormData(t,this,e)}const prototype=AxiosURLSearchParams.prototype;prototype.append=function(e,a){this._pairs.push([e,a])};prototype.toString=function(e){const a=e?function(r){return e.call(this,r,encode$1)}:encode$1;return this._pairs.map(function(n){return a(n[0])+"="+a(n[1])},"").join("&")};function encode(t){return encodeURIComponent(t).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function buildURL(t,e,a){if(!e)return t;const r=a&&a.encode||encode,n=a&&a.serialize;let i;if(n?i=n(e,a):i=utils$1.isURLSearchParams(e)?e.toString():new AxiosURLSearchParams(e,a).toString(r),i){const o=t.indexOf("#");o!==-1&&(t=t.slice(0,o)),t+=(t.indexOf("?")===-1?"?":"&")+i}return t}class InterceptorManager{constructor(){this.handlers=[]}use(e,a,r){return this.handlers.push({fulfilled:e,rejected:a,synchronous:r?r.synchronous:!1,runWhen:r?r.runWhen:null}),this.handlers.length-1}eject(e){this.handlers[e]&&(this.handlers[e]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(e){utils$1.forEach(this.handlers,function(r){r!==null&&e(r)})}}const InterceptorManager$1=InterceptorManager,transitionalDefaults={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},URLSearchParams$1=typeof URLSearchParams<"u"?URLSearchParams:AxiosURLSearchParams,FormData$1=typeof FormData<"u"?FormData:null,Blob$1=typeof Blob<"u"?Blob:null,platform$1={isBrowser:!0,classes:{URLSearchParams:URLSearchParams$1,FormData:FormData$1,Blob:Blob$1},protocols:["http","https","file","blob","url","data"]},hasBrowserEnv=typeof window<"u"&&typeof document<"u",hasStandardBrowserEnv=(t=>hasBrowserEnv&&["ReactNative","NativeScript","NS"].indexOf(t)<0)(typeof navigator<"u"&&navigator.product),hasStandardBrowserWebWorkerEnv=(()=>typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function")(),utils=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv,hasStandardBrowserEnv,hasStandardBrowserWebWorkerEnv},Symbol.toStringTag,{value:"Module"})),platform={...utils,...platform$1};function toURLEncodedForm(t,e){return toFormData(t,new platform.classes.URLSearchParams,Object.assign({visitor:function(a,r,n,i){return platform.isNode&&utils$1.isBuffer(a)?(this.append(r,a.toString("base64")),!1):i.defaultVisitor.apply(this,arguments)}},e))}function parsePropPath(t){return utils$1.matchAll(/\w+|\[(\w*)]/g,t).map(e=>e[0]==="[]"?"":e[1]||e[0])}function arrayToObject(t){const e={},a=Object.keys(t);let r;const n=a.length;let i;for(r=0;r=a.length;return o=!o&&utils$1.isArray(n)?n.length:o,u?(utils$1.hasOwnProp(n,o)?n[o]=[n[o],r]:n[o]=r,!s):((!n[o]||!utils$1.isObject(n[o]))&&(n[o]=[]),e(a,r,n[o],i)&&utils$1.isArray(n[o])&&(n[o]=arrayToObject(n[o])),!s)}if(utils$1.isFormData(t)&&utils$1.isFunction(t.entries)){const a={};return utils$1.forEachEntry(t,(r,n)=>{e(parsePropPath(r),n,a,0)}),a}return null}function stringifySafely(t,e,a){if(utils$1.isString(t))try{return(e||JSON.parse)(t),utils$1.trim(t)}catch(r){if(r.name!=="SyntaxError")throw r}return(a||JSON.stringify)(t)}const defaults={transitional:transitionalDefaults,adapter:["xhr","http"],transformRequest:[function(e,a){const r=a.getContentType()||"",n=r.indexOf("application/json")>-1,i=utils$1.isObject(e);if(i&&utils$1.isHTMLForm(e)&&(e=new FormData(e)),utils$1.isFormData(e))return n&&n?JSON.stringify(formDataToJSON(e)):e;if(utils$1.isArrayBuffer(e)||utils$1.isBuffer(e)||utils$1.isStream(e)||utils$1.isFile(e)||utils$1.isBlob(e))return e;if(utils$1.isArrayBufferView(e))return e.buffer;if(utils$1.isURLSearchParams(e))return a.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();let s;if(i){if(r.indexOf("application/x-www-form-urlencoded")>-1)return toURLEncodedForm(e,this.formSerializer).toString();if((s=utils$1.isFileList(e))||r.indexOf("multipart/form-data")>-1){const u=this.env&&this.env.FormData;return toFormData(s?{"files[]":e}:e,u&&new u,this.formSerializer)}}return i||n?(a.setContentType("application/json",!1),stringifySafely(e)):e}],transformResponse:[function(e){const a=this.transitional||defaults.transitional,r=a&&a.forcedJSONParsing,n=this.responseType==="json";if(e&&utils$1.isString(e)&&(r&&!this.responseType||n)){const o=!(a&&a.silentJSONParsing)&&n;try{return JSON.parse(e)}catch(s){if(o)throw s.name==="SyntaxError"?AxiosError.from(s,AxiosError.ERR_BAD_RESPONSE,this,null,this.response):s}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:platform.classes.FormData,Blob:platform.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};utils$1.forEach(["delete","get","head","post","put","patch"],t=>{defaults.headers[t]={}});const defaults$1=defaults,ignoreDuplicateOf=utils$1.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),parseHeaders=t=>{const e={};let a,r,n;return t&&t.split(` +`).forEach(function(o){n=o.indexOf(":"),a=o.substring(0,n).trim().toLowerCase(),r=o.substring(n+1).trim(),!(!a||e[a]&&ignoreDuplicateOf[a])&&(a==="set-cookie"?e[a]?e[a].push(r):e[a]=[r]:e[a]=e[a]?e[a]+", "+r:r)}),e},$internals=Symbol("internals");function normalizeHeader(t){return t&&String(t).trim().toLowerCase()}function normalizeValue(t){return t===!1||t==null?t:utils$1.isArray(t)?t.map(normalizeValue):String(t)}function parseTokens(t){const e=Object.create(null),a=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let r;for(;r=a.exec(t);)e[r[1]]=r[2];return e}const isValidHeaderName=t=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(t.trim());function matchHeaderValue(t,e,a,r,n){if(utils$1.isFunction(r))return r.call(this,e,a);if(n&&(e=a),!!utils$1.isString(e)){if(utils$1.isString(r))return e.indexOf(r)!==-1;if(utils$1.isRegExp(r))return r.test(e)}}function formatHeader(t){return t.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,a,r)=>a.toUpperCase()+r)}function buildAccessors(t,e){const a=utils$1.toCamelCase(" "+e);["get","set","has"].forEach(r=>{Object.defineProperty(t,r+a,{value:function(n,i,o){return this[r].call(this,e,n,i,o)},configurable:!0})})}class AxiosHeaders{constructor(e){e&&this.set(e)}set(e,a,r){const n=this;function i(s,u,l){const d=normalizeHeader(u);if(!d)throw new Error("header name must be a non-empty string");const h=utils$1.findKey(n,d);(!h||n[h]===void 0||l===!0||l===void 0&&n[h]!==!1)&&(n[h||u]=normalizeValue(s))}const o=(s,u)=>utils$1.forEach(s,(l,d)=>i(l,d,u));return utils$1.isPlainObject(e)||e instanceof this.constructor?o(e,a):utils$1.isString(e)&&(e=e.trim())&&!isValidHeaderName(e)?o(parseHeaders(e),a):e!=null&&i(a,e,r),this}get(e,a){if(e=normalizeHeader(e),e){const r=utils$1.findKey(this,e);if(r){const n=this[r];if(!a)return n;if(a===!0)return parseTokens(n);if(utils$1.isFunction(a))return a.call(this,n,r);if(utils$1.isRegExp(a))return a.exec(n);throw new TypeError("parser must be boolean|regexp|function")}}}has(e,a){if(e=normalizeHeader(e),e){const r=utils$1.findKey(this,e);return!!(r&&this[r]!==void 0&&(!a||matchHeaderValue(this,this[r],r,a)))}return!1}delete(e,a){const r=this;let n=!1;function i(o){if(o=normalizeHeader(o),o){const s=utils$1.findKey(r,o);s&&(!a||matchHeaderValue(r,r[s],s,a))&&(delete r[s],n=!0)}}return utils$1.isArray(e)?e.forEach(i):i(e),n}clear(e){const a=Object.keys(this);let r=a.length,n=!1;for(;r--;){const i=a[r];(!e||matchHeaderValue(this,this[i],i,e,!0))&&(delete this[i],n=!0)}return n}normalize(e){const a=this,r={};return utils$1.forEach(this,(n,i)=>{const o=utils$1.findKey(r,i);if(o){a[o]=normalizeValue(n),delete a[i];return}const s=e?formatHeader(i):String(i).trim();s!==i&&delete a[i],a[s]=normalizeValue(n),r[s]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){const a=Object.create(null);return utils$1.forEach(this,(r,n)=>{r!=null&&r!==!1&&(a[n]=e&&utils$1.isArray(r)?r.join(", "):r)}),a}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,a])=>e+": "+a).join(` +`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(e){return e instanceof this?e:new this(e)}static concat(e,...a){const r=new this(e);return a.forEach(n=>r.set(n)),r}static accessor(e){const r=(this[$internals]=this[$internals]={accessors:{}}).accessors,n=this.prototype;function i(o){const s=normalizeHeader(o);r[s]||(buildAccessors(n,o),r[s]=!0)}return utils$1.isArray(e)?e.forEach(i):i(e),this}}AxiosHeaders.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);utils$1.reduceDescriptors(AxiosHeaders.prototype,({value:t},e)=>{let a=e[0].toUpperCase()+e.slice(1);return{get:()=>t,set(r){this[a]=r}}});utils$1.freezeMethods(AxiosHeaders);const AxiosHeaders$1=AxiosHeaders;function transformData(t,e){const a=this||defaults$1,r=e||a,n=AxiosHeaders$1.from(r.headers);let i=r.data;return utils$1.forEach(t,function(s){i=s.call(a,i,n.normalize(),e?e.status:void 0)}),n.normalize(),i}function isCancel(t){return!!(t&&t.__CANCEL__)}function CanceledError(t,e,a){AxiosError.call(this,t??"canceled",AxiosError.ERR_CANCELED,e,a),this.name="CanceledError"}utils$1.inherits(CanceledError,AxiosError,{__CANCEL__:!0});function settle(t,e,a){const r=a.config.validateStatus;!a.status||!r||r(a.status)?t(a):e(new AxiosError("Request failed with status code "+a.status,[AxiosError.ERR_BAD_REQUEST,AxiosError.ERR_BAD_RESPONSE][Math.floor(a.status/100)-4],a.config,a.request,a))}const cookies=platform.hasStandardBrowserEnv?{write(t,e,a,r,n,i){const o=[t+"="+encodeURIComponent(e)];utils$1.isNumber(a)&&o.push("expires="+new Date(a).toGMTString()),utils$1.isString(r)&&o.push("path="+r),utils$1.isString(n)&&o.push("domain="+n),i===!0&&o.push("secure"),document.cookie=o.join("; ")},read(t){const e=document.cookie.match(new RegExp("(^|;\\s*)("+t+")=([^;]*)"));return e?decodeURIComponent(e[3]):null},remove(t){this.write(t,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function isAbsoluteURL(t){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(t)}function combineURLs(t,e){return e?t.replace(/\/+$/,"")+"/"+e.replace(/^\/+/,""):t}function buildFullPath(t,e){return t&&!isAbsoluteURL(e)?combineURLs(t,e):e}const isURLSameOrigin=platform.hasStandardBrowserEnv?function(){const e=/(msie|trident)/i.test(navigator.userAgent),a=document.createElement("a");let r;function n(i){let o=i;return e&&(a.setAttribute("href",o),o=a.href),a.setAttribute("href",o),{href:a.href,protocol:a.protocol?a.protocol.replace(/:$/,""):"",host:a.host,search:a.search?a.search.replace(/^\?/,""):"",hash:a.hash?a.hash.replace(/^#/,""):"",hostname:a.hostname,port:a.port,pathname:a.pathname.charAt(0)==="/"?a.pathname:"/"+a.pathname}}return r=n(window.location.href),function(o){const s=utils$1.isString(o)?n(o):o;return s.protocol===r.protocol&&s.host===r.host}}():function(){return function(){return!0}}();function parseProtocol(t){const e=/^([-+\w]{1,25})(:?\/\/|:)/.exec(t);return e&&e[1]||""}function speedometer(t,e){t=t||10;const a=new Array(t),r=new Array(t);let n=0,i=0,o;return e=e!==void 0?e:1e3,function(u){const l=Date.now(),d=r[i];o||(o=l),a[n]=u,r[n]=l;let h=i,v=0;for(;h!==n;)v+=a[h++],h=h%t;if(n=(n+1)%t,n===i&&(i=(i+1)%t),l-o{const i=n.loaded,o=n.lengthComputable?n.total:void 0,s=i-a,u=r(s),l=i<=o;a=i;const d={loaded:i,total:o,progress:o?i/o:void 0,bytes:s,rate:u||void 0,estimated:u&&o&&l?(o-i)/u:void 0,event:n};d[e?"download":"upload"]=!0,t(d)}}const isXHRAdapterSupported=typeof XMLHttpRequest<"u",xhrAdapter=isXHRAdapterSupported&&function(t){return new Promise(function(a,r){let n=t.data;const i=AxiosHeaders$1.from(t.headers).normalize();let{responseType:o,withXSRFToken:s}=t,u;function l(){t.cancelToken&&t.cancelToken.unsubscribe(u),t.signal&&t.signal.removeEventListener("abort",u)}let d;if(utils$1.isFormData(n)){if(platform.hasStandardBrowserEnv||platform.hasStandardBrowserWebWorkerEnv)i.setContentType(!1);else if((d=i.getContentType())!==!1){const[O,...M]=d?d.split(";").map(C=>C.trim()).filter(Boolean):[];i.setContentType([O||"multipart/form-data",...M].join("; "))}}let h=new XMLHttpRequest;if(t.auth){const O=t.auth.username||"",M=t.auth.password?unescape(encodeURIComponent(t.auth.password)):"";i.set("Authorization","Basic "+btoa(O+":"+M))}const v=buildFullPath(t.baseURL,t.url);h.open(t.method.toUpperCase(),buildURL(v,t.params,t.paramsSerializer),!0),h.timeout=t.timeout;function S(){if(!h)return;const O=AxiosHeaders$1.from("getAllResponseHeaders"in h&&h.getAllResponseHeaders()),C={data:!o||o==="text"||o==="json"?h.responseText:h.response,status:h.status,statusText:h.statusText,headers:O,config:t,request:h};settle(function(N){a(N),l()},function(N){r(N),l()},C),h=null}if("onloadend"in h?h.onloadend=S:h.onreadystatechange=function(){!h||h.readyState!==4||h.status===0&&!(h.responseURL&&h.responseURL.indexOf("file:")===0)||setTimeout(S)},h.onabort=function(){h&&(r(new AxiosError("Request aborted",AxiosError.ECONNABORTED,t,h)),h=null)},h.onerror=function(){r(new AxiosError("Network Error",AxiosError.ERR_NETWORK,t,h)),h=null},h.ontimeout=function(){let M=t.timeout?"timeout of "+t.timeout+"ms exceeded":"timeout exceeded";const C=t.transitional||transitionalDefaults;t.timeoutErrorMessage&&(M=t.timeoutErrorMessage),r(new AxiosError(M,C.clarifyTimeoutError?AxiosError.ETIMEDOUT:AxiosError.ECONNABORTED,t,h)),h=null},platform.hasStandardBrowserEnv&&(s&&utils$1.isFunction(s)&&(s=s(t)),s||s!==!1&&isURLSameOrigin(v))){const O=t.xsrfHeaderName&&t.xsrfCookieName&&cookies.read(t.xsrfCookieName);O&&i.set(t.xsrfHeaderName,O)}n===void 0&&i.setContentType(null),"setRequestHeader"in h&&utils$1.forEach(i.toJSON(),function(M,C){h.setRequestHeader(C,M)}),utils$1.isUndefined(t.withCredentials)||(h.withCredentials=!!t.withCredentials),o&&o!=="json"&&(h.responseType=t.responseType),typeof t.onDownloadProgress=="function"&&h.addEventListener("progress",progressEventReducer(t.onDownloadProgress,!0)),typeof t.onUploadProgress=="function"&&h.upload&&h.upload.addEventListener("progress",progressEventReducer(t.onUploadProgress)),(t.cancelToken||t.signal)&&(u=O=>{h&&(r(!O||O.type?new CanceledError(null,t,h):O),h.abort(),h=null)},t.cancelToken&&t.cancelToken.subscribe(u),t.signal&&(t.signal.aborted?u():t.signal.addEventListener("abort",u)));const A=parseProtocol(v);if(A&&platform.protocols.indexOf(A)===-1){r(new AxiosError("Unsupported protocol "+A+":",AxiosError.ERR_BAD_REQUEST,t));return}h.send(n||null)})},knownAdapters={http:httpAdapter,xhr:xhrAdapter};utils$1.forEach(knownAdapters,(t,e)=>{if(t){try{Object.defineProperty(t,"name",{value:e})}catch{}Object.defineProperty(t,"adapterName",{value:e})}});const renderReason=t=>`- ${t}`,isResolvedHandle=t=>utils$1.isFunction(t)||t===null||t===!1,adapters={getAdapter:t=>{t=utils$1.isArray(t)?t:[t];const{length:e}=t;let a,r;const n={};for(let i=0;i`adapter ${s} `+(u===!1?"is not supported by the environment":"is not available in the build"));let o=e?i.length>1?`since : `+i.map(renderReason).join(` -`):" "+renderReason(i[0]):"as no adapter specified";throw new AxiosError("There is no suitable adapter to dispatch the request "+o,"ERR_NOT_SUPPORT")}return r},adapters:knownAdapters};function throwIfCancellationRequested(t){if(t.cancelToken&&t.cancelToken.throwIfRequested(),t.signal&&t.signal.aborted)throw new CanceledError(null,t)}function dispatchRequest(t){return throwIfCancellationRequested(t),t.headers=AxiosHeaders$1.from(t.headers),t.data=transformData.call(t,t.transformRequest),["post","put","patch"].indexOf(t.method)!==-1&&t.headers.setContentType("application/x-www-form-urlencoded",!1),adapters.getAdapter(t.adapter||defaults$1.adapter)(t).then(function(r){return throwIfCancellationRequested(t),r.data=transformData.call(t,t.transformResponse,r),r.headers=AxiosHeaders$1.from(r.headers),r},function(r){return isCancel(r)||(throwIfCancellationRequested(t),r&&r.response&&(r.response.data=transformData.call(t,t.transformResponse,r.response),r.response.headers=AxiosHeaders$1.from(r.response.headers))),Promise.reject(r)})}const headersToObject=t=>t instanceof AxiosHeaders$1?t.toJSON():t;function mergeConfig(t,e){e=e||{};const a={};function r(c,l,p){return utils.isPlainObject(c)&&utils.isPlainObject(l)?utils.merge.call({caseless:p},c,l):utils.isPlainObject(l)?utils.merge({},l):utils.isArray(l)?l.slice():l}function n(c,l,p){if(utils.isUndefined(l)){if(!utils.isUndefined(c))return r(void 0,c,p)}else return r(c,l,p)}function i(c,l){if(!utils.isUndefined(l))return r(void 0,l)}function o(c,l){if(utils.isUndefined(l)){if(!utils.isUndefined(c))return r(void 0,c)}else return r(void 0,l)}function s(c,l,p){if(p in e)return r(c,l);if(p in t)return r(void 0,c)}const u={url:i,method:i,data:i,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(c,l)=>n(headersToObject(c),headersToObject(l),!0)};return utils.forEach(Object.keys(Object.assign({},t,e)),function(l){const p=u[l]||n,v=p(t[l],e[l],l);utils.isUndefined(v)&&p!==s||(a[l]=v)}),a}const VERSION$1="1.6.0",validators$1={};["object","boolean","number","function","string","symbol"].forEach((t,e)=>{validators$1[t]=function(r){return typeof r===t||"a"+(e<1?"n ":" ")+t}});const deprecatedWarnings={};validators$1.transitional=function(e,a,r){function n(i,o){return"[Axios v"+VERSION$1+"] Transitional option '"+i+"'"+o+(r?". "+r:"")}return(i,o,s)=>{if(e===!1)throw new AxiosError(n(o," has been removed"+(a?" in "+a:"")),AxiosError.ERR_DEPRECATED);return a&&!deprecatedWarnings[o]&&(deprecatedWarnings[o]=!0,console.warn(n(o," has been deprecated since v"+a+" and will be removed in the near future"))),e?e(i,o,s):!0}};function assertOptions(t,e,a){if(typeof t!="object")throw new AxiosError("options must be an object",AxiosError.ERR_BAD_OPTION_VALUE);const r=Object.keys(t);let n=r.length;for(;n-- >0;){const i=r[n],o=e[i];if(o){const s=t[i],u=s===void 0||o(s,i,t);if(u!==!0)throw new AxiosError("option "+i+" must be "+u,AxiosError.ERR_BAD_OPTION_VALUE);continue}if(a!==!0)throw new AxiosError("Unknown option "+i,AxiosError.ERR_BAD_OPTION)}}const validator={assertOptions,validators:validators$1},validators=validator.validators;class Axios{constructor(e){this.defaults=e,this.interceptors={request:new InterceptorManager$1,response:new InterceptorManager$1}}request(e,a){typeof e=="string"?(a=a||{},a.url=e):a=e||{},a=mergeConfig(this.defaults,a);const{transitional:r,paramsSerializer:n,headers:i}=a;r!==void 0&&validator.assertOptions(r,{silentJSONParsing:validators.transitional(validators.boolean),forcedJSONParsing:validators.transitional(validators.boolean),clarifyTimeoutError:validators.transitional(validators.boolean)},!1),n!=null&&(utils.isFunction(n)?a.paramsSerializer={serialize:n}:validator.assertOptions(n,{encode:validators.function,serialize:validators.function},!0)),a.method=(a.method||this.defaults.method||"get").toLowerCase();let o=i&&utils.merge(i.common,i[a.method]);i&&utils.forEach(["delete","get","head","post","put","patch","common"],T=>{delete i[T]}),a.headers=AxiosHeaders$1.concat(o,i);const s=[];let u=!0;this.interceptors.request.forEach(function(O){typeof O.runWhen=="function"&&O.runWhen(a)===!1||(u=u&&O.synchronous,s.unshift(O.fulfilled,O.rejected))});const c=[];this.interceptors.response.forEach(function(O){c.push(O.fulfilled,O.rejected)});let l,p=0,v;if(!u){const T=[dispatchRequest.bind(this),void 0];for(T.unshift.apply(T,s),T.push.apply(T,c),v=T.length,l=Promise.resolve(a);p{if(!r._listeners)return;let i=r._listeners.length;for(;i-- >0;)r._listeners[i](n);r._listeners=null}),this.promise.then=n=>{let i;const o=new Promise(s=>{r.subscribe(s),i=s}).then(n);return o.cancel=function(){r.unsubscribe(i)},o},e(function(i,o,s){r.reason||(r.reason=new CanceledError(i,o,s),a(r.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;const a=this._listeners.indexOf(e);a!==-1&&this._listeners.splice(a,1)}static source(){let e;return{token:new CancelToken(function(n){e=n}),cancel:e}}}const CancelToken$1=CancelToken;function spread(t){return function(a){return t.apply(null,a)}}function isAxiosError(t){return utils.isObject(t)&&t.isAxiosError===!0}const HttpStatusCode={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(HttpStatusCode).forEach(([t,e])=>{HttpStatusCode[e]=t});const HttpStatusCode$1=HttpStatusCode;function createInstance(t){const e=new Axios$1(t),a=bind$4(Axios$1.prototype.request,e);return utils.extend(a,Axios$1.prototype,e,{allOwnKeys:!0}),utils.extend(a,e,null,{allOwnKeys:!0}),a.create=function(n){return createInstance(mergeConfig(t,n))},a}const axios=createInstance(defaults$1);axios.Axios=Axios$1;axios.CanceledError=CanceledError;axios.CancelToken=CancelToken$1;axios.isCancel=isCancel;axios.VERSION=VERSION$1;axios.toFormData=toFormData;axios.AxiosError=AxiosError;axios.Cancel=axios.CanceledError;axios.all=function(e){return Promise.all(e)};axios.spread=spread;axios.isAxiosError=isAxiosError;axios.mergeConfig=mergeConfig;axios.AxiosHeaders=AxiosHeaders$1;axios.formToJSON=t=>formDataToJSON(utils.isHTMLForm(t)?new FormData(t):t);axios.getAdapter=adapters.getAdapter;axios.HttpStatusCode=HttpStatusCode$1;axios.default=axios;const axios$1=axios;var commonjsGlobal=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function getDefaultExportFromCjs(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var assign=make_assign(),create$2=make_create(),trim$1=make_trim(),Global$5=typeof window<"u"?window:commonjsGlobal,util$7={assign,create:create$2,trim:trim$1,bind:bind$3,slice:slice$2,each:each$8,map,pluck:pluck$1,isList:isList$1,isFunction:isFunction$4,isObject:isObject$a,Global:Global$5};function make_assign(){return Object.assign?Object.assign:function(e,a,r,n){for(var i=1;i"u"?null:console;if(t){var e=t.warn?t.warn:t.log;e.apply(t,arguments)}}function createStore(t,e,a){a||(a=""),t&&!isList(t)&&(t=[t]),e&&!isList(e)&&(e=[e]);var r=a?"__storejs_"+a+"_":"",n=a?new RegExp("^"+r):null,i=/^[a-zA-Z0-9_\-]*$/;if(!i.test(a))throw new Error("store.js namespaces can only have alphanumerics + underscores and dashes");var o={_namespacePrefix:r,_namespaceRegexp:n,_testStorage:function(u){try{var c="__storejs__test__";u.write(c,c);var l=u.read(c)===c;return u.remove(c),l}catch{return!1}},_assignPluginFnProp:function(u,c){var l=this[c];this[c]=function(){var v=slice$1(arguments,0),S=this;function T(){if(l)return each$7(arguments,function(M,C){v[C]=M}),l.apply(S,v)}var O=[T].concat(v);return u.apply(S,O)}},_serialize:function(u){return JSON.stringify(u)},_deserialize:function(u,c){if(!u)return c;var l="";try{l=JSON.parse(u)}catch{l=u}return l!==void 0?l:c},_addStorage:function(u){this.enabled||this._testStorage(u)&&(this.storage=u,this.enabled=!0)},_addPlugin:function(u){var c=this;if(isList(u)){each$7(u,function(v){c._addPlugin(v)});return}var l=pluck(this.plugins,function(v){return u===v});if(!l){if(this.plugins.push(u),!isFunction$3(u))throw new Error("Plugins must be function values that return objects");var p=u.call(this);if(!isObject$9(p))throw new Error("Plugins must return an object of function properties");each$7(p,function(v,S){if(!isFunction$3(v))throw new Error("Bad plugin property: "+S+" from plugin "+u.name+". Plugins should only return functions.");c._assignPluginFnProp(v,S)})}},addStorage:function(u){_warn("store.addStorage(storage) is deprecated. Use createStore([storages])"),this._addStorage(u)}},s=create$1(o,storeAPI,{plugins:[]});return s.raw={},each$7(s,function(u,c){isFunction$3(u)&&(s.raw[c]=bind$2(s,u))}),each$7(t,function(u){s._addStorage(u)}),each$7(e,function(u){s._addPlugin(u)}),s}var util$5=util$7,Global$4=util$5.Global,localStorage_1={name:"localStorage",read:read$6,write:write$6,each:each$6,remove:remove$5,clearAll:clearAll$5};function localStorage(){return Global$4.localStorage}function read$6(t){return localStorage().getItem(t)}function write$6(t,e){return localStorage().setItem(t,e)}function each$6(t){for(var e=localStorage().length-1;e>=0;e--){var a=localStorage().key(e);t(read$6(a),a)}}function remove$5(t){return localStorage().removeItem(t)}function clearAll$5(){return localStorage().clear()}var util$4=util$7,Global$3=util$4.Global,oldFFGlobalStorage={name:"oldFF-globalStorage",read:read$5,write:write$5,each:each$5,remove:remove$4,clearAll:clearAll$4},globalStorage=Global$3.globalStorage;function read$5(t){return globalStorage[t]}function write$5(t,e){globalStorage[t]=e}function each$5(t){for(var e=globalStorage.length-1;e>=0;e--){var a=globalStorage.key(e);t(globalStorage[a],a)}}function remove$4(t){return globalStorage.removeItem(t)}function clearAll$4(){each$5(function(t,e){delete globalStorage[t]})}var util$3=util$7,Global$2=util$3.Global,oldIEUserDataStorage={name:"oldIE-userDataStorage",write:write$4,read:read$4,each:each$4,remove:remove$3,clearAll:clearAll$3},storageName="storejs",doc$1=Global$2.document,_withStorageEl=_makeIEStorageElFunction(),disable=(Global$2.navigator?Global$2.navigator.userAgent:"").match(/ (MSIE 8|MSIE 9|MSIE 10)\./);function write$4(t,e){if(!disable){var a=fixKey(t);_withStorageEl(function(r){r.setAttribute(a,e),r.save(storageName)})}}function read$4(t){if(!disable){var e=fixKey(t),a=null;return _withStorageEl(function(r){a=r.getAttribute(e)}),a}}function each$4(t){_withStorageEl(function(e){for(var a=e.XMLDocument.documentElement.attributes,r=a.length-1;r>=0;r--){var n=a[r];t(e.getAttribute(n.name),n.name)}})}function remove$3(t){var e=fixKey(t);_withStorageEl(function(a){a.removeAttribute(e),a.save(storageName)})}function clearAll$3(){_withStorageEl(function(t){var e=t.XMLDocument.documentElement.attributes;t.load(storageName);for(var a=e.length-1;a>=0;a--)t.removeAttribute(e[a].name);t.save(storageName)})}var forbiddenCharsRegex=new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]","g");function fixKey(t){return t.replace(/^\d/,"___$&").replace(forbiddenCharsRegex,"___")}function _makeIEStorageElFunction(){if(!doc$1||!doc$1.documentElement||!doc$1.documentElement.addBehavior)return null;var t="script",e,a,r;try{a=new ActiveXObject("htmlfile"),a.open(),a.write("<"+t+">document.w=window'),a.close(),e=a.w.frames[0].document,r=e.createElement("div")}catch{r=doc$1.createElement("div"),e=doc$1.body}return function(n){var i=[].slice.call(arguments,0);i.unshift(r),e.appendChild(r),r.addBehavior("#default#userData"),r.load(storageName),n.apply(this,i),e.removeChild(r)}}var util$2=util$7,Global$1=util$2.Global,trim=util$2.trim,cookieStorage={name:"cookieStorage",read:read$3,write:write$3,each:each$3,remove:remove$2,clearAll:clearAll$2},doc=Global$1.document;function read$3(t){if(!t||!_has(t))return null;var e="(?:^|.*;\\s*)"+escape(t).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";return unescape(doc.cookie.replace(new RegExp(e),"$1"))}function each$3(t){for(var e=doc.cookie.split(/; ?/g),a=e.length-1;a>=0;a--)if(trim(e[a])){var r=e[a].split("="),n=unescape(r[0]),i=unescape(r[1]);t(i,n)}}function write$3(t,e){t&&(doc.cookie=escape(t)+"="+escape(e)+"; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/")}function remove$2(t){!t||!_has(t)||(doc.cookie=escape(t)+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/")}function clearAll$2(){each$3(function(t,e){remove$2(e)})}function _has(t){return new RegExp("(?:^|;\\s*)"+escape(t).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=").test(doc.cookie)}var util$1=util$7,Global=util$1.Global,sessionStorage_1={name:"sessionStorage",read:read$2,write:write$2,each:each$2,remove:remove$1,clearAll:clearAll$1};function sessionStorage(){return Global.sessionStorage}function read$2(t){return sessionStorage().getItem(t)}function write$2(t,e){return sessionStorage().setItem(t,e)}function each$2(t){for(var e=sessionStorage().length-1;e>=0;e--){var a=sessionStorage().key(e);t(read$2(a),a)}}function remove$1(t){return sessionStorage().removeItem(t)}function clearAll$1(){return sessionStorage().clear()}var memoryStorage_1={name:"memoryStorage",read:read$1,write:write$1,each:each$1,remove,clearAll},memoryStorage={};function read$1(t){return memoryStorage[t]}function write$1(t,e){memoryStorage[t]=e}function each$1(t){for(var e in memoryStorage)memoryStorage.hasOwnProperty(e)&&t(memoryStorage[e],e)}function remove(t){delete memoryStorage[t]}function clearAll(t){memoryStorage={}}var all=[localStorage_1,oldFFGlobalStorage,oldIEUserDataStorage,cookieStorage,sessionStorage_1,memoryStorage_1],json2$1={},hasRequiredJson2;function requireJson2(){return hasRequiredJson2||(hasRequiredJson2=1,typeof JSON!="object"&&(JSON={}),function(){var rx_one=/^[\],:{}\s]*$/,rx_two=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,rx_three=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,rx_four=/(?:^|:|,)(?:\s*\[)+/g,rx_escapable=/[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;function f(t){return t<10?"0"+t:t}function this_value(){return this.valueOf()}typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=this_value,Number.prototype.toJSON=this_value,String.prototype.toJSON=this_value);var gap,indent,meta,rep;function quote(t){return rx_escapable.lastIndex=0,rx_escapable.test(t)?'"'+t.replace(rx_escapable,function(e){var a=meta[e];return typeof a=="string"?a:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}function str(t,e){var a,r,n,i,o=gap,s,u=e[t];switch(u&&typeof u=="object"&&typeof u.toJSON=="function"&&(u=u.toJSON(t)),typeof rep=="function"&&(u=rep.call(e,t,u)),typeof u){case"string":return quote(u);case"number":return isFinite(u)?String(u):"null";case"boolean":case"null":return String(u);case"object":if(!u)return"null";if(gap+=indent,s=[],Object.prototype.toString.apply(u)==="[object Array]"){for(i=u.length,a=0;at instanceof AxiosHeaders$1?t.toJSON():t;function mergeConfig(t,e){e=e||{};const a={};function r(l,d,h){return utils$1.isPlainObject(l)&&utils$1.isPlainObject(d)?utils$1.merge.call({caseless:h},l,d):utils$1.isPlainObject(d)?utils$1.merge({},d):utils$1.isArray(d)?d.slice():d}function n(l,d,h){if(utils$1.isUndefined(d)){if(!utils$1.isUndefined(l))return r(void 0,l,h)}else return r(l,d,h)}function i(l,d){if(!utils$1.isUndefined(d))return r(void 0,d)}function o(l,d){if(utils$1.isUndefined(d)){if(!utils$1.isUndefined(l))return r(void 0,l)}else return r(void 0,d)}function s(l,d,h){if(h in e)return r(l,d);if(h in t)return r(void 0,l)}const u={url:i,method:i,data:i,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(l,d)=>n(headersToObject(l),headersToObject(d),!0)};return utils$1.forEach(Object.keys(Object.assign({},t,e)),function(d){const h=u[d]||n,v=h(t[d],e[d],d);utils$1.isUndefined(v)&&h!==s||(a[d]=v)}),a}const VERSION$1="1.6.2",validators$1={};["object","boolean","number","function","string","symbol"].forEach((t,e)=>{validators$1[t]=function(r){return typeof r===t||"a"+(e<1?"n ":" ")+t}});const deprecatedWarnings={};validators$1.transitional=function(e,a,r){function n(i,o){return"[Axios v"+VERSION$1+"] Transitional option '"+i+"'"+o+(r?". "+r:"")}return(i,o,s)=>{if(e===!1)throw new AxiosError(n(o," has been removed"+(a?" in "+a:"")),AxiosError.ERR_DEPRECATED);return a&&!deprecatedWarnings[o]&&(deprecatedWarnings[o]=!0,console.warn(n(o," has been deprecated since v"+a+" and will be removed in the near future"))),e?e(i,o,s):!0}};function assertOptions(t,e,a){if(typeof t!="object")throw new AxiosError("options must be an object",AxiosError.ERR_BAD_OPTION_VALUE);const r=Object.keys(t);let n=r.length;for(;n-- >0;){const i=r[n],o=e[i];if(o){const s=t[i],u=s===void 0||o(s,i,t);if(u!==!0)throw new AxiosError("option "+i+" must be "+u,AxiosError.ERR_BAD_OPTION_VALUE);continue}if(a!==!0)throw new AxiosError("Unknown option "+i,AxiosError.ERR_BAD_OPTION)}}const validator={assertOptions,validators:validators$1},validators=validator.validators;class Axios{constructor(e){this.defaults=e,this.interceptors={request:new InterceptorManager$1,response:new InterceptorManager$1}}request(e,a){typeof e=="string"?(a=a||{},a.url=e):a=e||{},a=mergeConfig(this.defaults,a);const{transitional:r,paramsSerializer:n,headers:i}=a;r!==void 0&&validator.assertOptions(r,{silentJSONParsing:validators.transitional(validators.boolean),forcedJSONParsing:validators.transitional(validators.boolean),clarifyTimeoutError:validators.transitional(validators.boolean)},!1),n!=null&&(utils$1.isFunction(n)?a.paramsSerializer={serialize:n}:validator.assertOptions(n,{encode:validators.function,serialize:validators.function},!0)),a.method=(a.method||this.defaults.method||"get").toLowerCase();let o=i&&utils$1.merge(i.common,i[a.method]);i&&utils$1.forEach(["delete","get","head","post","put","patch","common"],A=>{delete i[A]}),a.headers=AxiosHeaders$1.concat(o,i);const s=[];let u=!0;this.interceptors.request.forEach(function(O){typeof O.runWhen=="function"&&O.runWhen(a)===!1||(u=u&&O.synchronous,s.unshift(O.fulfilled,O.rejected))});const l=[];this.interceptors.response.forEach(function(O){l.push(O.fulfilled,O.rejected)});let d,h=0,v;if(!u){const A=[dispatchRequest.bind(this),void 0];for(A.unshift.apply(A,s),A.push.apply(A,l),v=A.length,d=Promise.resolve(a);h{if(!r._listeners)return;let i=r._listeners.length;for(;i-- >0;)r._listeners[i](n);r._listeners=null}),this.promise.then=n=>{let i;const o=new Promise(s=>{r.subscribe(s),i=s}).then(n);return o.cancel=function(){r.unsubscribe(i)},o},e(function(i,o,s){r.reason||(r.reason=new CanceledError(i,o,s),a(r.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;const a=this._listeners.indexOf(e);a!==-1&&this._listeners.splice(a,1)}static source(){let e;return{token:new CancelToken(function(n){e=n}),cancel:e}}}const CancelToken$1=CancelToken;function spread(t){return function(a){return t.apply(null,a)}}function isAxiosError(t){return utils$1.isObject(t)&&t.isAxiosError===!0}const HttpStatusCode={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(HttpStatusCode).forEach(([t,e])=>{HttpStatusCode[e]=t});const HttpStatusCode$1=HttpStatusCode;function createInstance(t){const e=new Axios$1(t),a=bind$4(Axios$1.prototype.request,e);return utils$1.extend(a,Axios$1.prototype,e,{allOwnKeys:!0}),utils$1.extend(a,e,null,{allOwnKeys:!0}),a.create=function(n){return createInstance(mergeConfig(t,n))},a}const axios=createInstance(defaults$1);axios.Axios=Axios$1;axios.CanceledError=CanceledError;axios.CancelToken=CancelToken$1;axios.isCancel=isCancel;axios.VERSION=VERSION$1;axios.toFormData=toFormData;axios.AxiosError=AxiosError;axios.Cancel=axios.CanceledError;axios.all=function(e){return Promise.all(e)};axios.spread=spread;axios.isAxiosError=isAxiosError;axios.mergeConfig=mergeConfig;axios.AxiosHeaders=AxiosHeaders$1;axios.formToJSON=t=>formDataToJSON(utils$1.isHTMLForm(t)?new FormData(t):t);axios.getAdapter=adapters.getAdapter;axios.HttpStatusCode=HttpStatusCode$1;axios.default=axios;const axios$1=axios;var commonjsGlobal=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function getDefaultExportFromCjs(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var assign=make_assign(),create$2=make_create(),trim$1=make_trim(),Global$5=typeof window<"u"?window:commonjsGlobal,util$7={assign,create:create$2,trim:trim$1,bind:bind$3,slice:slice$2,each:each$8,map,pluck:pluck$1,isList:isList$1,isFunction:isFunction$4,isObject:isObject$a,Global:Global$5};function make_assign(){return Object.assign?Object.assign:function(e,a,r,n){for(var i=1;i"u"?null:console;if(t){var e=t.warn?t.warn:t.log;e.apply(t,arguments)}}function createStore(t,e,a){a||(a=""),t&&!isList(t)&&(t=[t]),e&&!isList(e)&&(e=[e]);var r=a?"__storejs_"+a+"_":"",n=a?new RegExp("^"+r):null,i=/^[a-zA-Z0-9_\-]*$/;if(!i.test(a))throw new Error("store.js namespaces can only have alphanumerics + underscores and dashes");var o={_namespacePrefix:r,_namespaceRegexp:n,_testStorage:function(u){try{var l="__storejs__test__";u.write(l,l);var d=u.read(l)===l;return u.remove(l),d}catch{return!1}},_assignPluginFnProp:function(u,l){var d=this[l];this[l]=function(){var v=slice$1(arguments,0),S=this;function A(){if(d)return each$7(arguments,function(M,C){v[C]=M}),d.apply(S,v)}var O=[A].concat(v);return u.apply(S,O)}},_serialize:function(u){return JSON.stringify(u)},_deserialize:function(u,l){if(!u)return l;var d="";try{d=JSON.parse(u)}catch{d=u}return d!==void 0?d:l},_addStorage:function(u){this.enabled||this._testStorage(u)&&(this.storage=u,this.enabled=!0)},_addPlugin:function(u){var l=this;if(isList(u)){each$7(u,function(v){l._addPlugin(v)});return}var d=pluck(this.plugins,function(v){return u===v});if(!d){if(this.plugins.push(u),!isFunction$3(u))throw new Error("Plugins must be function values that return objects");var h=u.call(this);if(!isObject$9(h))throw new Error("Plugins must return an object of function properties");each$7(h,function(v,S){if(!isFunction$3(v))throw new Error("Bad plugin property: "+S+" from plugin "+u.name+". Plugins should only return functions.");l._assignPluginFnProp(v,S)})}},addStorage:function(u){_warn("store.addStorage(storage) is deprecated. Use createStore([storages])"),this._addStorage(u)}},s=create$1(o,storeAPI,{plugins:[]});return s.raw={},each$7(s,function(u,l){isFunction$3(u)&&(s.raw[l]=bind$2(s,u))}),each$7(t,function(u){s._addStorage(u)}),each$7(e,function(u){s._addPlugin(u)}),s}var util$5=util$7,Global$4=util$5.Global,localStorage_1={name:"localStorage",read:read$6,write:write$6,each:each$6,remove:remove$5,clearAll:clearAll$5};function localStorage(){return Global$4.localStorage}function read$6(t){return localStorage().getItem(t)}function write$6(t,e){return localStorage().setItem(t,e)}function each$6(t){for(var e=localStorage().length-1;e>=0;e--){var a=localStorage().key(e);t(read$6(a),a)}}function remove$5(t){return localStorage().removeItem(t)}function clearAll$5(){return localStorage().clear()}var util$4=util$7,Global$3=util$4.Global,oldFFGlobalStorage={name:"oldFF-globalStorage",read:read$5,write:write$5,each:each$5,remove:remove$4,clearAll:clearAll$4},globalStorage=Global$3.globalStorage;function read$5(t){return globalStorage[t]}function write$5(t,e){globalStorage[t]=e}function each$5(t){for(var e=globalStorage.length-1;e>=0;e--){var a=globalStorage.key(e);t(globalStorage[a],a)}}function remove$4(t){return globalStorage.removeItem(t)}function clearAll$4(){each$5(function(t,e){delete globalStorage[t]})}var util$3=util$7,Global$2=util$3.Global,oldIEUserDataStorage={name:"oldIE-userDataStorage",write:write$4,read:read$4,each:each$4,remove:remove$3,clearAll:clearAll$3},storageName="storejs",doc$1=Global$2.document,_withStorageEl=_makeIEStorageElFunction(),disable=(Global$2.navigator?Global$2.navigator.userAgent:"").match(/ (MSIE 8|MSIE 9|MSIE 10)\./);function write$4(t,e){if(!disable){var a=fixKey(t);_withStorageEl(function(r){r.setAttribute(a,e),r.save(storageName)})}}function read$4(t){if(!disable){var e=fixKey(t),a=null;return _withStorageEl(function(r){a=r.getAttribute(e)}),a}}function each$4(t){_withStorageEl(function(e){for(var a=e.XMLDocument.documentElement.attributes,r=a.length-1;r>=0;r--){var n=a[r];t(e.getAttribute(n.name),n.name)}})}function remove$3(t){var e=fixKey(t);_withStorageEl(function(a){a.removeAttribute(e),a.save(storageName)})}function clearAll$3(){_withStorageEl(function(t){var e=t.XMLDocument.documentElement.attributes;t.load(storageName);for(var a=e.length-1;a>=0;a--)t.removeAttribute(e[a].name);t.save(storageName)})}var forbiddenCharsRegex=new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]","g");function fixKey(t){return t.replace(/^\d/,"___$&").replace(forbiddenCharsRegex,"___")}function _makeIEStorageElFunction(){if(!doc$1||!doc$1.documentElement||!doc$1.documentElement.addBehavior)return null;var t="script",e,a,r;try{a=new ActiveXObject("htmlfile"),a.open(),a.write("<"+t+">document.w=window'),a.close(),e=a.w.frames[0].document,r=e.createElement("div")}catch{r=doc$1.createElement("div"),e=doc$1.body}return function(n){var i=[].slice.call(arguments,0);i.unshift(r),e.appendChild(r),r.addBehavior("#default#userData"),r.load(storageName),n.apply(this,i),e.removeChild(r)}}var util$2=util$7,Global$1=util$2.Global,trim=util$2.trim,cookieStorage={name:"cookieStorage",read:read$3,write:write$3,each:each$3,remove:remove$2,clearAll:clearAll$2},doc=Global$1.document;function read$3(t){if(!t||!_has(t))return null;var e="(?:^|.*;\\s*)"+escape(t).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";return unescape(doc.cookie.replace(new RegExp(e),"$1"))}function each$3(t){for(var e=doc.cookie.split(/; ?/g),a=e.length-1;a>=0;a--)if(trim(e[a])){var r=e[a].split("="),n=unescape(r[0]),i=unescape(r[1]);t(i,n)}}function write$3(t,e){t&&(doc.cookie=escape(t)+"="+escape(e)+"; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/")}function remove$2(t){!t||!_has(t)||(doc.cookie=escape(t)+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/")}function clearAll$2(){each$3(function(t,e){remove$2(e)})}function _has(t){return new RegExp("(?:^|;\\s*)"+escape(t).replace(/[\-\.\+\*]/g,"\\$&")+"\\s*\\=").test(doc.cookie)}var util$1=util$7,Global=util$1.Global,sessionStorage_1={name:"sessionStorage",read:read$2,write:write$2,each:each$2,remove:remove$1,clearAll:clearAll$1};function sessionStorage(){return Global.sessionStorage}function read$2(t){return sessionStorage().getItem(t)}function write$2(t,e){return sessionStorage().setItem(t,e)}function each$2(t){for(var e=sessionStorage().length-1;e>=0;e--){var a=sessionStorage().key(e);t(read$2(a),a)}}function remove$1(t){return sessionStorage().removeItem(t)}function clearAll$1(){return sessionStorage().clear()}var memoryStorage_1={name:"memoryStorage",read:read$1,write:write$1,each:each$1,remove,clearAll},memoryStorage={};function read$1(t){return memoryStorage[t]}function write$1(t,e){memoryStorage[t]=e}function each$1(t){for(var e in memoryStorage)memoryStorage.hasOwnProperty(e)&&t(memoryStorage[e],e)}function remove(t){delete memoryStorage[t]}function clearAll(t){memoryStorage={}}var all=[localStorage_1,oldFFGlobalStorage,oldIEUserDataStorage,cookieStorage,sessionStorage_1,memoryStorage_1],json2$1={},hasRequiredJson2;function requireJson2(){return hasRequiredJson2||(hasRequiredJson2=1,typeof JSON!="object"&&(JSON={}),function(){var rx_one=/^[\],:{}\s]*$/,rx_two=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,rx_three=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,rx_four=/(?:^|:|,)(?:\s*\[)+/g,rx_escapable=/[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;function f(t){return t<10?"0"+t:t}function this_value(){return this.valueOf()}typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},Boolean.prototype.toJSON=this_value,Number.prototype.toJSON=this_value,String.prototype.toJSON=this_value);var gap,indent,meta,rep;function quote(t){return rx_escapable.lastIndex=0,rx_escapable.test(t)?'"'+t.replace(rx_escapable,function(e){var a=meta[e];return typeof a=="string"?a:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}function str(t,e){var a,r,n,i,o=gap,s,u=e[t];switch(u&&typeof u=="object"&&typeof u.toJSON=="function"&&(u=u.toJSON(t)),typeof rep=="function"&&(u=rep.call(e,t,u)),typeof u){case"string":return quote(u);case"number":return isFinite(u)?String(u):"null";case"boolean":case"null":return String(u);case"object":if(!u)return"null";if(gap+=indent,s=[],Object.prototype.toString.apply(u)==="[object Array]"){for(i=u.length,a=0;alastFlushedIndex&&queue.splice(e,1)}function queueFlush(){!flushing&&!flushPending&&(flushPending=!0,queueMicrotask(flushJobs))}function flushJobs(){flushPending=!1,flushing=!0;for(let t=0;tt.effect(e,{scheduler:a=>{shouldSchedule?scheduler(a):a()}}),raw=t.raw}function overrideEffect(t){effect$3=t}function elementBoundEffect(t){let e=()=>{};return[r=>{let n=effect$3(r);return t._x_effects||(t._x_effects=new Set,t._x_runEffects=()=>{t._x_effects.forEach(i=>i())}),t._x_effects.add(n),e=()=>{n!==void 0&&(t._x_effects.delete(n),release(n))},n},()=>{e()}]}function dispatch(t,e,a={}){t.dispatchEvent(new CustomEvent(e,{detail:a,bubbles:!0,composed:!0,cancelable:!0}))}function walk(t,e){if(typeof ShadowRoot=="function"&&t instanceof ShadowRoot){Array.from(t.children).forEach(n=>walk(n,e));return}let a=!1;if(e(t,()=>a=!0),a)return;let r=t.firstElementChild;for(;r;)walk(r,e),r=r.nextElementSibling}function warn(t,...e){console.warn(`Alpine Warning: ${t}`,...e)}var started=!1;function start$1(){started&&warn("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),started=!0,document.body||warn("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's `