diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index 40ac982c67..a7ad9afcb3 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -151,16 +151,16 @@ }, { "name": "composer/semver", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { @@ -212,7 +212,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { @@ -222,13 +222,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { "name": "composer/xdebug-handler", @@ -406,16 +402,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.86.0", + "version": "v3.87.2", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "4a952bd19dc97879b0620f495552ef09b55f7d36" + "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4a952bd19dc97879b0620f495552ef09b55f7d36", - "reference": "4a952bd19dc97879b0620f495552ef09b55f7d36", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", + "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", "shasum": "" }, "require": { @@ -426,39 +422,38 @@ "ext-hash": "*", "ext-json": "*", "ext-tokenizer": "*", - "fidry/cpu-core-counter": "^1.2", + "fidry/cpu-core-counter": "^1.3", "php": "^7.4 || ^8.0", "react/child-process": "^0.6.6", "react/event-loop": "^1.5", - "react/promise": "^3.2", + "react/promise": "^3.3", "react/socket": "^1.16", "react/stream": "^1.4", "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", - "symfony/console": "^5.4.47 || ^6.4.13 || ^7.0", - "symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0", - "symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0", - "symfony/polyfill-mbstring": "^1.32", - "symfony/polyfill-php80": "^1.32", - "symfony/polyfill-php81": "^1.32", - "symfony/process": "^5.4.47 || ^6.4.20 || ^7.2", - "symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0" + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/polyfill-mbstring": "^1.33", + "symfony/polyfill-php80": "^1.33", + "symfony/polyfill-php81": "^1.33", + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.6", + "facile-it/paraunit": "^1.3.1 || ^2.7", "infection/infection": "^0.29.14", - "justinrainbow/json-schema": "^5.3 || ^6.4", + "justinrainbow/json-schema": "^6.5", "keradus/cli-executor": "^2.2", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.8", - "php-cs-fixer/accessible-object": "^1.1", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25", - "symfony/polyfill-php84": "^1.32", - "symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1", - "symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1" + "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", + "symfony/polyfill-php84": "^1.33", + "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", + "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -499,7 +494,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.86.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.87.2" }, "funding": [ { @@ -507,7 +502,7 @@ "type": "github" } ], - "time": "2025-08-13T22:36:21+00:00" + "time": "2025-09-10T09:51:40+00:00" }, { "name": "psr/container", @@ -959,23 +954,23 @@ }, { "name": "react/promise", - "version": "v3.2.0", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { "php": ">=7.1.0" }, "require-dev": { - "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpstan/phpstan": "1.12.28 || 1.4.10", "phpunit/phpunit": "^9.6 || ^7.5" }, "type": "library", @@ -1020,7 +1015,7 @@ ], "support": { "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.2.0" + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, "funding": [ { @@ -1028,7 +1023,7 @@ "type": "open_collective" } ], - "time": "2024-05-24T10:39:05+00:00" + "time": "2025-08-19T18:57:03+00:00" }, { "name": "react/socket", @@ -1257,16 +1252,16 @@ }, { "name": "symfony/console", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", "shasum": "" }, "require": { @@ -1331,7 +1326,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.2" + "source": "https://github.com/symfony/console/tree/v7.3.3" }, "funding": [ { @@ -1351,7 +1346,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1422,16 +1417,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", "shasum": "" }, "require": { @@ -1482,7 +1477,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/v7.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" }, "funding": [ { @@ -1493,12 +1488,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-22T09:11:45+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -1716,16 +1715,16 @@ }, { "name": "symfony/options-resolver", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", "shasum": "" }, "require": { @@ -1763,7 +1762,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" }, "funding": [ { @@ -1783,11 +1782,11 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-05T10:16:07+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -1846,7 +1845,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -1857,6 +1856,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -1866,16 +1869,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -1924,7 +1927,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -1935,16 +1938,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -2005,7 +2012,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -2016,6 +2023,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -2025,7 +2036,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -2086,7 +2097,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -2097,6 +2108,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -2106,7 +2121,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -2166,7 +2181,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -2177,6 +2192,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -2186,7 +2205,7 @@ }, { "name": "symfony/polyfill-php81", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -2242,7 +2261,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" }, "funding": [ { @@ -2253,6 +2272,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -2262,16 +2285,16 @@ }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", "shasum": "" }, "require": { @@ -2303,7 +2326,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v7.3.3" }, "funding": [ { @@ -2314,12 +2337,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-17T09:11:12+00:00" + "time": "2025-08-18T09:42:54+00:00" }, { "name": "symfony/service-contracts", @@ -2468,16 +2495,16 @@ }, { "name": "symfony/string", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", + "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", "shasum": "" }, "require": { @@ -2535,7 +2562,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.2" + "source": "https://github.com/symfony/string/tree/v7.3.3" }, "funding": [ { @@ -2555,7 +2582,7 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-25T06:35:40+00:00" } ], "packages-dev": [], diff --git a/.ci/phpstan.neon b/.ci/phpstan.neon index c07d5db7e7..1b8e889c72 100644 --- a/.ci/phpstan.neon +++ b/.ci/phpstan.neon @@ -1,6 +1,4 @@ parameters: - scanFiles: - - ../_ide_helper.php paths: - ../app - ../database @@ -9,28 +7,24 @@ parameters: - ../bootstrap/app.php universalObjectCratesClasses: - Illuminate\Database\Eloquent\Model - # TODO: slowly remove these parameters and fix the issues found. reportUnmatchedIgnoredErrors: true ignoreErrors: - # TODO: slowly remove these exceptions and fix the issues found. - - '#Dynamic call to static method#' # all the Laravel ORM things depend on this. - - identifier: varTag.nativeType - - identifier: varTag.type + # all errors below I will never fix. + - '#expects view-string\|null, string given#' + - '#expects view-string, string given#' + - "#Parameter \\#[1-2] \\$num[1-2] of function bc[a-z]+ expects numeric-string, [a-z\\-|&]+ given#" + - identifier: missingType.generics # not interesting enough to fix. - identifier: larastan.noEnvCallsOutsideOfConfig path: ../app/Console/Commands/System/CreatesDatabase.php - identifier: missingType.iterableValue # not interesting enough to fix. - - identifier: missingType.generics # not interesting enough to fix. - - "#Parameter \\#[1-2] \\$num[1-2] of function bc[a-z]+ expects numeric-string, [a-z\\-|&]+ given#" - - '#expects view-string, string given#' - - '#expects view-string\|null, string given#' - - # phpstan can't handle this so we ignore them. - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::before#' - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::after#' - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::withTrashed#' - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::accountTypeIn#' - - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\BelongsTo::withTrashed#' + - identifier: varTag.type # needs a custom extension for every repository, not gonna happen. + - '#Dynamic call to static method Illuminate#' + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::before#' # is custom scope + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::after#' # is custom scope + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::withTrashed#' # is to allow soft delete + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::accountTypeIn#' # is a custom scope + - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\BelongsTo::withTrashed#' # is to allow soft delete # The level 8 is the highest level. original was 5 # 7 is more than enough, higher just leaves NULL things. diff --git a/.env.example b/.env.example index 7e9d697581..e6737be1e7 100644 --- a/.env.example +++ b/.env.example @@ -314,8 +314,9 @@ DEMO_USERNAME= DEMO_PASSWORD= # -# Disable or enable the running balance column data -# Please disable this. It's a very experimental feature. +# Disable or enable the running balance column data. +# If you enable this, please also run "php artisan firefly-iii:correct-database" +# This will take some time the first run. # USE_RUNNING_BALANCE=false diff --git a/.github/release-notes/alpha.md b/.github/release-notes/alpha.md index 888a4093fc..9d10797037 100644 --- a/.github/release-notes/alpha.md +++ b/.github/release-notes/alpha.md @@ -16,6 +16,10 @@ Alpha releases are created to test new features and fixes before they are includ The release files are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/). +## Develop with Firefly III + +Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III. + ## Support Firefly III Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration. diff --git a/.github/release-notes/beta.md b/.github/release-notes/beta.md index 5940d83f67..d170cfc620 100644 --- a/.github/release-notes/beta.md +++ b/.github/release-notes/beta.md @@ -16,6 +16,10 @@ Alpha releases are created to test new features and fixes before they are includ The release files are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/). +## Develop with Firefly III + +Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III. + ## Support Firefly III Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration. diff --git a/.github/release-notes/branch.md b/.github/release-notes/branch.md index 22e1c3cc3e..2bb439a1fd 100644 --- a/.github/release-notes/branch.md +++ b/.github/release-notes/branch.md @@ -16,6 +16,10 @@ There is no changelog for this release, as it is not final. However, [changelog. The release files are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/). +## Develop with Firefly III + +Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III. + ## Support Firefly III Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration. diff --git a/.github/release-notes/develop.md b/.github/release-notes/develop.md index 04774b7dc1..899f969a42 100644 --- a/.github/release-notes/develop.md +++ b/.github/release-notes/develop.md @@ -16,6 +16,10 @@ The changelog for this release may not be up-to-date, so it is not included. How The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/). +## Develop with Firefly III + +Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III. + ## Support Firefly III Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration. diff --git a/.github/release-notes/release.md b/.github/release-notes/release.md index c1442d5630..1e1f999b6a 100644 --- a/.github/release-notes/release.md +++ b/.github/release-notes/release.md @@ -11,6 +11,10 @@ Welcome to release %version of Firefly III. It contains the latest fixes, transl The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/). +## Develop with Firefly III + +Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III. + ## Support Firefly III Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration. diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index a9dba92ca5..efd12f655e 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -114,6 +114,7 @@ class AccountController extends Controller 'id' => (string) $account->id, 'name' => $account->name, 'name_with_balance' => $nameWithBalance, + 'active' => $account->active, 'type' => $account->accountType->type, 'currency_id' => (string) $useCurrency->id, 'currency_name' => $useCurrency->name, diff --git a/app/Api/V1/Controllers/Autocomplete/BudgetController.php b/app/Api/V1/Controllers/Autocomplete/BudgetController.php index 3fd273d329..c292adc5b1 100644 --- a/app/Api/V1/Controllers/Autocomplete/BudgetController.php +++ b/app/Api/V1/Controllers/Autocomplete/BudgetController.php @@ -67,8 +67,9 @@ class BudgetController extends Controller $result = $this->repository->searchBudget($data['query'], $this->parameters->get('limit')); $filtered = $result->map( static fn (Budget $item) => [ - 'id' => (string) $item->id, - 'name' => $item->name, + 'id' => (string) $item->id, + 'name' => $item->name, + 'active' => $item->active, ] ); diff --git a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php index f061df1443..202cf398f2 100644 --- a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php +++ b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php @@ -69,6 +69,7 @@ class RecurrenceController extends Controller 'id' => (string) $recurrence->id, 'name' => $recurrence->title, 'description' => $recurrence->description, + 'active' => $recurrence->active, ]; } diff --git a/app/Api/V1/Controllers/Autocomplete/RuleController.php b/app/Api/V1/Controllers/Autocomplete/RuleController.php index acc44effcd..c2de7bb83b 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleController.php @@ -37,7 +37,7 @@ use Illuminate\Http\JsonResponse; class RuleController extends Controller { private RuleRepositoryInterface $repository; - protected array $acceptedRoles = [UserRoleEnum::READ_RULES]; + protected array $acceptedRoles = [UserRoleEnum::READ_RULES]; /** * RuleController constructor. @@ -66,9 +66,10 @@ class RuleController extends Controller /** @var Rule $rule */ foreach ($rules as $rule) { $response[] = [ - 'id' => (string) $rule->id, + 'id' => (string)$rule->id, 'name' => $rule->title, 'description' => $rule->description, + 'active' => $rule->active, ]; } diff --git a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php index d5e42b6733..ff496d1e13 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php @@ -69,6 +69,7 @@ class RuleGroupController extends Controller 'id' => (string) $group->id, 'name' => $group->title, 'description' => $group->description, + 'active' => $group->active, ]; } diff --git a/app/Api/V1/Controllers/Chart/BalanceController.php b/app/Api/V1/Controllers/Chart/BalanceController.php index d589be1661..cd3d967dc6 100644 --- a/app/Api/V1/Controllers/Chart/BalanceController.php +++ b/app/Api/V1/Controllers/Chart/BalanceController.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Chart; @@ -10,9 +31,7 @@ use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Http\Api\AccountBalanceGrouped; use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter; diff --git a/app/Api/V1/Controllers/Chart/BudgetController.php b/app/Api/V1/Controllers/Chart/BudgetController.php index 4102693d36..726f4c78bd 100644 --- a/app/Api/V1/Controllers/Chart/BudgetController.php +++ b/app/Api/V1/Controllers/Chart/BudgetController.php @@ -31,10 +31,10 @@ use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; @@ -55,7 +55,6 @@ class BudgetController extends Controller protected OperationsRepositoryInterface $opsRepository; private BudgetLimitRepositoryInterface $blRepository; private array $currencies = []; - private TransactionCurrency $currency; private BudgetRepositoryInterface $repository; public function __construct() @@ -115,7 +114,7 @@ class BudgetController extends Controller // get all limits: $limits = $this->blRepository->getBudgetLimits($budget, $start, $end); $rows = []; - $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget])); + $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection()->push($budget)); $expenses = $this->processExpenses($budget->id, $spent, $start, $end); $converter = new ExchangeRateConverter(); $currencies = [$this->primaryCurrency->id => $this->primaryCurrency]; @@ -134,9 +133,9 @@ class BudgetController extends Controller $row['pc_left'] = '0'; $row['pc_overspent'] = '0'; - if (null !== $limit) { + if ($limit instanceof BudgetLimit) { $row['budgeted'] = $limit->amount; - $row['left'] = bcsub($row['budgeted'], bcmul($row['spent'], '-1')); + $row['left'] = bcsub((string) $row['budgeted'], bcmul((string) $row['spent'], '-1')); $row['overspent'] = bcmul($row['left'], '-1'); $row['left'] = 1 === bccomp($row['left'], '0') ? $row['left'] : '0'; $row['overspent'] = 1 === bccomp($row['overspent'], '0') ? $row['overspent'] : '0'; @@ -144,7 +143,7 @@ class BudgetController extends Controller // convert data if necessary. if (true === $this->convertToPrimary && $currencyId !== $this->primaryCurrency->id) { - $currencies[$currencyId] ??= TransactionCurrency::find($currencyId); + $currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId); $row['pc_budgeted'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['budgeted']); $row['pc_spent'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['spent']); $row['pc_left'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['left']); @@ -201,18 +200,18 @@ class BudgetController extends Controller return $return; } - /** - * When no budget limits are present, the expenses of the whole period are collected and grouped. - * This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty. - * - * @throws FireflyException - */ - private function noBudgetLimits(Budget $budget, Carbon $start, Carbon $end): array - { - $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget])); - - return $this->processExpenses($budget->id, $spent, $start, $end); - } + // /** + // * When no budget limits are present, the expenses of the whole period are collected and grouped. + // * This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty. + // * + // * @throws FireflyException + // */ + // private function noBudgetLimits(Budget $budget, Carbon $start, Carbon $end): array + // { + // $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection()->push($budget)); + // + // return $this->processExpenses($budget->id, $spent, $start, $end); + // } /** * Shared between the "noBudgetLimits" function and "processLimit". Will take a single set of expenses and return @@ -232,7 +231,7 @@ class BudgetController extends Controller * @var array $block */ foreach ($spent as $currencyId => $block) { - $this->currencies[$currencyId] ??= TransactionCurrency::find($currencyId); + $this->currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId); $return[$currencyId] ??= [ 'currency_id' => (string)$currencyId, 'currency_code' => $block['currency_code'], @@ -251,66 +250,68 @@ class BudgetController extends Controller // var_dump($return); /** @var array $journal */ foreach ($currentBudgetArray['transaction_journals'] as $journal) { - $return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], (string)$journal['amount']); + /** @var numeric-string $amount */ + $amount = (string)$journal['amount']; + $return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $amount); } } return $return; } - /** - * Function that processes each budget limit (per budget). - * - * If you have a budget limit in EUR, only transactions in EUR will be considered. - * If you have a budget limit in GBP, only transactions in GBP will be considered. - * - * If you have a budget limit in EUR, and a transaction in GBP, it will not be considered for the EUR budget limit. - * - * @throws FireflyException - */ - private function budgetLimits(Budget $budget, Collection $limits): array - { - Log::debug(sprintf('Now in budgetLimits(#%d)', $budget->id)); - $data = []; + // /** + // * Function that processes each budget limit (per budget). + // * + // * If you have a budget limit in EUR, only transactions in EUR will be considered. + // * If you have a budget limit in GBP, only transactions in GBP will be considered. + // * + // * If you have a budget limit in EUR, and a transaction in GBP, it will not be considered for the EUR budget limit. + // * + // * @throws FireflyException + // */ + // private function budgetLimits(Budget $budget, Collection $limits): array + // { + // Log::debug(sprintf('Now in budgetLimits(#%d)', $budget->id)); + // $data = []; + // + // /** @var BudgetLimit $limit */ + // foreach ($limits as $limit) { + // $data = array_merge($data, $this->processLimit($budget, $limit)); + // } + // + // return $data; + // } - /** @var BudgetLimit $limit */ - foreach ($limits as $limit) { - $data = array_merge($data, $this->processLimit($budget, $limit)); - } - - return $data; - } - - /** - * @throws FireflyException - */ - private function processLimit(Budget $budget, BudgetLimit $limit): array - { - Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); - $end = clone $limit->end_date; - $end->endOfDay(); - $spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection([$budget])); - $limitCurrencyId = $limit->transaction_currency_id; - - /** @var array $entry */ - // only spent the entry where the entry's currency matches the budget limit's currency - // so $filtered will only have 1 or 0 entries - $filtered = array_filter($spent, fn ($entry) => $entry['currency_id'] === $limitCurrencyId); - $result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end); - if (1 === count($result)) { - $compare = bccomp($limit->amount, (string)app('steam')->positive($result[$limitCurrencyId]['spent'])); - $result[$limitCurrencyId]['budgeted'] = $limit->amount; - if (1 === $compare) { - // convert this amount into the primary currency: - $result[$limitCurrencyId]['left'] = bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent']); - } - if ($compare <= 0) { - $result[$limitCurrencyId]['overspent'] = app('steam')->positive(bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent'])); - } - } - - return $result; - } + // /** + // * @throws FireflyException + // */ + // private function processLimit(Budget $budget, BudgetLimit $limit): array + // { + // Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); + // $end = clone $limit->end_date; + // $end->endOfDay(); + // $spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection()->push($budget)); + // $limitCurrencyId = $limit->transaction_currency_id; + // + // /** @var array $entry */ + // // only spent the entry where the entry's currency matches the budget limit's currency + // // so $filtered will only have 1 or 0 entries + // $filtered = array_filter($spent, fn ($entry) => $entry['currency_id'] === $limitCurrencyId); + // $result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end); + // if (1 === count($result)) { + // $compare = bccomp($limit->amount, (string)app('steam')->positive($result[$limitCurrencyId]['spent'])); + // $result[$limitCurrencyId]['budgeted'] = $limit->amount; + // if (1 === $compare) { + // // convert this amount into the primary currency: + // $result[$limitCurrencyId]['left'] = bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent']); + // } + // if ($compare <= 0) { + // $result[$limitCurrencyId]['overspent'] = app('steam')->positive(bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent'])); + // } + // } + // + // return $result; + // } private function filterLimit(int $currencyId, Collection $limits): ?BudgetLimit { diff --git a/app/Api/V1/Controllers/Chart/CategoryController.php b/app/Api/V1/Controllers/Chart/CategoryController.php index 2ed6299222..9626c447a5 100644 --- a/app/Api/V1/Controllers/Chart/CategoryController.php +++ b/app/Api/V1/Controllers/Chart/CategoryController.php @@ -97,22 +97,23 @@ class CategoryController extends Controller $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withAccountInformation(); $collector->setXorAccounts($accounts)->withCategoryInformation(); - $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::RECONCILIATION->value]); + $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value]); $journals = $collector->getExtractedJournals(); /** @var array $journal */ foreach ($journals as $journal) { // find journal: - $journalCurrencyId = (int)$journal['currency_id']; - $currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId); - $currencies[$journalCurrencyId] = $currency; - $currencyId = (int)$currency->id; - $currencyName = (string)$currency->name; - $currencyCode = (string)$currency->code; - $currencySymbol = (string)$currency->symbol; - $currencyDecimalPlaces = (int)$currency->decimal_places; - $amount = Steam::positive($journal['amount']); - $pcAmount = null; + $journalCurrencyId = (int)$journal['currency_id']; + $type = $journal['transaction_type_type']; + $currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId); + $currencies[$journalCurrencyId] = $currency; + $currencyId = (int)$currency->id; + $currencyName = (string)$currency->name; + $currencyCode = (string)$currency->code; + $currencySymbol = (string)$currency->symbol; + $currencyDecimalPlaces = (int)$currency->decimal_places; + $amount = Steam::positive((string)$journal['amount']); + $pcAmount = null; // overrule if necessary: if ($this->convertToPrimary && $journalCurrencyId === $this->primaryCurrency->id) { @@ -129,8 +130,8 @@ class CategoryController extends Controller } - $categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category'); - $key = sprintf('%s-%s', $categoryName, $currencyCode); + $categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category'); + $key = sprintf('%s-%s', $categoryName, $currencyCode); // create arrays $return[$key] ??= [ 'label' => $categoryName, @@ -150,23 +151,37 @@ class CategoryController extends Controller 'yAxisID' => 0, 'type' => 'bar', 'entries' => [ - 'spent' => '0', + 'spent' => '0', + 'earned' => '0', ], 'pc_entries' => [ - 'spent' => '0', + 'spent' => '0', + 'earned' => '0', ], ]; // add monies - $return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], (string)$amount); - if (null !== $pcAmount) { - $return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], (string)$pcAmount); + // expenses to spent + if (TransactionTypeEnum::WITHDRAWAL->value === $type) { + $return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], $amount); + if (null !== $pcAmount) { + $return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], $pcAmount); + } + + continue; + } + // positive amount = earned + if (TransactionTypeEnum::DEPOSIT->value === $type) { + $return[$key]['entries']['earned'] = bcadd($return[$key]['entries']['earned'], $amount); + if (null !== $pcAmount) { + $return[$key]['pc_entries']['earned'] = bcadd($return[$key]['pc_entries']['earned'], $pcAmount); + } } } $return = array_values($return); // order by amount - usort($return, static fn (array $a, array $b) => (float)$a['entries']['spent'] < (float)$b['entries']['spent'] ? 1 : -1); + usort($return, static fn (array $a, array $b) => ((float)$a['entries']['spent'] + (float)$a['entries']['earned']) < ((float)$b['entries']['spent'] + (float)$b['entries']['earned']) ? 1 : -1); return response()->json($this->clean($return)); } diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index 196cca9290..298bc20cc7 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -27,7 +27,6 @@ namespace FireflyIII\Api\V1\Controllers; use Carbon\Carbon; use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Exceptions\BadHttpHeaderException; -use FireflyIII\Models\Preference; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Steam; @@ -66,8 +65,6 @@ abstract class Controller extends BaseController protected const string JSON_CONTENT_TYPE = 'application/json'; protected array $accepts = ['application/json', 'application/vnd.api+json']; - /** @var array */ - protected array $allowedSort; protected bool $convertToPrimary = false; protected TransactionCurrency $primaryCurrency; protected ParameterBag $parameters; @@ -78,7 +75,6 @@ abstract class Controller extends BaseController public function __construct() { // get global parameters - $this->allowedSort = config('firefly.allowed_sort_parameters'); $this->middleware( function ($request, $next) { $this->parameters = $this->getParameters(); @@ -108,12 +104,7 @@ abstract class Controller extends BaseController { $bag = new ParameterBag(); $page = (int)request()->get('page'); - if ($page < 1) { - $page = 1; - } - if ($page > 2 ** 16) { - $page = 2 ** 16; - } + $page = min(max(1, $page), 2 ** 16); $bag->set('page', $page); // some date fields: @@ -131,19 +122,15 @@ abstract class Controller extends BaseController $obj = null; if (null !== $date) { try { - $obj = Carbon::parse((string)$date); + $obj = Carbon::parse((string)$date, config('app.timezone')); } catch (InvalidFormatException $e) { // don't care - Log::warning( - sprintf( - 'Ignored invalid date "%s" in API controller parameter check: %s', - substr((string)$date, 0, 20), - $e->getMessage() - ) - ); + Log::warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', substr((string)$date, 0, 20), $e->getMessage())); } } - $bag->set($field, $obj); + if ($obj instanceof Carbon) { + $bag->set($field, $obj); + } } // integer fields: @@ -159,13 +146,7 @@ abstract class Controller extends BaseController } if (null !== $value) { $value = (int)$value; - if ($value < 1) { - $value = 1; - } - if ($value > 2 ** 16) { - $value = 2 ** 16; - } - + $value = min(max(1, $value), 2 ** 16); $bag->set($integer, $value); } if (null === $value @@ -175,46 +156,14 @@ abstract class Controller extends BaseController /** @var User $user */ $user = auth()->user(); - /** @var Preference $pageSize */ $pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data; $bag->set($integer, $pageSize); } } // sort fields: - return $this->getSortParameters($bag); - } - - private function getSortParameters(ParameterBag $bag): ParameterBag - { - $sortParameters = []; - - 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()); - $param = ''; - } - if ('' === $param) { - return $bag; - } - $parts = explode(',', $param); - foreach ($parts as $part) { - $part = trim($part); - $direction = 'asc'; - if ('-' === $part[0]) { - $part = substr($part, 1); - $direction = 'desc'; - } - if (in_array($part, $this->allowedSort, true)) { - $sortParameters[] = [$part, $direction]; - } - } - $bag->set('sort', $sortParameters); - return $bag; + // return $this->getSortParameters($bag); } /** @@ -283,8 +232,6 @@ abstract class Controller extends BaseController $baseUrl = sprintf('%s/api/v1', request()->getSchemeAndHttpHost()); $manager->setSerializer(new JsonApiSerializer($baseUrl)); - // $transformer->collectMetaData(new Collection([$object])); - $resource = new Item($object, $transformer, $key); return $manager->createData($resource)->toArray(); diff --git a/app/Api/V1/Controllers/Insight/Expense/BudgetController.php b/app/Api/V1/Controllers/Insight/Expense/BudgetController.php index ed91b96a16..84686fb367 100644 --- a/app/Api/V1/Controllers/Insight/Expense/BudgetController.php +++ b/app/Api/V1/Controllers/Insight/Expense/BudgetController.php @@ -76,7 +76,7 @@ class BudgetController extends Controller /** @var Budget $budget */ foreach ($budgets as $budget) { - $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection([$budget])); + $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection()->push($budget)); /** @var array $expense */ foreach ($expenses as $expense) { diff --git a/app/Api/V1/Controllers/Insight/Expense/CategoryController.php b/app/Api/V1/Controllers/Insight/Expense/CategoryController.php index d5d092b461..c470cef7ae 100644 --- a/app/Api/V1/Controllers/Insight/Expense/CategoryController.php +++ b/app/Api/V1/Controllers/Insight/Expense/CategoryController.php @@ -76,7 +76,7 @@ class CategoryController extends Controller /** @var Category $category */ foreach ($categories as $category) { - $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection([$category])); + $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection()->push($category)); /** @var array $expense */ foreach ($expenses as $expense) { diff --git a/app/Api/V1/Controllers/Insight/Income/CategoryController.php b/app/Api/V1/Controllers/Insight/Income/CategoryController.php index 1547090337..e2fb1d527d 100644 --- a/app/Api/V1/Controllers/Insight/Income/CategoryController.php +++ b/app/Api/V1/Controllers/Insight/Income/CategoryController.php @@ -76,7 +76,7 @@ class CategoryController extends Controller /** @var Category $category */ foreach ($categories as $category) { - $expenses = $this->opsRepository->sumIncome($start, $end, $assetAccounts, new Collection([$category])); + $expenses = $this->opsRepository->sumIncome($start, $end, $assetAccounts, new Collection()->push($category)); /** @var array $expense */ foreach ($expenses as $expense) { diff --git a/app/Api/V1/Controllers/Insight/Income/PeriodController.php b/app/Api/V1/Controllers/Insight/Income/PeriodController.php index c66a19eab8..f413788066 100644 --- a/app/Api/V1/Controllers/Insight/Income/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Income/PeriodController.php @@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\Insight\GenericRequest; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use Illuminate\Http\JsonResponse; /** @@ -71,7 +72,7 @@ class PeriodController extends Controller 'currency_id' => (string) $currencyId, 'currency_code' => $currencyCode, ]; - $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], (string) app('steam')->positive($journal[$field])); + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], Steam::positive($journal[$field])); $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // float but on purpose. } diff --git a/app/Api/V1/Controllers/Insight/Income/TagController.php b/app/Api/V1/Controllers/Insight/Income/TagController.php index fe45270654..49d6721b02 100644 --- a/app/Api/V1/Controllers/Insight/Income/TagController.php +++ b/app/Api/V1/Controllers/Insight/Income/TagController.php @@ -30,6 +30,7 @@ use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use Illuminate\Http\JsonResponse; /** @@ -97,7 +98,7 @@ class TagController extends Controller 'currency_id' => (string) $currencyId, 'currency_code' => $currencyCode, ]; - $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], (string) app('steam')->positive($journal[$field])); + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], Steam::positive($journal[$field])); $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; } @@ -148,7 +149,7 @@ class TagController extends Controller 'currency_id' => (string) $currencyId, 'currency_code' => $journal['currency_code'], ]; - $response[$key]['difference'] = bcadd((string) $response[$key]['difference'], (string) app('steam')->positive($journal['amount'])); + $response[$key]['difference'] = bcadd((string) $response[$key]['difference'], Steam::positive($journal['amount'])); $response[$key]['difference_float'] = (float) $response[$key]['difference']; } @@ -160,10 +161,7 @@ class TagController extends Controller 'currency_id' => (string) $foreignCurrencyId, 'currency_code' => $journal['foreign_currency_code'], ]; - $response[$foreignKey]['difference'] = bcadd( - (string) $response[$foreignKey]['difference'], - (string) app('steam')->positive($journal['foreign_amount']) - ); + $response[$foreignKey]['difference'] = bcadd((string) $response[$foreignKey]['difference'], Steam::positive($journal['foreign_amount'])); $response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; } } diff --git a/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php index 63cabef200..4df25a96f8 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php @@ -76,7 +76,7 @@ class CategoryController extends Controller /** @var Category $category */ foreach ($categories as $category) { - $expenses = $this->opsRepository->sumTransfers($start, $end, $assetAccounts, new Collection([$category])); + $expenses = $this->opsRepository->sumTransfers($start, $end, $assetAccounts, new Collection()->push($category)); /** @var array $expense */ foreach ($expenses as $expense) { diff --git a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php index e2dc396d19..bc716225bb 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php @@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\Insight\GenericRequest; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use Illuminate\Http\JsonResponse; /** @@ -71,7 +72,7 @@ class PeriodController extends Controller 'currency_id' => (string) $currencyId, 'currency_code' => $currencyCode, ]; - $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], (string) app('steam')->positive($journal[$field])); + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], Steam::positive($journal[$field])); $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; } diff --git a/app/Api/V1/Controllers/Insight/Transfer/TagController.php b/app/Api/V1/Controllers/Insight/Transfer/TagController.php index a03d623cfd..0fe8b86e17 100644 --- a/app/Api/V1/Controllers/Insight/Transfer/TagController.php +++ b/app/Api/V1/Controllers/Insight/Transfer/TagController.php @@ -30,6 +30,7 @@ use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use Illuminate\Http\JsonResponse; /** @@ -95,7 +96,7 @@ class TagController extends Controller 'currency_id' => (string) $currencyId, 'currency_code' => $currencyCode, ]; - $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], (string) app('steam')->positive($journal[$field])); + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], Steam::positive($journal[$field])); $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; } @@ -146,7 +147,7 @@ class TagController extends Controller 'currency_id' => (string) $currencyId, 'currency_code' => $journal['currency_code'], ]; - $response[$key]['difference'] = bcadd((string) $response[$key]['difference'], (string) app('steam')->positive($journal['amount'])); + $response[$key]['difference'] = bcadd((string) $response[$key]['difference'], Steam::positive($journal['amount'])); $response[$key]['difference_float'] = (float) $response[$key]['difference']; } @@ -160,7 +161,7 @@ class TagController extends Controller ]; $response[$foreignKey]['difference'] = bcadd( (string) $response[$foreignKey]['difference'], - (string) app('steam')->positive($journal['foreign_amount']) + Steam::positive($journal['foreign_amount']) ); $response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; // intentional float } diff --git a/app/Api/V1/Controllers/Models/Account/ListController.php b/app/Api/V1/Controllers/Models/Account/ListController.php index 15f8ac7c04..eacc1824f1 100644 --- a/app/Api/V1/Controllers/Models/Account/ListController.php +++ b/app/Api/V1/Controllers/Models/Account/ListController.php @@ -152,7 +152,7 @@ class ListController extends Controller // use new group collector: /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setUser($admin)->setAccounts(new Collection([$account])) + $collector->setUser($admin)->setAccounts(new Collection()->push($account)) ->withAPIInformation()->setLimit($pageSize)->setPage($this->parameters->get('page'))->setTypes($types) ; diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php index 5cdb73d10d..d2d02ac82a 100644 --- a/app/Api/V1/Controllers/Models/Account/ShowController.php +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Models\Account; use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Account\ShowRequest; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -33,7 +34,6 @@ use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment; use FireflyIII\Transformers\AccountTransformer; 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; @@ -71,34 +71,38 @@ class ShowController extends Controller * * @throws FireflyException */ - public function index(Request $request): JsonResponse + public function index(ShowRequest $request): JsonResponse { $manager = $this->getManager(); - $type = $request->get('type') ?? 'all'; - $this->parameters->set('type', $type); + $params = $request->getParameters(); + $this->parameters->set('type', $params['type']); // types to get, page size: - $types = $this->mapAccountTypes($this->parameters->get('type')); - $pageSize = $this->parameters->get('limit'); + $types = $this->mapAccountTypes($params['type']); // get list of accounts. Count it and split it. $this->repository->resetAccountOrder(); - $collection = $this->repository->getAccountsByType($types, $this->parameters->get('sort') ?? []); + $collection = $this->repository->getAccountsByType($types, $params['sort']); $count = $collection->count(); // continue sort: - $accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + // TODO if the user sorts on DB dependent field there must be no slice before enrichment, only after. + // TODO still need to figure out how to do this easily. + $accounts = $collection->slice(($this->parameters->get('page') - 1) * $params['limit'], $params['limit']); // enrich /** @var User $admin */ $admin = auth()->user(); $enrichment = new AccountEnrichment(); + $enrichment->setSort($params['sort']); $enrichment->setDate($this->parameters->get('date')); + $enrichment->setStart($this->parameters->get('start')); + $enrichment->setEnd($this->parameters->get('end')); $enrichment->setUser($admin); $accounts = $enrichment->enrich($accounts); // make paginator: - $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); + $paginator = new LengthAwarePaginator($accounts, $count, $params['limit'], $this->parameters->get('page')); $paginator->setPath(route('api.v1.accounts.index').$this->buildParams()); /** @var AccountTransformer $transformer */ @@ -117,7 +121,7 @@ class ShowController extends Controller * * Show single instance. */ - public function show(Account $account): JsonResponse + public function show(ShowRequest $request, Account $account): JsonResponse { // get list of accounts. Count it and split it. $this->repository->resetAccountOrder(); @@ -129,6 +133,8 @@ class ShowController extends Controller $admin = auth()->user(); $enrichment = new AccountEnrichment(); $enrichment->setDate($this->parameters->get('date')); + $enrichment->setStart($this->parameters->get('start')); + $enrichment->setEnd($this->parameters->get('end')); $enrichment->setUser($admin); $account = $enrichment->enrichSingle($account); diff --git a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php index 94f7dfdbc7..1fa1ed692d 100644 --- a/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php +++ b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php @@ -114,8 +114,8 @@ class ShowController extends Controller public function show(AvailableBudget $availableBudget): JsonResponse { $manager = $this->getManager(); - $start = $this->parameters->get('start'); - $end = $this->parameters->get('end'); + // $start = $this->parameters->get('start'); + // $end = $this->parameters->get('end'); /** @var AvailableBudgetTransformer $transformer */ $transformer = app(AvailableBudgetTransformer::class); @@ -126,8 +126,8 @@ class ShowController extends Controller $admin = auth()->user(); $enrichment = new AvailableBudgetEnrichment(); $enrichment->setUser($admin); - $enrichment->setStart($start); - $enrichment->setEnd($end); + // $enrichment->setStart($start); + // $enrichment->setEnd($end); $availableBudget = $enrichment->enrichSingle($availableBudget); diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php index 0253b3aac9..038c24a46c 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php @@ -84,6 +84,8 @@ class ShowController extends Controller $enrichment->setUser($admin); $enrichment->setStart($this->parameters->get('start')); $enrichment->setEnd($this->parameters->get('end')); + + /** @var Budget $budget */ $budget = $enrichment->enrichSingle($budget); diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php index 159c3c8b49..cd17067b5c 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php @@ -86,7 +86,7 @@ class UpdateController extends Controller $admin = auth()->user(); $enrichment = new BudgetLimitEnrichment(); $enrichment->setUser($admin); - $budgetLimit = $enrichment->enrich($budgetLimit); + $budgetLimit = $enrichment->enrichSingle($budgetLimit); /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); diff --git a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/StoreController.php b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/StoreController.php index 0e0f0fa8a1..45da4ff67e 100644 --- a/app/Api/V1/Controllers/Models/CurrencyExchangeRate/StoreController.php +++ b/app/Api/V1/Controllers/Models/CurrencyExchangeRate/StoreController.php @@ -33,6 +33,7 @@ use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Transformers\ExchangeRateTransformer; use Illuminate\Http\JsonResponse; @@ -69,7 +70,7 @@ class StoreController extends Controller foreach ($data as $date => $rate) { $date = Carbon::createFromFormat('Y-m-d', $date); $existing = $this->repository->getSpecificRateOnDate($from, $to, $date); - if (null !== $existing) { + if ($existing instanceof CurrencyExchangeRate) { // update existing rate. $existing = $this->repository->updateExchangeRate($existing, $rate); $collection->push($existing); @@ -98,12 +99,9 @@ class StoreController extends Controller $from = $request->getFromCurrency(); $collection = new Collection(); foreach ($data['rates'] as $key => $rate) { - $to = TransactionCurrency::where('code', $key)->first(); - if (null === $to) { - continue; // should not happen. - } + $to = Amount::getTransactionCurrencyByCode($key); $existing = $this->repository->getSpecificRateOnDate($from, $to, $date); - if (null !== $existing) { + if ($existing instanceof CurrencyExchangeRate) { // update existing rate. $existing = $this->repository->updateExchangeRate($existing, $rate); $collection->push($existing); diff --git a/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php new file mode 100644 index 0000000000..b00aa9767b --- /dev/null +++ b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php @@ -0,0 +1,126 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Recurrence; + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Generic\SingleDateRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Jobs\CreateRecurringTransactions; +use FireflyIII\Models\Recurrence; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Support\Facades\Preferences; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; +use FireflyIII\Transformers\TransactionGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; + +class TriggerController extends Controller +{ + private RecurringRepositoryInterface $repository; + + /** + * RecurrenceController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(RecurringRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + public function trigger(SingleDateRequest $request, Recurrence $recurrence): JsonResponse + { + // find recurrence occurrence for this date and trigger it. + // grab the date from the last time the recurrence fired: + $backupDate = $recurrence->latest_date; + $date = $request->getDate(); + + // 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'))); + + /** @var CreateRecurringTransactions $job */ + $job = app(CreateRecurringTransactions::class); + $job->setRecurrences(new Collection()->push($recurrence)); + $job->setDate($date); + $job->setForce(false); + $job->handle(); + Log::debug('Done with recurrence.'); + + $groups = $job->getGroups(); + $this->repository->markGroupsAsNow($groups); + $recurrence->latest_date = $backupDate; + $recurrence->latest_date_tz = $backupDate?->format('e'); + $recurrence->save(); + Preferences::mark(); + + // enrich groups and return them: + + if (0 === $groups->count()) { + $paginator = new LengthAwarePaginator(new Collection(), 0, 1); + } + if ($groups->count() > 0) { + /** @var User $admin */ + $admin = auth()->user(); + + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($admin) + ->setIds($groups->pluck('id')->toArray()) + ->withAPIInformation() + ; + $paginator = $collector->getPaginatedGroups(); + } + + $manager = $this->getManager(); + $paginator->setPath(route('api.v1.recurrences.trigger', [$recurrence->id]).$this->buildParams()); + + // enrich + $admin = auth()->user(); + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); + + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($transactions, $transformer, 'transactions'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} diff --git a/app/Api/V1/Controllers/Models/Rule/TriggerController.php b/app/Api/V1/Controllers/Models/Rule/TriggerController.php index 42465d3c85..2fab36ba92 100644 --- a/app/Api/V1/Controllers/Models/Rule/TriggerController.php +++ b/app/Api/V1/Controllers/Models/Rule/TriggerController.php @@ -75,7 +75,7 @@ class TriggerController extends Controller /** @var RuleEngineInterface $ruleEngine */ $ruleEngine = app(RuleEngineInterface::class); - $ruleEngine->setRules(new Collection([$rule])); + $ruleEngine->setRules(new Collection()->push($rule)); // overrule the rule(s) if necessary. if (array_key_exists('start', $parameters) && null !== $parameters['start']) { @@ -129,7 +129,7 @@ class TriggerController extends Controller /** @var RuleEngineInterface $ruleEngine */ $ruleEngine = app(RuleEngineInterface::class); - $ruleEngine->setRules(new Collection([$rule])); + $ruleEngine->setRules(new Collection()->push($rule)); // overrule the rule(s) if necessary. if (array_key_exists('start', $parameters) && null !== $parameters['start']) { diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index 17fb1c075a..67a6486487 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -30,9 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\Recurrence; -use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\Rule; -use FireflyIII\Models\RuleTrigger; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; @@ -192,7 +190,7 @@ class ListController extends Controller $enrichment->setUser($admin); $enrichment->setStart($this->parameters->get('start')); $enrichment->setEnd($this->parameters->get('end')); - $bills = $enrichment->enrichSingle($bills); + $bills = $enrichment->enrich($bills); // make paginator: $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page')); @@ -268,7 +266,6 @@ class ListController extends Controller // filter selection $collection = $unfiltered->filter( static function (Recurrence $recurrence) use ($currency) { // @phpstan-ignore-line - /** @var RecurrenceTransaction $transaction */ if (array_any($recurrence->recurrenceTransactions, fn ($transaction) => $transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id)) { return $recurrence; } @@ -320,7 +317,6 @@ class ListController extends Controller $collection = $unfiltered->filter( static function (Rule $rule) use ($currency) { // @phpstan-ignore-line - /** @var RuleTrigger $trigger */ if (array_any($rule->ruleTriggers, fn ($trigger) => 'currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value)) { return $rule; } diff --git a/app/Api/V1/Controllers/Summary/BasicController.php b/app/Api/V1/Controllers/Summary/BasicController.php index 096ed01db7..7550d63fb7 100644 --- a/app/Api/V1/Controllers/Summary/BasicController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -481,7 +481,7 @@ class BasicController extends Controller $currencies = []; // first, create an entry for each entry in the "available" array. - /** @var array $availableBudget */ + /** @var string $availableBudget */ foreach ($available as $currencyId => $availableBudget) { $currencies[$currencyId] ??= $this->currencyRepos->find($currencyId); $return[$currencyId] = [ diff --git a/app/Api/V1/Controllers/System/ConfigurationController.php b/app/Api/V1/Controllers/System/ConfigurationController.php index 6331ef0391..5902b8a343 100644 --- a/app/Api/V1/Controllers/System/ConfigurationController.php +++ b/app/Api/V1/Controllers/System/ConfigurationController.php @@ -133,7 +133,6 @@ class ConfigurationController extends Controller */ public function show(string $configKey): JsonResponse { - $data = []; $dynamic = $this->getDynamicConfiguration(); $shortKey = str_replace('configuration.', '', $configKey); if (str_starts_with($configKey, 'configuration.')) { @@ -156,13 +155,11 @@ class ConfigurationController extends Controller } // fallback - if (!str_starts_with($configKey, 'configuration.')) { - $data = [ - 'title' => $configKey, - 'value' => config($configKey), - 'editable' => false, - ]; - } + $data = [ + 'title' => $configKey, + 'value' => config($shortKey), + 'editable' => false, + ]; return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE); } diff --git a/app/Api/V1/Controllers/Webhook/ShowController.php b/app/Api/V1/Controllers/Webhook/ShowController.php index 31906c85ed..c0a27d5fce 100644 --- a/app/Api/V1/Controllers/Webhook/ShowController.php +++ b/app/Api/V1/Controllers/Webhook/ShowController.php @@ -32,7 +32,9 @@ use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\Webhook; use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment; use FireflyIII\Transformers\WebhookTransformer; +use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -90,6 +92,13 @@ class ShowController extends Controller $paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page')); $paginator->setPath(route('api.v1.webhooks.index').$this->buildParams()); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new WebhookEnrichment(); + $enrichment->setUser($admin); + $webhooks = $enrichment->enrich($webhooks); + /** @var WebhookTransformer $transformer */ $transformer = app(WebhookTransformer::class); $transformer->setParameters($this->parameters); @@ -117,6 +126,13 @@ class ShowController extends Controller Log::channel('audit')->info(sprintf('User views webhook #%d.', $webhook->id)); $manager = $this->getManager(); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new WebhookEnrichment(); + $enrichment->setUser($admin); + $webhook = $enrichment->enrichSingle($webhook); + /** @var WebhookTransformer $transformer */ $transformer = app(WebhookTransformer::class); $transformer->setParameters($this->parameters); @@ -149,14 +165,14 @@ class ShowController extends Controller // tell the generator which trigger it should look for $engine->setTrigger(WebhookTrigger::tryFrom($webhook->trigger)); // tell the generator which objects to process - $engine->setObjects(new Collection([$group])); + $engine->setObjects(new Collection()->push($group)); // set the webhook to trigger - $engine->setWebhooks(new Collection([$webhook])); + $engine->setWebhooks(new Collection()->push($webhook)); // tell the generator to generate the messages $engine->generateMessages(); // trigger event to send them: - Log::debug('send event RequestedSendWebhookMessages'); + Log::debug('send event RequestedSendWebhookMessages from ShowController::triggerTransaction()'); event(new RequestedSendWebhookMessages()); return response()->json([], 204); diff --git a/app/Api/V1/Controllers/Webhook/StoreController.php b/app/Api/V1/Controllers/Webhook/StoreController.php index 77ea1de82a..12c7b1bf75 100644 --- a/app/Api/V1/Controllers/Webhook/StoreController.php +++ b/app/Api/V1/Controllers/Webhook/StoreController.php @@ -27,7 +27,9 @@ namespace FireflyIII\Api\V1\Controllers\Webhook; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\Webhook\CreateRequest; use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment; use FireflyIII\Transformers\WebhookTransformer; +use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; use League\Fractal\Resource\Item; @@ -68,6 +70,15 @@ class StoreController extends Controller } $webhook = $this->repository->store($data); + + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new WebhookEnrichment(); + $enrichment->setUser($admin); + $webhook = $enrichment->enrichSingle($webhook); + + $manager = $this->getManager(); Log::channel('audit')->info('User stores new webhook', $data); diff --git a/app/Api/V1/Controllers/Webhook/UpdateController.php b/app/Api/V1/Controllers/Webhook/UpdateController.php index 7e2e0e2353..c8da474ff4 100644 --- a/app/Api/V1/Controllers/Webhook/UpdateController.php +++ b/app/Api/V1/Controllers/Webhook/UpdateController.php @@ -28,7 +28,9 @@ use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\Webhook\UpdateRequest; use FireflyIII\Models\Webhook; use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment; use FireflyIII\Transformers\WebhookTransformer; +use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; use League\Fractal\Resource\Item; @@ -70,6 +72,15 @@ class UpdateController extends Controller $webhook = $this->repository->update($webhook, $data); $manager = $this->getManager(); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new WebhookEnrichment(); + $enrichment->setUser($admin); + + /** @var Webhook $webhook */ + $webhook = $enrichment->enrichSingle($webhook); + Log::channel('audit')->info(sprintf('User updates webhook #%d', $webhook->id), $data); /** @var WebhookTransformer $transformer */ diff --git a/app/Api/V1/Requests/Chart/ChartRequest.php b/app/Api/V1/Requests/Chart/ChartRequest.php index 2ef933a1c2..d298a89116 100644 --- a/app/Api/V1/Requests/Chart/ChartRequest.php +++ b/app/Api/V1/Requests/Chart/ChartRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Chart; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Request\ChecksLogin; @@ -64,6 +64,7 @@ class ChartRequest extends FormRequest 'end' => 'required|date|after:1970-01-02|before:2038-01-17|after_or_equal:start', 'preselected' => sprintf('nullable|in:%s', implode(',', config('firefly.preselected_accounts'))), 'period' => sprintf('nullable|in:%s', implode(',', config('firefly.valid_view_ranges'))), + 'accounts' => 'nullable|array', 'accounts.*' => 'exists:accounts,id', ]; diff --git a/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php b/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php index 8731e354aa..6e8f9b285c 100644 --- a/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/MoveTransactionsRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Data\Bulk; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; diff --git a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php index b02ee21149..46aea15783 100644 --- a/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php +++ b/app/Api/V1/Requests/Data/Bulk/TransactionRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Data\Bulk; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use JsonException; use FireflyIII\Enums\ClauseType; use FireflyIII\Rules\IsValidBulkClause; diff --git a/app/Api/V1/Requests/Models/Account/ShowRequest.php b/app/Api/V1/Requests/Models/Account/ShowRequest.php new file mode 100644 index 0000000000..6d6dcb71c5 --- /dev/null +++ b/app/Api/V1/Requests/Models/Account/ShowRequest.php @@ -0,0 +1,99 @@ +. + */ + +namespace FireflyIII\Api\V1\Requests\Models\Account; + +use Illuminate\Validation\Validator; +use Carbon\Carbon; +use FireflyIII\Models\Account; +use FireflyIII\Rules\IsValidSortInstruction; +use FireflyIII\Support\Facades\Preferences; +use FireflyIII\Support\Http\Api\AccountFilter; +use FireflyIII\Support\Request\ConvertsDataTypes; +use FireflyIII\User; +use Illuminate\Foundation\Http\FormRequest; + +class ShowRequest extends FormRequest +{ + use AccountFilter; + use ConvertsDataTypes; + + public function getParameters(): array + { + $limit = $this->convertInteger('limit'); + if (0 === $limit) { + // get default for user: + /** @var User $user */ + $user = auth()->user(); + $limit = (int)Preferences::getForUser($user, 'listPageSize', 50)->data; + } + + $page = $this->convertInteger('page'); + $page = min(max(1, $page), 2 ** 16); + + return [ + 'type' => $this->convertString('type', 'all'), + 'limit' => $limit, + 'sort' => $this->convertSortParameters('sort', Account::class), + 'page' => $page, + ]; + } + + public function rules(): array + { + $keys = implode(',', array_keys($this->types)); + + return [ + 'date' => 'date', + 'start' => 'date|present_with:end|before_or_equal:end|before:2038-01-17|after:1970-01-02', + 'end' => 'date|present_with:start|after_or_equal:start|before:2038-01-17|after:1970-01-02', + 'sort' => ['nullable', new IsValidSortInstruction(Account::class)], + 'type' => sprintf('in:%s', $keys), + 'limit' => 'numeric|min:1|max:131337', + 'page' => 'numeric|min:1|max:131337', + ]; + } + + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator): void { + if (count($validator->failed()) > 0) { + return; + } + $data = $validator->getData(); + + + if (array_key_exists('date', $data) && array_key_exists('start', $data) && array_key_exists('end', $data)) { + // assume valid dates, before we got here. + $start = Carbon::parse($data['start'], config('app.timezone'))->startOfDay(); + $end = Carbon::parse($data['end'], config('app.timezone'))->endOfDay(); + $date = Carbon::parse($data['date'], config('app.timezone')); + if (!$date->between($start, $end)) { + $validator->errors()->add('date', (string)trans('validation.between_date')); + } + } + } + ); + } +} diff --git a/app/Api/V1/Requests/Models/Account/UpdateRequest.php b/app/Api/V1/Requests/Models/Account/UpdateRequest.php index 5fb31b6fc8..473e5b8714 100644 --- a/app/Api/V1/Requests/Models/Account/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Account/UpdateRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Account; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Account; use FireflyIII\Models\Location; use FireflyIII\Repositories\Account\AccountRepositoryInterface; diff --git a/app/Api/V1/Requests/Models/AvailableBudget/Request.php b/app/Api/V1/Requests/Models/AvailableBudget/Request.php index 93b3f732b0..02cbf088e6 100644 --- a/app/Api/V1/Requests/Models/AvailableBudget/Request.php +++ b/app/Api/V1/Requests/Models/AvailableBudget/Request.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\AvailableBudget; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use Carbon\Carbon; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Api/V1/Requests/Models/Bill/StoreRequest.php b/app/Api/V1/Requests/Models/Bill/StoreRequest.php index 46b0027e7e..4b35c5bb2e 100644 --- a/app/Api/V1/Requests/Models/Bill/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Bill/StoreRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Bill; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use ValueError; use TypeError; use FireflyIII\Rules\IsBoolean; diff --git a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php index 149f5ad4b7..f4dd92f9df 100644 --- a/app/Api/V1/Requests/Models/Bill/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Bill; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Bill; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; diff --git a/app/Api/V1/Requests/Models/Budget/StoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php index 43f44c7720..9a0118406d 100644 --- a/app/Api/V1/Requests/Models/Budget/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Budget; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php index ed1d119e93..6eb0dc7acc 100644 --- a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Budget; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Budget; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; diff --git a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php index 3c0fc08c37..42f7849292 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use Carbon\Carbon; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php index 67706bdc69..be2bfe8584 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByCurrenciesRequest.php @@ -28,7 +28,7 @@ use Carbon\Carbon; use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use Illuminate\Foundation\Http\FormRequest; class StoreByCurrenciesRequest extends FormRequest @@ -55,11 +55,11 @@ class StoreByCurrenciesRequest extends FormRequest { $validator->after( static function (Validator $validator): void { - $data = $validator->getData() ?? []; + $data = $validator->getData(); foreach ($data as $date => $rate) { try { - $date = Carbon::createFromFormat('Y-m-d', $date); - } catch (InvalidFormatException $e) { + Carbon::createFromFormat('Y-m-d', $date); + } catch (InvalidFormatException) { $validator->errors()->add('date', trans('validation.date', ['attribute' => 'date'])); return; diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php index 433e6ac90b..aa1eb2a5b7 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreByDateRequest.php @@ -24,10 +24,12 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate; +use Illuminate\Validation\Validator; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; -use Illuminate\Contracts\Validation\Validator; use Illuminate\Foundation\Http\FormRequest; class StoreByDateRequest extends FormRequest @@ -35,6 +37,9 @@ class StoreByDateRequest extends FormRequest use ChecksLogin; use ConvertsDataTypes; + /** + * @return array + */ public function getAll(): array { return [ @@ -45,11 +50,13 @@ class StoreByDateRequest extends FormRequest public function getFromCurrency(): TransactionCurrency { - return TransactionCurrency::where('code', $this->get('from'))->first(); + return Amount::getTransactionCurrencyByCode((string)$this->get('from')); } /** * The rules that the incoming request must be matched against. + * + * @return array */ public function rules(): array { @@ -79,8 +86,10 @@ class StoreByDateRequest extends FormRequest continue; } - $to = TransactionCurrency::where('code', $key)->first(); - if (null === $to) { + + try { + Amount::getTransactionCurrencyByCode((string)$key); + } catch (FireflyException) { $validator->errors()->add(sprintf('rates.%s', $key), trans('validation.invalid_currency_code', ['code' => $key])); } } diff --git a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreRequest.php b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreRequest.php index b4ef5d13b8..6038a65f02 100644 --- a/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreRequest.php +++ b/app/Api/V1/Requests/Models/CurrencyExchangeRate/StoreRequest.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate; use Carbon\Carbon; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; @@ -42,7 +43,7 @@ class StoreRequest extends FormRequest public function getFromCurrency(): TransactionCurrency { - return TransactionCurrency::where('code', $this->get('from'))->first(); + return Amount::getTransactionCurrencyByCode((string) $this->get('from')); } public function getRate(): string @@ -52,7 +53,7 @@ class StoreRequest extends FormRequest public function getToCurrency(): TransactionCurrency { - return TransactionCurrency::where('code', $this->get('to'))->first(); + return Amount::getTransactionCurrencyByCode((string) $this->get('to')); } /** diff --git a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php index a45297b832..0810b4ce4b 100644 --- a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php +++ b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php @@ -24,7 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\PiggyBank; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Rules\IsValidZeroOrMoreAmount; @@ -78,7 +79,7 @@ class StoreRequest extends FormRequest 'object_group_id' => 'numeric|belongsToUser:object_groups,id', 'object_group_title' => ['min:1', 'max:255'], 'target_amount' => ['required', new IsValidZeroOrMoreAmount()], - 'start_date' => 'date|nullable', + 'start_date' => 'required|date|after:1970-01-01|before:2038-01-17', 'transaction_currency_id' => 'exists:transaction_currencies,id|required_without:transaction_currency_code', 'transaction_currency_code' => 'exists:transaction_currencies,code|required_without:transaction_currency_id', 'target_date' => 'date|nullable|after:start_date', @@ -96,7 +97,7 @@ class StoreRequest extends FormRequest // validate start before end only if both are there. $data = $validator->getData(); $currency = $this->getCurrencyFromData($validator, $data); - if (null === $currency) { + if (!$currency instanceof TransactionCurrency) { return; } $targetAmount = (string) ($data['target_amount'] ?? '0'); @@ -135,16 +136,10 @@ class StoreRequest extends FormRequest private function getCurrencyFromData(Validator $validator, array $data): ?TransactionCurrency { if (array_key_exists('transaction_currency_code', $data) && '' !== (string) $data['transaction_currency_code']) { - $currency = TransactionCurrency::whereCode($data['transaction_currency_code'])->first(); - if (null !== $currency) { - return $currency; - } + return Amount::getTransactionCurrencyByCode((string) $data['transaction_currency_code']); } if (array_key_exists('transaction_currency_id', $data) && '' !== (string) $data['transaction_currency_id']) { - $currency = TransactionCurrency::find((int) $data['transaction_currency_id']); - if (null !== $currency) { - return $currency; - } + return Amount::getTransactionCurrencyById((int) $data['transaction_currency_id']); } $validator->errors()->add('transaction_currency_id', trans('validation.require_currency_id_code')); diff --git a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php index b9f736f74f..98b15ae55b 100644 --- a/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Recurrence; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; diff --git a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php index f16c521590..1b5c1590a4 100644 --- a/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Recurrence; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Recurrence; use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\IsBoolean; diff --git a/app/Api/V1/Requests/Models/Rule/StoreRequest.php b/app/Api/V1/Requests/Models/Rule/StoreRequest.php index 496dbc550a..5f9e0126c8 100644 --- a/app/Api/V1/Requests/Models/Rule/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Rule/StoreRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Rule; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php index 8b58fbd3d8..22880b581a 100644 --- a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Rule; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Rule; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidActionExpression; diff --git a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php index 4c7cb3d43d..a1a20fe4d1 100644 --- a/app/Api/V1/Requests/Models/Transaction/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Transaction; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Location; use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\IsBoolean; diff --git a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php index 1aa8af303d..7a2d0a7be4 100644 --- a/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Transaction; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionGroup; use FireflyIII\Rules\BelongsUser; diff --git a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php index b93334289d..693c109d5d 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\TransactionLink; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php index ce62f5a87c..71a8090810 100644 --- a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\TransactionLink; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; diff --git a/app/Api/V1/Requests/Models/Webhook/CreateRequest.php b/app/Api/V1/Requests/Models/Webhook/CreateRequest.php index 66064a0e19..73318823ca 100644 --- a/app/Api/V1/Requests/Models/Webhook/CreateRequest.php +++ b/app/Api/V1/Requests/Models/Webhook/CreateRequest.php @@ -24,15 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Webhook; -use FireflyIII\Enums\WebhookResponse; -use FireflyIII\Enums\WebhookTrigger; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Webhook; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; -use Illuminate\Contracts\Validation\Validator; +use FireflyIII\Support\Request\ValidatesWebhooks; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Support\Facades\Log; /** * Class CreateRequest @@ -41,27 +39,28 @@ class CreateRequest extends FormRequest { use ChecksLogin; use ConvertsDataTypes; + use ValidatesWebhooks; public function getData(): array { - $triggers = Webhook::getTriggersForValidation(); - $responses = Webhook::getResponsesForValidation(); - $deliveries = Webhook::getDeliveriesForValidation(); - - $fields = [ - 'title' => ['title', 'convertString'], - 'active' => ['active', 'boolean'], - 'trigger' => ['trigger', 'convertString'], - 'response' => ['response', 'convertString'], - 'delivery' => ['delivery', 'convertString'], - 'url' => ['url', 'convertString'], + $fields = [ + 'title' => ['title', 'convertString'], + 'active' => ['active', 'boolean'], + 'url' => ['url', 'convertString'], ]; + $triggers = $this->get('triggers', []); + $responses = $this->get('responses', []); + $deliveries = $this->get('deliveries', []); - // this is the way. - $return = $this->getAllData($fields); - $return['trigger'] = $triggers[$return['trigger']] ?? (int)$return['trigger']; - $return['response'] = $responses[$return['response']] ?? (int)$return['response']; - $return['delivery'] = $deliveries[$return['delivery']] ?? (int)$return['delivery']; + if (0 === count($triggers) || 0 === count($responses) || 0 === count($deliveries)) { + throw new FireflyException('Unexpectedly got no responses, triggers or deliveries.'); + } + + + $return = $this->getAllData($fields); + $return['triggers'] = $triggers; + $return['responses'] = $responses; + $return['deliveries'] = $deliveries; return $return; } @@ -71,59 +70,24 @@ class CreateRequest extends FormRequest */ public function rules(): array { - $triggers = implode(',', array_keys(Webhook::getTriggersForValidation())); - $responses = implode(',', array_keys(Webhook::getResponsesForValidation())); - $deliveries = implode(',', array_keys(Webhook::getDeliveriesForValidation())); + $triggers = implode(',', array_values(Webhook::getTriggers())); + $responses = implode(',', array_values(Webhook::getResponses())); + $deliveries = implode(',', array_values(Webhook::getDeliveries())); $validProtocols = config('firefly.valid_url_protocols'); return [ - 'title' => 'required|min:1|max:255|uniqueObjectForUser:webhooks,title', - 'active' => [new IsBoolean()], - 'trigger' => sprintf('required|in:%s', $triggers), - 'response' => sprintf('required|in:%s', $responses), - 'delivery' => sprintf('required|in:%s', $deliveries), - 'url' => ['required', sprintf('url:%s', $validProtocols), 'uniqueWebhook'], + 'title' => 'required|min:1|max:255|uniqueObjectForUser:webhooks,title', + 'active' => [new IsBoolean()], + 'trigger' => 'prohibited', + 'triggers' => 'required|array|min:1|max:10', + 'triggers.*' => sprintf('required|in:%s', $triggers), + 'response' => 'prohibited', + 'responses' => 'required|array|min:1|max:1', + 'responses.*' => sprintf('required|in:%s', $responses), + 'delivery' => 'prohibited', + 'deliveries' => 'required|array|min:1|max:1', + 'deliveries.*' => sprintf('required|in:%s', $deliveries), + 'url' => ['required', sprintf('url:%s', $validProtocols)], ]; } - - public function withValidator(Validator $validator): void - { - $validator->after( - function (Validator $validator): void { - Log::debug('Validating webhook'); - $data = $validator->getData(); - $trigger = $data['trigger'] ?? null; - $response = $data['response'] ?? null; - if (null === $trigger || null === $response) { - Log::debug('No trigger or response, return.'); - - return; - } - $triggers = array_keys(Webhook::getTriggersForValidation()); - $responses = array_keys(Webhook::getResponsesForValidation()); - if (!in_array($trigger, $triggers, true) || !in_array($response, $responses, true)) { - return; - } - // cannot deliver budget info. - if (is_int($trigger)) { - Log::debug(sprintf('Trigger was integer (%d).', $trigger)); - $trigger = WebhookTrigger::from($trigger)->name; - } - if (is_int($response)) { - Log::debug(sprintf('Response was integer (%d).', $response)); - $response = WebhookResponse::from($response)->name; - } - Log::debug(sprintf('Trigger is %s, response is %s', $trigger, $response)); - if (str_contains($trigger, 'TRANSACTION') && str_contains($response, 'BUDGET')) { - $validator->errors()->add('response', trans('validation.webhook_budget_info')); - } - if (str_contains($trigger, 'BUDGET') && str_contains($response, 'ACCOUNT')) { - $validator->errors()->add('response', trans('validation.webhook_account_info')); - } - if (str_contains($trigger, 'BUDGET') && str_contains($response, 'TRANSACTION')) { - $validator->errors()->add('response', trans('validation.webhook_transaction_info')); - } - } - ); - } } diff --git a/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php b/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php index d235ca048c..aa37a0b4ed 100644 --- a/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php @@ -24,15 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Webhook; -use FireflyIII\Enums\WebhookResponse; -use FireflyIII\Enums\WebhookTrigger; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Webhook; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; -use Illuminate\Contracts\Validation\Validator; +use FireflyIII\Support\Request\ValidatesWebhooks; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Support\Facades\Log; /** * Class UpdateRequest @@ -41,38 +39,29 @@ class UpdateRequest extends FormRequest { use ChecksLogin; use ConvertsDataTypes; + use ValidatesWebhooks; public function getData(): array { - $triggers = Webhook::getTriggersForValidation(); - $responses = Webhook::getResponsesForValidation(); - $deliveries = Webhook::getDeliveriesForValidation(); - - $fields = [ + $fields = [ 'title' => ['title', 'convertString'], 'active' => ['active', 'boolean'], - 'trigger' => ['trigger', 'convertString'], - 'response' => ['response', 'convertString'], - 'delivery' => ['delivery', 'convertString'], 'url' => ['url', 'convertString'], ]; - // this is the way. - $return = $this->getAllData($fields); - if (array_key_exists('trigger', $return)) { - $return['trigger'] = $triggers[$return['trigger']] ?? 0; - } - if (array_key_exists('response', $return)) { - $return['response'] = $responses[$return['response']] ?? 0; - } - if (array_key_exists('delivery', $return)) { - $return['delivery'] = $deliveries[$return['delivery']] ?? 0; - } - $return['secret'] = null !== $this->get('secret'); - if (null !== $this->get('title')) { - $return['title'] = $this->convertString('title'); + $triggers = $this->get('triggers', []); + $responses = $this->get('responses', []); + $deliveries = $this->get('deliveries', []); + + if (0 === count($triggers) || 0 === count($responses) || 0 === count($deliveries)) { + throw new FireflyException('Unexpectedly got no responses, triggers or deliveries.'); } + $return = $this->getAllData($fields); + $return['triggers'] = $triggers; + $return['responses'] = $responses; + $return['deliveries'] = $deliveries; + return $return; } @@ -81,62 +70,29 @@ class UpdateRequest extends FormRequest */ public function rules(): array { - $triggers = implode(',', array_keys(Webhook::getTriggersForValidation())); - $responses = implode(',', array_keys(Webhook::getResponsesForValidation())); - $deliveries = implode(',', array_keys(Webhook::getDeliveriesForValidation())); + $triggers = implode(',', array_values(Webhook::getTriggers())); + $responses = implode(',', array_values(Webhook::getResponses())); + $deliveries = implode(',', array_values(Webhook::getDeliveries())); $validProtocols = config('firefly.valid_url_protocols'); /** @var Webhook $webhook */ $webhook = $this->route()->parameter('webhook'); return [ - 'title' => sprintf('min:1|max:255|uniqueObjectForUser:webhooks,title,%d', $webhook->id), - 'active' => [new IsBoolean()], - 'trigger' => sprintf('in:%s', $triggers), - 'response' => sprintf('in:%s', $responses), - 'delivery' => sprintf('in:%s', $deliveries), - 'url' => [sprintf('url:%s', $validProtocols), sprintf('uniqueExistingWebhook:%d', $webhook->id)], + 'title' => sprintf('min:1|max:255|uniqueObjectForUser:webhooks,title,%d', $webhook->id), + 'active' => [new IsBoolean()], + + 'trigger' => 'prohibited', + 'triggers' => 'required|array|min:1|max:10', + 'triggers.*' => sprintf('required|in:%s', $triggers), + 'response' => 'prohibited', + 'responses' => 'required|array|min:1|max:1', + 'responses.*' => sprintf('required|in:%s', $responses), + 'delivery' => 'prohibited', + 'deliveries' => 'required|array|min:1|max:1', + 'deliveries.*' => sprintf('required|in:%s', $deliveries), + + 'url' => [sprintf('url:%s', $validProtocols), sprintf('uniqueExistingWebhook:%d', $webhook->id)], ]; } - - public function withValidator(Validator $validator): void - { - $validator->after( - function (Validator $validator): void { - Log::debug('Validating webhook'); - $data = $validator->getData(); - $trigger = $data['trigger'] ?? null; - $response = $data['response'] ?? null; - if (null === $trigger || null === $response) { - Log::debug('No trigger or response, return.'); - - return; - } - $triggers = array_keys(Webhook::getTriggersForValidation()); - $responses = array_keys(Webhook::getResponsesForValidation()); - if (!in_array($trigger, $triggers, true) || !in_array($response, $responses, true)) { - return; - } - // cannot deliver budget info. - if (is_int($trigger)) { - Log::debug(sprintf('Trigger was integer (%d).', $trigger)); - $trigger = WebhookTrigger::from($trigger)->name; - } - if (is_int($response)) { - Log::debug(sprintf('Response was integer (%d).', $response)); - $response = WebhookResponse::from($response)->name; - } - Log::debug(sprintf('Trigger is %s, response is %s', $trigger, $response)); - if (str_contains($trigger, 'TRANSACTION') && str_contains($response, 'BUDGET')) { - $validator->errors()->add('response', trans('validation.webhook_budget_info')); - } - if (str_contains($trigger, 'BUDGET') && str_contains($response, 'ACCOUNT')) { - $validator->errors()->add('response', trans('validation.webhook_account_info')); - } - if (str_contains($trigger, 'BUDGET') && str_contains($response, 'TRANSACTION')) { - $validator->errors()->add('response', trans('validation.webhook_transaction_info')); - } - } - ); - } } diff --git a/app/Api/V1/Requests/System/UserUpdateRequest.php b/app/Api/V1/Requests/System/UserUpdateRequest.php index d22090ba0b..03b201b6ad 100644 --- a/app/Api/V1/Requests/System/UserUpdateRequest.php +++ b/app/Api/V1/Requests/System/UserUpdateRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\System; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; diff --git a/app/Casts/SeparateTimezoneCaster.php b/app/Casts/SeparateTimezoneCaster.php index aa1091885f..67a56ae83e 100644 --- a/app/Casts/SeparateTimezoneCaster.php +++ b/app/Casts/SeparateTimezoneCaster.php @@ -28,6 +28,7 @@ namespace FireflyIII\Casts; use Carbon\Carbon; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Log; /** * Class SeparateTimezoneCaster @@ -51,6 +52,7 @@ class SeparateTimezoneCaster implements CastsAttributes $timeZone = $attributes[sprintf('%s_tz', $key)] ?? config('app.timezone'); return Carbon::parse($value, $timeZone)->setTimezone(config('app.timezone')); + // Log::debug(sprintf('SeparateTimezoneCaster: %s.%s = %s', str_replace('FireflyIII\\Models\\','',get_class($model)), $key, $result->toAtomString())); } /** diff --git a/app/Console/Commands/Correction/CorrectsGroupAccounts.php b/app/Console/Commands/Correction/CorrectsGroupAccounts.php index 39989cf164..5a836584bb 100644 --- a/app/Console/Commands/Correction/CorrectsGroupAccounts.php +++ b/app/Console/Commands/Correction/CorrectsGroupAccounts.php @@ -45,9 +45,7 @@ class CorrectsGroupAccounts extends Command public function handle(): int { $groups = []; - $res = TransactionJournal::groupBy('transaction_group_id') - ->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')])// @phpstan-ignore-line - ; + $res = TransactionJournal::groupBy('transaction_group_id')->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]); /** @var TransactionJournal $journal */ foreach ($res as $journal) { diff --git a/app/Console/Commands/Correction/CorrectsPiggyBanks.php b/app/Console/Commands/Correction/CorrectsPiggyBanks.php index 8922629960..66a737532a 100644 --- a/app/Console/Commands/Correction/CorrectsPiggyBanks.php +++ b/app/Console/Commands/Correction/CorrectsPiggyBanks.php @@ -57,8 +57,6 @@ class CorrectsPiggyBanks extends Command $event->transaction_journal_id = null; $event->save(); ++$count; - - continue; } } if (0 !== $count) { diff --git a/app/Console/Commands/Correction/CorrectsUnevenAmount.php b/app/Console/Commands/Correction/CorrectsUnevenAmount.php index 30a6107fe1..4d1e527f3e 100644 --- a/app/Console/Commands/Correction/CorrectsUnevenAmount.php +++ b/app/Console/Commands/Correction/CorrectsUnevenAmount.php @@ -263,9 +263,9 @@ class CorrectsUnevenAmount extends Command // Log::debug(sprintf('[c] %s', var_export($source->transaction_currency_id === $destination->foreign_currency_id,true))); // Log::debug(sprintf('[d] %s', var_export((int) $destination->transaction_currency_id ===(int) $source->foreign_currency_id, true))); - if (0 === bccomp((string) app('steam')->positive($source->amount), (string) app('steam')->positive($destination->foreign_amount)) + if (0 === bccomp(Steam::positive($source->amount), Steam::positive($destination->foreign_amount)) && $source->transaction_currency_id === $destination->foreign_currency_id - && 0 === bccomp((string) app('steam')->positive($destination->amount), (string) app('steam')->positive($source->foreign_amount)) + && 0 === bccomp(Steam::positive($destination->amount), Steam::positive($source->foreign_amount)) && (int) $destination->transaction_currency_id === (int) $source->foreign_currency_id ) { return true; @@ -302,10 +302,10 @@ class CorrectsUnevenAmount extends Command private function isBetweenAssetAndLiability(TransactionJournal $journal): bool { - /** @var Transaction $sourceTransaction */ + /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); - /** @var Transaction $destinationTransaction */ + /** @var null|Transaction $destinationTransaction */ $destinationTransaction = $journal->transactions()->where('amount', '>', 0)->first(); if (null === $sourceTransaction || null === $destinationTransaction) { Log::warning('Either transaction is false, stop.'); diff --git a/app/Console/Commands/Correction/RemovesEmptyJournals.php b/app/Console/Commands/Correction/RemovesEmptyJournals.php index 38b1a1c437..4ea323c72c 100644 --- a/app/Console/Commands/Correction/RemovesEmptyJournals.php +++ b/app/Console/Commands/Correction/RemovesEmptyJournals.php @@ -55,10 +55,7 @@ class RemovesEmptyJournals 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']) // @phpstan-ignore-line - ; + $set = Transaction::whereNull('deleted_at')->groupBy('transactions.transaction_journal_id')->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']); $total = 0; /** @var Transaction $row */ diff --git a/app/Console/Commands/Correction/RestoresOAuthKeys.php b/app/Console/Commands/Correction/RestoresOAuthKeys.php index 8eaf3c79a7..599f127395 100644 --- a/app/Console/Commands/Correction/RestoresOAuthKeys.php +++ b/app/Console/Commands/Correction/RestoresOAuthKeys.php @@ -71,7 +71,6 @@ class RestoresOAuthKeys extends Command $this->storeKeysInDB(); $this->friendlyInfo('Stored OAuth keys in database.'); - return; } } diff --git a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php index 06ba95035a..65a6fc2912 100644 --- a/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php +++ b/app/Console/Commands/Integrity/ValidatesEnvironmentVariables.php @@ -1,9 +1,9 @@ . */ +declare(strict_types=1); + namespace FireflyIII\Console\Commands\Integrity; use FireflyIII\Console\Commands\ShowsFriendlyMessages; @@ -30,18 +32,7 @@ class ValidatesEnvironmentVariables extends Command { use ShowsFriendlyMessages; - /** - * The console command description. - * - * @var null|string - */ protected $description = 'Makes sure you use the correct variables.'; - - /** - * The name and signature of the console command. - * - * @var string - */ protected $signature = 'integrity:validates-environment-variables'; /** diff --git a/app/Console/Commands/System/ForcesDecimalSize.php b/app/Console/Commands/System/ForcesDecimalSize.php index 4b54218f10..91884011ce 100644 --- a/app/Console/Commands/System/ForcesDecimalSize.php +++ b/app/Console/Commands/System/ForcesDecimalSize.php @@ -515,7 +515,7 @@ class ForcesDecimalSize extends Command continue; } // fix $field by rounding it down correctly. - $pow = (float) 10 ** $currency->decimal_places; + $pow = 10.0 ** $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)); @@ -546,7 +546,7 @@ class ForcesDecimalSize extends Command continue; } // fix $field by rounding it down correctly. - $pow = (float) 10 ** $currency->decimal_places; + $pow = 10.0 ** $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) diff --git a/app/Console/Commands/System/OutputsInstructions.php b/app/Console/Commands/System/OutputsInstructions.php index b144fcdf33..fb99e99692 100644 --- a/app/Console/Commands/System/OutputsInstructions.php +++ b/app/Console/Commands/System/OutputsInstructions.php @@ -27,6 +27,7 @@ namespace FireflyIII\Console\Commands\System; use Carbon\Carbon; use FireflyIII\Support\System\GeneratesInstallationId; use Illuminate\Console\Command; +use Random\RandomException; class OutputsInstructions extends Command { @@ -133,6 +134,9 @@ class OutputsInstructions extends Command if ('03-31' === $today) { $colors = ['bright-blue', 'bright-red', 'white', 'white', 'bright-red', 'bright-blue', 'default', 'default']; } + if ('ru_RU' === config('firefly.default_language')) { + $colors = ['blue', 'blue', 'blue', 'yellow', 'yellow', 'yellow', 'default', 'default']; + } $this->line(sprintf(' ______ _ __ _ _____ _____ _____ ', $colors[0])); $this->line(sprintf(' | ____(_) / _| | |_ _|_ _|_ _| ', $colors[1])); @@ -238,14 +242,42 @@ class OutputsInstructions extends Command private function someQuote(): void { - $lines = [ + $lines = [ 'Forgive yourself for not being at peace.', 'Doesn\'t look like anything to me.', 'Be proud of what you make.', 'Be there or forever wonder.', 'A year from now you will wish you had started today.', ]; - $random = random_int(0, count($lines) - 1); - $this->line(sprintf(' "%s"', $lines[$random])); + $addQuotes = true; + + // fuck the Russian aggression in Ukraine. + + // There is no point even trying to be neutral, because you can’t. When I say you can’t be neutral on + // a moving train, it means the world is already moving in certain directions. Children are going + // hungry, wars are taking place. In a situation like that, to be neutral or to try to be neutral, + // to stand aside, not to take a stand, not to participate, is to collaborate with whatever is + // going on, to allow that to happen. + + if ('ru_RU' === config('firefly.default_language')) { + $addQuotes = false; + $lines = [ + '🇺🇦 Слава Україні!', + '🇺🇦 Slava Ukraini!', + ]; + } + + try { + $random = random_int(0, count($lines) - 1); + } catch (RandomException) { + $random = 0; + } + if ($addQuotes) { + $this->line(sprintf(' "%s"', $lines[$random])); + + return; + } + $this->line(sprintf(' %s', $lines[$random])); + } } diff --git a/app/Console/Commands/System/RecalculatesRunningBalance.php b/app/Console/Commands/System/RecalculatesRunningBalance.php index 5e51a4fcc6..f14bba0ee7 100644 --- a/app/Console/Commands/System/RecalculatesRunningBalance.php +++ b/app/Console/Commands/System/RecalculatesRunningBalance.php @@ -1,9 +1,9 @@ . */ +declare(strict_types=1); + namespace FireflyIII\Console\Commands\System; use FireflyIII\Console\Commands\ShowsFriendlyMessages; @@ -48,7 +50,7 @@ class RecalculatesRunningBalance extends Command /** * Execute the console command. */ - public function handle() + public function handle(): int { if (true === config('firefly.feature_flags.running_balance_column')) { $this->friendlyInfo('Will recalculate account balances. This may take a LONG time. Please be patient.'); @@ -58,6 +60,8 @@ class RecalculatesRunningBalance extends Command return 0; } $this->friendlyWarning('This command has been disabled.'); + + return 0; } private function correctBalanceAmounts(bool $forced): void diff --git a/app/Console/Commands/System/ResetsErrorMailLimit.php b/app/Console/Commands/System/ResetsErrorMailLimit.php index 9aba24fa79..ffba1aad82 100644 --- a/app/Console/Commands/System/ResetsErrorMailLimit.php +++ b/app/Console/Commands/System/ResetsErrorMailLimit.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Console\Commands\System; @@ -8,6 +29,9 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use Illuminate\Console\Command; use Symfony\Component\Console\Command\Command as CommandAlias; +use function Safe\file_put_contents; +use function Safe\json_encode; + class ResetsErrorMailLimit extends Command { use ShowsFriendlyMessages; diff --git a/app/Console/Commands/System/ScansAttachments.php b/app/Console/Commands/System/ScansAttachments.php index 24c6f6199b..0225cf0008 100644 --- a/app/Console/Commands/System/ScansAttachments.php +++ b/app/Console/Commands/System/ScansAttachments.php @@ -78,8 +78,8 @@ class ScansAttachments extends Command } $tempFileName = tempnam(sys_get_temp_dir(), 'FireflyIII'); file_put_contents($tempFileName, $decryptedContent); - $attachment->md5 = (string)md5_file($tempFileName); - $attachment->mime = (string)mime_content_type($tempFileName); + $attachment->md5 = md5_file($tempFileName); + $attachment->mime = mime_content_type($tempFileName); $attachment->save(); $this->friendlyInfo(sprintf('Fixed attachment #%d', $attachment->id)); } diff --git a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php index 87e99cd706..b1e3d198db 100644 --- a/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php +++ b/app/Console/Commands/Upgrade/RemovesDatabaseDecryption.php @@ -66,7 +66,7 @@ class RemovesDatabaseDecryption extends Command * @var string $table * @var array $fields */ - foreach ($tables as $table => $fields) { + foreach ($tables as $table => $fields) { // @phpstan-ignore-line $this->decryptTable($table, $fields); } diff --git a/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php b/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php index 56b1a5ee0c..6e2527c8f4 100644 --- a/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php +++ b/app/Console/Commands/Upgrade/UpgradesCurrencyPreferences.php @@ -25,9 +25,11 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Upgrade; use FireflyIII\Console\Commands\ShowsFriendlyMessages; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Preference; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\UserGroup; +use FireflyIII\Support\Facades\Amount; use FireflyIII\User; use Illuminate\Console\Command; use Illuminate\Support\Collection; @@ -65,7 +67,7 @@ class UpgradesCurrencyPreferences extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; @@ -104,8 +106,8 @@ class UpgradesCurrencyPreferences extends Command private function upgradeUserPreferences(User $user): void { - $currencies = TransactionCurrency::get(); - $enabled = new Collection(); + $currencies = TransactionCurrency::get(); + $enabled = new Collection(); /** @var TransactionCurrency $currency */ foreach ($currencies as $currency) { @@ -116,10 +118,11 @@ class UpgradesCurrencyPreferences extends Command $user->currencies()->sync($enabled->pluck('id')->toArray()); // set the default currency for the user and for the group: - $preference = $this->getPreference($user); - $primaryCurrency = TransactionCurrency::where('code', $preference)->first(); - if (null === $primaryCurrency) { - // get EUR + $preference = $this->getPreference($user); + + try { + $primaryCurrency = Amount::getTransactionCurrencyByCode($preference); + } catch (FireflyException) { $primaryCurrency = TransactionCurrency::where('code', 'EUR')->first(); } $user->currencies()->updateExistingPivot($primaryCurrency->id, ['user_default' => true]); @@ -135,7 +138,7 @@ class UpgradesCurrencyPreferences extends Command } if (null !== $preference->data && !is_array($preference->data)) { - return (string) $preference->data; + return (string)$preference->data; } return 'EUR'; diff --git a/app/Console/Commands/Upgrade/UpgradesDatabase.php b/app/Console/Commands/Upgrade/UpgradesDatabase.php index a98ff922d8..92429a9a36 100644 --- a/app/Console/Commands/Upgrade/UpgradesDatabase.php +++ b/app/Console/Commands/Upgrade/UpgradesDatabase.php @@ -75,6 +75,7 @@ class UpgradesDatabase extends Command 'upgrade:610-currency-preferences', 'upgrade:620-piggy-banks', 'upgrade:620-pc-amounts', + 'upgrade:640-upgrade-webhooks', 'firefly-iii:correct-database', ]; $args = []; diff --git a/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php b/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php index 511d8c4cf4..9a2bf9f048 100644 --- a/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php +++ b/app/Console/Commands/Upgrade/UpgradesLiabilitiesEight.php @@ -157,7 +157,6 @@ class UpgradesLiabilitiesEight extends Command $service = new TransactionGroupDestroyService(); $service->destroy($group); - return; } } diff --git a/app/Console/Commands/Upgrade/UpgradesWebhooks.php b/app/Console/Commands/Upgrade/UpgradesWebhooks.php new file mode 100644 index 0000000000..46e39e11e7 --- /dev/null +++ b/app/Console/Commands/Upgrade/UpgradesWebhooks.php @@ -0,0 +1,116 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Console\Commands\Upgrade; + +use FireflyIII\Console\Commands\ShowsFriendlyMessages; +use FireflyIII\Enums\WebhookDelivery; +use FireflyIII\Enums\WebhookResponse; +use FireflyIII\Enums\WebhookTrigger; +use FireflyIII\Models\Webhook; +use FireflyIII\Models\WebhookDelivery as WebhookDeliveryModel; +use FireflyIII\Models\WebhookResponse as WebhookResponseModel; +use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel; +use Illuminate\Console\Command; + +class UpgradesWebhooks extends Command +{ + use ShowsFriendlyMessages; + + public const string CONFIG_NAME = '640_upgrade_webhooks'; + protected $description = 'Upgrade webhooks so they can handle multiple triggers.'; + protected $signature = 'upgrade:640-upgrade-webhooks {--F|force : Force the execution of this command.}'; + + /** + * Execute the console command. + */ + public function handle(): int + { + if ($this->isExecuted() && true !== $this->option('force')) { + $this->friendlyInfo('This command has already been executed.'); + + return 0; + } + + $this->upgradeWebhooks(); + $this->markAsExecuted(); + $this->friendlyPositive('Upgraded webhooks.'); + + return 0; + } + + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; + } + + private function upgradeWebhooks(): void + { + $set = Webhook::where('delivery', '>', 1)->orWhere('trigger', '>', 1)->orWhere('response', '>', 1)->get(); + + /** @var Webhook $webhook */ + foreach ($set as $webhook) { + $this->upgradeWebhook($webhook); + } + } + + private function upgradeWebhook(Webhook $webhook): void + { + $delivery = WebhookDelivery::tryFrom((int)$webhook->delivery); + $response = WebhookResponse::tryFrom((int)$webhook->response); + $trigger = WebhookTrigger::tryFrom((int)$webhook->trigger); + if (null === $delivery || null === $response || null === $trigger) { + $this->friendlyError(sprintf('[a] Webhook #%d has an invalid delivery, response or trigger value. Will not upgrade.', $webhook->id)); + + return; + } + $deliveryModel = WebhookDeliveryModel::where('key', $delivery->value)->first(); + $responseModel = WebhookResponseModel::where('key', $response->value)->first(); + $triggerModel = WebhookTriggerModel::where('key', $trigger->value)->first(); + if (null === $deliveryModel || null === $responseModel || null === $triggerModel) { + $this->friendlyError(sprintf('[b] Webhook #%d has an invalid delivery, response or trigger model. Will not upgrade.', $webhook->id)); + + return; + } + $webhook->webhookDeliveries()->attach([$deliveryModel->id]); + $webhook->webhookResponses()->attach([$responseModel->id]); + $webhook->webhookTriggers()->attach([$triggerModel->id]); + $webhook->delivery = 1; + $webhook->response = 1; + $webhook->trigger = 1; + $webhook->save(); + $this->friendlyPositive(sprintf('Webhook #%d upgraded.', $webhook->id)); + } + + private function markAsExecuted(): void + { + app('fireflyconfig')->set(self::CONFIG_NAME, true); + } +} diff --git a/app/Enums/WebhookResponse.php b/app/Enums/WebhookResponse.php index ead11117a0..20156cc4f5 100644 --- a/app/Enums/WebhookResponse.php +++ b/app/Enums/WebhookResponse.php @@ -32,5 +32,6 @@ enum WebhookResponse: int case TRANSACTIONS = 200; case ACCOUNTS = 210; case BUDGET = 230; + case RELEVANT = 240; case NONE = 220; } diff --git a/app/Enums/WebhookTrigger.php b/app/Enums/WebhookTrigger.php index 9f2951ff1d..1f64cb6032 100644 --- a/app/Enums/WebhookTrigger.php +++ b/app/Enums/WebhookTrigger.php @@ -29,6 +29,7 @@ namespace FireflyIII\Enums; */ enum WebhookTrigger: int { + case ANY = 50; case STORE_TRANSACTION = 100; case UPDATE_TRANSACTION = 110; case DESTROY_TRANSACTION = 120; diff --git a/app/Events/ActuallyLoggedIn.php b/app/Events/ActuallyLoggedIn.php index 6bb21a5a04..274af7149a 100644 --- a/app/Events/ActuallyLoggedIn.php +++ b/app/Events/ActuallyLoggedIn.php @@ -37,7 +37,7 @@ class ActuallyLoggedIn extends Event public User $user; - public function __construct(null|Authenticatable|User $user) + public function __construct(Authenticatable|User|null $user) { if ($user instanceof User) { $this->user = $user; diff --git a/app/Events/Model/Bill/WarnUserAboutBill.php b/app/Events/Model/Bill/WarnUserAboutBill.php index ceb23cae49..0c52d28e5e 100644 --- a/app/Events/Model/Bill/WarnUserAboutBill.php +++ b/app/Events/Model/Bill/WarnUserAboutBill.php @@ -1,6 +1,27 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Events\Model\Bill; diff --git a/app/Events/Model/Bill/WarnUserAboutOverdueSubscriptions.php b/app/Events/Model/Bill/WarnUserAboutOverdueSubscriptions.php index 0e84a849dd..9306bfc208 100644 --- a/app/Events/Model/Bill/WarnUserAboutOverdueSubscriptions.php +++ b/app/Events/Model/Bill/WarnUserAboutOverdueSubscriptions.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Events\Model\Bill; diff --git a/app/Events/Model/PiggyBank/ChangedName.php b/app/Events/Model/PiggyBank/ChangedName.php index bed6b78f20..38638c4e04 100644 --- a/app/Events/Model/PiggyBank/ChangedName.php +++ b/app/Events/Model/PiggyBank/ChangedName.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Events\Model\PiggyBank; diff --git a/app/Events/Security/DisabledMFA.php b/app/Events/Security/DisabledMFA.php index 58c6260ed3..ad3464e95f 100644 --- a/app/Events/Security/DisabledMFA.php +++ b/app/Events/Security/DisabledMFA.php @@ -35,7 +35,7 @@ class DisabledMFA extends Event public User $user; - public function __construct(null|Authenticatable|User $user) + public function __construct(Authenticatable|User|null $user) { if ($user instanceof User) { $this->user = $user; diff --git a/app/Events/Security/EnabledMFA.php b/app/Events/Security/EnabledMFA.php index d0a647fc86..44c5588353 100644 --- a/app/Events/Security/EnabledMFA.php +++ b/app/Events/Security/EnabledMFA.php @@ -35,7 +35,7 @@ class EnabledMFA extends Event public User $user; - public function __construct(null|Authenticatable|User $user) + public function __construct(Authenticatable|User|null $user) { if ($user instanceof User) { $this->user = $user; diff --git a/app/Events/Security/MFABackupFewLeft.php b/app/Events/Security/MFABackupFewLeft.php index 0379afeb3e..f31e325b18 100644 --- a/app/Events/Security/MFABackupFewLeft.php +++ b/app/Events/Security/MFABackupFewLeft.php @@ -35,7 +35,7 @@ class MFABackupFewLeft extends Event public User $user; - public function __construct(null|Authenticatable|User $user, public int $count) + public function __construct(Authenticatable|User|null $user, public int $count) { if ($user instanceof User) { $this->user = $user; diff --git a/app/Events/Security/MFABackupNoLeft.php b/app/Events/Security/MFABackupNoLeft.php index 43df68b3a1..a56b8ce097 100644 --- a/app/Events/Security/MFABackupNoLeft.php +++ b/app/Events/Security/MFABackupNoLeft.php @@ -35,7 +35,7 @@ class MFABackupNoLeft extends Event public User $user; - public function __construct(null|Authenticatable|User $user) + public function __construct(Authenticatable|User|null $user) { if ($user instanceof User) { $this->user = $user; diff --git a/app/Events/Security/MFAManyFailedAttempts.php b/app/Events/Security/MFAManyFailedAttempts.php index e1b9067c5b..c8c8b25bad 100644 --- a/app/Events/Security/MFAManyFailedAttempts.php +++ b/app/Events/Security/MFAManyFailedAttempts.php @@ -35,7 +35,7 @@ class MFAManyFailedAttempts extends Event public User $user; - public function __construct(null|Authenticatable|User $user, public int $count) + public function __construct(Authenticatable|User|null $user, public int $count) { if ($user instanceof User) { $this->user = $user; diff --git a/app/Events/Security/MFANewBackupCodes.php b/app/Events/Security/MFANewBackupCodes.php index 4cf80e8f07..b07878c1d6 100644 --- a/app/Events/Security/MFANewBackupCodes.php +++ b/app/Events/Security/MFANewBackupCodes.php @@ -35,7 +35,7 @@ class MFANewBackupCodes extends Event public User $user; - public function __construct(null|Authenticatable|User $user) + public function __construct(Authenticatable|User|null $user) { if ($user instanceof User) { $this->user = $user; diff --git a/app/Events/Security/MFAUsedBackupCode.php b/app/Events/Security/MFAUsedBackupCode.php index 8321faf3f3..47d7b84ed7 100644 --- a/app/Events/Security/MFAUsedBackupCode.php +++ b/app/Events/Security/MFAUsedBackupCode.php @@ -35,7 +35,7 @@ class MFAUsedBackupCode extends Event public User $user; - public function __construct(null|Authenticatable|User $user) + public function __construct(Authenticatable|User|null $user) { if ($user instanceof User) { $this->user = $user; diff --git a/app/Events/Security/UserAttemptedLogin.php b/app/Events/Security/UserAttemptedLogin.php index 6313bcbc35..e5ee2154b7 100644 --- a/app/Events/Security/UserAttemptedLogin.php +++ b/app/Events/Security/UserAttemptedLogin.php @@ -35,7 +35,7 @@ class UserAttemptedLogin extends Event public User $user; - public function __construct(null|Authenticatable|User $user) + public function __construct(Authenticatable|User|null $user) { if ($user instanceof User) { $this->user = $user; diff --git a/app/Exceptions/GracefulNotFoundHandler.php b/app/Exceptions/GracefulNotFoundHandler.php index 7866d20047..fe0ca9c896 100644 --- a/app/Exceptions/GracefulNotFoundHandler.php +++ b/app/Exceptions/GracefulNotFoundHandler.php @@ -128,10 +128,6 @@ class GracefulNotFoundHandler extends ExceptionHandler return redirect(route('categories.index')); case 'rules.edit': - $request->session()->reflash(); - - return redirect(route('rules.index')); - case 'rule-groups.edit': $request->session()->reflash(); @@ -170,7 +166,7 @@ class GracefulNotFoundHandler extends ExceptionHandler } /** @var null|Account $account */ - $account = $user->accounts()->with(['accountType'])->withTrashed()->find($accountId); + $account = $user->accounts()->withTrashed()->with(['accountType'])->find($accountId); if (null === $account) { app('log')->error(sprintf('Could not find account %d, so give big fat error.', $accountId)); diff --git a/app/Factory/PiggyBankFactory.php b/app/Factory/PiggyBankFactory.php index 539e1999b8..40455001d3 100644 --- a/app/Factory/PiggyBankFactory.php +++ b/app/Factory/PiggyBankFactory.php @@ -282,7 +282,7 @@ class PiggyBankFactory // create event: Log::debug('linkToAccountIds: Trigger change for positive amount [b].'); - event(new ChangedAmount($piggyBank, $toBeLinked[$account->id]['current_amount'], null, null)); + event(new ChangedAmount($piggyBank, $toBeLinked[$account->id]['current_amount'] ?? '0', null, null)); } if (!array_key_exists('current_amount', $info)) { $toBeLinked[$account->id] ??= []; diff --git a/app/Factory/RecurrenceFactory.php b/app/Factory/RecurrenceFactory.php index 8254a8a4ec..6147bacd22 100644 --- a/app/Factory/RecurrenceFactory.php +++ b/app/Factory/RecurrenceFactory.php @@ -30,6 +30,7 @@ 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; /** @@ -62,8 +63,8 @@ class RecurrenceFactory $type = $this->findTransactionType(ucfirst((string) $data['recurrence']['type'])); } catch (FireflyException $e) { $message = sprintf('Cannot make a recurring transaction of type "%s"', $data['recurrence']['type']); - app('log')->error($message); - app('log')->error($e->getTraceAsString()); + Log::error($message); + Log::error($e->getTraceAsString()); throw new FireflyException($message, 0, $e); } @@ -101,17 +102,18 @@ class RecurrenceFactory $recurrence = new Recurrence( [ - 'user_id' => $this->user->id, - 'user_group_id' => $this->user->user_group_id, - 'transaction_type_id' => $type->id, - 'title' => $title, - 'description' => $description, - 'first_date' => $firstDate?->format('Y-m-d'), - 'repeat_until' => $repetitions > 0 ? null : $repeatUntilString, - 'latest_date' => null, - 'repetitions' => $repetitions, - 'apply_rules' => $applyRules, - 'active' => $active, + 'user_id' => $this->user->id, + 'user_group_id' => $this->user->user_group_id, + 'transaction_type_id' => $type->id, + 'title' => $title, + 'description' => $description, + 'first_date' => $firstDate?->format('Y-m-d'), + 'first_date_tz' => $firstDate?->format('e'), + 'repeat_until' => $repetitions > 0 ? null : $repeatUntilString, + 'latest_date' => null, + 'repetitions' => $repetitions, + 'apply_rules' => $applyRules, + 'active' => $active, ] ); $recurrence->save(); @@ -125,8 +127,8 @@ class RecurrenceFactory try { $this->createTransactions($recurrence, $data['transactions'] ?? []); } catch (FireflyException $e) { - app('log')->error($e->getMessage()); - app('log')->error($e->getTraceAsString()); + Log::error($e->getMessage()); + 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/TransactionCurrencyFactory.php b/app/Factory/TransactionCurrencyFactory.php index b4088e200d..e57a90c983 100644 --- a/app/Factory/TransactionCurrencyFactory.php +++ b/app/Factory/TransactionCurrencyFactory.php @@ -26,7 +26,9 @@ namespace FireflyIII\Factory; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Support\Facades\Amount; use Illuminate\Database\QueryException; +use Illuminate\Support\Facades\Log; /** * Class TransactionCurrencyFactory @@ -41,14 +43,14 @@ class TransactionCurrencyFactory $data['code'] = e($data['code']); $data['symbol'] = e($data['symbol']); $data['name'] = e($data['name']); - $data['decimal_places'] = (int) $data['decimal_places']; + $data['decimal_places'] = (int)$data['decimal_places']; // if the code already exists (deleted) // force delete it and then create the transaction: $count = TransactionCurrency::withTrashed()->whereCode($data['code'])->count(); if (1 === $count) { $old = TransactionCurrency::withTrashed()->whereCode($data['code'])->first(); $old->forceDelete(); - app('log')->warning(sprintf('Force deleted old currency with ID #%d and code "%s".', $old->id, $data['code'])); + Log::warning(sprintf('Force deleted old currency with ID #%d and code "%s".', $old->id, $data['code'])); } try { @@ -64,8 +66,8 @@ class TransactionCurrencyFactory ); } catch (QueryException $e) { $result = null; - app('log')->error(sprintf('Could not create new currency: %s', $e->getMessage())); - app('log')->error($e->getTraceAsString()); + Log::error(sprintf('Could not create new currency: %s', $e->getMessage())); + Log::error($e->getTraceAsString()); throw new FireflyException('400004: Could not store new currency.', 0, $e); } @@ -76,32 +78,33 @@ class TransactionCurrencyFactory public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency { $currencyCode = e($currencyCode); - $currencyId = (int) $currencyId; + $currencyId = (int)$currencyId; + $currency = null; if ('' === $currencyCode && 0 === $currencyId) { - app('log')->debug('Cannot find anything on empty currency code and empty currency ID!'); + Log::debug('Cannot find anything on empty currency code and empty currency ID!'); return null; } // first by ID: if ($currencyId > 0) { - $currency = TransactionCurrency::find($currencyId); - if (null !== $currency) { - return $currency; + try { + $currency = Amount::getTransactionCurrencyById($currencyId); + } catch (FireflyException) { + Log::warning(sprintf('Currency ID is #%d but found nothing!', $currencyId)); } - app('log')->warning(sprintf('Currency ID is %d but found nothing!', $currencyId)); } // then by code: - if ('' !== $currencyCode) { - $currency = TransactionCurrency::whereCode($currencyCode)->first(); - if (null !== $currency) { - return $currency; + if ('' !== $currencyCode && null === $currency) { + try { + $currency = Amount::getTransactionCurrencyByCode($currencyCode); + } catch (FireflyException) { + Log::warning(sprintf('Currency code is "%s" but found nothing!', $currencyCode)); } - app('log')->warning(sprintf('Currency code is %d but found nothing!', $currencyCode)); } - app('log')->warning('Found nothing for currency.'); + Log::info(sprintf('Found currency #%d based on ID %d and code "%s".', $currency->id, $currencyId, $currencyCode)); - return null; + return $currency; } } diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 070f71fa26..7206947be2 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -270,7 +270,7 @@ class TransactionJournalFactory $negative = $transactionFactory->createNegative((string) $row['amount'], (string) $row['foreign_amount']); } catch (FireflyException $e) { Log::error(sprintf('Exception creating negative transaction: %s', $e->getMessage())); - $this->forceDeleteOnError(new Collection([$journal])); + $this->forceDeleteOnError(new Collection()->push($journal)); throw new FireflyException($e->getMessage(), 0, $e); } @@ -305,7 +305,7 @@ class TransactionJournalFactory } catch (FireflyException $e) { Log::error(sprintf('Exception creating positive transaction: %s', $e->getMessage())); $this->forceTrDelete($negative); - $this->forceDeleteOnError(new Collection([$journal])); + $this->forceDeleteOnError(new Collection()->push($journal)); throw new FireflyException($e->getMessage(), 0, $e); } diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index bc02d217a8..a0aea0c9df 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -132,7 +132,7 @@ class MonthReportGenerator implements ReportGeneratorInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end)->withAccountInformation() + $collector->setAccounts(new Collection()->push($account))->setRange($this->start, $this->end)->withAccountInformation() ->withBudgetInformation()->withCategoryInformation()->withBillInformation()->withNotes() ; $journals = $collector->getExtractedJournals(); diff --git a/app/Generator/Webhook/StandardMessageGenerator.php b/app/Generator/Webhook/StandardMessageGenerator.php index 6143aee75f..cd8ab1cdb9 100644 --- a/app/Generator/Webhook/StandardMessageGenerator.php +++ b/app/Generator/Webhook/StandardMessageGenerator.php @@ -34,6 +34,8 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\Webhook; use FireflyIII\Models\WebhookMessage; +use FireflyIII\Models\WebhookResponse as WebhookResponseModel; +use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel; use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment; use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment; use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment; @@ -80,7 +82,13 @@ class StandardMessageGenerator implements MessageGeneratorInterface private function getWebhooks(): Collection { - return $this->user->webhooks()->where('active', true)->where('trigger', $this->trigger)->get(['webhooks.*']); + return $this->user->webhooks() + ->leftJoin('webhook_webhook_trigger', 'webhook_webhook_trigger.webhook_id', 'webhooks.id') + ->leftJoin('webhook_triggers', 'webhook_webhook_trigger.webhook_trigger_id', 'webhook_triggers.id') + ->where('active', true) + ->whereIn('webhook_triggers.title', [$this->trigger->name, WebhookTrigger::ANY->name]) + ->get(['webhooks.*']) + ; } /** @@ -115,22 +123,25 @@ class StandardMessageGenerator implements MessageGeneratorInterface */ private function generateMessage(Webhook $webhook, Model $model): void { - $class = $model::class; + $class = $model::class; // 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)); - $uuid = Uuid::uuid4(); - $basicMessage = [ + $uuid = Uuid::uuid4(); + + /** @var WebhookResponseModel $response */ + $response = $webhook->webhookResponses()->first(); + $triggers = $this->getTriggerTitles($webhook->webhookTriggers()->get()); + $basicMessage = [ 'uuid' => $uuid->toString(), 'user_id' => 0, 'user_group_id' => 0, - 'trigger' => WebhookTrigger::from((int)$webhook->trigger)->name, - 'response' => WebhookResponse::from((int)$webhook->response)->name, + 'trigger' => $this->trigger->name, + 'response' => $response->title, // guess that the database is correct. 'url' => $webhook->url, 'version' => sprintf('v%d', $this->getVersion()), 'content' => [], ]; - // depends on the model how user_id is set: switch ($class) { default: // Line is ignored because all of Firefly III's Models have an id property. @@ -142,12 +153,14 @@ class StandardMessageGenerator implements MessageGeneratorInterface /** @var Budget $model */ $basicMessage['user_id'] = $model->user_id; $basicMessage['user_group_id'] = $model->user_group_id; + $relevantResponse = WebhookResponse::BUDGET->name; break; case BudgetLimit::class: $basicMessage['user_id'] = $model->budget->user_id; $basicMessage['user_group_id'] = $model->budget->user_group_id; + $relevantResponse = WebhookResponse::BUDGET->name; break; @@ -158,31 +171,27 @@ class StandardMessageGenerator implements MessageGeneratorInterface break; } + $responseTitle = $this->getRelevantResponse($triggers, $response, $class); - // then depends on the response what to put in the message: - switch ($webhook->response) { + switch ($responseTitle) { default: - Log::error(sprintf('The response code for webhook #%d is "%d" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response)); + Log::error(sprintf('The response code for webhook #%d is "%s" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response)); return; - case WebhookResponse::BUDGET->value: + case WebhookResponse::BUDGET->name: $basicMessage['content'] = []; if ($model instanceof Budget) { $enrichment = new BudgetEnrichment(); $enrichment->setUser($model->user); + + /** @var Budget $model */ $model = $enrichment->enrichSingle($model); $transformer = new BudgetTransformer(); $basicMessage['content'] = $transformer->transform($model); } if ($model instanceof BudgetLimit) { $user = $model->budget->user; - $enrichment = new BudgetEnrichment(); - $enrichment->setUser($user); - $enrichment->setStart($model->start_date); - $enrichment->setEnd($model->end_date); - $budget = $enrichment->enrichSingle($model->budget); - $enrichment = new BudgetLimitEnrichment(); $enrichment->setUser($user); @@ -190,6 +199,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface $parameters->set('start', $model->start_date); $parameters->set('end', $model->end_date); + /** @var BudgetLimit $model */ $model = $enrichment->enrichSingle($model); $transformer = new BudgetLimitTransformer(); $transformer->setParameters($parameters); @@ -198,12 +208,12 @@ class StandardMessageGenerator implements MessageGeneratorInterface break; - case WebhookResponse::NONE->value: + case WebhookResponse::NONE->name: $basicMessage['content'] = []; break; - case WebhookResponse::TRANSACTIONS->value: + case WebhookResponse::TRANSACTIONS->name: /** @var TransactionGroup $model */ $transformer = new TransactionGroupTransformer(); @@ -220,7 +230,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface break; - case WebhookResponse::ACCOUNTS->value: + case WebhookResponse::ACCOUNTS->name: /** @var TransactionGroup $model */ $accounts = $this->collectAccounts($model); $enrichment = new AccountEnrichment(); @@ -287,4 +297,50 @@ class StandardMessageGenerator implements MessageGeneratorInterface { $this->webhooks = $webhooks; } + + private function getRelevantResponse(array $triggers, WebhookResponseModel $response, string $class): string + { + // return none if none. + if (WebhookResponse::NONE->name === $response->title) { + Log::debug(sprintf('Return "%s" because requested nothing.', WebhookResponse::NONE->name)); + + return WebhookResponse::NONE->name; + } + + if (WebhookResponse::RELEVANT->name === $response->title) { + Log::debug('Expected response is any relevant data.'); + + // depends on the $class + switch ($class) { + case TransactionGroup::class: + Log::debug(sprintf('Return "%s" because class is %s', WebhookResponse::TRANSACTIONS->name, $class)); + + return WebhookResponse::TRANSACTIONS->name; + + case Budget::class: + case BudgetLimit::class: + Log::debug(sprintf('Return "%s" because class is %s', WebhookResponse::BUDGET->name, $class)); + + return WebhookResponse::BUDGET->name; + + default: + throw new FireflyException(sprintf('Cannot deal with "relevant" if the given object is a "%s"', $class)); + } + } + Log::debug(sprintf('Return response again: %s', $response->title)); + + return $response->title; + } + + private function getTriggerTitles(Collection $collection): array + { + $return = []; + + /** @var WebhookTriggerModel $item */ + foreach ($collection as $item) { + $return[] = $item->title; + } + + return array_unique($return); + } } diff --git a/app/Handlers/Events/AdminEventHandler.php b/app/Handlers/Events/AdminEventHandler.php index 5fe258067c..6619763806 100644 --- a/app/Handlers/Events/AdminEventHandler.php +++ b/app/Handlers/Events/AdminEventHandler.php @@ -32,7 +32,6 @@ use FireflyIII\Notifications\Admin\UserInvitation; use FireflyIII\Notifications\Admin\VersionCheckResult; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; use FireflyIII\Notifications\Test\OwnerTestNotificationEmail; -use FireflyIII\Notifications\Test\OwnerTestNotificationNtfy; use FireflyIII\Notifications\Test\OwnerTestNotificationPushover; use FireflyIII\Notifications\Test\OwnerTestNotificationSlack; use Illuminate\Support\Facades\Log; @@ -140,10 +139,10 @@ class AdminEventHandler break; - case 'ntfy': - $class = OwnerTestNotificationNtfy::class; - - break; + // case 'ntfy': + // $class = OwnerTestNotificationNtfy::class; + // + // break; case 'pushover': $class = OwnerTestNotificationPushover::class; diff --git a/app/Handlers/Events/BillEventHandler.php b/app/Handlers/Events/BillEventHandler.php index 6a96bf29a7..9969bfbf1c 100644 --- a/app/Handlers/Events/BillEventHandler.php +++ b/app/Handlers/Events/BillEventHandler.php @@ -34,6 +34,8 @@ use FireflyIII\Support\Facades\Preferences; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; +use function Safe\json_encode; + /** * Class BillEventHandler */ @@ -108,10 +110,10 @@ class BillEventHandler { Log::debug(sprintf('Now in %s', __METHOD__)); - $bill = $event->bill; + $bill = $event->bill; /** @var bool $preference */ - Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data; + $preference = Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data; if (true === $preference) { Log::debug('Bill reminder is true!'); diff --git a/app/Handlers/Events/DestroyedGroupEventHandler.php b/app/Handlers/Events/DestroyedGroupEventHandler.php index fb5baf4c22..638cbc8e76 100644 --- a/app/Handlers/Events/DestroyedGroupEventHandler.php +++ b/app/Handlers/Events/DestroyedGroupEventHandler.php @@ -52,10 +52,10 @@ class DestroyedGroupEventHandler /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); $engine->setUser($user); - $engine->setObjects(new Collection([$group])); + $engine->setObjects(new Collection()->push($group)); $engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION); $engine->generateMessages(); - + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); } diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index a5f806ae0c..367c4b4d09 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -116,11 +116,12 @@ class StoredGroupEventHandler // tell the generator which trigger it should look for $engine->setTrigger(WebhookTrigger::STORE_TRANSACTION); // tell the generator which objects to process - $engine->setObjects(new Collection([$group])); + $engine->setObjects(new Collection()->push($group)); // tell the generator to generate the messages $engine->generateMessages(); // trigger event to send them: + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); } } diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index b25c913d0b..973442abb3 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -163,10 +163,11 @@ class UpdatedGroupEventHandler /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); $engine->setUser($user); - $engine->setObjects(new Collection([$group])); + $engine->setObjects(new Collection()->push($group)); $engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION); $engine->generateMessages(); + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); } diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index ebe0cca9f6..991f7751d9 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -44,7 +44,6 @@ use FireflyIII\Models\UserRole; use FireflyIII\Notifications\Admin\UserRegistration as AdminRegistrationNotification; use FireflyIII\Notifications\Security\UserFailedLoginAttempt; use FireflyIII\Notifications\Test\UserTestNotificationEmail; -use FireflyIII\Notifications\Test\UserTestNotificationNtfy; use FireflyIII\Notifications\Test\UserTestNotificationPushover; use FireflyIII\Notifications\Test\UserTestNotificationSlack; use FireflyIII\Notifications\User\UserLogin; @@ -129,7 +128,7 @@ class UserEventHandler $groupTitle = $user->email; $index = 1; - /** @var UserGroup $group */ + /** @var null|UserGroup $group */ $group = null; // create a new group. @@ -411,10 +410,10 @@ class UserEventHandler break; - case 'ntfy': - $class = UserTestNotificationNtfy::class; - - break; + // case 'ntfy': + // $class = UserTestNotificationNtfy::class; + // + // break; case 'pushover': $class = UserTestNotificationPushover::class; diff --git a/app/Handlers/Events/WebhookEventHandler.php b/app/Handlers/Events/WebhookEventHandler.php index f48adef9df..adad80b942 100644 --- a/app/Handlers/Events/WebhookEventHandler.php +++ b/app/Handlers/Events/WebhookEventHandler.php @@ -40,7 +40,7 @@ class WebhookEventHandler { Log::debug(sprintf('Now in %s', __METHOD__)); if (false === config('firefly.feature_flags.webhooks') || false === config('firefly.allow_webhooks')) { - Log::info('Webhook event handler is disabled, do not run sendWebhookMessages().'); + Log::debug('Webhook event handler is disabled, do not run sendWebhookMessages().'); return; } diff --git a/app/Handlers/Observer/BudgetLimitObserver.php b/app/Handlers/Observer/BudgetLimitObserver.php index f6fcc85f03..ae6b1aac6a 100644 --- a/app/Handlers/Observer/BudgetLimitObserver.php +++ b/app/Handlers/Observer/BudgetLimitObserver.php @@ -53,6 +53,7 @@ class BudgetLimitObserver $engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT); $engine->generateMessages(); + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); } @@ -90,6 +91,7 @@ class BudgetLimitObserver $engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT); $engine->generateMessages(); + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); } } diff --git a/app/Handlers/Observer/BudgetObserver.php b/app/Handlers/Observer/BudgetObserver.php index 51a87b864f..d7366d3a4b 100644 --- a/app/Handlers/Observer/BudgetObserver.php +++ b/app/Handlers/Observer/BudgetObserver.php @@ -54,7 +54,7 @@ class BudgetObserver $engine->setObjects(new Collection()->push($budget)); $engine->setTrigger(WebhookTrigger::STORE_BUDGET); $engine->generateMessages(); - + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); } @@ -69,7 +69,7 @@ class BudgetObserver $engine->setObjects(new Collection()->push($budget)); $engine->setTrigger(WebhookTrigger::UPDATE_BUDGET); $engine->generateMessages(); - + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); } @@ -85,7 +85,7 @@ class BudgetObserver $engine->setObjects(new Collection()->push($budget)); $engine->setTrigger(WebhookTrigger::DESTROY_BUDGET); $engine->generateMessages(); - + Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); event(new RequestedSendWebhookMessages()); $repository = app(AttachmentRepositoryInterface::class); diff --git a/app/Handlers/Observer/TransactionObserver.php b/app/Handlers/Observer/TransactionObserver.php index a5c5fefd73..6c720f6f3f 100644 --- a/app/Handlers/Observer/TransactionObserver.php +++ b/app/Handlers/Observer/TransactionObserver.php @@ -72,7 +72,7 @@ class TransactionObserver } $transaction->saveQuietly(); - Log::debug('Transaction primary currency amounts are updated.'); + Log::debug(sprintf('Transaction #%d primary currency amounts are updated.', $transaction->id)); } public function deleting(?Transaction $transaction): void diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index 28194b7e03..145d39063a 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -35,6 +35,8 @@ use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Illuminate\Support\MessageBag; +use Safe\Exceptions\FileinfoException; +use Safe\Exceptions\FilesystemException; use Symfony\Component\HttpFoundation\File\UploadedFile; use function Safe\tmpfile; @@ -126,9 +128,11 @@ 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.'); + + try { + $resource = tmpfile(); + } catch (FilesystemException $e) { + Log::error(sprintf('Cannot create temp-file for file upload: %s', $e->getMessage())); return false; } @@ -141,17 +145,20 @@ class AttachmentHelper implements AttachmentHelperInterface $path = stream_get_meta_data($resource)['uri']; Log::debug(sprintf('Path is %s', $path)); - $result = fwrite($resource, $content); - if (false === $result) { - Log::error('Could not write temp file.'); + + try { + $result = fwrite($resource, $content); + } catch (FilesystemException $e) { + Log::error(sprintf('Could not write to temp file: %s', $e->getMessage())); 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); + + try { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + } catch (FileinfoException $e) { + Log::error(sprintf('Could not open finfo handler: %s', $e->getMessage())); return false; } @@ -171,7 +178,7 @@ class AttachmentHelper implements AttachmentHelperInterface $this->uploadDisk->put($file, $content); // update attachment. - $attachment->md5 = (string) md5_file($path); + $attachment->md5 = md5_file($path); $attachment->mime = $mime; $attachment->size = strlen($content); $attachment->uploaded = true; @@ -233,7 +240,7 @@ class AttachmentHelper implements AttachmentHelperInterface $attachment = new Attachment(); // create Attachment object. $attachment->user()->associate($user); $attachment->attachable()->associate($model); - $attachment->md5 = (string) md5_file($file->getRealPath()); + $attachment->md5 = md5_file($file->getRealPath()); $attachment->filename = $file->getClientOriginalName(); $attachment->mime = $file->getMimeType(); $attachment->size = $file->getSize(); diff --git a/app/Helpers/Collector/Extensions/AttachmentCollection.php b/app/Helpers/Collector/Extensions/AttachmentCollection.php index 7738fd305f..f4993d2d18 100644 --- a/app/Helpers/Collector/Extensions/AttachmentCollection.php +++ b/app/Helpers/Collector/Extensions/AttachmentCollection.php @@ -94,7 +94,7 @@ trait AttachmentCollection static function (EloquentBuilder $q1): void { // @phpstan-ignore-line $q1->where('attachments.attachable_type', TransactionJournal::class); // $q1->where('attachments.uploaded', true); - $q1->whereNull('attachments.deleted_at'); + // $q1->whereNull('attachments.deleted_at'); $q1->orWhereNull('attachments.attachable_type'); } ) @@ -107,6 +107,7 @@ trait AttachmentCollection $this->fields[] = 'attachments.id as attachment_id'; $this->fields[] = 'attachments.filename as attachment_filename'; $this->fields[] = 'attachments.title as attachment_title'; + $this->fields[] = 'attachments.deleted_at as attachment_deleted_at'; $this->fields[] = 'attachments.uploaded as attachment_uploaded'; $this->joinAttachmentTables(); diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index 5e6dd7e85c..11a15ef799 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Collector\Extensions; +use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget; @@ -199,7 +200,7 @@ trait MetaCollection public function excludeInternalReference(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -220,7 +221,7 @@ trait MetaCollection public function externalIdContains(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -232,7 +233,7 @@ trait MetaCollection public function externalIdDoesNotContain(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -244,7 +245,7 @@ trait MetaCollection public function externalIdDoesNotEnd(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -256,7 +257,7 @@ trait MetaCollection public function externalIdDoesNotStart(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -268,7 +269,7 @@ trait MetaCollection public function externalIdEnds(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -280,7 +281,7 @@ trait MetaCollection public function externalIdStarts(string $externalId): GroupCollectorInterface { - $externalId = (string) json_encode($externalId); + $externalId = json_encode($externalId); $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); $this->joinMetaDataTables(); @@ -293,7 +294,7 @@ trait MetaCollection public function externalUrlContains(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = json_encode($url); $url = str_replace('\\', '\\\\', trim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->whereLike('journal_meta.data', sprintf('%%%s%%', $url)); @@ -304,7 +305,7 @@ trait MetaCollection public function externalUrlDoesNotContain(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = json_encode($url); $url = str_replace('\\', '\\\\', trim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->whereNotLike('journal_meta.data', sprintf('%%%s%%', $url)); @@ -315,7 +316,7 @@ trait MetaCollection public function externalUrlDoesNotEnd(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = json_encode($url); $url = str_replace('\\', '\\\\', ltrim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->whereNotLike('journal_meta.data', sprintf('%%%s', $url)); @@ -326,7 +327,7 @@ trait MetaCollection public function externalUrlDoesNotStart(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = json_encode($url); $url = str_replace('\\', '\\\\', rtrim($url, '"')); // var_dump($url); @@ -339,7 +340,7 @@ trait MetaCollection public function externalUrlEnds(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = json_encode($url); $url = str_replace('\\', '\\\\', ltrim($url, '"')); $this->query->where('journal_meta.name', '=', 'external_url'); $this->query->whereLike('journal_meta.data', sprintf('%%%s', $url)); @@ -350,7 +351,7 @@ trait MetaCollection public function externalUrlStarts(string $url): GroupCollectorInterface { $this->joinMetaDataTables(); - $url = (string) json_encode($url); + $url = json_encode($url); $url = str_replace('\\', '\\\\', rtrim($url, '"')); // var_dump($url); @@ -401,7 +402,7 @@ trait MetaCollection public function internalReferenceContains(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); // var_dump($internalReference); // exit; @@ -416,7 +417,7 @@ trait MetaCollection public function internalReferenceDoesNotContain(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -429,7 +430,7 @@ trait MetaCollection public function internalReferenceDoesNotEnd(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -442,7 +443,7 @@ trait MetaCollection public function internalReferenceDoesNotStart(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -455,7 +456,7 @@ trait MetaCollection public function internalReferenceEnds(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -468,7 +469,7 @@ trait MetaCollection public function internalReferenceStarts(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -597,16 +598,16 @@ trait MetaCollection $foundTagCount = 0; foreach ($object['transactions'] as $transaction) { $transactionTagCount = count($transaction['tags']); - app('log')->debug(sprintf('Transaction #%d has %d tag(s)', $transaction['transaction_journal_id'], $transactionTagCount)); + Log::debug(sprintf('Transaction #%d has %d tag(s)', $transaction['transaction_journal_id'], $transactionTagCount)); if ($transactionTagCount < $expectedTagCount) { - app('log')->debug(sprintf('Transaction has %d tag(s), we expect %d tag(s), return false.', $transactionTagCount, $expectedTagCount)); + Log::debug(sprintf('Transaction has %d tag(s), we expect %d tag(s), return false.', $transactionTagCount, $expectedTagCount)); return false; } foreach ($transaction['tags'] as $tag) { Log::debug(sprintf('"%s" versus', strtolower((string) $tag['name'])), $list); if (in_array(strtolower((string) $tag['name']), $list, true)) { - app('log')->debug(sprintf('Transaction has tag "%s" so count++.', $tag['name'])); + Log::debug(sprintf('Transaction has tag "%s" so count++.', $tag['name'])); ++$foundTagCount; $journalId = $transaction['transaction_journal_id']; // #8377 prevent adding a transaction twice when multiple tag searches find this transaction @@ -724,7 +725,7 @@ trait MetaCollection public function setInternalReference(string $internalReference): GroupCollectorInterface { - $internalReference = (string) json_encode($internalReference); + $internalReference = json_encode($internalReference); $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); $this->joinMetaDataTables(); @@ -761,7 +762,7 @@ trait MetaCollection public function setTag(Tag $tag): GroupCollectorInterface { $this->withTagInformation(); - $this->setTags(new Collection([$tag])); + $this->setTags(new Collection()->push($tag)); return $this; } @@ -775,6 +776,9 @@ trait MetaCollection $this->withTagInformation(); $this->query->whereNotNull('tag_transaction_journal.tag_id'); + // Added this while fixing #10898, not sure why a post filter was ever necessary. + $this->query->whereIn('tag_transaction_journal.tag_id', $tags->pluck('id')->toArray()); + // this method adds a "postFilter" to the collector. $list = $tags->pluck('tag')->toArray(); $list = array_map('strtolower', $list); @@ -784,13 +788,13 @@ trait MetaCollection foreach ($transaction['tags'] as $tag) { Log::debug(sprintf('"%s" versus', strtolower((string) $tag['name'])), $list); if (in_array(strtolower((string) $tag['name']), $list, true)) { - app('log')->debug(sprintf('Transaction has tag "%s" so return true.', $tag['name'])); + Log::debug(sprintf('Transaction has tag "%s" so return true.', $tag['name'])); return true; } } } - app('log')->debug('Transaction has no tags from the list, so return false.'); + Log::debug('Transaction has no tags from the list, so return false.'); return false; }; @@ -812,11 +816,11 @@ trait MetaCollection $filter = static function (array $object) use ($list): bool { Log::debug(sprintf('Now in setWithoutSpecificTags(%s) filter', implode(', ', $list))); foreach ($object['transactions'] as $transaction) { - app('log')->debug(sprintf('Transaction has %d tag(s)', count($transaction['tags']))); + Log::debug(sprintf('Transaction has %d tag(s)', count($transaction['tags']))); foreach ($transaction['tags'] as $tag) { Log::debug(sprintf('"%s" versus', strtolower((string) $tag['name'])), $list); if (in_array(strtolower((string) $tag['name']), $list, true)) { - app('log')->debug(sprintf('Transaction has tag "%s", but should not have it, return false.', $tag['name'])); + Log::debug(sprintf('Transaction has tag "%s", but should not have it, return false.', $tag['name'])); return false; } @@ -919,6 +923,8 @@ trait MetaCollection { $this->withCategoryInformation(); $this->query->whereNull('category_transaction_journal.category_id'); + // better fix for #10507 + $this->query->whereNotIn('transaction_types.type', [TransactionTypeEnum::OPENING_BALANCE->value]); return $this; } diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 59b2748730..0d12819740 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -26,6 +26,7 @@ namespace FireflyIII\Helpers\Collector; use Carbon\Carbon; use Carbon\Exceptions\InvalidFormatException; +use Closure; use Exception; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; @@ -45,7 +46,6 @@ use Illuminate\Database\Query\JoinClause; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -use Closure; use Override; use function Safe\json_decode; @@ -303,7 +303,7 @@ class GroupCollector implements GroupCollectorInterface foreach ($params as $param) { $replace = sprintf('"%s"', $param); if (is_int($param)) { - $replace = (string) $param; + $replace = (string)$param; } $pos = strpos($query, '?'); if (false !== $pos) { @@ -518,13 +518,13 @@ class GroupCollector implements GroupCollectorInterface /** @var TransactionJournal $augumentedJournal */ foreach ($collection as $augumentedJournal) { - $groupId = (int) $augumentedJournal->transaction_group_id; + $groupId = (int)$augumentedJournal->transaction_group_id; if (!array_key_exists($groupId, $groups)) { // make new array $parsedGroup = $this->parseAugmentedJournal($augumentedJournal); $groupArray = [ - 'id' => (int) $augumentedJournal->transaction_group_id, + 'id' => (int)$augumentedJournal->transaction_group_id, 'user_id' => $augumentedJournal->user_id, 'user_group_id' => $augumentedJournal->user_group_id, // Field transaction_group_title was added by the query. @@ -537,7 +537,7 @@ class GroupCollector implements GroupCollectorInterface 'transactions' => [], ]; // Field transaction_journal_id was added by the query. - $journalId = (int) $augumentedJournal->transaction_journal_id; + $journalId = (int)$augumentedJournal->transaction_journal_id; $groupArray['transactions'][$journalId] = $parsedGroup; $groups[$groupId] = $groupArray; @@ -545,7 +545,7 @@ class GroupCollector implements GroupCollectorInterface } // or parse the rest. // Field transaction_journal_id was added by the query. - $journalId = (int) $augumentedJournal->transaction_journal_id; + $journalId = (int)$augumentedJournal->transaction_journal_id; if (array_key_exists($journalId, $groups[$groupId]['transactions'])) { // append data to existing group + journal (for multiple tags or multiple attachments) $groups[$groupId]['transactions'][$journalId] = $this->mergeTags($groups[$groupId]['transactions'][$journalId], $augumentedJournal); @@ -560,7 +560,7 @@ class GroupCollector implements GroupCollectorInterface } $groups = $this->parseSums($groups); - return new Collection($groups); + return new Collection()->push(...$groups); } /** @@ -597,8 +597,8 @@ class GroupCollector implements GroupCollectorInterface $dates = ['interest_date', 'payment_date', 'invoice_date', 'book_date', 'due_date', 'process_date']; if (array_key_exists('meta_name', $result) && in_array($result['meta_name'], $dates, true)) { $name = $result['meta_name']; - if (array_key_exists('meta_data', $result) && '' !== (string) $result['meta_data']) { - $result[$name] = Carbon::createFromFormat('!Y-m-d', substr((string) json_decode((string) $result['meta_data']), 0, 10)); + if (array_key_exists('meta_data', $result) && '' !== (string)$result['meta_data']) { + $result[$name] = Carbon::createFromFormat('!Y-m-d', substr((string)json_decode((string)$result['meta_data']), 0, 10)); } } @@ -611,9 +611,9 @@ class GroupCollector implements GroupCollectorInterface // convert back to strings because SQLite is dumb like that. $result = $this->convertToStrings($result); - $result['reconciled'] = 1 === (int) $result['reconciled']; + $result['reconciled'] = 1 === (int)$result['reconciled']; if (array_key_exists('tag_id', $result) && null !== $result['tag_id']) { // assume the other fields are present as well. - $tagId = (int) $augumentedJournal['tag_id']; + $tagId = (int)$augumentedJournal['tag_id']; $tagDate = null; try { @@ -623,7 +623,7 @@ class GroupCollector implements GroupCollectorInterface } $result['tags'][$tagId] = [ - 'id' => (int) $result['tag_id'], + 'id' => (int)$result['tag_id'], 'name' => $result['tag_name'], 'date' => $tagDate, 'description' => $result['tag_description'], @@ -631,10 +631,11 @@ class GroupCollector implements GroupCollectorInterface } // also merge attachments: - if (array_key_exists('attachment_id', $result)) { - $uploaded = 1 === (int) $result['attachment_uploaded']; - $attachmentId = (int) $augumentedJournal['attachment_id']; - if (0 !== $attachmentId && $uploaded) { + if (array_key_exists('attachment_id', $result) && null !== $result['attachment_id']) { + $uploaded = 1 === (int)$result['attachment_uploaded']; + $attachmentId = (int)$augumentedJournal['attachment_id']; + $deleted = null !== $result['attachment_deleted_at']; + if (0 !== $attachmentId && $uploaded && !$deleted) { $result['attachments'][$attachmentId] = [ 'id' => $attachmentId, 'filename' => $augumentedJournal['attachment_filename'], @@ -659,7 +660,7 @@ class GroupCollector implements GroupCollectorInterface private function convertToInteger(array $array): array { foreach ($this->integerFields as $field) { - $array[$field] = array_key_exists($field, $array) ? (int) $array[$field] : null; + $array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (int)$array[$field] : null; } return $array; @@ -668,7 +669,7 @@ class GroupCollector implements GroupCollectorInterface private function convertToBoolean(array $array): array { foreach ($this->booleanFields as $field) { - $array[$field] = array_key_exists($field, $array) ? (bool) $array[$field] : null; + $array[$field] = array_key_exists($field, $array) ? (bool)$array[$field] : null; } return $array; @@ -677,7 +678,7 @@ class GroupCollector implements GroupCollectorInterface private function convertToStrings(array $array): array { foreach ($this->stringFields as $field) { - $array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string) $array[$field] : null; + $array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string)$array[$field] : null; } return $array; @@ -687,7 +688,7 @@ class GroupCollector implements GroupCollectorInterface { $newArray = $newJournal->toArray(); if (array_key_exists('tag_id', $newArray)) { // assume the other fields are present as well. - $tagId = (int) $newJournal['tag_id']; + $tagId = (int)$newJournal['tag_id']; $tagDate = null; @@ -698,7 +699,7 @@ class GroupCollector implements GroupCollectorInterface } $existingJournal['tags'][$tagId] = [ - 'id' => (int) $newArray['tag_id'], + 'id' => (int)$newArray['tag_id'], 'name' => $newArray['tag_name'], 'date' => $tagDate, 'description' => $newArray['tag_description'], @@ -712,7 +713,7 @@ class GroupCollector implements GroupCollectorInterface { $newArray = $newJournal->toArray(); if (array_key_exists('attachment_id', $newArray)) { - $attachmentId = (int) $newJournal['attachment_id']; + $attachmentId = (int)$newJournal['attachment_id']; $existingJournal['attachments'][$attachmentId] = [ 'id' => $attachmentId, @@ -731,13 +732,13 @@ class GroupCollector implements GroupCollectorInterface foreach ($groups as $groudId => $group) { /** @var array $transaction */ foreach ($group['transactions'] as $transaction) { - $currencyId = (int) $transaction['currency_id']; + $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)); } - $pcAmount = (string) ('' === $transaction['pc_amount'] ? '0' : $transaction['pc_amount']); - $pcForeignAmount = (string) ('' === $transaction['pc_foreign_amount'] ? '0' : $transaction['pc_foreign_amount']); - $foreignAmount = (string) ('' === $transaction['foreign_amount'] ? '0' : $transaction['foreign_amount']); + $pcAmount = (string)('' === $transaction['pc_amount'] ? '0' : $transaction['pc_amount']); + $pcForeignAmount = (string)('' === $transaction['pc_foreign_amount'] ? '0' : $transaction['pc_foreign_amount']); + $foreignAmount = (string)('' === $transaction['foreign_amount'] ? '0' : $transaction['foreign_amount']); // set default: if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) { @@ -748,11 +749,11 @@ class GroupCollector implements GroupCollectorInterface $groups[$groudId]['sums'][$currencyId]['amount'] = '0'; $groups[$groudId]['sums'][$currencyId]['pc_amount'] = '0'; } - $groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string) $groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']); - $groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd((string) $groups[$groudId]['sums'][$currencyId]['pc_amount'], $pcAmount); + $groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']); + $groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['pc_amount'], $pcAmount); if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) { - $currencyId = (int) $transaction['foreign_currency_id']; + $currencyId = (int)$transaction['foreign_currency_id']; // set default: if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) { @@ -763,7 +764,7 @@ class GroupCollector implements GroupCollectorInterface $groups[$groudId]['sums'][$currencyId]['amount'] = '0'; $groups[$groudId]['sums'][$currencyId]['pc_amount'] = '0'; } - $groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string) $groups[$groudId]['sums'][$currencyId]['amount'], $foreignAmount); + $groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['amount'], $foreignAmount); $groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $pcForeignAmount); } } @@ -851,7 +852,7 @@ class GroupCollector implements GroupCollectorInterface */ public function getPaginatedGroups(): LengthAwarePaginator { - $set = $this->getGroups(); + $set = $this->getGroups(); if (0 === $this->limit) { $this->setLimit(50); } @@ -861,8 +862,9 @@ class GroupCollector implements GroupCollectorInterface return new LengthAwarePaginator($set, $this->total, $total, 1); } + $limit = $this->limit ?? 1; - return new LengthAwarePaginator($set, $this->total, $this->limit, $this->page); + return new LengthAwarePaginator($set, $this->total, $limit, $this->page); } /** @@ -1094,10 +1096,6 @@ class GroupCollector implements GroupCollectorInterface ->whereNull('transaction_groups.deleted_at') ->whereNull('transaction_journals.deleted_at') ->whereNull('source.deleted_at') - - // #10507 ignore opening balance. - ->where('transaction_types.type', '!=', TransactionTypeEnum::OPENING_BALANCE->value) - ->whereNotNull('transaction_groups.id') ->whereNull('destination.deleted_at') ->orderBy('transaction_journals.date', 'DESC') diff --git a/app/Helpers/Report/NetWorth.php b/app/Helpers/Report/NetWorth.php index c3a2f0592c..bb58b8761b 100644 --- a/app/Helpers/Report/NetWorth.php +++ b/app/Helpers/Report/NetWorth.php @@ -31,7 +31,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Steam; @@ -49,9 +48,8 @@ use Illuminate\Support\Facades\Log; class NetWorth implements NetWorthInterface { private AccountRepositoryInterface $accountRepository; - private CurrencyRepositoryInterface $currencyRepos; private User $user; // @phpstan-ignore-line - private ?UserGroup $userGroup = null; // @phpstan-ignore-line + private ?UserGroup $userGroup = null; /** * This method collects the user's net worth in ALL the user's currencies @@ -116,7 +114,7 @@ class NetWorth implements NetWorthInterface return $netWorth; } - public function setUser(null|Authenticatable|User $user): void + public function setUser(Authenticatable|User|null $user): void { if (!$user instanceof User) { return; @@ -131,8 +129,6 @@ class NetWorth implements NetWorthInterface $this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository->setUserGroup($userGroup); - $this->currencyRepos = app(CurrencyRepositoryInterface::class); - $this->currencyRepos->setUserGroup($this->userGroup); } #[Deprecated] diff --git a/app/Helpers/Report/NetWorthInterface.php b/app/Helpers/Report/NetWorthInterface.php index a73cd53b4d..466a055a4c 100644 --- a/app/Helpers/Report/NetWorthInterface.php +++ b/app/Helpers/Report/NetWorthInterface.php @@ -46,7 +46,7 @@ interface NetWorthInterface */ public function byAccounts(Collection $accounts, Carbon $date): array; - public function setUser(null|Authenticatable|User $user): void; + public function setUser(Authenticatable|User|null $user): void; public function setUserGroup(UserGroup $userGroup): void; diff --git a/app/Helpers/Report/PopupReport.php b/app/Helpers/Report/PopupReport.php index aa21319780..414cd57d78 100644 --- a/app/Helpers/Report/PopupReport.php +++ b/app/Helpers/Report/PopupReport.php @@ -45,7 +45,7 @@ class PopupReport implements PopupReportInterface { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts(new Collection([$account])) + $collector->setAccounts(new Collection()->push($account)) ->withAccountInformation() ->withBudgetInformation() ->withCategoryInformation() @@ -72,7 +72,7 @@ class PopupReport implements PopupReportInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector - ->setAccounts(new Collection([$account])) + ->setAccounts(new Collection()->push($account)) ->setTypes([TransactionTypeEnum::WITHDRAWAL->value]) ->withAccountInformation() ->withCategoryInformation() @@ -191,7 +191,7 @@ class PopupReport implements PopupReportInterface // $set = $attributes['accounts'] ?? new Collection; // $set->push($account); - $collector->setDestinationAccounts(new Collection([$account])) + $collector->setDestinationAccounts(new Collection()->push($account)) ->setRange($attributes['startDate'], $attributes['endDate']) ->withAccountInformation() ->withBudgetInformation() @@ -218,7 +218,7 @@ class PopupReport implements PopupReportInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector - ->setSourceAccounts(new Collection([$account])) + ->setSourceAccounts(new Collection()->push($account)) ->setDestinationAccounts($attributes['accounts']) ->setRange($attributes['startDate'], $attributes['endDate']) ->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value]) diff --git a/app/Http/Controllers/Account/CreateController.php b/app/Http/Controllers/Account/CreateController.php index 9098c4e2fa..743c2fdc7a 100644 --- a/app/Http/Controllers/Account/CreateController.php +++ b/app/Http/Controllers/Account/CreateController.php @@ -86,7 +86,7 @@ class CreateController extends Controller 'latitude' => $hasOldInput ? old('location_latitude') : config('firefly.default_location.latitude'), 'longitude' => $hasOldInput ? old('location_longitude') : config('firefly.default_location.longitude'), 'zoom_level' => $hasOldInput ? old('location_zoom_level') : config('firefly.default_location.zoom_level'), - 'has_location' => $hasOldInput ? 'true' === old('location_has_location') : false, + 'has_location' => $hasOldInput && 'true' === old('location_has_location'), ], ]; $liabilityDirections = [ @@ -106,7 +106,7 @@ class CreateController extends Controller 'preFilled', [ 'currency_id' => $this->primaryCurrency->id, - 'include_net_worth' => $hasOldInput ? (bool) $request->old('include_net_worth') : true, + 'include_net_worth' => !$hasOldInput || (bool)$request->old('include_net_worth'), ] ); // issue #8321 diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index 7171291390..4b77635b4b 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -143,7 +143,7 @@ class ShowController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector - ->setAccounts(new Collection([$account])) + ->setAccounts(new Collection()->push($account)) ->setLimit($pageSize) ->setPage($page) ->withAttachmentInformation() @@ -158,18 +158,8 @@ class ShowController extends Controller Log::debug('End collect transactions'); $timer->stop('collection'); - - // enrich data in arrays. - - // enrich - // $enrichment = new TransactionGroupEnrichment(); - // $enrichment->setUser(auth()->user()); - // $groups->setCollection($enrichment->enrich($groups->getCollection())); - - $groups->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')])); $showAll = false; - // correct $now = today()->endOfDay(); if ($now->gt($end) || $now->lt($start)) { $now = $end; @@ -231,7 +221,7 @@ class ShowController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page)->withAccountInformation()->withCategoryInformation(); + $collector->setAccounts(new Collection()->push($account))->setLimit($pageSize)->setPage($page)->withAccountInformation()->withCategoryInformation(); // this search will not include transaction groups where this asset account (or liability) // is just part of ONE of the journals. To force this: diff --git a/app/Http/Controllers/Admin/NotificationController.php b/app/Http/Controllers/Admin/NotificationController.php index 39823066d1..b8e52ebbe6 100644 --- a/app/Http/Controllers/Admin/NotificationController.php +++ b/app/Http/Controllers/Admin/NotificationController.php @@ -70,6 +70,7 @@ class NotificationController extends Controller } $forcedAvailability['ntfy'] = '' !== $ntfyTopic; $forcedAvailability['pushover'] = '' !== $pushoverAppToken && '' !== $pushoverUserToken; + $forcedAvailability['slack'] = '' !== $slackUrl; return view( 'settings.notifications.index', diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index dc292cc570..78fcc649db 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -33,6 +33,7 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\View\View; +use Safe\Exceptions\UrlException; use function Safe\parse_url; @@ -103,11 +104,15 @@ class ForgotPasswordController extends Controller */ private function validateHost(): void { - $configuredHost = parse_url((string) config('app.url'), PHP_URL_HOST); - if (false === $configuredHost || null === $configuredHost) { + try { + $configuredHost = parse_url((string)config('app.url'), PHP_URL_HOST); + } catch (UrlException $e) { + throw new FireflyException('Please set a valid and correct Firefly III URL in the APP_URL environment variable.', 0, $e); + } + if (!is_string($configuredHost)) { throw new FireflyException('Please set a valid and correct Firefly III URL in the APP_URL environment variable.'); } - $host = request()->host(); + $host = request()->host(); if ($configuredHost !== $host) { Log::error(sprintf('Host header is "%s", APP_URL is "%s".', $host, $configuredHost)); diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 19a1b37764..c1566b5ed1 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -249,8 +249,8 @@ class LoginController extends Controller $allowReset = false; } - $email = $request?->old('email'); - $remember = $request?->old('remember'); + $email = $request->old('email'); + $remember = $request->old('remember'); $storeInCookie = config('google2fa.store_in_cookie', false); if (false !== $storeInCookie) { diff --git a/app/Http/Controllers/Bill/CreateController.php b/app/Http/Controllers/Bill/CreateController.php index 401ead7c6d..ae97eefa84 100644 --- a/app/Http/Controllers/Bill/CreateController.php +++ b/app/Http/Controllers/Bill/CreateController.php @@ -69,15 +69,14 @@ class CreateController extends Controller */ public function create(Request $request) { - $periods = []; + $periods = []; /** @var array $billPeriods */ - $billPeriods = config('firefly.bill_periods'); + $billPeriods = config('firefly.bill_periods'); foreach ($billPeriods as $current) { $periods[$current] = (string) trans('firefly.repeat_freq_'.$current); } - $subTitle = (string) trans('firefly.create_new_bill'); - $primaryCurrency = $this->primaryCurrency; + $subTitle = (string) trans('firefly.create_new_bill'); // put previous url in session if not redirect from store (not "create another"). if (true !== session('bills.create.fromStore')) { @@ -85,7 +84,7 @@ class CreateController extends Controller } $request->session()->forget('bills.create.fromStore'); - return view('bills.create', compact('periods', 'subTitle', 'primaryCurrency')); + return view('bills.create', compact('periods', 'subTitle')); } /** diff --git a/app/Http/Controllers/Bill/EditController.php b/app/Http/Controllers/Bill/EditController.php index de520dfc7f..e9515ef071 100644 --- a/app/Http/Controllers/Bill/EditController.php +++ b/app/Http/Controllers/Bill/EditController.php @@ -88,7 +88,6 @@ class EditController extends Controller $bill->amount_min = app('steam')->bcround($bill->amount_min, $bill->transactionCurrency->decimal_places); $bill->amount_max = app('steam')->bcround($bill->amount_max, $bill->transactionCurrency->decimal_places); $rules = $this->repository->getRulesForBill($bill); - $primaryCurrency = $this->primaryCurrency; // code to handle active-checkboxes $hasOldInput = null !== $request->old('_token'); @@ -105,7 +104,7 @@ class EditController extends Controller $request->session()->flash('preFilled', $preFilled); $request->session()->forget('bills.edit.fromUpdate'); - return view('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'primaryCurrency', 'preFilled')); + return view('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'preFilled')); } /** diff --git a/app/Http/Controllers/Budget/BudgetLimitController.php b/app/Http/Controllers/Budget/BudgetLimitController.php index 367bbba1ad..63e0c93e7e 100644 --- a/app/Http/Controllers/Budget/BudgetLimitController.php +++ b/app/Http/Controllers/Budget/BudgetLimitController.php @@ -203,7 +203,7 @@ class BudgetLimitController extends Controller if ($request->expectsJson()) { $array = $limit->toArray(); // add some extra metadata: - $spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $currency); + $spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency); $array['spent'] = $spentArr[$currency->id]['sum'] ?? '0'; $array['left_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, bcadd($array['spent'], (string) $array['amount'])); $array['amount_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $limit['amount']); @@ -264,7 +264,7 @@ class BudgetLimitController extends Controller $limit->start_date, $limit->end_date, null, - new Collection([$budgetLimit->budget]), + new Collection()->push($budgetLimit->budget), $budgetLimit->transactionCurrency ); $daysLeft = $this->activeDaysLeft($limit->start_date, $limit->end_date); diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index a370bbd00f..2ba33347a1 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -129,14 +129,12 @@ class IndexController extends Controller $spent = $spentArr[$this->primaryCurrency->id]['sum'] ?? '0'; unset($spentArr); } - // number of days for consistent budgeting. $activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description. $activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description. // get all inactive budgets, and simply list them: $inactive = $this->repository->getInactiveBudgets(); - $primaryCurrency = $this->primaryCurrency; return view( 'budgets.index', @@ -149,7 +147,6 @@ class IndexController extends Controller 'budgets', 'currencies', 'periodTitle', - 'primaryCurrency', 'activeDaysPassed', 'activeDaysLeft', 'inactive', @@ -238,7 +235,7 @@ class IndexController extends Controller /** @var TransactionCurrency $currency */ foreach ($currencies as $currency) { - $spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$current]), $currency, false); + $spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($current), $currency, false); if (array_key_exists($currency->id, $spentArr) && array_key_exists('sum', $spentArr[$currency->id])) { $array['spent'][$currency->id]['spent'] = $spentArr[$currency->id]['sum']; $array['spent'][$currency->id]['currency_id'] = $currency->id; diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 00f387d7c4..1e48b0d65f 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -37,6 +37,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\Facades\Steam; +use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\ChartGeneration; use FireflyIII\Support\Http\Controllers\DateCalculation; @@ -234,7 +235,7 @@ class AccountController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts(new Collection([$account])) + $collector->setAccounts(new Collection()->push($account)) ->setRange($start, $end) ->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]) ; @@ -321,7 +322,7 @@ class AccountController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); + $collector->setAccounts(new Collection()->push($account))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $journals = $collector->getExtractedJournals(); $result = []; $chartData = []; @@ -329,6 +330,7 @@ class AccountController extends Controller /** @var array $journal */ foreach ($journals as $journal) { $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); + $field = 'amount'; if (!array_key_exists($key, $result)) { // currency info: @@ -337,7 +339,6 @@ class AccountController extends Controller $currencySymbol = $journal['currency_symbol']; $currencyCode = $journal['currency_code']; $currencyDecimalPlaces = $journal['currency_decimal_places']; - $field = 'amount'; if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) { $field = 'pc_amount'; $currencyName = $this->primaryCurrency->name; @@ -428,7 +429,7 @@ class AccountController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionTypeEnum::DEPOSIT->value]); + $collector->setAccounts(new Collection()->push($account))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionTypeEnum::DEPOSIT->value]); $journals = $collector->getExtractedJournals(); $result = []; $chartData = []; @@ -436,6 +437,7 @@ class AccountController extends Controller /** @var array $journal */ foreach ($journals as $journal) { $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); + $field = 'amount'; if (!array_key_exists($key, $result)) { // currency info: @@ -444,7 +446,6 @@ class AccountController extends Controller $currencySymbol = $journal['currency_symbol']; $currencyCode = $journal['currency_code']; $currencyDecimalPlaces = $journal['currency_decimal_places']; - $field = 'amount'; if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) { $field = 'pc_amount'; $currencyName = $this->primaryCurrency->name; @@ -504,6 +505,7 @@ class AccountController extends Controller Log::debug(sprintf('Step is %s', $step)); $locale = Steam::getLocale(); $return = []; + $converter = new ExchangeRateConverter(); // fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041 // have to make sure this chart is always based on the balance at the END of the period. @@ -512,10 +514,10 @@ class AccountController extends Controller $current = app('navigation')->endOfX($current, $step, null); $format = (string)trans('config.month_and_day_js', [], $locale); $accountCurrency = $this->accountRepository->getAccountCurrency($account); - + Log::debug('Get and filter balance for entire range start'); $range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary); $range = Steam::filterAccountBalances($range, $account, $this->convertToPrimary, $accountCurrency); - + Log::debug('Get and filter balance for entire range end'); // temp, get end balance. Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String())); Steam::finalAccountBalance($account, $end); @@ -552,7 +554,15 @@ class AccountController extends Controller $carbon = Carbon::createFromFormat('Y-m-d', $newRange[$expectedIndex]['date'])->endOfDay(); } } - Log::debug(sprintf('momentBalance is now %s', json_encode($momentBalance))); + Log::debug(sprintf('momentBalance[%s] is now %s', $current->format('Y-m-d H:i:s'), json_encode($momentBalance))); + + // check, perhaps recalculate the amount in currency X if the + if ($accountCurrency->id !== $this->primaryCurrency->id && $this->convertToPrimary && array_key_exists($accountCurrency->code, $momentBalance)) { + $converted = $converter->convert($accountCurrency, $this->primaryCurrency, $current, $momentBalance[$accountCurrency->code]); + $momentBalance['pc_balance'] = $converted; + } + + $return = $this->updateChartKeys($return, $momentBalance); $previous = $momentBalance; diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 49433fb8eb..a2d1ee0911 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -38,6 +38,7 @@ use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Chart\Budget\FrontpageChartGenerator; +use FireflyIII\Support\Facades\Navigation; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; @@ -99,17 +100,17 @@ class BudgetController extends Controller return response()->json($cache->get()); } $step = $this->calculateStep($start, $end); // depending on diff, do something with range of chart. - $collection = new Collection([$budget]); + $collection = new Collection()->push($budget); $chartData = []; $loopStart = clone $start; - $loopStart = app('navigation')->startOfPeriod($loopStart, $step); + $loopStart = Navigation::startOfPeriod($loopStart, $step); $currencies = []; $defaultEntries = []; while ($end >= $loopStart) { /** @var Carbon $loopEnd */ - $loopEnd = app('navigation')->endOfPeriod($loopStart, $step); + $loopEnd = Navigation::endOfPeriod($loopStart, $step); $spent = $this->opsRepository->sumExpenses($loopStart, $loopEnd, null, $collection); // this method already converts to primary currency. - $label = trim((string) app('navigation')->periodShow($loopStart, $step)); + $label = trim(Navigation::periodShow($loopStart, $step)); foreach ($spent as $row) { $currencyId = $row['currency_id']; @@ -168,7 +169,7 @@ class BudgetController extends Controller $locale = app('steam')->getLocale(); $entries = []; $amount = $budgetLimit->amount ?? '0'; - $budgetCollection = new Collection([$budget]); + $budgetCollection = new Collection()->push($budget); $currency = $budgetLimit->transactionCurrency; if ($this->convertToPrimary) { $amount = $budgetLimit->native_amount ?? $amount; @@ -496,8 +497,8 @@ class BudgetController extends Controller if ($cache->has()) { return response()->json($cache->get()); } - $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); - $preferredRange = app('navigation')->preferredRangeFormat($start, $end); + $titleFormat = Navigation::preferredCarbonLocalizedFormat($start, $end); + $preferredRange = Navigation::preferredRangeFormat($start, $end); $chartData = [ [ 'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency->name]), @@ -517,9 +518,9 @@ class BudgetController extends Controller $currentStart = clone $start; while ($currentStart <= $end) { - $currentStart = app('navigation')->startOfPeriod($currentStart, $preferredRange); + $currentStart = Navigation::startOfPeriod($currentStart, $preferredRange); $title = $currentStart->isoFormat($titleFormat); - $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); + $currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange); // default limit is no limit: $chartData[0]['entries'][$title] = 0; @@ -534,7 +535,7 @@ class BudgetController extends Controller } // get spent amount in this period for this currency. - $sum = $this->opsRepository->sumExpenses($currentStart, $currentEnd, $accounts, new Collection([$budget]), $currency); + $sum = $this->opsRepository->sumExpenses($currentStart, $currentEnd, $accounts, new Collection()->push($budget), $currency); $amount = app('steam')->positive($sum[$currency->id]['sum'] ?? '0'); $chartData[0]['entries'][$title] = app('steam')->bcround($amount, $currency->decimal_places); @@ -565,17 +566,17 @@ class BudgetController extends Controller } // the expenses: - $titleFormat = app('navigation')->preferredCarbonLocalizedFormat($start, $end); + $titleFormat = Navigation::preferredCarbonLocalizedFormat($start, $end); $chartData = []; $currentStart = clone $start; - $preferredRange = app('navigation')->preferredRangeFormat($start, $end); + $preferredRange = Navigation::preferredRangeFormat($start, $end); while ($currentStart <= $end) { - $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); + $currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange); $title = $currentStart->isoFormat($titleFormat); $sum = $this->nbRepository->sumExpenses($currentStart, $currentEnd, $accounts, $currency); $amount = app('steam')->positive($sum[$currency->id]['sum'] ?? '0'); $chartData[$title] = app('steam')->bcround($amount, $currency->decimal_places); - $currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0); + $currentStart = Navigation::addPeriod($currentStart, $preferredRange, 0); } $data = $this->generator->singleSet((string) trans('firefly.spent'), $chartData); diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php index aee1d2a958..9e66271b11 100644 --- a/app/Http/Controllers/Chart/BudgetReportController.php +++ b/app/Http/Controllers/Chart/BudgetReportController.php @@ -29,6 +29,8 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; +use FireflyIII\Support\Facades\Navigation; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\TransactionCalculation; use Illuminate\Http\JsonResponse; @@ -84,8 +86,8 @@ class BudgetReportController extends Controller 'currency_code' => $currency['currency_code'], ]; foreach ($budget['transaction_journals'] as $journal) { - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -114,8 +116,8 @@ class BudgetReportController extends Controller 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -144,8 +146,8 @@ class BudgetReportController extends Controller 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -161,8 +163,8 @@ class BudgetReportController extends Controller public function mainChart(Collection $accounts, Budget $budget, Carbon $start, Carbon $end): JsonResponse { $chartData = []; - $spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$budget])); - $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); + $spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection()->push($budget)); + $format = Navigation::preferredCarbonLocalizedFormat($start, $end); // loop expenses. foreach ($spent as $currency) { @@ -184,9 +186,9 @@ class BudgetReportController extends Controller foreach ($currency['budgets'] as $currentBudget) { foreach ($currentBudget['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); $chartData[$spentKey]['entries'][$key] ??= '0'; - $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], (string) $amount); + $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } } @@ -199,11 +201,11 @@ class BudgetReportController extends Controller private function makeEntries(Carbon $start, Carbon $end): array { $return = []; - $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); - $preferredRange = app('navigation')->preferredRangeFormat($start, $end); + $format = Navigation::preferredCarbonLocalizedFormat($start, $end); + $preferredRange = Navigation::preferredRangeFormat($start, $end); $currentStart = clone $start; while ($currentStart <= $end) { - $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange); + $currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange); $key = $currentStart->isoFormat($format); $return[$key] = '0'; $currentStart = clone $currentEnd; @@ -232,8 +234,8 @@ class BudgetReportController extends Controller 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 8c86235325..e22a6bd4d5 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -34,6 +34,7 @@ use FireflyIII\Repositories\Category\OperationsRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Chart\Category\FrontpageChartGenerator; use FireflyIII\Support\Chart\Category\WholePeriodChartGenerator; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\ChartGeneration; use FireflyIII\Support\Http\Controllers\DateCalculation; @@ -173,7 +174,7 @@ class CategoryController extends Controller $opsRepository = app(OperationsRepositoryInterface::class); $categoryId = $category->id; // this gives us all currencies - $collection = new Collection([$category]); + $collection = new Collection()->push($category); $expenses = $opsRepository->listExpenses($start, $end, $accounts, $collection); $income = $opsRepository->listIncome($start, $end, $accounts, $collection); } @@ -211,19 +212,19 @@ class CategoryController extends Controller // loop income and expenses for this category.: $outSet = $expenses[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []]; foreach ($outSet['transaction_journals'] as $journal) { - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); $date = $journal['date']->isoFormat($format); $chartData[$outKey]['entries'][$date] ??= '0'; - $chartData[$outKey]['entries'][$date] = bcadd((string) $amount, $chartData[$outKey]['entries'][$date]); + $chartData[$outKey]['entries'][$date] = bcadd($amount, $chartData[$outKey]['entries'][$date]); } $inSet = $income[$currencyId]['categories'][$categoryId] ?? ['transaction_journals' => []]; foreach ($inSet['transaction_journals'] as $journal) { - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); $date = $journal['date']->isoFormat($format); $chartData[$inKey]['entries'][$date] ??= '0'; - $chartData[$inKey]['entries'][$date] = bcadd((string) $amount, $chartData[$inKey]['entries'][$date]); + $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 14fc29bb7a..107509cece 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -28,6 +28,7 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\OperationsRepositoryInterface; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\TransactionCalculation; use Illuminate\Http\JsonResponse; @@ -82,8 +83,8 @@ class CategoryReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -109,8 +110,8 @@ class CategoryReportController extends Controller 'currency_code' => $currency['currency_code'], ]; foreach ($category['transaction_journals'] as $journal) { - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -137,8 +138,8 @@ class CategoryReportController extends Controller 'currency_code' => $currency['currency_code'], ]; foreach ($category['transaction_journals'] as $journal) { - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -165,8 +166,8 @@ class CategoryReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -193,8 +194,8 @@ class CategoryReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -207,8 +208,8 @@ class CategoryReportController extends Controller public function mainChart(Collection $accounts, Category $category, Carbon $start, Carbon $end): JsonResponse { $chartData = []; - $spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$category])); - $earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$category])); + $spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection()->push($category)); + $earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection()->push($category)); $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); // loop expenses. foreach ($spent as $currency) { @@ -230,9 +231,9 @@ class CategoryReportController extends Controller foreach ($currency['categories'] as $currentCategory) { foreach ($currentCategory['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); $chartData[$spentKey]['entries'][$key] ??= '0'; - $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], (string) $amount); + $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } } @@ -257,9 +258,9 @@ class CategoryReportController extends Controller foreach ($currency['categories'] as $currentCategory) { foreach ($currentCategory['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); $chartData[$spentKey]['entries'][$key] ??= '0'; - $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], (string) $amount); + $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } } @@ -306,8 +307,8 @@ class CategoryReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -334,8 +335,8 @@ class CategoryReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } diff --git a/app/Http/Controllers/Chart/DoubleReportController.php b/app/Http/Controllers/Chart/DoubleReportController.php index 1e88ceaa5d..12b75f9e23 100644 --- a/app/Http/Controllers/Chart/DoubleReportController.php +++ b/app/Http/Controllers/Chart/DoubleReportController.php @@ -30,6 +30,7 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\OperationsRepositoryInterface; +use FireflyIII\Support\Facades\Steam; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; @@ -81,8 +82,8 @@ class DoubleReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } @@ -108,8 +109,8 @@ class DoubleReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } @@ -135,8 +136,8 @@ class DoubleReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } @@ -149,7 +150,7 @@ class DoubleReportController extends Controller { $chartData = []; - $opposing = $this->repository->expandWithDoubles(new Collection([$account])); + $opposing = $this->repository->expandWithDoubles(new Collection()->push($account)); $accounts = $accounts->merge($opposing); $spent = $this->opsRepository->listExpenses($start, $end, $accounts); $earned = $this->opsRepository->listIncome($start, $end, $accounts); @@ -176,9 +177,9 @@ class DoubleReportController extends Controller foreach ($currency['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); $chartData[$spentKey]['entries'][$key] ??= '0'; - $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], (string) $amount); + $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } // loop income. @@ -202,9 +203,9 @@ class DoubleReportController extends Controller foreach ($currency['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); $chartData[$earnedKey]['entries'][$key] ??= '0'; - $chartData[$earnedKey]['entries'][$key] = bcadd($chartData[$earnedKey]['entries'][$key], (string) $amount); + $chartData[$earnedKey]['entries'][$key] = bcadd($chartData[$earnedKey]['entries'][$key], $amount); } } @@ -274,8 +275,8 @@ class DoubleReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } // loop each tag: @@ -293,8 +294,8 @@ class DoubleReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -327,8 +328,8 @@ class DoubleReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } // loop each tag: @@ -346,8 +347,8 @@ class DoubleReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 6dbb91ac9a..9809c24140 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -32,6 +32,7 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\BasicDataSupport; use FireflyIII\Support\Http\Controllers\ChartGeneration; use Illuminate\Http\JsonResponse; @@ -73,7 +74,7 @@ class ReportController extends Controller if ($cache->has()) { return response()->json($cache->get()); } - $locale = app('steam')->getLocale(); + $locale = Steam::getLocale(); $current = clone $start; $chartData = []; @@ -193,7 +194,7 @@ class ReportController extends Controller ]; // in our outgoing? $key = 'spent'; - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); // deposit = incoming // transfer or reconcile or opening balance, and these accounts are the destination. @@ -207,7 +208,7 @@ class ReportController extends Controller && in_array($journal['destination_account_id'], $ids, true))) { $key = 'earned'; } - $data[$currencyId][$period][$key] = bcadd((string) $data[$currencyId][$period][$key], (string) $amount); + $data[$currencyId][$period][$key] = bcadd((string) $data[$currencyId][$period][$key], $amount); } // loop this data, make chart bars for each currency: @@ -250,8 +251,8 @@ class ReportController extends Controller $title = $currentStart->isoFormat($titleFormat); // #8663 make sure the period exists in the data previously collected. if (array_key_exists($key, $currency)) { - $income['entries'][$title] = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); - $expense['entries'][$title] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); + $income['entries'][$title] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); + $expense['entries'][$title] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); } // #9477 if the period is not in the data, add it with zero values. if (!array_key_exists($key, $currency)) { diff --git a/app/Http/Controllers/Chart/TagReportController.php b/app/Http/Controllers/Chart/TagReportController.php index dbc9451834..6705377806 100644 --- a/app/Http/Controllers/Chart/TagReportController.php +++ b/app/Http/Controllers/Chart/TagReportController.php @@ -28,6 +28,7 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\OperationsRepositoryInterface; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\TransactionCalculation; use Illuminate\Http\JsonResponse; @@ -82,8 +83,8 @@ class TagReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -110,8 +111,8 @@ class TagReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -138,8 +139,8 @@ class TagReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -166,8 +167,8 @@ class TagReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -194,8 +195,8 @@ class TagReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -211,8 +212,8 @@ class TagReportController extends Controller public function mainChart(Collection $accounts, Tag $tag, Carbon $start, Carbon $end): JsonResponse { $chartData = []; - $spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$tag])); - $earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$tag])); + $spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection()->push($tag)); + $earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection()->push($tag)); $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); // loop expenses. @@ -235,9 +236,9 @@ class TagReportController extends Controller foreach ($currency['tags'] as $currentTag) { foreach ($currentTag['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); $chartData[$spentKey]['entries'][$key] ??= '0'; - $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], (string) $amount); + $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } } @@ -262,9 +263,9 @@ class TagReportController extends Controller foreach ($currency['tags'] as $currentTag) { foreach ($currentTag['transaction_journals'] as $journal) { $key = $journal['date']->isoFormat($format); - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); $chartData[$spentKey]['entries'][$key] ??= '0'; - $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], (string) $amount); + $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); } } } @@ -311,8 +312,8 @@ class TagReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -339,8 +340,8 @@ class TagReportController extends Controller 'currency_symbol' => $currency['currency_symbol'], 'currency_code' => $currency['currency_code'], ]; - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -366,8 +367,8 @@ class TagReportController extends Controller 'currency_code' => $currency['currency_code'], ]; foreach ($tag['transaction_journals'] as $journal) { - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } @@ -392,8 +393,8 @@ class TagReportController extends Controller 'currency_code' => $currency['currency_code'], ]; foreach ($tag['transaction_journals'] as $journal) { - $amount = app('steam')->positive($journal['amount']); - $result[$title]['amount'] = bcadd($result[$title]['amount'], (string) $amount); + $amount = Steam::positive($journal['amount']); + $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); } } } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index dc282fbc07..7a4c542730 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -95,8 +95,8 @@ abstract class Controller extends BaseController View::share('logoutUrl', $logoutUrl); // upload size - $maxFileSize = Steam::phpBytes((string) ini_get('upload_max_filesize')); - $maxPostSize = Steam::phpBytes((string) ini_get('post_max_size')); + $maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize')); + $maxPostSize = Steam::phpBytes(ini_get('post_max_size')); $uploadSize = min($maxFileSize, $maxPostSize); View::share('uploadSize', $uploadSize); @@ -140,6 +140,7 @@ abstract class Controller extends BaseController View::share('language', $language); View::share('locale', $locale); View::share('convertToPrimary', $this->convertToPrimary); + View::share('primaryCurrency', $this->primaryCurrency); View::share('shownDemo', $shownDemo); View::share('current_route_name', $page); View::share('original_route_name', Route::currentRouteName()); diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index 3970980c15..73141d0b8c 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -151,13 +151,13 @@ class DebugController extends Controller } if ('' !== $logContent) { // last few lines - $logContent = 'Truncated from this point <----|'.substr((string) $logContent, -16384); + $logContent = 'Truncated from this point <----|'.substr($logContent, -16384); } return view('debug', compact('table', 'now', 'logContent')); } - public function apiTest() + public function apiTest(): View { return view('test.api-test'); } @@ -175,8 +175,8 @@ class DebugController extends Controller private function getSystemInformation(): array { - $maxFileSize = Steam::phpBytes((string) ini_get('upload_max_filesize')); - $maxPostSize = Steam::phpBytes((string) ini_get('post_max_size')); + $maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize')); + $maxPostSize = Steam::phpBytes(ini_get('post_max_size')); $drivers = DB::availableDrivers(); $currentDriver = DB::getDriverName(); @@ -208,7 +208,7 @@ class DebugController extends Controller try { if (file_exists('/var/www/counter-main.txt')) { - $return['build'] = trim((string) file_get_contents('/var/www/counter-main.txt')); + $return['build'] = trim(file_get_contents('/var/www/counter-main.txt')); app('log')->debug(sprintf('build is now "%s"', $return['build'])); } } catch (Exception $e) { @@ -218,7 +218,7 @@ class DebugController extends Controller try { if (file_exists('/var/www/build-date-main.txt')) { - $return['build_date'] = trim((string) file_get_contents('/var/www/build-date-main.txt')); + $return['build_date'] = trim(file_get_contents('/var/www/build-date-main.txt')); } } catch (Exception $e) { app('log')->debug('Could not check build date, but thats ok.'); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index b829724308..3ab394545e 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -170,7 +170,7 @@ class HomeController extends Controller foreach ($accounts as $account) { /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts(new Collection([$account]))->withAccountInformation()->setRange($start, $end)->setLimit(10)->setPage(1); + $collector->setAccounts(new Collection()->push($account))->withAccountInformation()->setRange($start, $end)->setLimit(10)->setPage(1); $set = $collector->getExtractedJournals(); $transactions[] = ['transactions' => $set, 'account' => $account]; } diff --git a/app/Http/Controllers/Json/BoxController.php b/app/Http/Controllers/Json/BoxController.php index ff3f5e8c83..ce5f520180 100644 --- a/app/Http/Controllers/Json/BoxController.php +++ b/app/Http/Controllers/Json/BoxController.php @@ -35,6 +35,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; @@ -92,9 +93,9 @@ class BoxController extends Controller $currencyId = $this->convertToPrimary && $this->primaryCurrency->id !== (int) $journal['currency_id'] ? $this->primaryCurrency->id : (int) $journal['currency_id']; $amount = Amount::getAmountFromJournal($journal); $incomes[$currencyId] ??= '0'; - $incomes[$currencyId] = bcadd($incomes[$currencyId], (string) app('steam')->positive($amount)); + $incomes[$currencyId] = bcadd($incomes[$currencyId], Steam::positive($amount)); $sums[$currencyId] ??= '0'; - $sums[$currencyId] = bcadd($sums[$currencyId], (string) app('steam')->positive($amount)); + $sums[$currencyId] = bcadd($sums[$currencyId], Steam::positive($amount)); } // collect expenses @@ -170,7 +171,7 @@ class BoxController extends Controller $filtered = $allAccounts->filter( static function (Account $account) use ($accountRepository) { $includeNetWorth = $accountRepository->getMetaValue($account, 'include_net_worth'); - $result = null === $includeNetWorth ? true : '1' === $includeNetWorth; + $result = null === $includeNetWorth || '1' === $includeNetWorth; if (false === $result) { app('log')->debug(sprintf('Will not include "%s" in net worth charts.', $account->name)); } diff --git a/app/Http/Controllers/Json/ReconcileController.php b/app/Http/Controllers/Json/ReconcileController.php index 44a4d0cdf9..a1208182e8 100644 --- a/app/Http/Controllers/Json/ReconcileController.php +++ b/app/Http/Controllers/Json/ReconcileController.php @@ -217,7 +217,7 @@ class ReconcileController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts(new Collection([$account])) + $collector->setAccounts(new Collection()->push($account)) ->setRange($selectionStart, $selectionEnd) ->withBudgetInformation()->withCategoryInformation()->withAccountInformation() ; diff --git a/app/Http/Controllers/PiggyBank/ShowController.php b/app/Http/Controllers/PiggyBank/ShowController.php index 3783708c2e..9a877563a4 100644 --- a/app/Http/Controllers/PiggyBank/ShowController.php +++ b/app/Http/Controllers/PiggyBank/ShowController.php @@ -82,6 +82,8 @@ class ShowController extends Controller $admin = auth()->user(); $enrichment = new PiggyBankEnrichment(); $enrichment->setUser($admin); + + /** @var PiggyBank $piggyBank */ $piggyBank = $enrichment->enrichSingle($piggyBank); /** @var PiggyBankTransformer $transformer */ diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index 71a09fd706..d6f0435eed 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -155,7 +155,7 @@ class PreferencesController extends Controller // list of locales also has "equal" which makes it equal to whatever the language is. try { - $locales = json_decode((string) file_get_contents(resource_path(sprintf('locales/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR); + $locales = json_decode(file_get_contents(resource_path(sprintf('locales/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR); } catch (JsonException $e) { app('log')->error($e->getMessage()); $locales = []; diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index e784c4fb7a..b63ab5d86b 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -207,7 +207,7 @@ class ProfileController extends Controller $existing = $repository->findByEmail($newEmail); if ($existing instanceof User) { // force user logout. - Auth::guard()->logout(); // @phpstan-ignore-line (does not recognize function) + Auth::guard()->logout(); $request->session()->invalidate(); session()->flash('success', (string) trans('firefly.email_changed')); @@ -221,7 +221,7 @@ class ProfileController extends Controller event(new UserChangedEmail($user, $newEmail, $oldEmail)); // force user logout. - Auth::guard()->logout(); // @phpstan-ignore-line (does not recognize function) + Auth::guard()->logout(); $request->session()->invalidate(); session()->flash('success', (string) trans('firefly.email_changed')); @@ -412,7 +412,7 @@ class ProfileController extends Controller // found user.which email address to return to? $set = app('preferences')->beginsWith($user, 'previous_email_'); - /** @var string $match */ + /** @var null|string $match */ $match = null; foreach ($set as $entry) { $hashed = hash('sha256', sprintf('%s%s', (string) config('app.key'), $entry->data)); diff --git a/app/Http/Controllers/Recurring/CreateController.php b/app/Http/Controllers/Recurring/CreateController.php index f2b5bd599c..9faff410ee 100644 --- a/app/Http/Controllers/Recurring/CreateController.php +++ b/app/Http/Controllers/Recurring/CreateController.php @@ -49,7 +49,7 @@ class CreateController extends Controller private AttachmentHelperInterface $attachments; private BillRepositoryInterface $billRepository; private BudgetRepositoryInterface $budgetRepos; - private RecurringRepositoryInterface $recurring; + private RecurringRepositoryInterface $repository; /** * CreateController constructor. @@ -65,7 +65,7 @@ class CreateController extends Controller app('view')->share('title', (string) trans('firefly.recurrences')); app('view')->share('subTitle', (string) trans('firefly.create_new_recurrence')); - $this->recurring = app(RecurringRepositoryInterface::class); + $this->repository = app(RecurringRepositoryInterface::class); $this->budgetRepos = app(BudgetRepositoryInterface::class); $this->attachments = app(AttachmentHelperInterface::class); $this->billRepository = app(BillRepositoryInterface::class); @@ -84,7 +84,6 @@ class CreateController extends Controller { $budgets = app('expandedform')->makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets()); $bills = app('expandedform')->makeSelectListWithEmpty($this->billRepository->getActiveBills()); - $primaryCurrency = $this->primaryCurrency; $tomorrow = today(config('app.timezone')); $oldRepetitionType = $request->old('repetition_type'); $tomorrow->addDay(); @@ -116,7 +115,7 @@ class CreateController extends Controller return view( 'recurring.create', - compact('tomorrow', 'oldRepetitionType', 'bills', 'weekendResponses', 'preFilled', 'repetitionEnds', 'primaryCurrency', 'budgets') + compact('tomorrow', 'oldRepetitionType', 'bills', 'weekendResponses', 'preFilled', 'repetitionEnds', 'budgets') ); } @@ -129,7 +128,6 @@ class CreateController extends Controller { $budgets = app('expandedform')->makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets()); $bills = app('expandedform')->makeSelectListWithEmpty($this->billRepository->getActiveBills()); - $primaryCurrency = $this->primaryCurrency; $tomorrow = today(config('app.timezone')); $oldRepetitionType = $request->old('repetition_type'); $tomorrow->addDay(); @@ -208,10 +206,7 @@ class CreateController extends Controller } $request->session()->flash('preFilled', $preFilled); - return view( - 'recurring.create', - compact('tomorrow', 'oldRepetitionType', 'bills', 'weekendResponses', 'preFilled', 'repetitionEnds', 'primaryCurrency', 'budgets') - ); + return view('recurring.create', compact('tomorrow', 'oldRepetitionType', 'bills', 'weekendResponses', 'preFilled', 'repetitionEnds', 'budgets')); } /** @@ -226,7 +221,7 @@ class CreateController extends Controller $data = $request->getAll(); try { - $recurrence = $this->recurring->store($data); + $recurrence = $this->repository->store($data); } catch (FireflyException $e) { session()->flash('error', $e->getMessage()); diff --git a/app/Http/Controllers/Recurring/DeleteController.php b/app/Http/Controllers/Recurring/DeleteController.php index 4cdc4c971f..5ddb8fff08 100644 --- a/app/Http/Controllers/Recurring/DeleteController.php +++ b/app/Http/Controllers/Recurring/DeleteController.php @@ -38,8 +38,7 @@ use Illuminate\View\View; */ class DeleteController extends Controller { - /** @var RecurringRepositoryInterface Recurring repository */ - private $recurring; + private RecurringRepositoryInterface $repository; /** * DeleteController constructor. @@ -54,7 +53,7 @@ class DeleteController extends Controller app('view')->share('mainTitleIcon', 'fa-paint-brush'); app('view')->share('title', (string) trans('firefly.recurrences')); - $this->recurring = app(RecurringRepositoryInterface::class); + $this->repository = app(RecurringRepositoryInterface::class); return $next($request); } @@ -72,7 +71,7 @@ class DeleteController extends Controller // put previous url in session $this->rememberPreviousUrl('recurrences.delete.url'); - $journalsCreated = $this->recurring->getTransactions($recurrence)->count(); + $journalsCreated = $this->repository->getTransactions($recurrence)->count(); return view('recurring.delete', compact('recurrence', 'subTitle', 'journalsCreated')); } diff --git a/app/Http/Controllers/Recurring/EditController.php b/app/Http/Controllers/Recurring/EditController.php index 59772aa1b4..15d3e1d5b1 100644 --- a/app/Http/Controllers/Recurring/EditController.php +++ b/app/Http/Controllers/Recurring/EditController.php @@ -54,7 +54,7 @@ class EditController extends Controller private AttachmentHelperInterface $attachments; private BillRepositoryInterface $billRepository; private BudgetRepositoryInterface $budgetRepos; - private RecurringRepositoryInterface $recurring; + private RecurringRepositoryInterface $repository; /** * EditController constructor. @@ -70,7 +70,7 @@ class EditController extends Controller app('view')->share('title', (string) trans('firefly.recurrences')); app('view')->share('subTitle', (string) trans('firefly.recurrences')); - $this->recurring = app(RecurringRepositoryInterface::class); + $this->repository = app(RecurringRepositoryInterface::class); $this->budgetRepos = app(BudgetRepositoryInterface::class); $this->attachments = app(AttachmentHelperInterface::class); $this->billRepository = app(BillRepositoryInterface::class); @@ -100,12 +100,13 @@ class EditController extends Controller $admin = auth()->user(); $enrichment = new RecurringEnrichment(); $enrichment->setUser($admin); + + /** @var Recurrence $recurrence */ $recurrence = $enrichment->enrichSingle($recurrence); /** @var RecurrenceTransformer $transformer */ $transformer = app(RecurrenceTransformer::class); $transformer->setParameters(new ParameterBag()); - $array = $transformer->transform($recurrence); $budgets = ExpandedForm::makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets()); $bills = ExpandedForm::makeSelectListWithEmpty($this->billRepository->getActiveBills()); @@ -181,7 +182,7 @@ class EditController extends Controller public function update(RecurrenceFormRequest $request, Recurrence $recurrence) { $data = $request->getAll(); - $this->recurring->update($recurrence, $data); + $this->repository->update($recurrence, $data); $request->session()->flash('success', (string) trans('firefly.updated_recurrence', ['title' => $recurrence->title])); Log::channel('audit')->info(sprintf('Updated recurrence #%d.', $recurrence->id), $data); diff --git a/app/Http/Controllers/Recurring/IndexController.php b/app/Http/Controllers/Recurring/IndexController.php index 773c36f916..d6c7e41d7f 100644 --- a/app/Http/Controllers/Recurring/IndexController.php +++ b/app/Http/Controllers/Recurring/IndexController.php @@ -45,7 +45,7 @@ class IndexController extends Controller { use GetConfigurationData; - private RecurringRepositoryInterface $recurringRepos; + private RecurringRepositoryInterface $repository; /** * IndexController constructor. @@ -60,7 +60,7 @@ class IndexController extends Controller app('view')->share('mainTitleIcon', 'fa-paint-brush'); app('view')->share('title', (string) trans('firefly.recurrences')); - $this->recurringRepos = app(RecurringRepositoryInterface::class); + $this->repository = app(RecurringRepositoryInterface::class); return $next($request); } @@ -79,7 +79,7 @@ class IndexController extends Controller { $page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page'); $pageSize = (int) app('preferences')->get('listPageSize', 50)->data; - $collection = $this->recurringRepos->get(); + $collection = $this->repository->get(); $today = today(config('app.timezone')); $year = today(config('app.timezone')); diff --git a/app/Http/Controllers/Recurring/ShowController.php b/app/Http/Controllers/Recurring/ShowController.php index d44456fd11..2d11613550 100644 --- a/app/Http/Controllers/Recurring/ShowController.php +++ b/app/Http/Controllers/Recurring/ShowController.php @@ -47,8 +47,7 @@ class ShowController extends Controller { use GetConfigurationData; - /** @var RecurringRepositoryInterface Recurring repository */ - private $recurring; + private RecurringRepositoryInterface $repository; /** * IndexController constructor. @@ -64,7 +63,7 @@ class ShowController extends Controller app('view')->share('mainTitleIcon', 'fa-paint-brush'); app('view')->share('title', (string) trans('firefly.recurrences')); - $this->recurring = app(RecurringRepositoryInterface::class); + $this->repository = app(RecurringRepositoryInterface::class); return $next($request); } @@ -87,6 +86,8 @@ class ShowController extends Controller $admin = auth()->user(); $enrichment = new RecurringEnrichment(); $enrichment->setUser($admin); + + /** @var Recurrence $recurrence */ $recurrence = $enrichment->enrichSingle($recurrence); /** @var RecurrenceTransformer $transformer */ @@ -95,10 +96,10 @@ class ShowController extends Controller $array = $transformer->transform($recurrence); - $groups = $this->recurring->getTransactions($recurrence); + $groups = $this->repository->getTransactions($recurrence); $today = today(config('app.timezone')); $array['repeat_until'] = null !== $array['repeat_until'] ? new Carbon($array['repeat_until']) : null; - $array['journal_count'] = $this->recurring->getJournalCount($recurrence); + $array['journal_count'] = $this->repository->getJournalCount($recurrence); // transform dates back to Carbon objects and expand information foreach ($array['repetitions'] as $index => $repetition) { @@ -106,8 +107,8 @@ class ShowController extends Controller $date = new Carbon($occurrence)->startOfDay(); $set = [ 'date' => $date, - 'fired' => $this->recurring->createdPreviously($recurrence, $date) - || $this->recurring->getJournalCount($recurrence, $date) > 0, + 'fired' => $this->repository->createdPreviously($recurrence, $date) + || $this->repository->getJournalCount($recurrence, $date) > 0, ]; $array['repetitions'][$index]['occurrences'][$item] = $set; } diff --git a/app/Http/Controllers/Recurring/TriggerController.php b/app/Http/Controllers/Recurring/TriggerController.php index 818f353037..7719be82a8 100644 --- a/app/Http/Controllers/Recurring/TriggerController.php +++ b/app/Http/Controllers/Recurring/TriggerController.php @@ -28,8 +28,8 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\TriggerRecurrenceRequest; use FireflyIII\Jobs\CreateRecurringTransactions; use FireflyIII\Models\Recurrence; -use FireflyIII\Models\TransactionGroup; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Support\Facades\Preferences; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Collection; @@ -38,6 +38,29 @@ use Illuminate\Support\Collection; */ class TriggerController extends Controller { + private RecurringRepositoryInterface $repository; + + /** + * IndexController constructor. + */ + public function __construct() + { + parent::__construct(); + app('view')->share('showCategory', true); + + // translations: + $this->middleware( + function ($request, $next) { + app('view')->share('mainTitleIcon', 'fa-paint-brush'); + app('view')->share('title', (string) trans('firefly.recurrences')); + + $this->repository = app(RecurringRepositoryInterface::class); + + return $next($request); + } + ); + } + public function trigger(Recurrence $recurrence, TriggerRecurrenceRequest $request): RedirectResponse { $all = $request->getAll(); @@ -51,27 +74,18 @@ class TriggerController extends Controller /** @var CreateRecurringTransactions $job */ $job = app(CreateRecurringTransactions::class); - $job->setRecurrences(new Collection([$recurrence])); + $job->setRecurrences(new Collection()->push($recurrence)); $job->setDate($date); $job->setForce(false); $job->handle(); app('log')->debug('Done with recurrence.'); $groups = $job->getGroups(); - - /** @var TransactionGroup $group */ - foreach ($groups as $group) { - /** @var TransactionJournal $journal */ - foreach ($group->transactionJournals as $journal) { - app('log')->debug(sprintf('Set date of journal #%d to today!', $journal->id)); - $journal->date = today(config('app.timezone')); - $journal->save(); - } - } + $this->repository->markGroupsAsNow($groups); $recurrence->latest_date = $backupDate; $recurrence->latest_date_tz = $backupDate?->format('e'); $recurrence->save(); - app('preferences')->mark(); + Preferences::mark(); if (0 === $groups->count()) { $request->session()->flash('info', (string) trans('firefly.no_new_transaction_in_recurrence')); diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index 2b26e16501..a59076ab9a 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -291,7 +291,7 @@ class BudgetController extends Controller $cache->addProperty('budget-period-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - // return $cache->get(); + return $cache->get(); } $periods = Navigation::listOfPeriods($start, $end); diff --git a/app/Http/Controllers/Rule/SelectController.php b/app/Http/Controllers/Rule/SelectController.php index 24c7dc0860..4d85f8ae3d 100644 --- a/app/Http/Controllers/Rule/SelectController.php +++ b/app/Http/Controllers/Rule/SelectController.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Rule; use Throwable; -use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\SelectTransactionsRequest; @@ -74,20 +73,16 @@ class SelectController extends Controller /** @var User $user */ $user = auth()->user(); $accounts = implode(',', $request->get('accounts')); - $startDate = new Carbon($request->get('start')); - $endDate = new Carbon($request->get('end')); // create new rule engine: $newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine->setUser($user); // add extra operators: - $newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]); - $newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]); $newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]); // set rules: - $newRuleEngine->setRules(new Collection([$rule])); + $newRuleEngine->setRules(new Collection()->push($rule)); $newRuleEngine->fire(); $resultCount = $newRuleEngine->getResults(); @@ -107,11 +102,9 @@ class SelectController extends Controller return redirect(route('rules.index')); } // does the user have shared accounts? - $first = session('first', today(config('app.timezone'))->subYear())->format('Y-m-d'); - $today = today(config('app.timezone'))->format('Y-m-d'); $subTitle = (string) trans('firefly.apply_rule_selection', ['title' => $rule->title]); - return view('rules.rule.select-transactions', compact('first', 'today', 'rule', 'subTitle')); + return view('rules.rule.select-transactions', compact('rule', 'subTitle')); } /** @@ -159,7 +152,7 @@ class SelectController extends Controller $newRuleEngine = app(RuleEngineInterface::class); // set rules: - $newRuleEngine->setRules(new Collection([$rule])); + $newRuleEngine->setRules(new Collection()->push($rule)); $newRuleEngine->setRefreshTriggers(false); $collection = $newRuleEngine->find(); $collection = $collection->slice(0, 20); @@ -203,7 +196,7 @@ class SelectController extends Controller $newRuleEngine = app(RuleEngineInterface::class); // set rules: - $newRuleEngine->setRules(new Collection([$rule])); + $newRuleEngine->setRules(new Collection()->push($rule)); $collection = $newRuleEngine->find(); $collection = $collection->slice(0, 20); diff --git a/app/Http/Controllers/RuleGroup/ExecutionController.php b/app/Http/Controllers/RuleGroup/ExecutionController.php index 4f1b3137b2..a0191cba3f 100644 --- a/app/Http/Controllers/RuleGroup/ExecutionController.php +++ b/app/Http/Controllers/RuleGroup/ExecutionController.php @@ -25,11 +25,9 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\RuleGroup; use Exception; -use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\SelectTransactionsRequest; use FireflyIII\Models\RuleGroup; -use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\TransactionRules\Engine\RuleEngineInterface; use FireflyIII\User; use Illuminate\Contracts\View\Factory; @@ -42,8 +40,6 @@ use Illuminate\View\View; */ class ExecutionController extends Controller { - private RuleGroupRepositoryInterface $ruleGroupRepository; - /** * ExecutionController constructor. */ @@ -56,7 +52,6 @@ class ExecutionController extends Controller app('view')->share('title', (string) trans('firefly.rules')); app('view')->share('mainTitleIcon', 'fa-random'); - $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); return $next($request); } @@ -74,20 +69,16 @@ class ExecutionController extends Controller /** @var User $user */ $user = auth()->user(); $accounts = implode(',', $request->get('accounts')); - $startDate = new Carbon($request->get('start')); - $endDate = new Carbon($request->get('end')); // create new rule engine: $newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine->setUser($user); // add extra operators: - $newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]); - $newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]); $newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]); // set rules: // #10427, file rule group and not the set of rules. - $collection = new Collection([$ruleGroup]); + $collection = new Collection()->push($ruleGroup); $newRuleEngine->setRuleGroups($collection); $newRuleEngine->fire(); @@ -104,10 +95,8 @@ class ExecutionController extends Controller */ public function selectTransactions(RuleGroup $ruleGroup) { - $first = session('first')->format('Y-m-d'); - $today = today(config('app.timezone'))->format('Y-m-d'); $subTitle = (string) trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]); - return view('rules.rule-group.select-transactions', compact('first', 'today', 'ruleGroup', 'subTitle')); + return view('rules.rule-group.select-transactions', compact('ruleGroup', 'subTitle')); } } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 82020490e3..f69cd62c24 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -250,10 +250,7 @@ class TagController extends Controller /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation() - ->setTag($tag)->withBudgetInformation()->withCategoryInformation() - ->withAttachmentInformation() - ; + $collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation()->setTag($tag)->withBudgetInformation()->withCategoryInformation()->withAttachmentInformation(); $groups = $collector->getPaginatedGroups(); $groups->setPath($path); $sums = $this->repository->sumsOfTag($tag, $start, $end); diff --git a/app/Http/Controllers/Transaction/BulkController.php b/app/Http/Controllers/Transaction/BulkController.php index bca008b147..ab5365b1f4 100644 --- a/app/Http/Controllers/Transaction/BulkController.php +++ b/app/Http/Controllers/Transaction/BulkController.php @@ -117,7 +117,7 @@ class BulkController extends Controller // run rules on changed journals: /** @var TransactionJournal $journal */ - foreach ($collection as $journal) { + foreach ($collection as $journal) { // @phpstan-ignore-line event(new UpdatedTransactionGroup($journal->transactionGroup, true, true, false)); } diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index 8f24176f59..ec211a9b0d 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Transaction; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use Exception; use FireflyIII\Enums\AccountTypeEnum; @@ -303,22 +304,22 @@ class ConvertController extends Controller private function convertJournal(TransactionJournal $journal, TransactionType $transactionType, array $data): TransactionJournal { /** @var AccountValidator $validator */ - $validator = app(AccountValidator::class); + $validator = app(AccountValidator::class); $validator->setUser(auth()->user()); $validator->setTransactionType($transactionType->type); - $sourceId = $data['source_id'][$journal->id] ?? null; - $sourceName = $data['source_name'][$journal->id] ?? null; - $destinationId = $data['destination_id'][$journal->id] ?? null; - $destinationName = $data['destination_name'][$journal->id] ?? null; + $sourceId = $data['source_id'][$journal->id] ?? null; + $sourceName = $data['source_name'][$journal->id] ?? null; + $destinationId = $data['destination_id'][$journal->id] ?? null; + $destinationName = $data['destination_name'][$journal->id] ?? null; // double check it's not an empty string. - $sourceId = '' === $sourceId || null === $sourceId ? null : (int) $sourceId; - $sourceName = '' === $sourceName ? null : (string) $sourceName; - $destinationId = '' === $destinationId || null === $destinationId ? null : (int) $destinationId; - $destinationName = '' === $destinationName ? null : (string) $destinationName; - $validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]); - $validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]); + $sourceId = '' === $sourceId || null === $sourceId ? null : (int) $sourceId; + $sourceName = '' === $sourceName ? null : (string) $sourceName; + $destinationId = '' === $destinationId || null === $destinationId ? null : (int) $destinationId; + $destinationName = '' === $destinationName ? null : (string) $destinationName; + $validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]); + $validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]); if (false === $validSource) { throw new FireflyException(sprintf(trans('firefly.convert_invalid_source'), $journal->id)); @@ -329,7 +330,7 @@ class ConvertController extends Controller // TODO typeOverrule: the account validator may have another opinion on the transaction type. - $update = [ + $update = [ 'source_id' => $sourceId, 'source_name' => $sourceName, 'destination_id' => $destinationId, @@ -337,6 +338,10 @@ class ConvertController extends Controller 'type' => $transactionType->type, ]; + /** @var null|Transaction $sourceTransaction */ + $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); + $amount = $sourceTransaction->amount ?? '0'; + // also set the currency to the currency of the source account, in case you're converting a deposit into a transfer. if (TransactionTypeEnum::TRANSFER->value === $transactionType->type && TransactionTypeEnum::DEPOSIT->value === $journal->transactionType->type) { $source = $this->accountRepository->find((int) $sourceId); @@ -346,12 +351,25 @@ class ConvertController extends Controller if ($sourceCurrency instanceof TransactionCurrency && $destCurrency instanceof TransactionCurrency && $sourceCurrency->code !== $destCurrency->code) { $update['currency_id'] = $sourceCurrency->id; $update['foreign_currency_id'] = $destCurrency->id; - $update['foreign_amount'] = '1'; // not the best solution but at this point the amount is hard to get. + $update['foreign_amount'] = Steam::positive($amount); // not the best solution but at this point the amount is hard to get. + } + } + + // same thing for converting a withdrawal into a transfer, but with the currency of the destination account. + if (TransactionTypeEnum::TRANSFER->value === $transactionType->type && TransactionTypeEnum::WITHDRAWAL->value === $journal->transactionType->type) { + $source = $this->accountRepository->find((int) $sourceId); + $sourceCurrency = $this->accountRepository->getAccountCurrency($source); + $dest = $this->accountRepository->find((int) $destinationId); + $destCurrency = $this->accountRepository->getAccountCurrency($dest); + if ($sourceCurrency instanceof TransactionCurrency && $destCurrency instanceof TransactionCurrency && $sourceCurrency->code !== $destCurrency->code) { + $update['currency_id'] = $sourceCurrency->id; + $update['foreign_currency_id'] = $destCurrency->id; + $update['foreign_amount'] = Steam::positive($amount); // not the best solution but at this point the amount is hard to get. } } /** @var JournalUpdateService $service */ - $service = app(JournalUpdateService::class); + $service = app(JournalUpdateService::class); $service->setTransactionJournal($journal); $service->setData($update); $service->update(); diff --git a/app/Http/Controllers/Transaction/CreateController.php b/app/Http/Controllers/Transaction/CreateController.php index d8e6092837..519ff3d538 100644 --- a/app/Http/Controllers/Transaction/CreateController.php +++ b/app/Http/Controllers/Transaction/CreateController.php @@ -117,7 +117,6 @@ class CreateController extends Controller $optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data; $allowedOpposingTypes = config('firefly.allowed_opposing_types'); $accountToTypes = config('firefly.account_to_transaction'); - $primaryCurrency = $this->primaryCurrency; $previousUrl = $this->rememberPreviousUrl('transactions.create.url'); $parts = parse_url((string) $previousUrl); $search = sprintf('?%s', $parts['query'] ?? ''); @@ -145,26 +144,6 @@ class CreateController extends Controller session()->put('preFilled', $preFilled); - return view( - 'transactions.create', - compact( - 'subTitleIcon', - 'cash', - 'longitude', - 'latitude', - 'zoomLevel', - 'objectType', - 'optionalDateFields', - 'subTitle', - 'primaryCurrency', - 'previousUrl', - 'optionalFields', - 'preFilled', - 'allowedOpposingTypes', - 'accountToTypes', - 'sourceId', - 'destinationId' - ) - ); + return view('transactions.create', compact('subTitleIcon', 'cash', 'longitude', 'latitude', 'zoomLevel', 'objectType', 'optionalDateFields', 'subTitle', 'previousUrl', 'optionalFields', 'preFilled', 'allowedOpposingTypes', 'accountToTypes', 'sourceId', 'destinationId')); } } diff --git a/app/Http/Controllers/Transaction/EditController.php b/app/Http/Controllers/Transaction/EditController.php index 507cc0cc34..5b30ec7a9f 100644 --- a/app/Http/Controllers/Transaction/EditController.php +++ b/app/Http/Controllers/Transaction/EditController.php @@ -84,7 +84,6 @@ class EditController extends Controller $title = $transactionGroup->transactionJournals()->count() > 1 ? $transactionGroup->title : $transactionGroup->transactionJournals()->first()->description; $subTitle = (string) trans('firefly.edit_transaction_title', ['description' => $title]); $subTitleIcon = 'fa-plus'; - $primaryCurrency = $this->primaryCurrency; $cash = $repository->getCashAccount(); $previousUrl = $this->rememberPreviousUrl('transactions.edit.url'); $parts = parse_url((string) $previousUrl); @@ -114,26 +113,7 @@ class EditController extends Controller $latitude = config('firefly.default_location.latitude'); $zoomLevel = config('firefly.default_location.zoom_level'); - return view( - 'transactions.edit', - compact( - 'cash', - 'allowedSourceDests', - 'expectedSourceTypes', - 'optionalDateFields', - 'longitude', - 'latitude', - 'zoomLevel', - 'optionalFields', - 'subTitle', - 'subTitleIcon', - 'transactionGroup', - 'allowedOpposingTypes', - 'accountToTypes', - 'primaryCurrency', - 'previousUrl' - ) - ); + return view('transactions.edit', compact('cash', 'allowedSourceDests', 'expectedSourceTypes', 'optionalDateFields', 'longitude', 'latitude', 'zoomLevel', 'optionalFields', 'subTitle', 'subTitleIcon', 'transactionGroup', 'allowedOpposingTypes', 'accountToTypes', 'previousUrl')); } public function unreconcile(TransactionJournal $journal): JsonResponse diff --git a/app/Http/Controllers/Transaction/ShowController.php b/app/Http/Controllers/Transaction/ShowController.php index 3df555292c..2d9e81f511 100644 --- a/app/Http/Controllers/Transaction/ShowController.php +++ b/app/Http/Controllers/Transaction/ShowController.php @@ -92,6 +92,7 @@ class ShowController extends Controller $collector = app(GroupCollectorInterface::class); $collector->setUser($admin)->setTransactionGroup($transactionGroup)->withAPIInformation(); + /** @var null|TransactionGroup $selectedGroup */ $selectedGroup = $collector->getGroups()->first(); if (null === $selectedGroup) { throw new NotFoundHttpException(); @@ -103,9 +104,6 @@ class ShowController extends Controller $selectedGroup = $enrichment->enrichSingle($selectedGroup); - /** @var null|TransactionJournal $first */ - $first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']); - $splits = $transactionGroup->transactionJournals()->count(); $splits = count($selectedGroup['transactions']); $keys = array_keys($selectedGroup['transactions']); $first = $selectedGroup['transactions'][array_shift($keys)]; @@ -121,6 +119,8 @@ class ShowController extends Controller // enrich $enrichment = new TransactionGroupEnrichment(); $enrichment->setUser($admin); + + /** @var array $selectedGroup */ $selectedGroup = $enrichment->enrichSingle($selectedGroup); /** @var TransactionGroupTransformer $transformer */ diff --git a/app/Http/Middleware/InterestingMessage.php b/app/Http/Middleware/InterestingMessage.php index 30c9b65e68..70ca9fb2bf 100644 --- a/app/Http/Middleware/InterestingMessage.php +++ b/app/Http/Middleware/InterestingMessage.php @@ -25,14 +25,15 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; use Closure; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\GroupMembership; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\UserGroup; use FireflyIII\Models\Webhook; +use FireflyIII\Support\Facades\Amount; use FireflyIII\User; use Illuminate\Http\Request; @@ -103,7 +104,7 @@ class InterestingMessage // send message about newly created transaction group. /** @var null|TransactionGroup $group */ - $group = auth()->user()->transactionGroups()->with(['transactionJournals', 'transactionJournals.transactionType'])->find((int) $transactionGroupId); + $group = auth()->user()->transactionGroups()->with(['transactionJournals', 'transactionJournals.transactionType'])->find((int)$transactionGroupId); if (null === $group) { return; @@ -119,17 +120,17 @@ class InterestingMessage $title = $count > 1 ? $group->title : $journal->description; if ('created' === $message) { session()->flash('success_url', route('transactions.show', [$transactionGroupId])); - session()->flash('success', (string) trans('firefly.stored_journal', ['description' => $title])); + session()->flash('success', (string)trans('firefly.stored_journal', ['description' => $title])); } if ('updated' === $message) { - $type = strtolower((string) $journal->transactionType->type); + $type = strtolower((string)$journal->transactionType->type); session()->flash('success_url', route('transactions.show', [$transactionGroupId])); - session()->flash('success', (string) trans(sprintf('firefly.updated_%s', $type), ['description' => $title])); + session()->flash('success', (string)trans(sprintf('firefly.updated_%s', $type), ['description' => $title])); } if ('no_change' === $message) { - $type = strtolower((string) $journal->transactionType->type); + $type = strtolower((string)$journal->transactionType->type); session()->flash('warning_url', route('transactions.show', [$transactionGroupId])); - session()->flash('warning', (string) trans(sprintf('firefly.no_changes_%s', $type), ['description' => $title])); + session()->flash('warning', (string)trans(sprintf('firefly.no_changes_%s', $type), ['description' => $title])); } } @@ -170,13 +171,13 @@ class InterestingMessage if ('deleted' === $message) { - session()->flash('success', (string) trans('firefly.flash_administration_deleted', ['title' => $userGroup->title])); + session()->flash('success', (string)trans('firefly.flash_administration_deleted', ['title' => $userGroup->title])); } if ('created' === $message) { - session()->flash('success', (string) trans('firefly.flash_administration_created', ['title' => $userGroup->title])); + session()->flash('success', (string)trans('firefly.flash_administration_created', ['title' => $userGroup->title])); } if ('updated' === $message) { - session()->flash('success', (string) trans('firefly.flash_administration_updated', ['title' => $userGroup->title])); + session()->flash('success', (string)trans('firefly.flash_administration_updated', ['title' => $userGroup->title])); } } @@ -205,13 +206,13 @@ class InterestingMessage return; } if ('deleted' === $message) { - session()->flash('success', (string) trans('firefly.account_deleted', ['name' => $account->name])); + session()->flash('success', (string)trans('firefly.account_deleted', ['name' => $account->name])); } if ('created' === $message) { - session()->flash('success', (string) trans('firefly.stored_new_account', ['name' => $account->name])); + session()->flash('success', (string)trans('firefly.stored_new_account', ['name' => $account->name])); } if ('updated' === $message) { - session()->flash('success', (string) trans('firefly.updated_account', ['name' => $account->name])); + session()->flash('success', (string)trans('firefly.updated_account', ['name' => $account->name])); } } @@ -237,10 +238,10 @@ class InterestingMessage return; } if ('deleted' === $message) { - session()->flash('success', (string) trans('firefly.deleted_bill', ['name' => $bill->name])); + session()->flash('success', (string)trans('firefly.deleted_bill', ['name' => $bill->name])); } if ('created' === $message) { - session()->flash('success', (string) trans('firefly.stored_new_bill', ['name' => $bill->name])); + session()->flash('success', (string)trans('firefly.stored_new_bill', ['name' => $bill->name])); } } @@ -266,13 +267,13 @@ class InterestingMessage return; } if ('deleted' === $message) { - session()->flash('success', (string) trans('firefly.deleted_webhook', ['title' => $webhook->title])); + session()->flash('success', (string)trans('firefly.deleted_webhook', ['title' => $webhook->title])); } if ('updated' === $message) { - session()->flash('success', (string) trans('firefly.updated_webhook', ['title' => $webhook->title])); + session()->flash('success', (string)trans('firefly.updated_webhook', ['title' => $webhook->title])); } if ('created' === $message) { - session()->flash('success', (string) trans('firefly.stored_new_webhook', ['title' => $webhook->title])); + session()->flash('success', (string)trans('firefly.stored_new_webhook', ['title' => $webhook->title])); } } @@ -289,32 +290,32 @@ class InterestingMessage { // params: // get parameters from request. - $code = $request->get('code'); - $message = $request->get('message'); + $code = (string) $request->get('code'); + $message = (string) $request->get('message'); - /** @var null|TransactionCurrency $currency */ - $currency = TransactionCurrency::whereCode($code)->first(); - - if (null === $currency) { + try { + $currency = Amount::getTransactionCurrencyByCode($code); + } catch (FireflyException) { return; } + if ('enabled' === $message) { - session()->flash('success', (string) trans('firefly.currency_is_now_enabled', ['name' => $currency->name])); + session()->flash('success', (string)trans('firefly.currency_is_now_enabled', ['name' => $currency->name])); } if ('enable_failed' === $message) { - session()->flash('error', (string) trans('firefly.could_not_enable_currency', ['name' => $currency->name])); + session()->flash('error', (string)trans('firefly.could_not_enable_currency', ['name' => $currency->name])); } if ('disabled' === $message) { - session()->flash('success', (string) trans('firefly.currency_is_now_disabled', ['name' => $currency->name])); + session()->flash('success', (string)trans('firefly.currency_is_now_disabled', ['name' => $currency->name])); } if ('disable_failed' === $message) { - session()->flash('error', (string) trans('firefly.could_not_disable_currency', ['name' => $currency->name])); + session()->flash('error', (string)trans('firefly.could_not_disable_currency', ['name' => $currency->name])); } if ('default' === $message) { - session()->flash('success', (string) trans('firefly.new_default_currency', ['name' => $currency->name])); + session()->flash('success', (string)trans('firefly.new_default_currency', ['name' => $currency->name])); } if ('default_failed' === $message) { - session()->flash('error', (string) trans('firefly.default_currency_failed', ['name' => $currency->name])); + session()->flash('error', (string)trans('firefly.default_currency_failed', ['name' => $currency->name])); } } } diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 3390515840..a9e7a705cd 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; use Closure; +use FireflyIII\Support\Facades\Steam; use Illuminate\Support\Facades\App; use Carbon\Carbon; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; @@ -101,12 +102,12 @@ class Range private function configureView(): void { // get locale preference: - $language = app('steam')->getLanguage(); - $locale = app('steam')->getLocale(); + $language = Steam::getLanguage(); + $locale = Steam::getLocale(); App::setLocale($language); - Carbon::setLocale(substr((string) $locale, 0, 2)); + Carbon::setLocale(substr($locale, 0, 2)); - $localeArray = app('steam')->getLocaleArray($locale); + $localeArray = Steam::getLocaleArray($locale); setlocale(LC_TIME, $localeArray); $moneyResult = setlocale(LC_MONETARY, $localeArray); diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index 8d99bc4765..ced11547e0 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Models\Account; use FireflyIII\Models\Location; diff --git a/app/Http/Requests/AttachmentFormRequest.php b/app/Http/Requests/AttachmentFormRequest.php index 2da2240168..f2b4beb568 100644 --- a/app/Http/Requests/AttachmentFormRequest.php +++ b/app/Http/Requests/AttachmentFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; diff --git a/app/Http/Requests/BillStoreRequest.php b/app/Http/Requests/BillStoreRequest.php index 0f530a2191..46436a7c9d 100644 --- a/app/Http/Requests/BillStoreRequest.php +++ b/app/Http/Requests/BillStoreRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; diff --git a/app/Http/Requests/BillUpdateRequest.php b/app/Http/Requests/BillUpdateRequest.php index f4376c00b4..2f70b26df3 100644 --- a/app/Http/Requests/BillUpdateRequest.php +++ b/app/Http/Requests/BillUpdateRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Bill; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Http/Requests/BudgetFormStoreRequest.php b/app/Http/Requests/BudgetFormStoreRequest.php index cb6c99f037..ac4edf6e69 100644 --- a/app/Http/Requests/BudgetFormStoreRequest.php +++ b/app/Http/Requests/BudgetFormStoreRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; diff --git a/app/Http/Requests/BudgetFormUpdateRequest.php b/app/Http/Requests/BudgetFormUpdateRequest.php index 459665e50f..539e613fff 100644 --- a/app/Http/Requests/BudgetFormUpdateRequest.php +++ b/app/Http/Requests/BudgetFormUpdateRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Budget; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Http/Requests/BudgetIncomeRequest.php b/app/Http/Requests/BudgetIncomeRequest.php index d0a2b96b17..49ed18de14 100644 --- a/app/Http/Requests/BudgetIncomeRequest.php +++ b/app/Http/Requests/BudgetIncomeRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; diff --git a/app/Http/Requests/BulkEditJournalRequest.php b/app/Http/Requests/BulkEditJournalRequest.php index bcbbf533ca..169d1bc6ce 100644 --- a/app/Http/Requests/BulkEditJournalRequest.php +++ b/app/Http/Requests/BulkEditJournalRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; diff --git a/app/Http/Requests/CategoryFormRequest.php b/app/Http/Requests/CategoryFormRequest.php index 53e25c7be3..a4e5112669 100644 --- a/app/Http/Requests/CategoryFormRequest.php +++ b/app/Http/Requests/CategoryFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Category; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; diff --git a/app/Http/Requests/ConfigurationRequest.php b/app/Http/Requests/ConfigurationRequest.php index 212b706b5f..712d2be5a4 100644 --- a/app/Http/Requests/ConfigurationRequest.php +++ b/app/Http/Requests/ConfigurationRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; diff --git a/app/Http/Requests/CurrencyFormRequest.php b/app/Http/Requests/CurrencyFormRequest.php index 9a4f49672c..60ce09cbba 100644 --- a/app/Http/Requests/CurrencyFormRequest.php +++ b/app/Http/Requests/CurrencyFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; diff --git a/app/Http/Requests/DeleteAccountFormRequest.php b/app/Http/Requests/DeleteAccountFormRequest.php index c06cba8b51..eb36622b3a 100644 --- a/app/Http/Requests/DeleteAccountFormRequest.php +++ b/app/Http/Requests/DeleteAccountFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; diff --git a/app/Http/Requests/EmailFormRequest.php b/app/Http/Requests/EmailFormRequest.php index 26f2c66750..1f499245eb 100644 --- a/app/Http/Requests/EmailFormRequest.php +++ b/app/Http/Requests/EmailFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; diff --git a/app/Http/Requests/ExistingTokenFormRequest.php b/app/Http/Requests/ExistingTokenFormRequest.php index 37f7eb7867..f3222a8d57 100644 --- a/app/Http/Requests/ExistingTokenFormRequest.php +++ b/app/Http/Requests/ExistingTokenFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; diff --git a/app/Http/Requests/InviteUserFormRequest.php b/app/Http/Requests/InviteUserFormRequest.php index 5c71175d81..70ebad1f19 100644 --- a/app/Http/Requests/InviteUserFormRequest.php +++ b/app/Http/Requests/InviteUserFormRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; diff --git a/app/Http/Requests/JournalLinkRequest.php b/app/Http/Requests/JournalLinkRequest.php index f924a24ee6..487b47f91c 100644 --- a/app/Http/Requests/JournalLinkRequest.php +++ b/app/Http/Requests/JournalLinkRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\LinkType; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; diff --git a/app/Http/Requests/LinkTypeFormRequest.php b/app/Http/Requests/LinkTypeFormRequest.php index 202b3fe3b8..bc0a56c933 100644 --- a/app/Http/Requests/LinkTypeFormRequest.php +++ b/app/Http/Requests/LinkTypeFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; diff --git a/app/Http/Requests/MassDeleteJournalRequest.php b/app/Http/Requests/MassDeleteJournalRequest.php index 5700ac4224..fc1c5e93f8 100644 --- a/app/Http/Requests/MassDeleteJournalRequest.php +++ b/app/Http/Requests/MassDeleteJournalRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; diff --git a/app/Http/Requests/MassEditJournalRequest.php b/app/Http/Requests/MassEditJournalRequest.php index d236c3abc0..ccf00f8988 100644 --- a/app/Http/Requests/MassEditJournalRequest.php +++ b/app/Http/Requests/MassEditJournalRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; diff --git a/app/Http/Requests/NewUserFormRequest.php b/app/Http/Requests/NewUserFormRequest.php index 1d0da88187..967d7fe461 100644 --- a/app/Http/Requests/NewUserFormRequest.php +++ b/app/Http/Requests/NewUserFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidAmount; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; diff --git a/app/Http/Requests/ObjectGroupFormRequest.php b/app/Http/Requests/ObjectGroupFormRequest.php index 5febc46231..ccc0cf24f5 100644 --- a/app/Http/Requests/ObjectGroupFormRequest.php +++ b/app/Http/Requests/ObjectGroupFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\ObjectGroup; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; diff --git a/app/Http/Requests/PiggyBankStoreRequest.php b/app/Http/Requests/PiggyBankStoreRequest.php index d7a9058100..868b1cc4bd 100644 --- a/app/Http/Requests/PiggyBankStoreRequest.php +++ b/app/Http/Requests/PiggyBankStoreRequest.php @@ -23,7 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Rules\IsValidPositiveAmount; @@ -60,7 +61,7 @@ class PiggyBankStoreRequest extends FormRequest $accounts = []; } foreach ($accounts as $item) { - $data['accounts'][] = ['account_id' => (int) $item]; + $data['accounts'][] = ['account_id' => (int)$item]; } return $data; @@ -97,7 +98,7 @@ class PiggyBankStoreRequest extends FormRequest $repository = app(AccountRepositoryInterface::class); $types = config('firefly.piggy_bank_account_types'); foreach ($data['accounts'] as $value) { - $accountId = (int) $value; + $accountId = (int)$value; $account = $repository->find($accountId); if (null !== $account) { // check currency here. @@ -123,9 +124,11 @@ class PiggyBankStoreRequest extends FormRequest private function getCurrencyFromData(array $data): TransactionCurrency { - $currencyId = (int) ($data['transaction_currency_id'] ?? 0); - $currency = TransactionCurrency::find($currencyId); - if (null === $currency) { + $currencyId = (int)($data['transaction_currency_id'] ?? 0); + + try { + $currency = Amount::getTransactionCurrencyById($currencyId); + } catch (FireflyException) { return Amount::getPrimaryCurrency(); } diff --git a/app/Http/Requests/PiggyBankUpdateRequest.php b/app/Http/Requests/PiggyBankUpdateRequest.php index 37c895cde4..e0ecaf9614 100644 --- a/app/Http/Requests/PiggyBankUpdateRequest.php +++ b/app/Http/Requests/PiggyBankUpdateRequest.php @@ -23,7 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -129,8 +130,10 @@ class PiggyBankUpdateRequest extends FormRequest private function getCurrencyFromData(array $data): TransactionCurrency { $currencyId = (int) ($data['transaction_currency_id'] ?? 0); - $currency = TransactionCurrency::find($currencyId); - if (null === $currency) { + + try { + $currency = Amount::getTransactionCurrencyById($currencyId); + } catch (FireflyException) { return Amount::getPrimaryCurrency(); } diff --git a/app/Http/Requests/ProfileFormRequest.php b/app/Http/Requests/ProfileFormRequest.php index 65034257b9..1a7956d647 100644 --- a/app/Http/Requests/ProfileFormRequest.php +++ b/app/Http/Requests/ProfileFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; diff --git a/app/Http/Requests/ReconciliationStoreRequest.php b/app/Http/Requests/ReconciliationStoreRequest.php index d9827230ed..e8c8b1a9be 100644 --- a/app/Http/Requests/ReconciliationStoreRequest.php +++ b/app/Http/Requests/ReconciliationStoreRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Rules\IsValidAmount; use FireflyIII\Rules\ValidJournals; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index 8536cdf206..a4f9014078 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\CategoryFactory; @@ -138,7 +138,7 @@ class RecurrenceFormRequest extends FormRequest * @var int $index * @var array $transaction */ - foreach ($return['transactions'] as $index => $transaction) { + foreach ($return['transactions'] as $index => $transaction) { // @phpstan-ignore-line $categoryName = $transaction['category_name'] ?? null; if (null !== $categoryName) { $category = $factory->findOrCreate(null, $categoryName); diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index f3b96f4a3e..7a4fcd3cf9 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use Carbon\Carbon; use Exception; use FireflyIII\Exceptions\FireflyException; @@ -146,7 +146,7 @@ class ReportFormRequest extends FormRequest // if regex for YYYY-MM-DD: $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][\d]|3[01])$/'; $result = preg_match($pattern, $string); - if (false !== $result && 0 !== $result) { + if (0 !== $result) { try { $date = new Carbon($parts[1]); } catch (Exception $e) { // intentional generic exception @@ -184,7 +184,7 @@ class ReportFormRequest extends FormRequest // if regex for YYYY-MM-DD: $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][\d]|3[01])$/'; $result = preg_match($pattern, $string); - if (false !== $result && 0 !== $result) { + if (0 !== $result) { try { $date = new Carbon($parts[0]); } catch (Exception $e) { // intentional generic exception diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index 03b1beb270..f47f4b7c3f 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Rule; use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php index f73cff0b74..6ebc1a6233 100644 --- a/app/Http/Requests/RuleGroupFormRequest.php +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\RuleGroup; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Http/Requests/SelectTransactionsRequest.php b/app/Http/Requests/SelectTransactionsRequest.php index 0cbdba1784..6b27652abc 100644 --- a/app/Http/Requests/SelectTransactionsRequest.php +++ b/app/Http/Requests/SelectTransactionsRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; @@ -41,8 +41,6 @@ class SelectTransactionsRequest extends FormRequest public function rules(): array { return [ - 'start' => 'required|date|after:1970-01-02|before:2038-01-17|before:end|required_with:end', - 'end' => 'required|date|after:1970-01-02|before:2038-01-17|after:start|required_with:start', 'accounts' => 'required', 'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts', ]; diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index 069ffd5ef3..28a733b78c 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Models\Location; use FireflyIII\Models\Tag; use FireflyIII\Support\Request\AppendsLocationData; diff --git a/app/Http/Requests/TestRuleFormRequest.php b/app/Http/Requests/TestRuleFormRequest.php index fa4e289e4e..880c45625e 100644 --- a/app/Http/Requests/TestRuleFormRequest.php +++ b/app/Http/Requests/TestRuleFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\GetRuleConfiguration; use Illuminate\Foundation\Http\FormRequest; diff --git a/app/Http/Requests/TokenFormRequest.php b/app/Http/Requests/TokenFormRequest.php index 19c0f3bc06..a54a1e786b 100644 --- a/app/Http/Requests/TokenFormRequest.php +++ b/app/Http/Requests/TokenFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; diff --git a/app/Http/Requests/TriggerRecurrenceRequest.php b/app/Http/Requests/TriggerRecurrenceRequest.php index 36d16331d6..4b929adaa7 100644 --- a/app/Http/Requests/TriggerRecurrenceRequest.php +++ b/app/Http/Requests/TriggerRecurrenceRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; diff --git a/app/Http/Requests/UserFormRequest.php b/app/Http/Requests/UserFormRequest.php index cc89dff35b..b53be02934 100644 --- a/app/Http/Requests/UserFormRequest.php +++ b/app/Http/Requests/UserFormRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; diff --git a/app/Http/Requests/UserRegistrationRequest.php b/app/Http/Requests/UserRegistrationRequest.php index 8bd3504ea3..64df7a021b 100644 --- a/app/Http/Requests/UserRegistrationRequest.php +++ b/app/Http/Requests/UserRegistrationRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Support\Request\ChecksLogin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Log; diff --git a/app/Jobs/CreateAutoBudgetLimits.php b/app/Jobs/CreateAutoBudgetLimits.php index 396c21222c..e9779d0bc0 100644 --- a/app/Jobs/CreateAutoBudgetLimits.php +++ b/app/Jobs/CreateAutoBudgetLimits.php @@ -37,6 +37,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; /** * Class CreateAutoBudgetLimits @@ -59,7 +60,7 @@ class CreateAutoBudgetLimits implements ShouldQueue $newDate = clone $date; $newDate->startOfDay(); $this->date = $newDate; - app('log')->debug(sprintf('Created new CreateAutoBudgetLimits("%s")', $this->date->format('Y-m-d'))); + Log::debug(sprintf('Created new CreateAutoBudgetLimits("%s")', $this->date->format('Y-m-d'))); } } @@ -70,9 +71,9 @@ class CreateAutoBudgetLimits implements ShouldQueue */ public function handle(): void { - app('log')->debug(sprintf('Now at start of CreateAutoBudgetLimits() job for %s.', $this->date->format('D d M Y'))); + Log::debug(sprintf('Now at start of CreateAutoBudgetLimits() job for %s.', $this->date->format('D d M Y'))); $autoBudgets = AutoBudget::get(); - app('log')->debug(sprintf('Found %d auto budgets.', $autoBudgets->count())); + Log::debug(sprintf('Found %d auto budgets.', $autoBudgets->count())); foreach ($autoBudgets as $autoBudget) { $this->handleAutoBudget($autoBudget); } @@ -84,18 +85,18 @@ class CreateAutoBudgetLimits implements ShouldQueue private function handleAutoBudget(AutoBudget $autoBudget): void { if (null === $autoBudget->budget) { - app('log')->info(sprintf('Auto budget #%d is associated with a deleted budget.', $autoBudget->id)); + Log::info(sprintf('Auto budget #%d is associated with a deleted budget.', $autoBudget->id)); $autoBudget->delete(); return; } if (false === $autoBudget->budget->active) { - app('log')->info(sprintf('Auto budget #%d is associated with an inactive budget.', $autoBudget->id)); + Log::info(sprintf('Auto budget #%d is associated with an inactive budget.', $autoBudget->id)); return; } if (!$this->isMagicDay($autoBudget)) { - app('log')->info( + 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'), @@ -105,11 +106,11 @@ class CreateAutoBudgetLimits implements ShouldQueue $autoBudget->budget->name ) ); - app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } - app('log')->info( + 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'), @@ -131,7 +132,7 @@ class CreateAutoBudgetLimits implements ShouldQueue // that's easy: create one. // do nothing else. $this->createBudgetLimit($autoBudget, $start, $end); - app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } @@ -139,18 +140,18 @@ class CreateAutoBudgetLimits implements ShouldQueue if (!$budgetLimit instanceof BudgetLimit && AutoBudgetType::AUTO_BUDGET_ROLLOVER->value === (int) $autoBudget->auto_budget_type) { // budget limit exists already, $this->createRollover($autoBudget); - app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } if (!$budgetLimit instanceof BudgetLimit && AutoBudgetType::AUTO_BUDGET_ADJUSTED->value === (int) $autoBudget->auto_budget_type) { // budget limit exists already, $this->createAdjustedLimit($autoBudget); - app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } - app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); } /** @@ -193,7 +194,7 @@ class CreateAutoBudgetLimits implements ShouldQueue private function findBudgetLimit(Budget $budget, Carbon $start, Carbon $end): ?BudgetLimit { - app('log')->debug( + Log::debug( sprintf( 'Going to find a budget limit for budget #%d ("%s") between %s and %s', $budget->id, @@ -212,21 +213,21 @@ class CreateAutoBudgetLimits implements ShouldQueue private function createBudgetLimit(AutoBudget $autoBudget, Carbon $start, Carbon $end, ?string $amount = null): void { - app('log')->debug(sprintf('No budget limit exist. Must create one for auto-budget #%d', $autoBudget->id)); + Log::debug(sprintf('No budget limit exist. Must create one for auto-budget #%d', $autoBudget->id)); if (null !== $amount) { - app('log')->debug(sprintf('Amount is overruled and will be set to %s', $amount)); + Log::debug(sprintf('Amount is overruled and will be set to %s', $amount)); } $budgetLimit = new BudgetLimit(); $budgetLimit->budget()->associate($autoBudget->budget); $budgetLimit->transactionCurrency()->associate($autoBudget->transactionCurrency); - $budgetLimit->start_date = $start; - $budgetLimit->end_date = $end; + $budgetLimit->start_date = clone $start; + $budgetLimit->end_date = clone $end; $budgetLimit->amount = $amount ?? $autoBudget->amount; $budgetLimit->period = $autoBudget->period; $budgetLimit->generated = 1; $budgetLimit->save(); - app('log')->debug(sprintf('Created budget limit #%d.', $budgetLimit->id)); + Log::debug(sprintf('Created budget limit #%d.', $budgetLimit->id)); } /** @@ -234,7 +235,7 @@ class CreateAutoBudgetLimits implements ShouldQueue */ private function createRollover(AutoBudget $autoBudget): void { - app('log')->debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id)); + 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); @@ -243,7 +244,7 @@ class CreateAutoBudgetLimits implements ShouldQueue $previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period); $previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period); - app('log')->debug( + Log::debug( sprintf( 'Current period is %s-%s, so previous period is %s-%s', $start->format('Y-m-d'), @@ -257,44 +258,44 @@ class CreateAutoBudgetLimits implements ShouldQueue $budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd); if (!$budgetLimit instanceof BudgetLimit) { - app('log')->debug('No budget limit exists in previous period, so create one.'); + 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); - app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); return; } - app('log')->debug('Budget limit exists for previous period.'); + 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); + $spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency); $currencyId = $autoBudget->transaction_currency_id; $spentAmount = $spent[$currencyId]['sum'] ?? '0'; - app('log')->debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount)); + 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; - app('log')->debug(sprintf('Total amount left for previous budget period is %s', $budgetLeft)); + Log::debug(sprintf('Total amount left for previous budget period is %s', $budgetLeft)); if (-1 !== bccomp('0', $budgetLeft)) { - app('log')->info(sprintf('The amount left is negative, so it will be reset to %s.', $totalAmount)); + 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); - app('log')->info(sprintf('The amount left is positive, so the new amount will be %s.', $totalAmount)); + 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); - app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); } private function createAdjustedLimit(AutoBudget $autoBudget): void { - app('log')->debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id)); + 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); @@ -303,7 +304,7 @@ class CreateAutoBudgetLimits implements ShouldQueue $previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period); $previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period); - app('log')->debug( + Log::debug( sprintf( 'Current period is %s-%s, so previous period is %s-%s', $start->format('Y-m-d'), @@ -317,45 +318,45 @@ class CreateAutoBudgetLimits implements ShouldQueue $budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd); if (!$budgetLimit instanceof BudgetLimit) { - app('log')->debug('No budget limit exists in previous period, so create one.'); + 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; } - app('log')->debug('Budget limit exists for previous period.'); + 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); + $spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency); $currencyId = $autoBudget->transaction_currency_id; $spentAmount = $spent[$currencyId]['sum'] ?? '0'; - app('log')->debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount)); + 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; - app('log')->debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable)); + Log::debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable)); if (-1 !== bccomp($budgetAvailable, $totalAmount)) { - app('log')->info(sprintf('There is no overspending, no need to adjust. Budget limit amount will be %s.', $budgetAvailable)); + 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')) { - app('log')->info(sprintf('There was overspending, so the new amount will be %s.', $budgetAvailable)); + 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')) { - app('log')->info('There was overspending, but so much even this period cant fix that. Reset it to 1.'); + 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'); } - app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id)); + Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id)); } public function setDate(Carbon $date): void diff --git a/app/Jobs/DownloadExchangeRates.php b/app/Jobs/DownloadExchangeRates.php index 0a873f925c..8d22837468 100644 --- a/app/Jobs/DownloadExchangeRates.php +++ b/app/Jobs/DownloadExchangeRates.php @@ -39,6 +39,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; use function Safe\json_decode; @@ -74,7 +75,7 @@ class DownloadExchangeRates implements ShouldQueue $newDate = clone $date; $newDate->startOfDay(); $this->date = $newDate; - app('log')->debug(sprintf('Created new DownloadExchangeRates("%s")', $this->date->format('Y-m-d'))); + Log::debug(sprintf('Created new DownloadExchangeRates("%s")', $this->date->format('Y-m-d'))); } } @@ -83,7 +84,7 @@ class DownloadExchangeRates implements ShouldQueue */ public function handle(): void { - app('log')->debug('Now in handle()'); + Log::debug('Now in handle()'); $currencies = $this->repository->getCompleteSet(); /** @var TransactionCurrency $currency */ @@ -97,7 +98,7 @@ class DownloadExchangeRates implements ShouldQueue */ private function downloadRates(TransactionCurrency $currency): void { - app('log')->debug(sprintf('Now downloading new exchange rates for currency %s.', $currency->code)); + 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); @@ -105,20 +106,20 @@ class DownloadExchangeRates implements ShouldQueue try { $res = $client->get($url); } catch (ConnectException|RequestException $e) { - app('log')->warning(sprintf('Trying to grab "%s" resulted in error "%s".', $url, $e->getMessage())); + Log::warning(sprintf('Trying to grab "%s" resulted in error "%s".', $url, $e->getMessage())); return; } $statusCode = $res->getStatusCode(); if (200 !== $statusCode) { - app('log')->warning(sprintf('Trying to grab "%s" resulted in status code %d.', $url, $statusCode)); + Log::warning(sprintf('Trying to grab "%s" resulted in status code %d.', $url, $statusCode)); return; } $body = (string) $res->getBody(); $json = json_decode($body, true); if (false === $json || null === $json) { - app('log')->warning(sprintf('Trying to grab "%s" resulted in bad JSON.', $url)); + Log::warning(sprintf('Trying to grab "%s" resulted in bad JSON.', $url)); return; } @@ -134,11 +135,11 @@ class DownloadExchangeRates implements ShouldQueue foreach ($rates as $code => $rate) { $to = $this->getCurrency($code); if (!$to instanceof TransactionCurrency) { - app('log')->debug(sprintf('Currency %s is not in use, do not save rate.', $code)); + Log::debug(sprintf('Currency %s is not in use, do not save rate.', $code)); continue; } - app('log')->debug(sprintf('Currency %s is in use.', $code)); + Log::debug(sprintf('Currency %s is in use.', $code)); $this->saveRate($currency, $to, $date, $rate); } } @@ -147,25 +148,25 @@ class DownloadExchangeRates implements ShouldQueue { // if we have it already, don't bother searching for it again. if (array_key_exists($code, $this->active)) { - app('log')->debug(sprintf('Already know what the result is of searching for %s', $code)); + 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 (!$currency instanceof TransactionCurrency) { - app('log')->debug(sprintf('Did not find currency %s.', $code)); + Log::debug(sprintf('Did not find currency %s.', $code)); $this->active[$code] = null; return null; } if (false === $currency->enabled) { - app('log')->debug(sprintf('Currency %s is not enabled.', $code)); + Log::debug(sprintf('Currency %s is not enabled.', $code)); $this->active[$code] = null; return null; } - app('log')->debug(sprintf('Currency %s is enabled.', $code)); + Log::debug(sprintf('Currency %s is enabled.', $code)); $this->active[$code] = $currency; return $currency; @@ -177,7 +178,7 @@ class DownloadExchangeRates implements ShouldQueue $this->repository->setUser($user); $existing = $this->repository->getExchangeRate($from, $to, $date); if (!$existing instanceof CurrencyExchangeRate) { - app('log')->debug(sprintf('Saved rate from %s to %s for user #%d.', $from->code, $to->code, $user->id)); + 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 d1e34a4164..57cfaf7feb 100644 --- a/app/Jobs/MailError.php +++ b/app/Jobs/MailError.php @@ -129,7 +129,7 @@ class MailError extends Job implements ShouldQueue } if (file_exists($file)) { Log::debug(sprintf('Read file in "%s"', $file)); - $limits = json_decode((string) file_get_contents($file), true); + $limits = json_decode(file_get_contents($file), true); } // limit reached? foreach ($types as $type => $info) { diff --git a/app/Jobs/WarnAboutBills.php b/app/Jobs/WarnAboutBills.php index 155454790e..1433f54bc5 100644 --- a/app/Jobs/WarnAboutBills.php +++ b/app/Jobs/WarnAboutBills.php @@ -171,6 +171,8 @@ class WarnAboutBills implements ShouldQueue $enrichment->setUser($bill->user); $enrichment->setStart($start); $enrichment->setEnd($end); + + /** @var Bill $single */ $single = $enrichment->enrichSingle($bill); return [ diff --git a/app/Mail/InvitationMail.php b/app/Mail/InvitationMail.php index d1dd789cbe..14ad8821b5 100644 --- a/app/Mail/InvitationMail.php +++ b/app/Mail/InvitationMail.php @@ -45,7 +45,11 @@ class InvitationMail extends Mailable */ public function __construct(public string $invitee, public string $admin, public string $url) { - $this->host = (string) parse_url($this->url, PHP_URL_HOST); + $host = parse_url($this->url, PHP_URL_HOST); + if (is_array($host)) { + $host = ''; + } + $this->host = (string) $host; } /** diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 1eeb2007dc..7ed2b13f2e 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -40,8 +40,6 @@ class Configuration extends Model /** * TODO can be replaced with native laravel code. - * - * @return mixed */ protected function data(): Attribute { diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index 144bc79e82..80b7e42ca0 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -41,15 +41,12 @@ class TransactionJournalMeta extends Model protected $table = 'journal_meta'; - /** - * @return mixed - */ protected function data(): Attribute { return Attribute::make(get: fn ($value) => json_decode((string) $value, false), set: function ($value) { $data = json_encode($value); - return ['data' => $data, 'hash' => hash('sha256', (string) $data)]; + return ['data' => $data, 'hash' => hash('sha256', $data)]; }); } diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index 7932894770..a836b7fad6 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -24,14 +24,15 @@ declare(strict_types=1); namespace FireflyIII\Models; -use FireflyIII\Enums\WebhookDelivery; -use FireflyIII\Enums\WebhookResponse; -use FireflyIII\Enums\WebhookTrigger; +use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum; +use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum; +use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait; use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -56,7 +57,7 @@ class Webhook extends Model public static function getDeliveries(): array { $array = []; - $set = WebhookDelivery::cases(); + $set = WebhookDeliveryEnum::cases(); foreach ($set as $item) { $array[$item->value] = $item->name; } @@ -67,7 +68,7 @@ class Webhook extends Model public static function getDeliveriesForValidation(): array { $array = []; - $set = WebhookDelivery::cases(); + $set = WebhookDeliveryEnum::cases(); foreach ($set as $item) { $array[$item->name] = $item->value; $array[$item->value] = $item->value; @@ -79,7 +80,7 @@ class Webhook extends Model public static function getResponses(): array { $array = []; - $set = WebhookResponse::cases(); + $set = WebhookResponseEnum::cases(); foreach ($set as $item) { $array[$item->value] = $item->name; } @@ -90,7 +91,7 @@ class Webhook extends Model public static function getResponsesForValidation(): array { $array = []; - $set = WebhookResponse::cases(); + $set = WebhookResponseEnum::cases(); foreach ($set as $item) { $array[$item->name] = $item->value; $array[$item->value] = $item->value; @@ -102,7 +103,7 @@ class Webhook extends Model public static function getTriggers(): array { $array = []; - $set = WebhookTrigger::cases(); + $set = WebhookTriggerEnum::cases(); foreach ($set as $item) { $array[$item->value] = $item->name; } @@ -113,7 +114,7 @@ class Webhook extends Model public static function getTriggersForValidation(): array { $array = []; - $set = WebhookTrigger::cases(); + $set = WebhookTriggerEnum::cases(); foreach ($set as $item) { $array[$item->name] = $item->value; $array[$item->value] = $item->value; @@ -130,7 +131,7 @@ class Webhook extends Model public static function routeBinder(string $value): self { if (auth()->check()) { - $webhookId = (int) $value; + $webhookId = (int)$value; /** @var User $user */ $user = auth()->user(); @@ -155,6 +156,21 @@ class Webhook extends Model return $this->hasMany(WebhookMessage::class); } + public function webhookDeliveries(): BelongsToMany + { + return $this->belongsToMany(WebhookDelivery::class); + } + + public function webhookResponses(): BelongsToMany + { + return $this->belongsToMany(WebhookResponse::class); + } + + public function webhookTriggers(): BelongsToMany + { + return $this->belongsToMany(WebhookTrigger::class); + } + protected function casts(): array { return [ diff --git a/app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php b/app/Models/WebhookDelivery.php similarity index 60% rename from app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php rename to app/Models/WebhookDelivery.php index cf39595a4b..a43a47d417 100644 --- a/app/Repositories/UserGroups/PiggyBank/PiggyBankRepositoryInterface.php +++ b/app/Models/WebhookDelivery.php @@ -1,8 +1,9 @@ (int) $value, + ); + } } diff --git a/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php b/app/Models/WebhookResponse.php similarity index 60% rename from app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php rename to app/Models/WebhookResponse.php index c8321fcd7c..c970e8b70e 100644 --- a/app/Repositories/UserGroups/Journal/JournalRepositoryInterface.php +++ b/app/Models/WebhookResponse.php @@ -1,8 +1,9 @@ (int) $value, + ); + } } diff --git a/app/Events/DestroyedTransactionLink.php b/app/Models/WebhookTrigger.php similarity index 60% rename from app/Events/DestroyedTransactionLink.php rename to app/Models/WebhookTrigger.php index 60d43011ad..4bd8cf444d 100644 --- a/app/Events/DestroyedTransactionLink.php +++ b/app/Models/WebhookTrigger.php @@ -1,8 +1,9 @@ (int) $value, + ); + } } diff --git a/app/Notifications/Admin/UnknownUserLoginAttempt.php b/app/Notifications/Admin/UnknownUserLoginAttempt.php index b72b0b9324..86015ac892 100644 --- a/app/Notifications/Admin/UnknownUserLoginAttempt.php +++ b/app/Notifications/Admin/UnknownUserLoginAttempt.php @@ -26,7 +26,6 @@ namespace FireflyIII\Notifications\Admin; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\Support\Facades\Steam; use Illuminate\Bus\Queueable; @@ -35,7 +34,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; class UnknownUserLoginAttempt extends Notification { @@ -68,20 +66,21 @@ class UnknownUserLoginAttempt extends Notification ; } - /** - * @SuppressWarnings("PHPMD.UnusedFormalParameter") - */ - public function toNtfy(OwnerNotifiable $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); - $message = new Message(); - $ip = Request::ip(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.unknown_user_subject')); - $message->body((string) trans('email.unknown_user_message', ['address' => $this->address, 'ip' => $ip])); - - return $message; - } + // /** + // * @SuppressWarnings("PHPMD.UnusedFormalParameter") + // */ + // + // public function toNtfy(OwnerNotifiable $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); + // $message = new Message(); + // $ip = Request::ip(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.unknown_user_subject')); + // $message->body((string) trans('email.unknown_user_message', ['address' => $this->address, 'ip' => $ip])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Admin/UserInvitation.php b/app/Notifications/Admin/UserInvitation.php index 613bfa1f93..0ea2f8023f 100644 --- a/app/Notifications/Admin/UserInvitation.php +++ b/app/Notifications/Admin/UserInvitation.php @@ -27,7 +27,6 @@ namespace FireflyIII\Notifications\Admin; use FireflyIII\Models\InvitedUser; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; @@ -36,7 +35,6 @@ use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; /** * Class UserInvitation @@ -73,20 +71,20 @@ class UserInvitation extends Notification ; } - /** - * @SuppressWarnings("PHPMD.UnusedFormalParameter") - */ - public function toNtfy(OwnerNotifiable $notifiable): Message - { - Log::debug('Now in toNtfy() for UserInvitation'); - $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.invitation_created_subject')); - $message->body((string) trans('email.invitation_created_body', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email])); - - return $message; - } + // /** + // * @SuppressWarnings("PHPMD.UnusedFormalParameter") + // */ + // public function toNtfy(OwnerNotifiable $notifiable): Message + // { + // Log::debug('Now in toNtfy() for UserInvitation'); + // $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.invitation_created_subject')); + // $message->body((string) trans('email.invitation_created_body', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Admin/UserRegistration.php b/app/Notifications/Admin/UserRegistration.php index 4d6fcf9b29..9847201d61 100644 --- a/app/Notifications/Admin/UserRegistration.php +++ b/app/Notifications/Admin/UserRegistration.php @@ -26,7 +26,6 @@ namespace FireflyIII\Notifications\Admin; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -36,7 +35,6 @@ use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; /** * Class UserRegistration @@ -72,20 +70,20 @@ class UserRegistration extends Notification ; } - /** - * @SuppressWarnings("PHPMD.UnusedFormalParameter") - */ - public function toNtfy(OwnerNotifiable $notifiable): Message - { - Log::debug('Now in toNtfy() for (Admin) UserRegistration'); - $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.registered_subject_admin')); - $message->body((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'invitee' => $this->user->email])); - - return $message; - } + // /** + // * @SuppressWarnings("PHPMD.UnusedFormalParameter") + // */ + // public function toNtfy(OwnerNotifiable $notifiable): Message + // { + // Log::debug('Now in toNtfy() for (Admin) UserRegistration'); + // $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.registered_subject_admin')); + // $message->body((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'invitee' => $this->user->email])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Admin/VersionCheckResult.php b/app/Notifications/Admin/VersionCheckResult.php index deb2f5b4cb..39f449f9bd 100644 --- a/app/Notifications/Admin/VersionCheckResult.php +++ b/app/Notifications/Admin/VersionCheckResult.php @@ -26,14 +26,12 @@ namespace FireflyIII\Notifications\Admin; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Log; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; /** * Class VersionCheckResult @@ -64,20 +62,20 @@ class VersionCheckResult extends Notification ; } - /** - * @SuppressWarnings("PHPMD.UnusedFormalParameter") - */ - public function toNtfy(OwnerNotifiable $notifiable): Message - { - Log::debug('Now in toNtfy() for VersionCheckResult'); - $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.new_version_email_subject')); - $message->body($this->message); - - return $message; - } + // /** + // * @SuppressWarnings("PHPMD.UnusedFormalParameter") + // */ + // public function toNtfy(OwnerNotifiable $notifiable): Message + // { + // Log::debug('Now in toNtfy() for VersionCheckResult'); + // $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.new_version_email_subject')); + // $message->body($this->message); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/ReturnsAvailableChannels.php b/app/Notifications/ReturnsAvailableChannels.php index e3b2737b30..523e95bbaa 100644 --- a/app/Notifications/ReturnsAvailableChannels.php +++ b/app/Notifications/ReturnsAvailableChannels.php @@ -28,7 +28,8 @@ use FireflyIII\Support\Notifications\UrlValidator; use FireflyIII\User; use Illuminate\Support\Facades\Log; use NotificationChannels\Pushover\PushoverChannel; -use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel; + +// use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel; class ReturnsAvailableChannels { @@ -58,16 +59,16 @@ class ReturnsAvailableChannels } } - if (true === config('notifications.channels.ntfy.enabled', false)) { - // validate presence of of Ntfy settings. - if ('' !== (string) app('fireflyconfig')->getEncrypted('ntfy_topic', '')->data) { - Log::debug('Enabled ntfy.'); - $channels[] = NtfyChannel::class; - } - if ('' === (string) app('fireflyconfig')->getEncrypted('ntfy_topic', '')->data) { - Log::warning('No topic name for Ntfy, channel is disabled.'); - } - } + // if (true === config('notifications.channels.ntfy.enabled', false)) { + // // validate presence of of Ntfy settings. + // if ('' !== (string) app('fireflyconfig')->getEncrypted('ntfy_topic', '')->data) { + // Log::debug('Enabled ntfy.'); + // $channels[] = NtfyChannel::class; + // } + // if ('' === (string) app('fireflyconfig')->getEncrypted('ntfy_topic', '')->data) { + // Log::warning('No topic name for Ntfy, channel is disabled.'); + // } + // } // pushover if (true === config('notifications.channels.pushover.enabled', false)) { @@ -99,17 +100,17 @@ class ReturnsAvailableChannels } } - // validate presence of of Ntfy settings. - if (true === config('notifications.channels.nfy.enabled', false)) { - $ntfyTopic = (string) app('preferences')->getEncryptedForUser($user, 'ntfy_topic', '')->data; - if ('' !== $ntfyTopic) { - Log::debug(sprintf('Enabled ntfy, "%s"', $ntfyTopic)); - $channels[] = NtfyChannel::class; - } - if ('' === (string) app('preferences')->getEncryptedForUser($user, 'ntfy_topic', '')->data) { - Log::warning('No topic name for Ntfy, channel is disabled.'); - } - } + // // validate presence of of Ntfy settings. + // if (true === config('notifications.channels.nfy.enabled', false)) { + // $ntfyTopic = (string) app('preferences')->getEncryptedForUser($user, 'ntfy_topic', '')->data; + // if ('' !== $ntfyTopic) { + // Log::debug(sprintf('Enabled ntfy, "%s"', $ntfyTopic)); + // $channels[] = NtfyChannel::class; + // } + // if ('' === (string) app('preferences')->getEncryptedForUser($user, 'ntfy_topic', '')->data) { + // Log::warning('No topic name for Ntfy, channel is disabled.'); + // } + // } // pushover if (true === config('notifications.channels.slack.enabled', false)) { diff --git a/app/Notifications/Security/DisabledMFANotification.php b/app/Notifications/Security/DisabledMFANotification.php index 511b5cecab..2d7fb02922 100644 --- a/app/Notifications/Security/DisabledMFANotification.php +++ b/app/Notifications/Security/DisabledMFANotification.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Security; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; class DisabledMFANotification extends Notification { @@ -65,16 +63,16 @@ class DisabledMFANotification extends Notification return new MailMessage()->markdown('emails.security.disabled-mfa', ['user' => $this->user, 'ip' => $ip, 'host' => $host, 'userAgent' => $userAgent, 'time' => $time])->subject($subject); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.disabled_mfa_subject')); - $message->body((string) trans('email.disabled_mfa_slack', ['email' => $this->user->email])); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.disabled_mfa_subject')); + // $message->body((string) trans('email.disabled_mfa_slack', ['email' => $this->user->email])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Security/EnabledMFANotification.php b/app/Notifications/Security/EnabledMFANotification.php index e3863aee9e..d661aec9bb 100644 --- a/app/Notifications/Security/EnabledMFANotification.php +++ b/app/Notifications/Security/EnabledMFANotification.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Security; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; class EnabledMFANotification extends Notification { @@ -65,16 +63,16 @@ class EnabledMFANotification extends Notification return new MailMessage()->markdown('emails.security.enabled-mfa', ['user' => $this->user, 'ip' => $ip, 'host' => $host, 'userAgent' => $userAgent, 'time' => $time])->subject($subject); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.enabled_mfa_subject')); - $message->body((string) trans('email.enabled_mfa_slack', ['email' => $this->user->email])); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.enabled_mfa_subject')); + // $message->body((string) trans('email.enabled_mfa_slack', ['email' => $this->user->email])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Security/MFABackupFewLeftNotification.php b/app/Notifications/Security/MFABackupFewLeftNotification.php index 4302d10c46..8f691e3dd0 100644 --- a/app/Notifications/Security/MFABackupFewLeftNotification.php +++ b/app/Notifications/Security/MFABackupFewLeftNotification.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Security; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; class MFABackupFewLeftNotification extends Notification { @@ -65,16 +63,16 @@ class MFABackupFewLeftNotification extends Notification return new MailMessage()->markdown('emails.security.few-backup-codes', ['user' => $this->user, 'count' => $this->count, 'ip' => $ip, 'host' => $host, 'userAgent' => $userAgent, 'time' => $time])->subject($subject); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.mfa_few_backups_left_subject')); - $message->body((string) trans('email.mfa_few_backups_left_slack', ['email' => $this->user->email, 'count' => $this->count])); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.mfa_few_backups_left_subject')); + // $message->body((string) trans('email.mfa_few_backups_left_slack', ['email' => $this->user->email, 'count' => $this->count])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Security/MFABackupNoLeftNotification.php b/app/Notifications/Security/MFABackupNoLeftNotification.php index 246119e901..0c1fe520a6 100644 --- a/app/Notifications/Security/MFABackupNoLeftNotification.php +++ b/app/Notifications/Security/MFABackupNoLeftNotification.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Security; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; class MFABackupNoLeftNotification extends Notification { @@ -65,16 +63,16 @@ class MFABackupNoLeftNotification extends Notification return new MailMessage()->markdown('emails.security.no-backup-codes', ['user' => $this->user, 'ip' => $ip, 'host' => $host, 'userAgent' => $userAgent, 'time' => $time])->subject($subject); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.mfa_no_backups_left_subject')); - $message->body((string) trans('email.mfa_no_backups_left_slack', ['email' => $this->user->email])); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.mfa_no_backups_left_subject')); + // $message->body((string) trans('email.mfa_no_backups_left_slack', ['email' => $this->user->email])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Security/MFAManyFailedAttemptsNotification.php b/app/Notifications/Security/MFAManyFailedAttemptsNotification.php index 400cee87e7..a235fcbd28 100644 --- a/app/Notifications/Security/MFAManyFailedAttemptsNotification.php +++ b/app/Notifications/Security/MFAManyFailedAttemptsNotification.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Security; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; class MFAManyFailedAttemptsNotification extends Notification { @@ -62,16 +60,16 @@ class MFAManyFailedAttemptsNotification extends Notification return new MailMessage()->markdown('emails.security.many-failed-attempts', ['user' => $this->user, 'count' => $this->count, 'ip' => $ip, 'host' => $host, 'userAgent' => $userAgent, 'time' => $time])->subject($subject); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.mfa_many_failed_subject')); - $message->body((string) trans('email.mfa_many_failed_slack', ['email' => $this->user->email, 'count' => $this->count])); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.mfa_many_failed_subject')); + // $message->body((string) trans('email.mfa_many_failed_slack', ['email' => $this->user->email, 'count' => $this->count])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Security/MFAUsedBackupCodeNotification.php b/app/Notifications/Security/MFAUsedBackupCodeNotification.php index 01a3aac01c..3c0d61739f 100644 --- a/app/Notifications/Security/MFAUsedBackupCodeNotification.php +++ b/app/Notifications/Security/MFAUsedBackupCodeNotification.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Security; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; class MFAUsedBackupCodeNotification extends Notification { @@ -65,16 +63,16 @@ class MFAUsedBackupCodeNotification extends Notification return new MailMessage()->markdown('emails.security.used-backup-code', ['user' => $this->user, 'ip' => $ip, 'host' => $host, 'userAgent' => $userAgent, 'time' => $time])->subject($subject); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.used_backup_code_subject')); - $message->body((string) trans('email.used_backup_code_slack', ['email' => $this->user->email])); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.used_backup_code_subject')); + // $message->body((string) trans('email.used_backup_code_slack', ['email' => $this->user->email])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Security/NewBackupCodesNotification.php b/app/Notifications/Security/NewBackupCodesNotification.php index ede672e96b..a07d4282d3 100644 --- a/app/Notifications/Security/NewBackupCodesNotification.php +++ b/app/Notifications/Security/NewBackupCodesNotification.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Security; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; class NewBackupCodesNotification extends Notification { @@ -65,16 +63,16 @@ class NewBackupCodesNotification extends Notification return new MailMessage()->markdown('emails.security.new-backup-codes', ['user' => $this->user, 'ip' => $ip, 'host' => $host, 'userAgent' => $userAgent, 'time' => $time])->subject($subject); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.new_backup_codes_subject')); - $message->body((string) trans('email.new_backup_codes_slack', ['email' => $this->user->email])); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.new_backup_codes_subject')); + // $message->body((string) trans('email.new_backup_codes_slack', ['email' => $this->user->email])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Security/UserFailedLoginAttempt.php b/app/Notifications/Security/UserFailedLoginAttempt.php index 08159020be..8f9362d76f 100644 --- a/app/Notifications/Security/UserFailedLoginAttempt.php +++ b/app/Notifications/Security/UserFailedLoginAttempt.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Security; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; @@ -35,7 +34,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; class UserFailedLoginAttempt extends Notification { @@ -63,17 +61,17 @@ class UserFailedLoginAttempt extends Notification return new MailMessage()->markdown('emails.security.failed-login', ['user' => $this->user, 'ip' => $ip, 'host' => $host, 'userAgent' => $userAgent, 'time' => $time])->subject($subject); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $ip = Request::ip(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.failed_login_subject')); - $message->body((string) trans('email.failed_login_message', ['ip' => $ip, 'email' => $this->user->email])); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $ip = Request::ip(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.failed_login_subject')); + // $message->body((string) trans('email.failed_login_message', ['ip' => $ip, 'email' => $this->user->email])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/Test/OwnerTestNotificationNtfy.php b/app/Notifications/Test/OwnerTestNotificationNtfy.php index 9f72e8544b..6edae32817 100644 --- a/app/Notifications/Test/OwnerTestNotificationNtfy.php +++ b/app/Notifications/Test/OwnerTestNotificationNtfy.php @@ -25,11 +25,8 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Test; use FireflyIII\Notifications\Notifiables\OwnerNotifiable; -use FireflyIII\Notifications\ReturnsSettings; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; -use Ntfy\Message; -use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel; // use Illuminate\Notifications\Slack\SlackMessage; @@ -49,26 +46,20 @@ class OwnerTestNotificationNtfy extends Notification ]; } - /** - * @SuppressWarnings("PHPMD.UnusedFormalParameter") - */ - public function toNtfy(OwnerNotifiable $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.admin_test_subject')); - $message->body((string) trans('email.admin_test_message', ['channel' => 'ntfy'])); - $message->tags(['white_check_mark']); + // public function toNtfy(OwnerNotifiable $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'owner', null); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.admin_test_subject')); + // $message->body((string) trans('email.admin_test_message', ['channel' => 'ntfy'])); + // $message->tags(['white_check_mark']); + // + // return $message; + // } - return $message; - } - - /** - * @SuppressWarnings("PHPMD.UnusedFormalParameter") - */ - public function via(OwnerNotifiable $notifiable): array - { - return [NtfyChannel::class]; - } + // public function via(OwnerNotifiable $notifiable): array + // { + // return [NtfyChannel::class]; + // } } diff --git a/app/Notifications/Test/UserTestNotificationNtfy.php b/app/Notifications/Test/UserTestNotificationNtfy.php index babcb459f8..7517e962c7 100644 --- a/app/Notifications/Test/UserTestNotificationNtfy.php +++ b/app/Notifications/Test/UserTestNotificationNtfy.php @@ -24,12 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Notifications\Test; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; -use Ntfy\Message; -use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel; // use Illuminate\Notifications\Slack\SlackMessage; @@ -49,26 +46,20 @@ class UserTestNotificationNtfy extends Notification ]; } - /** - * @SuppressWarnings("PHPMD.UnusedFormalParameter") - */ - public function toNtfy(User $user): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $user); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.admin_test_subject')); - $message->body((string) trans('email.admin_test_message', ['channel' => 'ntfy'])); - $message->tags(['white_check_mark']); + // public function toNtfy(User $user): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $user); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.admin_test_subject')); + // $message->body((string) trans('email.admin_test_message', ['channel' => 'ntfy'])); + // $message->tags(['white_check_mark']); + // + // return $message; + // } - return $message; - } - - /** - * @SuppressWarnings("PHPMD.UnusedFormalParameter") - */ - public function via(User $user): array - { - return [NtfyChannel::class]; - } + // public function via(User $user): array + // { + // return [NtfyChannel::class]; + // } } diff --git a/app/Notifications/User/BillReminder.php b/app/Notifications/User/BillReminder.php index aebdf1333b..6765545d92 100644 --- a/app/Notifications/User/BillReminder.php +++ b/app/Notifications/User/BillReminder.php @@ -26,14 +26,12 @@ namespace FireflyIII\Notifications\User; use FireflyIII\Models\Bill; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; /** * Class BillReminder @@ -73,16 +71,16 @@ class BillReminder extends Notification return (string) trans(sprintf('email.bill_warning_subject_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title($this->getSubject()); - $message->body((string) trans('email.bill_warning_please_action')); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title($this->getSubject()); + // $message->body((string) trans('email.bill_warning_please_action')); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/User/NewAccessToken.php b/app/Notifications/User/NewAccessToken.php index 29d2c51b36..d6f4728e25 100644 --- a/app/Notifications/User/NewAccessToken.php +++ b/app/Notifications/User/NewAccessToken.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\User; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; @@ -35,7 +34,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; /** * Class NewAccessToken @@ -68,16 +66,16 @@ class NewAccessToken extends Notification ; } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.access_token_created_subject')); - $message->body((string) trans('email.access_token_created_body')); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.access_token_created_subject')); + // $message->body((string) trans('email.access_token_created_body')); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/User/RuleActionFailed.php b/app/Notifications/User/RuleActionFailed.php index 9a518edb2e..4a2cd4fd75 100644 --- a/app/Notifications/User/RuleActionFailed.php +++ b/app/Notifications/User/RuleActionFailed.php @@ -25,13 +25,11 @@ declare(strict_types=1); namespace FireflyIII\Notifications\User; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; /** * Class RuleActionFailed @@ -65,15 +63,15 @@ class RuleActionFailed extends Notification ]; } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->body($this->message); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->body($this->message); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/User/SubscriptionsOverdueReminder.php b/app/Notifications/User/SubscriptionsOverdueReminder.php index 2db7fa53d5..7f46a333ae 100644 --- a/app/Notifications/User/SubscriptionsOverdueReminder.php +++ b/app/Notifications/User/SubscriptionsOverdueReminder.php @@ -1,12 +1,32 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Notifications\User; use Carbon\Carbon; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\User; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; @@ -42,9 +62,7 @@ class SubscriptionsOverdueReminder extends Notification 'bill' => $item['bill'], ]; $current['pay_dates'] = array_map( - static function (string $date): string { - return new Carbon($date)->isoFormat((string)trans('config.month_and_day_moment_js')); - }, + static fn (string $date): string => new Carbon($date)->isoFormat((string)trans('config.month_and_day_moment_js')), $item['dates']['pay_dates'] ); $info[] = $current; @@ -66,16 +84,16 @@ class SubscriptionsOverdueReminder extends Notification return (string)trans('email.subscriptions_overdue_subject_single'); } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title($this->getSubject()); - $message->body((string)trans('email.bill_warning_please_action')); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title($this->getSubject()); + // $message->body((string)trans('email.bill_warning_please_action')); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/User/UserLogin.php b/app/Notifications/User/UserLogin.php index 5fc368d7e0..fc49a102f3 100644 --- a/app/Notifications/User/UserLogin.php +++ b/app/Notifications/User/UserLogin.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\User; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; /** * Class UserLogin @@ -65,18 +63,18 @@ class UserLogin extends Notification ; } - public function toNtfy(User $notifiable): Message - { - $ip = Request::ip(); - $host = Steam::getHostName($ip); - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->title((string) trans('email.login_from_new_ip')); - $message->body((string) trans('email.slack_login_from_new_ip', ['ip' => $ip, 'host' => $host])); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $ip = Request::ip(); + // $host = Steam::getHostName($ip); + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->title((string) trans('email.login_from_new_ip')); + // $message->body((string) trans('email.slack_login_from_new_ip', ['ip' => $ip, 'host' => $host])); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Notifications/User/UserNewPassword.php b/app/Notifications/User/UserNewPassword.php index 16ffb0b31a..b9d708b698 100644 --- a/app/Notifications/User/UserNewPassword.php +++ b/app/Notifications/User/UserNewPassword.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Notifications\User; use FireflyIII\Notifications\ReturnsAvailableChannels; -use FireflyIII\Notifications\ReturnsSettings; use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Bus\Queueable; @@ -34,7 +33,6 @@ use Illuminate\Notifications\Messages\SlackMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Request; use NotificationChannels\Pushover\PushoverMessage; -use Ntfy\Message; /** * Class UserNewPassword @@ -70,15 +68,15 @@ class UserNewPassword extends Notification ; } - public function toNtfy(User $notifiable): Message - { - $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); - $message = new Message(); - $message->topic($settings['ntfy_topic']); - $message->body((string) trans('email.reset_pw_message')); - - return $message; - } + // public function toNtfy(User $notifiable): Message + // { + // $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); + // $message = new Message(); + // $message->topic($settings['ntfy_topic']); + // $message->body((string) trans('email.reset_pw_message')); + // + // return $message; + // } /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index afd301bf22..c9039b7c0f 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -38,6 +38,7 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Services\Internal\Destroy\AccountDestroyService; use FireflyIII\Services\Internal\Update\AccountUpdateService; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; @@ -400,7 +401,7 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac } $currencyId = (int) $this->getMetaValue($account, 'currency_id'); if ($currencyId > 0) { - return TransactionCurrency::find($currencyId); + return Amount::getTransactionCurrencyById($currencyId); } return null; @@ -478,20 +479,25 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac public function getAccountsByType(array $types, ?array $sort = []): Collection { - $res = array_intersect([AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value], $types); - $query = $this->user->accounts(); + $res = array_intersect([AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value], $types); + $query = $this->user->accounts(); if (0 !== count($types)) { $query->accountTypeIn($types); } - // add sort parameters. At this point they're filtered to allowed fields to sort by: + // add sort parameters + $allowed = config('firefly.allowed_db_sort_parameters.Account', []); + $sorted = 0; if (0 !== count($sort)) { foreach ($sort as $param) { - $query->orderBy($param[0], $param[1]); + if (in_array($param[0], $allowed, true)) { + $query->orderBy($param[0], $param[1]); + ++$sorted; + } } } - if (0 === count($sort)) { + if (0 === $sorted) { if (0 !== count($res)) { $query->orderBy('accounts.order', 'ASC'); } diff --git a/app/Repositories/Account/OperationsRepository.php b/app/Repositories/Account/OperationsRepository.php index e402922365..71aebb11a3 100644 --- a/app/Repositories/Account/OperationsRepository.php +++ b/app/Repositories/Account/OperationsRepository.php @@ -28,6 +28,7 @@ use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Report\Summarizer\TransactionSummarizer; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; @@ -82,7 +83,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn ]; $array[$currencyId]['transaction_journals'][$journalId] = [ - 'amount' => app('steam')->{$direction}((string) $journal['amount']), // @phpstan-ignore-line + 'amount' => Steam::{$direction}((string) $journal['amount']), // @phpstan-ignore-line 'date' => $journal['date'], 'transaction_journal_id' => $journalId, 'budget_name' => $journal['budget_name'], @@ -330,7 +331,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn $currencyId = $journal['currency_id']; $sourceKey = sprintf('%d-%d', $currencyId, $sourceId); $destKey = sprintf('%d-%d', $currencyId, $destinationId); - $amount = app('steam')->positive($journal['amount']); + $amount = Steam::positive($journal['amount']); // source first $return[$sourceKey] ??= [ @@ -361,11 +362,11 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn ]; // source account? money goes out! - $return[$sourceKey]['out'] = bcadd((string) $return[$sourceKey]['out'], (string) app('steam')->negative($amount)); + $return[$sourceKey]['out'] = bcadd((string) $return[$sourceKey]['out'], Steam::negative($amount)); $return[$sourceKey]['difference'] = bcadd($return[$sourceKey]['out'], (string) $return[$sourceKey]['in']); // destination account? money comes in: - $return[$destKey]['in'] = bcadd((string) $return[$destKey]['in'], (string) $amount); + $return[$destKey]['in'] = bcadd((string) $return[$destKey]['in'], $amount); $return[$destKey]['difference'] = bcadd((string) $return[$destKey]['out'], $return[$destKey]['in']); // foreign currency @@ -373,7 +374,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn $currencyId = $journal['foreign_currency_id']; $sourceKey = sprintf('%d-%d', $currencyId, $sourceId); $destKey = sprintf('%d-%d', $currencyId, $destinationId); - $amount = app('steam')->positive($journal['foreign_amount']); + $amount = Steam::positive($journal['foreign_amount']); // same as above: // source first @@ -404,11 +405,11 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn 'currency_code' => $journal['foreign_currency_code'], ]; // source account? money goes out! (same as above) - $return[$sourceKey]['out'] = bcadd((string) $return[$sourceKey]['out'], (string) app('steam')->negative($amount)); + $return[$sourceKey]['out'] = bcadd((string) $return[$sourceKey]['out'], Steam::negative($amount)); $return[$sourceKey]['difference'] = bcadd($return[$sourceKey]['out'], (string) $return[$sourceKey]['in']); // destination account? money comes in: - $return[$destKey]['in'] = bcadd((string) $return[$destKey]['in'], (string) $amount); + $return[$destKey]['in'] = bcadd((string) $return[$destKey]['in'], $amount); $return[$destKey]['difference'] = bcadd((string) $return[$destKey]['out'], $return[$destKey]['in']); } diff --git a/app/Repositories/Budget/AvailableBudgetRepository.php b/app/Repositories/Budget/AvailableBudgetRepository.php index fb789f3ebb..177900d154 100644 --- a/app/Repositories/Budget/AvailableBudgetRepository.php +++ b/app/Repositories/Budget/AvailableBudgetRepository.php @@ -209,7 +209,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface, U #[Deprecated] public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget { - /** @var null|AvailableBudget */ + /** @var null|AvailableBudget $availableBudget */ $availableBudget = $this->user->availableBudgets() ->where('transaction_currency_id', $currency->id) ->where('start_date', $start->format('Y-m-d')) diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 56de39dcc6..3aa57328de 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -40,6 +40,7 @@ use FireflyIII\Models\RuleTrigger; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Services\Internal\Destroy\BudgetDestroyService; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; @@ -268,7 +269,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface */ foreach ($budgets as $index => $budget) { $budget->order = $index + 1; - $budget->save(); + $budget->saveQuietly(); } // other budgets, set to 0. $this->user->budgets()->where('active', 0)->update(['order' => 0]); @@ -309,10 +310,10 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface return $budget; } - if (0 === $autoBudgetType && !$autoBudget instanceof AutoBudget) { + if (0 === $autoBudgetType) { return $budget; } - if (null === $autoBudgetType && !$autoBudget instanceof AutoBudget) { + if (null === $autoBudgetType) { return $budget; } $this->updateAutoBudget($budget, $data); @@ -632,7 +633,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface 'decimal_places' => $journal['currency_decimal_places'], 'sum' => '0', ]; - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], (string) app('steam')->negative($journal['amount'])); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], Steam::negative($journal['amount'])); // also do foreign amount: $foreignId = (int) $journal['foreign_currency_id']; @@ -645,7 +646,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface 'decimal_places' => $journal['foreign_currency_decimal_places'], 'sum' => '0', ]; - $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], (string) app('steam')->negative($journal['foreign_amount'])); + $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], Steam::negative($journal['foreign_amount'])); } } @@ -694,7 +695,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface 'decimal_places' => $journal['currency_decimal_places'], 'sum' => '0', ]; - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], (string) app('steam')->negative($journal['amount'])); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], Steam::negative($journal['amount'])); // also do foreign amount: $foreignId = (int) $journal['foreign_currency_id']; @@ -707,7 +708,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface 'decimal_places' => $journal['foreign_currency_decimal_places'], 'sum' => '0', ]; - $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], (string) app('steam')->negative($journal['foreign_amount'])); + $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], Steam::negative($journal['foreign_amount'])); } } diff --git a/app/Repositories/Budget/OperationsRepository.php b/app/Repositories/Budget/OperationsRepository.php index 052a6622b8..211c4fa1f5 100644 --- a/app/Repositories/Budget/OperationsRepository.php +++ b/app/Repositories/Budget/OperationsRepository.php @@ -166,7 +166,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn $currencyDecimalPlaces = $journal['currency_decimal_places']; } if (true === $convertToPrimary && $journalCurrencyId !== $currencyId) { - $currencies[$journalCurrencyId] ??= TransactionCurrency::find($journalCurrencyId); + $currencies[$journalCurrencyId] ??= Amount::getTransactionCurrencyById($journalCurrencyId); $amount = $converter->convert($currencies[$journalCurrencyId], $primaryCurrency, $journal['date'], $amount); } @@ -294,9 +294,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn $summarizer->setConvertToPrimary($convertToPrimary); // filter $journals by range AND currency if it is present. - $expenses = array_filter($expenses, static function (array $expense) use ($start, $end, $transactionCurrency): bool { - return $expense['date']->between($start, $end) && $expense['currency_id'] === $transactionCurrency->id; - }); + $expenses = array_filter($expenses, static fn (array $expense): bool => $expense['date']->between($start, $end) && $expense['currency_id'] === $transactionCurrency->id); return $summarizer->groupByCurrencyId($expenses, 'negative', false); } @@ -308,9 +306,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn $summarizer->setConvertToPrimary($convertToPrimary); // filter $journals by range AND currency if it is present. - $expenses = array_filter($expenses, static function (array $expense) use ($budget): bool { - return $expense['budget_id'] === $budget->id; - }); + $expenses = array_filter($expenses, static fn (array $expense): bool => $expense['budget_id'] === $budget->id); return $summarizer->groupByCurrencyId($expenses, 'negative', false); } diff --git a/app/Repositories/Category/NoCategoryRepository.php b/app/Repositories/Category/NoCategoryRepository.php index dbaf464eb8..43c64c27ef 100644 --- a/app/Repositories/Category/NoCategoryRepository.php +++ b/app/Repositories/Category/NoCategoryRepository.php @@ -27,6 +27,7 @@ namespace FireflyIII\Repositories\Category; use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Report\Summarizer\TransactionSummarizer; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; @@ -174,7 +175,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface, UserGroupIn 'currency_code' => $journal['currency_code'], 'currency_decimal_places' => $journal['currency_decimal_places'], ]; - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], (string) app('steam')->positive($journal['amount'])); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], Steam::positive($journal['amount'])); } return $array; @@ -202,7 +203,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface, UserGroupIn 'currency_code' => $journal['currency_code'], 'currency_decimal_places' => $journal['currency_decimal_places'], ]; - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], (string) app('steam')->positive($journal['amount'])); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], Steam::positive($journal['amount'])); } return $array; diff --git a/app/Repositories/Category/OperationsRepository.php b/app/Repositories/Category/OperationsRepository.php index 26599d2c61..e020782f40 100644 --- a/app/Repositories/Category/OperationsRepository.php +++ b/app/Repositories/Category/OperationsRepository.php @@ -29,6 +29,7 @@ use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Category; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Report\Summarizer\TransactionSummarizer; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; @@ -403,7 +404,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn 'currency_code' => $currencyCode, 'currency_decimal_places' => $currencyDecimalPlaces, ]; - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], (string) app('steam')->positive($amount)); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], Steam::positive($amount)); } return $array; @@ -440,7 +441,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn 'currency_code' => $journal['currency_code'], 'currency_decimal_places' => $journal['currency_decimal_places'], ]; - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], (string) app('steam')->positive($journal['amount'])); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], Steam::positive($journal['amount'])); } return $array; diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index ce52a5f503..cb5e24858b 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -38,6 +38,7 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Services\Internal\Destroy\CurrencyDestroyService; use FireflyIII\Services\Internal\Update\CurrencyUpdateService; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; @@ -84,7 +85,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf } // is being used in accounts: - $meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string) $currency->id))->count(); + $meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string)$currency->id))->count(); if ($meta > 0) { Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); @@ -92,7 +93,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf } // second search using integer check. - $meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((int) $currency->id))->count(); + $meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((int)$currency->id))->count(); if ($meta > 0) { Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); @@ -183,7 +184,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf return $all->map(static function (TransactionCurrency $current) use ($local) { $hasId = $local->contains(static fn (TransactionCurrency $entry) => $entry->id === $current->id); - $isPrimary = $local->contains(static fn (TransactionCurrency $entry) => 1 === (int) $entry->pivot->group_default && $entry->id === $current->id); + $isPrimary = $local->contains(static fn (TransactionCurrency $entry) => 1 === (int)$entry->pivot->group_default && $entry->id === $current->id); $current->userGroupEnabled = $hasId; $current->userGroupNative = $isPrimary; @@ -196,7 +197,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf $all = $this->userGroup->currencies()->orderBy('code', 'ASC')->withPivot(['group_default'])->get(); $all->map(static function (TransactionCurrency $current) { // @phpstan-ignore-line $current->userGroupEnabled = true; - $current->userGroupNative = 1 === (int) $current->pivot->group_default; + $current->userGroupNative = 1 === (int)$current->pivot->group_default; return $current; }); @@ -261,16 +262,15 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency { Log::debug(sprintf('Now in findCurrencyNull(%s, "%s")', var_export($currencyId, true), $currencyCode)); - $result = $this->find((int) $currencyId); + $result = $this->find((int)$currencyId); if ($result instanceof TransactionCurrency) { Log::debug(sprintf('Found currency by ID: %s', $result->code)); return $result; } - if (null === $result) { - Log::debug(sprintf('Searching for currency with code "%s"...', $currencyCode)); - $result = $this->findByCode((string) $currencyCode); - } + Log::debug(sprintf('Searching for currency with code "%s"...', $currencyCode)); + $result = $this->findByCode((string)$currencyCode); + if ($result instanceof TransactionCurrency && false === $result->enabled) { Log::debug(sprintf('Also enabled currency %s', $result->code)); $this->enable($result); @@ -283,7 +283,13 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf #[Override] public function find(int $currencyId): ?TransactionCurrency { - return TransactionCurrency::find($currencyId); + try { + $result = Amount::getTransactionCurrencyById($currencyId); + } catch (FireflyException) { + return null; + } + + return $result; } /** @@ -291,7 +297,13 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf */ public function findByCode(string $currencyCode): ?TransactionCurrency { - return TransactionCurrency::where('code', $currencyCode)->first(); + try { + $result = Amount::getTransactionCurrencyByCode($currencyCode); + } catch (FireflyException) { + return null; + } + + return $result; } public function enable(TransactionCurrency $currency): void @@ -361,7 +373,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf return CurrencyExchangeRate::create( [ 'user_id' => $this->user->id, - 'user_group_id' => $this->user->user_group_id, + 'user_group_id' => $this->userGroup->id, 'from_currency_id' => $fromCurrency->id, 'to_currency_id' => $toCurrency->id, 'date' => $date, diff --git a/app/Repositories/Journal/JournalCLIRepository.php b/app/Repositories/Journal/JournalCLIRepository.php index b908dcc82f..ea923bfffc 100644 --- a/app/Repositories/Journal/JournalCLIRepository.php +++ b/app/Repositories/Journal/JournalCLIRepository.php @@ -182,7 +182,7 @@ class JournalCLIRepository implements JournalCLIRepositoryInterface, UserGroupIn $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 + $result = $query->get(['transaction_journals.id as id', DB::raw('count(transactions.id) as transaction_count')]); $journalIds = []; /** @var stdClass $row */ diff --git a/app/Repositories/LinkType/LinkTypeRepository.php b/app/Repositories/LinkType/LinkTypeRepository.php index 9b2924b191..1110eb62ee 100644 --- a/app/Repositories/LinkType/LinkTypeRepository.php +++ b/app/Repositories/LinkType/LinkTypeRepository.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\LinkType; -use FireflyIII\Events\DestroyedTransactionLink; +use Exception; use FireflyIII\Models\LinkType; use FireflyIII\Models\Note; use FireflyIII\Models\TransactionJournal; @@ -31,7 +31,6 @@ use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; -use Exception; /** * Class LinkTypeRepository. @@ -57,13 +56,13 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface, UserGroupInterf public function update(LinkType $linkType, array $data): LinkType { - if (array_key_exists('name', $data) && '' !== (string) $data['name']) { + if (array_key_exists('name', $data) && '' !== (string)$data['name']) { $linkType->name = $data['name']; } - if (array_key_exists('inward', $data) && '' !== (string) $data['inward']) { + if (array_key_exists('inward', $data) && '' !== (string)$data['inward']) { $linkType->inward = $data['inward']; } - if (array_key_exists('outward', $data) && '' !== (string) $data['outward']) { + if (array_key_exists('outward', $data) && '' !== (string)$data['outward']) { $linkType->outward = $data['outward']; } $linkType->save(); @@ -76,7 +75,6 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface, UserGroupInterf */ public function destroyLink(TransactionJournalLink $link): bool { - event(new DestroyedTransactionLink($link)); $link->delete(); return true; @@ -175,7 +173,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface, UserGroupInterf */ public function storeLink(array $information, TransactionJournal $inward, TransactionJournal $outward): ?TransactionJournalLink { - $linkType = $this->find((int) ($information['link_type_id'] ?? 0)); + $linkType = $this->find((int)($information['link_type_id'] ?? 0)); if (!$linkType instanceof LinkType) { $linkType = $this->findByName($information['link_type_name']); @@ -207,7 +205,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface, UserGroupInterf $link->save(); // make note in noteable: - $this->setNoteText($link, (string) $information['notes']); + $this->setNoteText($link, (string)$information['notes']); return $link; } diff --git a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php index 658ca6e1c8..0acb4b7ed8 100644 --- a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php +++ b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\PiggyBank; +use Exception; use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Events\Model\PiggyBank\ChangedName; use FireflyIII\Exceptions\FireflyException; @@ -32,12 +33,11 @@ use FireflyIII\Models\Account; use FireflyIII\Models\Note; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use Illuminate\Support\Facades\Log; -use Exception; /** * Trait ModifiesPiggyBanks @@ -67,7 +67,7 @@ trait ModifiesPiggyBanks { $currentAmount = $this->getCurrentAmount($piggyBank, $account); $pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot; - $pivot->current_amount = bcsub((string)$currentAmount, $amount); + $pivot->current_amount = bcsub($currentAmount, $amount); $pivot->native_current_amount = null; // also update native_current_amount. @@ -90,7 +90,7 @@ trait ModifiesPiggyBanks { $currentAmount = $this->getCurrentAmount($piggyBank, $account); $pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot; - $pivot->current_amount = bcadd((string)$currentAmount, $amount); + $pivot->current_amount = bcadd($currentAmount, $amount); $pivot->native_current_amount = null; // also update native_current_amount. @@ -122,13 +122,13 @@ trait ModifiesPiggyBanks if (0 !== bccomp($piggyBank->target_amount, '0')) { - $leftToSave = bcsub($piggyBank->target_amount, (string)$savedSoFar); - $maxAmount = 1 === bccomp((string)$leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount; + $leftToSave = bcsub($piggyBank->target_amount, $savedSoFar); + $maxAmount = 1 === bccomp($leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount; Log::debug(sprintf('Left to save: %s', $leftToSave)); Log::debug(sprintf('Maximum amount: %s', $maxAmount)); } - $compare = bccomp($amount, (string)$maxAmount); + $compare = bccomp($amount, $maxAmount); $result = $compare <= 0; Log::debug(sprintf('Compare <= 0? %d, so canAddAmount is %s', $compare, var_export($result, true))); @@ -140,7 +140,7 @@ trait ModifiesPiggyBanks { $savedSoFar = $this->getCurrentAmount($piggyBank, $account); - return bccomp($amount, (string)$savedSoFar) <= 0; + return bccomp($amount, $savedSoFar) <= 0; } /** @@ -234,9 +234,9 @@ trait ModifiesPiggyBanks // if the piggy bank is now smaller than the sum of the money saved, // remove money from all accounts until the piggy bank is the right amount. $currentAmount = $this->getCurrentAmount($piggyBank); - if (1 === bccomp((string)$currentAmount, (string)$piggyBank->target_amount) && 0 !== bccomp((string)$piggyBank->target_amount, '0')) { + if (1 === bccomp($currentAmount, (string)$piggyBank->target_amount) && 0 !== bccomp((string)$piggyBank->target_amount, '0')) { Log::debug(sprintf('Current amount is %s, target amount is %s', $currentAmount, $piggyBank->target_amount)); - $difference = bcsub((string)$piggyBank->target_amount, (string)$currentAmount); + $difference = bcsub((string)$piggyBank->target_amount, $currentAmount); // an amount will be removed, create "negative" event: // Log::debug(sprintf('ChangedAmount: is triggered with difference "%s"', $difference)); @@ -287,10 +287,8 @@ trait ModifiesPiggyBanks $piggyBank->name = $data['name']; } if (array_key_exists('transaction_currency_id', $data) && is_int($data['transaction_currency_id'])) { - $currency = TransactionCurrency::find($data['transaction_currency_id']); - if (null !== $currency) { - $piggyBank->transaction_currency_id = $currency->id; - } + $currency = Amount::getTransactionCurrencyById($data['transaction_currency_id']); + $piggyBank->transaction_currency_id = $currency->id; } if (array_key_exists('target_amount', $data) && '' !== $data['target_amount']) { diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 711767e051..8ca1f14723 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -67,12 +67,12 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte public function findPiggyBank(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank { - app('log')->debug('Searching for piggy information.'); + Log::debug('Searching for piggy information.'); if (null !== $piggyBankId) { $searchResult = $this->find($piggyBankId); if ($searchResult instanceof PiggyBank) { - app('log')->debug(sprintf('Found piggy based on #%d, will return it.', $piggyBankId)); + Log::debug(sprintf('Found piggy based on #%d, will return it.', $piggyBankId)); return $searchResult; } @@ -80,12 +80,12 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte if (null !== $piggyBankName) { $searchResult = $this->findByName($piggyBankName); if ($searchResult instanceof PiggyBank) { - app('log')->debug(sprintf('Found piggy based on "%s", will return it.', $piggyBankName)); + Log::debug(sprintf('Found piggy based on "%s", will return it.', $piggyBankName)); return $searchResult; } } - app('log')->debug('Found no piggy bank.'); + Log::debug('Found no piggy bank.'); return null; } @@ -158,7 +158,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte */ public function getExactAmount(PiggyBank $piggyBank, TransactionJournal $journal): string { - app('log')->debug(sprintf('Now in getExactAmount(%d, %d)', $piggyBank->id, $journal->id)); + Log::debug(sprintf('Now in getExactAmount(%d, %d)', $piggyBank->id, $journal->id)); $operator = null; $currency = null; @@ -173,7 +173,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte $primaryCurrency = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup); - app('log')->debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBank->transactionCurrency->code)); + Log::debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBank->transactionCurrency->code)); /** @var Transaction $source */ $source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first(); @@ -187,26 +187,26 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte if ($account->id === $source->account_id) { $operator = 'negative'; $currency = $accountRepos->getAccountCurrency($source->account) ?? $primaryCurrency; - app('log')->debug(sprintf('Currency will draw money out of piggy bank. Source currency is %s', $currency->code)); + Log::debug(sprintf('Currency will draw money out of piggy bank. Source currency is %s', $currency->code)); ++$hits; } // matches destination, which means amount will be added to piggy. if ($account->id === $destination->account_id) { $operator = 'positive'; $currency = $accountRepos->getAccountCurrency($destination->account) ?? $primaryCurrency; - app('log')->debug(sprintf('Currency will add money to piggy bank. Destination currency is %s', $currency->code)); + Log::debug(sprintf('Currency will add money to piggy bank. Destination currency is %s', $currency->code)); ++$hits; } } if ($hits > 1) { - app('log')->debug(sprintf('Transaction journal is related to %d of the accounts, cannot determine what to do. Return "0".', $hits)); + Log::debug(sprintf('Transaction journal is related to %d of the accounts, cannot determine what to do. Return "0".', $hits)); return '0'; } if (null === $operator || null === $currency) { - app('log')->debug('Currency is NULL and operator is NULL, return "0".'); + Log::debug('Currency is NULL and operator is NULL, return "0".'); return '0'; } @@ -214,52 +214,52 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte // which amount from the transaction matches? $amount = null; if ((int) $source->transaction_currency_id === $currency->id) { - app('log')->debug('Use normal amount'); - $amount = app('steam')->{$operator}($source->amount); // @phpstan-ignore-line + Log::debug('Use normal amount'); + $amount = Steam::{$operator}($source->amount); // @phpstan-ignore-line } if ((int) $source->foreign_currency_id === $currency->id) { - app('log')->debug('Use foreign amount'); - $amount = app('steam')->{$operator}($source->foreign_amount); // @phpstan-ignore-line + Log::debug('Use foreign amount'); + $amount = Steam::{$operator}($source->foreign_amount); // @phpstan-ignore-line } if (null === $amount) { - app('log')->debug('No match on currency, so amount remains null, return "0".'); + Log::debug('No match on currency, so amount remains null, return "0".'); return '0'; } - app('log')->debug(sprintf('The currency is %s and the amount is %s', $currency->code, $amount)); + Log::debug(sprintf('The currency is %s and the amount is %s', $currency->code, $amount)); $currentAmount = $this->getCurrentAmount($piggyBank); $room = bcsub($piggyBank->target_amount, $currentAmount); $compare = bcmul($currentAmount, '-1'); if (0 === bccomp($piggyBank->target_amount, '0')) { // amount is zero? then the "room" is positive amount of we wish to add or remove. - $room = app('steam')->positive($amount); - app('log')->debug(sprintf('Room is now %s', $room)); + $room = Steam::positive($amount); + Log::debug(sprintf('Room is now %s', $room)); } - app('log')->debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); + 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') && -1 === bccomp((string) $room, $amount)) { + if (1 === bccomp($amount, '0') && -1 === bccomp($room, $amount)) { // amount is positive and $room is smaller than $amount - 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)); + 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)); return $room; } // amount is negative and $currentAmount is smaller than $amount if (-1 === bccomp($amount, '0') && 1 === bccomp($compare, $amount)) { - app('log')->debug(sprintf('Max amount to remove is %f', $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)); + Log::debug(sprintf('Max amount to remove is %f', $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)); return $compare; } - return (string) $amount; + return $amount; } /** diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index e12a543e38..c8ee7b89c1 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -35,6 +35,7 @@ use FireflyIII\Models\RecurrenceMeta; use FireflyIII\Models\RecurrenceRepetition; use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\RecurrenceTransactionMeta; +use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournalMeta; use FireflyIII\Services\Internal\Destroy\RecurrenceDestroyService; @@ -582,4 +583,17 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte return $service->update($recurrence, $data); } + + public function markGroupsAsNow(Collection $groups): void + { + /** @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)); + $journal->date = now(config('app.timezone')); + $journal->save(); + } + } + } } diff --git a/app/Repositories/Recurring/RecurringRepositoryInterface.php b/app/Repositories/Recurring/RecurringRepositoryInterface.php index b59548b9de..9fb715e545 100644 --- a/app/Repositories/Recurring/RecurringRepositoryInterface.php +++ b/app/Repositories/Recurring/RecurringRepositoryInterface.php @@ -139,6 +139,8 @@ interface RecurringRepositoryInterface */ public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array; + public function markGroupsAsNow(Collection $groups): void; + /** * Parse the repetition in a string that is user readable. */ diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 11edb3a29d..58c998e760 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -31,6 +31,7 @@ use FireflyIII\Models\Attachment; use FireflyIII\Models\Location; use FireflyIII\Models\Note; use FireflyIII\Models\Tag; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; @@ -268,12 +269,12 @@ class TagRepository implements TagRepositoryInterface, UserGroupInterface ]; // add amount to correct type: - $amount = app('steam')->positive((string) $journal['amount']); + $amount = Steam::positive((string) $journal['amount']); $type = $journal['transaction_type_type']; if (TransactionTypeEnum::WITHDRAWAL->value === $type) { - $amount = bcmul((string) $amount, '-1'); + $amount = bcmul($amount, '-1'); } - $sums[$currencyId][$type] = bcadd((string) $sums[$currencyId][$type], (string) $amount); + $sums[$currencyId][$type] = bcadd((string) $sums[$currencyId][$type], $amount); $foreignCurrencyId = $journal['foreign_currency_id']; if (null !== $foreignCurrencyId && 0 !== $foreignCurrencyId) { @@ -289,11 +290,11 @@ class TagRepository implements TagRepositoryInterface, UserGroupInterface TransactionTypeEnum::OPENING_BALANCE->value => '0', ]; // add foreign amount to correct type: - $amount = app('steam')->positive((string) $journal['foreign_amount']); + $amount = Steam::positive((string) $journal['foreign_amount']); if (TransactionTypeEnum::WITHDRAWAL->value === $type) { - $amount = bcmul((string) $amount, '-1'); + $amount = bcmul($amount, '-1'); } - $sums[$foreignCurrencyId][$type] = bcadd((string) $sums[$foreignCurrencyId][$type], (string) $amount); + $sums[$foreignCurrencyId][$type] = bcadd((string) $sums[$foreignCurrencyId][$type], $amount); } } diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index 6fce8e43db..fec5d3f42f 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\TransactionGroup; use Carbon\Carbon; +use Exception; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\FireflyException; @@ -35,20 +36,19 @@ use FireflyIII\Models\Location; use FireflyIII\Models\Note; use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService; use FireflyIII\Services\Internal\Update\GroupUpdateService; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\NullArrayObject; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; -use Exception; use function Safe\json_decode; @@ -248,13 +248,10 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, $type = $journal->transactionType->type; $amount = app('steam')->positive($transaction->amount); if (TransactionTypeEnum::WITHDRAWAL->value === $type) { - return app('amount')->formatAnything($currency, app('steam')->negative($amount)); - } - if (TransactionTypeEnum::WITHDRAWAL->value !== $type) { - return app('amount')->formatAnything($currency, $amount); + return Amount::formatAnything($currency, app('steam')->negative($amount)); } - return ''; + return Amount::formatAnything($currency, $amount); } private function getFormattedForeignAmount(TransactionJournal $journal): string @@ -264,20 +261,19 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, if (null === $transaction->foreign_amount || '' === $transaction->foreign_amount) { return ''; } + if (0 === bccomp('0', (string)$transaction->foreign_amount)) { return ''; } + $currency = $transaction->foreignCurrency; $type = $journal->transactionType->type; $amount = app('steam')->positive($transaction->foreign_amount); if (TransactionTypeEnum::WITHDRAWAL->value === $type) { - return app('amount')->formatAnything($currency, app('steam')->negative($amount)); - } - if (TransactionTypeEnum::WITHDRAWAL->value !== $type) { - return app('amount')->formatAnything($currency, $amount); + return Amount::formatAnything($currency, app('steam')->negative($amount)); } - return ''; + return Amount::formatAnything($currency, $amount); } public function getLocation(int $journalId): ?Location @@ -340,7 +336,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, { $return = []; $journals = $group->transactionJournals->pluck('id')->toArray(); - $currency = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup); + $currency = Amount::getPrimaryCurrencyByUserGroup($this->user->userGroup); $data = PiggyBankEvent::whereIn('transaction_journal_id', $journals) ->with('piggyBank', 'piggyBank.account') ->get(['piggy_bank_events.*']) @@ -357,7 +353,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, ->first() ; if (null !== $currencyPreference) { - $currency = TransactionCurrency::where('id', $currencyPreference->data)->first(); + $currency = Amount::getTransactionCurrencyById((int) $currencyPreference->data); } $journalId = $row->transaction_journal_id; $return[$journalId] ??= []; @@ -365,7 +361,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, $return[$journalId][] = [ 'piggy' => $row->piggyBank->name, 'piggy_id' => $row->piggy_bank_id, - 'amount' => app('amount')->formatAnything($currency, $row->amount), + 'amount' => Amount::formatAnything($currency, $row->amount), ]; } @@ -377,7 +373,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, /** @var TransactionJournal $journal */ $journal = $this->user->transactionJournals()->find($journalId); - return $journal->tags()->get(); + return $journal->tags()->whereNull('deleted_at')->get(); } /** @@ -445,7 +441,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface, /** @var Transaction $transaction */ foreach ($journal->transactions as $transaction) { if (-1 === bccomp('0', (string)$transaction->amount)) { - $sum = bcadd($sum, (string) $transaction->amount); + $sum = bcadd($sum, (string)$transaction->amount); $names = sprintf('%s%s', $names, $transaction->account->name); } } diff --git a/app/Repositories/User/UserRepository.php b/app/Repositories/User/UserRepository.php index e7d89c4a26..e6dbb7ec52 100644 --- a/app/Repositories/User/UserRepository.php +++ b/app/Repositories/User/UserRepository.php @@ -229,7 +229,7 @@ class UserRepository implements UserRepositoryInterface return $return; } - public function hasRole(null|Authenticatable|User $user, string $role): bool + public function hasRole(Authenticatable|User|null $user, string $role): bool { if (!$user instanceof Authenticatable) { return false; @@ -270,7 +270,7 @@ class UserRepository implements UserRepositoryInterface return $collection; } - public function inviteUser(null|Authenticatable|User $user, string $email): InvitedUser + public function inviteUser(Authenticatable|User|null $user, string $email): InvitedUser { if (!$user instanceof User) { throw new FireflyException('User is not a User object.'); diff --git a/app/Repositories/User/UserRepositoryInterface.php b/app/Repositories/User/UserRepositoryInterface.php index 9c9da4e7e3..4b2ad0b9a9 100644 --- a/app/Repositories/User/UserRepositoryInterface.php +++ b/app/Repositories/User/UserRepositoryInterface.php @@ -105,9 +105,9 @@ interface UserRepositoryInterface public function getUserGroups(User $user): Collection; - public function hasRole(null|Authenticatable|User $user, string $role): bool; + public function hasRole(Authenticatable|User|null $user, string $role): bool; - public function inviteUser(null|Authenticatable|User $user, string $email): InvitedUser; + public function inviteUser(Authenticatable|User|null $user, string $email): InvitedUser; public function redeemCode(string $code): void; diff --git a/app/Repositories/UserGroup/UserGroupRepository.php b/app/Repositories/UserGroup/UserGroupRepository.php index f8c9e3a488..3f9bcdace1 100644 --- a/app/Repositories/UserGroup/UserGroupRepository.php +++ b/app/Repositories/UserGroup/UserGroupRepository.php @@ -137,13 +137,10 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte if (!$existingGroup instanceof UserGroup) { $exists = false; - /** @var null|UserGroup $existingGroup */ + /** @var UserGroup $existingGroup */ $existingGroup = $this->store(['user' => $user, 'title' => $groupName]); } - if (null !== $existingGroup) { - // group already exists - $groupName = sprintf('%s-%s', $user->email, substr(sha1(random_int(1000, 9999).microtime()), 0, 4)); - } + $groupName = sprintf('%s-%s', $user->email, substr(sha1(random_int(1000, 9999).microtime()), 0, 4)); ++$loop; } @@ -206,7 +203,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte if (array_key_exists('primary_currency_id', $data) && null === $currency) { $repository->setUser($this->user); - $currency = $repository->find((int) $data['primary_currency_id']); + $currency = $repository->find((int)$data['primary_currency_id']); } if (null !== $currency) { $repository->makePrimary($currency); @@ -233,7 +230,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte $user = User::find($data['id']); app('log')->debug('Found user by ID'); } - if (array_key_exists('email', $data) && '' !== (string) $data['email']) { + if (array_key_exists('email', $data) && '' !== (string)$data['email']) { /** @var null|User $user */ $user = User::whereEmail($data['email'])->first(); app('log')->debug('Found user by email'); @@ -251,13 +248,13 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte if (1 === $membershipCount) { $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'])) { + if ($lastUserId === (int)$user->id && 0 === count($data['roles'])) { app('log')->debug('User is last in this group, refuse to act'); throw new FireflyException('You cannot remove the last member from this user group. Delete the user group instead.'); } // if this is also the user we're editing right now, and do not grant them the owner role: - if ($lastUserId === (int) $user->id && count($data['roles']) > 0 && !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)) { + if ($lastUserId === (int)$user->id && count($data['roles']) > 0 && !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)) { app('log')->debug('User needs to have owner role in this group, refuse to act'); throw new FireflyException('The last member in this user group must get or keep the "owner" role.'); diff --git a/app/Repositories/UserGroups/Account/AccountRepository.php b/app/Repositories/UserGroups/Account/AccountRepository.php deleted file mode 100644 index 418f16ef8b..0000000000 --- a/app/Repositories/UserGroups/Account/AccountRepository.php +++ /dev/null @@ -1,417 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Account; - -use FireflyIII\Enums\AccountTypeEnum; -use FireflyIII\Models\Account; -use FireflyIII\Models\AccountMeta; -use FireflyIII\Models\AccountType; -use FireflyIII\Models\ObjectGroup; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Services\Internal\Update\AccountUpdateService; -use FireflyIII\Support\Facades\Steam; -use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; -use Illuminate\Support\Collection; -use Illuminate\Support\Facades\DB; -use Override; -use stdClass; - -use function Safe\json_encode; - -/** - * Class AccountRepository - * - * @deprecated - */ -class AccountRepository implements AccountRepositoryInterface -{ - use UserGroupTrait; - - #[Override] - public function countAccounts(array $types): int - { - $query = $this->userGroup->accounts(); - if (0 !== count($types)) { - $query->accountTypeIn($types); - } - - return $query->count(); - } - - 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): void { - $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 null|Account */ - return $dbQuery->first(['accounts.*']); - } - - public function findByIbanNull(string $iban, array $types): ?Account - { - $iban = Steam::filterSpaces($iban); - $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 null|Account */ - return $query->where('iban', $iban)->first(['accounts.*']); - } - - public function findByName(string $name, array $types): ?Account - { - $query = $this->userGroup->accounts(); - - if (0 !== count($types)) { - $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); - $query->whereIn('account_types.type', $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 null|Account $account */ - $account = $query->first(['accounts.*']); - if (null === $account) { - app('log')->debug(sprintf('There is no account with name "%s" of types', $name), $types); - - return null; - } - app('log')->debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id)); - - return $account; - } - - #[Override] - public function getAccountBalances(Account $account): Collection - { - return $account->accountBalances; - } - - public function getAccountCurrency(Account $account): ?TransactionCurrency - { - $type = $account->accountType->type; - $list = config('firefly.valid_currency_account_types'); - - // return null if not in this list. - if (!in_array($type, $list, true)) { - return null; - } - $currencyId = (int) $this->getMetaValue($account, 'currency_id'); - if ($currencyId > 0) { - return TransactionCurrency::find($currencyId); - } - - return null; - } - - /** - * Return meta value for account. Null if not found. - */ - public function getMetaValue(Account $account, string $field): ?string - { - $result = $account->accountMeta->filter( - static fn (AccountMeta $meta) => strtolower($meta->name) === strtolower($field) - ); - if (0 === $result->count()) { - return null; - } - if (1 === $result->count()) { - return (string) $result->first()->data; - } - - return null; - } - - public function find(int $accountId): ?Account - { - $account = $this->user->accounts()->find($accountId); - if (null === $account) { - return $this->userGroup->accounts()->find($accountId); - } - - /** @var null|Account */ - return $account; - } - - #[Override] - public function getAccountTypes(Collection $accounts): Collection - { - return AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id') - ->whereIn('accounts.id', $accounts->pluck('id')->toArray()) - ->get(['accounts.id', 'account_types.type']) - ; - } - - public function getAccountsById(array $accountIds): Collection - { - $query = $this->userGroup->accounts(); - - if (0 !== count($accountIds)) { - $query->whereIn('accounts.id', $accountIds); - } - $query->orderBy('accounts.order', 'ASC'); - $query->orderBy('accounts.active', 'DESC'); - $query->orderBy('accounts.name', 'ASC'); - - return $query->get(['accounts.*']); - } - - #[Override] - public function getAccountsInOrder(array $types, array $sort, int $startRow, int $endRow): Collection - { - $query = $this->userGroup->accounts(); - if (0 !== count($types)) { - $query->accountTypeIn($types); - } - $query->skip($startRow); - $query->take($endRow - $startRow); - - // add sort parameters. At this point they're filtered to allowed fields to sort by: - if (0 !== count($sort)) { - foreach ($sort as $label => $direction) { - $query->orderBy(sprintf('accounts.%s', $label), $direction); - } - } - - if (0 === count($sort)) { - $query->orderBy('accounts.order', 'ASC'); - $query->orderBy('accounts.active', 'DESC'); - $query->orderBy('accounts.name', 'ASC'); - } - - return $query->get(['accounts.*']); - } - - public function getActiveAccountsByType(array $types): Collection - { - $query = $this->userGroup->accounts(); - if (0 !== count($types)) { - $query->accountTypeIn($types); - } - $query->where('active', true); - $query->orderBy('accounts.account_type_id', 'ASC'); - $query->orderBy('accounts.order', 'ASC'); - $query->orderBy('accounts.name', 'ASC'); - - return $query->get(['accounts.*']); - } - - #[Override] - public function getLastActivity(Collection $accounts): array - { - return Transaction::whereIn('account_id', $accounts->pluck('id')->toArray()) - ->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id') - ->groupBy('transactions.account_id') - ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) as date_max')])->toArray() // @phpstan-ignore-line - ; - } - - #[Override] - public function getMetaValues(Collection $accounts, array $fields): Collection - { - $query = AccountMeta::whereIn('account_id', $accounts->pluck('id')->toArray()); - if (count($fields) > 0) { - $query->whereIn('name', $fields); - } - - return $query->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']); - } - - #[Override] - public function getObjectGroups(Collection $accounts): array - { - $groupIds = []; - $return = []; - $set = DB::table('object_groupables')->where('object_groupable_type', Account::class) - ->whereIn('object_groupable_id', $accounts->pluck('id')->toArray())->get() - ; - - /** @var stdClass $row */ - foreach ($set as $row) { - $groupIds[] = $row->object_group_id; - } - $groupIds = array_unique($groupIds); - $groups = ObjectGroup::whereIn('id', $groupIds)->get(); - - /** @var stdClass $row */ - foreach ($set as $row) { - if (!array_key_exists($row->object_groupable_id, $return)) { - /** @var null|ObjectGroup $group */ - $group = $groups->firstWhere('id', '=', $row->object_group_id); - if (null !== $group) { - $return[$row->object_groupable_id] = ['title' => $group->title, 'order' => $group->order, 'id' => $group->id]; - } - } - } - - return $return; - } - - public function resetAccountOrder(): void - { - $sets = [ - [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value], - [AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::MORTGAGE->value], - ]; - foreach ($sets as $set) { - $list = $this->getAccountsByType($set); - $index = 1; - foreach ($list as $account) { - if (false === $account->active) { - $account->order = 0; - - continue; - } - if ($index !== (int) $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(); - } - ++$index; - } - } - // reset the rest to zero. - $all = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::MORTGAGE->value]; - $this->user->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') - ->whereNotIn('account_types.type', $all) - ->update(['order' => 0]) - ; - } - - public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection - { - $sortable = ['name', 'active']; // TODO yes this is a duplicate array. - $res = array_intersect([AccountTypeEnum::ASSET->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value], $types); - $query = $this->userGroup->accounts(); - if (0 !== count($types)) { - $query->accountTypeIn($types); - } - - // process filters - // TODO this should be repeatable, it feels like a hack when you do it here. - // TODO some fields cannot be filtered using the query, and a second filter must be applied on the collection. - foreach ($filters as $column => $value) { - // filter on NULL values - if (null === $value) { - continue; - } - if ('active' === $column) { - $query->where('accounts.active', $value); - } - if ('name' === $column) { - $query->whereLike('accounts.name', sprintf('%%%s%%', $value)); - } - } - - // add sort parameters. At this point they're filtered to allowed fields to sort by: - $hasActiveColumn = array_key_exists('active', $sort); - if (count($sort) > 0) { - if (false === $hasActiveColumn) { - $query->orderBy('accounts.active', 'DESC'); - } - foreach ($sort as $column => $direction) { - if (in_array($column, $sortable, true)) { - $query->orderBy(sprintf('accounts.%s', $column), $direction); - } - } - } - - if (0 === count($sort)) { - if (0 !== count($res)) { - $query->orderBy('accounts.active', 'DESC'); - } - $query->orderBy('accounts.order', 'ASC'); - $query->orderBy('accounts.name', 'ASC'); - $query->orderBy('accounts.account_type_id', 'ASC'); - $query->orderBy('accounts.id', 'ASC'); - } - - return $query->get(['accounts.*']); - } - - #[Override] - public function update(Account $account, array $data): Account - { - /** @var AccountUpdateService $service */ - $service = app(AccountUpdateService::class); - - return $service->update($account, $data); - } - - public function searchAccount(string $query, array $types, int $page, int $limit): Collection - { - // search by group, not by user - $dbQuery = $this->userGroup->accounts() - ->where('active', true) - ->orderBy('accounts.updated_at', 'ASC') - ->orderBy('accounts.order', 'ASC') - ->orderBy('accounts.account_type_id', 'ASC') - ->orderBy('accounts.name', 'ASC') - ->with(['accountType']) - ; - - // split query on spaces just in case: - if ('' !== trim($query)) { - $dbQuery->where(function (EloquentBuilder $q) use ($query): void { - $parts = explode(' ', $query); - foreach ($parts as $part) { - $search = sprintf('%%%s%%', $part); - $q->orWhereLike('name', $search); - } - }); - } - - if (0 !== count($types)) { - $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); - $dbQuery->whereIn('account_types.type', $types); - } - - $dbQuery->skip(($page - 1) * $limit)->take($limit); - - return $dbQuery->get(['accounts.*']); - - } -} diff --git a/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php b/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php deleted file mode 100644 index 6ea484133c..0000000000 --- a/app/Repositories/UserGroups/Account/AccountRepositoryInterface.php +++ /dev/null @@ -1,84 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Account; - -use FireflyIII\Models\Account; -use FireflyIII\Models\TransactionCurrency; -use Illuminate\Support\Collection; - -/** - * Interface AccountRepositoryInterface - * - * @deprecated - */ -interface AccountRepositoryInterface -{ - public function countAccounts(array $types): int; - - public function find(int $accountId): ?Account; - - public function findByAccountNumber(string $number, array $types): ?Account; - - public function findByIbanNull(string $iban, array $types): ?Account; - - public function findByName(string $name, array $types): ?Account; - - public function getAccountBalances(Account $account): Collection; - - public function getAccountCurrency(Account $account): ?TransactionCurrency; - - public function getAccountTypes(Collection $accounts): Collection; - - public function getAccountsById(array $accountIds): Collection; - - public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection; - - /** - * Used in the infinite accounts list. - */ - public function getAccountsInOrder(array $types, array $sort, int $startRow, int $endRow): Collection; - - public function getActiveAccountsByType(array $types): Collection; - - public function getLastActivity(Collection $accounts): array; - - /** - * Return meta value for account. Null if not found. - */ - public function getMetaValue(Account $account, string $field): ?string; - - public function getMetaValues(Collection $accounts, array $fields): Collection; - - public function getObjectGroups(Collection $accounts): array; - - /** - * Reset order types of the mentioned accounts. - */ - public function resetAccountOrder(): void; - - public function searchAccount(string $query, array $types, int $page, int $limit): Collection; - - public function update(Account $account, array $data): Account; -} diff --git a/app/Repositories/UserGroups/Bill/BillRepository.php b/app/Repositories/UserGroups/Bill/BillRepository.php deleted file mode 100644 index defdaaba0d..0000000000 --- a/app/Repositories/UserGroups/Bill/BillRepository.php +++ /dev/null @@ -1,236 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Bill; - -use Carbon\Carbon; -use FireflyIII\Models\Bill; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Support\CacheProperties; -use FireflyIII\Support\Http\Api\ExchangeRateConverter; -use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; -use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; - -/** - * Class BillRepository - * - * @deprecated - */ -class BillRepository implements BillRepositoryInterface -{ - use UserGroupTrait; - - /** - * Correct order of piggies in case of issues. - */ - public function correctOrder(): void - { - $set = $this->userGroup->bills()->orderBy('order', 'ASC')->get(); - $current = 1; - foreach ($set as $bill) { - if ($bill->order !== $current) { - $bill->order = $current; - $bill->save(); - } - ++$current; - } - } - - public function getBills(): Collection - { - return $this->userGroup->bills() - ->orderBy('bills.name', 'ASC') - ->get(['bills.*']) - ; - } - - public function sumPaidInRange(Carbon $start, Carbon $end): array - { - Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); - $bills = $this->getActiveBills(); - $primary = app('amount')->getPrimaryCurrency(); - $return = []; - $converter = new ExchangeRateConverter(); - - /** @var Bill $bill */ - foreach ($bills as $bill) { - /** @var Collection $set */ - $set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']); - $currency = $bill->transactionCurrency; - $currencyId = $bill->transaction_currency_id; - - $return[$currencyId] ??= [ - 'currency_id' => (string) $currency->id, - 'currency_name' => $currency->name, - 'currency_symbol' => $currency->symbol, - 'currency_code' => $currency->code, - 'currency_decimal_places' => $currency->decimal_places, - 'primary_currency_id' => (string) $primary->id, - 'primary_currency_name' => $primary->name, - 'primary_currency_symbol' => $primary->symbol, - 'primary_currency_code' => $primary->code, - 'primary_currency_decimal_places' => $primary->decimal_places, - 'sum' => '0', - 'pc_sum' => '0', - ]; - - /** @var TransactionJournal $transactionJournal */ - foreach ($set as $transactionJournal) { - /** @var null|Transaction $sourceTransaction */ - $sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first(); - if (null !== $sourceTransaction) { - $amount = $sourceTransaction->amount; - if ((int) $sourceTransaction->foreign_currency_id === $currency->id) { - // use foreign amount instead! - $amount = (string) $sourceTransaction->foreign_amount; - } - // convert to primary currency - $pcAmount = $amount; - if ($currencyId !== $primary->id) { - // get rate and convert. - $pcAmount = $converter->convert($currency, $primary, $transactionJournal->date, $amount); - } - if ((int) $sourceTransaction->foreign_currency_id === $primary->id) { - // ignore conversion, use foreign amount - $pcAmount = (string) $sourceTransaction->foreign_amount; - } - $return[$currencyId]['sum'] = bcadd($return[$currencyId]['sum'], (string) $amount); - $return[$currencyId]['pc_sum'] = bcadd($return[$currencyId]['pc_sum'], (string) $pcAmount); - } - } - } - $converter->summarize(); - - return $return; - } - - public function getActiveBills(): Collection - { - return $this->userGroup->bills() - ->where('active', true) - ->orderBy('bills.name', 'ASC') - ->get(['bills.*']) - ; - } - - public function sumUnpaidInRange(Carbon $start, Carbon $end): array - { - Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); - $bills = $this->getActiveBills(); - $return = []; - $primary = app('amount')->getPrimaryCurrency(); - $converter = new ExchangeRateConverter(); - - /** @var Bill $bill */ - foreach ($bills as $bill) { - $dates = $this->getPayDatesInRange($bill, $start, $end); - $count = $bill->transactionJournals()->after($start)->before($end)->count(); - $total = $dates->count() - $count; - - if ($total > 0) { - $currency = $bill->transactionCurrency; - $currencyId = $bill->transaction_currency_id; - $average = bcdiv(bcadd((string) $bill->amount_max, (string) $bill->amount_min), '2'); - $pcAverage = $converter->convert($currency, $primary, $start, $average); - $return[$currencyId] ??= [ - 'currency_id' => (string) $currency->id, - 'currency_name' => $currency->name, - 'currency_symbol' => $currency->symbol, - 'currency_code' => $currency->code, - 'currency_decimal_places' => $currency->decimal_places, - 'primary_currency_id' => (string) $primary->id, - 'primary_currency_name' => $primary->name, - 'primary_currency_symbol' => $primary->symbol, - 'primary_currency_code' => $primary->code, - 'primary_currency_decimal_places' => $primary->decimal_places, - 'sum' => '0', - 'pc_sum' => '0', - ]; - $return[$currencyId]['sum'] = bcadd($return[$currencyId]['sum'], bcmul($average, (string) $total)); - $return[$currencyId]['pc_sum'] = bcadd($return[$currencyId]['pc_sum'], bcmul($pcAverage, (string) $total)); - } - } - $converter->summarize(); - - return $return; - } - - /** - * Between start and end, tells you on which date(s) the bill is expected to hit. - * TODO duplicate of function in other billrepositoryinterface - */ - public function getPayDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection - { - $set = new Collection(); - $currentStart = clone $start; - // 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) { - // app('log')->debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); - $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - // 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); - // app('log')->debug(sprintf('Now %d dates in set.', $set->count())); - $nextExpectedMatch->addDay(); - - // app('log')->debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); - - $currentStart = clone $nextExpectedMatch; - } - - return $set; - } - - /** - * Given a bill and a date, this method will tell you at which moment this bill expects its next - * transaction. Whether it is there already, is not relevant. - * - * TODO duplicate of other repos - */ - public function nextDateMatch(Bill $bill, Carbon $date): Carbon - { - $cache = new CacheProperties(); - $cache->addProperty($bill->id); - $cache->addProperty('nextDateMatch'); - $cache->addProperty($date); - if ($cache->has()) { - return $cache->get(); - } - // find the most recent date for this bill NOT in the future. Cache this date: - $start = clone $bill->date; - - while ($start < $date) { - $start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); - } - $cache->store($start); - - return $start; - } -} diff --git a/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php b/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php deleted file mode 100644 index 7c2819022e..0000000000 --- a/app/Repositories/UserGroups/Bill/BillRepositoryInterface.php +++ /dev/null @@ -1,72 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Bill; - -use Carbon\Carbon; -use FireflyIII\Models\Bill; -use Illuminate\Support\Collection; - -/** - * Interface BillRepositoryInterface - * - * @deprecated - */ -interface BillRepositoryInterface -{ - /** - * TODO duplicate of other repos - * Add correct order to bills. - */ - public function correctOrder(): void; - - public function getActiveBills(): Collection; - - public function getBills(): Collection; - - /** - * Between start and end, tells you on which date(s) the bill is expected to hit. - * - * TODO duplicate of method in other billrepositoryinterface - */ - public function getPayDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection; - - /** - * Given a bill and a date, this method will tell you at which moment this bill expects its next - * transaction. Whether it is there already, is not relevant. - * - * TODO duplicate of method in other bill repos - */ - public function nextDateMatch(Bill $bill, Carbon $date): Carbon; - - /** - * Collect multi-currency of sum of bills already paid. - */ - public function sumPaidInRange(Carbon $start, Carbon $end): array; - - /** - * Collect multi-currency of sum of bills yet to pay. - */ - public function sumUnpaidInRange(Carbon $start, Carbon $end): array; -} diff --git a/app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php b/app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php deleted file mode 100644 index c07ec329a9..0000000000 --- a/app/Repositories/UserGroups/Budget/AvailableBudgetRepository.php +++ /dev/null @@ -1,78 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Budget; - -use Carbon\Carbon; -use FireflyIII\Models\AvailableBudget; -use FireflyIII\Support\Http\Api\ExchangeRateConverter; -use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; -use Illuminate\Support\Facades\Log; - -/** - * Class AvailableBudgetRepository - * - * @deprecated - */ -class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface -{ - use UserGroupTrait; - - public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array - { - Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); - $return = []; - $converter = new ExchangeRateConverter(); - $primary = app('amount')->getPrimaryCurrency(); - $availableBudgets = $this->userGroup->availableBudgets() - ->where('start_date', $start->format('Y-m-d')) - ->where('end_date', $end->format('Y-m-d'))->get() - ; - - /** @var AvailableBudget $availableBudget */ - foreach ($availableBudgets as $availableBudget) { - $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' => $availableBudget->transactionCurrency->decimal_places, - 'primary_currency_id' => $primary->id, - 'primary_currency_code' => $primary->code, - 'primary_currency_symbol' => $primary->symbol, - 'primary_currency_name' => $primary->name, - 'primary_currency_decimal_places' => $primary->decimal_places, - 'amount' => '0', - 'pc_amount' => '0', - ]; - $pcAmount = $converter->convert($availableBudget->transactionCurrency, $primary, $availableBudget->start_date, $availableBudget->amount); - $return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], (string) $availableBudget->amount); - $return[$currencyId]['pc_amount'] = bcadd($return[$currencyId]['pc_amount'], $pcAmount); - } - $converter->summarize(); - - return $return; - } -} diff --git a/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php b/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php deleted file mode 100644 index 33a2c7b203..0000000000 --- a/app/Repositories/UserGroups/Budget/AvailableBudgetRepositoryInterface.php +++ /dev/null @@ -1,37 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Budget; - -use Carbon\Carbon; - -/** - * Interface AvailableBudgetRepositoryInterface - * - * @deprecated - */ -interface AvailableBudgetRepositoryInterface -{ - public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array; -} diff --git a/app/Repositories/UserGroups/Budget/BudgetRepository.php b/app/Repositories/UserGroups/Budget/BudgetRepository.php deleted file mode 100644 index cb43479b71..0000000000 --- a/app/Repositories/UserGroups/Budget/BudgetRepository.php +++ /dev/null @@ -1,56 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Budget; - -use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; -use Illuminate\Support\Collection; - -/** - * Class BudgetRepository - * - * @deprecated - */ -class BudgetRepository implements BudgetRepositoryInterface -{ - use UserGroupTrait; - - public function getActiveBudgets(): Collection - { - return $this->userGroup->budgets()->where('active', true) - ->orderBy('order', 'ASC') - ->orderBy('name', 'ASC') - ->get() - ; - } - - public function getBudgets(): Collection - { - return $this->userGroup->budgets() - ->orderBy('order', 'ASC') - ->orderBy('name', 'ASC') - ->get() - ; - } -} diff --git a/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php b/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php deleted file mode 100644 index 021736f595..0000000000 --- a/app/Repositories/UserGroups/Budget/BudgetRepositoryInterface.php +++ /dev/null @@ -1,39 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Budget; - -use Illuminate\Support\Collection; - -/** - * Interface BudgetRepositoryInterface - * - * @deprecated - */ -interface BudgetRepositoryInterface -{ - public function getActiveBudgets(): Collection; - - public function getBudgets(): Collection; -} diff --git a/app/Repositories/UserGroups/Budget/OperationsRepository.php b/app/Repositories/UserGroups/Budget/OperationsRepository.php deleted file mode 100644 index 78944b9d51..0000000000 --- a/app/Repositories/UserGroups/Budget/OperationsRepository.php +++ /dev/null @@ -1,136 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Budget; - -use Carbon\Carbon; -use FireflyIII\Enums\TransactionTypeEnum; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Helpers\Collector\GroupCollectorInterface; -use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; -use Illuminate\Support\Collection; - -/** - * Class OperationsRepository - * - * @deprecated - */ -class OperationsRepository implements OperationsRepositoryInterface -{ - use UserGroupTrait; - - /** - * @throws FireflyException - */ - public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setUserGroup($this->userGroup)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); - if ($accounts instanceof Collection && $accounts->count() > 0) { - $collector->setAccounts($accounts); - } - if ($budgets instanceof Collection && $budgets->count() > 0) { - $collector->setBudgets($budgets); - } - if (!$budgets instanceof Collection || (0 === $budgets->count())) { - $collector->setBudgets($this->getBudgets()); - } - $collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation(); - $journals = $collector->getExtractedJournals(); - $array = []; - - foreach ($journals as $journal) { - $currencyId = (int) $journal['currency_id']; - $budgetId = (int) $journal['budget_id']; - $budgetName = (string) $journal['budget_name']; - - // catch "no budget" entries. - if (0 === $budgetId) { - continue; - } - - // info about the currency: - $array[$currencyId] ??= [ - 'budgets' => [], - 'currency_id' => $currencyId, - 'currency_name' => $journal['currency_name'], - 'currency_symbol' => $journal['currency_symbol'], - 'currency_code' => $journal['currency_code'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - ]; - - // info about the budgets: - $array[$currencyId]['budgets'][$budgetId] ??= [ - 'id' => $budgetId, - 'name' => $budgetName, - 'transaction_journals' => [], - ]; - - // add journal to array: - // only a subset of the fields. - $journalId = (int) $journal['transaction_journal_id']; - $final = [ - 'amount' => app('steam')->negative($journal['amount']), - 'currency_id' => $journal['currency_id'], - 'foreign_amount' => null, - 'foreign_currency_id' => null, - 'foreign_currency_code' => null, - 'foreign_currency_symbol' => null, - 'foreign_currency_name' => null, - 'foreign_currency_decimal_places' => null, - 'destination_account_id' => $journal['destination_account_id'], - 'destination_account_name' => $journal['destination_account_name'], - 'source_account_id' => $journal['source_account_id'], - 'source_account_name' => $journal['source_account_name'], - 'category_name' => $journal['category_name'], - 'description' => $journal['description'], - 'transaction_group_id' => $journal['transaction_group_id'], - 'date' => $journal['date'], - ]; - if (null !== $journal['foreign_amount']) { - $final['foreign_amount'] = app('steam')->negative($journal['foreign_amount']); - $final['foreign_currency_id'] = $journal['foreign_currency_id']; - $final['foreign_currency_code'] = $journal['foreign_currency_code']; - $final['foreign_currency_symbol'] = $journal['foreign_currency_symbol']; - $final['foreign_currency_name'] = $journal['foreign_currency_name']; - $final['foreign_currency_decimal_places'] = $journal['foreign_currency_decimal_places']; - } - - $array[$currencyId]['budgets'][$budgetId]['transaction_journals'][$journalId] = $final; - } - - return $array; - } - - private function getBudgets(): Collection - { - /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class); - $repository->setUserGroup($this->getUserGroup()); - - return $repository->getActiveBudgets(); - } -} diff --git a/app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php b/app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php deleted file mode 100644 index a96b94f658..0000000000 --- a/app/Repositories/UserGroups/Budget/OperationsRepositoryInterface.php +++ /dev/null @@ -1,43 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Budget; - -use Carbon\Carbon; -use Illuminate\Support\Collection; - -/** - * Interface OperationsRepositoryInterface - * - * @deprecated - */ -interface OperationsRepositoryInterface -{ - /** - * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period - * which have the specified budget set to them. It's grouped per currency, with as few details in the array - * as possible. Amounts are always negative. - */ - public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array; -} diff --git a/app/Repositories/UserGroups/Category/CategoryRepository.php b/app/Repositories/UserGroups/Category/CategoryRepository.php deleted file mode 100644 index 2b93a540cf..0000000000 --- a/app/Repositories/UserGroups/Category/CategoryRepository.php +++ /dev/null @@ -1,58 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Category; - -use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; -use Illuminate\Support\Collection; - -/** - * Class CategoryRepository - * - * @deprecated - */ -class CategoryRepository implements CategoryRepositoryInterface -{ - use UserGroupTrait; - - public function searchCategory(array $query, int $limit): Collection - { - $search = $this->userGroup->categories(); - if (count($query) > 0) { - // split query on spaces just in case: - $search->where(function (EloquentBuilder $q) use ($query): void { - foreach ($query as $line) { - $parts = explode(' ', $line); - foreach ($parts as $part) { - $search = sprintf('%%%s%%', $part); - $q->orWhereLike('name', $search); - } - } - }); - } - - return $search->take($limit)->get(); - } -} diff --git a/app/Repositories/UserGroups/Category/CategoryRepositoryInterface.php b/app/Repositories/UserGroups/Category/CategoryRepositoryInterface.php deleted file mode 100644 index 1a8cc54a4c..0000000000 --- a/app/Repositories/UserGroups/Category/CategoryRepositoryInterface.php +++ /dev/null @@ -1,40 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Category; - -use Illuminate\Support\Collection; - -/** - * Interface CategoryRepositoryInterface - * - * @deprecated - */ -interface CategoryRepositoryInterface -{ - /** - * Search for a category using wild cards. Uses the database, so case sensitive. - */ - public function searchCategory(array $query, int $limit): Collection; -} diff --git a/app/Repositories/UserGroups/Currency/CurrencyRepository.php b/app/Repositories/UserGroups/Currency/CurrencyRepository.php deleted file mode 100644 index 7928ec74e6..0000000000 --- a/app/Repositories/UserGroups/Currency/CurrencyRepository.php +++ /dev/null @@ -1,392 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Currency; - -use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Factory\TransactionCurrencyFactory; -use FireflyIII\Models\AccountMeta; -use FireflyIII\Models\AvailableBudget; -use FireflyIII\Models\Bill; -use FireflyIII\Models\BudgetLimit; -use FireflyIII\Models\RecurrenceTransaction; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\User\UserRepositoryInterface; -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 function Safe\json_encode; - -/** - * Class CurrencyRepository - * - * @deprecated - */ -class CurrencyRepository implements CurrencyRepositoryInterface -{ - use UserGroupTrait; - - /** - * @throws FireflyException - */ - public function currencyInUse(TransactionCurrency $currency): bool - { - $result = $this->currencyInUseAt($currency); - - return null !== $result; - } - - /** - * @throws FireflyException - */ - public function currencyInUseAt(TransactionCurrency $currency): ?string - { - Log::debug(sprintf('Now in currencyInUse() for #%d ("%s")', $currency->id, $currency->code)); - $countJournals = $this->countJournals($currency); - if ($countJournals > 0) { - Log::info(sprintf('Count journals is %d, return true.', $countJournals)); - - return 'journals'; - } - - // is the only currency left - if (1 === $this->getAll()->count()) { - Log::info('Is the last currency in the system, return true. '); - - return 'last_left'; - } - - // is being used in accounts: - $meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((string) $currency->id))->count(); - if ($meta > 0) { - Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); - - return 'account_meta'; - } - - // second search using integer check. - $meta = AccountMeta::where('name', 'currency_id')->where('data', json_encode((int) $currency->id))->count(); - if ($meta > 0) { - Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); - - return 'account_meta'; - } - - // is being used in bills: - $bills = Bill::where('transaction_currency_id', $currency->id)->count(); - if ($bills > 0) { - Log::info(sprintf('Used in %d bills as currency, return true. ', $bills)); - - return 'bills'; - } - - // is being used in recurring transactions - $recurringAmount = RecurrenceTransaction::where('transaction_currency_id', $currency->id)->count(); - $recurringForeign = RecurrenceTransaction::where('foreign_currency_id', $currency->id)->count(); - - if ($recurringAmount > 0 || $recurringForeign > 0) { - Log::info(sprintf('Used in %d recurring transactions as (foreign) currency id, return true. ', $recurringAmount + $recurringForeign)); - - return 'recurring'; - } - - // 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($currency->id))->count() - ; - if ($meta > 0) { - Log::info(sprintf('Used in %d accounts as currency_id, return true. ', $meta)); - - return 'account_meta'; - } - - // is being used in available budgets - $availableBudgets = AvailableBudget::where('transaction_currency_id', $currency->id)->count(); - if ($availableBudgets > 0) { - Log::info(sprintf('Used in %d available budgets as currency, return true. ', $availableBudgets)); - - return 'available_budgets'; - } - - // is being used in budget limits - $budgetLimit = BudgetLimit::where('transaction_currency_id', $currency->id)->count(); - if ($budgetLimit > 0) { - Log::info(sprintf('Used in %d budget limits as currency, return true. ', $budgetLimit)); - - return 'budget_limits'; - } - - // is the default currency for the user or the system - $count = $this->userGroup->currencies()->where('transaction_currencies.id', $currency->id)->wherePivot('group_default', 1)->count(); - if ($count > 0) { - Log::info('Is the default currency of the user, return true.'); - - return 'current_default'; - } - - // is the default currency for the user or the system - $count = $this->userGroup->currencies()->where('transaction_currencies.id', $currency->id)->wherePivot('group_default', 1)->count(); - if ($count > 0) { - Log::info('Is the default currency of the user group, return true.'); - - return 'current_default'; - } - - Log::debug('Currency is not used, return false.'); - - return null; - } - - private function countJournals(TransactionCurrency $currency): int - { - $count = $currency->transactions()->whereNull('deleted_at')->count() + $currency->transactionJournals()->whereNull('deleted_at')->count(); - - // also count foreign: - return $count + Transaction::where('foreign_currency_id', $currency->id)->count(); - } - - /** - * Returns ALL currencies, regardless of whether they are enabled or not. - */ - public function getAll(): Collection - { - $all = TransactionCurrency::orderBy('code', 'ASC')->get(); - $local = $this->get(); - - return $all->map(static function (TransactionCurrency $current) use ($local) { - $hasId = $local->contains(static fn (TransactionCurrency $entry) => $entry->id === $current->id); - $isPrimary = $local->contains(static fn (TransactionCurrency $entry) => 1 === (int) $entry->pivot->group_default && $entry->id === $current->id); - $current->userGroupEnabled = $hasId; - $current->userGroupNative = $isPrimary; - - return $current; - }); - } - - public function get(): Collection - { - $all = $this->userGroup->currencies()->orderBy('code', 'ASC')->withPivot(['group_default'])->get(); - $all->map(static function (TransactionCurrency $current) { // @phpstan-ignore-line - $current->userGroupEnabled = true; - $current->userGroupNative = 1 === (int) $current->pivot->group_default; - - return $current; - }); - - /** @var Collection */ - return $all; - } - - public function destroy(TransactionCurrency $currency): bool - { - /** @var UserRepositoryInterface $repository */ - $repository = app(UserRepositoryInterface::class); - if ($repository->hasRole($this->user, 'owner')) { - /** @var CurrencyDestroyService $service */ - $service = app(CurrencyDestroyService::class); - $service->destroy($currency); - } - - return true; - } - - /** - * Disables a currency - */ - public function disable(TransactionCurrency $currency): void - { - $this->userGroup->currencies()->detach($currency->id); - $currency->enabled = false; - $currency->save(); - } - - public function findByName(string $name): ?TransactionCurrency - { - return TransactionCurrency::where('name', $name)->first(); - } - - /** - * Find by object, ID or code. Returns user default or system default. - * - * @throws FireflyException - */ - public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency - { - $result = $this->findCurrencyNull($currencyId, $currencyCode); - - if (!$result instanceof TransactionCurrency) { - Log::debug('Grabbing default currency for this user...'); - - /** @var null|TransactionCurrency $result */ - $result = app('amount')->getPrimaryCurrencyByUserGroup($this->user->userGroup); - } - - Log::debug(sprintf('Final result: %s', $result->code)); - if (false === $result->enabled) { - Log::debug(sprintf('Also enabled currency %s', $result->code)); - $this->enable($result); - } - - return $result; - } - - /** - * Find by object, ID or code. Returns NULL if nothing found. - */ - public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency - { - Log::debug(sprintf('Now in findCurrencyNull("%s", "%s")', $currencyId, $currencyCode)); - $result = $this->find((int) $currencyId); - if (!$result instanceof TransactionCurrency) { - Log::debug(sprintf('Searching for currency with code "%s"...', $currencyCode)); - $result = $this->findByCode((string) $currencyCode); - } - if ($result instanceof TransactionCurrency && false === $result->enabled) { - Log::debug(sprintf('Also enabled currency %s', $result->code)); - $this->enable($result); - } - - return $result; - } - - /** - * Find by ID, return NULL if not found. - */ - public function find(int $currencyId): ?TransactionCurrency - { - return TransactionCurrency::find($currencyId); - } - - /** - * Find by currency code, return NULL if unfound. - */ - public function findByCode(string $currencyCode): ?TransactionCurrency - { - return TransactionCurrency::where('code', $currencyCode)->first(); - } - - public function enable(TransactionCurrency $currency): void - { - $this->userGroup->currencies()->syncWithoutDetaching([$currency->id]); - $currency->enabled = false; - $currency->save(); - } - - public function getByIds(array $ids): Collection - { - return TransactionCurrency::orderBy('code', 'ASC')->whereIn('id', $ids)->get(); - } - - public function isFallbackCurrency(TransactionCurrency $currency): bool - { - return $currency->code === config('firefly.default_currency', 'EUR'); - } - - public function searchCurrency(string $search, int $limit): Collection - { - $query = TransactionCurrency::where('enabled', true); - if ('' !== $search) { - $query->whereLike('name', sprintf('%%%s%%', $search)); - } - - return $query->take($limit)->get(); - } - - /** - * @throws FireflyException - */ - public function store(array $data): TransactionCurrency - { - /** @var TransactionCurrencyFactory $factory */ - $factory = app(TransactionCurrencyFactory::class); - $result = $factory->create($data); - - if (true === $data['enabled']) { - $this->userGroup->currencies()->attach($result->id); - } - - return $result; - } - - public function update(TransactionCurrency $currency, array $data): TransactionCurrency - { - Log::debug('Now in update()'); - // can be true, false, null - $enabled = array_key_exists('enabled', $data) ? $data['enabled'] : null; - // can be true, false, but method only responds to "true". - $default = array_key_exists('default', $data) ? $data['default'] : false; - - // remove illegal combo's: - if (false === $enabled && true === $default) { - $enabled = true; - } - - // update currency with current user specific settings - $currency->refreshForUser($this->user); - - // currency is enabled, must be disabled. - if (false === $enabled) { - Log::debug(sprintf('Disabled currency %s for user #%d', $currency->code, $this->userGroup->id)); - $this->userGroup->currencies()->detach($currency->id); - } - // currency must be enabled - if (true === $enabled) { - Log::debug(sprintf('Enabled currency %s for user #%d', $currency->code, $this->userGroup->id)); - $this->userGroup->currencies()->detach($currency->id); - $this->userGroup->currencies()->syncWithoutDetaching([$currency->id => ['group_default' => false]]); - } - - // currency must be made default. - if (true === $default) { - $this->makePrimary($currency); - } - - /** @var CurrencyUpdateService $service */ - $service = app(CurrencyUpdateService::class); - - return $service->update($currency, $data); - } - - public function makePrimary(TransactionCurrency $currency): void - { - $current = app('amount')->getPrimaryCurrencyByUserGroup($this->userGroup); - Log::debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->userGroup->id)); - $this->userGroup->currencies()->detach($currency->id); - foreach ($this->userGroup->currencies()->get() as $item) { - $this->userGroup->currencies()->updateExistingPivot($item->id, ['group_default' => false]); - } - $this->userGroup->currencies()->syncWithoutDetaching([$currency->id => ['group_default' => true]]); - if ($current->id !== $currency->id) { - Log::debug('Trigger on a different default currency.'); - // clear all primary currency amounts through an event. - event(new UserGroupChangedPrimaryCurrency($this->userGroup)); - } - } -} diff --git a/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php b/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php deleted file mode 100644 index 8c34e22708..0000000000 --- a/app/Repositories/UserGroups/Currency/CurrencyRepositoryInterface.php +++ /dev/null @@ -1,102 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Currency; - -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\TransactionCurrency; -use Illuminate\Support\Collection; - -/** - * Interface CurrencyRepositoryInterface - * - * @deprecated - */ -interface CurrencyRepositoryInterface -{ - public function currencyInUse(TransactionCurrency $currency): bool; - - /** - * Currency is in use where exactly. - */ - public function currencyInUseAt(TransactionCurrency $currency): ?string; - - public function destroy(TransactionCurrency $currency): bool; - - /** - * Disables a currency - */ - public function disable(TransactionCurrency $currency): void; - - /** - * Enables a currency - */ - public function enable(TransactionCurrency $currency): void; - - /** - * Find by ID, return NULL if not found. - */ - public function find(int $currencyId): ?TransactionCurrency; - - public function findByCode(string $currencyCode): ?TransactionCurrency; - - public function findByName(string $name): ?TransactionCurrency; - - /** - * Find by object, ID or code. Returns user default or system default. - */ - public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency; - - /** - * Find by object, ID or code. Returns NULL if nothing found. - */ - public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency; - - /** - * Get the user group's currencies. - * - * @return Collection - */ - public function get(): Collection; - - /** - * Get ALL currencies. - */ - public function getAll(): Collection; - - public function getByIds(array $ids): Collection; - - public function isFallbackCurrency(TransactionCurrency $currency): bool; - - public function makePrimary(TransactionCurrency $currency): void; - - public function searchCurrency(string $search, int $limit): Collection; - - /** - * @throws FireflyException - */ - public function store(array $data): TransactionCurrency; - - public function update(TransactionCurrency $currency, array $data): TransactionCurrency; -} diff --git a/app/Repositories/UserGroups/ExchangeRate/ExchangeRateRepository.php b/app/Repositories/UserGroups/ExchangeRate/ExchangeRateRepository.php deleted file mode 100644 index 3dc1d6a797..0000000000 --- a/app/Repositories/UserGroups/ExchangeRate/ExchangeRateRepository.php +++ /dev/null @@ -1,119 +0,0 @@ -userGroup->currencyExchangeRates()->where('id', $rate->id)->delete(); - } - - #[Override] - public function getAll(): Collection - { - return $this->userGroup->currencyExchangeRates()->orderBy('date', 'ASC')->get(); - } - - #[Override] - public function getRates(TransactionCurrency $from, TransactionCurrency $to): Collection - { - // orderBy('date', 'DESC')->toRawSql(); - return - $this->userGroup->currencyExchangeRates() - ->where(function (Builder $q1) use ($from, $to): void { - $q1->where(function (Builder $q) use ($from, $to): void { - $q->where('from_currency_id', $from->id) - ->where('to_currency_id', $to->id) - ; - })->orWhere(function (Builder $q) use ($from, $to): void { - $q->where('from_currency_id', $to->id) - ->where('to_currency_id', $from->id) - ; - }); - }) - ->orderBy('date', 'DESC') - ->get(['currency_exchange_rates.*']) - ; - - } - - #[Override] - public function getSpecificRateOnDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): ?CurrencyExchangeRate - { - /** @var null|CurrencyExchangeRate */ - return - $this->userGroup->currencyExchangeRates() - ->where('from_currency_id', $from->id) - ->where('to_currency_id', $to->id) - ->where('date', $date->format('Y-m-d')) - ->first() - ; - } - - #[Override] - public function storeExchangeRate(TransactionCurrency $from, TransactionCurrency $to, string $rate, Carbon $date): CurrencyExchangeRate - { - $object = new CurrencyExchangeRate(); - $object->user_id = auth()->user()->id; - $object->user_group_id = $this->userGroup->id; - $object->from_currency_id = $from->id; - $object->to_currency_id = $to->id; - $object->rate = $rate; - $object->date = $date; - $object->date_tz = $date->format('e'); - $object->save(); - - return $object; - } - - #[Override] - public function updateExchangeRate(CurrencyExchangeRate $object, string $rate, ?Carbon $date = null): CurrencyExchangeRate - { - $object->rate = $rate; - if ($date instanceof Carbon) { - $object->date = $date; - } - $object->save(); - - return $object; - } -} diff --git a/app/Repositories/UserGroups/ExchangeRate/ExchangeRateRepositoryInterface.php b/app/Repositories/UserGroups/ExchangeRate/ExchangeRateRepositoryInterface.php deleted file mode 100644 index 00e164d15e..0000000000 --- a/app/Repositories/UserGroups/ExchangeRate/ExchangeRateRepositoryInterface.php +++ /dev/null @@ -1,50 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Journal; - -use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; -use Illuminate\Support\Collection; - -/** - * Class JournalRepository - * - * @deprecated - */ -class JournalRepository implements JournalRepositoryInterface -{ - use UserGroupTrait; - - public function searchJournalDescriptions(array $query, int $limit): Collection - { - $search = $this->userGroup->transactionJournals() - ->orderBy('date', 'DESC') - ; - if (count($query) > 0) { - // split query on spaces just in case: - $search->where(function (EloquentBuilder $q) use ($query): void { - foreach ($query as $line) { - $parts = explode(' ', $line); - foreach ($parts as $part) { - $search = sprintf('%%%s%%', $part); - $q->orWhereLike('description', $search); - } - } - }); - } - - return $search->take($limit)->get(); - } -} diff --git a/app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php b/app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php deleted file mode 100644 index c1312306f7..0000000000 --- a/app/Repositories/UserGroups/PiggyBank/PiggyBankRepository.php +++ /dev/null @@ -1,53 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\PiggyBank; - -use FireflyIII\Models\PiggyBank; -use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; -use Illuminate\Support\Collection; - -/** - * Class PiggyBankRepository - * - * @deprecated - */ -class PiggyBankRepository implements PiggyBankRepositoryInterface -{ - use UserGroupTrait; - - public function getPiggyBanks(): Collection - { - return PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') - ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') - ->where('accounts.user_group_id', $this->userGroup->id) - ->with( - [ - 'objectGroups', - ] - ) - ->orderBy('piggy_banks.order', 'ASC')->distinct()->get(['piggy_banks.*']) - ; - } -} diff --git a/app/Repositories/UserGroups/Tag/TagRepository.php b/app/Repositories/UserGroups/Tag/TagRepository.php deleted file mode 100644 index 49f9c2e044..0000000000 --- a/app/Repositories/UserGroups/Tag/TagRepository.php +++ /dev/null @@ -1,58 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Tag; - -use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; -use Illuminate\Support\Collection; - -/** - * Class TagRepository - * - * @deprecated - */ -class TagRepository implements TagRepositoryInterface -{ - use UserGroupTrait; - - public function searchTag(array $query, int $limit): Collection - { - $search = $this->userGroup->tags(); - if (count($query) > 0) { - // split query on spaces just in case: - $search->where(function (EloquentBuilder $q) use ($query): void { - foreach ($query as $line) { - $parts = explode(' ', $line); - foreach ($parts as $part) { - $search = sprintf('%%%s%%', $part); - $q->orWhereLike('tag', $search); - } - } - }); - } - - return $search->take($limit)->get(['tags.*']); - } -} diff --git a/app/Repositories/UserGroups/Tag/TagRepositoryInterface.php b/app/Repositories/UserGroups/Tag/TagRepositoryInterface.php deleted file mode 100644 index f5aa677560..0000000000 --- a/app/Repositories/UserGroups/Tag/TagRepositoryInterface.php +++ /dev/null @@ -1,40 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Repositories\UserGroups\Tag; - -use Illuminate\Support\Collection; - -/** - * Interface TagRepositoryInterface - * - * @deprecated - */ -interface TagRepositoryInterface -{ - /** - * Find one or more tags based on the query. - */ - public function searchTag(array $query, int $limit): Collection; -} diff --git a/app/Repositories/Webhook/WebhookRepository.php b/app/Repositories/Webhook/WebhookRepository.php index 6d684b8984..fb8da51d2c 100644 --- a/app/Repositories/Webhook/WebhookRepository.php +++ b/app/Repositories/Webhook/WebhookRepository.php @@ -24,9 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Webhook; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Webhook; use FireflyIII\Models\WebhookAttempt; +use FireflyIII\Models\WebhookDelivery; use FireflyIII\Models\WebhookMessage; +use FireflyIII\Models\WebhookResponse; +use FireflyIII\Models\WebhookTrigger; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use Illuminate\Support\Collection; @@ -41,11 +45,20 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac public function all(): Collection { - return $this->user->webhooks()->get(); + return $this->user->webhooks() + // only get upgraded webhooks + ->where('delivery', 1) + ->where('response', 1) + ->where('trigger', 1) + ->get() + ; } public function destroy(Webhook $webhook): void { + // force delete all messages and attempts: + $webhook->webhookMessages()->delete(); + $webhook->delete(); } @@ -87,38 +100,108 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac public function store(array $data): Webhook { - $secret = Str::random(24); - $fullData = [ + $secret = Str::random(24); + $fullData = [ 'user_id' => $this->user->id, 'user_group_id' => $this->user->user_group_id, 'active' => $data['active'] ?? false, 'title' => $data['title'] ?? null, - 'trigger' => $data['trigger'], - 'response' => $data['response'], - 'delivery' => $data['delivery'], + // 'trigger' => $data['trigger'], + // 'response' => $data['response'], + // 'delivery' => $data['delivery'], + 'trigger' => 1, + 'response' => 1, + 'delivery' => 1, 'secret' => $secret, 'url' => $data['url'], ]; - return Webhook::create($fullData); + /** @var Webhook $webhook */ + $webhook = Webhook::create($fullData); + $triggers = new Collection(); + $responses = new Collection(); + $deliveries = new Collection(); + + foreach ($data['triggers'] as $trigger) { + // get the relevant ID: + $object = WebhookTrigger::where('title', $trigger)->first(); + if (null === $object) { + throw new FireflyException(sprintf('Could not find webhook trigger with title "%s".', $trigger)); + } + $triggers->push($object); + } + $webhook->webhookTriggers()->saveMany($triggers); + + foreach ($data['responses'] as $response) { + // get the relevant ID: + $object = WebhookResponse::where('title', $response)->first(); + if (null === $object) { + throw new FireflyException(sprintf('Could not find webhook response with title "%s".', $response)); + } + $responses->push($object); + } + $webhook->webhookResponses()->saveMany($responses); + + foreach ($data['deliveries'] as $delivery) { + // get the relevant ID: + $object = WebhookDelivery::where('title', $delivery)->first(); + if (null === $object) { + throw new FireflyException(sprintf('Could not find webhook delivery with title "%s".', $delivery)); + } + $deliveries->push($object); + } + $webhook->webhookDeliveries()->saveMany($deliveries); + + return $webhook; } public function update(Webhook $webhook, array $data): Webhook { - $webhook->active = $data['active'] ?? $webhook->active; - $webhook->trigger = $data['trigger'] ?? $webhook->trigger; - $webhook->response = $data['response'] ?? $webhook->response; - $webhook->delivery = $data['delivery'] ?? $webhook->delivery; - $webhook->title = $data['title'] ?? $webhook->title; - $webhook->url = $data['url'] ?? $webhook->url; + $webhook->active = $data['active'] ?? $webhook->active; + $webhook->title = $data['title'] ?? $webhook->title; + $webhook->url = $data['url'] ?? $webhook->url; - if (true === $data['secret']) { + if (array_key_exists('secret', $data) && true === $data['secret']) { $secret = Str::random(24); $webhook->secret = $secret; } $webhook->save(); + $triggers = new Collection(); + $responses = new Collection(); + $deliveries = new Collection(); + + foreach ($data['triggers'] as $trigger) { + // get the relevant ID: + $object = WebhookTrigger::where('title', $trigger)->first(); + if (null === $object) { + throw new FireflyException(sprintf('Could not find webhook trigger with title "%s".', $trigger)); + } + $triggers->push($object); + } + $webhook->webhookTriggers()->sync($triggers); + + foreach ($data['responses'] as $response) { + // get the relevant ID: + $object = WebhookResponse::where('title', $response)->first(); + if (null === $object) { + throw new FireflyException(sprintf('Could not find webhook response with title "%s".', $response)); + } + $responses->push($object); + } + $webhook->webhookResponses()->sync($responses); + + foreach ($data['deliveries'] as $delivery) { + // get the relevant ID: + $object = WebhookDelivery::where('title', $delivery)->first(); + if (null === $object) { + throw new FireflyException(sprintf('Could not find webhook delivery with title "%s".', $delivery)); + } + $deliveries->push($object); + } + $webhook->webhookDeliveries()->sync($deliveries); + return $webhook; } } diff --git a/app/Rules/IsDuplicateTransaction.php b/app/Rules/IsDuplicateTransaction.php index e0f6d4ac23..1f9a125205 100644 --- a/app/Rules/IsDuplicateTransaction.php +++ b/app/Rules/IsDuplicateTransaction.php @@ -34,15 +34,11 @@ use Closure; */ class IsDuplicateTransaction implements ValidationRule { - private string $value; - /** * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function validate(string $attribute, mixed $value, Closure $fail): void { - $this->value = $value; - - $fail($this->value); + $fail($value); } } diff --git a/app/Rules/IsValidBulkClause.php b/app/Rules/IsValidBulkClause.php index 8be8759eed..7d5ed57290 100644 --- a/app/Rules/IsValidBulkClause.php +++ b/app/Rules/IsValidBulkClause.php @@ -96,7 +96,7 @@ class IsValidBulkClause implements ValidationRule 'value' => $this->rules[$clause][$arrayKey], ]); if ($validator->fails()) { - $this->error = sprintf('%s: %s: %s', $clause, $arrayKey, implode(', ', $validator->errors()->get('value'))); + $this->error = sprintf('%s: %s: %s', $clause, $arrayKey, implode(', ', $validator->errors()->get('value'))); // @phpstan-ignore-line return false; } diff --git a/app/Rules/IsValidSortInstruction.php b/app/Rules/IsValidSortInstruction.php new file mode 100644 index 0000000000..4d9e7b0cf7 --- /dev/null +++ b/app/Rules/IsValidSortInstruction.php @@ -0,0 +1,65 @@ +. + */ + +namespace FireflyIII\Rules; + +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; + +class IsValidSortInstruction implements ValidationRule +{ + public function __construct(private readonly string $class) {} + + public function validate(string $attribute, mixed $value, Closure $fail): void + { + $shortClass = str_replace('FireflyIII\Models\\', '', $this->class); + if (!is_string($value)) { + $fail('validation.invalid_sort_instruction')->translate(['object' => $shortClass]); + + return; + } + $validParameters = config(sprintf('firefly.allowed_sort_parameters.%s', $shortClass)); + if (!is_array($validParameters)) { + $fail('validation.no_sort_instructions')->translate(['object' => $shortClass]); + + return; + } + $parts = explode(',', $value); + foreach ($parts as $i => $part) { + $part = trim($part); + if (strlen($part) < 2) { + $fail('validation.invalid_sort_instruction_index')->translate(['index' => $i, 'object' => $shortClass]); + + return; + } + if ('-' === $part[0]) { + $part = substr($part, 1); + } + if (!in_array($part, $validParameters, true)) { + $fail('validation.invalid_sort_instruction_index')->translate(['index' => $i, 'object' => $shortClass]); + + return; + } + } + } +} diff --git a/app/Services/Internal/Destroy/AccountDestroyService.php b/app/Services/Internal/Destroy/AccountDestroyService.php index e2e4cb994b..2bd2f62c4e 100644 --- a/app/Services/Internal/Destroy/AccountDestroyService.php +++ b/app/Services/Internal/Destroy/AccountDestroyService.php @@ -108,10 +108,7 @@ class AccountDestroyService 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')]) // @phpstan-ignore-line - ; + $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')]); if (0 === $collection->count()) { return; } diff --git a/app/Services/Internal/Support/CreditRecalculateService.php b/app/Services/Internal/Support/CreditRecalculateService.php index da77efb15a..eb6b33b8d4 100644 --- a/app/Services/Internal/Support/CreditRecalculateService.php +++ b/app/Services/Internal/Support/CreditRecalculateService.php @@ -227,7 +227,6 @@ class CreditRecalculateService $source->save(); $dest->save(); - return; } // Log::debug('Opening balance is valid'); } @@ -277,66 +276,66 @@ class CreditRecalculateService if ($isSameAccount && $isCredit && $this->isWithdrawalIn($usedAmount, $type)) { // case 1 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, (string) $usedAmount); + return bcadd($leftOfDebt, $usedAmount); // Log::debug(sprintf('Case 1 (withdrawal into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isCredit && $this->isWithdrawalOut($usedAmount, $type)) { // case 2 $usedAmount = app('steam')->positive($usedAmount); - return bcsub($leftOfDebt, (string) $usedAmount); + return bcsub($leftOfDebt, $usedAmount); // Log::debug(sprintf('Case 2 (withdrawal away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isCredit && $this->isDepositOut($usedAmount, $type)) { // case 3 $usedAmount = app('steam')->positive($usedAmount); - return bcsub($leftOfDebt, (string) $usedAmount); + return bcsub($leftOfDebt, $usedAmount); // Log::debug(sprintf('Case 3 (deposit away from liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isCredit && $this->isDepositIn($usedAmount, $type)) { // case 4 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, (string) $usedAmount); + return bcadd($leftOfDebt, $usedAmount); // Log::debug(sprintf('Case 4 (deposit into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isCredit && $this->isTransferIn($usedAmount, $type)) { // case 5 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, (string) $usedAmount); + return bcadd($leftOfDebt, $usedAmount); // Log::debug(sprintf('Case 5 (transfer into credit liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isWithdrawalIn($usedAmount, $type)) { // case 6 $usedAmount = app('steam')->positive($usedAmount); - return bcsub($leftOfDebt, (string) $usedAmount); + return bcsub($leftOfDebt, $usedAmount); // Log::debug(sprintf('Case 6 (withdrawal into debit liability): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isDepositOut($usedAmount, $type)) { // case 7 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, (string) $usedAmount); + return bcadd($leftOfDebt, $usedAmount); // Log::debug(sprintf('Case 7 (deposit away from liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isWithdrawalOut($usedAmount, $type)) { // case 8 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, (string) $usedAmount); + return bcadd($leftOfDebt, $usedAmount); // Log::debug(sprintf('Case 8 (withdrawal away from liability): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isTransferIn($usedAmount, $type)) { // case 9 $usedAmount = app('steam')->positive($usedAmount); - return bcsub($leftOfDebt, (string) $usedAmount); + return bcsub($leftOfDebt, $usedAmount); // 2024-10-05, #9225 this used to say you would owe more, but a transfer INTO a debit from wherever means you owe LESS. // Log::debug(sprintf('Case 9 (transfer into debit liability, means you owe LESS): %s - %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } if ($isSameAccount && $isDebit && $this->isTransferOut($usedAmount, $type)) { // case 10 $usedAmount = app('steam')->positive($usedAmount); - return bcadd($leftOfDebt, (string) $usedAmount); + return bcadd($leftOfDebt, $usedAmount); // 2024-10-05, #9225 this used to say you would owe less, but a transfer OUT OF a debit from wherever means you owe MORE. // Log::debug(sprintf('Case 10 (transfer out of debit liability, means you owe MORE): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } @@ -345,7 +344,7 @@ class CreditRecalculateService if (in_array($type, [TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value], true)) { $usedAmount = app('steam')->negative($usedAmount); - return bcadd($leftOfDebt, (string) $usedAmount); + return bcadd($leftOfDebt, $usedAmount); // Log::debug(sprintf('Case X (all other cases): %s + %s = %s', app('steam')->bcround($leftOfDebt, 2), app('steam')->bcround($usedAmount, 2), app('steam')->bcround($result, 2))); } diff --git a/app/Services/Internal/Update/JournalUpdateService.php b/app/Services/Internal/Update/JournalUpdateService.php index 9f53540109..f960b8e3e0 100644 --- a/app/Services/Internal/Update/JournalUpdateService.php +++ b/app/Services/Internal/Update/JournalUpdateService.php @@ -221,13 +221,7 @@ class JournalUpdateService private function hasFields(array $fields): bool { - foreach ($fields as $field) { - if (array_key_exists($field, $this->data)) { - return true; - } - } - - return false; + return array_any($fields, fn ($field) => array_key_exists($field, $this->data)); } private function getOriginalSourceAccount(): Account @@ -780,10 +774,10 @@ class JournalUpdateService private function isBetweenAssetAndLiability(): bool { - /** @var Transaction $sourceTransaction */ + /** @var null|Transaction $sourceTransaction */ $sourceTransaction = $this->transactionJournal->transactions()->where('amount', '<', 0)->first(); - /** @var Transaction $destinationTransaction */ + /** @var null|Transaction $destinationTransaction */ $destinationTransaction = $this->transactionJournal->transactions()->where('amount', '>', 0)->first(); if (null === $sourceTransaction || null === $destinationTransaction) { Log::warning('Either transaction is false, stop.'); diff --git a/app/Support/Amount.php b/app/Support/Amount.php index c0c40ac390..547cf8dd79 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -29,9 +29,11 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\UserGroup; use FireflyIII\Support\Facades\Preferences; +use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Singleton\PreferencesSingleton; use FireflyIII\User; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; use NumberFormatter; /** @@ -58,8 +60,8 @@ class Amount */ public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string { - $locale = app('steam')->getLocale(); - $rounded = app('steam')->bcround($amount, $decimalPlaces); + $locale = Steam::getLocale(); + $rounded = Steam::bcround($amount, $decimalPlaces); $coloured ??= true; $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); @@ -69,10 +71,10 @@ class Amount $result = (string)$fmt->format((float)$rounded); // intentional float if (true === $coloured) { - if (1 === bccomp((string)$rounded, '0')) { + if (1 === bccomp($rounded, '0')) { return sprintf('%s', $result); } - if (-1 === bccomp((string)$rounded, '0')) { + if (-1 === bccomp($rounded, '0')) { return sprintf('%s', $result); } @@ -84,7 +86,7 @@ class Amount public function formatByCurrencyId(int $currencyId, string $amount, ?bool $coloured = null): string { - $format = TransactionCurrency::find($currencyId); + $format = $this->getTransactionCurrencyById($currencyId); return $this->formatFlat($format->symbol, $format->decimal_places, $amount, $coloured); } @@ -114,6 +116,50 @@ class Amount return (string)$amount; } + public function getTransactionCurrencyById(int $currencyId): TransactionCurrency + { + $instance = PreferencesSingleton::getInstance(); + $key = sprintf('transaction_currency_%d', $currencyId); + + /** @var null|TransactionCurrency $pref */ + $pref = $instance->getPreference($key); + if (null !== $pref) { + return $pref; + } + $currency = TransactionCurrency::find($currencyId); + if (null === $currency) { + $message = sprintf('Could not find a transaction currency with ID #%d', $currencyId); + Log::error($message); + + throw new FireflyException($message); + } + $instance->setPreference($key, $currency); + + return $currency; + } + + public function getTransactionCurrencyByCode(string $code): TransactionCurrency + { + $instance = PreferencesSingleton::getInstance(); + $key = sprintf('transaction_currency_%s', $code); + + /** @var null|TransactionCurrency $pref */ + $pref = $instance->getPreference($key); + if (null !== $pref) { + return $pref; + } + $currency = TransactionCurrency::whereCode($code)->first(); + if (null === $currency) { + $message = sprintf('Could not find a transaction currency with code "%s"', $code); + Log::error($message); + + throw new FireflyException($message); + } + $instance->setPreference($key, $currency); + + return $currency; + } + public function convertToPrimary(?User $user = null): bool { $instance = PreferencesSingleton::getInstance(); @@ -176,7 +222,7 @@ class Amount public function getSystemCurrency(): TransactionCurrency { - return TransactionCurrency::where('code', 'EUR')->first(); + return TransactionCurrency::whereNull('deleted_at')->where('code', 'EUR')->first(); } /** @@ -242,8 +288,8 @@ class Amount private function getLocaleInfo(): array { // get config from preference, not from translation: - $locale = app('steam')->getLocale(); - $array = app('steam')->getLocaleArray($locale); + $locale = Steam::getLocale(); + $array = Steam::getLocaleArray($locale); setlocale(LC_MONETARY, $array); $info = localeconv(); diff --git a/app/Support/Authentication/RemoteUserGuard.php b/app/Support/Authentication/RemoteUserGuard.php index 7870187a34..c2e534184b 100644 --- a/app/Support/Authentication/RemoteUserGuard.php +++ b/app/Support/Authentication/RemoteUserGuard.php @@ -139,14 +139,14 @@ class RemoteUserGuard implements Guard /** * @SuppressWarnings("PHPMD.ShortMethodName") */ - public function id(): null|int|string + public function id(): int|string|null { Log::debug(sprintf('Now at %s', __METHOD__)); return $this->user?->id; } - public function setUser(null|Authenticatable|User $user): void // @phpstan-ignore-line + public function setUser(Authenticatable|User|null $user): void // @phpstan-ignore-line { Log::debug(sprintf('Now at %s', __METHOD__)); if ($user instanceof User) { diff --git a/app/Support/Balance.php b/app/Support/Balance.php index 188a967ba6..6b97a04628 100644 --- a/app/Support/Balance.php +++ b/app/Support/Balance.php @@ -26,7 +26,7 @@ namespace FireflyIII\Support; use Carbon\Carbon; use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Support\Facades\Amount; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -61,7 +61,7 @@ class Balance foreach ($result as $entry) { $accountId = (int) $entry->account_id; $currencyId = (int) $entry->transaction_currency_id; - $currencies[$currencyId] ??= TransactionCurrency::find($currencyId); + $currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId); $return[$accountId] ??= []; if (array_key_exists($currencyId, $return[$accountId])) { continue; diff --git a/app/Support/Binder/CurrencyCode.php b/app/Support/Binder/CurrencyCode.php index cc5000ede6..577ae9bdd1 100644 --- a/app/Support/Binder/CurrencyCode.php +++ b/app/Support/Binder/CurrencyCode.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Support\Binder; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Support\Facades\Amount; use Illuminate\Routing\Route; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -38,10 +40,13 @@ class CurrencyCode implements BinderInterface public static function routeBinder(string $value, Route $route): TransactionCurrency { if (auth()->check()) { - $currency = TransactionCurrency::where('code', trim($value))->first(); - if (null !== $currency) { - return $currency; + try { + $currency = Amount::getTransactionCurrencyByCode(trim($value)); + } catch (FireflyException) { + throw new NotFoundHttpException(); } + + return $currency; } throw new NotFoundHttpException(); diff --git a/app/Support/Binder/EitherConfigKey.php b/app/Support/Binder/EitherConfigKey.php index e342c6d158..ed41ac53ca 100644 --- a/app/Support/Binder/EitherConfigKey.php +++ b/app/Support/Binder/EitherConfigKey.php @@ -52,6 +52,7 @@ class EitherConfigKey 'firefly.languages', 'app.timezone', 'firefly.valid_view_ranges', + 'firefly.preselected_accounts', // triggers and actions: 'firefly.rule-actions', diff --git a/app/Support/Calendar/Calculator.php b/app/Support/Calendar/Calculator.php index 8d200fba17..3dff9dff07 100644 --- a/app/Support/Calendar/Calculator.php +++ b/app/Support/Calendar/Calculator.php @@ -70,7 +70,7 @@ class Calculator } self::$intervalMap = new SplObjectStorage(); foreach (Periodicity::cases() as $interval) { - $periodicityClass = __NAMESPACE__."\\Periodicity\\{$interval->name}"; + $periodicityClass = sprintf('%s\Periodicity\%s', __NAMESPACE__, $interval->name); self::$intervals[] = $interval->name; self::$intervalMap->attach($interval, new $periodicityClass()); } diff --git a/app/Support/Chart/Budget/FrontpageChartGenerator.php b/app/Support/Chart/Budget/FrontpageChartGenerator.php index f2728f89e5..9e351afc78 100644 --- a/app/Support/Chart/Budget/FrontpageChartGenerator.php +++ b/app/Support/Chart/Budget/FrontpageChartGenerator.php @@ -114,7 +114,7 @@ class FrontpageChartGenerator */ private function noBudgetLimits(array $data, Budget $budget): array { - $spent = $this->opsRepository->sumExpenses($this->start, $this->end, null, new Collection([$budget])); + $spent = $this->opsRepository->sumExpenses($this->start, $this->end, null, new Collection()->push($budget)); /** @var array $entry */ foreach ($spent as $entry) { @@ -158,7 +158,7 @@ class FrontpageChartGenerator Log::debug(sprintf('Processing limit #%d with %s %s', $limit->id, $limit->transactionCurrency->code, $limit->amount)); } - $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $currency); + $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency); Log::debug(sprintf('Spent array has %d entries.', count($spent))); /** @var array $entry */ diff --git a/app/Support/Chart/Category/WholePeriodChartGenerator.php b/app/Support/Chart/Category/WholePeriodChartGenerator.php index 344b4b68b3..2a28cb4d62 100644 --- a/app/Support/Chart/Category/WholePeriodChartGenerator.php +++ b/app/Support/Chart/Category/WholePeriodChartGenerator.php @@ -40,7 +40,7 @@ class WholePeriodChartGenerator public function generate(Category $category, Carbon $start, Carbon $end): array { - $collection = new Collection([$category]); + $collection = new Collection()->push($category); /** @var OperationsRepositoryInterface $opsRepository */ $opsRepository = app(OperationsRepositoryInterface::class); diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index ad0f0b4b71..d4a44e4e6a 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -85,7 +85,7 @@ class ExportDataGenerator private bool $exportTransactions; private Carbon $start; private User $user; - private UserGroup $userGroup; + private UserGroup $userGroup; // @phpstan-ignore-line public function __construct() { diff --git a/app/Support/Facades/Navigation.php b/app/Support/Facades/Navigation.php index bb171321a6..82b9d4a3e4 100644 --- a/app/Support/Facades/Navigation.php +++ b/app/Support/Facades/Navigation.php @@ -23,28 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Support\Facades; -use Carbon\Carbon; use Illuminate\Support\Facades\Facade; -/** - * Class Navigation. - * - * @method Carbon addPeriod(Carbon $theDate, string $repeatFreq, int $skip) - * @method array blockPeriods(Carbon $start, Carbon $end, string $range) - * @method Carbon endOfPeriod(Carbon $end, string $repeatFreq) - * @method Carbon endOfX(Carbon $theCurrentEnd, string $repeatFreq, Carbon $maxDate = null) - * @method array listOfPeriods(Carbon $start, Carbon $end) - * @method string periodShow(Carbon $theDate, string $repeatFrequency) - * @method string preferredCarbonFormat(Carbon $start, Carbon $end) - * @method string preferredCarbonLocalizedFormat(Carbon $start, Carbon $end) - * @method string preferredEndOfPeriod(Carbon $start, Carbon $end) - * @method string preferredRangeFormat(Carbon $start, Carbon $end) - * @method string preferredSqlFormat(Carbon $start, Carbon $end) - * @method Carbon startOfPeriod(Carbon $theDate, string $repeatFreq) - * @method Carbon subtractPeriod(Carbon $theDate, string $repeatFreq, int $subtract = 1) - * @method Carbon updateEndDate(string $range, Carbon $start) - * @method Carbon updateStartDate(string $range, Carbon $start) - */ class Navigation extends Facade { /** diff --git a/app/Support/Form/FormSupport.php b/app/Support/Form/FormSupport.php index 8240bc45a1..4bcc0fcb87 100644 --- a/app/Support/Form/FormSupport.php +++ b/app/Support/Form/FormSupport.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Support\Form; use Carbon\Carbon; -use Carbon\Exceptions\InvalidDateException; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Support\MessageBag; use Throwable; @@ -63,7 +62,7 @@ trait FormSupport } $name = str_replace('[]', '', $name); - return (string) trans('form.'.$name); + return (string)trans('form.'.$name); } /** @@ -76,7 +75,7 @@ trait FormSupport $options['class'] = 'form-control'; $options['id'] = 'ffInput_'.$name; $options['autocomplete'] = 'off'; - $options['placeholder'] = ucfirst((string) $label); + $options['placeholder'] = ucfirst((string)$label); return $options; } @@ -146,15 +145,6 @@ trait FormSupport protected function getDate(): Carbon { - /** @var Carbon $date */ - $date = null; - - try { - $date = today(config('app.timezone')); - } catch (InvalidDateException $e) { // @phpstan-ignore-line - app('log')->error($e->getMessage()); - } - - return $date; + return today(config('app.timezone')); } } diff --git a/app/Support/Http/Api/AccountBalanceGrouped.php b/app/Support/Http/Api/AccountBalanceGrouped.php index 397146be61..839c87bfb0 100644 --- a/app/Support/Http/Api/AccountBalanceGrouped.php +++ b/app/Support/Http/Api/AccountBalanceGrouped.php @@ -28,6 +28,7 @@ use Carbon\Carbon; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Navigation; use FireflyIII\Support\Facades\Steam; use Illuminate\Support\Collection; @@ -163,7 +164,7 @@ class AccountBalanceGrouped // get conversion rate $rate = $this->getRate($currency, $journal['date']); - $amountConverted = bcmul((string)$amount, $rate); + $amountConverted = bcmul($amount, $rate); // perhaps transaction already has the foreign amount in the primary currency. if ((int)$journal['foreign_currency_id'] === $this->primary->id) { @@ -172,11 +173,11 @@ class AccountBalanceGrouped } // add normal entry - $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], (string)$amount); + $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], $amount); // add converted entry $convertedKey = sprintf('pc_%s', $key); - $this->data[$currencyId][$period][$convertedKey] = bcadd((string)$this->data[$currencyId][$period][$convertedKey], (string)$amountConverted); + $this->data[$currencyId][$period][$convertedKey] = bcadd((string)$this->data[$currencyId][$period][$convertedKey], $amountConverted); } private function findCurrency(int $currencyId): TransactionCurrency @@ -184,7 +185,7 @@ class AccountBalanceGrouped if (array_key_exists($currencyId, $this->currencies)) { return $this->currencies[$currencyId]; } - $this->currencies[$currencyId] = TransactionCurrency::find($currencyId); + $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); return $this->currencies[$currencyId]; } diff --git a/app/Support/Http/Api/CleansChartData.php b/app/Support/Http/Api/CleansChartData.php index 4b02726c1b..aef2ec443f 100644 --- a/app/Support/Http/Api/CleansChartData.php +++ b/app/Support/Http/Api/CleansChartData.php @@ -43,7 +43,7 @@ trait CleansChartData $return = []; /** - * @var mixed $index + * @var int $index * @var array $array */ foreach ($data as $index => $array) { diff --git a/app/Support/Http/Api/ExchangeRateConverter.php b/app/Support/Http/Api/ExchangeRateConverter.php index 1ff60dbaaa..8abb75fe76 100644 --- a/app/Support/Http/Api/ExchangeRateConverter.php +++ b/app/Support/Http/Api/ExchangeRateConverter.php @@ -30,6 +30,7 @@ use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\UserGroup; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Steam; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; @@ -103,7 +104,7 @@ class ExchangeRateConverter // find in cache if (null !== $res) { - Log::debug(sprintf('ExchangeRateConverter: Return cached rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); + Log::debug(sprintf('ExchangeRateConverter: Return cached rate (%s) from %s to %s on %s.', $res, $from->code, $to->code, $date->format('Y-m-d'))); return $res; } @@ -264,11 +265,8 @@ class ExchangeRateConverter if ($cache->has()) { return (int) $cache->get(); } - $euro = TransactionCurrency::whereCode('EUR')->first(); + $euro = Amount::getTransactionCurrencyByCode('EUR'); ++$this->queryCount; - if (null === $euro) { - throw new FireflyException('Cannot find EUR in system, cannot do currency conversion.'); - } $cache->store($euro->id); return $euro->id; diff --git a/app/Support/Http/Api/SummaryBalanceGrouped.php b/app/Support/Http/Api/SummaryBalanceGrouped.php index 6fc534b59a..b50ab2160e 100644 --- a/app/Support/Http/Api/SummaryBalanceGrouped.php +++ b/app/Support/Http/Api/SummaryBalanceGrouped.php @@ -26,6 +26,7 @@ namespace FireflyIII\Support\Http\Api; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Support\Facades\Amount; use Illuminate\Support\Facades\Log; class SummaryBalanceGrouped @@ -110,7 +111,7 @@ class SummaryBalanceGrouped // transaction info: $currencyId = (int) $journal['currency_id']; $amount = bcmul((string) $journal['amount'], $multiplier); - $currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId); + $currency = $this->currencies[$currencyId] ?? Amount::getTransactionCurrencyById($currencyId); $this->currencies[$currencyId] = $currency; $pcAmount = $converter->convert($currency, $this->default, $journal['date'], $amount); if ((int) $journal['foreign_currency_id'] === $this->default->id) { diff --git a/app/Support/Http/Api/ValidatesUserGroupTrait.php b/app/Support/Http/Api/ValidatesUserGroupTrait.php index c33be45a1e..6ce611396d 100644 --- a/app/Support/Http/Api/ValidatesUserGroupTrait.php +++ b/app/Support/Http/Api/ValidatesUserGroupTrait.php @@ -38,8 +38,8 @@ use Illuminate\Support\Facades\Log; */ trait ValidatesUserGroupTrait { - protected ?UserGroup $userGroup = null; - protected ?User $user = null; + protected UserGroup $userGroup; + protected User $user; /** * An "undocumented" filter diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index a95a1e6ff2..d882ce2b5b 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -191,7 +191,7 @@ trait AugumentData $set = $blRepository->getBudgetLimits($budget, $start, $end); - $budgetCollection = new Collection([$budget]); + $budgetCollection = new Collection()->push($budget); // merge sets based on a key, in case of convert to primary currency $limits = new Collection(); diff --git a/app/Support/JsonApi/Enrichments/AccountEnrichment.php b/app/Support/JsonApi/Enrichments/AccountEnrichment.php index c1a162d8fc..62dee47082 100644 --- a/app/Support/JsonApi/Enrichments/AccountEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AccountEnrichment.php @@ -60,17 +60,22 @@ class AccountEnrichment implements EnrichmentInterface private array $currencies = []; private array $locations = []; private array $meta = []; - private TransactionCurrency $primaryCurrency; + private readonly TransactionCurrency $primaryCurrency; private array $notes = []; private array $openingBalances = []; private User $user; private UserGroup $userGroup; private array $lastActivities = []; private ?Carbon $date = null; - private bool $convertToPrimary; + private ?Carbon $start = null; + private ?Carbon $end = null; + private readonly bool $convertToPrimary; private array $balances = []; + private array $startBalances = []; + private array $endBalances = []; private array $objectGroups = []; private array $mappedObjects = []; + private array $sort = []; /** * TODO The account enricher must do conversion from and to the primary currency. @@ -85,7 +90,7 @@ class AccountEnrichment implements EnrichmentInterface public function enrichSingle(array|Model $model): Account|array { Log::debug(__METHOD__); - $collection = new Collection([$model]); + $collection = new Collection()->push($model); $collection = $this->enrich($collection); return $collection->first(); @@ -111,6 +116,7 @@ class AccountEnrichment implements EnrichmentInterface $this->collectObjectGroups(); $this->collectBalances(); $this->appendCollectedData(); + $this->sortData(); return $this->collection; } @@ -234,9 +240,9 @@ class AccountEnrichment implements EnrichmentInterface private function appendCollectedData(): void { $this->collection = $this->collection->map(function (Account $item) { - $id = (int)$item->id; - $item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null; - $meta = [ + $id = (int)$item->id; + $item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null; + $meta = [ 'currency' => null, 'location' => [ 'latitude' => null, @@ -249,14 +255,14 @@ class AccountEnrichment implements EnrichmentInterface 'opening_balance_date' => null, 'opening_balance_amount' => null, 'account_number' => null, - 'notes' => $notes[$id] ?? null, + 'notes' => $this->notes[$id] ?? null, 'last_activity' => $this->lastActivities[$id] ?? null, ]; // add object group if available if (array_key_exists($id, $this->mappedObjects)) { $key = $this->mappedObjects[$id]; - $meta['object_group_id'] = $this->objectGroups[$key]['id']; + $meta['object_group_id'] = (string)$this->objectGroups[$key]['id']; $meta['object_group_title'] = $this->objectGroups[$key]['title']; $meta['object_group_order'] = $this->objectGroups[$key]['order']; } @@ -283,43 +289,47 @@ class AccountEnrichment implements EnrichmentInterface // add balances // get currencies: - $currency = $this->primaryCurrency; // assume primary currency + $currency = $this->primaryCurrency; // assume primary currency if (null !== $meta['currency']) { $currency = $meta['currency']; } // get the current balance: - $date = $this->getDate(); + $date = $this->getDate(); // $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary); - $finalBalance = $this->balances[$id]; + $finalBalance = $this->balances[$id]; + $balanceDifference = $this->getBalanceDifference($id, $currency); Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance); // collect current balances: - $currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places); - $openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places); - $virtualBalance = Steam::bcround($account->virtual_balance ?? '0', $currency->decimal_places); - $debtAmount = $meta['current_debt'] ?? null; + $currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places); + $openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places); + $virtualBalance = Steam::bcround($item->virtual_balance ?? '0', $currency->decimal_places); + $debtAmount = $meta['current_debt'] ?? null; // set some pc_ default values to NULL: - $pcCurrentBalance = null; - $pcOpeningBalance = null; - $pcVirtualBalance = null; - $pcDebtAmount = null; + $pcCurrentBalance = null; + $pcOpeningBalance = null; + $pcVirtualBalance = null; + $pcDebtAmount = null; + $pcBalanceDifference = null; // convert to primary currency if needed: if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) { Log::debug(sprintf('Convert to primary, from %s to %s', $currency->code, $this->primaryCurrency->code)); - $converter = new ExchangeRateConverter(); - $pcCurrentBalance = $converter->convert($currency, $this->primaryCurrency, $date, $currentBalance); - $pcOpeningBalance = $converter->convert($currency, $this->primaryCurrency, $date, $openingBalance); - $pcVirtualBalance = $converter->convert($currency, $this->primaryCurrency, $date, $virtualBalance); - $pcDebtAmount = null === $debtAmount ? null : $converter->convert($currency, $this->primaryCurrency, $date, $debtAmount); + $converter = new ExchangeRateConverter(); + $pcCurrentBalance = $converter->convert($currency, $this->primaryCurrency, $date, $currentBalance); + $pcOpeningBalance = $converter->convert($currency, $this->primaryCurrency, $date, $openingBalance); + $pcVirtualBalance = $converter->convert($currency, $this->primaryCurrency, $date, $virtualBalance); + $pcBalanceDifference = null === $balanceDifference ? null : $converter->convert($currency, $this->primaryCurrency, $date, $balanceDifference); + $pcDebtAmount = null === $debtAmount ? null : $converter->convert($currency, $this->primaryCurrency, $date, $debtAmount); } if ($this->convertToPrimary && $currency->id === $this->primaryCurrency->id) { - $pcCurrentBalance = $currentBalance; - $pcOpeningBalance = $openingBalance; - $pcVirtualBalance = $virtualBalance; - $pcDebtAmount = $debtAmount; + $pcCurrentBalance = $currentBalance; + $pcOpeningBalance = $openingBalance; + $pcVirtualBalance = $virtualBalance; + $pcBalanceDifference = $balanceDifference; + $pcDebtAmount = $debtAmount; } // set opening balance(s) to NULL if the date is null @@ -327,18 +337,21 @@ class AccountEnrichment implements EnrichmentInterface $openingBalance = null; $pcOpeningBalance = null; } - $meta['balances'] = [ - 'current_balance' => $currentBalance, - 'pc_current_balance' => $pcCurrentBalance, - 'opening_balance' => $openingBalance, - 'pc_opening_balance' => $pcOpeningBalance, - 'virtual_balance' => $virtualBalance, - 'pc_virtual_balance' => $pcVirtualBalance, - 'debt_amount' => $debtAmount, - 'pc_debt_amount' => $pcDebtAmount, + $meta['current_balance_date'] = $this->getDate(); + $meta['balances'] = [ + 'current_balance' => $currentBalance, + 'pc_current_balance' => $pcCurrentBalance, + 'opening_balance' => $openingBalance, + 'pc_opening_balance' => $pcOpeningBalance, + 'virtual_balance' => $virtualBalance, + 'pc_virtual_balance' => $pcVirtualBalance, + 'debt_amount' => $debtAmount, + 'pc_debt_amount' => $pcDebtAmount, + 'balance_difference' => $balanceDifference, + 'pc_balance_difference' => $pcBalanceDifference, ]; // end add balances - $item->meta = $meta; + $item->meta = $meta; return $item; }); @@ -352,6 +365,10 @@ class AccountEnrichment implements EnrichmentInterface private function collectBalances(): void { $this->balances = Steam::accountsBalancesOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary); + if ($this->start instanceof Carbon && $this->end instanceof Carbon) { + $this->startBalances = Steam::accountsBalancesOptimized($this->collection, $this->start, $this->primaryCurrency, $this->convertToPrimary); + $this->endBalances = Steam::accountsBalancesOptimized($this->collection, $this->end, $this->primaryCurrency, $this->convertToPrimary); + } } private function collectObjectGroups(): void @@ -378,15 +395,73 @@ class AccountEnrichment implements EnrichmentInterface public function setDate(?Carbon $date): void { + if ($date instanceof Carbon) { + $date->endOfDay(); + Log::debug(sprintf('Date is now %s', $date->toW3cString())); + } $this->date = $date; } public function getDate(): Carbon { - if (null === $this->date) { - return today(); + if (!$this->date instanceof Carbon) { + return now(); } return $this->date; } + + public function setStart(?Carbon $start): void + { + $this->start = $start; + } + + public function setEnd(?Carbon $end): void + { + $this->end = $end; + } + + private function getBalanceDifference(int $id, TransactionCurrency $currency): ?string + { + if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) { + return null; + } + $startBalance = $this->startBalances[$id] ?? []; + $endBalance = $this->endBalances[$id] ?? []; + if (0 === count($startBalance) || 0 === count($endBalance)) { + return null; + } + $start = $startBalance[$currency->code] ?? '0'; + $end = $endBalance[$currency->code] ?? '0'; + + return bcsub($end, $start); + } + + public function setSort(array $sort): void + { + $this->sort = $sort; + } + + private function sortData(): void + { + $dbParams = config('firefly.allowed_db_sort_parameters.Account', []); + + /** @var array $parameter */ + foreach ($this->sort as $parameter) { + if (in_array($parameter[0], $dbParams, true)) { + continue; + } + + switch ($parameter[0]) { + default: + throw new FireflyException(sprintf('Account enrichment cannot sort on field "%s"', $parameter[0])); + + case 'current_balance': + case 'pc_current_balance': + $this->collection = $this->collection->sortBy(static fn (Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]); + + break; + } + } + } } diff --git a/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php b/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php index b8ad52e31a..6154c941aa 100644 --- a/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php +++ b/app/Support/JsonApi/Enrichments/AvailableBudgetEnrichment.php @@ -40,10 +40,9 @@ use Override; class AvailableBudgetEnrichment implements EnrichmentInterface { - private User $user; - private UserGroup $userGroup; - private TransactionCurrency $primaryCurrency; - private bool $convertToPrimary; + private User $user; // @phpstan-ignore-line + private UserGroup $userGroup; // @phpstan-ignore-line + private readonly bool $convertToPrimary; private array $ids = []; private array $currencyIds = []; private array $currencies = []; @@ -56,13 +55,9 @@ class AvailableBudgetEnrichment implements EnrichmentInterface private readonly OperationsRepositoryInterface $opsRepository; private readonly BudgetRepositoryInterface $repository; - - private ?Carbon $start = null; - private ?Carbon $end = null; - public function __construct() { - $this->primaryCurrency = Amount::getPrimaryCurrency(); + // $this->primaryCurrency = Amount::getPrimaryCurrency(); $this->convertToPrimary = Amount::convertToPrimary(); $this->noBudgetRepository = app(NoBudgetRepositoryInterface::class); $this->opsRepository = app(OperationsRepositoryInterface::class); @@ -124,8 +119,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface $start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); $end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); $allActive = $this->repository->getActiveBudgets(); - $spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null); - $spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end, null, null, null); + $spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive); + $spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end); foreach ($this->collection as $availableBudget) { $id = (int)$availableBudget->id; $currencyId = $this->currencyIds[$id]; diff --git a/app/Support/JsonApi/Enrichments/BudgetEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetEnrichment.php index 5605d7f94a..2aa21bc9cf 100644 --- a/app/Support/JsonApi/Enrichments/BudgetEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetEnrichment.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Support\JsonApi\Enrichments; @@ -9,10 +30,8 @@ use FireflyIII\Models\AutoBudget; use FireflyIII\Models\Budget; use FireflyIII\Models\Note; use FireflyIII\Models\ObjectGroup; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; -use FireflyIII\Support\Facades\Amount; use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; @@ -22,8 +41,6 @@ use Illuminate\Support\Facades\Log; class BudgetEnrichment implements EnrichmentInterface { private Collection $collection; - private bool $convertToPrimary; - private TransactionCurrency $primaryCurrency; private User $user; private UserGroup $userGroup; private array $ids = []; @@ -37,11 +54,7 @@ class BudgetEnrichment implements EnrichmentInterface private array $objectGroups = []; private array $mappedObjects = []; - public function __construct() - { - $this->convertToPrimary = Amount::convertToPrimary(); - $this->primaryCurrency = Amount::getPrimaryCurrency(); - } + public function __construct() {} public function enrich(Collection $collection): Collection { @@ -60,7 +73,7 @@ class BudgetEnrichment implements EnrichmentInterface public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); - $collection = new Collection([$model]); + $collection = new Collection()->push($model); $collection = $this->enrich($collection); return $collection->first(); @@ -117,7 +130,7 @@ class BudgetEnrichment implements EnrichmentInterface // add object group if available if (array_key_exists($id, $this->mappedObjects)) { $key = $this->mappedObjects[$id]; - $meta['object_group_id'] = $this->objectGroups[$key]['id']; + $meta['object_group_id'] = (string) $this->objectGroups[$key]['id']; $meta['object_group_title'] = $this->objectGroups[$key]['title']; $meta['object_group_order'] = $this->objectGroups[$key]['order']; } @@ -148,13 +161,13 @@ class BudgetEnrichment implements EnrichmentInterface private function collectExpenses(): void { - if (null !== $this->start && null !== $this->end) { + if ($this->start instanceof Carbon && $this->end instanceof Carbon) { /** @var OperationsRepositoryInterface $opsRepository */ $opsRepository = app(OperationsRepositoryInterface::class); $opsRepository->setUser($this->user); $opsRepository->setUserGroup($this->userGroup); // $spent = $this->beautify(); - // $set = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget])) + // $set = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($budget)) $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null); foreach ($this->collection as $item) { $id = (int)$item->id; diff --git a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php index 787f3c7844..87e8b7a0a8 100644 --- a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php +++ b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Support\JsonApi\Enrichments; @@ -20,19 +41,18 @@ use Illuminate\Support\Facades\Log; class BudgetLimitEnrichment implements EnrichmentInterface { private User $user; - private UserGroup $userGroup; + private UserGroup $userGroup; // @phpstan-ignore-line private Collection $collection; private array $ids = []; private array $notes = []; private Carbon $start; private Carbon $end; - private Collection $budgets; private array $expenses = []; private array $pcExpenses = []; private array $currencyIds = []; private array $currencies = []; private bool $convertToPrimary = true; - private TransactionCurrency $primaryCurrency; + private readonly TransactionCurrency $primaryCurrency; public function __construct() { @@ -125,16 +145,17 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function collectBudgets(): void { - $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); - $this->budgets = Budget::whereIn('id', $budgetIds)->get(); + $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); + $budgets = Budget::whereIn('id', $budgetIds)->get(); - $repository = app(OperationsRepository::class); + $repository = app(OperationsRepository::class); $repository->setUser($this->user); - $expenses = $repository->collectExpenses($this->start, $this->end, null, $this->budgets, null); + $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); /** @var BudgetLimit $budgetLimit */ foreach ($this->collection as $budgetLimit) { $id = (int)$budgetLimit->id; + $filteredExpenses = $this->filterToBudget($expenses, $budgetLimit->budget_id); $filteredExpenses = $repository->sumCollectedExpenses($expenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, false); $this->expenses[$id] = array_values($filteredExpenses); @@ -159,20 +180,21 @@ class BudgetLimitEnrichment implements EnrichmentInterface private function stringifyIds(): void { - $this->expenses = array_map(function ($first) { - return array_map(function ($second) { - $second['currency_id'] = (string)($second['currency_id'] ?? 0); + $this->expenses = array_map(fn ($first) => array_map(function ($second) { + $second['currency_id'] = (string)($second['currency_id'] ?? 0); - return $second; - }, $first); - }, $this->expenses); + return $second; + }, $first), $this->expenses); - $this->pcExpenses = array_map(function ($first) { - return array_map(function ($second) { - $second['currency_id'] = (string)($second['currency_id'] ?? 0); + $this->pcExpenses = array_map(fn ($first) => array_map(function ($second) { + $second['currency_id'] = (string)($second['currency_id'] ?? 0); - return $second; - }, $first); - }, $this->expenses); + return $second; + }, $first), $this->expenses); + } + + private function filterToBudget(array $expenses, int $budget): array + { + return array_filter($expenses, fn (array $item) => (int)$item['budget_id'] === $budget); } } diff --git a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php index 39be921a31..bbe24f94c2 100644 --- a/app/Support/JsonApi/Enrichments/CategoryEnrichment.php +++ b/app/Support/JsonApi/Enrichments/CategoryEnrichment.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Support\JsonApi\Enrichments; @@ -44,7 +65,7 @@ class CategoryEnrichment implements EnrichmentInterface public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); - $collection = new Collection([$model]); + $collection = new Collection()->push($model); $collection = $this->enrich($collection); return $collection->first(); diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php index 62a0ecb11d..9dd940801a 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEnrichment.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Support\JsonApi\Enrichments; @@ -22,8 +43,8 @@ use Illuminate\Support\Facades\Log; class PiggyBankEnrichment implements EnrichmentInterface { - private User $user; - private UserGroup $userGroup; + private User $user; // @phpstan-ignore-line + private UserGroup $userGroup; // @phpstan-ignore-line private Collection $collection; private array $ids = []; private array $currencyIds = []; @@ -32,8 +53,10 @@ class PiggyBankEnrichment implements EnrichmentInterface // private array $accountCurrencies = []; private array $notes = []; private array $mappedObjects = []; - private TransactionCurrency $primaryCurrency; + private readonly TransactionCurrency $primaryCurrency; private array $amounts = []; + private array $accounts = []; + private array $objectGroups = []; public function __construct() { @@ -57,7 +80,7 @@ class PiggyBankEnrichment implements EnrichmentInterface public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); - $collection = new Collection([$model]); + $collection = new Collection()->push($model); $collection = $this->enrich($collection); return $collection->first(); @@ -105,7 +128,7 @@ class PiggyBankEnrichment implements EnrichmentInterface 'pc_current_amount' => '0', ]; } - $this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], $item->current_amount); + $this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], (string) $item->current_amount); if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) { $this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], $item->native_current_amount); } @@ -119,7 +142,7 @@ class PiggyBankEnrichment implements EnrichmentInterface $accountId = (int)$item->account_id; $currencyId = (int)$item->data; if (!array_key_exists($currencyId, $this->currencies)) { - $this->currencies[$currencyId] = TransactionCurrency::find($currencyId); + $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); } // $this->accountCurrencies[$accountId] = $this->currencies[$currencyId]; } @@ -248,7 +271,7 @@ class PiggyBankEnrichment implements EnrichmentInterface */ private function getSuggestedMonthlyAmount(?Carbon $startDate, ?Carbon $targetDate, ?string $targetAmount, string $currentAmount): string { - if (null === $targetAmount || null === $targetDate || null === $startDate) { + if (null === $targetAmount || !$targetDate instanceof Carbon || !$startDate instanceof Carbon) { return '0'; } $savePerMonth = '0'; diff --git a/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php b/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php index 76ea5318f4..fad6293f90 100644 --- a/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php +++ b/app/Support/JsonApi/Enrichments/PiggyBankEventEnrichment.php @@ -1,12 +1,32 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Support\JsonApi\Enrichments; use FireflyIII\Models\AccountMeta; use FireflyIII\Models\PiggyBankEvent; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\UserGroup; use FireflyIII\Support\Facades\Amount; @@ -18,8 +38,8 @@ use Illuminate\Support\Facades\Log; class PiggyBankEventEnrichment implements EnrichmentInterface { - private User $user; - private UserGroup $userGroup; + private User $user; // @phpstan-ignore-line + private UserGroup $userGroup; // @phpstan-ignore-line private Collection $collection; private array $ids = []; private array $journalIds = []; @@ -49,7 +69,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); - $collection = new Collection([$model]); + $collection = new Collection()->push($model); $collection = $this->enrich($collection); return $collection->first(); @@ -100,7 +120,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface $accountId = (int)$item->account_id; $currencyId = (int)$item->data; if (!array_key_exists($currencyId, $this->currencies)) { - $this->currencies[$currencyId] = TransactionCurrency::find($currencyId); + $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); } $this->accountCurrencies[$accountId] = $this->currencies[$currencyId]; } diff --git a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php index 989e9ea924..c8a3d8707a 100644 --- a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php +++ b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php @@ -1,12 +1,32 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Support\JsonApi\Enrichments; use Carbon\Carbon; use FireflyIII\Enums\RecurrenceRepetitionWeekend; -use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\CategoryFactory; use FireflyIII\Models\Account; @@ -21,7 +41,6 @@ use FireflyIII\Models\RecurrenceRepetition; use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Models\TransactionType; use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Support\Facades\Amount; @@ -33,12 +52,14 @@ use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; +use function Safe\json_decode; + class RecurringEnrichment implements EnrichmentInterface { private Collection $collection; private array $ids = []; - private array $transactionTypeIds = []; - private array $transactionTypes = []; + // private array $transactionTypeIds = []; + // private array $transactionTypes = []; private array $notes = []; private array $repetitions = []; private array $transactions = []; @@ -52,7 +73,7 @@ class RecurringEnrichment implements EnrichmentInterface private array $accounts = []; private array $currencies = []; private array $recurrenceIds = []; - private TransactionCurrency $primaryCurrency; + private readonly TransactionCurrency $primaryCurrency; private bool $convertToPrimary = false; public function __construct() @@ -80,7 +101,7 @@ class RecurringEnrichment implements EnrichmentInterface public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); - $collection = new Collection([$model]); + $collection = new Collection()->push($model); $collection = $this->enrich($collection); return $collection->first(); @@ -102,19 +123,19 @@ class RecurringEnrichment implements EnrichmentInterface { /** @var Recurrence $recurrence */ foreach ($this->collection as $recurrence) { - $id = (int)$recurrence->id; - $typeId = (int)$recurrence->transaction_type_id; - $this->ids[] = $id; - $this->transactionTypeIds[$id] = $typeId; + $id = (int)$recurrence->id; + // $typeId = (int)$recurrence->transaction_type_id; + $this->ids[] = $id; + // $this->transactionTypeIds[$id] = $typeId; } - $this->ids = array_unique($this->ids); + $this->ids = array_unique($this->ids); // collect transaction types. - $transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get(); - foreach ($transactionTypes as $transactionType) { - $id = (int)$transactionType->id; - $this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type); - } + // $transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get(); + // foreach ($transactionTypes as $transactionType) { + // $id = (int)$transactionType->id; + // $this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type); + // } } private function collectRepetitions(): void @@ -126,10 +147,8 @@ class RecurringEnrichment implements EnrichmentInterface /** @var RecurrenceRepetition $repetition */ foreach ($set as $repetition) { - $recurrence = $this->collection->filter(function (Recurrence $item) use ($repetition) { - return (int)$item->id === (int)$repetition->recurrence_id; - })->first(); - $fromDate = $recurrence->latest_date ?? $recurrence->first_date; + $recurrence = $this->collection->filter(fn (Recurrence $item) => (int)$item->id === (int)$repetition->recurrence_id)->first(); + $fromDate = clone ($recurrence->latest_date ?? $recurrence->first_date); $id = (int)$repetition->recurrence_id; $repId = (int)$repetition->id; $this->repetitions[$id] ??= []; @@ -137,20 +156,20 @@ class RecurringEnrichment implements EnrichmentInterface // get the (future) occurrences for this specific type of repetition: $amount = 'daily' === $repetition->repetition_type ? 9 : 5; $set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount); + $occurrences = []; /** @var Carbon $carbon */ foreach ($set as $carbon) { $occurrences[] = $carbon->toAtomString(); } - $this->repetitions[$id][$repId] = [ 'id' => (string)$repId, 'created_at' => $repetition->created_at->toAtomString(), 'updated_at' => $repetition->updated_at->toAtomString(), 'type' => $repetition->repetition_type, - 'moment' => (string)$repetition->moment, - 'skip' => (int)$repetition->skip, - 'weekend' => RecurrenceRepetitionWeekend::from((int)$repetition->weekend), + 'moment' => (string)$repetition->repetition_moment, + 'skip' => (int)$repetition->repetition_skip, + 'weekend' => RecurrenceRepetitionWeekend::from((int)$repetition->weekend)->value, 'description' => $this->getRepetitionDescription($repetition), 'occurrences' => $occurrences, ]; @@ -378,7 +397,7 @@ class RecurringEnrichment implements EnrichmentInterface private function collectTransactionMetaData(): void { $ids = array_keys($this->transactions); - $meta = RecurrenceTransactionMeta::whereIn('rt_id', $ids)->get(); + $meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get(); // other meta-data to be collected: $billIds = []; $piggyBankIds = []; @@ -388,8 +407,15 @@ class RecurringEnrichment implements EnrichmentInterface foreach ($meta as $entry) { $id = (int)$entry->id; $transactionId = (int)$entry->rt_id; - $recurrenceId = $this->recurrenceIds[$transactionId]; - $name = (string)$entry->name; + + // this should refer to another array, were rtIds can be used to find the recurrence. + $recurrenceId = $this->recurrenceIds[$transactionId] ?? 0; + $name = (string)($entry->name ?? ''); + if (0 === $recurrenceId) { + Log::error(sprintf('Could not find recurrence ID for recurrence transaction ID %d', $transactionId)); + + continue; + } switch ($name) { default: diff --git a/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php b/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php index 88307e028d..06388a80bc 100644 --- a/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php +++ b/app/Support/JsonApi/Enrichments/SubscriptionEnrichment.php @@ -1,11 +1,33 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Support\JsonApi\Enrichments; use Carbon\Carbon; use Carbon\CarbonInterface; +use Carbon\Exceptions\InvalidFormatException; use FireflyIII\Models\Bill; use FireflyIII\Models\Note; use FireflyIII\Models\ObjectGroup; @@ -25,9 +47,9 @@ use Illuminate\Support\Facades\Log; class SubscriptionEnrichment implements EnrichmentInterface { private User $user; - private UserGroup $userGroup; + private UserGroup $userGroup; // @phpstan-ignore-line private Collection $collection; - private bool $convertToPrimary; + private readonly bool $convertToPrimary; private ?Carbon $start = null; private ?Carbon $end = null; private array $subscriptionIds = []; @@ -36,7 +58,7 @@ class SubscriptionEnrichment implements EnrichmentInterface private array $paidDates = []; private array $notes = []; private array $payDates = []; - private TransactionCurrency $primaryCurrency; + private readonly TransactionCurrency $primaryCurrency; private BillDateCalculator $calculator; public function __construct() @@ -64,7 +86,7 @@ class SubscriptionEnrichment implements EnrichmentInterface $paidDates = $this->paidDates; $payDates = $this->payDates; $this->collection = $this->collection->map(function (Bill $item) use ($notes, $objectGroups, $paidDates, $payDates) { - $id = (int) $item->id; + $id = (int)$item->id; $currency = $item->transactionCurrency; $nem = $this->getNextExpectedMatch($payDates[$id] ?? []); @@ -101,7 +123,7 @@ class SubscriptionEnrichment implements EnrichmentInterface // add object group if available if (array_key_exists($id, $this->mappedObjects)) { $key = $this->mappedObjects[$id]; - $meta['object_group_id'] = $objectGroups[$key]['id']; + $meta['object_group_id'] = (string)$objectGroups[$key]['id']; $meta['object_group_title'] = $objectGroups[$key]['title']; $meta['object_group_order'] = $objectGroups[$key]['order']; } @@ -123,7 +145,7 @@ class SubscriptionEnrichment implements EnrichmentInterface public function enrichSingle(array|Model $model): array|Model { Log::debug(__METHOD__); - $collection = new Collection([$model]); + $collection = new Collection()->push($model); $collection = $this->enrich($collection); return $collection->first(); @@ -137,7 +159,7 @@ class SubscriptionEnrichment implements EnrichmentInterface ->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray() ; foreach ($notes as $note) { - $this->notes[(int) $note['noteable_id']] = (string) $note['text']; + $this->notes[(int)$note['noteable_id']] = (string)$note['text']; } Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); } @@ -157,7 +179,7 @@ class SubscriptionEnrichment implements EnrichmentInterface { /** @var Bill $bill */ foreach ($this->collection as $bill) { - $this->subscriptionIds[] = (int) $bill->id; + $this->subscriptionIds[] = (int)$bill->id; } $this->subscriptionIds = array_unique($this->subscriptionIds); } @@ -173,14 +195,14 @@ class SubscriptionEnrichment implements EnrichmentInterface $ids = array_unique($set->pluck('object_group_id')->toArray()); foreach ($set as $entry) { - $this->mappedObjects[(int) $entry->object_groupable_id] = (int) $entry->object_group_id; + $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; } $groups = ObjectGroup::whereIn('id', $ids)->get(['id', 'title', 'order'])->toArray(); foreach ($groups as $group) { - $group['id'] = (int) $group['id']; - $group['order'] = (int) $group['order']; - $this->objectGroups[(int) $group['id']] = $group; + $group['id'] = (int)$group['id']; + $group['order'] = (int)$group['order']; + $this->objectGroups[(int)$group['id']] = $group; } } @@ -188,7 +210,7 @@ class SubscriptionEnrichment implements EnrichmentInterface { $this->paidDates = []; Log::debug('Now in collectPaidDates for bills'); - if (null === $this->start || null === $this->end) { + if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) { Log::debug('Parameters are NULL, set empty array'); return; @@ -247,63 +269,62 @@ class SubscriptionEnrichment implements EnrichmentInterface foreach ($this->collection as $subscription) { // Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date. Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $start->format('Y-m-d'))); - $lastPaidDate = $this->lastPaidDate($subscription, $set, $start); + $lastPaidDate = $this->lastPaidDate($subscription, $set, $start); Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d'))); // At this point the "next match" is exactly after the last time the bill was paid. - $result = []; - $filtered = $set->filter(function (TransactionJournal $journal) use ($subscription) { - return (int) $journal->bill_id === (int) $subscription->id; - }); + $result = []; + $filtered = $set->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); foreach ($filtered as $entry) { $array = [ - 'transaction_group_id' => (string) $entry->transaction_group_id, - 'transaction_journal_id' => (string) $entry->id, - 'date' => $entry->date->toAtomString(), - 'date_object' => $entry->date, - 'subscription_id' => (string) $entry->bill_id, - 'currency_id' => (string) $entry->transaction_currency_id, - 'currency_code' => $entry->transaction_currency_code, - 'currency_symbol' => $entry->transaction_currency_symbol, - 'currency_decimal_places' => $entry->transaction_currency_decimal_places, - 'primary_currency_id' => (string) $this->primaryCurrency->id, - 'primary_currency_code' => $this->primaryCurrency->code, - 'primary_currency_symbol' => $this->primaryCurrency->symbol, - 'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places, - 'amount' => Steam::bcround($entry->amount, $entry->transaction_currency_decimal_places), - 'pc_amount' => null, - 'foreign_amount' => null, - 'pc_foreign_amount' => null, + 'transaction_group_id' => (string)$entry->transaction_group_id, + 'transaction_journal_id' => (string)$entry->id, + 'date' => $entry->date->toAtomString(), + 'date_object' => $entry->date, + 'subscription_id' => (string)$entry->bill_id, + 'currency_id' => (string)$entry->transaction_currency_id, + 'currency_code' => $entry->transaction_currency_code, + 'currency_symbol' => $entry->transaction_currency_symbol, + 'currency_decimal_places' => $entry->transaction_currency_decimal_places, + 'primary_currency_id' => (string)$this->primaryCurrency->id, + 'primary_currency_code' => $this->primaryCurrency->code, + 'primary_currency_symbol' => $this->primaryCurrency->symbol, + 'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places, + 'amount' => Steam::bcround($entry->amount, $entry->transaction_currency_decimal_places), + 'pc_amount' => null, + 'foreign_amount' => null, + 'pc_foreign_amount' => null, ]; if (null !== $entry->foreign_amount && null !== $entry->foreign_currency_code) { - $array['foreign_currency_id'] = (string) $entry->foreign_currency_id; + $array['foreign_currency_id'] = (string)$entry->foreign_currency_id; $array['foreign_currency_code'] = $entry->foreign_currency_code; $array['foreign_currency_symbol'] = $entry->foreign_currency_symbol; $array['foreign_currency_decimal_places'] = $entry->foreign_currency_decimal_places; $array['foreign_amount'] = Steam::bcround($entry->foreign_amount, $entry->foreign_currency_decimal_places); } // convert to primary, but is already primary. - if ($this->convertToPrimary && (int) $entry->transaction_currency_id === $this->primaryCurrency->id) { + if ($this->convertToPrimary && (int)$entry->transaction_currency_id === $this->primaryCurrency->id) { $array['pc_amount'] = $array['amount']; } // convert to primary, but is NOT already primary. - if ($this->convertToPrimary && (int) $entry->transaction_currency_id !== $this->primaryCurrency->id) { + if ($this->convertToPrimary && (int)$entry->transaction_currency_id !== $this->primaryCurrency->id) { $array['pc_amount'] = $converter->convert($entry->transactionCurrency, $this->primaryCurrency, $entry->date, $entry->amount); } // convert to primary, but foreign is already primary. - if ($this->convertToPrimary && (int) $entry->foreign_currency_id === $this->primaryCurrency->id) { + if ($this->convertToPrimary && (int)$entry->foreign_currency_id === $this->primaryCurrency->id) { $array['pc_foreign_amount'] = $array['foreign_amount']; } // convert to primary, but foreign is NOT already primary. - if ($this->convertToPrimary && null !== $entry->foreign_currency_id && (int) $entry->foreign_currency_id !== $this->primaryCurrency->id) { + if ($this->convertToPrimary && null !== $entry->foreign_currency_id && (int)$entry->foreign_currency_id !== $this->primaryCurrency->id) { // TODO this is very database intensive. - $foreignCurrency = TransactionCurrency::find($entry->foreign_currency_id); + /** @var TransactionCurrency $foreignCurrency */ + $foreignCurrency = Amount::getTransactionCurrencyById($entry->foreign_currency_id); $array['pc_foreign_amount'] = $converter->convert($foreignCurrency, $this->primaryCurrency, $entry->date, $entry->amount); } $result[] = $array; } - $this->paidDates[(int) $subscription->id] = $result; + $this->paidDates[(int)$subscription->id] = $result; } } @@ -323,9 +344,7 @@ class SubscriptionEnrichment implements EnrichmentInterface */ protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon { - $filtered = $dates->filter(function (TransactionJournal $journal) use ($subscription) { - return (int) $journal->bill_id === (int) $subscription->id; - }); + $filtered = $dates->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id)); if (0 === $filtered->count()) { return $default; @@ -362,14 +381,14 @@ class SubscriptionEnrichment implements EnrichmentInterface Log::debug(sprintf('[b] Last paid date is: %s', $return->format('Y-m-d'))); } } - Log::debug(sprintf('[c] Last paid date is: "%s"', $return?->format('Y-m-d'))); + // Log::debug(sprintf('[c] Last paid date is: "%s"', $return?->format('Y-m-d'))); return $return; } private function collectPayDates(): void { - if (null === $this->start || null === $this->end) { + if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) { Log::debug('Parameters are NULL, set empty array'); return; @@ -377,8 +396,8 @@ class SubscriptionEnrichment implements EnrichmentInterface /** @var Bill $subscription */ foreach ($this->collection as $subscription) { - $id = (int) $subscription->id; - $lastPaidDate = $this->getLastPaidDate($paidDates[$id] ?? []); + $id = (int)$subscription->id; + $lastPaidDate = $this->getLastPaidDate($this->paidDates[$id] ?? []); $payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate); $payDatesFormatted = []; foreach ($payDates as $string) { @@ -408,16 +427,17 @@ class SubscriptionEnrichment implements EnrichmentInterface $firstPayDate = $payDates[0] ?? null; if (null !== $firstPayDate) { - $nemDate = Carbon::parse($firstPayDate, config('app.timezone')); - if (!$nemDate instanceof Carbon) { + try { + $nemDate = Carbon::parse($firstPayDate, config('app.timezone')); + } catch (InvalidFormatException) { $nemDate = today(config('app.timezone')); } - $nem = $nemDate; + $nem = $nemDate; // nullify again when it's outside the current view range. if ( - (null !== $this->start && $nemDate->lt($this->start)) - || (null !== $this->end && $nemDate->gt($this->end)) + ($this->start instanceof Carbon && $nemDate->lt($this->start)) + || ($this->end instanceof Carbon && $nemDate->gt($this->end)) ) { $nem = null; $nemDate = null; @@ -430,7 +450,7 @@ class SubscriptionEnrichment implements EnrichmentInterface private function getNextExpectedMatchDiff(?Carbon $nem, array $payDates): string { - if (null === $nem) { + if (!$nem instanceof Carbon) { return trans('firefly.not_expected_period'); } $nemDiff = trans('firefly.not_expected_period'); @@ -441,8 +461,9 @@ class SubscriptionEnrichment implements EnrichmentInterface $current = $payDates[0] ?? null; if (null !== $current && !$nem->isToday()) { - $temp2 = Carbon::parse($current, config('app.timezone')); - if (!$temp2 instanceof Carbon) { + try { + $temp2 = Carbon::parse($current, config('app.timezone')); + } catch (InvalidFormatException) { $temp2 = today(config('app.timezone')); } $nemDiff = trans('firefly.bill_expected_date', ['date' => $temp2->diffForHumans(today(config('app.timezone')), CarbonInterface::DIFF_RELATIVE_TO_NOW)]); diff --git a/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php index 0e552281a3..d322331135 100644 --- a/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php +++ b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php @@ -53,9 +53,9 @@ class TransactionGroupEnrichment implements EnrichmentInterface private array $metaData = []; private array $notes = []; private array $tags = []; - private User $user; + private User $user; // @phpstan-ignore-line private readonly TransactionCurrency $primaryCurrency; - private UserGroup $userGroup; + private UserGroup $userGroup; // @phpstan-ignore-line public function __construct() { @@ -68,7 +68,7 @@ class TransactionGroupEnrichment implements EnrichmentInterface { Log::debug(__METHOD__); if (is_array($model)) { - $collection = new Collection([$model]); + $collection = new Collection()->push($model); $collection = $this->enrich($collection); return $collection->first(); @@ -143,9 +143,9 @@ class TransactionGroupEnrichment implements EnrichmentInterface continue; } if (in_array($name, $this->dateFields, true)) { - Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data)); + // Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data)); $this->metaData[$entry['transaction_journal_id']][$name] = Carbon::parse($data, config('app.timezone')); - Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString())); + // Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString())); continue; } @@ -177,7 +177,7 @@ class TransactionGroupEnrichment implements EnrichmentInterface ->whereIn('attachable_id', $this->journalIds) ->where('attachable_type', TransactionJournal::class) ->groupBy('attachable_id') - ->get(['attachable_id', DB::raw('COUNT(id) as nr_of_attachments')]) // @phpstan-ignore-line + ->get(['attachable_id', DB::raw('COUNT(id) as nr_of_attachments')]) ->toArray() ; foreach ($attachments as $row) { diff --git a/app/Support/JsonApi/Enrichments/WebhookEnrichment.php b/app/Support/JsonApi/Enrichments/WebhookEnrichment.php new file mode 100644 index 0000000000..516705892a --- /dev/null +++ b/app/Support/JsonApi/Enrichments/WebhookEnrichment.php @@ -0,0 +1,164 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\JsonApi\Enrichments; + +use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum; +use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum; +use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum; +use FireflyIII\Models\UserGroup; +use FireflyIII\Models\Webhook; +use FireflyIII\Models\WebhookDelivery; +use FireflyIII\Models\WebhookResponse; +use FireflyIII\Models\WebhookTrigger; +use FireflyIII\User; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; +use stdClass; + +class WebhookEnrichment implements EnrichmentInterface +{ + private Collection $collection; + private User $user; // @phpstan-ignore-line + private UserGroup $userGroup; // @phpstan-ignore-line + private array $ids = []; + private array $deliveries = []; + private array $responses = []; + private array $triggers = []; + + private array $webhookDeliveries = []; + private array $webhookResponses = []; + private array $webhookTriggers = []; + + public function enrich(Collection $collection): Collection + { + $this->collection = $collection; + if ($this->collection->count() > 0) { + $this->collectIds(); + $this->collectInfo(); + $this->collectWebhookInfo(); + $this->appendCollectedInfo(); + } + + return $this->collection; + } + + public function enrichSingle(array|Model $model): array|Model + { + Log::debug(__METHOD__); + $collection = new Collection()->push($model); + $collection = $this->enrich($collection); + + return $collection->first(); + } + + public function setUser(User $user): void + { + $this->user = $user; + } + + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } + + private function collectIds(): void + { + /** @var Webhook $webhook */ + foreach ($this->collection as $webhook) { + $this->ids[] = $webhook->id; + } + $this->ids = array_unique($this->ids); + } + + private function collectInfo(): void + { + $all = WebhookDelivery::get(); + + /** @var WebhookDelivery $item */ + foreach ($all as $item) { + $this->deliveries[$item->id] = $item->key; + } + $all = WebhookResponse::get(); + + /** @var WebhookResponse $item */ + foreach ($all as $item) { + $this->responses[$item->id] = $item->key; + } + $all = WebhookTrigger::get(); + + /** @var WebhookTrigger $item */ + foreach ($all as $item) { + $this->triggers[$item->id] = $item->key; + } + + } + + private function collectWebhookInfo(): void + { + $set = DB::table('webhook_webhook_delivery')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_delivery_id']); + + /** @var stdClass $item */ + foreach ($set as $item) { + $id = $item->webhook_id; + $deliveryId = $item->webhook_delivery_id; + $this->webhookDeliveries[$id][] = WebhookDeliveryEnum::from($this->deliveries[$deliveryId])->name; + } + + $set = DB::table('webhook_webhook_response')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_response_id']); + + /** @var stdClass $item */ + foreach ($set as $item) { + $id = $item->webhook_id; + $responseId = $item->webhook_response_id; + $this->webhookResponses[$id][] = WebhookResponseEnum::from($this->responses[$responseId])->name; + } + + $set = DB::table('webhook_webhook_trigger')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_trigger_id']); + + /** @var stdClass $item */ + foreach ($set as $item) { + $id = $item->webhook_id; + $triggerId = $item->webhook_trigger_id; + $this->webhookTriggers[$id][] = WebhookTriggerEnum::from($this->triggers[$triggerId])->name; + } + } + + private function appendCollectedInfo(): void + { + $this->collection = $this->collection->map(function (Webhook $item) { + $meta = [ + 'deliveries' => $this->webhookDeliveries[$item->id] ?? [], + 'responses' => $this->webhookResponses[$item->id] ?? [], + 'triggers' => $this->webhookTriggers[$item->id] ?? [], + ]; + $item->meta = $meta; + + return $item; + }); + } +} diff --git a/app/Support/Models/AccountBalanceCalculator.php b/app/Support/Models/AccountBalanceCalculator.php index f4ae008911..d2a3572b7d 100644 --- a/app/Support/Models/AccountBalanceCalculator.php +++ b/app/Support/Models/AccountBalanceCalculator.php @@ -25,11 +25,12 @@ declare(strict_types=1); namespace FireflyIII\Support\Models; use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\AccountBalance; use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Support\Facades\Amount; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -102,7 +103,7 @@ class AccountBalanceCalculator // before and after are easy: $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; - $after = bcadd($before, (string) $entry->amount); + $after = bcadd($before, (string)$entry->amount); if (true === $entry->balance_dirty || $accounts->count() > 0) { // update the transaction: $entry->balance_before = $before; @@ -144,7 +145,7 @@ class AccountBalanceCalculator $query->where('transaction_journals.date', '<', $notBefore); $first = $query->first(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount', 'transactions.balance_after']); - $balance = (string) ($first->balance_after ?? '0'); + $balance = (string)($first->balance_after ?? '0'); Log::debug(sprintf('getLatestBalance: found balance: %s in transaction #%d', $balance, $first->id ?? 0)); return $balance; @@ -170,9 +171,9 @@ class AccountBalanceCalculator * @var array $balance */ foreach ($currencies as $currencyId => $balance) { - /** @var null|TransactionCurrency $currency */ - $currency = TransactionCurrency::find($currencyId); - if (null === $currency) { + try { + $currency = Amount::getTransactionCurrencyById($currencyId); + } catch (FireflyException) { Log::error(sprintf('Could not find currency #%d, will not save account balance.', $currencyId)); continue; @@ -205,7 +206,7 @@ class AccountBalanceCalculator foreach ($transactionJournal->transactions as $transaction) { $set[$transaction->account_id] = $transaction->account; } - $accounts = new Collection($set); + $accounts = new Collection()->push(...$set); $object->optimizedCalculation($accounts, $transactionJournal->date); } } diff --git a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php index a6f33660dc..3f50036ab1 100644 --- a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php +++ b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Support\Observers; diff --git a/app/Support/ParseDateString.php b/app/Support/ParseDateString.php index 90a2ac6d88..f2ab22a672 100644 --- a/app/Support/ParseDateString.php +++ b/app/Support/ParseDateString.php @@ -78,7 +78,7 @@ class ParseDateString */ public function parseDate(string $date): Carbon { - app('log')->debug(sprintf('parseDate("%s")', $date)); + Log::debug(sprintf('parseDate("%s")', $date)); $date = strtolower($date); // parse keywords: if (in_array($date, $this->keywords, true)) { @@ -88,7 +88,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])$/'; $result = preg_match($pattern, $date); - if (false !== $result && 0 !== $result) { + if (0 !== $result) { return $this->parseDefaultDate($date); } @@ -107,7 +107,7 @@ class ParseDateString // maybe a date range if (10 === strlen($date) && (str_contains($date, 'xx') || str_contains($date, 'xxxx'))) { - app('log')->debug(sprintf('[c] Detected a date range ("%s"), return a fake date.', $date)); + 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. @@ -158,7 +158,7 @@ class ParseDateString protected function parseRelativeDate(string $date): Carbon { - app('log')->debug(sprintf('Now in parseRelativeDate("%s")', $date)); + Log::debug(sprintf('Now in parseRelativeDate("%s")', $date)); $parts = explode(' ', $date); $today = today(config('app.timezone'))->startOfDay(); $functions = [ @@ -179,14 +179,14 @@ class ParseDateString ]; foreach ($parts as $part) { - app('log')->debug(sprintf('Now parsing part "%s"', $part)); + Log::debug(sprintf('Now parsing part "%s"', $part)); $part = trim($part); // verify if correct $pattern = '/[+-]\d+[wqmdy]/'; $result = preg_match($pattern, $part); - if (0 === $result || false === $result) { - app('log')->error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); + if (0 === $result) { + Log::error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); continue; } @@ -194,14 +194,14 @@ class ParseDateString $period = $part[strlen($part) - 1]; $number = (int) substr($part, 1, -1); if (!array_key_exists($period, $functions[$direction])) { - app('log')->error(sprintf('No method for direction %d and period "%s".', $direction, $period)); + Log::error(sprintf('No method for direction %d and period "%s".', $direction, $period)); continue; } $func = $functions[$direction][$period]; - app('log')->debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d'))); + 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'))); + Log::debug(sprintf('Resulting date is %s', $today->format('Y-m-d'))); } return $today; @@ -259,12 +259,12 @@ class ParseDateString { $pattern = '/^xxxx-xx-(0[1-9]|[12]\d|3[01])$/'; $result = preg_match($pattern, $date); - if (false !== $result && 0 !== $result) { - app('log')->debug(sprintf('"%s" is a day range.', $date)); + if (0 !== $result) { + Log::debug(sprintf('"%s" is a day range.', $date)); return true; } - app('log')->debug(sprintf('"%s" is not a day range.', $date)); + Log::debug(sprintf('"%s" is not a day range.', $date)); return false; } @@ -286,12 +286,12 @@ class ParseDateString // if regex for xxxx-MM-xx: $pattern = '/^xxxx-(0[1-9]|1[012])-xx$/'; $result = preg_match($pattern, $date); - if (false !== $result && 0 !== $result) { - app('log')->debug(sprintf('"%s" is a month range.', $date)); + if (0 !== $result) { + Log::debug(sprintf('"%s" is a month range.', $date)); return true; } - app('log')->debug(sprintf('"%s" is not a month range.', $date)); + Log::debug(sprintf('"%s" is not a month range.', $date)); return false; } @@ -301,7 +301,7 @@ class ParseDateString */ protected function parseMonthRange(string $date): array { - app('log')->debug(sprintf('parseMonthRange: Parsed "%s".', $date)); + Log::debug(sprintf('parseMonthRange: Parsed "%s".', $date)); $parts = explode('-', $date); return [ @@ -314,12 +314,12 @@ class ParseDateString // if regex for YYYY-xx-xx: $pattern = '/^(19|20)\d\d-xx-xx$/'; $result = preg_match($pattern, $date); - if (false !== $result && 0 !== $result) { - app('log')->debug(sprintf('"%s" is a year range.', $date)); + if (0 !== $result) { + Log::debug(sprintf('"%s" is a year range.', $date)); return true; } - app('log')->debug(sprintf('"%s" is not a year range.', $date)); + Log::debug(sprintf('"%s" is not a year range.', $date)); return false; } @@ -329,7 +329,7 @@ class ParseDateString */ protected function parseYearRange(string $date): array { - app('log')->debug(sprintf('parseYearRange: Parsed "%s"', $date)); + Log::debug(sprintf('parseYearRange: Parsed "%s"', $date)); $parts = explode('-', $date); return [ @@ -342,12 +342,12 @@ class ParseDateString // if regex for xxxx-MM-DD: $pattern = '/^xxxx-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; $result = preg_match($pattern, $date); - if (false !== $result && 0 !== $result) { - app('log')->debug(sprintf('"%s" is a month/day range.', $date)); + if (0 !== $result) { + Log::debug(sprintf('"%s" is a month/day range.', $date)); return true; } - app('log')->debug(sprintf('"%s" is not a month/day range.', $date)); + Log::debug(sprintf('"%s" is not a month/day range.', $date)); return false; } @@ -357,7 +357,7 @@ class ParseDateString */ private function parseMonthDayRange(string $date): array { - app('log')->debug(sprintf('parseMonthDayRange: Parsed "%s".', $date)); + Log::debug(sprintf('parseMonthDayRange: Parsed "%s".', $date)); $parts = explode('-', $date); return [ @@ -371,12 +371,12 @@ class ParseDateString // if regex for YYYY-xx-DD: $pattern = '/^(19|20)\d\d-xx-(0[1-9]|[12]\d|3[01])$/'; $result = preg_match($pattern, $date); - if (false !== $result && 0 !== $result) { - app('log')->debug(sprintf('"%s" is a day/year range.', $date)); + if (0 !== $result) { + Log::debug(sprintf('"%s" is a day/year range.', $date)); return true; } - app('log')->debug(sprintf('"%s" is not a day/year range.', $date)); + Log::debug(sprintf('"%s" is not a day/year range.', $date)); return false; } @@ -386,7 +386,7 @@ class ParseDateString */ private function parseDayYearRange(string $date): array { - app('log')->debug(sprintf('parseDayYearRange: Parsed "%s".', $date)); + Log::debug(sprintf('parseDayYearRange: Parsed "%s".', $date)); $parts = explode('-', $date); return [ @@ -400,12 +400,12 @@ class ParseDateString // if regex for YYYY-MM-xx: $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-xx$/'; $result = preg_match($pattern, $date); - if (false !== $result && 0 !== $result) { - app('log')->debug(sprintf('"%s" is a month/year range.', $date)); + if (0 !== $result) { + Log::debug(sprintf('"%s" is a month/year range.', $date)); return true; } - app('log')->debug(sprintf('"%s" is not a month/year range.', $date)); + Log::debug(sprintf('"%s" is not a month/year range.', $date)); return false; } @@ -415,7 +415,7 @@ class ParseDateString */ protected function parseMonthYearRange(string $date): array { - app('log')->debug(sprintf('parseMonthYearRange: Parsed "%s".', $date)); + Log::debug(sprintf('parseMonthYearRange: Parsed "%s".', $date)); $parts = explode('-', $date); return [ diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 41dbe8f9dd..4a068cae06 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -57,7 +57,7 @@ class Preferences ; } - public function get(string $name, null|array|bool|int|string $default = null): ?Preference + public function get(string $name, array|bool|int|string|null $default = null): ?Preference { /** @var null|User $user */ $user = auth()->user(); @@ -71,7 +71,7 @@ class Preferences return $this->getForUser($user, $name, $default); } - public function getForUser(User $user, string $name, null|array|bool|int|string $default = null): ?Preference + public function getForUser(User $user, string $name, array|bool|int|string|null $default = null): ?Preference { // Log::debug(sprintf('getForUser(#%d, "%s")', $user->id, $name)); // don't care about user group ID, except for some specific preferences. @@ -135,7 +135,7 @@ class Preferences Cache::put($key, '', 5); } - public function setForUser(User $user, string $name, null|array|bool|int|string $value): Preference + public function setForUser(User $user, string $name, array|bool|int|string|null $value): Preference { $fullName = sprintf('preference%s%s', $user->id, $name); $userGroupId = $this->getUserGroupId($user, $name); @@ -240,7 +240,7 @@ class Preferences return $result; } - public function getEncryptedForUser(User $user, string $name, null|array|bool|int|string $default = null): ?Preference + public function getEncryptedForUser(User $user, string $name, array|bool|int|string|null $default = null): ?Preference { $result = $this->getForUser($user, $name, $default); if ('' === $result->data) { @@ -265,7 +265,7 @@ class Preferences return $result; } - public function getFresh(string $name, null|array|bool|int|string $default = null): ?Preference + public function getFresh(string $name, array|bool|int|string|null $default = null): ?Preference { /** @var null|User $user */ $user = auth()->user(); @@ -313,7 +313,7 @@ class Preferences Session::forget('first'); } - public function set(string $name, null|array|bool|int|string $value): Preference + public function set(string $name, array|bool|int|string|null $value): Preference { /** @var null|User $user */ $user = auth()->user(); diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index 3014e97dc4..c478e3cce4 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -188,7 +188,7 @@ class BudgetReportGenerator $limitId = $limit->id; $limitCurrency = $limit->transactionCurrency ?? $this->currency; $currencyId = $limitCurrency->id; - $expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection([$budget])); + $expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection()->push($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'; diff --git a/app/Support/Report/Summarizer/TransactionSummarizer.php b/app/Support/Report/Summarizer/TransactionSummarizer.php index a1ab2abf07..aea25a5663 100644 --- a/app/Support/Report/Summarizer/TransactionSummarizer.php +++ b/app/Support/Report/Summarizer/TransactionSummarizer.php @@ -26,6 +26,7 @@ namespace FireflyIII\Support\Report\Summarizer; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -119,10 +120,10 @@ class TransactionSummarizer ]; if ('positive' === $method) { - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], (string) app('steam')->positive($amount)); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], Steam::positive($amount)); } if ('negative' === $method) { - $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], (string) app('steam')->negative($amount)); + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], Steam::negative($amount)); } // then process foreign amount, if it exists. @@ -138,10 +139,10 @@ class TransactionSummarizer ]; if ('positive' === $method) { - $array[$foreignCurrencyId]['sum'] = bcadd($array[$foreignCurrencyId]['sum'], (string) app('steam')->positive($amount)); + $array[$foreignCurrencyId]['sum'] = bcadd($array[$foreignCurrencyId]['sum'], Steam::positive($amount)); } if ('negative' === $method) { - $array[$foreignCurrencyId]['sum'] = bcadd($array[$foreignCurrencyId]['sum'], (string) app('steam')->negative($amount)); + $array[$foreignCurrencyId]['sum'] = bcadd($array[$foreignCurrencyId]['sum'], Steam::negative($amount)); } } @@ -199,7 +200,7 @@ class TransactionSummarizer ]; // add the data from the $field to the array. - $array[$key]['sum'] = bcadd($array[$key]['sum'], (string) app('steam')->{$method}((string) ($journal[$field] ?? '0'))); // @phpstan-ignore-line + $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string) ($journal[$field] ?? '0'))); // @phpstan-ignore-line Log::debug(sprintf('Field for transaction #%d is "%s" (%s). Sum: %s', $journal['transaction_group_id'], $currencyCode, $field, $array[$key]['sum'])); // also do foreign amount, but only when convertToPrimary is false (otherwise we have it already) @@ -217,7 +218,7 @@ class TransactionSummarizer 'currency_code' => $journal['foreign_currency_code'], 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], ]; - $array[$key]['sum'] = bcadd($array[$key]['sum'], (string) app('steam')->{$method}((string) $journal['foreign_amount'])); // @phpstan-ignore-line + $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string) $journal['foreign_amount'])); // @phpstan-ignore-line } } diff --git a/app/Support/Repositories/Recurring/FiltersWeekends.php b/app/Support/Repositories/Recurring/FiltersWeekends.php index 222879cc29..508e13f638 100644 --- a/app/Support/Repositories/Recurring/FiltersWeekends.php +++ b/app/Support/Repositories/Recurring/FiltersWeekends.php @@ -28,6 +28,7 @@ use Carbon\Carbon; use FireflyIII\Enums\RecurrenceRepetitionWeekend; use FireflyIII\Models\RecurrenceRepetition; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; /** * Trait FiltersWeekends @@ -39,9 +40,9 @@ trait FiltersWeekends */ protected function filterWeekends(RecurrenceRepetition $repetition, array $dates): array { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); if (RecurrenceRepetitionWeekend::WEEKEND_DO_NOTHING->value === $repetition->weekend) { - app('log')->debug('Repetition will not be filtered on weekend days.'); + Log::debug('Repetition will not be filtered on weekend days.'); return $dates; } @@ -53,7 +54,7 @@ trait FiltersWeekends if (!$isWeekend) { $return[] = clone $date; - // app('log')->debug(sprintf('Date is %s, not a weekend date.', $date->format('D d M Y'))); + // Log::debug(sprintf('Date is %s, not a weekend date.', $date->format('D d M Y'))); continue; } @@ -61,7 +62,7 @@ trait FiltersWeekends if (RecurrenceRepetitionWeekend::WEEKEND_TO_FRIDAY->value === $repetition->weekend) { $clone = clone $date; $clone->addDays(5 - $date->dayOfWeekIso); - app('log')->debug( + 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; @@ -73,23 +74,22 @@ trait FiltersWeekends if (RecurrenceRepetitionWeekend::WEEKEND_TO_MONDAY->value === $repetition->weekend) { $clone = clone $date; $clone->addDays(8 - $date->dayOfWeekIso); - app('log')->debug( + 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; } - // app('log')->debug(sprintf('Date is %s, removed from final result', $date->format('D d M Y'))); + // Log::debug(sprintf('Date is %s, removed from final result', $date->format('D d M Y'))); } // filter unique dates - app('log')->debug(sprintf('Count before filtering: %d', count($dates))); - $collection = new Collection($return); + Log::debug(sprintf('Count before filtering: %d', count($dates))); + $collection = new Collection()->push(...$return); $filtered = $collection->unique(); $return = $filtered->toArray(); - app('log')->debug(sprintf('Count after filtering: %d', count($return))); + Log::debug(sprintf('Count after filtering: %d', count($return))); return $return; } diff --git a/app/Support/Repositories/UserGroup/UserGroupInterface.php b/app/Support/Repositories/UserGroup/UserGroupInterface.php index 8a087f4edf..d7a737b919 100644 --- a/app/Support/Repositories/UserGroup/UserGroupInterface.php +++ b/app/Support/Repositories/UserGroup/UserGroupInterface.php @@ -37,7 +37,7 @@ interface UserGroupInterface public function getUserGroup(): ?UserGroup; - public function setUser(null|Authenticatable|User $user): void; + public function setUser(Authenticatable|User|null $user): void; public function setUserGroup(UserGroup $userGroup): void; diff --git a/app/Support/Repositories/UserGroup/UserGroupTrait.php b/app/Support/Repositories/UserGroup/UserGroupTrait.php index 5e5eb6c59f..98781e5596 100644 --- a/app/Support/Repositories/UserGroup/UserGroupTrait.php +++ b/app/Support/Repositories/UserGroup/UserGroupTrait.php @@ -61,7 +61,7 @@ trait UserGroupTrait /** * @throws FireflyException */ - public function setUser(null|Authenticatable|User $user): void + public function setUser(Authenticatable|User|null $user): void { if ($user instanceof User) { $this->user = $user; @@ -72,7 +72,7 @@ trait UserGroupTrait return; } - $class = null === $user ? 'NULL' : $user::class; + $class = $user instanceof Authenticatable ? $user::class : 'NULL'; throw new FireflyException(sprintf('Object is %s, not User.', $class)); } diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index 60e954945a..c90541b081 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -99,6 +99,28 @@ trait ConvertsDataTypes return Steam::filterSpaces($string); } + public function convertSortParameters(string $field, string $class): array + { + // assume this all works, because the validator would have caught any errors. + $parameter = (string)request()->query->get($field); + if ('' === $parameter) { + return []; + } + $parts = explode(',', $parameter); + $sortParameters = []; + foreach ($parts as $part) { + $part = trim($part); + $direction = 'asc'; + if ('-' === $part[0]) { + $part = substr($part, 1); + $direction = 'desc'; + } + $sortParameters[] = [$part, $direction]; + } + + return $sortParameters; + } + public function clearString(?string $string): ?string { $string = $this->clearStringKeepNewlines($string); @@ -129,7 +151,7 @@ trait ConvertsDataTypes // clear zalgo text (TODO also in API v2) $string = preg_replace('/(\pM{2})\pM+/u', '\1', $string); - return trim((string) $string); + return trim($string); } public function convertIban(string $field): string @@ -147,7 +169,7 @@ trait ConvertsDataTypes return $default; } - return (string) $this->clearString((string) $entry); + return (string)$this->clearString((string)$entry); } /** @@ -161,7 +183,7 @@ trait ConvertsDataTypes */ public function convertInteger(string $field): int { - return (int) $this->get($field); + return (int)$this->get($field); } /** @@ -186,7 +208,7 @@ trait ConvertsDataTypes $collection = new Collection(); if (is_array($set)) { foreach ($set as $accountId) { - $account = $repository->find((int) $accountId); + $account = $repository->find((int)$accountId); if (null !== $account) { $collection->push($account); } @@ -201,7 +223,7 @@ trait ConvertsDataTypes */ public function stringWithNewlines(string $field): string { - return (string) $this->clearStringKeepNewlines((string) ($this->get($field) ?? '')); + return (string)$this->clearStringKeepNewlines((string)($this->get($field) ?? '')); } /** @@ -245,17 +267,17 @@ trait ConvertsDataTypes protected function convertDateTime(?string $string): ?Carbon { - $value = $this->get((string) $string); + $value = $this->get((string)$string); if (null === $value) { return null; } if ('' === $value) { return null; } - if (10 === strlen((string) $value)) { + if (10 === strlen((string)$value)) { // probably a date format. try { - $carbon = Carbon::createFromFormat('Y-m-d', $value); + $carbon = Carbon::createFromFormat('Y-m-d', $value, config('app.timezone')); } catch (InvalidDateException $e) { // @phpstan-ignore-line Log::error(sprintf('[1] "%s" is not a valid date: %s', $value, $e->getMessage())); @@ -276,7 +298,7 @@ trait ConvertsDataTypes // is an atom string, I hope? try { - $carbon = Carbon::parse($value); + $carbon = Carbon::parse($value, $value, config('app.timezone')); } catch (InvalidDateException $e) { // @phpstan-ignore-line Log::error(sprintf('[3] "%s" is not a valid date or time: %s', $value, $e->getMessage())); @@ -300,7 +322,7 @@ trait ConvertsDataTypes return null; } - return (float) $res; + return (float)$res; } protected function dateFromValue(?string $string): ?Carbon @@ -338,7 +360,7 @@ trait ConvertsDataTypes return null; } - return (float) $string; + return (float)$string; } /** @@ -375,10 +397,10 @@ trait ConvertsDataTypes { $result = null; - Log::debug(sprintf('Date string is "%s"', (string) $this->get($field))); + Log::debug(sprintf('Date string is "%s"', (string)$this->get($field))); try { - $result = '' !== (string) $this->get($field) ? new Carbon((string) $this->get($field), config('app.timezone')) : null; + $result = '' !== (string)$this->get($field) ? new Carbon((string)$this->get($field), config('app.timezone')) : null; } catch (InvalidFormatException) { // @ignoreException Log::debug(sprintf('Exception when parsing date "%s".', $this->get($field))); @@ -399,12 +421,12 @@ trait ConvertsDataTypes return null; } - $value = (string) $this->get($field); + $value = (string)$this->get($field); if ('' === $value) { return null; } - return (int) $value; + return (int)$value; } protected function parseAccounts(mixed $array): array @@ -419,7 +441,7 @@ trait ConvertsDataTypes } $amount = null; if (array_key_exists('current_amount', $entry)) { - $amount = $this->clearString((string) ($entry['current_amount'] ?? '0')); + $amount = $this->clearString((string)($entry['current_amount'] ?? '0')); if (null === $entry['current_amount']) { $amount = null; } @@ -428,7 +450,7 @@ trait ConvertsDataTypes $amount = null; } $return[] = [ - 'account_id' => $this->integerFromValue((string) ($entry['account_id'] ?? '0')), + 'account_id' => $this->integerFromValue((string)($entry['account_id'] ?? '0')), 'current_amount' => $amount, ]; } @@ -448,6 +470,6 @@ trait ConvertsDataTypes return null; } - return (int) $string; + return (int)$string; } } diff --git a/app/Support/Request/GetFilterInstructions.php b/app/Support/Request/GetFilterInstructions.php deleted file mode 100644 index cd8c68b568..0000000000 --- a/app/Support/Request/GetFilterInstructions.php +++ /dev/null @@ -1,83 +0,0 @@ -get('filters', []); - $result = []; - if (0 === count($set)) { - return []; - } - foreach ($set as $info) { - $column = $info['column'] ?? 'NOPE'; - $filterValue = (string) ($info['filter'] ?? self::INVALID_FILTER); - if (false === in_array($column, $allowed, true)) { - // skip invalid column - continue; - } - $filterType = $config[$column] ?? false; - - switch ($filterType) { - default: - throw new FireflyException(sprintf('Do not support filter type "%s"', $filterType)); - - case 'boolean': - $filterValue = $this->booleanInstruction($filterValue); - - break; - - case 'string': - break; - } - $result[$column] = $filterValue; - } - - return $result; - } - - public function booleanInstruction(string $filterValue): ?bool - { - if ('true' === $filterValue) { - return true; - } - if ('false' === $filterValue) { - return false; - } - - return null; - } -} diff --git a/app/Support/Request/GetSortInstructions.php b/app/Support/Request/GetSortInstructions.php deleted file mode 100644 index 38343b5a15..0000000000 --- a/app/Support/Request/GetSortInstructions.php +++ /dev/null @@ -1,53 +0,0 @@ -get('sorting', []); - $result = []; - if (0 === count($set)) { - return []; - } - foreach ($set as $info) { - $column = $info['column'] ?? 'NOPE'; - $direction = $info['direction'] ?? 'NOPE'; - if ('asc' !== $direction && 'desc' !== $direction) { - // skip invalid direction - continue; - } - if (false === in_array($column, $allowed, true)) { - // skip invalid column - continue; - } - $result[$column] = $direction; - } - - return $result; - } -} diff --git a/app/Support/Request/ValidatesWebhooks.php b/app/Support/Request/ValidatesWebhooks.php new file mode 100644 index 0000000000..5647184ef4 --- /dev/null +++ b/app/Support/Request/ValidatesWebhooks.php @@ -0,0 +1,95 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Request; + +use Illuminate\Validation\Validator; +use FireflyIII\Enums\WebhookTrigger; +use FireflyIII\Models\Webhook; +use Illuminate\Support\Facades\Log; + +trait ValidatesWebhooks +{ + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator): void { + Log::debug('Validating webhook'); + if (count($validator->failed()) > 0) { + return; + } + $data = $validator->getData(); + $triggers = $data['triggers'] ?? []; + $responses = $data['responses'] ?? []; + + if (0 === count($triggers) || 0 === count($responses)) { + Log::debug('No trigger or response, return.'); + + return; + } + $validTriggers = array_values(Webhook::getTriggers()); + $validResponses = array_values(Webhook::getResponses()); + $containsAny = false; + $count = 0; + foreach ($triggers as $trigger) { + if (!in_array($trigger, $validTriggers, true)) { + return; + } + ++$count; + if ($trigger === WebhookTrigger::ANY->name) { + $containsAny = true; + } + } + if ($containsAny && $count > 1) { + $validator->errors()->add('triggers.0', trans('validation.only_any_trigger')); + + return; + } + foreach ($responses as $response) { + if (!in_array($response, $validResponses, true)) { + return; + } + } + // some combinations are illegal. + foreach ($triggers as $i => $trigger) { + $forbidden = config(sprintf('webhooks.forbidden_responses.%s', $trigger)); + if (null === $forbidden) { + $validator->errors()->add(sprintf('triggers.%d', $i), trans('validation.unknown_webhook_trigger', ['trigger' => $trigger])); + + continue; + } + foreach ($responses as $ii => $response) { + if (in_array($response, $forbidden, true)) { + Log::debug(sprintf('Trigger %s and response %s are forbidden.', $trigger, $response)); + $validator->errors()->add(sprintf('responses.%d', $ii), trans('validation.bad_webhook_combination', ['trigger' => $trigger, 'response' => $response])); + + return; + } + } + } + } + ); + } +} diff --git a/app/Support/Search/AccountSearch.php b/app/Support/Search/AccountSearch.php index f374895c65..99b89f9c99 100644 --- a/app/Support/Search/AccountSearch.php +++ b/app/Support/Search/AccountSearch.php @@ -137,7 +137,7 @@ class AccountSearch implements GenericSearchInterface $this->types = $types; } - public function setUser(null|Authenticatable|User $user): void + public function setUser(Authenticatable|User|null $user): void { if ($user instanceof User) { $this->user = $user; diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index 69781b2157..dc975609f0 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -450,7 +450,7 @@ class OperatorQuerySearch implements SearchInterface case 'source_account_id': $account = $this->accountRepository->find((int) $value); if (null !== $account) { - $this->collector->setSourceAccounts(new Collection([$account])); + $this->collector->setSourceAccounts(new Collection()->push($account)); } if (null === $account) { // since the source does not exist, cannot return results: @@ -463,7 +463,7 @@ class OperatorQuerySearch implements SearchInterface case '-source_account_id': $account = $this->accountRepository->find((int) $value); if (null !== $account) { - $this->collector->excludeSourceAccounts(new Collection([$account])); + $this->collector->excludeSourceAccounts(new Collection()->push($account)); } if (null === $account) { // since the source does not exist, cannot return results: @@ -580,7 +580,7 @@ class OperatorQuerySearch implements SearchInterface case 'destination_account_id': $account = $this->accountRepository->find((int) $value); if (null !== $account) { - $this->collector->setDestinationAccounts(new Collection([$account])); + $this->collector->setDestinationAccounts(new Collection()->push($account)); } if (null === $account) { Log::warning('Call to findNothing() because dest account does not exist (destination_account_id).'); @@ -592,7 +592,7 @@ class OperatorQuerySearch implements SearchInterface case '-destination_account_id': $account = $this->accountRepository->find((int) $value); if (null !== $account) { - $this->collector->excludeDestinationAccounts(new Collection([$account])); + $this->collector->excludeDestinationAccounts(new Collection()->push($account)); } if (null === $account) { Log::warning('Call to findNothing() because dest account does not exist (-destination_account_id).'); @@ -652,37 +652,37 @@ class OperatorQuerySearch implements SearchInterface // case 'source_is_cash': $account = $this->getCashAccount(); - $this->collector->setSourceAccounts(new Collection([$account])); + $this->collector->setSourceAccounts(new Collection()->push($account)); break; case '-source_is_cash': $account = $this->getCashAccount(); - $this->collector->excludeSourceAccounts(new Collection([$account])); + $this->collector->excludeSourceAccounts(new Collection()->push($account)); break; case 'destination_is_cash': $account = $this->getCashAccount(); - $this->collector->setDestinationAccounts(new Collection([$account])); + $this->collector->setDestinationAccounts(new Collection()->push($account)); break; case '-destination_is_cash': $account = $this->getCashAccount(); - $this->collector->excludeDestinationAccounts(new Collection([$account])); + $this->collector->excludeDestinationAccounts(new Collection()->push($account)); break; case 'account_is_cash': $account = $this->getCashAccount(); - $this->collector->setAccounts(new Collection([$account])); + $this->collector->setAccounts(new Collection()->push($account)); break; case '-account_is_cash': $account = $this->getCashAccount(); - $this->collector->excludeAccounts(new Collection([$account])); + $this->collector->excludeAccounts(new Collection()->push($account)); break; @@ -1070,7 +1070,7 @@ class OperatorQuerySearch implements SearchInterface case '-bill_is': $bill = $this->billRepository->findByName($value); if (null !== $bill) { - $this->collector->excludeBills(new Collection([$bill])); + $this->collector->excludeBills(new Collection()->push($bill)); break; } diff --git a/app/Support/Singleton/PreferencesSingleton.php b/app/Support/Singleton/PreferencesSingleton.php index e955a0f5ec..32b9bb94f6 100644 --- a/app/Support/Singleton/PreferencesSingleton.php +++ b/app/Support/Singleton/PreferencesSingleton.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Support\Singleton; diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 0d1cca50fa..e103f19ece 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -224,6 +224,7 @@ class Steam */ $request = clone $start; $request->subDay()->endOfDay(); + Log::debug('Get first balance to start.'); Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String())); $startBalance = $this->finalAccountBalance($account, $request); $primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); @@ -284,7 +285,7 @@ class Steam $sumOfDay = $this->floatalize($sumOfDay); // find currency of this entry, does not have to exist. - $currencies[$entry->transaction_currency_id] ??= TransactionCurrency::find($entry->transaction_currency_id); + $currencies[$entry->transaction_currency_id] ??= Amount::getTransactionCurrencyById($entry->transaction_currency_id); // make sure this $entry has its own $entryCurrency /** @var TransactionCurrency $entryCurrency */ @@ -315,7 +316,7 @@ class Steam Log::debug(sprintf('Updated entry [%s]', $carbonKey), $currentBalance); } $cache->store($balances); - Log::debug('End of method'); + Log::debug('End of method finalAccountBalanceInRange'); return $balances; } @@ -347,9 +348,7 @@ class Steam $currency = $currencies[$account->id]; // second array - $accountSum = array_filter($arrayOfSums, function ($entry) use ($account) { - return $entry['account_id'] === $account->id; - }); + $accountSum = array_filter($arrayOfSums, fn ($entry) => $entry['account_id'] === $account->id); if (0 === count($accountSum)) { $result[$account->id] = $return; @@ -501,7 +500,7 @@ class Steam return null; } - return TransactionCurrency::find((int)$result->data); + return Amount::getTransactionCurrencyById((int)$result->data); } private function groupAndSumTransactions(array $array, string $group, string $field): array @@ -523,8 +522,10 @@ class Steam $singleton = PreferencesSingleton::getInstance(); foreach ($others as $key => $amount) { $preference = $singleton->getPreference($key); - $currency = $preference ?? TransactionCurrency::where('code', $key)->first(); - if (null === $currency) { + + try { + $currency = $preference ?? Amount::getTransactionCurrencyByCode($key); + } catch (FireflyException) { continue; } if (null === $preference) { diff --git a/app/Support/Twig/AmountFormat.php b/app/Support/Twig/AmountFormat.php index 3a014e8003..ac6efaa8b7 100644 --- a/app/Support/Twig/AmountFormat.php +++ b/app/Support/Twig/AmountFormat.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Support\Twig; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account as AccountModel; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -52,9 +53,9 @@ class AmountFormat extends AbstractExtension return new TwigFilter( 'formatAmount', static function (string $string): string { - $currency = app('amount')->getPrimaryCurrency(); + $currency = Amount::getPrimaryCurrency(); - return app('amount')->formatAnything($currency, $string, true); + return Amount::formatAnything($currency, $string, true); }, ['is_safe' => ['html']] ); @@ -65,9 +66,9 @@ class AmountFormat extends AbstractExtension return new TwigFilter( 'formatAmountPlain', static function (string $string): string { - $currency = app('amount')->getPrimaryCurrency(); + $currency = Amount::getPrimaryCurrency(); - return app('amount')->formatAnything($currency, $string, false); + return Amount::formatAnything($currency, $string, false); }, ['is_safe' => ['html']] ); @@ -98,9 +99,9 @@ class AmountFormat extends AbstractExtension /** @var AccountRepositoryInterface $accountRepos */ $accountRepos = app(AccountRepositoryInterface::class); - $currency = $accountRepos->getAccountCurrency($account) ?? app('amount')->getPrimaryCurrency(); + $currency = $accountRepos->getAccountCurrency($account) ?? Amount::getPrimaryCurrency(); - return app('amount')->formatAnything($currency, $amount, $coloured); + return Amount::formatAnything($currency, $amount, $coloured); }, ['is_safe' => ['html']] ); @@ -113,14 +114,22 @@ class AmountFormat extends AbstractExtension { return new TwigFunction( 'formatAmountBySymbol', - static function (string $amount, string $symbol, ?int $decimalPlaces = null, ?bool $coloured = null): string { + static function (string $amount, ?string $symbol, ?int $decimalPlaces = null, ?bool $coloured = null): string { + + if (null === $symbol) { + $message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true)); + Log::error($message); + + throw new FireflyException($message); + } + $decimalPlaces ??= 2; $coloured ??= true; $currency = new TransactionCurrency(); $currency->symbol = $symbol; $currency->decimal_places = $decimalPlaces; - return app('amount')->formatAnything($currency, $amount, $coloured); + return Amount::formatAnything($currency, $amount, $coloured); }, ['is_safe' => ['html']] ); @@ -136,7 +145,7 @@ class AmountFormat extends AbstractExtension static function (TransactionCurrency $currency, string $amount, ?bool $coloured = null): string { $coloured ??= true; - return app('amount')->formatAnything($currency, $amount, $coloured); + return Amount::formatAnything($currency, $amount, $coloured); }, ['is_safe' => ['html']] ); @@ -153,15 +162,15 @@ class AmountFormat extends AbstractExtension static function (string $amount, string $code, ?bool $coloured = null): string { $coloured ??= true; - /** @var null|TransactionCurrency $currency */ - $currency = TransactionCurrency::whereCode($code)->first(); - if (null === $currency) { + try { + $currency = Amount::getTransactionCurrencyByCode($code); + } catch (FireflyException) { Log::error(sprintf('Could not find currency with code "%s". Fallback to primary currency.', $code)); $currency = Amount::getPrimaryCurrency(); Log::error(sprintf('Fallback currency is "%s".', $currency->code)); } - return app('amount')->formatAnything($currency, $amount, $coloured); + return Amount::formatAnything($currency, $amount, $coloured); }, ['is_safe' => ['html']] ); diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index b2c4fc9bcb..6f71d6f578 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -25,19 +25,18 @@ namespace FireflyIII\Support\Twig; use Carbon\Carbon; use FireflyIII\Models\Account; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Search\OperatorQuerySearch; use Illuminate\Support\Facades\Log; -use League\CommonMark\GithubFlavoredMarkdownConverter; use Illuminate\Support\Facades\Route; +use League\CommonMark\GithubFlavoredMarkdownConverter; +use Override; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; -use Override; use function Safe\parse_url; @@ -99,7 +98,7 @@ class General extends AbstractExtension } // for multi currency accounts. if ($usePrimary && $key !== $primary->code) { - $strings[] = app('amount')->formatAnything(TransactionCurrency::where('code', $key)->first(), $balance, false); + $strings[] = app('amount')->formatAnything(Amount::getTransactionCurrencyByCode($key), $balance, false); } } @@ -143,7 +142,7 @@ class General extends AbstractExtension 'mimeIcon', static fn (string $string): string => match ($string) { 'application/pdf' => 'fa-file-pdf-o', - 'image/png', 'image/jpeg', 'image/svg+xml', 'image/heic', 'image/heic-sequence', 'application/vnd.oasis.opendocument.image' => 'fa-file-image-o', + 'image/webp', 'image/png', 'image/jpeg', 'image/svg+xml', 'image/heic', 'image/heic-sequence', 'application/vnd.oasis.opendocument.image' => 'fa-file-image-o', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/x-iwork-pages-sffpages', 'application/vnd.sun.xml.writer', 'application/vnd.sun.xml.writer.template', 'application/vnd.sun.xml.writer.global', 'application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.text-template', 'application/vnd.oasis.opendocument.text-web', 'application/vnd.oasis.opendocument.text-master' => 'fa-file-word-o', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'application/vnd.sun.xml.calc', 'application/vnd.sun.xml.calc.template', 'application/vnd.stardivision.calc', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.spreadsheet-template' => 'fa-file-excel-o', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.openxmlformats-officedocument.presentationml.template', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'application/vnd.sun.xml.impress', 'application/vnd.sun.xml.impress.template', 'application/vnd.stardivision.impress', 'application/vnd.oasis.opendocument.presentation', 'application/vnd.oasis.opendocument.presentation-template' => 'fa-file-powerpoint-o', @@ -168,7 +167,7 @@ class General extends AbstractExtension ] ); - return (string) $converter->convert($text); + return (string)$converter->convert($text); }, ['is_safe' => ['html']] ); @@ -182,8 +181,14 @@ class General extends AbstractExtension return new TwigFilter( 'phphost', static function (string $string): string { - $proto = (string) parse_url($string, PHP_URL_SCHEME); - $host = (string) parse_url($string, PHP_URL_HOST); + $proto = parse_url($string, PHP_URL_SCHEME); + $host = parse_url($string, PHP_URL_HOST); + if (is_array($host)) { + $host = implode(' ', $host); + } + if (is_array($proto)) { + $proto = implode(' ', $proto); + } return e(sprintf('%s://%s', $proto, $host)); } @@ -274,8 +279,8 @@ class General extends AbstractExtension if ($objectType === $activeObjectType && false !== stripos( - (string) Route::getCurrentRoute()->getName(), - (string) $route + (string)Route::getCurrentRoute()->getName(), + (string)$route )) { return 'active'; } diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index f8f8700a7e..c4b4822ba4 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -317,7 +317,7 @@ class SearchRuleEngine implements RuleEngineInterface Log::debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count())); /** @var Rule $rule */ - foreach ($this->rules as $rule) { + foreach ($this->rules as $rule) { // @phpstan-ignore-line $result = $this->fireRule($rule); if (true === $result && true === $rule->stop_processing) { Log::debug(sprintf('Rule #%d has triggered and executed, but calls to stop processing. Since not in the context of a group, do not stop.', $rule->id)); @@ -335,7 +335,7 @@ class SearchRuleEngine implements RuleEngineInterface // fire each group: /** @var RuleGroup $group */ - foreach ($this->groups as $group) { + foreach ($this->groups as $group) { // @phpstan-ignore-line $this->fireGroup($group); } } diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index 1f8ff4dadc..c9e5d3aeee 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -66,22 +66,18 @@ class AccountTransformer extends AbstractTransformer } // get account type: - $accountType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $account->full_account_type)); - $liabilityType = (string) config(sprintf('firefly.shortLiabilityNameByFullName.%s', $account->full_account_type)); + $accountType = (string)config(sprintf('firefly.shortNamesByFullName.%s', $account->full_account_type)); + $liabilityType = (string)config(sprintf('firefly.shortLiabilityNameByFullName.%s', $account->full_account_type)); $liabilityType = '' === $liabilityType ? null : strtolower($liabilityType); $liabilityDirection = $account->meta['liability_direction'] ?? null; $accountRole = $this->getAccountRole($account, $accountType); $hasCurrencySettings = null !== $account->meta['currency']; - $includeNetWorth = 1 === (int) ($account->meta['include_net_worth'] ?? 0); + $includeNetWorth = 1 === (int)($account->meta['include_net_worth'] ?? 0); $longitude = $account->meta['location']['longitude'] ?? null; $latitude = $account->meta['location']['latitude'] ?? null; $zoomLevel = $account->meta['location']['zoom_level'] ?? null; $order = $account->order; - // date (for balance etc.) - $date = $this->getDate(); - $date->endOfDay(); - // get primary currency as fallback: $currency = $this->primary; // assume primary currency if ($hasCurrencySettings) { @@ -99,7 +95,7 @@ class AccountTransformer extends AbstractTransformer [$interest, $interestPeriod] = $this->getInterest($account, $accountType); return [ - 'id' => (string) $account->id, + 'id' => (string)$account->id, 'created_at' => $account->created_at->toAtomString(), 'updated_at' => $account->updated_at->toAtomString(), 'active' => $account->active, @@ -116,13 +112,13 @@ class AccountTransformer extends AbstractTransformer 'object_has_currency_setting' => $hasCurrencySettings, // currency is object specific or primary, already determined above. - 'currency_id' => (string) $currency['id'], + 'currency_id' => (string)$currency['id'], 'currency_name' => $currency['name'], 'currency_code' => $currency['code'], 'currency_symbol' => $currency['symbol'], 'currency_decimal_places' => $currency['decimal_places'], - 'primary_currency_id' => (string) $this->primary->id, + 'primary_currency_id' => (string)$this->primary->id, 'primary_currency_name' => $this->primary->name, 'primary_currency_code' => $this->primary->code, 'primary_currency_symbol' => $this->primary->symbol, @@ -141,7 +137,10 @@ class AccountTransformer extends AbstractTransformer 'debt_amount' => $account->meta['balances']['debt_amount'], 'pc_debt_amount' => $account->meta['balances']['pc_debt_amount'], - 'current_balance_date' => $date->toAtomString(), + 'balance_difference' => $account->meta['balances']['balance_difference'], + 'pc_balance_difference' => $account->meta['balances']['pc_balance_difference'], + + 'current_balance_date' => $account->meta['current_balance_date']->toAtomString(), 'notes' => $account->meta['notes'] ?? null, 'monthly_payment_date' => $monthlyPaymentDate, 'credit_card_type' => $creditCardType, @@ -170,25 +169,13 @@ class AccountTransformer extends AbstractTransformer private function getAccountRole(Account $account, string $accountType): ?string { $accountRole = $account->meta['account_role'] ?? null; - if ('asset' !== $accountType || '' === (string) $accountRole) { + if ('asset' !== $accountType || '' === (string)$accountRole) { return null; } return $accountRole; } - /** - * TODO duplicated in the V2 transformer. - */ - private function getDate(): Carbon - { - if (null !== $this->parameters->get('date')) { - return $this->parameters->get('date'); - } - - return today(config('app.timezone')); - } - private function getCCInfo(Account $account, ?string $accountRole, string $accountType): array { $monthlyPaymentDate = null; @@ -206,7 +193,7 @@ class AccountTransformer extends AbstractTransformer } $monthlyPaymentDate = $object->toAtomString(); } - if (10 !== strlen((string) $monthlyPaymentDate)) { + if (10 !== strlen((string)$monthlyPaymentDate)) { $monthlyPaymentDate = Carbon::parse($monthlyPaymentDate, config('app.timezone'))->toAtomString(); } } diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index 0b3b9205ff..10c3379441 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -34,7 +34,7 @@ use FireflyIII\Support\Facades\Amount; */ class PiggyBankTransformer extends AbstractTransformer { - private TransactionCurrency $primaryCurrency; + private readonly TransactionCurrency $primaryCurrency; /** * PiggyBankTransformer constructor. @@ -53,8 +53,8 @@ class PiggyBankTransformer extends AbstractTransformer { // Amounts, depending on 0.0 state of target amount $percentage = null; - if (null !== $piggyBank->meta['target_amount'] && 0 !== bccomp($piggyBank->meta['current_amount'], '0')) { // target amount is not 0.00 - $percentage = (int)bcmul(bcdiv($piggyBank->meta['current_amount'], $piggyBank->meta['target_amount']), '100'); + if (null !== $piggyBank->meta['target_amount'] && 0 !== bccomp((string) $piggyBank->meta['current_amount'], '0')) { // target amount is not 0.00 + $percentage = (int)bcmul(bcdiv((string) $piggyBank->meta['current_amount'], $piggyBank->meta['target_amount']), '100'); } $startDate = $piggyBank->start_date?->toAtomString(); $targetDate = $piggyBank->target_date?->toAtomString(); @@ -107,20 +107,4 @@ class PiggyBankTransformer extends AbstractTransformer ], ]; } - - private function renderAccounts(PiggyBank $piggyBank): array - { - $return = []; - foreach ($piggyBank->accounts()->get() as $account) { - $return[] = [ - 'id' => (string)$account->id, - 'name' => $account->name, - 'current_amount' => (string)$account->pivot->current_amount, - 'pc_current_amount' => (string)$account->pivot->native_current_amount, - // TODO add balance, add left to save. - ]; - } - - return $return; - } } diff --git a/app/Transformers/WebhookTransformer.php b/app/Transformers/WebhookTransformer.php index d913b38f3d..d402cd7852 100644 --- a/app/Transformers/WebhookTransformer.php +++ b/app/Transformers/WebhookTransformer.php @@ -24,9 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Transformers; -use FireflyIII\Enums\WebhookDelivery; -use FireflyIII\Enums\WebhookResponse; -use FireflyIII\Enums\WebhookTrigger; use FireflyIII\Models\Webhook; /** @@ -51,9 +48,9 @@ class WebhookTransformer extends AbstractTransformer 'active' => $webhook->active, 'title' => $webhook->title, 'secret' => $webhook->secret, - 'trigger' => $this->getEnum('trigger', $webhook->trigger), - 'response' => $this->getEnum('response', $webhook->response), - 'delivery' => $this->getEnum('delivery', $webhook->delivery), + 'triggers' => $webhook->meta['triggers'], + 'deliveries' => $webhook->meta['deliveries'], + 'responses' => $webhook->meta['responses'], 'url' => $webhook->url, 'links' => [ [ @@ -63,16 +60,4 @@ class WebhookTransformer extends AbstractTransformer ], ]; } - - private function getEnum(string $type, int $value): string - { - if ('trigger' === $type) { - return WebhookTrigger::from($value)->name; - } - if ('response' === $type) { - return WebhookResponse::from($value)->name; - } - - return WebhookDelivery::from($value)->name; - } } diff --git a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php index 89ea275b39..a218f323ee 100644 --- a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php +++ b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Validation\Api\Data\Bulk; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use function Safe\json_decode; diff --git a/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php b/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php index 1967857616..581e0da61c 100644 --- a/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php +++ b/app/Validation/AutoBudget/ValidatesAutoBudgetRequest.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Validation\AutoBudget; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; /** * Trait ValidatesAutoBudgetRequest diff --git a/app/Validation/CurrencyValidation.php b/app/Validation/CurrencyValidation.php index bc4711ba9e..2bdc1357d1 100644 --- a/app/Validation/CurrencyValidation.php +++ b/app/Validation/CurrencyValidation.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use Illuminate\Support\Facades\Log; /** diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index c24fcd54ea..bb9bbd3198 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -118,7 +118,7 @@ class FireflyValidator extends Validator } $regex = '/^[a-z]{6}[0-9a-z]{2}([0-9a-z]{3})?\z/i'; $result = preg_match($regex, $value); - if (false === $result || 0 === $result) { + if (0 === $result) { return false; } @@ -207,7 +207,7 @@ class FireflyValidator extends Validator $value = strtoupper($value); // replace characters outside of ASCI range. - $value = (string) iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value); + $value = 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 = ['', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35']; diff --git a/app/Validation/GroupValidation.php b/app/Validation/GroupValidation.php index 7a8176f7ec..bf4801856f 100644 --- a/app/Validation/GroupValidation.php +++ b/app/Validation/GroupValidation.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionGroup; diff --git a/app/Validation/RecurrenceValidation.php b/app/Validation/RecurrenceValidation.php index 9ec2ccd3ab..2ba6e58a95 100644 --- a/app/Validation/RecurrenceValidation.php +++ b/app/Validation/RecurrenceValidation.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use Carbon\Carbon; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceTransaction; diff --git a/app/Validation/TransactionValidation.php b/app/Validation/TransactionValidation.php index 82f9cbdb0d..944b9372d2 100644 --- a/app/Validation/TransactionValidation.php +++ b/app/Validation/TransactionValidation.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Validation; -use Illuminate\Contracts\Validation\Validator; +use Illuminate\Validation\Validator; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Exceptions\FireflyException; @@ -296,7 +296,7 @@ trait TransactionValidation return true; } - return (bool) $this->isAsset($account); + return $this->isAsset($account); } private function isLiability(Account $account): bool diff --git a/bootstrap/app.php b/bootstrap/app.php index 9c9c5c85b1..f384f4ba78 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -25,6 +25,7 @@ use Illuminate\Foundation\Application; use Illuminate\Contracts\Http\Kernel; use Illuminate\Contracts\Debug\ExceptionHandler; use FireflyIII\Exceptions\Handler; +use function Safe\realpath; /* |-------------------------------------------------------------------------- @@ -71,7 +72,7 @@ if (!function_exists('stringIsEqual')) { } $app = new Application( - (string)realpath(__DIR__ . '/../') + realpath(__DIR__ . '/../') ); /* diff --git a/changelog.md b/changelog.md index 556ade8e27..b37af58197 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,47 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 6.4.0 - 2025-09-19 + +### Added + +- [Issue 5532](https://github.com/firefly-iii/firefly-iii/issues/5532) (Asset prices and exchange rates) reported by @svozniuk +- [Discussion 10725](https://github.com/orgs/firefly-iii/discussions/10725) (New webhook triggers) started by @Billos. See the [documentation](https://docs.firefly-iii.org/how-to/firefly-iii/features/webhooks/). + +### Fixed + +- [Issue 10790](https://github.com/firefly-iii/firefly-iii/issues/10790) (Undefined variable $occurrences) reported by @senna1992 +- [Issue 10791](https://github.com/firefly-iii/firefly-iii/issues/10791) (Clone and edit a transaction with a different currency doesn't clear the foreign transaction amount) reported by @jxtxzzw +- [Issue 10794](https://github.com/firefly-iii/firefly-iii/issues/10794) (Error with recurring transaction) reported by @MaximSN +- [Issue 10799](https://github.com/firefly-iii/firefly-iii/issues/10799) (Budget - "Left (per day)" not showing the correct value) reported by @GensHaze +- [Issue 10802](https://github.com/firefly-iii/firefly-iii/issues/10802) (Crash when trying to update a budget limit) reported by @Billos +- [Issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803) (Issue in /v1/budget-limits spent attribute) reported by @Billos +- [Issue 10808](https://github.com/firefly-iii/firefly-iii/issues/10808) (cron job Error: Undefined variable $preference) reported by @MexerSam +- [Issue 10813](https://github.com/firefly-iii/firefly-iii/issues/10813) (Error "Argument #2 ($symbol) must be of type string" while try open subscriptions section) reported by @mrResident +- [Issue 10814](https://github.com/firefly-iii/firefly-iii/issues/10814) (Deposit show negative amounts & red numbers in detail view) reported by @dreautall +- [Issue 10819](https://github.com/firefly-iii/firefly-iii/issues/10819) (Internal Server Error when trying to open piggy banks) reported by @noantiq +- [Issue 10820](https://github.com/firefly-iii/firefly-iii/issues/10820) (Unable to search date 1970-01-01 to apply rule.) reported by @Kage1 +- [Issue 10824](https://github.com/firefly-iii/firefly-iii/issues/10824) (Converting withdrawal to transfer to account in different currency doesn't allow setting correct currencies) reported by @avee87 +- [Issue 10833](https://github.com/firefly-iii/firefly-iii/issues/10833) (Can't open transaction after assigning a tag to it) reported by @zynexiz +- [Issue 10837](https://github.com/firefly-iii/firefly-iii/issues/10837) (Internal Server Erorr when viewing or editing Recurring transaction) reported by @Tommy78649 +- [Discussion 10846](https://github.com/orgs/firefly-iii/discussions/10846) (Add support for WebP format in attachments) started by @Idestius +- [Issue 10853](https://github.com/firefly-iii/firefly-iii/issues/10853) (Failed to Send Discord Notification) reported by @pimonteiro +- [Issue 10854](https://github.com/firefly-iii/firefly-iii/issues/10854) (string / null in budget causes budget page to not render) reported by @4e868df3 +- [Issue 10871](https://github.com/firefly-iii/firefly-iii/issues/10871) (Transactions disappear after deleting attachment) reported by @dvmfa90 +- [Discussion 10883](https://github.com/orgs/firefly-iii/discussions/10883) (Initial balance for account is missing from exported CSV) started by @ajaskiewiczpl +- [Issue 10888](https://github.com/firefly-iii/firefly-iii/issues/10888) (Recurring transation forgets repetition type, skip and weekend action) reported by @mansellrace +- [Discussion 10891](https://github.com/orgs/firefly-iii/discussions/10891) (User group id is null when downloading new exchange rates) started by @dakennguyen +- [Issue 10898](https://github.com/firefly-iii/firefly-iii/issues/10898) (Paperclip icon appears for transactions without attachments in Tag view) reported by @empeig + +### API + +- [Issue 8345](https://github.com/firefly-iii/firefly-iii/issues/8345) (API: Distinguish spent & earned at `/v2/chart/category/dashboard` (or future `v2/categories`)) reported by @dreautall +- [Issue 10804](https://github.com/firefly-iii/firefly-iii/issues/10804) (No notes information included in the "List all accounts" API call) reported by @gpampuro +- [Issue 10806](https://github.com/firefly-iii/firefly-iii/issues/10806) (API: `/v1/chart/balance/balance` has undocumented `period` parameter) reported by @dreautall +- [Issue 10807](https://github.com/firefly-iii/firefly-iii/issues/10807) (API: `/v1/bills` field `object_group_id` returns int, should be string) reported by @dreautall +- [Issue 10815](https://github.com/firefly-iii/firefly-iii/issues/10815) (API: `/v1/accounts` balance is off by a day) reported by @dreautall +- [Issue 10827](https://github.com/firefly-iii/firefly-iii/issues/10827) (Trigger Recurrence by API) reported by @MexerSam + ## 6.3.2 - 2025-08-20 ### Fixed diff --git a/composer.lock b/composer.lock index 0d01a6e935..50beadca73 100644 --- a/composer.lock +++ b/composer.lock @@ -129,25 +129,25 @@ }, { "name": "brick/math", - "version": "0.13.1", + "version": "0.14.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", + "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -177,7 +177,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.13.1" + "source": "https://github.com/brick/math/tree/0.14.0" }, "funding": [ { @@ -185,7 +185,7 @@ "type": "github" } ], - "time": "2025-03-29T13:50:30+00:00" + "time": "2025-08-29T12:40:03+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -1244,22 +1244,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.3", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1350,7 +1350,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -1366,20 +1366,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:37:11+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -1387,7 +1387,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -1433,7 +1433,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.2.0" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -1449,20 +1449,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:27:01+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -1478,7 +1478,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1549,7 +1549,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -1565,20 +1565,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:30:47+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.4", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2" + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", "shasum": "" }, "require": { @@ -1587,7 +1587,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1635,7 +1635,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.4" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" }, "funding": [ { @@ -1651,7 +1651,7 @@ "type": "tidelift" } ], - "time": "2025-02-03T10:55:03+00:00" + "time": "2025-08-22T14:27:06+00:00" }, { "name": "jc5/google2fa-laravel", @@ -1811,16 +1811,16 @@ }, { "name": "laravel-notification-channels/pushover", - "version": "4.1.1", + "version": "4.1.2", "source": { "type": "git", "url": "https://github.com/laravel-notification-channels/pushover.git", - "reference": "68697f85a01c5f10168ad0ab05b14ef3e244cec4" + "reference": "53be939273e79e832a417d5863c1d443f0b3c665" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel-notification-channels/pushover/zipball/68697f85a01c5f10168ad0ab05b14ef3e244cec4", - "reference": "68697f85a01c5f10168ad0ab05b14ef3e244cec4", + "url": "https://api.github.com/repos/laravel-notification-channels/pushover/zipball/53be939273e79e832a417d5863c1d443f0b3c665", + "reference": "53be939273e79e832a417d5863c1d443f0b3c665", "shasum": "" }, "require": { @@ -1872,26 +1872,26 @@ "homepage": "https://github.com/laravel-notification-channels/pushover", "support": { "issues": "https://github.com/laravel-notification-channels/pushover/issues", - "source": "https://github.com/laravel-notification-channels/pushover/tree/4.1.1" + "source": "https://github.com/laravel-notification-channels/pushover/tree/4.1.2" }, - "time": "2025-03-01T09:05:11+00:00" + "time": "2025-09-09T09:14:17+00:00" }, { "name": "laravel/framework", - "version": "v12.25.0", + "version": "v12.28.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20" + "reference": "868c1f2d3dba4df6d21e3a8d818479f094cfd942" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20", - "reference": "2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20", + "url": "https://api.github.com/repos/laravel/framework/zipball/868c1f2d3dba4df6d21e3a8d818479f094cfd942", + "reference": "868c1f2d3dba4df6d21e3a8d818479f094cfd942", "shasum": "" }, "require": { - "brick/math": "^0.11|^0.12|^0.13", + "brick/math": "^0.11|^0.12|^0.13|^0.14", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", @@ -1927,9 +1927,9 @@ "symfony/http-kernel": "^7.2.0", "symfony/mailer": "^7.2.0", "symfony/mime": "^7.2.0", - "symfony/polyfill-php83": "^1.31", - "symfony/polyfill-php84": "^1.31", - "symfony/polyfill-php85": "^1.31", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33", "symfony/process": "^7.2.0", "symfony/routing": "^7.2.0", "symfony/uid": "^7.2.0", @@ -1965,6 +1965,7 @@ "illuminate/filesystem": "self.version", "illuminate/hashing": "self.version", "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", "illuminate/log": "self.version", "illuminate/macroable": "self.version", "illuminate/mail": "self.version", @@ -1997,7 +1998,8 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^10.6.0", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^10.6.5", "pda/pheanstalk": "^5.0.6|^7.0.0", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", @@ -2091,7 +2093,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-08-18T22:20:52+00:00" + "time": "2025-09-04T14:58:12+00:00" }, { "name": "laravel/passport", @@ -2809,16 +2811,16 @@ }, { "name": "league/csv", - "version": "9.24.1", + "version": "9.25.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "e0221a3f16aa2a823047d59fab5809d552e29bc8" + "reference": "f856f532866369fb1debe4e7c5a1db185f40ef86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/e0221a3f16aa2a823047d59fab5809d552e29bc8", - "reference": "e0221a3f16aa2a823047d59fab5809d552e29bc8", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/f856f532866369fb1debe4e7c5a1db185f40ef86", + "reference": "f856f532866369fb1debe4e7c5a1db185f40ef86", "shasum": "" }, "require": { @@ -2834,7 +2836,7 @@ "phpstan/phpstan-deprecation-rules": "^1.2.1", "phpstan/phpstan-phpunit": "^1.4.2", "phpstan/phpstan-strict-rules": "^1.6.2", - "phpunit/phpunit": "^10.5.16 || ^11.5.22", + "phpunit/phpunit": "^10.5.16 || ^11.5.22 || ^12.3.6", "symfony/var-dumper": "^6.4.8 || ^7.3.0" }, "suggest": { @@ -2896,7 +2898,7 @@ "type": "github" } ], - "time": "2025-06-25T14:53:51+00:00" + "time": "2025-09-11T08:29:08+00:00" }, { "name": "league/event", @@ -3712,16 +3714,16 @@ }, { "name": "nesbot/carbon", - "version": "3.10.2", + "version": "3.10.3", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24" + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", - "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", "shasum": "" }, "require": { @@ -3739,13 +3741,13 @@ "require-dev": { "doctrine/dbal": "^3.6.3 || ^4.0", "doctrine/orm": "^2.15.2 || ^3.0", - "friendsofphp/php-cs-fixer": "^3.75.0", + "friendsofphp/php-cs-fixer": "^v3.87.1", "kylekatarnls/multi-tester": "^2.5.3", "phpmd/phpmd": "^2.15.0", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.17", - "phpunit/phpunit": "^10.5.46", - "squizlabs/php_codesniffer": "^3.13.0" + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4" }, "bin": [ "bin/carbon" @@ -3813,7 +3815,7 @@ "type": "tidelift" } ], - "time": "2025-08-02T09:36:06+00:00" + "time": "2025-09-06T13:39:36+00:00" }, { "name": "nette/schema", @@ -4734,16 +4736,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", "shasum": "" }, "require": { @@ -4751,7 +4753,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -4793,7 +4795,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" }, "funding": [ { @@ -4805,7 +4807,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-08-21T11:53:16+00:00" }, { "name": "phpseclib/phpseclib", @@ -5749,20 +5751,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.0", + "version": "4.9.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", - "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -5821,29 +5823,29 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.0" + "source": "https://github.com/ramsey/uuid/tree/4.9.1" }, - "time": "2025-06-25T14:20:11+00:00" + "time": "2025-09-04T20:59:21+00:00" }, { "name": "rcrowe/twigbridge", - "version": "v0.14.5", + "version": "v0.14.6", "source": { "type": "git", "url": "https://github.com/rcrowe/TwigBridge.git", - "reference": "88c83c9658a2c029c64ec80dd8a15d5a67433ac4" + "reference": "0798ee4b5e5b943d0200850acaa87ccd82e2fe45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/88c83c9658a2c029c64ec80dd8a15d5a67433ac4", - "reference": "88c83c9658a2c029c64ec80dd8a15d5a67433ac4", + "url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/0798ee4b5e5b943d0200850acaa87ccd82e2fe45", + "reference": "0798ee4b5e5b943d0200850acaa87ccd82e2fe45", "shasum": "" }, "require": { "illuminate/support": "^9|^10|^11|^12", "illuminate/view": "^9|^10|^11|^12", "php": "^8.1", - "twig/twig": "~3.12" + "twig/twig": "~3.21" }, "require-dev": { "ext-json": "*", @@ -5893,22 +5895,22 @@ ], "support": { "issues": "https://github.com/rcrowe/TwigBridge/issues", - "source": "https://github.com/rcrowe/TwigBridge/tree/v0.14.5" + "source": "https://github.com/rcrowe/TwigBridge/tree/v0.14.6" }, - "time": "2025-04-18T18:48:57+00:00" + "time": "2025-08-20T11:25:49+00:00" }, { "name": "spatie/backtrace", - "version": "1.7.4", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "cd37a49fce7137359ac30ecc44ef3e16404cccbe" + "reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/cd37a49fce7137359ac30ecc44ef3e16404cccbe", - "reference": "cd37a49fce7137359ac30ecc44ef3e16404cccbe", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/8c0f16a59ae35ec8c62d85c3c17585158f430110", + "reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110", "shasum": "" }, "require": { @@ -5946,7 +5948,8 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/backtrace/tree/1.7.4" + "issues": "https://github.com/spatie/backtrace/issues", + "source": "https://github.com/spatie/backtrace/tree/1.8.1" }, "funding": [ { @@ -5958,7 +5961,7 @@ "type": "other" } ], - "time": "2025-05-08T15:41:09+00:00" + "time": "2025-08-26T08:22:30+00:00" }, { "name": "spatie/error-solutions", @@ -6663,16 +6666,16 @@ }, { "name": "symfony/console", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", "shasum": "" }, "require": { @@ -6737,7 +6740,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.2" + "source": "https://github.com/symfony/console/tree/v7.3.3" }, "funding": [ { @@ -6757,7 +6760,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "symfony/css-selector", @@ -6974,16 +6977,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", "shasum": "" }, "require": { @@ -7034,7 +7037,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/v7.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" }, "funding": [ { @@ -7045,12 +7048,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-22T09:11:45+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -7266,16 +7273,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "1c064a0c67749923483216b081066642751cc2c7" + "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7", - "reference": "1c064a0c67749923483216b081066642751cc2c7", + "url": "https://api.github.com/repos/symfony/http-client/zipball/333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", + "reference": "333b9bd7639cbdaecd25a3a48a9d2dcfaa86e019", "shasum": "" }, "require": { @@ -7283,6 +7290,7 @@ "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/polyfill-php83": "^1.29", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -7341,7 +7349,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.2" + "source": "https://github.com/symfony/http-client/tree/v7.3.3" }, "funding": [ { @@ -7361,7 +7369,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-27T07:45:05+00:00" }, { "name": "symfony/http-client-contracts", @@ -7443,16 +7451,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6" + "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6877c122b3a6cc3695849622720054f6e6fa5fa6", - "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00", + "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00", "shasum": "" }, "require": { @@ -7502,7 +7510,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.2" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.3" }, "funding": [ { @@ -7522,20 +7530,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-20T08:04:18+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c" + "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6ecc895559ec0097e221ed2fd5eb44d5fede083c", - "reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/72c304de37e1a1cec6d5d12b81187ebd4850a17b", + "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b", "shasum": "" }, "require": { @@ -7620,7 +7628,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/v7.3.2" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.3" }, "funding": [ { @@ -7640,20 +7648,20 @@ "type": "tidelift" } ], - "time": "2025-07-31T10:45:04+00:00" + "time": "2025-08-29T08:23:45+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b" + "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/d43e84d9522345f96ad6283d5dfccc8c1cfc299b", - "reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b", + "url": "https://api.github.com/repos/symfony/mailer/zipball/a32f3f45f1990db8c4341d5122a7d3a381c7e575", + "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575", "shasum": "" }, "require": { @@ -7704,7 +7712,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.2" + "source": "https://github.com/symfony/mailer/tree/v7.3.3" }, "funding": [ { @@ -7724,7 +7732,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/mailgun-mailer", @@ -7885,16 +7893,16 @@ }, { "name": "symfony/options-resolver", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", - "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", "shasum": "" }, "require": { @@ -7932,7 +7940,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" }, "funding": [ { @@ -7952,11 +7960,11 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:36:08+00:00" + "time": "2025-08-05T10:16:07+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -8015,7 +8023,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -8026,6 +8034,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8035,16 +8047,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -8093,7 +8105,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -8104,16 +8116,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -8176,7 +8192,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -8187,6 +8203,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8196,7 +8216,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -8257,7 +8277,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -8268,6 +8288,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8277,7 +8301,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -8338,7 +8362,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -8349,6 +8373,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8358,7 +8386,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -8418,7 +8446,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -8429,6 +8457,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8438,16 +8470,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -8494,7 +8526,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -8505,25 +8537,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "000df7860439609837bbe28670b0be15783b7fbf" + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/000df7860439609837bbe28670b0be15783b7fbf", - "reference": "000df7860439609837bbe28670b0be15783b7fbf", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { @@ -8570,7 +8606,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -8581,25 +8617,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-20T12:04:08+00:00" + "time": "2025-06-24T13:30:11+00:00" }, { "name": "symfony/polyfill-php85", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "6fedf31ce4e3648f4ff5ca58bfd53127d38f05fd" + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/6fedf31ce4e3648f4ff5ca58bfd53127d38f05fd", - "reference": "6fedf31ce4e3648f4ff5ca58bfd53127d38f05fd", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", "shasum": "" }, "require": { @@ -8646,7 +8686,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" }, "funding": [ { @@ -8657,16 +8697,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-02T08:40:52+00:00" + "time": "2025-06-23T16:12:55+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -8725,7 +8769,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -8736,6 +8780,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8745,16 +8793,16 @@ }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", "shasum": "" }, "require": { @@ -8786,7 +8834,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v7.3.3" }, "funding": [ { @@ -8797,12 +8845,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-17T09:11:12+00:00" + "time": "2025-08-18T09:42:54+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -9057,16 +9109,16 @@ }, { "name": "symfony/string", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", + "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", "shasum": "" }, "require": { @@ -9124,7 +9176,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.2" + "source": "https://github.com/symfony/string/tree/v7.3.3" }, "funding": [ { @@ -9144,20 +9196,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "symfony/translation", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90" + "reference": "e0837b4cbcef63c754d89a4806575cada743a38d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/81b48f4daa96272efcce9c7a6c4b58e629df3c90", - "reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90", + "url": "https://api.github.com/repos/symfony/translation/zipball/e0837b4cbcef63c754d89a4806575cada743a38d", + "reference": "e0837b4cbcef63c754d89a4806575cada743a38d", "shasum": "" }, "require": { @@ -9224,7 +9276,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.2" + "source": "https://github.com/symfony/translation/tree/v7.3.3" }, "funding": [ { @@ -9244,7 +9296,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:31:46+00:00" + "time": "2025-08-01T21:02:37+00:00" }, { "name": "symfony/translation-contracts", @@ -9400,16 +9452,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "53205bea27450dc5c65377518b3275e126d45e75" + "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/53205bea27450dc5c65377518b3275e126d45e75", - "reference": "53205bea27450dc5c65377518b3275e126d45e75", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", + "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", "shasum": "" }, "require": { @@ -9463,7 +9515,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.2" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.3" }, "funding": [ { @@ -9483,20 +9535,20 @@ "type": "tidelift" } ], - "time": "2025-07-29T20:02:46+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.3.2", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "05b3e90654c097817325d6abd284f7938b05f467" + "reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/05b3e90654c097817325d6abd284f7938b05f467", - "reference": "05b3e90654c097817325d6abd284f7938b05f467", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/d4dfcd2a822cbedd7612eb6fbd260e46f87b7137", + "reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137", "shasum": "" }, "require": { @@ -9544,7 +9596,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.3.2" + "source": "https://github.com/symfony/var-exporter/tree/v7.3.3" }, "funding": [ { @@ -9564,7 +9616,7 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-08-18T13:10:53+00:00" }, { "name": "thecodingmachine/safe", @@ -10348,16 +10400,16 @@ }, { "name": "composer/class-map-generator", - "version": "1.6.1", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34" + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/134b705ddb0025d397d8318a75825fe3c9d1da34", - "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076", + "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076", "shasum": "" }, "require": { @@ -10401,7 +10453,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.6.1" + "source": "https://github.com/composer/class-map-generator/tree/1.6.2" }, "funding": [ { @@ -10411,13 +10463,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2025-03-24T13:50:44+00:00" + "time": "2025-08-20T18:52:43+00:00" }, { "name": "composer/pcre", @@ -10500,16 +10548,16 @@ }, { "name": "driftingly/rector-laravel", - "version": "2.0.6", + "version": "2.0.7", "source": { "type": "git", "url": "https://github.com/driftingly/rector-laravel.git", - "reference": "5be95811801fc06126dd844beaeb6a41721ba3d3" + "reference": "625dc02cee08d47ecf0ac86de2f02a55026cf34e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/5be95811801fc06126dd844beaeb6a41721ba3d3", - "reference": "5be95811801fc06126dd844beaeb6a41721ba3d3", + "url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/625dc02cee08d47ecf0ac86de2f02a55026cf34e", + "reference": "625dc02cee08d47ecf0ac86de2f02a55026cf34e", "shasum": "" }, "require": { @@ -10529,9 +10577,9 @@ "description": "Rector upgrades rules for Laravel Framework", "support": { "issues": "https://github.com/driftingly/rector-laravel/issues", - "source": "https://github.com/driftingly/rector-laravel/tree/2.0.6" + "source": "https://github.com/driftingly/rector-laravel/tree/2.0.7" }, - "time": "2025-08-08T22:10:01+00:00" + "time": "2025-08-19T20:49:47+00:00" }, { "name": "fakerphp/faker", @@ -10690,16 +10738,16 @@ }, { "name": "larastan/larastan", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/larastan/larastan.git", - "reference": "6431d010dd383a9279eb8874a76ddb571738564a" + "reference": "2e653fd19585a825e283b42f38378b21ae481cc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/larastan/larastan/zipball/6431d010dd383a9279eb8874a76ddb571738564a", - "reference": "6431d010dd383a9279eb8874a76ddb571738564a", + "url": "https://api.github.com/repos/larastan/larastan/zipball/2e653fd19585a825e283b42f38378b21ae481cc7", + "reference": "2e653fd19585a825e283b42f38378b21ae481cc7", "shasum": "" }, "require": { @@ -10713,7 +10761,7 @@ "illuminate/pipeline": "^11.44.2 || ^12.4.1", "illuminate/support": "^11.44.2 || ^12.4.1", "php": "^8.2", - "phpstan/phpstan": "^2.1.11" + "phpstan/phpstan": "^2.1.23" }, "require-dev": { "doctrine/coding-standard": "^13", @@ -10767,7 +10815,7 @@ ], "support": { "issues": "https://github.com/larastan/larastan/issues", - "source": "https://github.com/larastan/larastan/tree/v3.6.0" + "source": "https://github.com/larastan/larastan/tree/v3.7.1" }, "funding": [ { @@ -10775,7 +10823,7 @@ "type": "github" } ], - "time": "2025-07-11T06:52:52+00:00" + "time": "2025-09-10T19:42:11+00:00" }, { "name": "laravel-json-api/testing", @@ -11284,16 +11332,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.22", + "version": "2.1.25", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4" + "reference": "4087d28bd252895874e174d65e26b2c202ed893a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/41600c8379eb5aee63e9413fe9e97273e25d57e4", - "reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4087d28bd252895874e174d65e26b2c202ed893a", + "reference": "4087d28bd252895874e174d65e26b2c202ed893a", "shasum": "" }, "require": { @@ -11338,7 +11386,7 @@ "type": "github" } ], - "time": "2025-08-04T19:17:37+00:00" + "time": "2025-09-12T14:26:42+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -11437,34 +11485,34 @@ }, { "name": "phpunit/php-code-coverage", - "version": "12.3.2", + "version": "12.3.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "086553c5b2e0e1e20293d782d788ab768202b621" + "reference": "bbede0f5593dad37af3be6a6f8e6ae1885e8a0a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/086553c5b2e0e1e20293d782d788ab768202b621", - "reference": "086553c5b2e0e1e20293d782d788ab768202b621", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bbede0f5593dad37af3be6a6f8e6ae1885e8a0a9", + "reference": "bbede0f5593dad37af3be6a6f8e6ae1885e8a0a9", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^5.4.0", + "nikic/php-parser": "^5.6.1", "php": ">=8.3", "phpunit/php-file-iterator": "^6.0", "phpunit/php-text-template": "^5.0", "sebastian/complexity": "^5.0", - "sebastian/environment": "^8.0", + "sebastian/environment": "^8.0.3", "sebastian/lines-of-code": "^4.0", "sebastian/version": "^6.0", "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^12.1" + "phpunit/phpunit": "^12.3.7" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -11502,7 +11550,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/12.3.2" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.7" }, "funding": [ { @@ -11522,7 +11570,7 @@ "type": "tidelift" } ], - "time": "2025-07-29T06:19:24+00:00" + "time": "2025-09-10T09:59:06+00:00" }, { "name": "phpunit/php-file-iterator", @@ -11771,16 +11819,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.3.5", + "version": "12.3.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f10ba5f12a256026ad3c7ee4894ffe47f60d7dc7" + "reference": "0d401d0df2e3c1703be425ecdc2d04f5c095938d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f10ba5f12a256026ad3c7ee4894ffe47f60d7dc7", - "reference": "f10ba5f12a256026ad3c7ee4894ffe47f60d7dc7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0d401d0df2e3c1703be425ecdc2d04f5c095938d", + "reference": "0d401d0df2e3c1703be425ecdc2d04f5c095938d", "shasum": "" }, "require": { @@ -11794,17 +11842,17 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.3", - "phpunit/php-code-coverage": "^12.3.2", + "phpunit/php-code-coverage": "^12.3.7", "phpunit/php-file-iterator": "^6.0.0", "phpunit/php-invoker": "^6.0.0", "phpunit/php-text-template": "^5.0.0", "phpunit/php-timer": "^8.0.0", "sebastian/cli-parser": "^4.0.0", - "sebastian/comparator": "^7.1.2", + "sebastian/comparator": "^7.1.3", "sebastian/diff": "^7.0.0", "sebastian/environment": "^8.0.3", "sebastian/exporter": "^7.0.0", - "sebastian/global-state": "^8.0.0", + "sebastian/global-state": "^8.0.2", "sebastian/object-enumerator": "^7.0.0", "sebastian/type": "^6.0.3", "sebastian/version": "^6.0.0", @@ -11848,7 +11896,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.5" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.10" }, "funding": [ { @@ -11872,20 +11920,20 @@ "type": "tidelift" } ], - "time": "2025-08-16T05:20:09+00:00" + "time": "2025-09-11T10:35:19+00:00" }, { "name": "rector/rector", - "version": "2.1.4", + "version": "2.1.7", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "fe613c528819222f8686a9a037a315ef9d4915b3" + "reference": "c34cc07c4698f007a20dc5c99ff820089ae413ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/fe613c528819222f8686a9a037a315ef9d4915b3", - "reference": "fe613c528819222f8686a9a037a315ef9d4915b3", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/c34cc07c4698f007a20dc5c99ff820089ae413ce", + "reference": "c34cc07c4698f007a20dc5c99ff820089ae413ce", "shasum": "" }, "require": { @@ -11924,7 +11972,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.1.4" + "source": "https://github.com/rectorphp/rector/tree/2.1.7" }, "funding": [ { @@ -11932,20 +11980,20 @@ "type": "github" } ], - "time": "2025-08-15T14:41:36+00:00" + "time": "2025-09-10T11:13:58+00:00" }, { "name": "sebastian/cli-parser", - "version": "4.0.0", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" + "reference": "8fd93be538992d556aaa45c74570129448a42084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", - "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/8fd93be538992d556aaa45c74570129448a42084", + "reference": "8fd93be538992d556aaa45c74570129448a42084", "shasum": "" }, "require": { @@ -11957,7 +12005,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "4.1-dev" } }, "autoload": { @@ -11981,28 +12029,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.1.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" } ], - "time": "2025-02-07T04:53:50+00:00" + "time": "2025-09-13T14:16:18+00:00" }, { "name": "sebastian/comparator", - "version": "7.1.2", + "version": "7.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1a7c2bce03a13a457ed3c975dfd331b3b4b133aa" + "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1a7c2bce03a13a457ed3c975dfd331b3b4b133aa", - "reference": "1a7c2bce03a13a457ed3c975dfd331b3b4b133aa", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dc904b4bb3ab070865fa4068cd84f3da8b945148", + "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148", "shasum": "" }, "require": { @@ -12061,7 +12121,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.2" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.3" }, "funding": [ { @@ -12081,7 +12141,7 @@ "type": "tidelift" } ], - "time": "2025-08-10T08:50:08+00:00" + "time": "2025-08-20T11:27:00+00:00" }, { "name": "sebastian/complexity", @@ -12364,16 +12424,16 @@ }, { "name": "sebastian/global-state", - "version": "8.0.0", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc" + "reference": "ef1377171613d09edd25b7816f05be8313f9115d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc", - "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d", "shasum": "" }, "require": { @@ -12414,15 +12474,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0" + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2025-02-07T04:56:59+00:00" + "time": "2025-08-29T11:29:25+00:00" }, { "name": "sebastian/lines-of-code", diff --git a/config/database.php b/config/database.php index 431f405c1e..77fe7499e7 100644 --- a/config/database.php +++ b/config/database.php @@ -22,6 +22,8 @@ declare(strict_types=1); +use function Safe\parse_url; + $databaseUrl = getenv('DATABASE_URL'); $host = ''; $username = ''; diff --git a/config/firefly.php b/config/firefly.php index 0e19932477..c2af8616ae 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -78,8 +78,8 @@ return [ 'running_balance_column' => env('USE_RUNNING_BALANCE', false), // see cer.php for exchange rates feature flag. ], - 'version' => '6.3.2', - 'build_time' => 1755576388, + 'version' => '6.4.0', + 'build_time' => 1757781366, 'api_version' => '2.1.0', // field is no longer used. 'db_version' => 26, @@ -182,12 +182,12 @@ return [ 'darkMode' => 'browser', 'list_length' => 10, // to be removed if v1 is cancelled. 'default_preferences' => [ - 'frontpageAccounts' => [], - 'listPageSize' => 50, - 'currencyPreference' => 'EUR', - 'language' => 'en_US', - 'locale' => 'equal', - 'convertToPrimary' => false, + 'frontpageAccounts' => [], + 'listPageSize' => 50, + 'currencyPreference' => 'EUR', + 'language' => 'en_US', + 'locale' => 'equal', + 'convertToPrimary' => false, ], 'default_currency' => 'EUR', 'default_language' => envNonEmpty('DEFAULT_LANGUAGE', 'en_US'), @@ -232,6 +232,13 @@ return [ 'image/png', 'image/heic', 'image/heic-sequence', + 'image/webp', + 'image/gif', + 'image/tiff', + 'image/bmp', + 'image/x-icon', + 'image/vnd.microsoft.icon', + // PDF 'application/pdf', @@ -820,8 +827,24 @@ return [ // dynamic date ranges are as follows: 'dynamic_date_ranges' => ['last7', 'last30', 'last90', 'last365', 'MTD', 'QTD', 'YTD'], - // only used in v1 - 'allowed_sort_parameters' => ['order', 'name', 'iban'], + 'allowed_sort_parameters' => [ + 'Account' => ['id', 'order', 'name', 'iban', 'active', 'account_type_id', + 'current_balance', + 'pc_current_balance', + 'opening_balance', + 'pc_opening_balance', + 'virtual_balance', + 'pc_virtual_balance', + 'debt_amount', + 'pc_debt_amount', + 'balance_difference', + 'pc_balance_difference', + ], + ], + 'allowed_db_sort_parameters' => [ + 'Account' => ['id', 'order', 'name', 'iban', 'active', 'account_type_id'], + ], + // preselected account lists possibilities: 'preselected_accounts' => ['all', 'assets', 'liabilities'], diff --git a/config/mail.php b/config/mail.php index 7558f4f936..771a214c62 100644 --- a/config/mail.php +++ b/config/mail.php @@ -21,6 +21,7 @@ */ declare(strict_types=1); +use function Safe\parse_url; return [ /* diff --git a/config/translations.php b/config/translations.php index 426e3ca355..2bb39e82d9 100644 --- a/config/translations.php +++ b/config/translations.php @@ -248,6 +248,7 @@ return [ 'multi_account_warning_deposit', 'multi_account_warning_transfer', + 'webhook_trigger_ANY', 'webhook_trigger_STORE_TRANSACTION', 'webhook_trigger_UPDATE_TRANSACTION', 'webhook_trigger_DESTROY_TRANSACTION', @@ -258,6 +259,7 @@ return [ 'webhook_trigger_STORE_UPDATE_BUDGET_LIMIT', 'webhook_response_TRANSACTIONS', + 'webhook_response_RELEVANT', 'webhook_response_ACCOUNTS', 'webhook_response_NONE', 'webhook_delivery_JSON', diff --git a/config/view.php b/config/view.php index 9c089067a6..a0eb9aa3b8 100644 --- a/config/view.php +++ b/config/view.php @@ -21,6 +21,7 @@ */ declare(strict_types=1); +use function Safe\realpath; $paths = [realpath(base_path('resources/views'))]; if ('v2' === env('FIREFLY_III_LAYOUT')) { diff --git a/config/webhooks.php b/config/webhooks.php new file mode 100644 index 0000000000..7aada2399c --- /dev/null +++ b/config/webhooks.php @@ -0,0 +1,107 @@ +. + */ + +declare(strict_types=1); + +// this is hard coded, which is unfortunate. + +use FireflyIII\Enums\WebhookResponse; +use FireflyIII\Enums\WebhookTrigger; + +return [ + 'force_relevant_response' => [ + WebhookTrigger::STORE_TRANSACTION->name => [ + WebhookTrigger::STORE_BUDGET->name, + WebhookTrigger::UPDATE_BUDGET->name, + WebhookTrigger::DESTROY_BUDGET->name, + WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name, + + ], + WebhookTrigger::UPDATE_TRANSACTION->name => [ + WebhookTrigger::STORE_BUDGET->name, + WebhookTrigger::UPDATE_BUDGET->name, + WebhookTrigger::DESTROY_BUDGET->name, + WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name, + ], + WebhookTrigger::DESTROY_TRANSACTION->name => [ + WebhookTrigger::STORE_BUDGET->name, + WebhookTrigger::UPDATE_BUDGET->name, + WebhookTrigger::DESTROY_BUDGET->name, + WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name, + ], + WebhookTrigger::STORE_BUDGET->name => [ + WebhookTrigger::STORE_TRANSACTION->name, + WebhookTrigger::UPDATE_TRANSACTION->name, + WebhookTrigger::DESTROY_TRANSACTION->name, + + ], + WebhookTrigger::UPDATE_BUDGET->name => [ + WebhookTrigger::STORE_TRANSACTION->name, + WebhookTrigger::UPDATE_TRANSACTION->name, + WebhookTrigger::DESTROY_TRANSACTION->name, + ], + WebhookTrigger::DESTROY_BUDGET->name => [ + WebhookTrigger::STORE_TRANSACTION->name, + WebhookTrigger::UPDATE_TRANSACTION->name, + WebhookTrigger::DESTROY_TRANSACTION->name, + ], + WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name => [ + WebhookTrigger::STORE_TRANSACTION->name, + WebhookTrigger::UPDATE_TRANSACTION->name, + WebhookTrigger::DESTROY_TRANSACTION->name, + ], + ], + 'forbidden_responses' => [ + WebhookTrigger::ANY->name => [ + WebhookResponse::BUDGET->name, + WebhookResponse::TRANSACTIONS->name, + WebhookResponse::ACCOUNTS->name, + ], + WebhookTrigger::STORE_TRANSACTION->name => [ + WebhookResponse::BUDGET->name, + ], + WebhookTrigger::UPDATE_TRANSACTION->name => [ + WebhookResponse::BUDGET->name, + ], + WebhookTrigger::DESTROY_TRANSACTION->name => [ + WebhookResponse::BUDGET->name, + ], + WebhookTrigger::STORE_BUDGET->name => [ + WebhookResponse::TRANSACTIONS->name, + WebhookResponse::ACCOUNTS->name, + + ], + WebhookTrigger::UPDATE_BUDGET->name => [ + WebhookResponse::TRANSACTIONS->name, + WebhookResponse::ACCOUNTS->name, + ], + WebhookTrigger::DESTROY_BUDGET->name => [ + WebhookResponse::TRANSACTIONS->name, + WebhookResponse::ACCOUNTS->name, + ], + WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name => [ + WebhookResponse::TRANSACTIONS->name, + WebhookResponse::ACCOUNTS->name, + ], + ], +]; diff --git a/database/migrations/2025_07_10_065736_rename_tag_mode.php b/database/migrations/2025_07_10_065736_rename_tag_mode.php index 26efd17a93..1785933cc9 100644 --- a/database/migrations/2025_07_10_065736_rename_tag_mode.php +++ b/database/migrations/2025_07_10_065736_rename_tag_mode.php @@ -1,5 +1,26 @@ . + */ + use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Log; diff --git a/database/migrations/2025_08_19_180459_create_webhook_details_tables.php b/database/migrations/2025_08_19_180459_create_webhook_details_tables.php new file mode 100644 index 0000000000..bed7e0b0ad --- /dev/null +++ b/database/migrations/2025_08_19_180459_create_webhook_details_tables.php @@ -0,0 +1,148 @@ +. + */ + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\QueryException; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration { + private const TABLE_ALREADY_EXISTS = 'If this table exists already (see the error message), this is not a problem. Other errors? Please open a discussion on GitHub.'; + private const TABLE_ERROR = 'Could not create table "%s": %s'; + + /** + * Run the migrations. + */ + public function up(): void + { + if (!Schema::hasTable('webhook_triggers')) { + Schema::create('webhook_triggers', function (Blueprint $table) { + $table->id(); + $table->timestamps(); + $table->smallInteger('key')->unsigned(); + $table->string('title', 100); + $table->unique(['key', 'title']); + }); + } + if (!Schema::hasTable('webhook_responses')) { + Schema::create('webhook_responses', function (Blueprint $table) { + $table->id(); + $table->timestamps(); + $table->smallInteger('key')->unsigned(); + $table->string('title', 100); + $table->unique(['key', 'title']); + }); + } + if (!Schema::hasTable('webhook_deliveries')) { + Schema::create('webhook_deliveries', function (Blueprint $table) { + $table->id(); + $table->timestamps(); + $table->smallInteger('key')->unsigned(); + $table->string('title', 100); + $table->unique(['key', 'title']); + }); + } + + // webhook_webhook_trigger + if (!Schema::hasTable('webhook_webhook_trigger')) { + try { + Schema::create( + 'webhook_webhook_trigger', + static function (Blueprint $table): void { + $table->increments('id'); + $table->integer('webhook_id', false, true); + $table->bigInteger('webhook_trigger_id', false, true); + $table->foreign('webhook_id')->references('id')->on('webhooks')->onDelete('cascade'); + $table->foreign('webhook_trigger_id','link_to_trigger')->references('id')->on('webhook_triggers')->onDelete('cascade'); + + // unique combi: + $table->unique(['webhook_id', 'webhook_trigger_id']); + } + ); + } catch (QueryException $e) { + app('log')->error(sprintf(self::TABLE_ERROR, 'webhook_webhook_trigger', $e->getMessage())); + app('log')->error(self::TABLE_ALREADY_EXISTS); + } + } + + + // webhook_webhook_response + if (!Schema::hasTable('webhook_webhook_response')) { + try { + Schema::create( + 'webhook_webhook_response', + static function (Blueprint $table): void { + $table->increments('id'); + $table->integer('webhook_id', false, true); + $table->bigInteger('webhook_response_id', false, true); + $table->foreign('webhook_id')->references('id')->on('webhooks')->onDelete('cascade'); + $table->foreign('webhook_response_id','link_to_response')->references('id')->on('webhook_responses')->onDelete('cascade'); + + // unique combi: + $table->unique(['webhook_id', 'webhook_response_id']); + } + ); + } catch (QueryException $e) { + app('log')->error(sprintf(self::TABLE_ERROR, 'webhook_webhook_response', $e->getMessage())); + app('log')->error(self::TABLE_ALREADY_EXISTS); + } + } + + // webhook_webhook_delivery + if (!Schema::hasTable('webhook_webhook_delivery')) { + try { + Schema::create( + 'webhook_webhook_delivery', + static function (Blueprint $table): void { + $table->increments('id'); + $table->integer('webhook_id', false, true); + $table->bigInteger('webhook_delivery_id', false, true); + $table->foreign('webhook_id')->references('id')->on('webhooks')->onDelete('cascade'); + $table->foreign('webhook_delivery_id','link_to_delivery')->references('id')->on('webhook_deliveries')->onDelete('cascade'); + + // unique combi: + $table->unique(['webhook_id', 'webhook_delivery_id']); + } + ); + } catch (QueryException $e) { + app('log')->error(sprintf(self::TABLE_ERROR, 'webhook_webhook_delivery', $e->getMessage())); + app('log')->error(self::TABLE_ALREADY_EXISTS); + } + } + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('webhook_webhook_delivery'); + Schema::dropIfExists('webhook_webhook_trigger'); + Schema::dropIfExists('webhook_webhook_response'); + + Schema::dropIfExists('webhook_triggers'); + Schema::dropIfExists('webhook_responses'); + Schema::dropIfExists('webhook_deliveries'); + } +}; diff --git a/database/seeders/AccountTypeSeeder.php b/database/seeders/AccountTypeSeeder.php index 0b9dda0723..ab34b8b772 100644 --- a/database/seeders/AccountTypeSeeder.php +++ b/database/seeders/AccountTypeSeeder.php @@ -35,25 +35,10 @@ class AccountTypeSeeder extends Seeder { public function run(): void { - $types = [ - AccountTypeEnum::DEFAULT->value, - AccountTypeEnum::CASH->value, - AccountTypeEnum::ASSET->value, - AccountTypeEnum::EXPENSE->value, - AccountTypeEnum::REVENUE->value, - AccountTypeEnum::INITIAL_BALANCE->value, - AccountTypeEnum::BENEFICIARY->value, - AccountTypeEnum::IMPORT->value, - AccountTypeEnum::LOAN->value, - AccountTypeEnum::RECONCILIATION->value, - AccountTypeEnum::DEBT->value, - AccountTypeEnum::MORTGAGE->value, - AccountTypeEnum::LIABILITY_CREDIT->value, - ]; - foreach ($types as $type) { - if (null === AccountType::where('type', $type)->first()) { + foreach(AccountTypeEnum::cases() as $type) { + if (null === AccountType::where('type', $type->value)->first()) { try { - AccountType::create(['type' => $type]); + AccountType::create(['type' => $type->value]); } catch (PDOException $e) { // @ignoreException } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index bdce19f553..42305fd851 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -43,5 +43,6 @@ class DatabaseSeeder extends Seeder $this->call(ConfigSeeder::class); $this->call(UserRoleSeeder::class); $this->call(ExchangeRateSeeder::class); + $this->call(WebhookDataSeeder::class); } } diff --git a/database/seeders/TransactionTypeSeeder.php b/database/seeders/TransactionTypeSeeder.php index 2a7d813164..71d247a799 100644 --- a/database/seeders/TransactionTypeSeeder.php +++ b/database/seeders/TransactionTypeSeeder.php @@ -35,24 +35,17 @@ class TransactionTypeSeeder extends Seeder { public function run(): void { - $types = [ - TransactionTypeEnum::WITHDRAWAL->value, - TransactionTypeEnum::DEPOSIT->value, - TransactionTypeEnum::TRANSFER->value, - TransactionTypeEnum::OPENING_BALANCE->value, - TransactionTypeEnum::RECONCILIATION->value, - TransactionTypeEnum::INVALID->value, - TransactionTypeEnum::LIABILITY_CREDIT->value, - ]; - - foreach ($types as $type) { - if (null === TransactionType::where('type', $type)->first()) { + /** @var TransactionTypeEnum $type */ + foreach (TransactionTypeEnum::cases() as $type) { // @phpstan-ignore-line + if (null === TransactionType::where('type', $type->value)->first()) { try { - TransactionType::create(['type' => $type]); + TransactionType::create(['type' => $type->value]); } catch (PDOException $e) { // @ignoreException } } } + + } } diff --git a/database/seeders/UserRoleSeeder.php b/database/seeders/UserRoleSeeder.php index c779b91812..3325f08aee 100644 --- a/database/seeders/UserRoleSeeder.php +++ b/database/seeders/UserRoleSeeder.php @@ -39,16 +39,11 @@ class UserRoleSeeder extends Seeder */ public function run(): void { - $roles = []; - foreach (UserRoleEnum::cases() as $role) { - $roles[] = $role->value; - } - - /** @var string $role */ - foreach ($roles as $role) { - if (null === UserRole::where('title', $role)->first()) { + /** @var UserRoleEnum $role */ + foreach (UserRoleEnum::cases() as $role) { // @phpstan-ignore-line + if (null === UserRole::where('title', $role->value)->first()) { try { - UserRole::create(['title' => $role]); + UserRole::create(['title' => $role->value]); } catch (PDOException $e) { // @ignoreException } diff --git a/database/seeders/WebhookDataSeeder.php b/database/seeders/WebhookDataSeeder.php new file mode 100644 index 0000000000..67cabb792a --- /dev/null +++ b/database/seeders/WebhookDataSeeder.php @@ -0,0 +1,69 @@ +. + */ + +namespace Database\Seeders; + +use FireflyIII\Enums\WebhookTrigger; +use FireflyIII\Enums\WebhookResponse; +use FireflyIII\Enums\WebhookDelivery; +use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel; +use FireflyIII\Models\WebhookResponse as WebhookResponseModel; +use FireflyIII\Models\WebhookDelivery as WebhookDeliveryModel; +use Illuminate\Database\Seeder; + +class WebhookDataSeeder extends Seeder +{ + /** + * Run the database seeds. + */ + public function run(): void + { + foreach (WebhookTrigger::cases() as $trigger) { + if (null === WebhookTriggerModel::where('key', $trigger->value)->where('title', $trigger->name)->first()) { + try { + WebhookTriggerModel::create(['key' => $trigger->value, 'title' => $trigger->name]); + } catch (\PDOException $e) { + // @ignoreException + } + } + } + foreach (WebhookResponse::cases() as $response) { + if (null === WebhookResponseModel::where('key', $response->value)->where('title', $response->name)->first()) { + try { + WebhookResponseModel::create(['key' => $response->value, 'title' => $response->name]); + } catch (\PDOException $e) { + // @ignoreException + } + } + } + foreach (WebhookDelivery::cases() as $delivery) { + if (null === WebhookDeliveryModel::where('key', $delivery->value)->where('title', $delivery->name)->first()) { + try { + WebhookDeliveryModel::create(['key' => $delivery->value, 'title' => $delivery->name]); + } catch (\PDOException $e) { + // @ignoreException + } + } + } + } +} diff --git a/package-lock.json b/package-lock.json index 5a1afee5b6..211a5d750c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,20 +13,6 @@ "postcss": "^8.4.47" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -43,9 +29,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", "dev": true, "license": "MIT", "engines": { @@ -53,22 +39,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", - "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -402,27 +388,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", - "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.2" + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -693,9 +679,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.4.tgz", + "integrity": "sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==", "dev": true, "license": "MIT", "dependencies": { @@ -743,9 +729,9 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", - "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", + "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", "dev": true, "license": "MIT", "dependencies": { @@ -754,7 +740,7 @@ "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/traverse": "^7.28.4" }, "engines": { "node": ">=6.9.0" @@ -1147,9 +1133,9 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", - "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz", + "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==", "dev": true, "license": "MIT", "dependencies": { @@ -1157,7 +1143,7 @@ "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.4" }, "engines": { "node": ">=6.9.0" @@ -1284,9 +1270,9 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", - "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", + "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", "dev": true, "license": "MIT", "dependencies": { @@ -1622,9 +1608,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", - "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1646,18 +1632,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", - "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", + "@babel/types": "^7.28.4", "debug": "^4.3.1" }, "engines": { @@ -1665,9 +1651,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2149,9 +2135,9 @@ } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.0.0.tgz", - "integrity": "sha512-X48nISrSOa89zu2VMljC4XaRf8NmgTwQBVHfS2Nu5G00ZwM31oOVrAtGxZF3b6wDYf9lJsf/Eq4cCSFKIkOWPQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.0.1.tgz", + "integrity": "sha512-RLmb9U6H2rJDnGxEqXxzy7ANPrQz7WK2/eTjdZqyU9uRU5W+FkAec9uU5gTYzFBH7aoXIw2WTJSCJR4KPlReQw==", "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", "engines": { "node": ">=6" @@ -2181,6 +2167,17 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -2210,9 +2207,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { @@ -2592,9 +2589,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.3.tgz", - "integrity": "sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz", + "integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==", "cpu": [ "arm" ], @@ -2606,9 +2603,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.3.tgz", - "integrity": "sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz", + "integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==", "cpu": [ "arm64" ], @@ -2620,9 +2617,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.3.tgz", - "integrity": "sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz", + "integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==", "cpu": [ "arm64" ], @@ -2634,9 +2631,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.3.tgz", - "integrity": "sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz", + "integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==", "cpu": [ "x64" ], @@ -2648,9 +2645,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.3.tgz", - "integrity": "sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz", + "integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==", "cpu": [ "arm64" ], @@ -2662,9 +2659,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.3.tgz", - "integrity": "sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz", + "integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==", "cpu": [ "x64" ], @@ -2676,9 +2673,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.3.tgz", - "integrity": "sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz", + "integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==", "cpu": [ "arm" ], @@ -2690,9 +2687,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.3.tgz", - "integrity": "sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz", + "integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==", "cpu": [ "arm" ], @@ -2704,9 +2701,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.3.tgz", - "integrity": "sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz", + "integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==", "cpu": [ "arm64" ], @@ -2718,9 +2715,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.3.tgz", - "integrity": "sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz", + "integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==", "cpu": [ "arm64" ], @@ -2732,9 +2729,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.3.tgz", - "integrity": "sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz", + "integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==", "cpu": [ "loong64" ], @@ -2746,9 +2743,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.3.tgz", - "integrity": "sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz", + "integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==", "cpu": [ "ppc64" ], @@ -2760,9 +2757,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.3.tgz", - "integrity": "sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz", + "integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==", "cpu": [ "riscv64" ], @@ -2774,9 +2771,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.3.tgz", - "integrity": "sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz", + "integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==", "cpu": [ "riscv64" ], @@ -2788,9 +2785,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.3.tgz", - "integrity": "sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz", + "integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==", "cpu": [ "s390x" ], @@ -2802,9 +2799,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.3.tgz", - "integrity": "sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz", + "integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==", "cpu": [ "x64" ], @@ -2816,9 +2813,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.3.tgz", - "integrity": "sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz", + "integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==", "cpu": [ "x64" ], @@ -2829,10 +2826,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz", + "integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.3.tgz", - "integrity": "sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz", + "integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==", "cpu": [ "arm64" ], @@ -2844,9 +2855,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.3.tgz", - "integrity": "sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz", + "integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==", "cpu": [ "ia32" ], @@ -2858,9 +2869,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.3.tgz", - "integrity": "sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz", + "integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==", "cpu": [ "x64" ], @@ -3148,9 +3159,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", - "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "version": "24.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.3.tgz", + "integrity": "sha512-GKBNHjoNw3Kra1Qg5UXttsY5kiWMEfoHq2TmXb+b1rcm6N7B3wTrFYIf/oSZ1xNQ+hVVijgLkiDZh7jRRsh+Gw==", "dev": true, "license": "MIT", "dependencies": { @@ -3158,9 +3169,9 @@ } }, "node_modules/@types/node-forge": { - "version": "1.3.13", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.13.tgz", - "integrity": "sha512-zePQJSW5QkwSHKRApqWCVKeKoSOt4xvEnLENZPjyvm9Ezdf/EyDeJM7jqLzOwjVICQQzvLZ63T55MKdJB5H6ww==", + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz", + "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==", "dev": true, "license": "MIT", "dependencies": { @@ -3256,57 +3267,57 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", - "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@vue/shared": "3.5.18", + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", - "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.18", - "@vue/shared": "3.5.18" + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", - "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@vue/compiler-core": "3.5.18", - "@vue/compiler-dom": "3.5.18", - "@vue/compiler-ssr": "3.5.18", - "@vue/shared": "3.5.18", + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", "estree-walker": "^2.0.2", - "magic-string": "^0.30.17", + "magic-string": "^0.30.18", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", - "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.18", - "@vue/shared": "3.5.18" + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" } }, "node_modules/@vue/component-compiler-utils": { @@ -3388,9 +3399,9 @@ "license": "MIT" }, "node_modules/@vue/shared": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", - "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", "dev": true, "license": "MIT" }, @@ -3741,9 +3752,9 @@ } }, "node_modules/alpinejs": { - "version": "3.14.9", - "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.9.tgz", - "integrity": "sha512-gqSOhTEyryU9FhviNqiHBHzgjkvtukq9tevew29fTj+ofZtfsYriw4zPirHHOAy9bw8QoL3WGhyk7QqCh5AYlw==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.0.tgz", + "integrity": "sha512-lpokA5okCF1BKh10LG8YjqhfpxyHBk4gE7boIgVHltJzYoM7O9nK3M7VlntLEJGsVmu7U/RzUWajmHREGT38Eg==", "license": "MIT", "dependencies": { "@vue/reactivity": "~3.1.1" @@ -3938,9 +3949,9 @@ } }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.1.tgz", + "integrity": "sha512-Kn4kbSXpkFHCGE6rBFNwIv0GQs4AvDT80jlveJDKFxjbTYMUeB4QtsdPCv6H8Cm19Je7IU6VFtRl2zWZI0rudQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4049,6 +4060,16 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.3.tgz", + "integrity": "sha512-mcE+Wr2CAhHNWxXN/DdTI+n4gsPc5QpXpWnyCQWiQYIYZX+ZMJ8juXZgjRa/0/YPJo/NSsgW15/YgmI4nbysYw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -4170,9 +4191,9 @@ "license": "ISC" }, "node_modules/bootstrap": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz", - "integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==", + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", "funding": [ { "type": "github", @@ -4326,9 +4347,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", - "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.0.tgz", + "integrity": "sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==", "dev": true, "funding": [ { @@ -4346,9 +4367,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001735", - "electron-to-chromium": "^1.5.204", - "node-releases": "^2.0.19", + "baseline-browser-mapping": "^2.8.2", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { @@ -4486,9 +4508,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001735", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", - "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", + "version": "1.0.30001741", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz", + "integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==", "dev": true, "funding": [ { @@ -4930,13 +4952,13 @@ "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.45.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz", - "integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==", + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", + "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.25.1" + "browserslist": "^4.25.3" }, "funding": { "type": "opencollective", @@ -5700,9 +5722,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.205", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.205.tgz", - "integrity": "sha512-gBtbT7IgOHu7CwdtIiXwbNRD1l6oG6GAyanmwMCLVqaoGy92Jfe1dSHLiSj8xUEZNxOTIVlXuaAalMMD+S4y0w==", + "version": "1.5.218", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.218.tgz", + "integrity": "sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==", "dev": true, "license": "ISC" }, @@ -6159,9 +6181,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "dev": true, "funding": [ { @@ -7052,9 +7074,9 @@ } }, "node_modules/i18next": { - "version": "25.3.6", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.6.tgz", - "integrity": "sha512-dThZ0CTCM3sUG/qS0ZtQYZQcUI6DtBN8yBHK+SKEqihPcEYmjVWh/YJ4luic73Iq6Uxhp6q7LJJntRK5+1t7jQ==", + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.5.2.tgz", + "integrity": "sha512-lW8Zeh37i/o0zVr+NoCHfNnfvVw+M6FQbRp36ZZ/NyHDJ3NJVpp2HhAUyU9WafL5AssymNoOjMRB48mmx2P6Hw==", "funding": [ { "type": "individual", @@ -7818,9 +7840,9 @@ } }, "node_modules/laravel-vite-plugin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.0.0.tgz", - "integrity": "sha512-pnaKHInJgiWpG/g+LmaISHl7D/1s5wnOXnrGiBdt4NOs+tYZRw0v/ZANELGX2/dGgHyEzO+iZ6x4idpoK04z/Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.0.1.tgz", + "integrity": "sha512-zQuvzWfUKQu9oNVi1o0RZAJCwhGsdhx4NEOyrVQwJHaWDseGP9tl7XUPLY2T8Cj6+IrZ6lmyxlR1KC8unf3RLA==", "dev": true, "license": "MIT", "dependencies": { @@ -7958,13 +7980,13 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/make-dir": { @@ -8428,9 +8450,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", "dev": true, "license": "MIT" }, @@ -9912,9 +9934,9 @@ "license": "MIT" }, "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "dev": true, "license": "MIT", "dependencies": { @@ -9925,18 +9947,18 @@ } }, "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.3.1.tgz", + "integrity": "sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ==", "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", + "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "unicode-match-property-value-ecmascript": "^2.2.1" }, "engines": { "node": ">=4" @@ -10123,9 +10145,9 @@ } }, "node_modules/rollup": { - "version": "4.46.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.3.tgz", - "integrity": "sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw==", + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", + "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==", "dev": true, "license": "MIT", "dependencies": { @@ -10139,26 +10161,27 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.46.3", - "@rollup/rollup-android-arm64": "4.46.3", - "@rollup/rollup-darwin-arm64": "4.46.3", - "@rollup/rollup-darwin-x64": "4.46.3", - "@rollup/rollup-freebsd-arm64": "4.46.3", - "@rollup/rollup-freebsd-x64": "4.46.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.46.3", - "@rollup/rollup-linux-arm-musleabihf": "4.46.3", - "@rollup/rollup-linux-arm64-gnu": "4.46.3", - "@rollup/rollup-linux-arm64-musl": "4.46.3", - "@rollup/rollup-linux-loongarch64-gnu": "4.46.3", - "@rollup/rollup-linux-ppc64-gnu": "4.46.3", - "@rollup/rollup-linux-riscv64-gnu": "4.46.3", - "@rollup/rollup-linux-riscv64-musl": "4.46.3", - "@rollup/rollup-linux-s390x-gnu": "4.46.3", - "@rollup/rollup-linux-x64-gnu": "4.46.3", - "@rollup/rollup-linux-x64-musl": "4.46.3", - "@rollup/rollup-win32-arm64-msvc": "4.46.3", - "@rollup/rollup-win32-ia32-msvc": "4.46.3", - "@rollup/rollup-win32-x64-msvc": "4.46.3", + "@rollup/rollup-android-arm-eabi": "4.50.1", + "@rollup/rollup-android-arm64": "4.50.1", + "@rollup/rollup-darwin-arm64": "4.50.1", + "@rollup/rollup-darwin-x64": "4.50.1", + "@rollup/rollup-freebsd-arm64": "4.50.1", + "@rollup/rollup-freebsd-x64": "4.50.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", + "@rollup/rollup-linux-arm-musleabihf": "4.50.1", + "@rollup/rollup-linux-arm64-gnu": "4.50.1", + "@rollup/rollup-linux-arm64-musl": "4.50.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", + "@rollup/rollup-linux-ppc64-gnu": "4.50.1", + "@rollup/rollup-linux-riscv64-gnu": "4.50.1", + "@rollup/rollup-linux-riscv64-musl": "4.50.1", + "@rollup/rollup-linux-s390x-gnu": "4.50.1", + "@rollup/rollup-linux-x64-gnu": "4.50.1", + "@rollup/rollup-linux-x64-musl": "4.50.1", + "@rollup/rollup-openharmony-arm64": "4.50.1", + "@rollup/rollup-win32-arm64-msvc": "4.50.1", + "@rollup/rollup-win32-ia32-msvc": "4.50.1", + "@rollup/rollup-win32-x64-msvc": "4.50.1", "fsevents": "~2.3.2" } }, @@ -10214,9 +10237,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.90.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz", - "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", + "version": "1.92.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.92.1.tgz", + "integrity": "sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11005,24 +11028,28 @@ } }, "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", "dev": true, "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/terser": { - "version": "5.43.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", - "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", + "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", + "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -11361,9 +11388,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", "dev": true, "license": "MIT", "engines": { diff --git a/public/index.php b/public/index.php index e1efbd4614..d91104f6a2 100644 --- a/public/index.php +++ b/public/index.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); use Illuminate\Contracts\Http\Kernel; diff --git a/public/v1/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.svg b/public/v1/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.svg index 94fb5490a2..187805af66 100644 --- a/public/v1/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.svg +++ b/public/v1/fonts/vendor/bootstrap-sass/bootstrap/glyphicons-halflings-regular.svg @@ -285,4 +285,4 @@ - \ No newline at end of file + diff --git a/public/v1/fonts/vendor/font-awesome/fontawesome-webfont.svg b/public/v1/fonts/vendor/font-awesome/fontawesome-webfont.svg index 855c845e53..52c0773359 100644 --- a/public/v1/fonts/vendor/font-awesome/fontawesome-webfont.svg +++ b/public/v1/fonts/vendor/font-awesome/fontawesome-webfont.svg @@ -8,7 +8,7 @@ Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/assets/v1/src/components/form/WebhookDelivery.vue b/resources/assets/v1/src/components/form/WebhookDelivery.vue index ef844cdb1d..ff184466ac 100644 --- a/resources/assets/v1/src/components/form/WebhookDelivery.vue +++ b/resources/assets/v1/src/components/form/WebhookDelivery.vue @@ -68,7 +68,7 @@ export default { } }, value: { - type: Number, + type: String, required: true, } }, @@ -84,7 +84,7 @@ export default { } this.deliveries.push( { - id: response.data.data.value[key], + id: key, name: this.$t('firefly.webhook_delivery_' + key), } ); diff --git a/resources/assets/v1/src/components/form/WebhookResponse.vue b/resources/assets/v1/src/components/form/WebhookResponse.vue index 754c29e609..1ce8eb32b9 100644 --- a/resources/assets/v1/src/components/form/WebhookResponse.vue +++ b/resources/assets/v1/src/components/form/WebhookResponse.vue @@ -66,7 +66,7 @@ export default { } }, value: { - type: Number, + type: String, required: true, } }, @@ -88,7 +88,7 @@ export default { } this.responses.push( { - id: response.data.data.value[key], + id: key, name: this.$t('firefly.webhook_response_' + key), } ); diff --git a/resources/assets/v1/src/components/form/WebhookTrigger.vue b/resources/assets/v1/src/components/form/WebhookTrigger.vue index c62d75a44f..0a13381b03 100644 --- a/resources/assets/v1/src/components/form/WebhookTrigger.vue +++ b/resources/assets/v1/src/components/form/WebhookTrigger.vue @@ -29,6 +29,7 @@ - + {{ $t('list.secret') }} + { // process columns: for (let k in res[0]) { @@ -283,6 +285,9 @@ let index = function () { // group accounts this.pageOptions.groupedAccounts = res[4]; + // convert to primary? + this.convertToPrimary = res[5]; + this.loadAccounts(); }); }, @@ -348,7 +353,6 @@ let index = function () { if('asc' === this.pageOptions.sortDirection && '' !== sorting) { sorting = '-' + sorting; } - //const sorting = [{column: this.pageOptions.sortingColumn, direction: this.pageOptions.sortDirection}]; // filter instructions let filters = {}; @@ -371,20 +375,20 @@ let index = function () { sort: sorting, filter: filters, active: active, - currentMoment: today, + date: format(today,'yyyy-MM-dd'), type: type, page: this.page, - startPeriod: start, - endPeriod: end + start: start, + end: end }; if (!this.tableColumns.balance_difference.enabled) { - delete params.startPeriod; - delete params.endPeriod; + // delete params.startPeriod; + // delete params.endPeriod; } this.accounts = []; let groupedAccounts = {}; - // one page only.o + // one page only (new Get()).index(params).then(response => { this.totalPages = response.meta.pagination.total_pages; for (let i = 0; i < response.data.length; i++) { @@ -399,17 +403,26 @@ let index = function () { role: current.attributes.account_role, iban: null === current.attributes.iban ? '' : current.attributes.iban.match(/.{1,4}/g).join(' '), account_number: null === current.attributes.account_number ? '' : current.attributes.account_number, - current_balance: current.attributes.current_balance, - currency_code: current.attributes.currency_code, last_activity: null === current.attributes.last_activity ? '' : format(new Date(current.attributes.last_activity), i18next.t('config.month_and_day_fns')), liability_type: current.attributes.liability_type, liability_direction: current.attributes.liability_direction, interest: current.attributes.interest, interest_period: current.attributes.interest_period, - balance: current.attributes.balance, - pc_balance: current.attributes.pc_balance, - balances: current.attributes.balances, + + // currency info + currency_code: current.attributes.currency_code, + primary_currency_code: current.attributes.primary_currency_code, + + + // balances. + current_balance: current.attributes.current_balance, + pc_current_balance: current.attributes.pc_current_balance, + balance_difference: current.attributes.balance_difference, + pc_balance_difference: current.attributes.pc_balance_difference, + debt_amount: current.attributes.debt_amount, + pc_debt_amount: current.attributes.debt_amount, }; + console.log(account); // get group info: let groupId = current.attributes.object_group_id; if(!this.pageOptions.groupedAccounts) { @@ -426,10 +439,9 @@ let index = function () { } } groupedAccounts[groupId].accounts.push(account); - - //this.accounts.push(account); } } + // order grouped accounts by order. let sortable = []; for (let set in groupedAccounts) { diff --git a/resources/assets/v2/src/pages/transactions/show.js b/resources/assets/v2/src/pages/transactions/show.js index ad5a3f55ab..84522924a5 100644 --- a/resources/assets/v2/src/pages/transactions/show.js +++ b/resources/assets/v2/src/pages/transactions/show.js @@ -87,11 +87,16 @@ let show = function () { if (this.entries.hasOwnProperty(i)) { const currencyCode = this.entries[i].currency_code; const foreignCurrencyCode = this.entries[i].foreign_currency_code; + const primaryCurrencyCode = this.entries[i].primary_currency_code; if (undefined === this.amounts[currencyCode]) { this.amounts[currencyCode] = 0; this.amounts[currencyCode] += parseFloat(this.entries[i].amount); } + if (undefined === this.amounts[primaryCurrencyCode]) { + this.amounts[primaryCurrencyCode] = 0; + this.amounts[primaryCurrencyCode] += parseFloat(this.entries[i].pc_amount); + } if (null !== foreignCurrencyCode && '' !== foreignCurrencyCode && undefined === this.amounts[foreignCurrencyCode]) { this.amounts[foreignCurrencyCode] = 0; this.amounts[foreignCurrencyCode] += parseFloat(this.entries[i].foreign_amount); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 3c8efe5e07..4a2ab3d092 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -241,6 +241,7 @@ return [ 'webhooks_breadcrumb' => 'Webhooks', 'webhooks_menu_disabled' => 'disabled', 'no_webhook_messages' => 'There are no webhook messages', + 'webhook_trigger_ANY' => 'After any event', 'webhook_trigger_STORE_TRANSACTION' => 'After transaction creation', 'webhook_trigger_UPDATE_TRANSACTION' => 'After transaction update', 'webhook_trigger_DESTROY_TRANSACTION' => 'After transaction delete', @@ -250,8 +251,8 @@ return [ 'webhook_trigger_STORE_UPDATE_BUDGET_LIMIT' => 'After budgeted amount change', 'webhook_response_TRANSACTIONS' => 'Transaction details', 'webhook_response_ACCOUNTS' => 'Account details', - 'webhook_response_none_BUDGET' => 'Budget details', - 'webhook_response_none_NONE' => 'No details', + 'webhook_response_BUDGET' => 'Budget details', + 'webhook_response_RELEVANT' => 'Relevant details', 'webhook_response_NONE' => 'No details', 'webhook_delivery_JSON' => 'JSON', 'inspect' => 'Inspect', diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index 91d04df320..9c9052b050 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -24,6 +24,9 @@ declare(strict_types=1); return [ + 'invalid_sort_instruction' => 'The sort instruction is invalid for an object of type ":object".', + 'invalid_sort_instruction_index' => 'The sort instruction at index #:index is invalid for an object of type ":object".', + 'no_sort_instructions' => 'There are no sort instructions defined for an object of type ":object".', 'webhook_budget_info' => 'Cannot deliver budget information for transaction related webhooks.', 'webhook_account_info' => 'Cannot deliver account information for budget related webhooks.', 'webhook_transaction_info' => 'Cannot deliver transaction information for budget related webhooks.', @@ -34,6 +37,10 @@ return [ 'filter_not_string' => 'Filter ":filter" is expected to be a string of text', 'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.', 'nog_logged_in' => 'You are not logged in.', + 'prohibited' => 'You must not submit anything in field.', + 'bad_webhook_combination' => 'Webhook trigger ":trigger" cannot be combined with webhook response ":response".', + 'unknown_webhook_trigger' => 'Unknown webhook trigger ":trigger".', + 'only_any_trigger' => 'If you select the "Any event"-trigger, you may not select any other triggers.', 'bad_type_source' => 'Firefly III can\'t determine the transaction type based on this source account.', 'bad_type_destination' => 'Firefly III can\'t determine the transaction type based on this destination account.', 'missing_where' => 'Array is missing "where"-clause', @@ -115,6 +122,7 @@ return [ 'between.file' => 'The :attribute must be between :min and :max kilobytes.', 'between.string' => 'The :attribute must be between :min and :max characters.', 'between.array' => 'The :attribute must have between :min and :max items.', + 'between_date' => 'The date must be between the given start and end date.', 'boolean' => 'The :attribute field must be true or false.', 'confirmed' => 'The :attribute confirmation does not match.', 'date' => 'The :attribute is not a valid date.', diff --git a/resources/views/accounts/show.twig b/resources/views/accounts/show.twig index e2145c1465..6a79e5fbbc 100644 --- a/resources/views/accounts/show.twig +++ b/resources/views/accounts/show.twig @@ -147,7 +147,7 @@

{{ 'transactions'|_ }} {% if balances.balance %} - ({{ formatAmountBySymbol(balances.balance, currency.symbol, currency.decimal_places, true)|raw }}) + ({{ formatAmountBySymbol(balances.balance, currency.symbol, currency.decimal_places, true)|raw }}) {% elseif balances.pc_balance %} ({{ formatAmountBySymbol(balances.pc_balance, primaryCurrency.symbol, primaryCurrency.decimal_places, true)|raw }}) {% endif %} diff --git a/resources/views/budgets/index.twig b/resources/views/budgets/index.twig index d773948874..73b8814968 100644 --- a/resources/views/budgets/index.twig +++ b/resources/views/budgets/index.twig @@ -356,16 +356,17 @@
{% endif %} + {% if spentInfo.currency_id == budgetLimit.currency_id and not budgetLimit.in_range %} + + {{ formatAmountBySymbol(spentInfo.spent + budgetLimit.amount, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }} + + ({{ 'unknown'|_ }}) + {% endif %} {% endfor %} {% if countLimit == 0 %} - - {# this code is used for budget limits OUTSIDE the current view range. #} - - {{ formatAmountBySymbol(spentInfo.spent, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }} - -
+ {# display nothing #} {% endif %} {% endfor %} {% for budgetLimit in budget.budgeted %} diff --git a/resources/views/errors/FireflyException.blade.php b/resources/views/errors/FireflyException.blade.php index 5dfc2be6c8..d1311f1cb3 100644 --- a/resources/views/errors/FireflyException.blade.php +++ b/resources/views/errors/FireflyException.blade.php @@ -42,6 +42,7 @@

@endif + @if($debug)
@@ -49,6 +50,19 @@

{!! trans('errors.error_location', ['file' => $exception->getFile(), 'line' => $exception->getLine(), 'code' => $exception->getCode() ]) !!}

+

+ {{ trans('errors.github_help') }} +

+

+ {!! trans('errors.github_instructions') !!} +

+
    +
  1. {{ trans('errors.use_search') }}
  2. +
  3. {!! trans('errors.include_info', ['link' => route('debug') ]) !!}
  4. +
  5. {{ trans('errors.tell_more') }}
  6. +
  7. {{ trans('errors.include_logs') }}
  8. +
  9. {{ trans('errors.what_did_you_do') }}
  10. +

{{ trans('errors.stacktrace') }}

diff --git a/resources/views/list/bills.twig b/resources/views/list/bills.twig index e326f77c99..ef0093b389 100644 --- a/resources/views/list/bills.twig +++ b/resources/views/list/bills.twig @@ -68,8 +68,8 @@ > ~ {{ formatAmountBySymbol((entry.amount_max + entry.amount_min)/2, entry.currency_symbol, entry.currency_decimal_places) }} - {% if '0' != entry.pc_amount_max %} - (~ {{ formatAmountBySymbol((entry.pc_amount_max + entry.pc_amount_min)/2, primaryCurrency.symbol, primaryCurrency.decimal_places) }}) + {% if '0' != entry.pc_amount_max and null != entry.pc_amount_max %} + (~ {{ formatAmountBySymbol((entry.pc_amount_max + entry.pc_amount_min)/2, primaryCurrency.symbol, primaryCurrency.decimal_places) }}) {% endif %} diff --git a/resources/views/rules/rule-group/select-transactions.twig b/resources/views/rules/rule-group/select-transactions.twig index 842458804f..81abe48208 100644 --- a/resources/views/rules/rule-group/select-transactions.twig +++ b/resources/views/rules/rule-group/select-transactions.twig @@ -24,8 +24,6 @@

- {{ ExpandedForm.date('start', first) }} - {{ ExpandedForm.date('end', today) }} {{ AccountForm.assetAccountCheckList('accounts', {'select_all': true,'class': 'account-checkbox', 'label': trans('firefly.include_transactions_from_accounts') }) }}
diff --git a/resources/views/rules/rule/select-transactions.twig b/resources/views/rules/rule/select-transactions.twig index aae7bc3b9a..a6575e53f9 100644 --- a/resources/views/rules/rule/select-transactions.twig +++ b/resources/views/rules/rule/select-transactions.twig @@ -23,8 +23,6 @@

- {{ ExpandedForm.date('start', first) }} - {{ ExpandedForm.date('end', today) }} {{ AccountForm.assetAccountCheckList('accounts', {'select_all': true, 'class': 'account-checkbox', 'label': trans('firefly.include_transactions_from_accounts') }) }}
diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index cb0c6c1f3a..9202da88dd 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -160,12 +160,13 @@ {% for amount in amounts %} {% if first.transaction_type_type == 'Withdrawal' %} - {{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %} - {% elseif first.transaction_type_type == 'Deposit' %} {{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %} + {% elseif first.transaction_type_type == 'Deposit' %} + {{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %} {% elseif first.transaction_type_type == 'Transfer' %} - {{ formatAmountBySymbol(amount.amount, amount.symbol, amount.decimal_places, false) }}{% if loop.index0 != amounts|length -1 %}, {% endif %} + + {{ formatAmountBySymbol(amount.amount*-1, amount.symbol, amount.decimal_places, false) }}{% if loop.index0 != amounts|length -1 %}, {% endif %} {% elseif first.transaction_type_type == 'Opening balance' %} {# Opening balance stored amount is always negative: find out which way the money goes #} @@ -289,7 +290,6 @@ diff --git a/resources/views/v2/accounts/index.blade.php b/resources/views/v2/accounts/index.blade.php index 05d54582ae..e5f5fb8e5d 100644 --- a/resources/views/v2/accounts/index.blade.php +++ b/resources/views/v2/accounts/index.blade.php @@ -10,7 +10,7 @@

{{ __('firefly.net_worth') }}

- TODO + Not yet implemented.
@@ -20,17 +20,17 @@

{{ __('firefly.in_out_period') }}

- TODO + Not yet implemented.
-

TODO

+

Not yet implemented.

- TODO + Not yet implemented.
@@ -262,43 +262,75 @@
- {% if 'Cash account' == journal.source_account_type %} ({{ 'cash'|_ }}) {% else %} @@ -298,12 +298,12 @@ {% endif %} {% if first.transaction_type_type == 'Withdrawal' %} - {{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }} - {% elseif first.transaction_type_type == 'Deposit' %} {{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places) }} + {% elseif first.transaction_type_type == 'Deposit' %} + {{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }} {% elseif first.transaction_type_type == 'Transfer' or first.transaction_type_type == 'Opening balance' %} - {{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places, false) }} + {{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places, false) }} {% elseif first.transaction_type_type == 'Liability credit' %} @@ -314,12 +314,12 @@ {% if null != journal.pc_amount and primaryCurrency.id != journal.currency_id %} {% if first.transaction_type_type == 'Withdrawal' %} - ({{ formatAmountBySymbol(journal.pc_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }}) - {% elseif first.transaction_type_type == 'Deposit' %} ({{ formatAmountBySymbol(journal.pc_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }}) + {% elseif first.transaction_type_type == 'Deposit' %} + ({{ formatAmountBySymbol(journal.pc_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }}) {% elseif first.transaction_type_type == 'Transfer' %} - ({{ formatAmountBySymbol(journal.pc_amount, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }}) + ({{ formatAmountBySymbol(journal.pc_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }}) {% endif %} {% endif %} @@ -327,12 +327,12 @@ {% if null != journal.foreign_amount %} {% if first.transaction_type_type == 'Withdrawal' %} - ({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }}) - {% elseif first.transaction_type_type == 'Deposit' %} ({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }}) + {% elseif first.transaction_type_type == 'Deposit' %} + ({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }}) {% elseif first.transaction_type_type == 'Transfer' %} - ({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }}) + ({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }}) {% endif %} {% endif %} @@ -340,12 +340,12 @@ {% if null != journal.pc_foreign_amount %} {% if first.transaction_type_type == 'Withdrawal' %} - ({{ formatAmountBySymbol(journal.pc_foreign_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }}) - {% elseif first.transaction_type_type == 'Deposit' %} ({{ formatAmountBySymbol(journal.pc_foreign_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }}) + {% elseif first.transaction_type_type == 'Deposit' %} + ({{ formatAmountBySymbol(journal.pc_foreign_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }}) {% elseif first.transaction_type_type == 'Transfer' %} - ({{ formatAmountBySymbol(journal.pc_foreign_amount, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }}) + ({{ formatAmountBySymbol(journal.pc_foreign_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }}) {% endif %} {% endif %} @@ -440,10 +440,9 @@ {{ 'tags'|_ }} {% for tag in journal.tags %} -

- {{ tag.tag }} -

+ {% if null != tag.id %} +

{{ tag.tag }}

+ {% endif %} {% endfor %}
-