mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-11-04 05:15:39 +00:00 
			
		
		
		
	Compare commits
	
		
			178 Commits
		
	
	
		
			develop-20
			...
			develop-20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a51501025b | ||
| 
						 | 
					a9d26e4586 | ||
| 
						 | 
					949691935f | ||
| 
						 | 
					4835b05304 | ||
| 
						 | 
					cce5a73dd2 | ||
| 
						 | 
					3152f635dd | ||
| 
						 | 
					12e8651017 | ||
| 
						 | 
					f88286c848 | ||
| 
						 | 
					39cf0533d9 | ||
| 
						 | 
					6d82887960 | ||
| 
						 | 
					262f1bae34 | ||
| 
						 | 
					75dfdcc220 | ||
| 
						 | 
					c3a3bdf525 | ||
| 
						 | 
					602df95f3c | ||
| 
						 | 
					296a64e284 | ||
| 
						 | 
					b87b99a755 | ||
| 
						 | 
					bf53f5d6b7 | ||
| 
						 | 
					bcbb868acc | ||
| 
						 | 
					b3a9e6569e | ||
| 
						 | 
					f174f124ef | ||
| 
						 | 
					8745377f31 | ||
| 
						 | 
					cf76e93f31 | ||
| 
						 | 
					19e36f6f6e | ||
| 
						 | 
					2a7bb6f048 | ||
| 
						 | 
					536eacbc0c | ||
| 
						 | 
					af78158d0b | ||
| 
						 | 
					fb97910a34 | ||
| 
						 | 
					66b4d14129 | ||
| 
						 | 
					fd53047dbc | ||
| 
						 | 
					5f6fc1bab4 | ||
| 
						 | 
					cd0b64bd24 | ||
| 
						 | 
					91b26bce0e | ||
| 
						 | 
					26a8bd921d | ||
| 
						 | 
					ad77d55c16 | ||
| 
						 | 
					445a383dd0 | ||
| 
						 | 
					1666af939e | ||
| 
						 | 
					35447f5eee | ||
| 
						 | 
					7c6fcb5731 | ||
| 
						 | 
					fabd9bf765 | ||
| 
						 | 
					6352d26633 | ||
| 
						 | 
					ebef145bd6 | ||
| 
						 | 
					acc89eb5f9 | ||
| 
						 | 
					6523596415 | ||
| 
						 | 
					b6c2d23116 | ||
| 
						 | 
					2a123354f9 | ||
| 
						 | 
					1e7ea4b76c | ||
| 
						 | 
					d959526eb3 | ||
| 
						 | 
					8846ee9091 | ||
| 
						 | 
					6eb8d0fc8c | ||
| 
						 | 
					1b0e16b6a5 | ||
| 
						 | 
					2e4df28288 | ||
| 
						 | 
					f3b7a3015d | ||
| 
						 | 
					5de5e08b1d | ||
| 
						 | 
					0a116cd04c | ||
| 
						 | 
					fd32a692c1 | ||
| 
						 | 
					1ac762aba8 | ||
| 
						 | 
					315dc532b6 | ||
| 
						 | 
					e19ed1be15 | ||
| 
						 | 
					cbb0621fd9 | ||
| 
						 | 
					049cbab861 | ||
| 
						 | 
					28b620fb5c | ||
| 
						 | 
					c183f91ff6 | ||
| 
						 | 
					172efae41c | ||
| 
						 | 
					756e857ba0 | ||
| 
						 | 
					1cde7aab0c | ||
| 
						 | 
					2d67eece5d | ||
| 
						 | 
					b1f79c4c0f | ||
| 
						 | 
					33bd2ceae8 | ||
| 
						 | 
					e68850f192 | ||
| 
						 | 
					450ac7e6ee | ||
| 
						 | 
					91f52b5dbc | ||
| 
						 | 
					eed2405d76 | ||
| 
						 | 
					c956df7790 | ||
| 
						 | 
					0fdccec6a8 | ||
| 
						 | 
					8ded54d7a8 | ||
| 
						 | 
					bb1b4ca5ca | ||
| 
						 | 
					e90d60113b | ||
| 
						 | 
					d95dada0e0 | ||
| 
						 | 
					8722456595 | ||
| 
						 | 
					b5ad226451 | ||
| 
						 | 
					ddb0e66651 | ||
| 
						 | 
					e802899608 | ||
| 
						 | 
					0894d3bf42 | ||
| 
						 | 
					80bcfd3bcd | ||
| 
						 | 
					8c410f42bd | ||
| 
						 | 
					7a1021dffc | ||
| 
						 | 
					63883c9a84 | ||
| 
						 | 
					50d7f9d1ec | ||
| 
						 | 
					ebc7ea0eb6 | ||
| 
						 | 
					b905efd0aa | ||
| 
						 | 
					93085599b7 | ||
| 
						 | 
					8a8bbaf827 | ||
| 
						 | 
					96a66b894a | ||
| 
						 | 
					1ba641c279 | ||
| 
						 | 
					6c5ddfcb8a | ||
| 
						 | 
					65ddc246dc | ||
| 
						 | 
					e4aff5ff4c | ||
| 
						 | 
					dfece91541 | ||
| 
						 | 
					6ddda13c3a | ||
| 
						 | 
					46219c4678 | ||
| 
						 | 
					a1595d0647 | ||
| 
						 | 
					4ee9f9bb27 | ||
| 
						 | 
					bcaa0bddea | ||
