mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 18:54:58 +00:00 
			
		
		
		
	Merge pull request #10906 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
			
			
This commit is contained in:
		
							
								
								
									
										203
									
								
								.ci/php-cs-fixer/composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										203
									
								
								.ci/php-cs-fixer/composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -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": [], | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/release-notes/alpha.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/release-notes/alpha.md
									
									
									
									
										vendored
									
									
								
							| @@ -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. | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/release-notes/beta.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/release-notes/beta.md
									
									
									
									
										vendored
									
									
								
							| @@ -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. | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/release-notes/branch.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/release-notes/branch.md
									
									
									
									
										vendored
									
									
								
							| @@ -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. | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/release-notes/develop.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/release-notes/develop.md
									
									
									
									
										vendored
									
									
								
							| @@ -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. | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/release-notes/release.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/release-notes/release.md
									
									
									
									
										vendored
									
									
								
							| @@ -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. | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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, | ||||
|             ] | ||||
|         ); | ||||
| 
 | ||||
|   | ||||
| @@ -69,6 +69,7 @@ class RecurrenceController extends Controller | ||||
|                 'id'          => (string) $recurrence->id, | ||||
|                 'name'        => $recurrence->title, | ||||
|                 'description' => $recurrence->description, | ||||
|                 'active'      => $recurrence->active, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -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, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -69,6 +69,7 @@ class RuleGroupController extends Controller | ||||
|                 'id'          => (string) $group->id, | ||||
|                 'name'        => $group->title, | ||||
|                 'description' => $group->description, | ||||
|                 'active'      => $group->active, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -1,5 +1,26 @@ | ||||
| <?php | ||||
| 
 | ||||
| 
 | ||||
| /* | ||||
|  * BalanceController.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\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; | ||||
|   | ||||
| @@ -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 | ||||
|     { | ||||
|   | ||||
| @@ -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)); | ||||
|     } | ||||
|   | ||||
| @@ -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<int, string> */ | ||||
|     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(); | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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.
 | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -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']; | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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']; | ||||
| 
 | ||||
|         } | ||||
|   | ||||
| @@ -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
 | ||||
|                 } | ||||
|   | ||||
| @@ -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) | ||||
|         ; | ||||
| 
 | ||||
|   | ||||
| @@ -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); | ||||
| 
 | ||||
|   | ||||
| @@ -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); | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
| @@ -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); | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
							
								
								
									
										126
									
								
								app/Api/V1/Controllers/Models/Recurrence/TriggerController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								app/Api/V1/Controllers/Models/Recurrence/TriggerController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| /* | ||||
|  * TriggerController.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| 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); | ||||
|     } | ||||
| } | ||||
| @@ -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']) { | ||||
|   | ||||
| @@ -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; | ||||
|                 } | ||||
|   | ||||
| @@ -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] = [ | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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 */ | ||||
|   | ||||
| @@ -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', | ||||
|         ]; | ||||
| 
 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
							
								
								
									
										99
									
								
								app/Api/V1/Requests/Models/Account/ShowRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								app/Api/V1/Requests/Models/Account/ShowRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| /* | ||||
|  * ShowRequest.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| 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')); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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<string, mixed> | ||||
|      */ | ||||
|     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<string, string> | ||||
|      */ | ||||
|     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])); | ||||
|                     } | ||||
|                 } | ||||
|   | ||||
| @@ -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')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -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')); | ||||
| 
 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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')); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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')); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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()));
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -57,8 +57,6 @@ class CorrectsPiggyBanks extends Command | ||||
|                 $event->transaction_journal_id = null; | ||||
|                 $event->save(); | ||||
|                 ++$count; | ||||
| 
 | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|         if (0 !== $count) { | ||||
|   | ||||
| @@ -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.'); | ||||
|   | ||||
| @@ -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 */ | ||||
|   | ||||
| @@ -71,7 +71,6 @@ class RestoresOAuthKeys extends Command | ||||
|             $this->storeKeysInDB(); | ||||
|             $this->friendlyInfo('Stored OAuth keys in database.'); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| /* | ||||
|  * ValidatesEnvironmentVariables.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org. | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
| @@ -18,9 +18,11 @@ declare(strict_types=1); | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| 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'; | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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('<fg=%s>              ______ _           __ _            _____ _____ _____  </>', $colors[0])); | ||||
|         $this->line(sprintf('<fg=%s>             |  ____(_)         / _| |          |_   _|_   _|_   _| </>', $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])); | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| /* | ||||
|  * RecalculatesRunningBalance.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org. | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
| @@ -18,9 +18,11 @@ declare(strict_types=1); | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| 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 | ||||
|   | ||||
| @@ -1,5 +1,26 @@ | ||||
| <?php | ||||
| 
 | ||||
| 
 | ||||
| /* | ||||
|  * ResetsErrorMailLimit.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\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; | ||||
|   | ||||
| @@ -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)); | ||||
|         } | ||||
|   | ||||
| @@ -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); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -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'; | ||||
|   | ||||
| @@ -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     = []; | ||||
|   | ||||
| @@ -157,7 +157,6 @@ class UpgradesLiabilitiesEight extends Command | ||||
|             $service = new TransactionGroupDestroyService(); | ||||
|             $service->destroy($group); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										116
									
								
								app/Console/Commands/Upgrade/UpgradesWebhooks.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								app/Console/Commands/Upgrade/UpgradesWebhooks.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| <?php | ||||
| 
 | ||||
| 
 | ||||
| /* | ||||
|  * UpgradesWebhooks.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\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); | ||||
|     } | ||||
| } | ||||
| @@ -32,5 +32,6 @@ enum WebhookResponse: int | ||||
|     case TRANSACTIONS = 200; | ||||
|     case ACCOUNTS     = 210; | ||||
|     case BUDGET       = 230; | ||||
|     case RELEVANT     = 240; | ||||
|     case NONE         = 220; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -1,6 +1,27 @@ | ||||
| <?php | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* | ||||
|  * WarnUserAboutBill.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Events\Model\Bill; | ||||
|   | ||||
| @@ -1,5 +1,26 @@ | ||||
| <?php | ||||
| 
 | ||||
| 
 | ||||
| /* | ||||
|  * WarnUserAboutOverdueSubscriptions.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Events\Model\Bill; | ||||
|   | ||||
| @@ -1,5 +1,26 @@ | ||||
| <?php | ||||
| 
 | ||||
| 
 | ||||
| /* | ||||
|  * ChangedName.php | ||||
|  * Copyright (c) 2025 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Events\Model\PiggyBank; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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)); | ||||
| 
 | ||||
|   | ||||
| @@ -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] ??= []; | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
| } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user