| 
						 | 
					01cce49070 | ||
| 
						 | 
					293be04d40 | ||
| 
						 | 
					44a00ec8eb | ||
| 
						 | 
					d84e772945 | ||
| 
						 | 
					b475f6c51d | ||
| 
						 | 
					2712662510 | ||
| 
						 | 
					1f2eeba862 | ||
| 
						 | 
					46a60134f4 | ||
| 
						 | 
					f3f7820816 | ||
| 
						 | 
					a57f8076b2 | ||
| 
						 | 
					517afa2273 | ||
| 
						 | 
					2142b23aec | ||
| 
						 | 
					1bec43b111 | ||
| 
						 | 
					d2978a5ee8 | ||
| 
						 | 
					39b61c71e8 | ||
| 
						 | 
					fa2c964790 | ||
| 
						 | 
					134aeb3a46 | ||
| 
						 | 
					6f6e1a4ff4 | ||
| 
						 | 
					b743bf3d9e | ||
| 
						 | 
					84ee6f16c9 | ||
| 
						 | 
					9fe39e42b3 | ||
| 
						 | 
					4013c7e316 | ||
| 
						 | 
					0b76747531 | ||
| 
						 | 
					3129756b37 | ||
| 
						 | 
					b0df383004 | ||
| 
						 | 
					9c5b1df86c | ||
| 
						 | 
					5854e24775 | ||
| 
						 | 
					3f873422f2 | ||
| 
						 | 
					f898990773 | ||
| 
						 | 
					a5759ab1c6 | ||
| 
						 | 
					9bd6417e02 | ||
| 
						 | 
					cd12a10214 | ||
| 
						 | 
					ee8cb62e04 | ||
| 
						 | 
					87ee95a852 | ||
| 
						 | 
					10f8436885 | ||
| 
						 | 
					6955846a1c | ||
| 
						 | 
					11d2f8d471 | ||
| 
						 | 
					99347ffff1 | ||
| 
						 | 
					3ddc11a905 | ||
| 
						 | 
					0a48c0c20f | ||
| 
						 | 
					8bc764d6ef | ||
| 
						 | 
					4b4f568558 | ||
| 
						 | 
					d42117281a | ||
| 
						 | 
					d68ed5a713 | ||
| 
						 | 
					2f6f36c3f0 | ||
| 
						 | 
					984aa02e35 | ||
| 
						 | 
					cdadc7d533 | ||
| 
						 | 
					4d59955cc5 | ||
| 
						 | 
					fc547ba59a | ||
| 
						 | 
					1b2ded3167 | ||
| 
						 | 
					dcf20a472a | ||
| 
						 | 
					7bd528defe | ||
| 
						 | 
					9e6d123165 | ||
| 
						 | 
					c592f51c83 | ||
| 
						 | 
					fbb6f30366 | ||
| 
						 | 
					4546c721fb | ||
| 
						 | 
					3a659c9a81 | ||
| 
						 | 
					e28a988eb3 | ||
| 
						 | 
					a29742fe1f | ||
| 
						 | 
					dd06838eec | ||
| 
						 | 
					5eb828bff8 | ||
| 
						 | 
					c7f3701053 | ||
| 
						 | 
					ab6799442c | ||
| 
						 | 
					1fe9bf7d76 | ||
| 
						 | 
					bd14797da6 | ||
| 
						 | 
					93b4e6a8d0 | ||
| 
						 | 
					5566671971 | ||
| 
						 | 
					defcc7a00c | ||
| 
						 | 
					5183de634b | ||
| 
						 | 
					7771b0311c | ||
| 
						 | 
					52abe3bbc2 | ||
| 
						 | 
					34db9f41c2 | ||
| 
						 | 
					72c31fbe6a | ||
| 
						 | 
					8bd44f429b | ||
| 
						 | 
					fb7866b165 | 
							
								
								
									
										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.1",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
 | 
			
		||||
                "reference": "4a952bd19dc97879b0620f495552ef09b55f7d36"
 | 
			
		||||
                "reference": "2f5170365e2a422d0c5421f9c8818b2c078105f6"
 | 
			
		||||
            },
 | 
			
		||||
            "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/2f5170365e2a422d0c5421f9c8818b2c078105f6",
 | 
			
		||||
                "reference": "2f5170365e2a422d0c5421f9c8818b2c078105f6",
 | 
			
		||||
                "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.1"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -507,7 +502,7 @@
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2025-08-13T22:36:21+00:00"
 | 
			
		||||
            "time": "2025-09-02T15:27:36+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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/label-actions.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/label-actions.yml
									
									
									
									
										vendored
									
									
								
							@@ -25,7 +25,7 @@ feature:
 | 
			
		||||
 | 
			
		||||
      This issue has been marked as a feature request.
 | 
			
		||||
 | 
			
		||||
      If you come across this issue, please be aware there is NO need to reply with "+1" or "me too" or "I need this too" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted. You can subscribe to this issue to get updates.
 | 
			
		||||
      If you come across this issue, please be aware there is NO need to reply with "+1" or "I need this too" or "any updates?" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted. You can subscribe to this issue to get updates.
 | 
			
		||||
 | 
			
		||||
      Thank you for your contributions.
 | 
			
		||||
 | 
			
		||||
@@ -39,7 +39,7 @@ epic:
 | 
			
		||||
 | 
			
		||||
      This issue has been marked as an epic. In epics, large amounts of works are collected that will be part of a major new feature. If you have more ideas that could be a part of this epic, feel free to reply.
 | 
			
		||||
 | 
			
		||||
      *However*, please be aware there is NO need to reply with "+1" or "me too" or "I need this too" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted.
 | 
			
		||||
      *However*, please be aware there is NO need to reply with "+1" or "I need this too" or "any updates?" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted.
 | 
			
		||||
 | 
			
		||||
      If you are merely interested in this epic's progress, you can subscribe to this issue to get updates.
 | 
			
		||||
 | 
			
		||||
@@ -56,7 +56,7 @@ enhancement:
 | 
			
		||||
 | 
			
		||||
      This issue has been marked as an enhancement.
 | 
			
		||||
 | 
			
		||||
      If you come across this issue, please be aware there is NO need to reply with "+1" or "me too" or "I need this too" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted. You can subscribe to this issue to get updates.
 | 
			
		||||
      If you come across this issue, please be aware there is NO need to reply with "+1" or "I need this too" or "any updates?" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted. You can subscribe to this issue to get updates.
 | 
			
		||||
 | 
			
		||||
      Thank you for your contributions.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/depsreview.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/depsreview.yml
									
									
									
									
										vendored
									
									
								
							@@ -9,7 +9,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: 'Checkout repository'
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
        uses: actions/checkout@v5
 | 
			
		||||
        with:
 | 
			
		||||
          fetch-depth: 0
 | 
			
		||||
      - name: 'Dependency review'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							@@ -21,7 +21,7 @@ jobs:
 | 
			
		||||
      discussions: write
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: dessant/lock-threads@v5
 | 
			
		||||
      - uses: JC5/lock-threads@v6.0.6
 | 
			
		||||
        with:
 | 
			
		||||
          issue-inactive-days: 21
 | 
			
		||||
          pr-inactive-days: 21
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -50,7 +50,7 @@ jobs:
 | 
			
		||||
          git pull
 | 
			
		||||
          echo "Current branch is $(git branch --show-current)"
 | 
			
		||||
        env:
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
 | 
			
		||||
      - name: Configure Git
 | 
			
		||||
        run: |
 | 
			
		||||
          # do some configuration
 | 
			
		||||
@@ -118,7 +118,7 @@ jobs:
 | 
			
		||||
        env:
 | 
			
		||||
          FIREFLY_III_ROOT: /github/workspace
 | 
			
		||||
          GH_TOKEN: ""
 | 
			
		||||
          FF_III_VERSION: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
 | 
			
		||||
          FF_III_VERSION: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
 | 
			
		||||
      - name: Generate JSON v1
 | 
			
		||||
        id: json-v1
 | 
			
		||||
        uses: JC5/firefly-iii-dev@main
 | 
			
		||||
@@ -221,7 +221,7 @@ jobs:
 | 
			
		||||
          echo "tarName=$tarName" >> "$GITHUB_ENV"
 | 
			
		||||
          echo "BRANCH_NAME=$BRANCH_NAME" >> "$GITHUB_ENV"
 | 
			
		||||
        env:
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
 | 
			
		||||
      - name: Commit all changes
 | 
			
		||||
        run: |
 | 
			
		||||
          # add all content, except output.txt (this contains the changelog and/or the download instructions)
 | 
			
		||||
@@ -232,12 +232,12 @@ jobs:
 | 
			
		||||
          git commit -m "🤖 Auto commit for release '$version' on $(date +'%Y-%m-%d')" || true
 | 
			
		||||
          git push
 | 
			
		||||
        env:
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
 | 
			
		||||
      - name: Generate release description
 | 
			
		||||
        id: release-description
 | 
			
		||||
        uses: JC5/firefly-iii-dev@main
 | 
			
		||||
        with:
 | 
			
		||||
          action: "ff3:generate-release-notes firefly-iii ${{ github.event.inputs.version }}"
 | 
			
		||||
          action: "ff3:generate-release-notes firefly-iii ${{ inputs.version || 'develop' }}"
 | 
			
		||||
          output: 'output'
 | 
			
		||||
        env:
 | 
			
		||||
          FIREFLY_III_ROOT: /github/workspace
 | 
			
		||||
@@ -291,7 +291,7 @@ jobs:
 | 
			
		||||
          echo "DONE!"
 | 
			
		||||
        env:
 | 
			
		||||
          GH_TOKEN: ${{ github.token }}
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
 | 
			
		||||
      - name: Create archives
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "Create zip file $zipName"
 | 
			
		||||
@@ -375,7 +375,7 @@ jobs:
 | 
			
		||||
          fi
 | 
			
		||||
        env:
 | 
			
		||||
          GH_TOKEN: ${{ github.token }}
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
 | 
			
		||||
      - name: Upload artifacts
 | 
			
		||||
        run: |
 | 
			
		||||
          # add zip file to release.
 | 
			
		||||
@@ -411,4 +411,4 @@ jobs:
 | 
			
		||||
          rm -f $tarName.sha256
 | 
			
		||||
        env:
 | 
			
		||||
          GH_TOKEN: ${{ github.token }}
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
 | 
			
		||||
          version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -49,8 +49,6 @@ class TransactionTypeController extends Controller
 | 
			
		||||
            function ($request, $next) {
 | 
			
		||||
                $this->validateUserGroup($request);
 | 
			
		||||
                $this->repository = app(TransactionTypeRepositoryInterface::class);
 | 
			
		||||
                $this->repository->setUser($this->user);
 | 
			
		||||
                $this->repository->setUserGroup($this->userGroup);
 | 
			
		||||
 | 
			
		||||
                return $next($request);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -130,6 +130,7 @@ class AccountController extends Controller
 | 
			
		||||
            'yAxisID'                 => 0,
 | 
			
		||||
            'period'                  => '1D',
 | 
			
		||||
            'entries'                 => [],
 | 
			
		||||
            'pc_entries'              => [],
 | 
			
		||||
        ];
 | 
			
		||||
        if ($this->convertToPrimary) {
 | 
			
		||||
            $currentSet['pc_entries']                      = [];
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
@@ -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([$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([$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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -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'];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -96,7 +98,6 @@ class ShowController extends Controller
 | 
			
		||||
        $paginator    = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
 | 
			
		||||
        $paginator->setPath(route('api.v1.budgets.limits.index', [$budget->id]).$this->buildParams());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // enrich
 | 
			
		||||
        $enrichment   = new BudgetLimitEnrichment();
 | 
			
		||||
        $enrichment->setUser($admin);
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -98,10 +99,7 @@ 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) {
 | 
			
		||||
                // update existing rate.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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] = [
 | 
			
		||||
 
 | 
			
		||||
@@ -24,15 +24,18 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Api\V1\Controllers\System;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Support\Facades\FireflyConfig;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
use Illuminate\Support\Facades\Validator;
 | 
			
		||||
use FireflyIII\Api\V1\Controllers\Controller;
 | 
			
		||||
use FireflyIII\Api\V1\Requests\System\UpdateRequest;
 | 
			
		||||
use FireflyIII\Enums\WebhookDelivery;
 | 
			
		||||
use FireflyIII\Enums\WebhookResponse;
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
use FireflyIII\Exceptions\FireflyException;
 | 
			
		||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
 | 
			
		||||
use FireflyIII\Support\Binder\EitherConfigKey;
 | 
			
		||||
use FireflyIII\Support\Facades\FireflyConfig;
 | 
			
		||||
use Illuminate\Http\JsonResponse;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
use Illuminate\Support\Facades\Validator;
 | 
			
		||||
use Illuminate\Validation\ValidationException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -107,8 +110,8 @@ class ConfigurationController extends Controller
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'is_demo_site'            => $isDemoSite?->data,
 | 
			
		||||
            'permission_update_check' => null === $updateCheck ? null : (int) $updateCheck->data,
 | 
			
		||||
            'last_update_check'       => null === $lastCheck ? null : (int) $lastCheck->data,
 | 
			
		||||
            'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data,
 | 
			
		||||
            'last_update_check'       => null === $lastCheck ? null : (int)$lastCheck->data,
 | 
			
		||||
            'single_user_mode'        => $singleUser?->data,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
@@ -139,15 +142,26 @@ class ConfigurationController extends Controller
 | 
			
		||||
                'value'    => $dynamic[$shortKey],
 | 
			
		||||
                'editable' => true,
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
 | 
			
		||||
        }
 | 
			
		||||
        if (!str_starts_with($configKey, 'configuration.')) {
 | 
			
		||||
        if (str_starts_with($configKey, 'webhook.')) {
 | 
			
		||||
            $data = [
 | 
			
		||||
                'title'    => $configKey,
 | 
			
		||||
                'value'    => config($configKey),
 | 
			
		||||
                'value'    => $this->getWebhookConfiguration($configKey),
 | 
			
		||||
                'editable' => false,
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // fallback
 | 
			
		||||
        $data     = [
 | 
			
		||||
            'title'    => $configKey,
 | 
			
		||||
            'value'    => config($shortKey),
 | 
			
		||||
            'editable' => false,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -182,4 +196,39 @@ class ConfigurationController extends Controller
 | 
			
		||||
 | 
			
		||||
        return response()->api(['data' => $data])->header('Content-Type', self::CONTENT_TYPE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getWebhookConfiguration(string $configKey): array
 | 
			
		||||
    {
 | 
			
		||||
        switch ($configKey) {
 | 
			
		||||
            case 'webhook.triggers':
 | 
			
		||||
                $cases = WebhookTrigger::cases();
 | 
			
		||||
                $data  = [];
 | 
			
		||||
                foreach ($cases as $c) {
 | 
			
		||||
                    $data[$c->name] = $c->value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return $data;
 | 
			
		||||
 | 
			
		||||
            case 'webhook.responses':
 | 
			
		||||
                $cases = WebhookResponse::cases();
 | 
			
		||||
                $data  = [];
 | 
			
		||||
                foreach ($cases as $c) {
 | 
			
		||||
                    $data[$c->name] = $c->value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return $data;
 | 
			
		||||
 | 
			
		||||
            case 'webhook.deliveries':
 | 
			
		||||
                $cases = WebhookDelivery::cases();
 | 
			
		||||
                $data  = [];
 | 
			
		||||
                foreach ($cases as $c) {
 | 
			
		||||
                    $data[$c->name] = $c->value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return $data;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                throw new FireflyException(sprintf('Unknown webhook configuration key "%s".', $configKey));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,13 +25,16 @@ declare(strict_types=1);
 | 
			
		||||
namespace FireflyIII\Api\V1\Controllers\Webhook;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Api\V1\Controllers\Controller;
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
use FireflyIII\Events\RequestedSendWebhookMessages;
 | 
			
		||||
use FireflyIII\Exceptions\FireflyException;
 | 
			
		||||
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;
 | 
			
		||||
@@ -89,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);
 | 
			
		||||
@@ -116,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);
 | 
			
		||||
@@ -146,7 +163,7 @@ class ShowController extends Controller
 | 
			
		||||
        $engine->setUser(auth()->user());
 | 
			
		||||
 | 
			
		||||
        // tell the generator which trigger it should look for
 | 
			
		||||
        $engine->setTrigger($webhook->trigger);
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::tryFrom($webhook->trigger));
 | 
			
		||||
        // tell the generator which objects to process
 | 
			
		||||
        $engine->setObjects(new Collection([$group]));
 | 
			
		||||
        // set the webhook to trigger
 | 
			
		||||
@@ -155,7 +172,7 @@ class ShowController extends Controller
 | 
			
		||||
        $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 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\Validation\Validator;
 | 
			
		||||
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,7 +55,7 @@ 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);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,17 +24,22 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
use Illuminate\Validation\Validator;
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
                        $to = 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 FireflyIII\Support\Facades\Amount;
 | 
			
		||||
use Illuminate\Validation\Validator;
 | 
			
		||||
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',
 | 
			
		||||
@@ -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,10 +24,12 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Exceptions\FireflyException;
 | 
			
		||||
use FireflyIII\Models\Webhook;
 | 
			
		||||
use FireflyIII\Rules\IsBoolean;
 | 
			
		||||
use FireflyIII\Support\Request\ChecksLogin;
 | 
			
		||||
use FireflyIII\Support\Request\ConvertsDataTypes;
 | 
			
		||||
use FireflyIII\Support\Request\ValidatesWebhooks;
 | 
			
		||||
use Illuminate\Foundation\Http\FormRequest;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -37,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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -67,18 +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)],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,10 +24,12 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Exceptions\FireflyException;
 | 
			
		||||
use FireflyIII\Models\Webhook;
 | 
			
		||||
use FireflyIII\Rules\IsBoolean;
 | 
			
		||||
use FireflyIII\Support\Request\ChecksLogin;
 | 
			
		||||
use FireflyIII\Support\Request\ConvertsDataTypes;
 | 
			
		||||
use FireflyIII\Support\Request\ValidatesWebhooks;
 | 
			
		||||
use Illuminate\Foundation\Http\FormRequest;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -37,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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -77,21 +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)],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -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';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -133,6 +133,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 +241,38 @@ 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!',
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $random    = random_int(0, count($lines) - 1);
 | 
			
		||||
        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     = [];
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -31,5 +31,7 @@ enum WebhookResponse: int
 | 
			
		||||
{
 | 
			
		||||
    case TRANSACTIONS = 200;
 | 
			
		||||
    case ACCOUNTS     = 210;
 | 
			
		||||
    case BUDGET       = 230;
 | 
			
		||||
    case RELEVANT     = 240;
 | 
			
		||||
    case NONE         = 220;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,10 +29,12 @@ namespace FireflyIII\Enums;
 | 
			
		||||
 */
 | 
			
		||||
enum WebhookTrigger: int
 | 
			
		||||
{
 | 
			
		||||
    case STORE_TRANSACTION   = 100;
 | 
			
		||||
    // case BEFORE_STORE_TRANSACTION = 101;
 | 
			
		||||
    case UPDATE_TRANSACTION  = 110;
 | 
			
		||||
    // case BEFORE_UPDATE_TRANSACTION = 111;
 | 
			
		||||
    case DESTROY_TRANSACTION = 120;
 | 
			
		||||
    // case BEFORE_DESTROY_TRANSACTION = 121;
 | 
			
		||||
    case ANY                       = 50;
 | 
			
		||||
    case STORE_TRANSACTION         = 100;
 | 
			
		||||
    case UPDATE_TRANSACTION        = 110;
 | 
			
		||||
    case DESTROY_TRANSACTION       = 120;
 | 
			
		||||
    case STORE_BUDGET              = 200;
 | 
			
		||||
    case UPDATE_BUDGET             = 210;
 | 
			
		||||
    case DESTROY_BUDGET            = 220;
 | 
			
		||||
    case STORE_UPDATE_BUDGET_LIMIT = 230;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,39 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Updated.php
 | 
			
		||||
 * Copyright (c) 2023 james@firefly-iii.org
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of Firefly III (https://github.com/firefly-iii).
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 * published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Events\Model\BudgetLimit;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Events\Event;
 | 
			
		||||
use FireflyIII\Models\BudgetLimit;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class Updated
 | 
			
		||||
 */
 | 
			
		||||
class Updated extends Event
 | 
			
		||||
{
 | 
			
		||||
    use SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public function __construct(public BudgetLimit $budgetLimit) {}
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -170,7 +170,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ namespace FireflyIII\Factory;
 | 
			
		||||
use FireflyIII\Exceptions\FireflyException;
 | 
			
		||||
use FireflyIII\Models\TransactionCurrency;
 | 
			
		||||
use Illuminate\Database\QueryException;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class TransactionCurrencyFactory
 | 
			
		||||
@@ -48,7 +49,7 @@ class TransactionCurrencyFactory
 | 
			
		||||
        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 +65,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);
 | 
			
		||||
        }
 | 
			
		||||
@@ -79,7 +80,7 @@ class TransactionCurrencyFactory
 | 
			
		||||
        $currencyId   = (int) $currencyId;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -90,7 +91,7 @@ class TransactionCurrencyFactory
 | 
			
		||||
            if (null !== $currency) {
 | 
			
		||||
                return $currency;
 | 
			
		||||
            }
 | 
			
		||||
            app('log')->warning(sprintf('Currency ID is %d but found nothing!', $currencyId));
 | 
			
		||||
            Log::warning(sprintf('Currency ID is %d but found nothing!', $currencyId));
 | 
			
		||||
        }
 | 
			
		||||
        // then by code:
 | 
			
		||||
        if ('' !== $currencyCode) {
 | 
			
		||||
@@ -98,9 +99,9 @@ class TransactionCurrencyFactory
 | 
			
		||||
            if (null !== $currency) {
 | 
			
		||||
                return $currency;
 | 
			
		||||
            }
 | 
			
		||||
            app('log')->warning(sprintf('Currency code is %d but found nothing!', $currencyCode));
 | 
			
		||||
            Log::warning(sprintf('Currency code is %d but found nothing!', $currencyCode));
 | 
			
		||||
        }
 | 
			
		||||
        app('log')->warning('Found nothing for currency.');
 | 
			
		||||
        Log::warning('Found nothing for currency.');
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Generator\Webhook;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
use FireflyIII\User;
 | 
			
		||||
use Illuminate\Support\Collection;
 | 
			
		||||
 | 
			
		||||
@@ -38,7 +39,7 @@ interface MessageGeneratorInterface
 | 
			
		||||
 | 
			
		||||
    public function setObjects(Collection $objects): void;
 | 
			
		||||
 | 
			
		||||
    public function setTrigger(int $trigger): void;
 | 
			
		||||
    public function setTrigger(WebhookTrigger $trigger): void;
 | 
			
		||||
 | 
			
		||||
    public function setUser(User $user): void;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,13 +27,21 @@ namespace FireflyIII\Generator\Webhook;
 | 
			
		||||
use FireflyIII\Enums\WebhookResponse;
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
use FireflyIII\Exceptions\FireflyException;
 | 
			
		||||
use FireflyIII\Models\Budget;
 | 
			
		||||
use FireflyIII\Models\BudgetLimit;
 | 
			
		||||
use FireflyIII\Models\Transaction;
 | 
			
		||||
use FireflyIII\Models\TransactionGroup;
 | 
			
		||||
use FireflyIII\Models\TransactionJournal;
 | 
			
		||||
use FireflyIII\Models\Webhook;
 | 
			
		||||
use FireflyIII\Models\WebhookMessage;
 | 
			
		||||
use FireflyIII\Models\WebhookResponse as WebhookResponseModel;
 | 
			
		||||
use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel;
 | 
			
		||||
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
 | 
			
		||||
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
 | 
			
		||||
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
 | 
			
		||||
use FireflyIII\Transformers\AccountTransformer;
 | 
			
		||||
use FireflyIII\Transformers\BudgetLimitTransformer;
 | 
			
		||||
use FireflyIII\Transformers\BudgetTransformer;
 | 
			
		||||
use FireflyIII\Transformers\TransactionGroupTransformer;
 | 
			
		||||
use FireflyIII\User;
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
@@ -47,11 +55,11 @@ use Symfony\Component\HttpFoundation\ParameterBag;
 | 
			
		||||
 */
 | 
			
		||||
class StandardMessageGenerator implements MessageGeneratorInterface
 | 
			
		||||
{
 | 
			
		||||
    private Collection $objects;
 | 
			
		||||
    private int        $trigger;
 | 
			
		||||
    private User       $user;
 | 
			
		||||
    private int        $version = 0;
 | 
			
		||||
    private Collection $webhooks;
 | 
			
		||||
    private Collection     $objects;
 | 
			
		||||
    private WebhookTrigger $trigger;
 | 
			
		||||
    private User           $user;
 | 
			
		||||
    private int            $version = 0;
 | 
			
		||||
    private Collection     $webhooks;
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
@@ -68,17 +76,24 @@ class StandardMessageGenerator implements MessageGeneratorInterface
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // do some debugging
 | 
			
		||||
        Log::debug(
 | 
			
		||||
            sprintf('StandardMessageGenerator will generate messages for %d object(s) and %d webhook(s).', $this->objects->count(), $this->webhooks->count())
 | 
			
		||||
        );
 | 
			
		||||
        Log::debug(sprintf('StandardMessageGenerator will generate messages for %d object(s) and %d webhook(s).', $this->objects->count(), $this->webhooks->count()));
 | 
			
		||||
        $this->run();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getWebhooks(): Collection
 | 
			
		||||
    {
 | 
			
		||||
        return $this->user->webhooks()->where('active', true)->where('trigger', $this->trigger)->get(['webhooks.*']);
 | 
			
		||||
        return $this->user->webhooks()
 | 
			
		||||
            ->leftJoin('webhook_webhook_trigger', 'webhook_webhook_trigger.webhook_id', 'webhooks.id')
 | 
			
		||||
            ->leftJoin('webhook_triggers', 'webhook_webhook_trigger.webhook_trigger_id', 'webhook_triggers.id')
 | 
			
		||||
            ->where('active', true)
 | 
			
		||||
            ->whereIn('webhook_triggers.title', [$this->trigger->name, WebhookTrigger::ANY->name])
 | 
			
		||||
            ->get(['webhooks.*'])
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @throws FireflyException
 | 
			
		||||
     */
 | 
			
		||||
    private function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug('Now in StandardMessageGenerator::run');
 | 
			
		||||
@@ -108,53 +123,97 @@ class StandardMessageGenerator implements MessageGeneratorInterface
 | 
			
		||||
     */
 | 
			
		||||
    private function generateMessage(Webhook $webhook, Model $model): void
 | 
			
		||||
    {
 | 
			
		||||
        $class        = $model::class;
 | 
			
		||||
        $class         = $model::class;
 | 
			
		||||
        // Line is ignored because all of Firefly III's Models have an id property.
 | 
			
		||||
        Log::debug(sprintf('Now in generateMessage(#%d, %s#%d)', $webhook->id, $class, $model->id));
 | 
			
		||||
        $uuid          = Uuid::uuid4();
 | 
			
		||||
 | 
			
		||||
        $uuid         = Uuid::uuid4();
 | 
			
		||||
        $basicMessage = [
 | 
			
		||||
            'uuid'     => $uuid->toString(),
 | 
			
		||||
            'user_id'  => 0,
 | 
			
		||||
            'trigger'  => WebhookTrigger::from($webhook->trigger)->name,
 | 
			
		||||
            'response' => WebhookResponse::from($webhook->response)->name,
 | 
			
		||||
            'url'      => $webhook->url,
 | 
			
		||||
            'version'  => sprintf('v%d', $this->getVersion()),
 | 
			
		||||
            'content'  => [],
 | 
			
		||||
        /** @var WebhookResponseModel $response */
 | 
			
		||||
        $response      = $webhook->webhookResponses()->first();
 | 
			
		||||
        $triggers      = $this->getTriggerTitles($webhook->webhookTriggers()->get());
 | 
			
		||||
        $basicMessage  = [
 | 
			
		||||
            'uuid'          => $uuid->toString(),
 | 
			
		||||
            'user_id'       => 0,
 | 
			
		||||
            'user_group_id' => 0,
 | 
			
		||||
            'trigger'       => $this->trigger->name,
 | 
			
		||||
            'response'      => $response->title, // guess that the database is correct.
 | 
			
		||||
            'url'           => $webhook->url,
 | 
			
		||||
            'version'       => sprintf('v%d', $this->getVersion()),
 | 
			
		||||
            'content'       => [],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // depends on the model how user_id is set:
 | 
			
		||||
        switch ($class) {
 | 
			
		||||
            default:
 | 
			
		||||
                // Line is ignored because all of Firefly III's Models have an id property.
 | 
			
		||||
                Log::error(
 | 
			
		||||
                    sprintf('Webhook #%d was given %s#%d to deal with but can\'t extract user ID from it.', $webhook->id, $class, $model->id)
 | 
			
		||||
                );
 | 
			
		||||
                Log::error(sprintf('Webhook #%d was given %s#%d to deal with but can\'t extract user ID from it.', $webhook->id, $class, $model->id));
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            case Budget::class:
 | 
			
		||||
                /** @var Budget $model */
 | 
			
		||||
                $basicMessage['user_id']       = $model->user_id;
 | 
			
		||||
                $basicMessage['user_group_id'] = $model->user_group_id;
 | 
			
		||||
                $relevantResponse              = WebhookResponse::BUDGET->name;
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case BudgetLimit::class:
 | 
			
		||||
                $basicMessage['user_id']       = $model->budget->user_id;
 | 
			
		||||
                $basicMessage['user_group_id'] = $model->budget->user_group_id;
 | 
			
		||||
                $relevantResponse              = WebhookResponse::BUDGET->name;
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case TransactionGroup::class:
 | 
			
		||||
                /** @var TransactionGroup $model */
 | 
			
		||||
                $basicMessage['user_id'] = $model->user->id;
 | 
			
		||||
                $basicMessage['user_id']       = $model->user_id;
 | 
			
		||||
                $basicMessage['user_group_id'] = $model->user_group_id;
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        $responseTitle = $this->getRelevantResponse($triggers, $response, $class);
 | 
			
		||||
 | 
			
		||||
        // then depends on the response what to put in the message:
 | 
			
		||||
        switch ($webhook->response) {
 | 
			
		||||
        switch ($responseTitle) {
 | 
			
		||||
            default:
 | 
			
		||||
                Log::error(
 | 
			
		||||
                    sprintf('The response code for webhook #%d is "%d" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response)
 | 
			
		||||
                );
 | 
			
		||||
                Log::error(sprintf('The response code for webhook #%d is "%s" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response));
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            case WebhookResponse::NONE->value:
 | 
			
		||||
            case WebhookResponse::BUDGET->name:
 | 
			
		||||
                $basicMessage['content'] = [];
 | 
			
		||||
                if ($model instanceof Budget) {
 | 
			
		||||
                    $enrichment              = new BudgetEnrichment();
 | 
			
		||||
                    $enrichment->setUser($model->user);
 | 
			
		||||
 | 
			
		||||
                    /** @var Budget $model */
 | 
			
		||||
                    $model                   = $enrichment->enrichSingle($model);
 | 
			
		||||
                    $transformer             = new BudgetTransformer();
 | 
			
		||||
                    $basicMessage['content'] = $transformer->transform($model);
 | 
			
		||||
                }
 | 
			
		||||
                if ($model instanceof BudgetLimit) {
 | 
			
		||||
                    $user                    = $model->budget->user;
 | 
			
		||||
                    $enrichment              = new BudgetLimitEnrichment();
 | 
			
		||||
                    $enrichment->setUser($user);
 | 
			
		||||
 | 
			
		||||
                    $parameters              = new ParameterBag();
 | 
			
		||||
                    $parameters->set('start', $model->start_date);
 | 
			
		||||
                    $parameters->set('end', $model->end_date);
 | 
			
		||||
 | 
			
		||||
                    /** @var BudgetLimit $model */
 | 
			
		||||
                    $model                   = $enrichment->enrichSingle($model);
 | 
			
		||||
                    $transformer             = new BudgetLimitTransformer();
 | 
			
		||||
                    $transformer->setParameters($parameters);
 | 
			
		||||
                    $basicMessage['content'] = $transformer->transform($model);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case WebhookResponse::NONE->name:
 | 
			
		||||
                $basicMessage['content'] = [];
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case WebhookResponse::TRANSACTIONS->value:
 | 
			
		||||
            case WebhookResponse::TRANSACTIONS->name:
 | 
			
		||||
                /** @var TransactionGroup $model */
 | 
			
		||||
                $transformer             = new TransactionGroupTransformer();
 | 
			
		||||
 | 
			
		||||
@@ -171,7 +230,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case WebhookResponse::ACCOUNTS->value:
 | 
			
		||||
            case WebhookResponse::ACCOUNTS->name:
 | 
			
		||||
                /** @var TransactionGroup $model */
 | 
			
		||||
                $accounts                = $this->collectAccounts($model);
 | 
			
		||||
                $enrichment              = new AccountEnrichment();
 | 
			
		||||
@@ -224,7 +283,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
 | 
			
		||||
        $this->objects = $objects;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setTrigger(int $trigger): void
 | 
			
		||||
    public function setTrigger(WebhookTrigger $trigger): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->trigger = $trigger;
 | 
			
		||||
    }
 | 
			
		||||
@@ -238,4 +297,50 @@ class StandardMessageGenerator implements MessageGeneratorInterface
 | 
			
		||||
    {
 | 
			
		||||
        $this->webhooks = $webhooks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getRelevantResponse(array $triggers, WebhookResponseModel $response, string $class): string
 | 
			
		||||
    {
 | 
			
		||||
        // return none if none.
 | 
			
		||||
        if (WebhookResponse::NONE->name === $response->title) {
 | 
			
		||||
            Log::debug(sprintf('Return "%s" because requested nothing.', WebhookResponse::NONE->name));
 | 
			
		||||
 | 
			
		||||
            return WebhookResponse::NONE->name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (WebhookResponse::RELEVANT->name === $response->title) {
 | 
			
		||||
            Log::debug('Expected response is any relevant data.');
 | 
			
		||||
 | 
			
		||||
            // depends on the $class
 | 
			
		||||
            switch ($class) {
 | 
			
		||||
                case TransactionGroup::class:
 | 
			
		||||
                    Log::debug(sprintf('Return "%s" because class is %s', WebhookResponse::TRANSACTIONS->name, $class));
 | 
			
		||||
 | 
			
		||||
                    return WebhookResponse::TRANSACTIONS->name;
 | 
			
		||||
 | 
			
		||||
                case Budget::class:
 | 
			
		||||
                case BudgetLimit::class:
 | 
			
		||||
                    Log::debug(sprintf('Return "%s" because class is %s', WebhookResponse::BUDGET->name, $class));
 | 
			
		||||
 | 
			
		||||
                    return WebhookResponse::BUDGET->name;
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                    throw new FireflyException(sprintf('Cannot deal with "relevant" if the given object is a "%s"', $class));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Log::debug(sprintf('Return response again: %s', $response->title));
 | 
			
		||||
 | 
			
		||||
        return $response->title;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getTriggerTitles(Collection $collection): array
 | 
			
		||||
    {
 | 
			
		||||
        $return = [];
 | 
			
		||||
 | 
			
		||||
        /** @var WebhookTriggerModel $item */
 | 
			
		||||
        foreach ($collection as $item) {
 | 
			
		||||
            $return[] = $item->title;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return array_unique($return);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -140,10 +140,10 @@ class AdminEventHandler
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'ntfy':
 | 
			
		||||
                $class = OwnerTestNotificationNtfy::class;
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
                //            case 'ntfy':
 | 
			
		||||
                //                $class = OwnerTestNotificationNtfy::class;
 | 
			
		||||
                //
 | 
			
		||||
                //                break;
 | 
			
		||||
 | 
			
		||||
            case 'pushover':
 | 
			
		||||
                $class = OwnerTestNotificationPushover::class;
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,8 @@ use FireflyIII\Support\Facades\Preferences;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
use Illuminate\Support\Facades\Notification;
 | 
			
		||||
 | 
			
		||||
use function Safe\json_encode;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class BillEventHandler
 | 
			
		||||
 */
 | 
			
		||||
@@ -108,10 +110,10 @@ class BillEventHandler
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(sprintf('Now in %s', __METHOD__));
 | 
			
		||||
 | 
			
		||||
        $bill = $event->bill;
 | 
			
		||||
        $bill       = $event->bill;
 | 
			
		||||
 | 
			
		||||
        /** @var bool $preference */
 | 
			
		||||
        Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
 | 
			
		||||
        $preference = Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
 | 
			
		||||
 | 
			
		||||
        if (true === $preference) {
 | 
			
		||||
            Log::debug('Bill reminder is true!');
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user