mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-11-03 20:55:05 +00:00 
			
		
		
		
	Compare commits
	
		
			97 Commits
		
	
	
		
			develop-20
			...
			develop-20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | ||
| 
						 | 
					1d8dd41564 | ||
| 
						 | 
					3409240a19 | ||
| 
						 | 
					eccc58e75a | ||
| 
						 | 
					24098f35bb | ||
| 
						 | 
					e7d9dc57d8 | ||
| 
						 | 
					f04ed5b8f0 | ||
| 
						 | 
					8bd44f429b | ||
| 
						 | 
					fb7866b165 | ||
| 
						 | 
					98db6db1eb | ||
| 
						 | 
					0a235ec523 | 
							
								
								
									
										86
									
								
								.ci/php-cs-fixer/composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										86
									
								
								.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",
 | 
			
		||||
@@ -959,23 +955,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 +1016,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 +1024,7 @@
 | 
			
		||||
                    "type": "open_collective"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2024-05-24T10:39:05+00:00"
 | 
			
		||||
            "time": "2025-08-19T18:57:03+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "react/socket",
 | 
			
		||||
@@ -1787,7 +1783,7 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "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 +1842,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 +1853,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 +1866,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 +1924,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 +1935,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 +2009,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 +2020,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 +2033,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 +2094,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 +2105,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 +2118,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 +2178,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 +2189,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 +2202,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 +2258,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 +2269,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"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 }}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -47,9 +47,9 @@ class AccountController extends Controller
 | 
			
		||||
    use CleansChartData;
 | 
			
		||||
    use CollectsAccountsFromFilter;
 | 
			
		||||
 | 
			
		||||
    protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
 | 
			
		||||
    protected array $acceptedRoles            = [UserRoleEnum::READ_ONLY];
 | 
			
		||||
 | 
			
		||||
    private array                  $chartData;
 | 
			
		||||
    private array                  $chartData = [];
 | 
			
		||||
    private AccountRepositoryInterface $repository;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -61,6 +61,7 @@ class AccountController extends Controller
 | 
			
		||||
        $this->middleware(
 | 
			
		||||
            function ($request, $next) {
 | 
			
		||||
                $this->repository = app(AccountRepositoryInterface::class);
 | 
			
		||||
                $this->validateUserGroup($request);
 | 
			
		||||
                $this->repository->setUserGroup($this->userGroup);
 | 
			
		||||
                $this->repository->setUser($this->user);
 | 
			
		||||
 | 
			
		||||
@@ -129,6 +130,7 @@ class AccountController extends Controller
 | 
			
		||||
            'yAxisID'                 => 0,
 | 
			
		||||
            'period'                  => '1D',
 | 
			
		||||
            'entries'                 => [],
 | 
			
		||||
            'pc_entries'              => [],
 | 
			
		||||
        ];
 | 
			
		||||
        if ($this->convertToPrimary) {
 | 
			
		||||
            $currentSet['pc_entries']                      = [];
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ class BalanceController extends Controller
 | 
			
		||||
        parent::__construct();
 | 
			
		||||
        $this->middleware(
 | 
			
		||||
            function ($request, $next) {
 | 
			
		||||
                $this->validateUserGroup($request);
 | 
			
		||||
                $this->repository = app(AccountRepositoryInterface::class);
 | 
			
		||||
                $this->collector  = app(GroupCollectorInterface::class);
 | 
			
		||||
                $this->repository->setUserGroup($this->userGroup);
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
 | 
			
		||||
 | 
			
		||||
use Carbon\Carbon;
 | 
			
		||||
use FireflyIII\Api\V1\Controllers\Controller;
 | 
			
		||||
use FireflyIII\Api\V1\Requests\Data\DateRequest;
 | 
			
		||||
use FireflyIII\Api\V1\Requests\Data\SameDateRequest;
 | 
			
		||||
use FireflyIII\Enums\UserRoleEnum;
 | 
			
		||||
use FireflyIII\Exceptions\FireflyException;
 | 
			
		||||
use FireflyIII\Models\Budget;
 | 
			
		||||
@@ -67,7 +67,6 @@ class BudgetController extends Controller
 | 
			
		||||
                $this->repository    = app(BudgetRepositoryInterface::class);
 | 
			
		||||
                $this->blRepository  = app(BudgetLimitRepositoryInterface::class);
 | 
			
		||||
                $this->opsRepository = app(OperationsRepositoryInterface::class);
 | 
			
		||||
                $this->validateUserGroup($request);
 | 
			
		||||
                $this->repository->setUserGroup($this->userGroup);
 | 
			
		||||
                $this->opsRepository->setUserGroup($this->userGroup);
 | 
			
		||||
                $this->blRepository->setUserGroup($this->userGroup);
 | 
			
		||||
@@ -85,7 +84,7 @@ class BudgetController extends Controller
 | 
			
		||||
     *
 | 
			
		||||
     * @throws FireflyException
 | 
			
		||||
     */
 | 
			
		||||
    public function overview(DateRequest $request): JsonResponse
 | 
			
		||||
    public function overview(SameDateRequest $request): JsonResponse
 | 
			
		||||
    {
 | 
			
		||||
        $params  = $request->getAll();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
 | 
			
		||||
 | 
			
		||||
use Carbon\Carbon;
 | 
			
		||||
use FireflyIII\Api\V1\Controllers\Controller;
 | 
			
		||||
use FireflyIII\Api\V1\Requests\Data\DateRequest;
 | 
			
		||||
use FireflyIII\Api\V1\Requests\Data\SameDateRequest;
 | 
			
		||||
use FireflyIII\Enums\AccountTypeEnum;
 | 
			
		||||
use FireflyIII\Enums\TransactionTypeEnum;
 | 
			
		||||
use FireflyIII\Enums\UserRoleEnum;
 | 
			
		||||
@@ -59,6 +59,7 @@ class CategoryController extends Controller
 | 
			
		||||
        parent::__construct();
 | 
			
		||||
        $this->middleware(
 | 
			
		||||
            function ($request, $next) {
 | 
			
		||||
                $this->validateUserGroup($request);
 | 
			
		||||
                $this->accountRepos  = app(AccountRepositoryInterface::class);
 | 
			
		||||
                $this->currencyRepos = app(CurrencyRepositoryInterface::class);
 | 
			
		||||
                $this->accountRepos->setUserGroup($this->userGroup);
 | 
			
		||||
@@ -79,7 +80,7 @@ class CategoryController extends Controller
 | 
			
		||||
     *
 | 
			
		||||
     * @SuppressWarnings("PHPMD.UnusedFormalParameter")
 | 
			
		||||
     */
 | 
			
		||||
    public function overview(DateRequest $request): JsonResponse
 | 
			
		||||
    public function overview(SameDateRequest $request): JsonResponse
 | 
			
		||||
    {
 | 
			
		||||
        /** @var Carbon $start */
 | 
			
		||||
        $start      = $this->parameters->get('start');
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ declare(strict_types=1);
 | 
			
		||||
namespace FireflyIII\Api\V1\Controllers\Data;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Api\V1\Controllers\Controller;
 | 
			
		||||
use FireflyIII\Enums\UserRoleEnum;
 | 
			
		||||
use FireflyIII\Models\Account;
 | 
			
		||||
use FireflyIII\Models\Bill;
 | 
			
		||||
use FireflyIII\Models\Budget;
 | 
			
		||||
@@ -44,6 +45,20 @@ use Illuminate\Http\JsonResponse;
 | 
			
		||||
 */
 | 
			
		||||
class PurgeController extends Controller
 | 
			
		||||
{
 | 
			
		||||
    protected array $acceptedRoles = [UserRoleEnum::FULL];
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
        parent::__construct();
 | 
			
		||||
        $this->middleware(
 | 
			
		||||
            function ($request, $next) {
 | 
			
		||||
                $this->validateUserGroup($request);
 | 
			
		||||
 | 
			
		||||
                return $next($request);
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * TODO cleanup and use repositories.
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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,7 +142,20 @@ 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, 'webhook.')) {
 | 
			
		||||
            $data = [
 | 
			
		||||
                'title'    => $configKey,
 | 
			
		||||
                'value'    => $this->getWebhookConfiguration($configKey),
 | 
			
		||||
                'editable' => false,
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // fallback
 | 
			
		||||
        if (!str_starts_with($configKey, 'configuration.')) {
 | 
			
		||||
            $data = [
 | 
			
		||||
                'title'    => $configKey,
 | 
			
		||||
@@ -182,4 +198,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,13 @@ 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);
 | 
			
		||||
        $webhook     = $enrichment->enrichSingle($webhook);
 | 
			
		||||
 | 
			
		||||
        Log::channel('audit')->info(sprintf('User updates webhook #%d', $webhook->id), $data);
 | 
			
		||||
 | 
			
		||||
        /** @var WebhookTransformer $transformer */
 | 
			
		||||
 
 | 
			
		||||
@@ -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)],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -49,14 +49,23 @@ class ValidatesEnvironmentVariables extends Command
 | 
			
		||||
     */
 | 
			
		||||
    public function handle(): int
 | 
			
		||||
    {
 | 
			
		||||
        $this->validateLanguage();
 | 
			
		||||
        $this->validateGuard();
 | 
			
		||||
        $this->validateStaticToken();
 | 
			
		||||
        $result = $this->validateLanguage();
 | 
			
		||||
        if (false === $result) {
 | 
			
		||||
            return Command::FAILURE;
 | 
			
		||||
        }
 | 
			
		||||
        $result = $this->validateGuard();
 | 
			
		||||
        if (false === $result) {
 | 
			
		||||
            return Command::FAILURE;
 | 
			
		||||
        }
 | 
			
		||||
        $result = $this->validateStaticToken();
 | 
			
		||||
        if (false === $result) {
 | 
			
		||||
            return Command::FAILURE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Command::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function validateLanguage(): void
 | 
			
		||||
    private function validateLanguage(): bool
 | 
			
		||||
    {
 | 
			
		||||
        $language  = config('firefly.default_language');
 | 
			
		||||
        $locale    = config('firefly.default_locale');
 | 
			
		||||
@@ -67,7 +76,7 @@ class ValidatesEnvironmentVariables extends Command
 | 
			
		||||
            $this->friendlyError('Please check your .env file and make sure you use a valid setting.');
 | 
			
		||||
            $this->friendlyError(sprintf('Valid languages are: %s', implode(', ', $options)));
 | 
			
		||||
 | 
			
		||||
            exit(1);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        $options[] = 'equal';
 | 
			
		||||
        if (!in_array($locale, $options, true)) {
 | 
			
		||||
@@ -75,11 +84,13 @@ class ValidatesEnvironmentVariables extends Command
 | 
			
		||||
            $this->friendlyError('Please check your .env file and make sure you use a valid setting.');
 | 
			
		||||
            $this->friendlyError(sprintf('Valid locales are: %s', implode(', ', $options)));
 | 
			
		||||
 | 
			
		||||
            exit(1);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function validateGuard(): void
 | 
			
		||||
    private function validateGuard(): bool
 | 
			
		||||
    {
 | 
			
		||||
        $guard = config('auth.defaults.guard');
 | 
			
		||||
        if ('web' !== $guard && 'remote_user_guard' !== $guard) {
 | 
			
		||||
@@ -87,18 +98,22 @@ class ValidatesEnvironmentVariables extends Command
 | 
			
		||||
            $this->friendlyError('Please check your .env file and make sure you use a valid setting.');
 | 
			
		||||
            $this->friendlyError('Valid guards are: web, remote_user_guard');
 | 
			
		||||
 | 
			
		||||
            exit(1);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function validateStaticToken(): void
 | 
			
		||||
    private function validateStaticToken(): bool
 | 
			
		||||
    {
 | 
			
		||||
        $token = (string) config('firefly.static_cron_token');
 | 
			
		||||
        $token = (string)config('firefly.static_cron_token');
 | 
			
		||||
        if ('' !== $token && 32 !== strlen($token)) {
 | 
			
		||||
            $this->friendlyError('STATIC_CRON_TOKEN must be empty or a 32-character string.');
 | 
			
		||||
            $this->friendlyError('Please check your .env file and make sure you use a valid setting.');
 | 
			
		||||
 | 
			
		||||
            exit(1);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,12 +29,16 @@ use FireflyIII\Models\Attachment;
 | 
			
		||||
use Illuminate\Console\Command;
 | 
			
		||||
use Illuminate\Contracts\Encryption\DecryptException;
 | 
			
		||||
use Illuminate\Support\Facades\Crypt;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
use Illuminate\Support\Facades\Storage;
 | 
			
		||||
use Safe\Exceptions\FileinfoException;
 | 
			
		||||
use Safe\Exceptions\FilesystemException;
 | 
			
		||||
use Safe\Exceptions\StringsException;
 | 
			
		||||
 | 
			
		||||
use function Safe\tempnam;
 | 
			
		||||
use function Safe\file_put_contents;
 | 
			
		||||
use function Safe\md5_file;
 | 
			
		||||
use function Safe\mime_content_type;
 | 
			
		||||
use function Safe\tempnam;
 | 
			
		||||
 | 
			
		||||
class ScansAttachments extends Command
 | 
			
		||||
{
 | 
			
		||||
@@ -46,6 +50,10 @@ class ScansAttachments extends Command
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the console command.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws FilesystemException
 | 
			
		||||
     * @throws StringsException
 | 
			
		||||
     * @throws FileinfoException
 | 
			
		||||
     */
 | 
			
		||||
    public function handle(): int
 | 
			
		||||
    {
 | 
			
		||||
@@ -57,7 +65,7 @@ class ScansAttachments extends Command
 | 
			
		||||
            $fileName         = $attachment->fileName();
 | 
			
		||||
            $encryptedContent = $disk->get($fileName);
 | 
			
		||||
            if (null === $encryptedContent) {
 | 
			
		||||
                app('log')->error(sprintf('No content for attachment #%d under filename "%s"', $attachment->id, $fileName));
 | 
			
		||||
                Log::error(sprintf('No content for attachment #%d under filename "%s"', $attachment->id, $fileName));
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
@@ -65,18 +73,13 @@ class ScansAttachments extends Command
 | 
			
		||||
            try {
 | 
			
		||||
                $decryptedContent = Crypt::decrypt($encryptedContent); // verified
 | 
			
		||||
            } catch (DecryptException $e) {
 | 
			
		||||
                app('log')->error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage()));
 | 
			
		||||
                Log::error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage()));
 | 
			
		||||
                $decryptedContent = $encryptedContent;
 | 
			
		||||
            }
 | 
			
		||||
            $tempFileName     = tempnam(sys_get_temp_dir(), 'FireflyIII');
 | 
			
		||||
            if (false === $tempFileName) {
 | 
			
		||||
                app('log')->error(sprintf('Could not create temporary file for attachment #%d', $attachment->id));
 | 
			
		||||
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
            file_put_contents($tempFileName, $decryptedContent);
 | 
			
		||||
            $attachment->md5  = (string) md5_file($tempFileName);
 | 
			
		||||
            $attachment->mime = (string) mime_content_type($tempFileName);
 | 
			
		||||
            $attachment->md5  = (string)md5_file($tempFileName);
 | 
			
		||||
            $attachment->mime = (string)mime_content_type($tempFileName);
 | 
			
		||||
            $attachment->save();
 | 
			
		||||
            $this->friendlyInfo(sprintf('Fixed attachment #%d', $attachment->id));
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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     = [];
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										95
									
								
								app/Console/Commands/Upgrade/UpgradesWebhooks.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								app/Console/Commands/Upgrade/UpgradesWebhooks.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Created.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 Created
 | 
			
		||||
 */
 | 
			
		||||
class Created extends Event
 | 
			
		||||
{
 | 
			
		||||
    use SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public function __construct(public BudgetLimit $budgetLimit) {}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Deleted.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 Deleted
 | 
			
		||||
 */
 | 
			
		||||
class Deleted extends Event
 | 
			
		||||
{
 | 
			
		||||
    use SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public function __construct(public BudgetLimit $budgetLimit) {}
 | 
			
		||||
}
 | 
			
		||||
@@ -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) {}
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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,94 @@ 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);
 | 
			
		||||
                    $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);
 | 
			
		||||
 | 
			
		||||
                    $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 +227,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 +280,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 +294,50 @@ class StandardMessageGenerator implements MessageGeneratorInterface
 | 
			
		||||
    {
 | 
			
		||||
        $this->webhooks = $webhooks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getRelevantResponse(array $triggers, WebhookResponseModel $response, $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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,9 +53,9 @@ class DestroyedGroupEventHandler
 | 
			
		||||
        $engine = app(MessageGeneratorInterface::class);
 | 
			
		||||
        $engine->setUser($user);
 | 
			
		||||
        $engine->setObjects(new Collection([$group]));
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION->value);
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION);
 | 
			
		||||
        $engine->generateMessages();
 | 
			
		||||
 | 
			
		||||
        Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
 | 
			
		||||
        event(new RequestedSendWebhookMessages());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
 | 
			
		||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
 | 
			
		||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
 | 
			
		||||
use Illuminate\Support\Collection;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class StoredGroupEventHandler
 | 
			
		||||
@@ -51,11 +52,11 @@ class StoredGroupEventHandler
 | 
			
		||||
    private function processRules(StoredTransactionGroup $storedGroupEvent): void
 | 
			
		||||
    {
 | 
			
		||||
        if (false === $storedGroupEvent->applyRules) {
 | 
			
		||||
            app('log')->info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id));
 | 
			
		||||
            Log::info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id));
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        app('log')->debug('Now in StoredGroupEventHandler::processRules()');
 | 
			
		||||
        Log::debug('Now in StoredGroupEventHandler::processRules()');
 | 
			
		||||
 | 
			
		||||
        $journals            = $storedGroupEvent->transactionGroup->transactionJournals;
 | 
			
		||||
        $array               = [];
 | 
			
		||||
@@ -65,7 +66,7 @@ class StoredGroupEventHandler
 | 
			
		||||
            $array[] = $journal->id;
 | 
			
		||||
        }
 | 
			
		||||
        $journalIds          = implode(',', $array);
 | 
			
		||||
        app('log')->debug(sprintf('Add local operator for journal(s): %s', $journalIds));
 | 
			
		||||
        Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
 | 
			
		||||
 | 
			
		||||
        // collect rules:
 | 
			
		||||
        $ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
 | 
			
		||||
@@ -98,10 +99,10 @@ class StoredGroupEventHandler
 | 
			
		||||
     */
 | 
			
		||||
    private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void
 | 
			
		||||
    {
 | 
			
		||||
        app('log')->debug(__METHOD__);
 | 
			
		||||
        Log::debug(__METHOD__);
 | 
			
		||||
        $group  = $storedGroupEvent->transactionGroup;
 | 
			
		||||
        if (false === $storedGroupEvent->fireWebhooks) {
 | 
			
		||||
            app('log')->info(sprintf('Will not fire webhooks for transaction group #%d', $group->id));
 | 
			
		||||
            Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id));
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -113,13 +114,14 @@ class StoredGroupEventHandler
 | 
			
		||||
        $engine->setUser($user);
 | 
			
		||||
 | 
			
		||||
        // tell the generator which trigger it should look for
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::STORE_TRANSACTION->value);
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::STORE_TRANSACTION);
 | 
			
		||||
        // tell the generator which objects to process
 | 
			
		||||
        $engine->setObjects(new Collection([$group]));
 | 
			
		||||
        // tell the generator to generate the messages
 | 
			
		||||
        $engine->generateMessages();
 | 
			
		||||
 | 
			
		||||
        // trigger event to send them:
 | 
			
		||||
        Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
 | 
			
		||||
        event(new RequestedSendWebhookMessages());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -164,9 +164,10 @@ class UpdatedGroupEventHandler
 | 
			
		||||
        $engine = app(MessageGeneratorInterface::class);
 | 
			
		||||
        $engine->setUser($user);
 | 
			
		||||
        $engine->setObjects(new Collection([$group]));
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION->value);
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION);
 | 
			
		||||
        $engine->generateMessages();
 | 
			
		||||
 | 
			
		||||
        Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
 | 
			
		||||
        event(new RequestedSendWebhookMessages());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,17 +24,37 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Handlers\Observer;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
use FireflyIII\Events\RequestedSendWebhookMessages;
 | 
			
		||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
 | 
			
		||||
use FireflyIII\Models\BudgetLimit;
 | 
			
		||||
use FireflyIII\Support\Facades\Amount;
 | 
			
		||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
 | 
			
		||||
use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait;
 | 
			
		||||
use Illuminate\Support\Collection;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
 | 
			
		||||
class BudgetLimitObserver
 | 
			
		||||
{
 | 
			
		||||
    use RecalculatesAvailableBudgetsTrait;
 | 
			
		||||
 | 
			
		||||
    public function created(BudgetLimit $budgetLimit): void
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug('Observe "created" of a budget limit.');
 | 
			
		||||
        $this->updatePrimaryCurrencyAmount($budgetLimit);
 | 
			
		||||
        $this->updateAvailableBudget($budgetLimit);
 | 
			
		||||
 | 
			
		||||
        $user   = $budgetLimit->budget->user;
 | 
			
		||||
 | 
			
		||||
        /** @var MessageGeneratorInterface $engine */
 | 
			
		||||
        $engine = app(MessageGeneratorInterface::class);
 | 
			
		||||
        $engine->setUser($user);
 | 
			
		||||
        $engine->setObjects(new Collection()->push($budgetLimit));
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
 | 
			
		||||
        $engine->generateMessages();
 | 
			
		||||
 | 
			
		||||
        Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
 | 
			
		||||
        event(new RequestedSendWebhookMessages());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updatePrimaryCurrencyAmount(BudgetLimit $budgetLimit): void
 | 
			
		||||
@@ -60,5 +80,18 @@ class BudgetLimitObserver
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug('Observe "updated" of a budget limit.');
 | 
			
		||||
        $this->updatePrimaryCurrencyAmount($budgetLimit);
 | 
			
		||||
        $this->updateAvailableBudget($budgetLimit);
 | 
			
		||||
 | 
			
		||||
        $user   = $budgetLimit->budget->user;
 | 
			
		||||
 | 
			
		||||
        /** @var MessageGeneratorInterface $engine */
 | 
			
		||||
        $engine = app(MessageGeneratorInterface::class);
 | 
			
		||||
        $engine->setUser($user);
 | 
			
		||||
        $engine->setObjects(new Collection()->push($budgetLimit));
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
 | 
			
		||||
        $engine->generateMessages();
 | 
			
		||||
 | 
			
		||||
        Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
 | 
			
		||||
        event(new RequestedSendWebhookMessages());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,19 +23,70 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Handlers\Observer;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
use FireflyIII\Events\RequestedSendWebhookMessages;
 | 
			
		||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
 | 
			
		||||
use FireflyIII\Models\Attachment;
 | 
			
		||||
use FireflyIII\Models\Budget;
 | 
			
		||||
use FireflyIII\Models\BudgetLimit;
 | 
			
		||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
 | 
			
		||||
use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait;
 | 
			
		||||
use Illuminate\Support\Collection;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class BudgetObserver
 | 
			
		||||
 */
 | 
			
		||||
class BudgetObserver
 | 
			
		||||
{
 | 
			
		||||
    use RecalculatesAvailableBudgetsTrait;
 | 
			
		||||
 | 
			
		||||
    public function created(Budget $budget): void
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(sprintf('Observe "created" of budget #%d ("%s").', $budget->id, $budget->name));
 | 
			
		||||
 | 
			
		||||
        // fire event.
 | 
			
		||||
        $user   = $budget->user;
 | 
			
		||||
 | 
			
		||||
        /** @var MessageGeneratorInterface $engine */
 | 
			
		||||
        $engine = app(MessageGeneratorInterface::class);
 | 
			
		||||
        $engine->setUser($user);
 | 
			
		||||
        $engine->setObjects(new Collection()->push($budget));
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::STORE_BUDGET);
 | 
			
		||||
        $engine->generateMessages();
 | 
			
		||||
        Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
 | 
			
		||||
        event(new RequestedSendWebhookMessages());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function updated(Budget $budget): void
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name));
 | 
			
		||||
        $user   = $budget->user;
 | 
			
		||||
 | 
			
		||||
        /** @var MessageGeneratorInterface $engine */
 | 
			
		||||
        $engine = app(MessageGeneratorInterface::class);
 | 
			
		||||
        $engine->setUser($user);
 | 
			
		||||
        $engine->setObjects(new Collection()->push($budget));
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::UPDATE_BUDGET);
 | 
			
		||||
        $engine->generateMessages();
 | 
			
		||||
        Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
 | 
			
		||||
        event(new RequestedSendWebhookMessages());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleting(Budget $budget): void
 | 
			
		||||
    {
 | 
			
		||||
        app('log')->debug('Observe "deleting" of a budget.');
 | 
			
		||||
        Log::debug('Observe "deleting" of a budget.');
 | 
			
		||||
 | 
			
		||||
        $user         = $budget->user;
 | 
			
		||||
 | 
			
		||||
        /** @var MessageGeneratorInterface $engine */
 | 
			
		||||
        $engine       = app(MessageGeneratorInterface::class);
 | 
			
		||||
        $engine->setUser($user);
 | 
			
		||||
        $engine->setObjects(new Collection()->push($budget));
 | 
			
		||||
        $engine->setTrigger(WebhookTrigger::DESTROY_BUDGET);
 | 
			
		||||
        $engine->generateMessages();
 | 
			
		||||
        Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
 | 
			
		||||
        event(new RequestedSendWebhookMessages());
 | 
			
		||||
 | 
			
		||||
        $repository   = app(AttachmentRepositoryInterface::class);
 | 
			
		||||
        $repository->setUser($budget->user);
 | 
			
		||||
@@ -49,7 +100,10 @@ class BudgetObserver
 | 
			
		||||
        /** @var BudgetLimit $budgetLimit */
 | 
			
		||||
        foreach ($budgetLimits as $budgetLimit) {
 | 
			
		||||
            // this loop exists so several events are fired.
 | 
			
		||||
            $budgetLimit->delete();
 | 
			
		||||
            $copy     = clone $budgetLimit;
 | 
			
		||||
            $copy->id = 0;
 | 
			
		||||
            $this->updateAvailableBudget($copy);
 | 
			
		||||
            $budgetLimit->deleteQuietly(); // delete is quietly when in a loop.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $budget->notes()->delete();
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
 | 
			
		||||
namespace FireflyIII\Handlers\Observer;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Models\TransactionGroup;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class TransactionGroup
 | 
			
		||||
@@ -32,7 +33,7 @@ class TransactionGroupObserver
 | 
			
		||||
{
 | 
			
		||||
    public function deleting(TransactionGroup $transactionGroup): void
 | 
			
		||||
    {
 | 
			
		||||
        app('log')->debug('Observe "deleting" of a transaction group.');
 | 
			
		||||
        Log::debug('Observe "deleting" of a transaction group.');
 | 
			
		||||
        foreach ($transactionGroup->transactionJournals()->get() as $journal) {
 | 
			
		||||
            $journal->delete();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Handlers\Observer;
 | 
			
		||||
use FireflyIII\Models\Attachment;
 | 
			
		||||
use FireflyIII\Models\TransactionJournal;
 | 
			
		||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class TransactionJournalObserver
 | 
			
		||||
@@ -34,7 +35,7 @@ class TransactionJournalObserver
 | 
			
		||||
{
 | 
			
		||||
    public function deleting(TransactionJournal $transactionJournal): void
 | 
			
		||||
    {
 | 
			
		||||
        app('log')->debug('Observe "deleting" of a transaction journal.');
 | 
			
		||||
        Log::debug('Observe "deleting" of a transaction journal.');
 | 
			
		||||
 | 
			
		||||
        $repository = app(AttachmentRepositoryInterface::class);
 | 
			
		||||
        $repository->setUser($transactionJournal->user);
 | 
			
		||||
 
 | 
			
		||||
@@ -839,7 +839,7 @@ class GroupCollector implements GroupCollectorInterface
 | 
			
		||||
                    return 'zzz';
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                exit('here we are 2');
 | 
			
		||||
                return 'zzz';
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@ use Illuminate\Foundation\Auth\RegistersUsers;
 | 
			
		||||
use Illuminate\Http\RedirectResponse;
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
use Illuminate\Routing\Redirector;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
use Illuminate\Validation\ValidationException;
 | 
			
		||||
use Illuminate\View\View;
 | 
			
		||||
use Psr\Container\ContainerExceptionInterface;
 | 
			
		||||
@@ -94,7 +95,7 @@ class RegisterController extends Controller
 | 
			
		||||
 | 
			
		||||
        $this->validator($request->all())->validate();
 | 
			
		||||
        $user              = $this->createUser($request->all());
 | 
			
		||||
        app('log')->info(sprintf('Registered new user %s', $user->email));
 | 
			
		||||
        Log::info(sprintf('Registered new user %s', $user->email));
 | 
			
		||||
        $owner             = new OwnerNotifiable();
 | 
			
		||||
        event(new RegisteredUser($owner, $user));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -129,7 +129,6 @@ class IndexController extends Controller
 | 
			
		||||
            $spent    = $spentArr[$this->primaryCurrency->id]['sum'] ?? '0';
 | 
			
		||||
            unset($spentArr);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // number of days for consistent budgeting.
 | 
			
		||||
        $activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description.
 | 
			
		||||
        $activeDaysLeft   = $this->activeDaysLeft($start, $end);   // see method description.
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,6 @@ class EditController extends Controller
 | 
			
		||||
        /** @var RecurrenceTransformer $transformer */
 | 
			
		||||
        $transformer                      = app(RecurrenceTransformer::class);
 | 
			
		||||
        $transformer->setParameters(new ParameterBag());
 | 
			
		||||
 | 
			
		||||
        $array                            = $transformer->transform($recurrence);
 | 
			
		||||
        $budgets                          = ExpandedForm::makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets());
 | 
			
		||||
        $bills                            = ExpandedForm::makeSelectListWithEmpty($this->billRepository->getActiveBills());
 | 
			
		||||
 
 | 
			
		||||
@@ -24,9 +24,6 @@ declare(strict_types=1);
 | 
			
		||||
namespace FireflyIII\Models;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Casts\SeparateTimezoneCaster;
 | 
			
		||||
use FireflyIII\Events\Model\BudgetLimit\Created;
 | 
			
		||||
use FireflyIII\Events\Model\BudgetLimit\Deleted;
 | 
			
		||||
use FireflyIII\Events\Model\BudgetLimit\Updated;
 | 
			
		||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
 | 
			
		||||
use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
@@ -37,12 +34,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 | 
			
		||||
class BudgetLimit extends Model
 | 
			
		||||
{
 | 
			
		||||
    use ReturnsIntegerIdTrait;
 | 
			
		||||
    protected $dispatchesEvents
 | 
			
		||||
                        = [
 | 
			
		||||
            'created' => Created::class,
 | 
			
		||||
            'updated' => Updated::class,
 | 
			
		||||
            'deleted' => Deleted::class,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
    protected $fillable = ['budget_id', 'start_date', 'end_date', 'start_date_tz', 'end_date_tz', 'amount', 'transaction_currency_id', 'native_amount'];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,14 +24,15 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Models;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Enums\WebhookDelivery;
 | 
			
		||||
use FireflyIII\Enums\WebhookResponse;
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum;
 | 
			
		||||
use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum;
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum;
 | 
			
		||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
 | 
			
		||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
 | 
			
		||||
use FireflyIII\User;
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 | 
			
		||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 | 
			
		||||
use Illuminate\Database\Eloquent\Relations\HasMany;
 | 
			
		||||
use Illuminate\Database\Eloquent\SoftDeletes;
 | 
			
		||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 | 
			
		||||
@@ -56,7 +57,7 @@ class Webhook extends Model
 | 
			
		||||
    public static function getDeliveries(): array
 | 
			
		||||
    {
 | 
			
		||||
        $array = [];
 | 
			
		||||
        $set   = WebhookDelivery::cases();
 | 
			
		||||
        $set   = WebhookDeliveryEnum::cases();
 | 
			
		||||
        foreach ($set as $item) {
 | 
			
		||||
            $array[$item->value] = $item->name;
 | 
			
		||||
        }
 | 
			
		||||
@@ -67,7 +68,7 @@ class Webhook extends Model
 | 
			
		||||
    public static function getDeliveriesForValidation(): array
 | 
			
		||||
    {
 | 
			
		||||
        $array = [];
 | 
			
		||||
        $set   = WebhookDelivery::cases();
 | 
			
		||||
        $set   = WebhookDeliveryEnum::cases();
 | 
			
		||||
        foreach ($set as $item) {
 | 
			
		||||
            $array[$item->name]  = $item->value;
 | 
			
		||||
            $array[$item->value] = $item->value;
 | 
			
		||||
@@ -79,7 +80,7 @@ class Webhook extends Model
 | 
			
		||||
    public static function getResponses(): array
 | 
			
		||||
    {
 | 
			
		||||
        $array = [];
 | 
			
		||||
        $set   = WebhookResponse::cases();
 | 
			
		||||
        $set   = WebhookResponseEnum::cases();
 | 
			
		||||
        foreach ($set as $item) {
 | 
			
		||||
            $array[$item->value] = $item->name;
 | 
			
		||||
        }
 | 
			
		||||
@@ -90,7 +91,7 @@ class Webhook extends Model
 | 
			
		||||
    public static function getResponsesForValidation(): array
 | 
			
		||||
    {
 | 
			
		||||
        $array = [];
 | 
			
		||||
        $set   = WebhookResponse::cases();
 | 
			
		||||
        $set   = WebhookResponseEnum::cases();
 | 
			
		||||
        foreach ($set as $item) {
 | 
			
		||||
            $array[$item->name]  = $item->value;
 | 
			
		||||
            $array[$item->value] = $item->value;
 | 
			
		||||
@@ -102,7 +103,7 @@ class Webhook extends Model
 | 
			
		||||
    public static function getTriggers(): array
 | 
			
		||||
    {
 | 
			
		||||
        $array = [];
 | 
			
		||||
        $set   = WebhookTrigger::cases();
 | 
			
		||||
        $set   = WebhookTriggerEnum::cases();
 | 
			
		||||
        foreach ($set as $item) {
 | 
			
		||||
            $array[$item->value] = $item->name;
 | 
			
		||||
        }
 | 
			
		||||
@@ -113,7 +114,7 @@ class Webhook extends Model
 | 
			
		||||
    public static function getTriggersForValidation(): array
 | 
			
		||||
    {
 | 
			
		||||
        $array = [];
 | 
			
		||||
        $set   = WebhookTrigger::cases();
 | 
			
		||||
        $set   = WebhookTriggerEnum::cases();
 | 
			
		||||
        foreach ($set as $item) {
 | 
			
		||||
            $array[$item->name]  = $item->value;
 | 
			
		||||
            $array[$item->value] = $item->value;
 | 
			
		||||
@@ -130,7 +131,7 @@ class Webhook extends Model
 | 
			
		||||
    public static function routeBinder(string $value): self
 | 
			
		||||
    {
 | 
			
		||||
        if (auth()->check()) {
 | 
			
		||||
            $webhookId = (int) $value;
 | 
			
		||||
            $webhookId = (int)$value;
 | 
			
		||||
 | 
			
		||||
            /** @var User $user */
 | 
			
		||||
            $user      = auth()->user();
 | 
			
		||||
@@ -155,6 +156,21 @@ class Webhook extends Model
 | 
			
		||||
        return $this->hasMany(WebhookMessage::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function webhookDeliveries(): BelongsToMany
 | 
			
		||||
    {
 | 
			
		||||
        return $this->belongsToMany(WebhookDelivery::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function webhookResponses(): BelongsToMany
 | 
			
		||||
    {
 | 
			
		||||
        return $this->belongsToMany(WebhookResponse::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function webhookTriggers(): BelongsToMany
 | 
			
		||||
    {
 | 
			
		||||
        return $this->belongsToMany(WebhookTrigger::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function casts(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								app/Models/WebhookDelivery.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/Models/WebhookDelivery.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Models;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
 | 
			
		||||
use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
 | 
			
		||||
class WebhookDelivery extends Model
 | 
			
		||||
{
 | 
			
		||||
    use ReturnsIntegerIdTrait;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the ID
 | 
			
		||||
     *
 | 
			
		||||
     * @SuppressWarnings("PHPMD.ShortMethodName")
 | 
			
		||||
     */
 | 
			
		||||
    protected function key(): Attribute
 | 
			
		||||
    {
 | 
			
		||||
        return Attribute::make(
 | 
			
		||||
            get: static fn ($value) => (int) $value,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								app/Models/WebhookResponse.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/Models/WebhookResponse.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Models;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
 | 
			
		||||
use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
 | 
			
		||||
class WebhookResponse extends Model
 | 
			
		||||
{
 | 
			
		||||
    use ReturnsIntegerIdTrait;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the ID
 | 
			
		||||
     *
 | 
			
		||||
     * @SuppressWarnings("PHPMD.ShortMethodName")
 | 
			
		||||
     */
 | 
			
		||||
    protected function key(): Attribute
 | 
			
		||||
    {
 | 
			
		||||
        return Attribute::make(
 | 
			
		||||
            get: static fn ($value) => (int) $value,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								app/Models/WebhookTrigger.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/Models/WebhookTrigger.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Models;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
 | 
			
		||||
use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
 | 
			
		||||
class WebhookTrigger extends Model
 | 
			
		||||
{
 | 
			
		||||
    use ReturnsIntegerIdTrait;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the ID
 | 
			
		||||
     *
 | 
			
		||||
     * @SuppressWarnings("PHPMD.ShortMethodName")
 | 
			
		||||
     */
 | 
			
		||||
    protected function key(): Attribute
 | 
			
		||||
    {
 | 
			
		||||
        return Attribute::make(
 | 
			
		||||
            get: static fn ($value) => (int) $value,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -29,9 +29,6 @@ use FireflyIII\Events\DestroyedTransactionGroup;
 | 
			
		||||
use FireflyIII\Events\DetectedNewIPAddress;
 | 
			
		||||
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
 | 
			
		||||
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
 | 
			
		||||
use FireflyIII\Events\Model\BudgetLimit\Created;
 | 
			
		||||
use FireflyIII\Events\Model\BudgetLimit\Deleted;
 | 
			
		||||
use FireflyIII\Events\Model\BudgetLimit\Updated;
 | 
			
		||||
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
 | 
			
		||||
use FireflyIII\Events\Model\PiggyBank\ChangedName;
 | 
			
		||||
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
 | 
			
		||||
@@ -219,17 +216,6 @@ class EventServiceProvider extends ServiceProvider
 | 
			
		||||
                'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
 | 
			
		||||
            ],
 | 
			
		||||
 | 
			
		||||
            // budget related events: CRUD budget limit
 | 
			
		||||
            Created::class                           => [
 | 
			
		||||
                'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@created',
 | 
			
		||||
            ],
 | 
			
		||||
            Updated::class                           => [
 | 
			
		||||
                'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@updated',
 | 
			
		||||
            ],
 | 
			
		||||
            Deleted::class                           => [
 | 
			
		||||
                'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
 | 
			
		||||
            ],
 | 
			
		||||
 | 
			
		||||
            // rule actions
 | 
			
		||||
            RuleActionFailedOnArray::class           => [
 | 
			
		||||
                'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
 | 
			
		||||
 
 | 
			
		||||
@@ -268,7 +268,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
 | 
			
		||||
         */
 | 
			
		||||
        foreach ($budgets as $index => $budget) {
 | 
			
		||||
            $budget->order = $index + 1;
 | 
			
		||||
            $budget->save();
 | 
			
		||||
            $budget->saveQuietly();
 | 
			
		||||
        }
 | 
			
		||||
        // other budgets, set to 0.
 | 
			
		||||
        $this->user->budgets()->where('active', 0)->update(['order' => 0]);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,9 +24,13 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Repositories\Webhook;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Exceptions\FireflyException;
 | 
			
		||||
use FireflyIII\Models\Webhook;
 | 
			
		||||
use FireflyIII\Models\WebhookAttempt;
 | 
			
		||||
use FireflyIII\Models\WebhookDelivery;
 | 
			
		||||
use FireflyIII\Models\WebhookMessage;
 | 
			
		||||
use FireflyIII\Models\WebhookResponse;
 | 
			
		||||
use FireflyIII\Models\WebhookTrigger;
 | 
			
		||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
 | 
			
		||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
 | 
			
		||||
use Illuminate\Support\Collection;
 | 
			
		||||
@@ -41,11 +45,20 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac
 | 
			
		||||
 | 
			
		||||
    public function all(): Collection
 | 
			
		||||
    {
 | 
			
		||||
        return $this->user->webhooks()->get();
 | 
			
		||||
        return $this->user->webhooks()
 | 
			
		||||
            // only get upgraded webhooks
 | 
			
		||||
            ->where('delivery', 1)
 | 
			
		||||
            ->where('response', 1)
 | 
			
		||||
            ->where('trigger', 1)
 | 
			
		||||
            ->get()
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function destroy(Webhook $webhook): void
 | 
			
		||||
    {
 | 
			
		||||
        // force delete all messages and attempts:
 | 
			
		||||
        $webhook->webhookMessages()->delete();
 | 
			
		||||
 | 
			
		||||
        $webhook->delete();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -87,38 +100,108 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac
 | 
			
		||||
 | 
			
		||||
    public function store(array $data): Webhook
 | 
			
		||||
    {
 | 
			
		||||
        $secret   = Str::random(24);
 | 
			
		||||
        $fullData = [
 | 
			
		||||
        $secret     = Str::random(24);
 | 
			
		||||
        $fullData   = [
 | 
			
		||||
            'user_id'       => $this->user->id,
 | 
			
		||||
            'user_group_id' => $this->user->user_group_id,
 | 
			
		||||
            'active'        => $data['active'] ?? false,
 | 
			
		||||
            'title'         => $data['title'] ?? null,
 | 
			
		||||
            'trigger'       => $data['trigger'],
 | 
			
		||||
            'response'      => $data['response'],
 | 
			
		||||
            'delivery'      => $data['delivery'],
 | 
			
		||||
            //            'trigger'       => $data['trigger'],
 | 
			
		||||
            //            'response'      => $data['response'],
 | 
			
		||||
            //            'delivery'      => $data['delivery'],
 | 
			
		||||
            'trigger'       => 1,
 | 
			
		||||
            'response'      => 1,
 | 
			
		||||
            'delivery'      => 1,
 | 
			
		||||
            'secret'        => $secret,
 | 
			
		||||
            'url'           => $data['url'],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        return Webhook::create($fullData);
 | 
			
		||||
        /** @var Webhook $webhook */
 | 
			
		||||
        $webhook    = Webhook::create($fullData);
 | 
			
		||||
        $triggers   = new Collection();
 | 
			
		||||
        $responses  = new Collection();
 | 
			
		||||
        $deliveries = new Collection();
 | 
			
		||||
 | 
			
		||||
        foreach ($data['triggers'] as $trigger) {
 | 
			
		||||
            // get the relevant ID:
 | 
			
		||||
            $object = WebhookTrigger::where('title', $trigger)->first();
 | 
			
		||||
            if (null === $object) {
 | 
			
		||||
                throw new FireflyException(sprintf('Could not find webhook trigger with title "%s".', $trigger));
 | 
			
		||||
            }
 | 
			
		||||
            $triggers->push($object);
 | 
			
		||||
        }
 | 
			
		||||
        $webhook->webhookTriggers()->saveMany($triggers);
 | 
			
		||||
 | 
			
		||||
        foreach ($data['responses'] as $response) {
 | 
			
		||||
            // get the relevant ID:
 | 
			
		||||
            $object = WebhookResponse::where('title', $response)->first();
 | 
			
		||||
            if (null === $object) {
 | 
			
		||||
                throw new FireflyException(sprintf('Could not find webhook response with title "%s".', $response));
 | 
			
		||||
            }
 | 
			
		||||
            $responses->push($object);
 | 
			
		||||
        }
 | 
			
		||||
        $webhook->webhookResponses()->saveMany($responses);
 | 
			
		||||
 | 
			
		||||
        foreach ($data['deliveries'] as $delivery) {
 | 
			
		||||
            // get the relevant ID:
 | 
			
		||||
            $object = WebhookDelivery::where('title', $delivery)->first();
 | 
			
		||||
            if (null === $object) {
 | 
			
		||||
                throw new FireflyException(sprintf('Could not find webhook delivery with title "%s".', $delivery));
 | 
			
		||||
            }
 | 
			
		||||
            $deliveries->push($object);
 | 
			
		||||
        }
 | 
			
		||||
        $webhook->webhookDeliveries()->saveMany($deliveries);
 | 
			
		||||
 | 
			
		||||
        return $webhook;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function update(Webhook $webhook, array $data): Webhook
 | 
			
		||||
    {
 | 
			
		||||
        $webhook->active   = $data['active'] ?? $webhook->active;
 | 
			
		||||
        $webhook->trigger  = $data['trigger'] ?? $webhook->trigger;
 | 
			
		||||
        $webhook->response = $data['response'] ?? $webhook->response;
 | 
			
		||||
        $webhook->delivery = $data['delivery'] ?? $webhook->delivery;
 | 
			
		||||
        $webhook->title    = $data['title'] ?? $webhook->title;
 | 
			
		||||
        $webhook->url      = $data['url'] ?? $webhook->url;
 | 
			
		||||
        $webhook->active = $data['active'] ?? $webhook->active;
 | 
			
		||||
        $webhook->title  = $data['title'] ?? $webhook->title;
 | 
			
		||||
        $webhook->url    = $data['url'] ?? $webhook->url;
 | 
			
		||||
 | 
			
		||||
        if (true === $data['secret']) {
 | 
			
		||||
        if (array_key_exists('secret', $data) && true === $data['secret']) {
 | 
			
		||||
            $secret          = Str::random(24);
 | 
			
		||||
            $webhook->secret = $secret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $webhook->save();
 | 
			
		||||
 | 
			
		||||
        $triggers        = new Collection();
 | 
			
		||||
        $responses       = new Collection();
 | 
			
		||||
        $deliveries      = new Collection();
 | 
			
		||||
 | 
			
		||||
        foreach ($data['triggers'] as $trigger) {
 | 
			
		||||
            // get the relevant ID:
 | 
			
		||||
            $object = WebhookTrigger::where('title', $trigger)->first();
 | 
			
		||||
            if (null === $object) {
 | 
			
		||||
                throw new FireflyException(sprintf('Could not find webhook trigger with title "%s".', $trigger));
 | 
			
		||||
            }
 | 
			
		||||
            $triggers->push($object);
 | 
			
		||||
        }
 | 
			
		||||
        $webhook->webhookTriggers()->sync($triggers);
 | 
			
		||||
 | 
			
		||||
        foreach ($data['responses'] as $response) {
 | 
			
		||||
            // get the relevant ID:
 | 
			
		||||
            $object = WebhookResponse::where('title', $response)->first();
 | 
			
		||||
            if (null === $object) {
 | 
			
		||||
                throw new FireflyException(sprintf('Could not find webhook response with title "%s".', $response));
 | 
			
		||||
            }
 | 
			
		||||
            $responses->push($object);
 | 
			
		||||
        }
 | 
			
		||||
        $webhook->webhookResponses()->sync($responses);
 | 
			
		||||
 | 
			
		||||
        foreach ($data['deliveries'] as $delivery) {
 | 
			
		||||
            // get the relevant ID:
 | 
			
		||||
            $object = WebhookDelivery::where('title', $delivery)->first();
 | 
			
		||||
            if (null === $object) {
 | 
			
		||||
                throw new FireflyException(sprintf('Could not find webhook delivery with title "%s".', $delivery));
 | 
			
		||||
            }
 | 
			
		||||
            $deliveries->push($object);
 | 
			
		||||
        }
 | 
			
		||||
        $webhook->webhookDeliveries()->sync($deliveries);
 | 
			
		||||
 | 
			
		||||
        return $webhook;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,11 @@ class EitherConfigKey
 | 
			
		||||
            'firefly.rule-actions',
 | 
			
		||||
            'firefly.context-rule-actions',
 | 
			
		||||
            'search.operators',
 | 
			
		||||
 | 
			
		||||
            // webhooks
 | 
			
		||||
            'webhook.triggers',
 | 
			
		||||
            'webhook.responses',
 | 
			
		||||
            'webhook.deliveries',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -65,55 +65,27 @@ class FrontpageChartGenerator
 | 
			
		||||
 | 
			
		||||
    public function generate(): array
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug('Now in generate()');
 | 
			
		||||
        Log::debug(sprintf('Now in %s', __METHOD__));
 | 
			
		||||
        $categories   = $this->repository->getCategories();
 | 
			
		||||
        $accounts     = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]);
 | 
			
		||||
 | 
			
		||||
        // get expenses + income per category:
 | 
			
		||||
        $collection   = [];
 | 
			
		||||
 | 
			
		||||
        /** @var Category $category */
 | 
			
		||||
        foreach ($categories as $category) {
 | 
			
		||||
            // get expenses
 | 
			
		||||
            $collection[] = $this->collectExpenses($category, $accounts);
 | 
			
		||||
        }
 | 
			
		||||
        $collection   = $this->collectExpensesAll($categories, $accounts);
 | 
			
		||||
 | 
			
		||||
        // collect for no-category:
 | 
			
		||||
        $collection[] = $this->collectNoCatExpenses($accounts);
 | 
			
		||||
 | 
			
		||||
        $tempData     = array_merge(...$collection);
 | 
			
		||||
        $noCategory   = $this->collectNoCatExpenses($accounts);
 | 
			
		||||
        $collection   = array_merge($collection, $noCategory);
 | 
			
		||||
 | 
			
		||||
        // sort temp array by amount.
 | 
			
		||||
        $amounts      = array_column($tempData, 'sum_float');
 | 
			
		||||
        array_multisort($amounts, SORT_ASC, $tempData);
 | 
			
		||||
        $amounts      = array_column($collection, 'sum_float');
 | 
			
		||||
        array_multisort($amounts, SORT_ASC, $collection);
 | 
			
		||||
 | 
			
		||||
        $currencyData = $this->createCurrencyGroups($tempData);
 | 
			
		||||
        $currencyData = $this->createCurrencyGroups($collection);
 | 
			
		||||
 | 
			
		||||
        return $this->insertValues($currencyData, $tempData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function collectExpenses(Category $category, Collection $accounts): array
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(sprintf('Collect expenses for category #%d ("%s")', $category->id, $category->name));
 | 
			
		||||
        $spent    = $this->opsRepos->sumExpenses($this->start, $this->end, $accounts, new Collection([$category]));
 | 
			
		||||
        $tempData = [];
 | 
			
		||||
        foreach ($spent as $currency) {
 | 
			
		||||
            Log::debug(sprintf('Spent %s %s', $currency['currency_code'], $currency['sum']));
 | 
			
		||||
            $this->addCurrency($currency);
 | 
			
		||||
            $tempData[] = [
 | 
			
		||||
                'name'        => $category->name,
 | 
			
		||||
                'sum'         => $currency['sum'],
 | 
			
		||||
                'sum_float'   => round((float) $currency['sum'], $currency['currency_decimal_places']),
 | 
			
		||||
                'currency_id' => (int) $currency['currency_id'],
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $tempData;
 | 
			
		||||
        return $this->insertValues($currencyData, $collection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function addCurrency(array $currency): void
 | 
			
		||||
    {
 | 
			
		||||
        $currencyId = (int) $currency['currency_id'];
 | 
			
		||||
        $currencyId = (int)$currency['currency_id'];
 | 
			
		||||
 | 
			
		||||
        $this->currencies[$currencyId] ??= [
 | 
			
		||||
            'currency_id'             => $currencyId,
 | 
			
		||||
@@ -133,8 +105,8 @@ class FrontpageChartGenerator
 | 
			
		||||
            $tempData[] = [
 | 
			
		||||
                'name'        => trans('firefly.no_category'),
 | 
			
		||||
                'sum'         => $currency['sum'],
 | 
			
		||||
                'sum_float'   => round((float) $currency['sum'], $currency['currency_decimal_places'] ?? 2), // intentional float
 | 
			
		||||
                'currency_id' => (int) $currency['currency_id'],
 | 
			
		||||
                'sum_float'   => round((float)$currency['sum'], $currency['currency_decimal_places'] ?? 2), // intentional float
 | 
			
		||||
                'currency_id' => (int)$currency['currency_id'],
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -152,7 +124,7 @@ class FrontpageChartGenerator
 | 
			
		||||
        foreach ($this->currencies as $currencyId => $currency) {
 | 
			
		||||
            $key          = sprintf('spent-%d', $currencyId);
 | 
			
		||||
            $return[$key] = [
 | 
			
		||||
                'label'           => sprintf('%s (%s)', (string) trans('firefly.spent'), $currency['currency_name']),
 | 
			
		||||
                'label'           => sprintf('%s (%s)', (string)trans('firefly.spent'), $currency['currency_name']),
 | 
			
		||||
                'type'            => 'bar',
 | 
			
		||||
                'currency_symbol' => $currency['currency_symbol'],
 | 
			
		||||
                'entries'         => $names,
 | 
			
		||||
@@ -175,4 +147,28 @@ class FrontpageChartGenerator
 | 
			
		||||
 | 
			
		||||
        return $currencyData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function collectExpensesAll(Collection $categories, Collection $accounts): array
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(sprintf('Collect expenses for %d category(ies).', count($categories)));
 | 
			
		||||
        $spent    = $this->opsRepos->collectExpenses($this->start, $this->end, $accounts, $categories);
 | 
			
		||||
        $tempData = [];
 | 
			
		||||
        foreach ($categories as $category) {
 | 
			
		||||
            $sums = $this->opsRepos->sumCollectedTransactionsByCategory($spent, $category, 'negative', $this->convertToPrimary);
 | 
			
		||||
            if (0 === count($sums)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            foreach ($sums as $currency) {
 | 
			
		||||
                $this->addCurrency($currency);
 | 
			
		||||
                $tempData[] = [
 | 
			
		||||
                    'name'        => $category->name,
 | 
			
		||||
                    'sum'         => $currency['sum'],
 | 
			
		||||
                    'sum_float'   => round((float)$currency['sum'], $currency['currency_decimal_places']),
 | 
			
		||||
                    'currency_id' => (int)$currency['currency_id'],
 | 
			
		||||
                ];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $tempData;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -73,10 +73,12 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
 | 
			
		||||
    public function enrich(Collection $collection): Collection
 | 
			
		||||
    {
 | 
			
		||||
        $this->collection = $collection;
 | 
			
		||||
        $this->collectIds();
 | 
			
		||||
        $this->collectCurrencies();
 | 
			
		||||
        $this->collectSpentInfo();
 | 
			
		||||
        $this->appendCollectedData();
 | 
			
		||||
        if ($this->collection->count() > 0) {
 | 
			
		||||
            $this->collectIds();
 | 
			
		||||
            $this->collectCurrencies();
 | 
			
		||||
            $this->collectSpentInfo();
 | 
			
		||||
            $this->appendCollectedData();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->collection;
 | 
			
		||||
    }
 | 
			
		||||
@@ -85,7 +87,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
 | 
			
		||||
    public function enrichSingle(array|Model $model): array|Model
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(__METHOD__);
 | 
			
		||||
        $collection = new Collection([$model]);
 | 
			
		||||
        $collection = new Collection()->push($model);
 | 
			
		||||
        $collection = $this->enrich($collection);
 | 
			
		||||
 | 
			
		||||
        return $collection->first();
 | 
			
		||||
@@ -119,8 +121,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
 | 
			
		||||
 | 
			
		||||
    private function collectSpentInfo(): void
 | 
			
		||||
    {
 | 
			
		||||
        $start               = $this->collection->min('start_date');
 | 
			
		||||
        $end                 = $this->collection->max('end_date');
 | 
			
		||||
        $start               = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth();
 | 
			
		||||
        $end                 = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth();
 | 
			
		||||
        $allActive           = $this->repository->getActiveBudgets();
 | 
			
		||||
        $spentInBudgets      = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null);
 | 
			
		||||
        $spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end, null, null, null);
 | 
			
		||||
@@ -139,14 +141,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
 | 
			
		||||
                $this->pcSpentInBudgets[$id]      = array_values($pcFilteredSpentInBudgets);
 | 
			
		||||
                $this->pcSpentOutsideBudgets[$id] = array_values($pcFilteredSpentOutsideBudgets);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // filter arrays on date.
 | 
			
		||||
            // send them to sumCollection thing.
 | 
			
		||||
            // save.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // first collect, then filter and append.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function appendCollectedData(): void
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
 | 
			
		||||
        $this->collectCurrencies();
 | 
			
		||||
        $this->collectNotes();
 | 
			
		||||
        $this->collectBudgets();
 | 
			
		||||
        $this->stringifyIds();
 | 
			
		||||
        $this->appendCollectedData();
 | 
			
		||||
 | 
			
		||||
        return $this->collection;
 | 
			
		||||
@@ -155,4 +156,23 @@ class BudgetLimitEnrichment implements EnrichmentInterface
 | 
			
		||||
            $this->currencies[(int)$currency->id] = $currency;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function stringifyIds(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->expenses   = array_map(function ($first) {
 | 
			
		||||
            return array_map(function ($second) {
 | 
			
		||||
                $second['currency_id'] = (string)($second['currency_id'] ?? 0);
 | 
			
		||||
 | 
			
		||||
                return $second;
 | 
			
		||||
            }, $first);
 | 
			
		||||
        }, $this->expenses);
 | 
			
		||||
 | 
			
		||||
        $this->pcExpenses = array_map(function ($first) {
 | 
			
		||||
            return array_map(function ($second) {
 | 
			
		||||
                $second['currency_id'] = (string)($second['currency_id'] ?? 0);
 | 
			
		||||
 | 
			
		||||
                return $second;
 | 
			
		||||
            }, $first);
 | 
			
		||||
        }, $this->expenses);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -170,7 +170,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
 | 
			
		||||
            // add object group if available
 | 
			
		||||
            if (array_key_exists($id, $this->mappedObjects)) {
 | 
			
		||||
                $key                        = $this->mappedObjects[$id];
 | 
			
		||||
                $meta['object_group_id']    = $this->objectGroups[$key]['id'];
 | 
			
		||||
                $meta['object_group_id']    = (string) $this->objectGroups[$key]['id'];
 | 
			
		||||
                $meta['object_group_title'] = $this->objectGroups[$key]['title'];
 | 
			
		||||
                $meta['object_group_order'] = $this->objectGroups[$key]['order'];
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -129,7 +129,7 @@ class RecurringEnrichment implements EnrichmentInterface
 | 
			
		||||
            $recurrence                     = $this->collection->filter(function (Recurrence $item) use ($repetition) {
 | 
			
		||||
                return (int)$item->id === (int)$repetition->recurrence_id;
 | 
			
		||||
            })->first();
 | 
			
		||||
            $fromDate                       = $recurrence->latest_date ?? $recurrence->first_date;
 | 
			
		||||
            $fromDate                       = clone ($recurrence->latest_date ?? $recurrence->first_date);
 | 
			
		||||
            $id                             = (int)$repetition->recurrence_id;
 | 
			
		||||
            $repId                          = (int)$repetition->id;
 | 
			
		||||
            $this->repetitions[$id] ??= [];
 | 
			
		||||
@@ -137,6 +137,7 @@ class RecurringEnrichment implements EnrichmentInterface
 | 
			
		||||
            // get the (future) occurrences for this specific type of repetition:
 | 
			
		||||
            $amount                         = 'daily' === $repetition->repetition_type ? 9 : 5;
 | 
			
		||||
            $set                            = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount);
 | 
			
		||||
            $occurrences                    = [];
 | 
			
		||||
 | 
			
		||||
            /** @var Carbon $carbon */
 | 
			
		||||
            foreach ($set as $carbon) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										143
									
								
								app/Support/JsonApi/Enrichments/WebhookEnrichment.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								app/Support/JsonApi/Enrichments/WebhookEnrichment.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Support\JsonApi\Enrichments;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum;
 | 
			
		||||
use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum;
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum;
 | 
			
		||||
use FireflyIII\Models\UserGroup;
 | 
			
		||||
use FireflyIII\Models\Webhook;
 | 
			
		||||
use FireflyIII\Models\WebhookDelivery;
 | 
			
		||||
use FireflyIII\Models\WebhookResponse;
 | 
			
		||||
use FireflyIII\Models\WebhookTrigger;
 | 
			
		||||
use FireflyIII\User;
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
use Illuminate\Support\Collection;
 | 
			
		||||
use Illuminate\Support\Facades\DB;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
use stdClass;
 | 
			
		||||
 | 
			
		||||
class WebhookEnrichment implements EnrichmentInterface
 | 
			
		||||
{
 | 
			
		||||
    private Collection $collection;
 | 
			
		||||
    private User       $user;
 | 
			
		||||
    private UserGroup  $userGroup;
 | 
			
		||||
    private array      $ids          = [];
 | 
			
		||||
    private array      $deliveries   = [];
 | 
			
		||||
    private array      $responses    = [];
 | 
			
		||||
    private array      $triggers     = [];
 | 
			
		||||
 | 
			
		||||
    private array $webhookDeliveries = [];
 | 
			
		||||
    private array $webhookResponses  = [];
 | 
			
		||||
    private array $webhookTriggers   = [];
 | 
			
		||||
 | 
			
		||||
    public function enrich(Collection $collection): Collection
 | 
			
		||||
    {
 | 
			
		||||
        $this->collection = $collection;
 | 
			
		||||
        if ($this->collection->count() > 0) {
 | 
			
		||||
            $this->collectIds();
 | 
			
		||||
            $this->collectInfo();
 | 
			
		||||
            $this->collectWebhookInfo();
 | 
			
		||||
            $this->appendCollectedInfo();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->collection;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function enrichSingle(array|Model $model): array|Model
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(__METHOD__);
 | 
			
		||||
        $collection = new Collection([$model]);
 | 
			
		||||
        $collection = $this->enrich($collection);
 | 
			
		||||
 | 
			
		||||
        return $collection->first();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setUser(User $user): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->user = $user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setUserGroup(UserGroup $userGroup): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->userGroup = $userGroup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function collectIds(): void
 | 
			
		||||
    {
 | 
			
		||||
        /** @var Webhook $webhook */
 | 
			
		||||
        foreach ($this->collection as $webhook) {
 | 
			
		||||
            $this->ids[] = $webhook->id;
 | 
			
		||||
        }
 | 
			
		||||
        $this->ids = array_unique($this->ids);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function collectInfo(): void
 | 
			
		||||
    {
 | 
			
		||||
        $all = WebhookDelivery::get();
 | 
			
		||||
 | 
			
		||||
        /** @var WebhookDelivery $item */
 | 
			
		||||
        foreach ($all as $item) {
 | 
			
		||||
            $this->deliveries[$item->id] = $item->key;
 | 
			
		||||
        }
 | 
			
		||||
        $all = WebhookResponse::get();
 | 
			
		||||
 | 
			
		||||
        /** @var WebhookResponse $item */
 | 
			
		||||
        foreach ($all as $item) {
 | 
			
		||||
            $this->responses[$item->id] = $item->key;
 | 
			
		||||
        }
 | 
			
		||||
        $all = WebhookTrigger::get();
 | 
			
		||||
 | 
			
		||||
        /** @var WebhookTrigger $item */
 | 
			
		||||
        foreach ($all as $item) {
 | 
			
		||||
            $this->triggers[$item->id] = $item->key;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function collectWebhookInfo(): void
 | 
			
		||||
    {
 | 
			
		||||
        $set = DB::table('webhook_webhook_delivery')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_delivery_id']);
 | 
			
		||||
 | 
			
		||||
        /** @var stdClass $item */
 | 
			
		||||
        foreach ($set as $item) {
 | 
			
		||||
            $id                             = $item->webhook_id;
 | 
			
		||||
            $deliveryId                     = $item->webhook_delivery_id;
 | 
			
		||||
            $this->webhookDeliveries[$id][] = WebhookDeliveryEnum::from($this->deliveries[$deliveryId])->name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $set = DB::table('webhook_webhook_response')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_response_id']);
 | 
			
		||||
 | 
			
		||||
        /** @var stdClass $item */
 | 
			
		||||
        foreach ($set as $item) {
 | 
			
		||||
            $id                            = $item->webhook_id;
 | 
			
		||||
            $responseId                    = $item->webhook_response_id;
 | 
			
		||||
            $this->webhookResponses[$id][] = WebhookResponseEnum::from($this->responses[$responseId])->name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $set = DB::table('webhook_webhook_trigger')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_trigger_id']);
 | 
			
		||||
 | 
			
		||||
        /** @var stdClass $item */
 | 
			
		||||
        foreach ($set as $item) {
 | 
			
		||||
            $id                           = $item->webhook_id;
 | 
			
		||||
            $triggerId                    = $item->webhook_trigger_id;
 | 
			
		||||
            $this->webhookTriggers[$id][] = WebhookTriggerEnum::from($this->triggers[$triggerId])->name;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function appendCollectedInfo(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->collection = $this->collection->map(function (Webhook $item) {
 | 
			
		||||
            $meta       = [
 | 
			
		||||
                'deliveries' => $this->webhookDeliveries[$item->id] ?? [],
 | 
			
		||||
                'responses'  => $this->webhookResponses[$item->id] ?? [],
 | 
			
		||||
                'triggers'   => $this->webhookTriggers[$item->id] ?? [],
 | 
			
		||||
            ];
 | 
			
		||||
            $item->meta = $meta;
 | 
			
		||||
 | 
			
		||||
            return $item;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -464,7 +464,7 @@ class Navigation
 | 
			
		||||
        $displayFormat = (string) trans('config.month_and_day_js', [], $locale);
 | 
			
		||||
        $diff          = $start->diffInMonths($end, true);
 | 
			
		||||
        // increment by month (for year)
 | 
			
		||||
        if ($diff >= 1.0001) {
 | 
			
		||||
        if ($diff >= 1.0001 && $diff < 12.001) {
 | 
			
		||||
            $increment     = 'addMonth';
 | 
			
		||||
            $displayFormat = (string) trans('config.month_js');
 | 
			
		||||
        }
 | 
			
		||||
@@ -495,7 +495,7 @@ class Navigation
 | 
			
		||||
        $format = 'Y-m-d';
 | 
			
		||||
        $diff   = $start->diffInMonths($end, true);
 | 
			
		||||
        // Log::debug(sprintf('preferredCarbonFormat(%s, %s) = %f', $start->format('Y-m-d'), $end->format('Y-m-d'), $diff));
 | 
			
		||||
        if ($diff >= 1.001) {
 | 
			
		||||
        if ($diff >= 1.001 && $diff < 12.001) {
 | 
			
		||||
            //            Log::debug(sprintf('Return Y-m because %s', $diff));
 | 
			
		||||
            $format = 'Y-m';
 | 
			
		||||
        }
 | 
			
		||||
@@ -566,7 +566,7 @@ class Navigation
 | 
			
		||||
    {
 | 
			
		||||
        $locale = app('steam')->getLocale();
 | 
			
		||||
        $diff   = $start->diffInMonths($end, true);
 | 
			
		||||
        if ($diff >= 1.001) {
 | 
			
		||||
        if ($diff >= 1.001 && $diff < 12.001) {
 | 
			
		||||
            return (string) trans('config.month_js', [], $locale);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -584,7 +584,7 @@ class Navigation
 | 
			
		||||
    public function preferredEndOfPeriod(Carbon $start, Carbon $end): string
 | 
			
		||||
    {
 | 
			
		||||
        $diff = $start->diffInMonths($end, true);
 | 
			
		||||
        if ($diff >= 1.001) {
 | 
			
		||||
        if ($diff >= 1.001 && $diff < 12.001) {
 | 
			
		||||
            return 'endOfMonth';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -602,7 +602,7 @@ class Navigation
 | 
			
		||||
    public function preferredRangeFormat(Carbon $start, Carbon $end): string
 | 
			
		||||
    {
 | 
			
		||||
        $diff = $start->diffInMonths($end, true);
 | 
			
		||||
        if ($diff >= 1.001) {
 | 
			
		||||
        if ($diff >= 1.001 && $diff < 12.001) {
 | 
			
		||||
            return '1M';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -620,7 +620,7 @@ class Navigation
 | 
			
		||||
    public function preferredSqlFormat(Carbon $start, Carbon $end): string
 | 
			
		||||
    {
 | 
			
		||||
        $diff = $start->diffInMonths($end, true);
 | 
			
		||||
        if ($diff >= 1.001) {
 | 
			
		||||
        if ($diff >= 1.001 && $diff < 12.001) {
 | 
			
		||||
            return '%Y-%m';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +1,9 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * BudgetLimitHandler.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\Handlers\Events\Model;
 | 
			
		||||
namespace FireflyIII\Support\Observers;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Events\Model\BudgetLimit\Created;
 | 
			
		||||
use FireflyIII\Events\Model\BudgetLimit\Deleted;
 | 
			
		||||
use FireflyIII\Events\Model\BudgetLimit\Updated;
 | 
			
		||||
use FireflyIII\Models\AvailableBudget;
 | 
			
		||||
use FireflyIII\Models\Budget;
 | 
			
		||||
use FireflyIII\Models\BudgetLimit;
 | 
			
		||||
@@ -39,17 +16,8 @@ use Spatie\Period\Boundaries;
 | 
			
		||||
use Spatie\Period\Period;
 | 
			
		||||
use Spatie\Period\Precision;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class BudgetLimitHandler
 | 
			
		||||
 */
 | 
			
		||||
class BudgetLimitHandler
 | 
			
		||||
trait RecalculatesAvailableBudgetsTrait
 | 
			
		||||
{
 | 
			
		||||
    public function created(Created $event): void
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(sprintf('BudgetLimitHandler::created(#%s)', $event->budgetLimit->id));
 | 
			
		||||
        $this->updateAvailableBudget($event->budgetLimit);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateAvailableBudget(BudgetLimit $budgetLimit): void
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(sprintf('Now in updateAvailableBudget(limit #%d)', $budgetLimit->id));
 | 
			
		||||
@@ -241,18 +209,4 @@ class BudgetLimitHandler
 | 
			
		||||
 | 
			
		||||
        return $amount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleted(Deleted $event): void
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(sprintf('BudgetLimitHandler::deleted(#%s)', $event->budgetLimit->id));
 | 
			
		||||
        $budgetLimit     = $event->budgetLimit;
 | 
			
		||||
        $budgetLimit->id = 0;
 | 
			
		||||
        $this->updateAvailableBudget($event->budgetLimit);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function updated(Updated $event): void
 | 
			
		||||
    {
 | 
			
		||||
        Log::debug(sprintf('BudgetLimitHandler::updated(#%s)', $event->budgetLimit->id));
 | 
			
		||||
        $this->updateAvailableBudget($event->budgetLimit);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -66,7 +66,7 @@ trait UserGroupTrait
 | 
			
		||||
        if ($user instanceof User) {
 | 
			
		||||
            $this->user      = $user;
 | 
			
		||||
            if (null === $user->userGroup) {
 | 
			
		||||
                throw new FireflyException(sprintf('User #%d has no user group.', $user->id));
 | 
			
		||||
                throw new FireflyException(sprintf('User #%d ("%s") has no user group.', $user->id, $user->email));
 | 
			
		||||
            }
 | 
			
		||||
            $this->userGroup = $user->userGroup;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -255,7 +255,7 @@ trait ConvertsDataTypes
 | 
			
		||||
        if (10 === strlen((string) $value)) {
 | 
			
		||||
            // probably a date format.
 | 
			
		||||
            try {
 | 
			
		||||
                $carbon = Carbon::createFromFormat('Y-m-d', $value);
 | 
			
		||||
                $carbon = Carbon::createFromFormat('Y-m-d', $value, config('app.timezone'));
 | 
			
		||||
            } catch (InvalidDateException $e) { // @phpstan-ignore-line
 | 
			
		||||
                Log::error(sprintf('[1] "%s" is not a valid date: %s', $value, $e->getMessage()));
 | 
			
		||||
 | 
			
		||||
@@ -276,7 +276,7 @@ trait ConvertsDataTypes
 | 
			
		||||
 | 
			
		||||
        // is an atom string, I hope?
 | 
			
		||||
        try {
 | 
			
		||||
            $carbon = Carbon::parse($value);
 | 
			
		||||
            $carbon = Carbon::parse($value, $value, config('app.timezone'));
 | 
			
		||||
        } catch (InvalidDateException $e) { // @phpstan-ignore-line
 | 
			
		||||
            Log::error(sprintf('[3] "%s" is not a valid date or time: %s', $value, $e->getMessage()));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,10 +24,15 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Support\Request;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Exceptions\FireflyException;
 | 
			
		||||
 | 
			
		||||
trait GetFilterInstructions
 | 
			
		||||
{
 | 
			
		||||
    private const string INVALID_FILTER = '%INVALID_JAMES_%';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @throws FireflyException
 | 
			
		||||
     */
 | 
			
		||||
    final public function getFilterInstructions(string $key): array
 | 
			
		||||
    {
 | 
			
		||||
        $config  = config(sprintf('firefly.filters.allowed.%s', $key));
 | 
			
		||||
@@ -48,7 +53,7 @@ trait GetFilterInstructions
 | 
			
		||||
 | 
			
		||||
            switch ($filterType) {
 | 
			
		||||
                default:
 | 
			
		||||
                    exit(sprintf('Do not support filter type "%s"', $filterType));
 | 
			
		||||
                    throw new FireflyException(sprintf('Do not support filter type "%s"', $filterType));
 | 
			
		||||
 | 
			
		||||
                case 'boolean':
 | 
			
		||||
                    $filterValue = $this->booleanInstruction($filterValue);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								app/Support/Request/ValidatesWebhooks.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								app/Support/Request/ValidatesWebhooks.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace FireflyIII\Support\Request;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
use FireflyIII\Models\Webhook;
 | 
			
		||||
use Illuminate\Contracts\Validation\Validator;
 | 
			
		||||
use Illuminate\Support\Facades\Log;
 | 
			
		||||
 | 
			
		||||
trait ValidatesWebhooks
 | 
			
		||||
{
 | 
			
		||||
    public function withValidator(Validator $validator): void
 | 
			
		||||
    {
 | 
			
		||||
        $validator->after(
 | 
			
		||||
            function (Validator $validator): void {
 | 
			
		||||
                Log::debug('Validating webhook');
 | 
			
		||||
                if ($validator->failed()) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                $data           = $validator->getData();
 | 
			
		||||
                $triggers       = $data['triggers'] ?? [];
 | 
			
		||||
                $responses      = $data['responses'] ?? [];
 | 
			
		||||
 | 
			
		||||
                if (0 === count($triggers) || 0 === count($responses)) {
 | 
			
		||||
                    Log::debug('No trigger or response, return.');
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                $validTriggers  = array_values(Webhook::getTriggers());
 | 
			
		||||
                $validResponses = array_values(Webhook::getResponses());
 | 
			
		||||
                $containsAny    = false;
 | 
			
		||||
                $count          = 0;
 | 
			
		||||
                foreach ($triggers as $trigger) {
 | 
			
		||||
                    if (!in_array($trigger, $validTriggers, true)) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    ++$count;
 | 
			
		||||
                    if ($trigger === WebhookTrigger::ANY->name) {
 | 
			
		||||
                        $containsAny = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if ($containsAny && $count > 1) {
 | 
			
		||||
                    $validator->errors()->add('triggers.0', trans('validation.only_any_trigger'));
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                foreach ($responses as $response) {
 | 
			
		||||
                    if (!in_array($response, $validResponses, true)) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // some combinations are illegal.
 | 
			
		||||
                foreach ($triggers as $i => $trigger) {
 | 
			
		||||
                    $forbidden = config(sprintf('webhooks.forbidden_responses.%s', $trigger));
 | 
			
		||||
                    if (null === $forbidden) {
 | 
			
		||||
                        $validator->errors()->add(sprintf('triggers.%d', $i), trans('validation.unknown_webhook_trigger', ['trigger' => $trigger]));
 | 
			
		||||
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    foreach ($responses as $ii => $response) {
 | 
			
		||||
                        if (in_array($response, $forbidden, true)) {
 | 
			
		||||
                            Log::debug(sprintf('Trigger %s and response %s are forbidden.', $trigger, $response));
 | 
			
		||||
                            $validator->errors()->add(sprintf('responses.%d', $ii), trans('validation.bad_webhook_combination', ['trigger' => $trigger, 'response' => $response]));
 | 
			
		||||
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -356,8 +356,10 @@ class Steam
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            $accountSum           = array_values($accountSum)[0];
 | 
			
		||||
            $sumOfAmount          = (string)$accountSum['sum_of_amount'];
 | 
			
		||||
            $sumOfAmount          = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount);
 | 
			
		||||
            $sumsByCode           = [
 | 
			
		||||
                $accountSum['code'] => $accountSum['sum_of_amount'],
 | 
			
		||||
                $accountSum['code'] => $sumOfAmount,
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            // Log::debug('All balances are (joined)', $others);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ use FireflyIII\Models\BudgetLimit;
 | 
			
		||||
use FireflyIII\Models\TransactionCurrency;
 | 
			
		||||
use FireflyIII\Support\Facades\Amount;
 | 
			
		||||
use FireflyIII\Support\Facades\Steam;
 | 
			
		||||
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
 | 
			
		||||
use League\Fractal\Resource\Item;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -55,7 +56,15 @@ class BudgetLimitTransformer extends AbstractTransformer
 | 
			
		||||
     */
 | 
			
		||||
    public function includeBudget(BudgetLimit $limit)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->item($limit->budget, new BudgetTransformer(), 'budgets');
 | 
			
		||||
        // enrich budget
 | 
			
		||||
        $budget     = $limit->budget;
 | 
			
		||||
        $enrichment = new BudgetEnrichment();
 | 
			
		||||
        $enrichment->setStart($this->parameters->get('start'));
 | 
			
		||||
        $enrichment->setEnd($this->parameters->get('end'));
 | 
			
		||||
        $enrichment->setUser($budget->user);
 | 
			
		||||
        $budget     = $enrichment->enrichSingle($budget);
 | 
			
		||||
 | 
			
		||||
        return $this->item($budget, new BudgetTransformer(), 'budgets');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -91,7 +100,7 @@ class BudgetLimitTransformer extends AbstractTransformer
 | 
			
		||||
            'currency_symbol'                 => $currency->symbol,
 | 
			
		||||
            'currency_decimal_places'         => $currency->decimal_places,
 | 
			
		||||
 | 
			
		||||
            'primary_currency_id'             => (int)$this->primaryCurrency->id,
 | 
			
		||||
            'primary_currency_id'             => (string) $this->primaryCurrency->id,
 | 
			
		||||
            'primary_currency_name'           => $this->primaryCurrency->name,
 | 
			
		||||
            'primary_currency_code'           => $this->primaryCurrency->code,
 | 
			
		||||
            'primary_currency_symbol'         => $this->primaryCurrency->symbol,
 | 
			
		||||
 
 | 
			
		||||
@@ -51,9 +51,12 @@ class WebhookTransformer extends AbstractTransformer
 | 
			
		||||
            'active'     => $webhook->active,
 | 
			
		||||
            'title'      => $webhook->title,
 | 
			
		||||
            'secret'     => $webhook->secret,
 | 
			
		||||
            'trigger'    => $this->getEnum('trigger', $webhook->trigger),
 | 
			
		||||
            'response'   => $this->getEnum('response', $webhook->response),
 | 
			
		||||
            'delivery'   => $this->getEnum('delivery', $webhook->delivery),
 | 
			
		||||
            'triggers'   => $webhook->meta['triggers'],
 | 
			
		||||
            'deliveries' => $webhook->meta['deliveries'],
 | 
			
		||||
            'responses'  => $webhook->meta['responses'],
 | 
			
		||||
            //            'trigger'    => $this->getEnum('trigger', $webhook->trigger),
 | 
			
		||||
            //            'response'   => $this->getEnum('response', $webhook->response),
 | 
			
		||||
            //            'delivery'   => $this->getEnum('delivery', $webhook->delivery),
 | 
			
		||||
            'url'        => $webhook->url,
 | 
			
		||||
            'links'      => [
 | 
			
		||||
                [
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,7 @@ class User extends Authenticatable
 | 
			
		||||
    use HasApiTokens;
 | 
			
		||||
    use Notifiable;
 | 
			
		||||
    use ReturnsIntegerIdTrait;
 | 
			
		||||
    protected $fillable = ['email', 'password', 'blocked', 'blocked_code'];
 | 
			
		||||
    protected $fillable = ['email', 'password', 'blocked', 'blocked_code', 'user_group_id'];
 | 
			
		||||
    protected $hidden   = ['password', 'remember_token'];
 | 
			
		||||
    protected $table    = 'users';
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								changelog.md
									
									
									
									
									
								
							@@ -3,7 +3,27 @@
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
This project adheres to [Semantic Versioning](http://semver.org/).
 | 
			
		||||
 | 
			
		||||
## 6.3.0 - 2025-08-xx
 | 
			
		||||
## 6.3.2 - 2025-08-20
 | 
			
		||||
 | 
			
		||||
### Fixed
 | 
			
		||||
 | 
			
		||||
- [Discussion 10768](https://github.com/orgs/firefly-iii/discussions/10768) (Argument #1 ($start) must be of type Carbon\Carbon, null given) started by @tangodance
 | 
			
		||||
- [Issue 10771](https://github.com/firefly-iii/firefly-iii/issues/10771) (/v1/budgets/{id}/limits seems broken) reported by @Sceptorrh
 | 
			
		||||
- [Issue 10773](https://github.com/firefly-iii/firefly-iii/issues/10773) (API: Wrong Return types) reported by @dreautall
 | 
			
		||||
- [Issue 10775](https://github.com/firefly-iii/firefly-iii/issues/10775) (API: /v1/chart/account/overview broken) reported by @dreautall
 | 
			
		||||
- [Issue 10782](https://github.com/firefly-iii/firefly-iii/issues/10782) ([error'] /accounts/[asset,expense,revenue]) reported by @vkanev
 | 
			
		||||
 | 
			
		||||
## 6.3.1 - 2025-08-19
 | 
			
		||||
 | 
			
		||||
### Fixed 
 | 
			
		||||
 | 
			
		||||
- [Discussion 10768](https://github.com/orgs/firefly-iii/discussions/10768) (Argument #1 ($start) must be of type Carbon\Carbon, null given) started by @tangodance
 | 
			
		||||
- [Issue 10771](https://github.com/firefly-iii/firefly-iii/issues/10771) (/v1/budgets/{id}/limits seems broken) reported by @Sceptorrh
 | 
			
		||||
- [Issue 10773](https://github.com/firefly-iii/firefly-iii/issues/10773) (API: Wrong Return types) reported by @dreautall
 | 
			
		||||
- [Issue 10775](https://github.com/firefly-iii/firefly-iii/issues/10775) (API: /v1/chart/account/overview broken) reported by @dreautall
 | 
			
		||||
- [Issue 10782](https://github.com/firefly-iii/firefly-iii/issues/10782) ([error'] /accounts/[asset,expense,revenue]) reported by @vkanev
 | 
			
		||||
 | 
			
		||||
## 6.3.0 - 2025-08-17
 | 
			
		||||
 | 
			
		||||
> [!WARNING]
 | 
			
		||||
> Firefly III v6.3.0 introduces a lot of API changes that deal with multi-currency support. Make sure your beloved apps are updated to support this.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										192
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										192
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							@@ -1878,16 +1878,16 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "laravel/framework",
 | 
			
		||||
            "version": "v12.24.0",
 | 
			
		||||
            "version": "v12.25.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/laravel/framework.git",
 | 
			
		||||
                "reference": "6dcf2c46da23d159f35d6246234953a74b740d83"
 | 
			
		||||
                "reference": "2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/laravel/framework/zipball/6dcf2c46da23d159f35d6246234953a74b740d83",
 | 
			
		||||
                "reference": "6dcf2c46da23d159f35d6246234953a74b740d83",
 | 
			
		||||
                "url": "https://api.github.com/repos/laravel/framework/zipball/2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20",
 | 
			
		||||
                "reference": "2ee2ba94ae60efd24c7a787cbb1a2f82f714bb20",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -2091,7 +2091,7 @@
 | 
			
		||||
                "issues": "https://github.com/laravel/framework/issues",
 | 
			
		||||
                "source": "https://github.com/laravel/framework"
 | 
			
		||||
            },
 | 
			
		||||
            "time": "2025-08-13T20:30:36+00:00"
 | 
			
		||||
            "time": "2025-08-18T22:20:52+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "laravel/passport",
 | 
			
		||||
@@ -4734,16 +4734,16 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "phpoption/phpoption",
 | 
			
		||||
            "version": "1.9.3",
 | 
			
		||||
            "version": "1.9.4",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/schmittjoh/php-option.git",
 | 
			
		||||
                "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54"
 | 
			
		||||
                "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54",
 | 
			
		||||
                "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54",
 | 
			
		||||
                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d",
 | 
			
		||||
                "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -4751,7 +4751,7 @@
 | 
			
		||||
            },
 | 
			
		||||
            "require-dev": {
 | 
			
		||||
                "bamarni/composer-bin-plugin": "^1.8.2",
 | 
			
		||||
                "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
 | 
			
		||||
                "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "library",
 | 
			
		||||
            "extra": {
 | 
			
		||||
@@ -4793,7 +4793,7 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "issues": "https://github.com/schmittjoh/php-option/issues",
 | 
			
		||||
                "source": "https://github.com/schmittjoh/php-option/tree/1.9.3"
 | 
			
		||||
                "source": "https://github.com/schmittjoh/php-option/tree/1.9.4"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -4805,7 +4805,7 @@
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2024-07-20T21:41:07+00:00"
 | 
			
		||||
            "time": "2025-08-21T11:53:16+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "phpseclib/phpseclib",
 | 
			
		||||
@@ -7956,7 +7956,7 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-ctype",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-ctype.git",
 | 
			
		||||
@@ -8015,7 +8015,7 @@
 | 
			
		||||
                "portable"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8026,6 +8026,10 @@
 | 
			
		||||
                    "url": "https://github.com/fabpot",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/nicolas-grekas",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
@@ -8035,16 +8039,16 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-intl-grapheme",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
 | 
			
		||||
                "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe"
 | 
			
		||||
                "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
 | 
			
		||||
                "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70",
 | 
			
		||||
                "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -8093,7 +8097,7 @@
 | 
			
		||||
                "shim"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8104,16 +8108,20 @@
 | 
			
		||||
                    "url": "https://github.com/fabpot",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/nicolas-grekas",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2024-09-09T11:45:10+00:00"
 | 
			
		||||
            "time": "2025-06-27T09:58:17+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-intl-idn",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-intl-idn.git",
 | 
			
		||||
@@ -8176,7 +8184,7 @@
 | 
			
		||||
                "shim"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8187,6 +8195,10 @@
 | 
			
		||||
                    "url": "https://github.com/fabpot",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/nicolas-grekas",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
@@ -8196,7 +8208,7 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-intl-normalizer",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
 | 
			
		||||
@@ -8257,7 +8269,7 @@
 | 
			
		||||
                "shim"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8268,6 +8280,10 @@
 | 
			
		||||
                    "url": "https://github.com/fabpot",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/nicolas-grekas",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
@@ -8277,7 +8293,7 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-mbstring",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-mbstring.git",
 | 
			
		||||
@@ -8338,7 +8354,7 @@
 | 
			
		||||
                "shim"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8349,6 +8365,10 @@
 | 
			
		||||
                    "url": "https://github.com/fabpot",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/nicolas-grekas",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
@@ -8358,7 +8378,7 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-php80",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-php80.git",
 | 
			
		||||
@@ -8418,7 +8438,7 @@
 | 
			
		||||
                "shim"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8429,6 +8449,10 @@
 | 
			
		||||
                    "url": "https://github.com/fabpot",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/nicolas-grekas",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
@@ -8438,16 +8462,16 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-php83",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-php83.git",
 | 
			
		||||
                "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491"
 | 
			
		||||
                "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491",
 | 
			
		||||
                "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5",
 | 
			
		||||
                "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -8494,7 +8518,7 @@
 | 
			
		||||
                "shim"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8505,25 +8529,29 @@
 | 
			
		||||
                    "url": "https://github.com/fabpot",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/nicolas-grekas",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2024-09-09T11:45:10+00:00"
 | 
			
		||||
            "time": "2025-07-08T02:45:35+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-php84",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-php84.git",
 | 
			
		||||
                "reference": "000df7860439609837bbe28670b0be15783b7fbf"
 | 
			
		||||
                "reference": "d8ced4d875142b6a7426000426b8abc631d6b191"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/000df7860439609837bbe28670b0be15783b7fbf",
 | 
			
		||||
                "reference": "000df7860439609837bbe28670b0be15783b7fbf",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191",
 | 
			
		||||
                "reference": "d8ced4d875142b6a7426000426b8abc631d6b191",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -8570,7 +8598,7 @@
 | 
			
		||||
                "shim"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-php84/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8581,25 +8609,29 @@
 | 
			
		||||
                    "url": "https://github.com/fabpot",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/nicolas-grekas",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2025-02-20T12:04:08+00:00"
 | 
			
		||||
            "time": "2025-06-24T13:30:11+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-php85",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-php85.git",
 | 
			
		||||
                "reference": "6fedf31ce4e3648f4ff5ca58bfd53127d38f05fd"
 | 
			
		||||
                "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/6fedf31ce4e3648f4ff5ca58bfd53127d38f05fd",
 | 
			
		||||
                "reference": "6fedf31ce4e3648f4ff5ca58bfd53127d38f05fd",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91",
 | 
			
		||||
                "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -8646,7 +8678,7 @@
 | 
			
		||||
                "shim"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-php85/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8657,16 +8689,20 @@
 | 
			
		||||
                    "url": "https://github.com/fabpot",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/nicolas-grekas",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2025-05-02T08:40:52+00:00"
 | 
			
		||||
            "time": "2025-06-23T16:12:55+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-uuid",
 | 
			
		||||
            "version": "v1.32.0",
 | 
			
		||||
            "version": "v1.33.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-uuid.git",
 | 
			
		||||
@@ -8725,7 +8761,7 @@
 | 
			
		||||
                "uuid"
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0"
 | 
			
		||||
                "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -8736,6 +8772,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"
 | 
			
		||||
@@ -10348,16 +10388,16 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "composer/class-map-generator",
 | 
			
		||||
            "version": "1.6.1",
 | 
			
		||||
            "version": "1.6.2",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/composer/class-map-generator.git",
 | 
			
		||||
                "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34"
 | 
			
		||||
                "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/composer/class-map-generator/zipball/134b705ddb0025d397d8318a75825fe3c9d1da34",
 | 
			
		||||
                "reference": "134b705ddb0025d397d8318a75825fe3c9d1da34",
 | 
			
		||||
                "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ba9f089655d4cdd64e762a6044f411ccdaec0076",
 | 
			
		||||
                "reference": "ba9f089655d4cdd64e762a6044f411ccdaec0076",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -10401,7 +10441,7 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "support": {
 | 
			
		||||
                "issues": "https://github.com/composer/class-map-generator/issues",
 | 
			
		||||
                "source": "https://github.com/composer/class-map-generator/tree/1.6.1"
 | 
			
		||||
                "source": "https://github.com/composer/class-map-generator/tree/1.6.2"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -10411,13 +10451,9 @@
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://github.com/composer",
 | 
			
		||||
                    "type": "github"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2025-03-24T13:50:44+00:00"
 | 
			
		||||
            "time": "2025-08-20T18:52:43+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "composer/pcre",
 | 
			
		||||
@@ -10500,16 +10536,16 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "driftingly/rector-laravel",
 | 
			
		||||
            "version": "2.0.6",
 | 
			
		||||
            "version": "2.0.7",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/driftingly/rector-laravel.git",
 | 
			
		||||
                "reference": "5be95811801fc06126dd844beaeb6a41721ba3d3"
 | 
			
		||||
                "reference": "625dc02cee08d47ecf0ac86de2f02a55026cf34e"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/5be95811801fc06126dd844beaeb6a41721ba3d3",
 | 
			
		||||
                "reference": "5be95811801fc06126dd844beaeb6a41721ba3d3",
 | 
			
		||||
                "url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/625dc02cee08d47ecf0ac86de2f02a55026cf34e",
 | 
			
		||||
                "reference": "625dc02cee08d47ecf0ac86de2f02a55026cf34e",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -10529,9 +10565,9 @@
 | 
			
		||||
            "description": "Rector upgrades rules for Laravel Framework",
 | 
			
		||||
            "support": {
 | 
			
		||||
                "issues": "https://github.com/driftingly/rector-laravel/issues",
 | 
			
		||||
                "source": "https://github.com/driftingly/rector-laravel/tree/2.0.6"
 | 
			
		||||
                "source": "https://github.com/driftingly/rector-laravel/tree/2.0.7"
 | 
			
		||||
            },
 | 
			
		||||
            "time": "2025-08-08T22:10:01+00:00"
 | 
			
		||||
            "time": "2025-08-19T20:49:47+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "fakerphp/faker",
 | 
			
		||||
@@ -11771,16 +11807,16 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "phpunit/phpunit",
 | 
			
		||||
            "version": "12.3.4",
 | 
			
		||||
            "version": "12.3.6",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/sebastianbergmann/phpunit.git",
 | 
			
		||||
                "reference": "429095031bd38cb5070ca44166bd9dd5a9245dd6"
 | 
			
		||||
                "reference": "a2cab3224f687150ac2f3cc13d99b64ba1e1d088"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/429095031bd38cb5070ca44166bd9dd5a9245dd6",
 | 
			
		||||
                "reference": "429095031bd38cb5070ca44166bd9dd5a9245dd6",
 | 
			
		||||
                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2cab3224f687150ac2f3cc13d99b64ba1e1d088",
 | 
			
		||||
                "reference": "a2cab3224f687150ac2f3cc13d99b64ba1e1d088",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -11800,9 +11836,9 @@
 | 
			
		||||
                "phpunit/php-text-template": "^5.0.0",
 | 
			
		||||
                "phpunit/php-timer": "^8.0.0",
 | 
			
		||||
                "sebastian/cli-parser": "^4.0.0",
 | 
			
		||||
                "sebastian/comparator": "^7.1.2",
 | 
			
		||||
                "sebastian/comparator": "^7.1.3",
 | 
			
		||||
                "sebastian/diff": "^7.0.0",
 | 
			
		||||
                "sebastian/environment": "^8.0.2",
 | 
			
		||||
                "sebastian/environment": "^8.0.3",
 | 
			
		||||
                "sebastian/exporter": "^7.0.0",
 | 
			
		||||
                "sebastian/global-state": "^8.0.0",
 | 
			
		||||
                "sebastian/object-enumerator": "^7.0.0",
 | 
			
		||||
@@ -11848,7 +11884,7 @@
 | 
			
		||||
            "support": {
 | 
			
		||||
                "issues": "https://github.com/sebastianbergmann/phpunit/issues",
 | 
			
		||||
                "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
 | 
			
		||||
                "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.4"
 | 
			
		||||
                "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.6"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -11872,7 +11908,7 @@
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2025-08-12T07:35:30+00:00"
 | 
			
		||||
            "time": "2025-08-20T14:43:23+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "rector/rector",
 | 
			
		||||
@@ -11993,16 +12029,16 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "sebastian/comparator",
 | 
			
		||||
            "version": "7.1.2",
 | 
			
		||||
            "version": "7.1.3",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/sebastianbergmann/comparator.git",
 | 
			
		||||
                "reference": "1a7c2bce03a13a457ed3c975dfd331b3b4b133aa"
 | 
			
		||||
                "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1a7c2bce03a13a457ed3c975dfd331b3b4b133aa",
 | 
			
		||||
                "reference": "1a7c2bce03a13a457ed3c975dfd331b3b4b133aa",
 | 
			
		||||
                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dc904b4bb3ab070865fa4068cd84f3da8b945148",
 | 
			
		||||
                "reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@@ -12061,7 +12097,7 @@
 | 
			
		||||
            "support": {
 | 
			
		||||
                "issues": "https://github.com/sebastianbergmann/comparator/issues",
 | 
			
		||||
                "security": "https://github.com/sebastianbergmann/comparator/security/policy",
 | 
			
		||||
                "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.2"
 | 
			
		||||
                "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.3"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -12081,7 +12117,7 @@
 | 
			
		||||
                    "type": "tidelift"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2025-08-10T08:50:08+00:00"
 | 
			
		||||
            "time": "2025-08-20T11:27:00+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "sebastian/complexity",
 | 
			
		||||
 
 | 
			
		||||
@@ -78,8 +78,8 @@ return [
 | 
			
		||||
        'running_balance_column' => env('USE_RUNNING_BALANCE', false),
 | 
			
		||||
        // see cer.php for exchange rates feature flag.
 | 
			
		||||
    ],
 | 
			
		||||
    'version'                      => 'develop/2025-08-16',
 | 
			
		||||
    'build_time'                   => 1755317476,
 | 
			
		||||
    'version'                      => 'develop/2025-08-22',
 | 
			
		||||
    'build_time'                   => 1755838953,
 | 
			
		||||
    'api_version'                  => '2.1.0', // field is no longer used.
 | 
			
		||||
    'db_version'                   => 26,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,7 @@ return [
 | 
			
		||||
                'liability_direction_credit_short',
 | 
			
		||||
                'liability_direction_null_short',
 | 
			
		||||
                'interest_calc_yearly',
 | 
			
		||||
                'loading',
 | 
			
		||||
                'interest_calc_',
 | 
			
		||||
                'interest_calc_null',
 | 
			
		||||
                'interest_calc_daily',
 | 
			
		||||
@@ -246,12 +247,21 @@ return [
 | 
			
		||||
                'multi_account_warning_withdrawal',
 | 
			
		||||
                'multi_account_warning_deposit',
 | 
			
		||||
                'multi_account_warning_transfer',
 | 
			
		||||
 | 
			
		||||
                'webhook_trigger_ANY',
 | 
			
		||||
                'webhook_trigger_STORE_TRANSACTION',
 | 
			
		||||
                'webhook_trigger_UPDATE_TRANSACTION',
 | 
			
		||||
                'webhook_trigger_DESTROY_TRANSACTION',
 | 
			
		||||
 | 
			
		||||
                'webhook_trigger_STORE_BUDGET',
 | 
			
		||||
                'webhook_trigger_UPDATE_BUDGET',
 | 
			
		||||
                'webhook_trigger_DESTROY_BUDGET',
 | 
			
		||||
                'webhook_trigger_STORE_UPDATE_BUDGET_LIMIT',
 | 
			
		||||
 | 
			
		||||
                'webhook_response_TRANSACTIONS',
 | 
			
		||||
                'webhook_response_RELEVANT',
 | 
			
		||||
                'webhook_response_ACCOUNTS',
 | 
			
		||||
                'webhook_response_none_NONE',
 | 
			
		||||
                'webhook_response_NONE',
 | 
			
		||||
                'webhook_delivery_JSON',
 | 
			
		||||
                'actions',
 | 
			
		||||
                'meta_data',
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								config/webhooks.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								config/webhooks.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
// this is hard coded, which is unfortunate.
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Enums\WebhookResponse;
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
 | 
			
		||||
return [
 | 
			
		||||
    'force_relevant_response' => [
 | 
			
		||||
        WebhookTrigger::STORE_TRANSACTION->name         => [
 | 
			
		||||
            WebhookTrigger::STORE_BUDGET->name,
 | 
			
		||||
            WebhookTrigger::UPDATE_BUDGET->name,
 | 
			
		||||
            WebhookTrigger::DESTROY_BUDGET->name,
 | 
			
		||||
            WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name,
 | 
			
		||||
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::UPDATE_TRANSACTION->name        => [
 | 
			
		||||
            WebhookTrigger::STORE_BUDGET->name,
 | 
			
		||||
            WebhookTrigger::UPDATE_BUDGET->name,
 | 
			
		||||
            WebhookTrigger::DESTROY_BUDGET->name,
 | 
			
		||||
            WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::DESTROY_TRANSACTION->name       => [
 | 
			
		||||
            WebhookTrigger::STORE_BUDGET->name,
 | 
			
		||||
            WebhookTrigger::UPDATE_BUDGET->name,
 | 
			
		||||
            WebhookTrigger::DESTROY_BUDGET->name,
 | 
			
		||||
            WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::STORE_BUDGET->name              => [
 | 
			
		||||
            WebhookTrigger::STORE_TRANSACTION->name,
 | 
			
		||||
            WebhookTrigger::UPDATE_TRANSACTION->name,
 | 
			
		||||
            WebhookTrigger::DESTROY_TRANSACTION->name,
 | 
			
		||||
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::UPDATE_BUDGET->name             => [
 | 
			
		||||
            WebhookTrigger::STORE_TRANSACTION->name,
 | 
			
		||||
            WebhookTrigger::UPDATE_TRANSACTION->name,
 | 
			
		||||
            WebhookTrigger::DESTROY_TRANSACTION->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::DESTROY_BUDGET->name            => [
 | 
			
		||||
            WebhookTrigger::STORE_TRANSACTION->name,
 | 
			
		||||
            WebhookTrigger::UPDATE_TRANSACTION->name,
 | 
			
		||||
            WebhookTrigger::DESTROY_TRANSACTION->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name => [
 | 
			
		||||
            WebhookTrigger::STORE_TRANSACTION->name,
 | 
			
		||||
            WebhookTrigger::UPDATE_TRANSACTION->name,
 | 
			
		||||
            WebhookTrigger::DESTROY_TRANSACTION->name,
 | 
			
		||||
        ],
 | 
			
		||||
    ],
 | 
			
		||||
    'forbidden_responses'     => [
 | 
			
		||||
        WebhookTrigger::ANY->name                       => [
 | 
			
		||||
            WebhookResponse::BUDGET->name,
 | 
			
		||||
            WebhookResponse::TRANSACTIONS->name,
 | 
			
		||||
            WebhookResponse::ACCOUNTS->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::STORE_TRANSACTION->name         => [
 | 
			
		||||
            WebhookResponse::BUDGET->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::UPDATE_TRANSACTION->name        => [
 | 
			
		||||
            WebhookResponse::BUDGET->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::DESTROY_TRANSACTION->name       => [
 | 
			
		||||
            WebhookResponse::BUDGET->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::STORE_BUDGET->name              => [
 | 
			
		||||
            WebhookResponse::TRANSACTIONS->name,
 | 
			
		||||
            WebhookResponse::ACCOUNTS->name,
 | 
			
		||||
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::UPDATE_BUDGET->name             => [
 | 
			
		||||
            WebhookResponse::TRANSACTIONS->name,
 | 
			
		||||
            WebhookResponse::ACCOUNTS->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::DESTROY_BUDGET->name            => [
 | 
			
		||||
            WebhookResponse::TRANSACTIONS->name,
 | 
			
		||||
            WebhookResponse::ACCOUNTS->name,
 | 
			
		||||
        ],
 | 
			
		||||
        WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT->name => [
 | 
			
		||||
            WebhookResponse::TRANSACTIONS->name,
 | 
			
		||||
            WebhookResponse::ACCOUNTS->name,
 | 
			
		||||
        ],
 | 
			
		||||
    ],
 | 
			
		||||
];
 | 
			
		||||
@@ -0,0 +1,127 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\QueryException;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration {
 | 
			
		||||
    private const TABLE_ALREADY_EXISTS = 'If this table exists already (see the error message), this is not a problem. Other errors? Please open a discussion on GitHub.';
 | 
			
		||||
    private const TABLE_ERROR          = 'Could not create table "%s": %s';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        if (!Schema::hasTable('webhook_triggers')) {
 | 
			
		||||
            Schema::create('webhook_triggers', function (Blueprint $table) {
 | 
			
		||||
                $table->id();
 | 
			
		||||
                $table->timestamps();
 | 
			
		||||
                $table->smallInteger('key')->unsigned();
 | 
			
		||||
                $table->string('title', 100);
 | 
			
		||||
                $table->unique(['key', 'title']);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if (!Schema::hasTable('webhook_responses')) {
 | 
			
		||||
            Schema::create('webhook_responses', function (Blueprint $table) {
 | 
			
		||||
                $table->id();
 | 
			
		||||
                $table->timestamps();
 | 
			
		||||
                $table->smallInteger('key')->unsigned();
 | 
			
		||||
                $table->string('title', 100);
 | 
			
		||||
                $table->unique(['key', 'title']);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if (!Schema::hasTable('webhook_deliveries')) {
 | 
			
		||||
            Schema::create('webhook_deliveries', function (Blueprint $table) {
 | 
			
		||||
                $table->id();
 | 
			
		||||
                $table->timestamps();
 | 
			
		||||
                $table->smallInteger('key')->unsigned();
 | 
			
		||||
                $table->string('title', 100);
 | 
			
		||||
                $table->unique(['key', 'title']);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // webhook_webhook_trigger
 | 
			
		||||
        if (!Schema::hasTable('webhook_webhook_trigger')) {
 | 
			
		||||
            try {
 | 
			
		||||
                Schema::create(
 | 
			
		||||
                    'webhook_webhook_trigger',
 | 
			
		||||
                    static function (Blueprint $table): void {
 | 
			
		||||
                        $table->increments('id');
 | 
			
		||||
                        $table->integer('webhook_id', false, true);
 | 
			
		||||
                        $table->bigInteger('webhook_trigger_id', false, true);
 | 
			
		||||
                        $table->foreign('webhook_id')->references('id')->on('webhooks')->onDelete('cascade');
 | 
			
		||||
                        $table->foreign('webhook_trigger_id','link_to_trigger')->references('id')->on('webhook_triggers')->onDelete('cascade');
 | 
			
		||||
 | 
			
		||||
                        // unique combi:
 | 
			
		||||
                        $table->unique(['webhook_id', 'webhook_trigger_id']);
 | 
			
		||||
                    }
 | 
			
		||||
                );
 | 
			
		||||
            } catch (QueryException $e) {
 | 
			
		||||
                app('log')->error(sprintf(self::TABLE_ERROR, 'webhook_webhook_trigger', $e->getMessage()));
 | 
			
		||||
                app('log')->error(self::TABLE_ALREADY_EXISTS);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // webhook_webhook_response
 | 
			
		||||
        if (!Schema::hasTable('webhook_webhook_response')) {
 | 
			
		||||
            try {
 | 
			
		||||
                Schema::create(
 | 
			
		||||
                    'webhook_webhook_response',
 | 
			
		||||
                    static function (Blueprint $table): void {
 | 
			
		||||
                        $table->increments('id');
 | 
			
		||||
                        $table->integer('webhook_id', false, true);
 | 
			
		||||
                        $table->bigInteger('webhook_response_id', false, true);
 | 
			
		||||
                        $table->foreign('webhook_id')->references('id')->on('webhooks')->onDelete('cascade');
 | 
			
		||||
                        $table->foreign('webhook_response_id','link_to_response')->references('id')->on('webhook_responses')->onDelete('cascade');
 | 
			
		||||
 | 
			
		||||
                        // unique combi:
 | 
			
		||||
                        $table->unique(['webhook_id', 'webhook_response_id']);
 | 
			
		||||
                    }
 | 
			
		||||
                );
 | 
			
		||||
            } catch (QueryException $e) {
 | 
			
		||||
                app('log')->error(sprintf(self::TABLE_ERROR, 'webhook_webhook_response', $e->getMessage()));
 | 
			
		||||
                app('log')->error(self::TABLE_ALREADY_EXISTS);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // webhook_webhook_delivery
 | 
			
		||||
        if (!Schema::hasTable('webhook_webhook_delivery')) {
 | 
			
		||||
            try {
 | 
			
		||||
                Schema::create(
 | 
			
		||||
                    'webhook_webhook_delivery',
 | 
			
		||||
                    static function (Blueprint $table): void {
 | 
			
		||||
                        $table->increments('id');
 | 
			
		||||
                        $table->integer('webhook_id', false, true);
 | 
			
		||||
                        $table->bigInteger('webhook_delivery_id', false, true);
 | 
			
		||||
                        $table->foreign('webhook_id')->references('id')->on('webhooks')->onDelete('cascade');
 | 
			
		||||
                        $table->foreign('webhook_delivery_id','link_to_delivery')->references('id')->on('webhook_deliveries')->onDelete('cascade');
 | 
			
		||||
 | 
			
		||||
                        // unique combi:
 | 
			
		||||
                        $table->unique(['webhook_id', 'webhook_delivery_id']);
 | 
			
		||||
                    }
 | 
			
		||||
                );
 | 
			
		||||
            } catch (QueryException $e) {
 | 
			
		||||
                app('log')->error(sprintf(self::TABLE_ERROR, 'webhook_webhook_delivery', $e->getMessage()));
 | 
			
		||||
                app('log')->error(self::TABLE_ALREADY_EXISTS);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reverse the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function down(): void
 | 
			
		||||
    {
 | 
			
		||||
        Schema::dropIfExists('webhook_webhook_delivery');
 | 
			
		||||
        Schema::dropIfExists('webhook_webhook_trigger');
 | 
			
		||||
        Schema::dropIfExists('webhook_webhook_response');
 | 
			
		||||
 | 
			
		||||
        Schema::dropIfExists('webhook_triggers');
 | 
			
		||||
        Schema::dropIfExists('webhook_responses');
 | 
			
		||||
        Schema::dropIfExists('webhook_deliveries');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -35,25 +35,10 @@ class AccountTypeSeeder extends Seeder
 | 
			
		||||
{
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $types = [
 | 
			
		||||
            AccountTypeEnum::DEFAULT->value,
 | 
			
		||||
            AccountTypeEnum::CASH->value,
 | 
			
		||||
            AccountTypeEnum::ASSET->value,
 | 
			
		||||
            AccountTypeEnum::EXPENSE->value,
 | 
			
		||||
            AccountTypeEnum::REVENUE->value,
 | 
			
		||||
            AccountTypeEnum::INITIAL_BALANCE->value,
 | 
			
		||||
            AccountTypeEnum::BENEFICIARY->value,
 | 
			
		||||
            AccountTypeEnum::IMPORT->value,
 | 
			
		||||
            AccountTypeEnum::LOAN->value,
 | 
			
		||||
            AccountTypeEnum::RECONCILIATION->value,
 | 
			
		||||
            AccountTypeEnum::DEBT->value,
 | 
			
		||||
            AccountTypeEnum::MORTGAGE->value,
 | 
			
		||||
            AccountTypeEnum::LIABILITY_CREDIT->value,
 | 
			
		||||
        ];
 | 
			
		||||
        foreach ($types as $type) {
 | 
			
		||||
            if (null === AccountType::where('type', $type)->first()) {
 | 
			
		||||
        foreach(AccountTypeEnum::cases() as $type) {
 | 
			
		||||
            if (null === AccountType::where('type', $type->value)->first()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    AccountType::create(['type' => $type]);
 | 
			
		||||
                    AccountType::create(['type' => $type->value]);
 | 
			
		||||
                } catch (PDOException $e) {
 | 
			
		||||
                    // @ignoreException
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -43,5 +43,6 @@ class DatabaseSeeder extends Seeder
 | 
			
		||||
        $this->call(ConfigSeeder::class);
 | 
			
		||||
        $this->call(UserRoleSeeder::class);
 | 
			
		||||
        $this->call(ExchangeRateSeeder::class);
 | 
			
		||||
        $this->call(WebhookDataSeeder::class);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,24 +35,17 @@ class TransactionTypeSeeder extends Seeder
 | 
			
		||||
{
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $types = [
 | 
			
		||||
            TransactionTypeEnum::WITHDRAWAL->value,
 | 
			
		||||
            TransactionTypeEnum::DEPOSIT->value,
 | 
			
		||||
            TransactionTypeEnum::TRANSFER->value,
 | 
			
		||||
            TransactionTypeEnum::OPENING_BALANCE->value,
 | 
			
		||||
            TransactionTypeEnum::RECONCILIATION->value,
 | 
			
		||||
            TransactionTypeEnum::INVALID->value,
 | 
			
		||||
            TransactionTypeEnum::LIABILITY_CREDIT->value,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        foreach ($types as $type) {
 | 
			
		||||
            if (null === TransactionType::where('type', $type)->first()) {
 | 
			
		||||
        /** @var TransactionTypeEnum $type */
 | 
			
		||||
        foreach (TransactionTypeEnum::cases() as $type) {
 | 
			
		||||
            if (null === TransactionType::where('type', $type->value)->first()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    TransactionType::create(['type' => $type]);
 | 
			
		||||
                    TransactionType::create(['type' => $type->value]);
 | 
			
		||||
                } catch (PDOException $e) {
 | 
			
		||||
                    // @ignoreException
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,16 +39,11 @@ class UserRoleSeeder extends Seeder
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        $roles = [];
 | 
			
		||||
        /** @var UserRoleEnum $role */
 | 
			
		||||
        foreach (UserRoleEnum::cases() as $role) {
 | 
			
		||||
            $roles[] = $role->value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** @var string $role */
 | 
			
		||||
        foreach ($roles as $role) {
 | 
			
		||||
            if (null === UserRole::where('title', $role)->first()) {
 | 
			
		||||
            if (null === UserRole::where('title', $role->value)->first()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    UserRole::create(['title' => $role]);
 | 
			
		||||
                    UserRole::create(['title' => $role->value]);
 | 
			
		||||
                } catch (PDOException $e) {
 | 
			
		||||
                    // @ignoreException
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								database/seeders/WebhookDataSeeder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								database/seeders/WebhookDataSeeder.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use FireflyIII\Enums\WebhookTrigger;
 | 
			
		||||
use FireflyIII\Enums\WebhookResponse;
 | 
			
		||||
use FireflyIII\Enums\WebhookDelivery;
 | 
			
		||||
use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel;
 | 
			
		||||
use FireflyIII\Models\WebhookResponse as WebhookResponseModel;
 | 
			
		||||
use FireflyIII\Models\WebhookDelivery as WebhookDeliveryModel;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class WebhookDataSeeder extends Seeder
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the database seeds.
 | 
			
		||||
     */
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        foreach (WebhookTrigger::cases() as $trigger) {
 | 
			
		||||
            if (null === WebhookTriggerModel::where('key', $trigger->value)->where('title', $trigger->name)->first()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    WebhookTriggerModel::create(['key' => $trigger->value, 'title' => $trigger->name]);
 | 
			
		||||
                } catch (\PDOException $e) {
 | 
			
		||||
                    // @ignoreException
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        foreach (WebhookResponse::cases() as $response) {
 | 
			
		||||
            if (null === WebhookResponseModel::where('key', $response->value)->where('title', $response->name)->first()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    WebhookResponseModel::create(['key' => $response->value, 'title' => $response->name]);
 | 
			
		||||
                } catch (\PDOException $e) {
 | 
			
		||||
                    // @ignoreException
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        foreach (WebhookDelivery::cases() as $delivery) {
 | 
			
		||||
            if (null === WebhookDeliveryModel::where('key', $delivery->value)->where('title', $delivery->name)->first()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    WebhookDeliveryModel::create(['key' => $delivery->value, 'title' => $delivery->name]);
 | 
			
		||||
                } catch (\PDOException $e) {
 | 
			
		||||
                    // @ignoreException
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										282
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										282
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -2592,9 +2592,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-android-arm-eabi": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-lTahKRJip0knffA/GTNFJMrToD+CM+JJ+Qt5kjzBK/sFQ0EWqfKW3AYQSlZXN98tX0lx66083U9JYIMioMMK7g==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "arm"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2606,9 +2606,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-android-arm64": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-uqxkb3RJLzlBbh/bbNQ4r7YpSZnjgMgyoEOY7Fy6GCbelkDSAzeiogxMG9TfLsBbqmGsdDObo3mzGqa8hps4MA==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "arm64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2620,9 +2620,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-darwin-arm64": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-tV6reObmxBDS4DDyLzTDIpymthNlxrLBGAoQx6m2a7eifSNEZdkXQl1PE4ZjCkEDPVgNXSzND/k9AQ3mC4IOEQ==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "arm64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2634,9 +2634,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-darwin-x64": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-XuJRPTnMk1lwsSnS3vYyVMu4x/+WIw1MMSiqj5C4j3QOWsMzbJEK90zG+SWV1h0B1ABGCQ0UZUjti+TQK35uHQ==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "x64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2648,9 +2648,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-freebsd-arm64": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-79BAm8Ag/tmJ5asCqgOXsb3WY28Rdd5Lxj8ONiQzWzy9LvWORd5qVuOnjlqiWWZJw+dWewEktZb5yiM1DLLaHw==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "arm64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2662,9 +2662,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-freebsd-x64": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-OQ2/ZDGzdOOlyfqBiip0ZX/jVFekzYrGtUsqAfLDbWy0jh1PUU18+jYp8UMpqhly5ltEqotc2miLngf9FPSWIA==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "x64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2676,9 +2676,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-HZZBXJL1udxlCVvoVadstgiU26seKkHbbAMLg7680gAcMnRNP9SAwTMVet02ANA94kXEI2VhBnXs4e5nf7KG2A==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "arm"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2690,9 +2690,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-arm-musleabihf": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-sZ5p2I9UA7T950JmuZ3pgdKA6+RTBr+0FpK427ExW0t7n+QwYOcmDTK/aRlzoBrWyTpJNlS3kacgSlSTUg6P/Q==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "arm"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2704,9 +2704,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-arm64-gnu": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-3hBFoqPyU89Dyf1mQRXCdpc6qC6At3LV6jbbIOZd72jcx7xNk3aAp+EjzAtN6sDlmHFzsDJN5yeUySvorWeRXA==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "arm64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2718,9 +2718,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-arm64-musl": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-49J4FnMHfGodJWPw73Ve+/hsPjZgcXQGkmqBGZFvltzBKRS+cvMiWNLadOMXKGnYRhs1ToTGM0sItKISoSGUNA==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "arm64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2732,9 +2732,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-4yYU8p7AneEpQkRX03pbpLmE21z5JNys16F1BZBZg5fP9rIlb0TkeQjn5du5w4agConCCEoYIG57sNxjryHEGg==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "loong64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2746,9 +2746,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-ppc64-gnu": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-fAiq+J28l2YMWgC39jz/zPi2jqc0y3GSRo1yyxlBHt6UN0yYgnegHSRPa3pnHS5amT/efXQrm0ug5+aNEu9UuQ==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "ppc64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2760,9 +2760,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-riscv64-gnu": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-daoT0PMENNdjVYYU9xec30Y2prb1AbEIbb64sqkcQcSaR0zYuKkoPuhIztfxuqN82KYCKKrj+tQe4Gi7OSm1ow==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "riscv64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2774,9 +2774,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-riscv64-musl": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-JNyXaAhWtdzfXu5pUcHAuNwGQKevR+6z/poYQKVW+pLaYOj9G1meYc57/1Xv2u4uTxfu9qEWmNTjv/H/EpAisw==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "riscv64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2788,9 +2788,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-s390x-gnu": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-U/CHbqKSwEQyZXjCpY43/GLYcTVKEXeRHw0rMBJP7fP3x6WpYG4LTJWR3ic6TeYKX6ZK7mrhltP4ppolyVhLVQ==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "s390x"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2802,9 +2802,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-x64-gnu": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-uTLEakjxOTElfeZIGWkC34u2auLHB1AYS6wBjPGI00bWdxdLcCzK5awjs25YXpqB9lS8S0vbO0t9ZcBeNibA7g==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "x64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2816,9 +2816,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-linux-x64-musl": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-Ft+d/9DXs30BK7CHCTX11FtQGHUdpNDLJW0HHLign4lgMgBcPFN3NkdIXhC5r9iwsMwYreBBc4Rho5ieOmKNVQ==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "x64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2830,9 +2830,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-win32-arm64-msvc": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-N9X5WqGYzZnjGAFsKSfYFtAShYjwOmFJoWbLg3dYixZOZqU7hdMq+/xyS14zKLhFhZDhP9VfkzQnsdk0ZDS9IA==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "arm64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2844,9 +2844,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-win32-ia32-msvc": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-O+KcfeCORZADEY8oQJk4HK8wtEOCRE4MdOkb8qGZQNun3jzmj2nmhV/B/ZaaZOkPmJyvm/gW9n0gsB4eRa1eiQ==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "ia32"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -2858,9 +2858,9 @@
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@rollup/rollup-win32-x64-msvc": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-CpKnYa8eHthJa3c+C38v/E+/KZyF1Jdh2Cz3DyKZqEWYgrM1IHFArXNWvBLPQCKUEsAqqKX27tTqVEFbDNUcOA==",
 | 
			
		||||
            "cpu": [
 | 
			
		||||
                "x64"
 | 
			
		||||
            ],
 | 
			
		||||
@@ -3158,9 +3158,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@types/node-forge": {
 | 
			
		||||
            "version": "1.3.13",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.13.tgz",
 | 
			
		||||
            "integrity": "sha512-zePQJSW5QkwSHKRApqWCVKeKoSOt4xvEnLENZPjyvm9Ezdf/EyDeJM7jqLzOwjVICQQzvLZ63T55MKdJB5H6ww==",
 | 
			
		||||
            "version": "1.3.14",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz",
 | 
			
		||||
            "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
@@ -3256,42 +3256,42 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-core": {
 | 
			
		||||
            "version": "3.5.18",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz",
 | 
			
		||||
            "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==",
 | 
			
		||||
            "version": "3.5.19",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.19.tgz",
 | 
			
		||||
            "integrity": "sha512-/afpyvlkrSNYbPo94Qu8GtIOWS+g5TRdOvs6XZNw6pWQQmj5pBgSZvEPOIZlqWq0YvoUhDDQaQ2TnzuJdOV4hA==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@babel/parser": "^7.28.0",
 | 
			
		||||
                "@vue/shared": "3.5.18",
 | 
			
		||||
                "@babel/parser": "^7.28.3",
 | 
			
		||||
                "@vue/shared": "3.5.19",
 | 
			
		||||
                "entities": "^4.5.0",
 | 
			
		||||
                "estree-walker": "^2.0.2",
 | 
			
		||||
                "source-map-js": "^1.2.1"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-dom": {
 | 
			
		||||
            "version": "3.5.18",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz",
 | 
			
		||||
            "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==",
 | 
			
		||||
            "version": "3.5.19",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.19.tgz",
 | 
			
		||||
            "integrity": "sha512-Drs6rPHQZx/pN9S6ml3Z3K/TWCIRPvzG2B/o5kFK9X0MNHt8/E+38tiRfojufrYBfA6FQUFB2qBBRXlcSXWtOA==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/compiler-core": "3.5.18",
 | 
			
		||||
                "@vue/shared": "3.5.18"
 | 
			
		||||
                "@vue/compiler-core": "3.5.19",
 | 
			
		||||
                "@vue/shared": "3.5.19"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-sfc": {
 | 
			
		||||
            "version": "3.5.18",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz",
 | 
			
		||||
            "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==",
 | 
			
		||||
            "version": "3.5.19",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.19.tgz",
 | 
			
		||||
            "integrity": "sha512-YWCm1CYaJ+2RvNmhCwI7t3I3nU+hOrWGWMsn+Z/kmm1jy5iinnVtlmkiZwbLlbV1SRizX7vHsc0/bG5dj0zRTg==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@babel/parser": "^7.28.0",
 | 
			
		||||
                "@vue/compiler-core": "3.5.18",
 | 
			
		||||
                "@vue/compiler-dom": "3.5.18",
 | 
			
		||||
                "@vue/compiler-ssr": "3.5.18",
 | 
			
		||||
                "@vue/shared": "3.5.18",
 | 
			
		||||
                "@babel/parser": "^7.28.3",
 | 
			
		||||
                "@vue/compiler-core": "3.5.19",
 | 
			
		||||
                "@vue/compiler-dom": "3.5.19",
 | 
			
		||||
                "@vue/compiler-ssr": "3.5.19",
 | 
			
		||||
                "@vue/shared": "3.5.19",
 | 
			
		||||
                "estree-walker": "^2.0.2",
 | 
			
		||||
                "magic-string": "^0.30.17",
 | 
			
		||||
                "postcss": "^8.5.6",
 | 
			
		||||
@@ -3299,14 +3299,14 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-ssr": {
 | 
			
		||||
            "version": "3.5.18",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz",
 | 
			
		||||
            "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==",
 | 
			
		||||
            "version": "3.5.19",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.19.tgz",
 | 
			
		||||
            "integrity": "sha512-/wx0VZtkWOPdiQLWPeQeqpHWR/LuNC7bHfSX7OayBTtUy8wur6vT6EQIX6Et86aED6J+y8tTw43qo2uoqGg5sw==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/compiler-dom": "3.5.18",
 | 
			
		||||
                "@vue/shared": "3.5.18"
 | 
			
		||||
                "@vue/compiler-dom": "3.5.19",
 | 
			
		||||
                "@vue/shared": "3.5.19"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/component-compiler-utils": {
 | 
			
		||||
@@ -3388,9 +3388,9 @@
 | 
			
		||||
            "license": "MIT"
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.5.18",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz",
 | 
			
		||||
            "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==",
 | 
			
		||||
            "version": "3.5.19",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.19.tgz",
 | 
			
		||||
            "integrity": "sha512-IhXCOn08wgKrLQxRFKKlSacWg4Goi1BolrdEeLYn6tgHjJNXVrWJ5nzoxZqNwl5p88aLlQ8LOaoMa3AYvaKJ/Q==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT"
 | 
			
		||||
        },
 | 
			
		||||
@@ -4326,9 +4326,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/browserslist": {
 | 
			
		||||
            "version": "4.25.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz",
 | 
			
		||||
            "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==",
 | 
			
		||||
            "version": "4.25.3",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz",
 | 
			
		||||
            "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -4346,8 +4346,8 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "caniuse-lite": "^1.0.30001733",
 | 
			
		||||
                "electron-to-chromium": "^1.5.199",
 | 
			
		||||
                "caniuse-lite": "^1.0.30001735",
 | 
			
		||||
                "electron-to-chromium": "^1.5.204",
 | 
			
		||||
                "node-releases": "^2.0.19",
 | 
			
		||||
                "update-browserslist-db": "^1.1.3"
 | 
			
		||||
            },
 | 
			
		||||
@@ -4486,9 +4486,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/caniuse-lite": {
 | 
			
		||||
            "version": "1.0.30001735",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz",
 | 
			
		||||
            "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==",
 | 
			
		||||
            "version": "1.0.30001736",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001736.tgz",
 | 
			
		||||
            "integrity": "sha512-ImpN5gLEY8gWeqfLUyEF4b7mYWcYoR2Si1VhnrbM4JizRFmfGaAQ12PhNykq6nvI4XvKLrsp8Xde74D5phJOSw==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
@@ -4930,13 +4930,13 @@
 | 
			
		||||
            "license": "MIT"
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/core-js-compat": {
 | 
			
		||||
            "version": "3.45.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz",
 | 
			
		||||
            "integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==",
 | 
			
		||||
            "version": "3.45.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz",
 | 
			
		||||
            "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "browserslist": "^4.25.1"
 | 
			
		||||
                "browserslist": "^4.25.3"
 | 
			
		||||
            },
 | 
			
		||||
            "funding": {
 | 
			
		||||
                "type": "opencollective",
 | 
			
		||||
@@ -5700,9 +5700,9 @@
 | 
			
		||||
            "license": "MIT"
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/electron-to-chromium": {
 | 
			
		||||
            "version": "1.5.203",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz",
 | 
			
		||||
            "integrity": "sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==",
 | 
			
		||||
            "version": "1.5.208",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.208.tgz",
 | 
			
		||||
            "integrity": "sha512-ozZyibehoe7tOhNaf16lKmljVf+3npZcJIEbJRVftVsmAg5TeA1mGS9dVCZzOwr2xT7xK15V0p7+GZqSPgkuPg==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "ISC"
 | 
			
		||||
        },
 | 
			
		||||
@@ -7052,9 +7052,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/i18next": {
 | 
			
		||||
            "version": "25.3.6",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.6.tgz",
 | 
			
		||||
            "integrity": "sha512-dThZ0CTCM3sUG/qS0ZtQYZQcUI6DtBN8yBHK+SKEqihPcEYmjVWh/YJ4luic73Iq6Uxhp6q7LJJntRK5+1t7jQ==",
 | 
			
		||||
            "version": "25.4.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.4.0.tgz",
 | 
			
		||||
            "integrity": "sha512-UH5aiamXsO3cfrZFurCHiB6YSs3C+s+XY9UaJllMMSbmaoXILxFgqDEZu4NbfzJFjmUo3BNMa++Rjkr3ofjfLw==",
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
                    "type": "individual",
 | 
			
		||||
@@ -7958,13 +7958,13 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/magic-string": {
 | 
			
		||||
            "version": "0.30.17",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
 | 
			
		||||
            "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
 | 
			
		||||
            "version": "0.30.18",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
 | 
			
		||||
            "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@jridgewell/sourcemap-codec": "^1.5.0"
 | 
			
		||||
                "@jridgewell/sourcemap-codec": "^1.5.5"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/make-dir": {
 | 
			
		||||
@@ -10123,9 +10123,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/rollup": {
 | 
			
		||||
            "version": "4.46.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
 | 
			
		||||
            "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
 | 
			
		||||
            "version": "4.47.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.47.1.tgz",
 | 
			
		||||
            "integrity": "sha512-iasGAQoZ5dWDzULEUX3jiW0oB1qyFOepSyDyoU6S/OhVlDIwj5knI5QBa5RRQ0sK7OE0v+8VIi2JuV+G+3tfNg==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
@@ -10139,26 +10139,26 @@
 | 
			
		||||
                "npm": ">=8.0.0"
 | 
			
		||||
            },
 | 
			
		||||
            "optionalDependencies": {
 | 
			
		||||
                "@rollup/rollup-android-arm-eabi": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-android-arm64": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-darwin-arm64": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-darwin-x64": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-freebsd-arm64": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-freebsd-x64": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-arm-musleabihf": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-arm64-gnu": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-arm64-musl": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-ppc64-gnu": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-riscv64-gnu": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-riscv64-musl": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-s390x-gnu": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-x64-gnu": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-linux-x64-musl": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-win32-arm64-msvc": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-win32-ia32-msvc": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-win32-x64-msvc": "4.46.2",
 | 
			
		||||
                "@rollup/rollup-android-arm-eabi": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-android-arm64": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-darwin-arm64": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-darwin-x64": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-freebsd-arm64": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-freebsd-x64": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-arm-gnueabihf": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-arm-musleabihf": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-arm64-gnu": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-arm64-musl": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-loongarch64-gnu": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-ppc64-gnu": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-riscv64-gnu": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-riscv64-musl": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-s390x-gnu": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-x64-gnu": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-linux-x64-musl": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-win32-arm64-msvc": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-win32-ia32-msvc": "4.47.1",
 | 
			
		||||
                "@rollup/rollup-win32-x64-msvc": "4.47.1",
 | 
			
		||||
                "fsevents": "~2.3.2"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
@@ -11527,14 +11527,14 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/vite": {
 | 
			
		||||
            "version": "7.1.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz",
 | 
			
		||||
            "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==",
 | 
			
		||||
            "version": "7.1.3",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz",
 | 
			
		||||
            "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "esbuild": "^0.25.0",
 | 
			
		||||
                "fdir": "^6.4.6",
 | 
			
		||||
                "fdir": "^6.5.0",
 | 
			
		||||
                "picomatch": "^4.0.3",
 | 
			
		||||
                "postcss": "^8.5.6",
 | 
			
		||||
                "rollup": "^4.43.0",
 | 
			
		||||
@@ -11850,9 +11850,9 @@
 | 
			
		||||
            "license": "BSD-2-Clause"
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/webpack": {
 | 
			
		||||
            "version": "5.101.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.2.tgz",
 | 
			
		||||
            "integrity": "sha512-4JLXU0tD6OZNVqlwzm3HGEhAHufSiyv+skb7q0d2367VDMzrU1Q/ZeepvkcHH0rZie6uqEtTQQe0OEOOluH3Mg==",
 | 
			
		||||
            "version": "5.101.3",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz",
 | 
			
		||||
            "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "license": "MIT",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,12 +31,18 @@
 | 
			
		||||
         failOnRisky="true"
 | 
			
		||||
         failOnWarning="true"
 | 
			
		||||
         processIsolation="false"
 | 
			
		||||
         stopOnError="true"
 | 
			
		||||
         stopOnFailure="true">
 | 
			
		||||
    <php>
 | 
			
		||||
        <env name="APP_ENV" value="testing"/>
 | 
			
		||||
        <env name="DB_CONNECTION" value="sqlite"/>
 | 
			
		||||
        <env name="APP_LOG_ENV" value="notice"/>
 | 
			
		||||
        <env name="DB_DATABASE" value=""/>
 | 
			
		||||
        <env name="APP_LOG_LEVEL" value="notice"/>
 | 
			
		||||
        <env name="AUDIT_LOG_LEVEL" value="emergency" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        <env name="QUERY_PARSER_IMPLEMENTATION" value="new"/>
 | 
			
		||||
        <ini name="xdebug.mode" value="coverage"/>
 | 
			
		||||
    </php>
 | 
			
		||||
    <testsuites>
 | 
			
		||||
        <testsuite name="unit">
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,10 @@
 | 
			
		||||
      {{ $t('form.webhook_delivery') }}
 | 
			
		||||
    </label>
 | 
			
		||||
    <div class="col-sm-8">
 | 
			
		||||
      <select
 | 
			
		||||
        <div v-if="loading" class="form-control-static">
 | 
			
		||||
            <em class="fa fa-spinner fa-spin"></em> {{ $t('firefly.loading') }}
 | 
			
		||||
        </div>
 | 
			
		||||
        <select v-if="!loading"
 | 
			
		||||
          ref="bill"
 | 
			
		||||
          v-model="delivery"
 | 
			
		||||
          :title="$t('form.webhook_delivery')"
 | 
			
		||||
@@ -49,6 +52,7 @@ export default {
 | 
			
		||||
  name: "WebhookDelivery",
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
    loading: true,
 | 
			
		||||
      delivery : 0,
 | 
			
		||||
      deliveries: [
 | 
			
		||||
 | 
			
		||||
@@ -64,15 +68,31 @@ export default {
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    value: {
 | 
			
		||||
      type: Number,
 | 
			
		||||
      type: String,
 | 
			
		||||
      required: true,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.delivery = this.value;
 | 
			
		||||
    this.deliveries = [
 | 
			
		||||
      {id: 300, name: this.$t('firefly.webhook_delivery_JSON')},
 | 
			
		||||
      //{id: 300, name: this.$t('firefly.webhook_delivery_JSON')},
 | 
			
		||||
    ];
 | 
			
		||||
      axios.get('./api/v1/configuration/webhook.deliveries').then((response) => {
 | 
			
		||||
          for (let key in response.data.data.value) {
 | 
			
		||||
              if (!response.data.data.value.hasOwnProperty(key)) {
 | 
			
		||||
                  continue;
 | 
			
		||||
              }
 | 
			
		||||
              this.deliveries.push(
 | 
			
		||||
                  {
 | 
			
		||||
                      id: key,
 | 
			
		||||
                      name: this.$t('firefly.webhook_delivery_' + key),
 | 
			
		||||
                  }
 | 
			
		||||
              );
 | 
			
		||||
          }
 | 
			
		||||
          this.loading = false;
 | 
			
		||||
      }).catch((error) => {
 | 
			
		||||
          this.loading = false;
 | 
			
		||||
      });
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    value() {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,73 +19,89 @@
 | 
			
		||||
  -->
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="form-group" v-bind:class="{ 'has-error': hasError()}">
 | 
			
		||||
    <label class="col-sm-4 control-label">
 | 
			
		||||
      {{ $t('form.webhook_response') }}
 | 
			
		||||
    </label>
 | 
			
		||||
    <div class="col-sm-8">
 | 
			
		||||
      <select
 | 
			
		||||
          ref="bill"
 | 
			
		||||
          v-model="response"
 | 
			
		||||
          :title="$t('form.webhook_response')"
 | 
			
		||||
          class="form-control"
 | 
			
		||||
          name="webhook_response"
 | 
			
		||||
      >
 | 
			
		||||
        <option v-for="response in this.responses"
 | 
			
		||||
                :label="response.name"
 | 
			
		||||
                :value="response.id">{{ response.name }}
 | 
			
		||||
        </option>
 | 
			
		||||
      </select>
 | 
			
		||||
      <p class="help-block" v-text="$t('firefly.webhook_response_form_help')"></p>
 | 
			
		||||
      <ul v-for="error in this.error" class="list-unstyled">
 | 
			
		||||
        <li class="text-danger">{{ error }}</li>
 | 
			
		||||
      </ul>
 | 
			
		||||
    <div class="form-group" v-bind:class="{ 'has-error': hasError()}">
 | 
			
		||||
        <label class="col-sm-4 control-label">
 | 
			
		||||
            {{ $t('form.webhook_response') }}
 | 
			
		||||
        </label>
 | 
			
		||||
        <div class="col-sm-8">
 | 
			
		||||
            <div v-if="loading" class="form-control-static">
 | 
			
		||||
                <em class="fa fa-spinner fa-spin"></em> {{ $t('firefly.loading') }}
 | 
			
		||||
            </div>
 | 
			
		||||
            <select v-if="!loading"
 | 
			
		||||
                    ref="response"
 | 
			
		||||
                    v-model="response"
 | 
			
		||||
                    :title="$t('form.webhook_response')"
 | 
			
		||||
                    class="form-control"
 | 
			
		||||
                    name="webhook_response"
 | 
			
		||||
            >
 | 
			
		||||
                <option v-for="response in this.responses"
 | 
			
		||||
                        :label="response.name"
 | 
			
		||||
                        :value="response.id">{{ response.name }}
 | 
			
		||||
                </option>
 | 
			
		||||
            </select>
 | 
			
		||||
            <p class="help-block" v-text="$t('firefly.webhook_response_form_help')"></p>
 | 
			
		||||
            <ul v-for="error in this.error" class="list-unstyled">
 | 
			
		||||
                <li class="text-danger">{{ error }}</li>
 | 
			
		||||
            </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
  name: "WebhookResponse",
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      response: 0,
 | 
			
		||||
      responses: [],
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  props: {
 | 
			
		||||
    error: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      required: true,
 | 
			
		||||
      default() {
 | 
			
		||||
        return []
 | 
			
		||||
      }
 | 
			
		||||
    name: "WebhookResponse",
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            loading: true,
 | 
			
		||||
            response: 0,
 | 
			
		||||
            responses: [],
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    value: {
 | 
			
		||||
      type: Number,
 | 
			
		||||
      required: true,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    value() {
 | 
			
		||||
      this.response = this.value;
 | 
			
		||||
    props: {
 | 
			
		||||
        error: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            required: true,
 | 
			
		||||
            default() {
 | 
			
		||||
                return []
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        value: {
 | 
			
		||||
            type: String,
 | 
			
		||||
            required: true,
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        value() {
 | 
			
		||||
            this.response = this.value;
 | 
			
		||||
        },
 | 
			
		||||
        response(newValue) {
 | 
			
		||||
            this.$emit('input', newValue);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.response  = this.value;
 | 
			
		||||
        this.responses = [];
 | 
			
		||||
        axios.get('./api/v1/configuration/webhook.responses').then((response) => {
 | 
			
		||||
            for (let key in response.data.data.value) {
 | 
			
		||||
                if (!response.data.data.value.hasOwnProperty(key)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                this.responses.push(
 | 
			
		||||
                    {
 | 
			
		||||
                        id: key,
 | 
			
		||||
                        name: this.$t('firefly.webhook_response_' + key),
 | 
			
		||||
                    }
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            this.loading = false;
 | 
			
		||||
        }).catch((error) => {
 | 
			
		||||
            this.loading = false;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        hasError() {
 | 
			
		||||
            return this.error?.length > 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
  response(newValue) {
 | 
			
		||||
    this.$emit('input', newValue);
 | 
			
		||||
  }
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.response = this.value;
 | 
			
		||||
    this.responses = [
 | 
			
		||||
      {id: 200, name: this.$t('firefly.webhook_response_TRANSACTIONS')},
 | 
			
		||||
      {id: 210, name: this.$t('firefly.webhook_response_ACCOUNTS')},
 | 
			
		||||
      {id: 220, name: this.$t('firefly.webhook_response_none_NONE')},
 | 
			
		||||
    ];
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    hasError() {
 | 
			
		||||
      return this.error?.length > 0;
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,73 +19,92 @@
 | 
			
		||||
  -->
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="form-group" v-bind:class="{ 'has-error': hasError()}">
 | 
			
		||||
    <label class="col-sm-4 control-label">
 | 
			
		||||
      {{ $t('form.webhook_trigger') }}
 | 
			
		||||
    </label>
 | 
			
		||||
    <div class="col-sm-8">
 | 
			
		||||
      <select
 | 
			
		||||
          ref="bill"
 | 
			
		||||
          v-model="trigger"
 | 
			
		||||
          :title="$t('form.webhook_trigger')"
 | 
			
		||||
          class="form-control"
 | 
			
		||||
          name="webhook_trigger"
 | 
			
		||||
      >
 | 
			
		||||
        <option v-for="trigger in this.triggers"
 | 
			
		||||
                :label="trigger.name"
 | 
			
		||||
                :value="trigger.id">{{ trigger.name }}
 | 
			
		||||
        </option>
 | 
			
		||||
      </select>
 | 
			
		||||
      <p class="help-block" v-text="$t('firefly.webhook_trigger_form_help')"></p>
 | 
			
		||||
      <ul v-for="error in this.error" class="list-unstyled">
 | 
			
		||||
        <li class="text-danger">{{ error }}</li>
 | 
			
		||||
      </ul>
 | 
			
		||||
    <div class="form-group" v-bind:class="{ 'has-error': hasError()}">
 | 
			
		||||
        <label class="col-sm-4 control-label">
 | 
			
		||||
            {{ $t('form.webhook_trigger') }}
 | 
			
		||||
        </label>
 | 
			
		||||
        <div class="col-sm-8">
 | 
			
		||||
            <div v-if="loading" class="form-control-static">
 | 
			
		||||
                <em class="fa fa-spinner fa-spin"></em> {{ $t('firefly.loading') }}
 | 
			
		||||
            </div>
 | 
			
		||||
            <select v-if="!loading"
 | 
			
		||||
                    ref="trigger"
 | 
			
		||||
                    multiple
 | 
			
		||||
                    v-model="trigger"
 | 
			
		||||
                    :title="$t('form.webhook_trigger')"
 | 
			
		||||
                    class="form-control"
 | 
			
		||||
                    name="webhook_trigger"
 | 
			
		||||
            >
 | 
			
		||||
                <option v-for="trigger in this.triggers"
 | 
			
		||||
                        :label="trigger.name"
 | 
			
		||||
                        :value="trigger.id">{{ trigger.name }}
 | 
			
		||||
                </option>
 | 
			
		||||
            </select>
 | 
			
		||||
            <p class="help-block" v-text="$t('firefly.webhook_trigger_form_help')"></p>
 | 
			
		||||
            <ul v-for="error in this.error" class="list-unstyled">
 | 
			
		||||
                <li class="text-danger">{{ error }}</li>
 | 
			
		||||
            </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
  name: "WebhookTrigger",
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      trigger: 0,
 | 
			
		||||
      triggers: [],
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  props: {
 | 
			
		||||
    error: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      required: true,
 | 
			
		||||
      default() {
 | 
			
		||||
        return []
 | 
			
		||||
      }
 | 
			
		||||
    name: "WebhookTrigger",
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            trigger: [],
 | 
			
		||||
            loading: true,
 | 
			
		||||
            triggers: [],
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    value: {
 | 
			
		||||
      type: Number,
 | 
			
		||||
      required: true,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.trigger = this.value;
 | 
			
		||||
    this.triggers = [
 | 
			
		||||
      {id: 100, name: this.$t('firefly.webhook_trigger_STORE_TRANSACTION')},
 | 
			
		||||
      {id: 110, name: this.$t('firefly.webhook_trigger_UPDATE_TRANSACTION')},
 | 
			
		||||
      {id: 120, name: this.$t('firefly.webhook_trigger_DESTROY_TRANSACTION')},
 | 
			
		||||
    ];
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    value() {
 | 
			
		||||
      this.trigger = this.value;
 | 
			
		||||
    props: {
 | 
			
		||||
        error: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            required: true,
 | 
			
		||||
            default() {
 | 
			
		||||
                return []
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        value: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            required: true,
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.trigger  = this.value;
 | 
			
		||||
        this.triggers = [];
 | 
			
		||||
        axios.get('./api/v1/configuration/webhook.triggers').then((response) => {
 | 
			
		||||
            for (let key in response.data.data.value) {
 | 
			
		||||
                if (!response.data.data.value.hasOwnProperty(key)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                this.triggers.push(
 | 
			
		||||
                    {
 | 
			
		||||
                        id: key,
 | 
			
		||||
                        name: this.$t('firefly.webhook_trigger_' + key),
 | 
			
		||||
                    }
 | 
			
		||||
                );
 | 
			
		||||
                // console.log('webhook trigger: id=' + response.data.data.value[key] + ', name=' + key);
 | 
			
		||||
            }
 | 
			
		||||
            this.loading = false;
 | 
			
		||||
        }).catch((error) => {
 | 
			
		||||
            this.loading = false;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        value() {
 | 
			
		||||
            console.log('Value changed to ' + this.value);
 | 
			
		||||
            this.trigger = this.value;
 | 
			
		||||
        },
 | 
			
		||||
        trigger(newValue) {
 | 
			
		||||
            this.$emit('input', newValue);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        hasError() {
 | 
			
		||||
            return this.error?.length > 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    trigger(newValue) {
 | 
			
		||||
      this.$emit('input', newValue);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    hasError() {
 | 
			
		||||
      return this.error?.length > 0;
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -112,12 +112,10 @@ export default {
 | 
			
		||||
                amount: this.$refs.amount.value,
 | 
			
		||||
                currency_id: this.$refs.currency_select.value,
 | 
			
		||||
            };
 | 
			
		||||
            // console.log(obj);
 | 
			
		||||
            this.$emit('input', obj
 | 
			
		||||
            );
 | 
			
		||||
            this.$emit('input', obj);
 | 
			
		||||
        },
 | 
			
		||||
        changeData: function () {
 | 
			
		||||
            // console.log('ForeignAmountSelect changeData');
 | 
			
		||||
            console.log('ForeignAmountSelect changeData');
 | 
			
		||||
            this.enabledCurrencies = [];
 | 
			
		||||
            let destType = this.destination.type ? this.destination.type.toLowerCase() : 'invalid';
 | 
			
		||||
            let srcType = this.source.type ? this.source.type.toLowerCase() : 'invalid';
 | 
			
		||||
@@ -160,6 +158,7 @@ export default {
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                this.checkSelection();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -172,6 +171,7 @@ export default {
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                this.checkSelection();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            for (const key in this.currencies) {
 | 
			
		||||
@@ -180,6 +180,23 @@ export default {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        checkSelection: function () {
 | 
			
		||||
            let selectedCurrency = this.$refs.currency_select.value;
 | 
			
		||||
            let hasSelected = false;
 | 
			
		||||
            for(const key in this.enabledCurrencies) {
 | 
			
		||||
                if(parseInt(this.enabledCurrencies[key].id) === parseInt(selectedCurrency)) {
 | 
			
		||||
                    hasSelected = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if(false === hasSelected) {
 | 
			
		||||
                let obj = {
 | 
			
		||||
                    amount: '',
 | 
			
		||||
                    currency_id: null,
 | 
			
		||||
                };
 | 
			
		||||
                this.$emit('input', obj);
 | 
			
		||||
                console.warn('Reset foreign amount.');
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        loadCurrencies: function () {
 | 
			
		||||
            // console.log('loadCurrencies');
 | 
			
		||||
            // reset list of currencies:
 | 
			
		||||
 
 | 
			
		||||
@@ -54,12 +54,12 @@
 | 
			
		||||
            <div class="row">
 | 
			
		||||
              <div class="col-lg-12">
 | 
			
		||||
                <Title :value=this.title :error="errors.title" v-on:input="title = $event"></Title>
 | 
			
		||||
                <WebhookTrigger :value=this.trigger :error="errors.trigger"
 | 
			
		||||
                                v-on:input="trigger = $event"></WebhookTrigger>
 | 
			
		||||
                <WebhookResponse :value=this.response :error="errors.response"
 | 
			
		||||
                                 v-on:input="response = $event"></WebhookResponse>
 | 
			
		||||
                <WebhookDelivery :value=this.delivery :error="errors.delivery"
 | 
			
		||||
                                 v-on:input="delivery = $event"></WebhookDelivery>
 | 
			
		||||
                <WebhookTrigger :value=this.triggers :error="errors.trigger"
 | 
			
		||||
                                v-on:input="triggers = $event"></WebhookTrigger>
 | 
			
		||||
                <WebhookResponse :value=this.responses :error="errors.response"
 | 
			
		||||
                                 v-on:input="responses = $event"></WebhookResponse>
 | 
			
		||||
                <WebhookDelivery :value=this.deliveries :error="errors.delivery"
 | 
			
		||||
                                 v-on:input="deliveries = $event"></WebhookDelivery>
 | 
			
		||||
                <URL :value=this.url :error="errors.url" v-on:input="url = $event"></URL>
 | 
			
		||||
                <Checkbox :value=this.active :error="errors.active" help="ACTIVE HELP TODO" :title="$t('form.active')" v-on:input="active = $event"></Checkbox>
 | 
			
		||||
              </div>
 | 
			
		||||
@@ -96,16 +96,16 @@ export default {
 | 
			
		||||
      error_message: '',
 | 
			
		||||
      success_message: '',
 | 
			
		||||
      title: '',
 | 
			
		||||
      trigger: 100,
 | 
			
		||||
      response: 200,
 | 
			
		||||
      delivery: 300,
 | 
			
		||||
      triggers: ["STORE_TRANSACTION"],
 | 
			
		||||
      responses: "RELEVANT",
 | 
			
		||||
      deliveries: "JSON",
 | 
			
		||||
      active: true,
 | 
			
		||||
      url: '',
 | 
			
		||||
      errors: {
 | 
			
		||||
        title: [],
 | 
			
		||||
        trigger: [],
 | 
			
		||||
        response: [],
 | 
			
		||||
        delivery: [],
 | 
			
		||||
        triggers: [],
 | 
			
		||||
        responses: [],
 | 
			
		||||
        deliveries: [],
 | 
			
		||||
        url: [],
 | 
			
		||||
        active: []
 | 
			
		||||
      }
 | 
			
		||||
@@ -118,9 +118,9 @@ export default {
 | 
			
		||||
      this.success_message = '';
 | 
			
		||||
      this.errors = {
 | 
			
		||||
        title: [],
 | 
			
		||||
        trigger: [],
 | 
			
		||||
        response: [],
 | 
			
		||||
        delivery: [],
 | 
			
		||||
        triggers: [],
 | 
			
		||||
        responses: [],
 | 
			
		||||
        deliveries: [],
 | 
			
		||||
        url: [],
 | 
			
		||||
        active: [],
 | 
			
		||||
      };
 | 
			
		||||
@@ -131,9 +131,9 @@ export default {
 | 
			
		||||
      // collect data
 | 
			
		||||
      let data = {
 | 
			
		||||
        title: this.title,
 | 
			
		||||
        trigger: this.trigger,
 | 
			
		||||
        response: this.response,
 | 
			
		||||
        delivery: this.delivery,
 | 
			
		||||
        triggers: this.triggers,
 | 
			
		||||
        responses: [this.responses],
 | 
			
		||||
        deliveries: [this.deliveries],
 | 
			
		||||
        url: this.url,
 | 
			
		||||
        active: this.active,
 | 
			
		||||
      };
 | 
			
		||||
@@ -148,9 +148,9 @@ export default {
 | 
			
		||||
        //console.log(error.response.data);
 | 
			
		||||
        this.error_message = error.response.data.message;
 | 
			
		||||
        this.errors.title = error.response.data.errors.title;
 | 
			
		||||
        this.errors.trigger = error.response.data.errors.trigger;
 | 
			
		||||
        this.errors.response = error.response.data.errors.response;
 | 
			
		||||
        this.errors.delivery = error.response.data.errors.delivery;
 | 
			
		||||
        this.errors.triggers = error.response.data.errors.triggers;
 | 
			
		||||
        this.errors.responses = error.response.data.errors.responses;
 | 
			
		||||
        this.errors.deliveries = error.response.data.errors.deliveries;
 | 
			
		||||
        this.errors.url = error.response.data.errors.url;
 | 
			
		||||
 | 
			
		||||
        // enable button again
 | 
			
		||||
 
 | 
			
		||||
@@ -54,12 +54,12 @@
 | 
			
		||||
            <div class="row">
 | 
			
		||||
              <div class="col-lg-12">
 | 
			
		||||
                <Title :value=this.title :error="errors.title" v-on:input="title = $event"></Title>
 | 
			
		||||
                <WebhookTrigger :value=this.trigger :error="errors.trigger"
 | 
			
		||||
                                v-on:input="trigger = $event"></WebhookTrigger>
 | 
			
		||||
                <WebhookResponse :value=this.response :error="errors.response"
 | 
			
		||||
                                 v-on:input="response = $event"></WebhookResponse>
 | 
			
		||||
                <WebhookDelivery :value=this.delivery :error="errors.delivery"
 | 
			
		||||
                                 v-on:input="delivery = $event"></WebhookDelivery>
 | 
			
		||||
                <WebhookTrigger :value=this.triggers :error="errors.triggers"
 | 
			
		||||
                                v-on:input="triggers = $event"></WebhookTrigger>
 | 
			
		||||
                <WebhookResponse :value=this.responses :error="errors.responses"
 | 
			
		||||
                                 v-on:input="responses = $event"></WebhookResponse>
 | 
			
		||||
                <WebhookDelivery :value=this.deliveries :error="errors.deliveries"
 | 
			
		||||
                                 v-on:input="deliveries = $event"></WebhookDelivery>
 | 
			
		||||
                <URL :value=this.url :error="errors.url" v-on:input="url = $event"></URL>
 | 
			
		||||
                <Checkbox :value=this.active :error="errors.active" help="ACTIVE HELP TODO" :title="$t('form.active')"
 | 
			
		||||
                          v-on:input="active = $event"></Checkbox>
 | 
			
		||||
@@ -97,17 +97,17 @@ export default {
 | 
			
		||||
      error_message: '',
 | 
			
		||||
      success_message: '',
 | 
			
		||||
      title: '',
 | 
			
		||||
      trigger: 100,
 | 
			
		||||
      response: 200,
 | 
			
		||||
      delivery: 300,
 | 
			
		||||
      triggers: ["STORE_TRANSACTION"],
 | 
			
		||||
      responses: "RELEVANT",
 | 
			
		||||
      deliveries: "JSON",
 | 
			
		||||
      id: 0,
 | 
			
		||||
      active: false,
 | 
			
		||||
      url: '',
 | 
			
		||||
      errors: {
 | 
			
		||||
        title: [],
 | 
			
		||||
        trigger: [],
 | 
			
		||||
        response: [],
 | 
			
		||||
        delivery: [],
 | 
			
		||||
        triggers: [],
 | 
			
		||||
        responses: [],
 | 
			
		||||
        deliveries: [],
 | 
			
		||||
        url: [],
 | 
			
		||||
        active: []
 | 
			
		||||
      }
 | 
			
		||||
@@ -127,32 +127,9 @@ export default {
 | 
			
		||||
        // console.log(response.data.data.attributes);
 | 
			
		||||
        this.title = response.data.data.attributes.title;
 | 
			
		||||
        this.id = parseInt(response.data.data.id);
 | 
			
		||||
 | 
			
		||||
        // trigger value on content
 | 
			
		||||
        if ('STORE_TRANSACTION' === response.data.data.attributes.trigger) {
 | 
			
		||||
          this.trigger = 100;
 | 
			
		||||
        }
 | 
			
		||||
        if ('UPDATE_TRANSACTION' === response.data.data.attributes.trigger) {
 | 
			
		||||
          this.trigger = 110;
 | 
			
		||||
        }
 | 
			
		||||
        if ('DESTROY_TRANSACTION' === response.data.data.attributes.trigger) {
 | 
			
		||||
          this.trigger = 120;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // response value
 | 
			
		||||
        if ('TRANSACTIONS' === response.data.data.attributes.response) {
 | 
			
		||||
          this.response = 200;
 | 
			
		||||
        }
 | 
			
		||||
        if ('ACCOUNTS' === response.data.data.attributes.response) {
 | 
			
		||||
          this.response = 210;
 | 
			
		||||
        }
 | 
			
		||||
        if ('NONE' === response.data.data.attributes.response) {
 | 
			
		||||
          this.response = 220;
 | 
			
		||||
        }
 | 
			
		||||
        if ('JSON' === response.data.data.attributes.delivery) {
 | 
			
		||||
          this.delivery = 300;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.triggers = response.data.data.attributes.triggers;
 | 
			
		||||
        this.responses = response.data.data.attributes.responses[0];
 | 
			
		||||
        this.deliveries = response.data.data.attributes.deliveries[0];
 | 
			
		||||
        this.active = response.data.data.attributes.active;
 | 
			
		||||
        this.url = response.data.data.attributes.url;
 | 
			
		||||
      }).catch(error => {
 | 
			
		||||
@@ -165,9 +142,9 @@ export default {
 | 
			
		||||
      this.success_message = '';
 | 
			
		||||
      this.errors = {
 | 
			
		||||
        title: [],
 | 
			
		||||
        trigger: [],
 | 
			
		||||
        response: [],
 | 
			
		||||
        delivery: [],
 | 
			
		||||
        triggers: [],
 | 
			
		||||
        responses: [],
 | 
			
		||||
        deliveries: [],
 | 
			
		||||
        url: [],
 | 
			
		||||
        active: [],
 | 
			
		||||
      };
 | 
			
		||||
@@ -178,9 +155,9 @@ export default {
 | 
			
		||||
      // collect data
 | 
			
		||||
      let data = {
 | 
			
		||||
        title: this.title,
 | 
			
		||||
        trigger: this.trigger,
 | 
			
		||||
        response: this.response,
 | 
			
		||||
        delivery: this.delivery,
 | 
			
		||||
        triggers: this.triggers,
 | 
			
		||||
        responses: [this.responses],
 | 
			
		||||
        deliveries: [this.deliveries],
 | 
			
		||||
        url: this.url,
 | 
			
		||||
        active: this.active,
 | 
			
		||||
      };
 | 
			
		||||
@@ -193,9 +170,9 @@ export default {
 | 
			
		||||
 | 
			
		||||
        this.error_message = error.response.data.message;
 | 
			
		||||
        this.errors.title = error.response.data.errors.title;
 | 
			
		||||
        this.errors.trigger = error.response.data.errors.trigger;
 | 
			
		||||
        this.errors.response = error.response.data.errors.response;
 | 
			
		||||
        this.errors.delivery = error.response.data.errors.delivery;
 | 
			
		||||
        this.errors.triggers = error.response.data.errors.trigger;
 | 
			
		||||
        this.errors.responses = error.response.data.errors.response;
 | 
			
		||||
        this.errors.deliveries = error.response.data.errors.deliveries;
 | 
			
		||||
        this.errors.url = error.response.data.errors.url;
 | 
			
		||||
 | 
			
		||||
        // enable button again
 | 
			
		||||
 
 | 
			
		||||
@@ -19,138 +19,184 @@
 | 
			
		||||
  -->
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-lg-12 col-md-12 col-sm-12">
 | 
			
		||||
      <div class="box">
 | 
			
		||||
        <div class="box-header with-border">
 | 
			
		||||
          <h3 class="box-title">
 | 
			
		||||
            {{ $t('firefly.webhooks') }}
 | 
			
		||||
          </h3>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="box-body no-padding">
 | 
			
		||||
          <div style="padding:8px;">
 | 
			
		||||
            <a href="webhooks/create" class="btn btn-success"><span class="fa fa-plus fa-fw"></span>{{ $t('firefly.create_new_webhook') }}</a>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <table class="table table-responsive table-hover" v-if="webhooks.length > 0" aria-label="A table.">
 | 
			
		||||
            <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <th>Title</th>
 | 
			
		||||
              <th>Responds when</th>
 | 
			
		||||
              <th>Responds with (delivery)</th>
 | 
			
		||||
              <th style="width:20%;">Secret (show / hide)</th>
 | 
			
		||||
              <th>URL</th>
 | 
			
		||||
              <th class="hidden-sm hidden-xs"> </th>
 | 
			
		||||
            </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
            <tr v-for="webhook in webhooks" :key="webhook.id">
 | 
			
		||||
              <td>
 | 
			
		||||
                <a :href="'webhooks/show/' + webhook.id">{{ webhook.title }}</a>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>
 | 
			
		||||
                <span v-if="webhook.active">{{ triggers[webhook.trigger] }}</span>
 | 
			
		||||
                <span v-if="!webhook.active" class="text-muted"><s>{{ triggers[webhook.trigger] }}</s> ({{ $t('firefly.inactive') }})</span>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>{{ responses[webhook.response] }} ({{ deliveries[webhook.delivery] }})</td>
 | 
			
		||||
              <td>
 | 
			
		||||
                <em style="cursor:pointer"
 | 
			
		||||
                    v-if="webhook.show_secret" class="fa fa-eye" @click="toggleSecret(webhook)"></em>
 | 
			
		||||
                <em style="cursor:pointer"
 | 
			
		||||
                    v-if="!webhook.show_secret" class="fa fa-eye-slash" @click="toggleSecret(webhook)"></em>
 | 
			
		||||
                <code v-if="webhook.show_secret">{{ webhook.secret }}</code>
 | 
			
		||||
                <code v-if="!webhook.show_secret">********</code>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>
 | 
			
		||||
                <code :title="webhook.full_url">{{ webhook.url }}</code>
 | 
			
		||||
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="hidden-sm hidden-xs">
 | 
			
		||||
                <div class="btn-group btn-group-xs pull-right">
 | 
			
		||||
                  <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
 | 
			
		||||
                    {{ $t('firefly.actions') }} <span class="caret"></span></button>
 | 
			
		||||
                  <ul class="dropdown-menu dropdown-menu-right" role="menu">
 | 
			
		||||
                    <li><a :href="'webhooks/show/' + webhook.id"><span class="fa fa-fw fa-search"></span> {{ $t('firefly.inspect') }}</a></li>
 | 
			
		||||
                    <li><a :href="'webhooks/edit/' + webhook.id"><span class="fa fa-fw fa-pencil"></span> {{$t( 'firefly.edit') }}</a></li>
 | 
			
		||||
                    <li><a  :href="'webhooks/delete/' + webhook.id"><span class="fa fa-fw fa-trash"></span> {{ $t('firefly.delete') }}</a></li>
 | 
			
		||||
                  </ul>
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col-lg-12 col-md-12 col-sm-12">
 | 
			
		||||
            <div class="box">
 | 
			
		||||
                <div class="box-header with-border">
 | 
			
		||||
                    <h3 class="box-title">
 | 
			
		||||
                        {{ $t('firefly.webhooks') }}
 | 
			
		||||
                    </h3>
 | 
			
		||||
                </div>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
                <div class="box-body no-padding">
 | 
			
		||||
                    <div style="padding:8px;">
 | 
			
		||||
                        <a href="webhooks/create" class="btn btn-success"><span
 | 
			
		||||
                            class="fa fa-plus fa-fw"></span>{{ $t('firefly.create_new_webhook') }}</a>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
          <div v-if="webhooks.length > 0" style="padding:8px;">
 | 
			
		||||
            <a href="webhooks/create" class="btn btn-success"><span class="fa fa-plus fa-fw"></span>{{ $t('firefly.create_new_webhook') }}</a>
 | 
			
		||||
          </div>
 | 
			
		||||
                    <table class="table table-responsive table-hover" v-if="webhooks.length > 0" aria-label="A table.">
 | 
			
		||||
                        <thead>
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <th>Title</th>
 | 
			
		||||
                            <th>Responds when</th>
 | 
			
		||||
                            <th>Responds with (delivery)</th>
 | 
			
		||||
                            <th style="width:20%;">Secret (show / hide)</th>
 | 
			
		||||
                            <th>URL</th>
 | 
			
		||||
                            <th class="hidden-sm hidden-xs"> </th>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                        </thead>
 | 
			
		||||
                        <tbody>
 | 
			
		||||
                        <tr v-for="webhook in webhooks" :key="webhook.id">
 | 
			
		||||
                            <td>
 | 
			
		||||
                                <a :href="'webhooks/show/' + webhook.id">{{ webhook.title }}</a>
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                <span v-if="webhook.active">
 | 
			
		||||
                                    <ul class="list-unstyled">
 | 
			
		||||
                                        <li v-for="trigger in webhook.triggers" :key="trigger">
 | 
			
		||||
                                            {{ triggers[trigger] }}
 | 
			
		||||
                                        </li>
 | 
			
		||||
                                    </ul>
 | 
			
		||||
                                </span>
 | 
			
		||||
                                <span v-if="!webhook.active" class="text-muted">
 | 
			
		||||
                                    <ul class="list-unstyled">
 | 
			
		||||
                                        <li v-for="trigger in webhook.triggers" :key="trigger">
 | 
			
		||||
                                            <s>{{ triggers[trigger] }}</s> ({{$t('firefly.inactive') }})
 | 
			
		||||
                                        </li>
 | 
			
		||||
                                    </ul>
 | 
			
		||||
                                    </span>
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <td>
 | 
			
		||||
                              {{ responses[webhook.responses[0]] }} ({{ deliveries[webhook.deliveries[0]] }})
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                <em style="cursor:pointer"
 | 
			
		||||
                                    v-if="webhook.show_secret" class="fa fa-eye" @click="toggleSecret(webhook)"></em>
 | 
			
		||||
                                <em style="cursor:pointer"
 | 
			
		||||
                                    v-if="!webhook.show_secret" class="fa fa-eye-slash"
 | 
			
		||||
                                    @click="toggleSecret(webhook)"></em>
 | 
			
		||||
                                <code v-if="webhook.show_secret">{{ webhook.secret }}</code>
 | 
			
		||||
                                <code v-if="!webhook.show_secret">********</code>
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <td>
 | 
			
		||||
                                <code :title="webhook.full_url">{{ webhook.url }}</code>
 | 
			
		||||
 | 
			
		||||
                            </td>
 | 
			
		||||
                            <td class="hidden-sm hidden-xs">
 | 
			
		||||
                                <div class="btn-group btn-group-xs pull-right">
 | 
			
		||||
                                    <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
 | 
			
		||||
                                            aria-haspopup="true" aria-expanded="false">
 | 
			
		||||
                                        {{ $t('firefly.actions') }} <span class="caret"></span></button>
 | 
			
		||||
                                    <ul class="dropdown-menu dropdown-menu-right" role="menu">
 | 
			
		||||
                                        <li><a :href="'webhooks/show/' + webhook.id"><span
 | 
			
		||||
                                            class="fa fa-fw fa-search"></span> {{ $t('firefly.inspect') }}</a></li>
 | 
			
		||||
                                        <li><a :href="'webhooks/edit/' + webhook.id"><span
 | 
			
		||||
                                            class="fa fa-fw fa-pencil"></span> {{ $t('firefly.edit') }}</a></li>
 | 
			
		||||
                                        <li><a :href="'webhooks/delete/' + webhook.id"><span
 | 
			
		||||
                                            class="fa fa-fw fa-trash"></span> {{ $t('firefly.delete') }}</a></li>
 | 
			
		||||
                                    </ul>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </td>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                        </tbody>
 | 
			
		||||
                    </table>
 | 
			
		||||
 | 
			
		||||
                    <div v-if="webhooks.length > 0" style="padding:8px;">
 | 
			
		||||
                        <a href="webhooks/create" class="btn btn-success"><span
 | 
			
		||||
                            class="fa fa-plus fa-fw"></span>{{ $t('firefly.create_new_webhook') }}</a>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
  name: "Index",
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      webhooks: [],
 | 
			
		||||
      triggers: {
 | 
			
		||||
        STORE_TRANSACTION: this.$t('firefly.webhook_trigger_STORE_TRANSACTION'),
 | 
			
		||||
        UPDATE_TRANSACTION: this.$t('firefly.webhook_trigger_UPDATE_TRANSACTION'),
 | 
			
		||||
        DESTROY_TRANSACTION: this.$t('firefly.webhook_trigger_DESTROY_TRANSACTION'),
 | 
			
		||||
      },
 | 
			
		||||
      responses: {
 | 
			
		||||
        TRANSACTIONS: this.$t('firefly.webhook_response_TRANSACTIONS'),
 | 
			
		||||
        ACCOUNTS: this.$t('firefly.webhook_response_ACCOUNTS'),
 | 
			
		||||
        NONE: this.$t('firefly.webhook_response_none_NONE'),
 | 
			
		||||
      },
 | 
			
		||||
      deliveries: {
 | 
			
		||||
        JSON: this.$t('firefly.webhook_delivery_JSON'),
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.getWebhooks();
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    getWebhooks: function () {
 | 
			
		||||
      this.webhooks = [];
 | 
			
		||||
      this.downloadWebhooks(1);
 | 
			
		||||
    name: "Index",
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            webhooks: [],
 | 
			
		||||
            triggers: {
 | 
			
		||||
            },
 | 
			
		||||
            responses: {
 | 
			
		||||
            },
 | 
			
		||||
            deliveries: {
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    toggleSecret: function (webhook) {
 | 
			
		||||
      webhook.show_secret = !webhook.show_secret;
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.getOptions();
 | 
			
		||||
    },
 | 
			
		||||
    downloadWebhooks: function (page) {
 | 
			
		||||
      axios.get("./api/v1/webhooks?page=" + page).then((response) => {
 | 
			
		||||
        for (let i in response.data.data) {
 | 
			
		||||
          if (response.data.data.hasOwnProperty(i)) {
 | 
			
		||||
            let current = response.data.data[i];
 | 
			
		||||
            let webhook = {
 | 
			
		||||
              id: current.id,
 | 
			
		||||
              title: current.attributes.title,
 | 
			
		||||
              url: current.attributes.url,
 | 
			
		||||
              active: current.attributes.active,
 | 
			
		||||
              full_url: current.attributes.url,
 | 
			
		||||
              secret: current.attributes.secret,
 | 
			
		||||
              trigger: current.attributes.trigger,
 | 
			
		||||
              response: current.attributes.response,
 | 
			
		||||
              delivery: current.attributes.delivery,
 | 
			
		||||
              show_secret: false,
 | 
			
		||||
            };
 | 
			
		||||
            if(current.attributes.url.length > 20) {
 | 
			
		||||
              webhook.url = current.attributes.url.slice(0, 20) + '...';
 | 
			
		||||
            }
 | 
			
		||||
            this.webhooks.push(webhook);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
    methods: {
 | 
			
		||||
        getOptions: function () {
 | 
			
		||||
            // get triggers
 | 
			
		||||
            axios.get('./api/v1/configuration/webhook.triggers').then((response) => {
 | 
			
		||||
                for (let key in response.data.data.value) {
 | 
			
		||||
                    if (!response.data.data.value.hasOwnProperty(key)) {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    this.triggers[key] = this.$t('firefly.webhook_trigger_' + key);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        if (response.data.meta.pagination.current_page < response.data.meta.pagination.total_pages) {
 | 
			
		||||
          this.downloadWebhooks(response.data.meta.pagination.current_page + 1);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  }
 | 
			
		||||
                // get responses
 | 
			
		||||
                axios.get('./api/v1/configuration/webhook.responses').then((response) => {
 | 
			
		||||
                    for (let key in response.data.data.value) {
 | 
			
		||||
                        if (!response.data.data.value.hasOwnProperty(key)) {
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                        this.responses[key] = this.$t('firefly.webhook_response_' + key);
 | 
			
		||||
                    }
 | 
			
		||||
                    // get deliveries
 | 
			
		||||
                    axios.get('./api/v1/configuration/webhook.deliveries').then((response) => {
 | 
			
		||||
                        for (let key in response.data.data.value) {
 | 
			
		||||
                            if (!response.data.data.value.hasOwnProperty(key)) {
 | 
			
		||||
                                continue;
 | 
			
		||||
                            }
 | 
			
		||||
                            this.deliveries[key] = this.$t('firefly.webhook_delivery_' + key);
 | 
			
		||||
                        }
 | 
			
		||||
                        // get webhooks
 | 
			
		||||
                        this.getWebhooks();
 | 
			
		||||
                    })
 | 
			
		||||
                })
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        getWebhooks: function () {
 | 
			
		||||
            this.webhooks = [];
 | 
			
		||||
            this.downloadWebhooks(1);
 | 
			
		||||
        },
 | 
			
		||||
        toggleSecret: function (webhook) {
 | 
			
		||||
            webhook.show_secret = !webhook.show_secret;
 | 
			
		||||
        },
 | 
			
		||||
        downloadWebhooks: function (page) {
 | 
			
		||||
            axios.get("./api/v1/webhooks?page=" + page).then((response) => {
 | 
			
		||||
                for (let i in response.data.data) {
 | 
			
		||||
                    if (response.data.data.hasOwnProperty(i)) {
 | 
			
		||||
                        let current = response.data.data[i];
 | 
			
		||||
                        let webhook = {
 | 
			
		||||
                            id: current.id,
 | 
			
		||||
                            title: current.attributes.title,
 | 
			
		||||
                            url: current.attributes.url,
 | 
			
		||||
                            active: current.attributes.active,
 | 
			
		||||
                            full_url: current.attributes.url,
 | 
			
		||||
                            secret: current.attributes.secret,
 | 
			
		||||
                            triggers: current.attributes.triggers,
 | 
			
		||||
                            responses: current.attributes.responses,
 | 
			
		||||
                            deliveries: current.attributes.deliveries,
 | 
			
		||||
                            show_secret: false,
 | 
			
		||||
                        };
 | 
			
		||||
                        if (current.attributes.url.length > 20) {
 | 
			
		||||
                            webhook.url = current.attributes.url.slice(0, 20) + '...';
 | 
			
		||||
                        }
 | 
			
		||||
                        this.webhooks.push(webhook);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (response.data.meta.pagination.current_page < response.data.meta.pagination.total_pages) {
 | 
			
		||||
                    this.downloadWebhooks(response.data.meta.pagination.current_page + 1);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,27 +40,39 @@
 | 
			
		||||
            <table class="table table-hover" aria-label="A table">
 | 
			
		||||
              <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th scope="row" style="width:40%;">Title</th>
 | 
			
		||||
                <td style="width:40%;"><strong>{{ $t('list.title') }}</strong></td>
 | 
			
		||||
                <td>{{ title }}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th scope="row">{{ $t('list.active') }}</th>
 | 
			
		||||
                  <td style="width:40%;"><strong>{{ $t('list.active') }}</strong></td>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <em class="fa fa-check text-success" v-if="active"></em>
 | 
			
		||||
                  <em class="fa fa-times text-danger" v-if="!active"></em>
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th scope="row">{{ $t('list.trigger') }}</th>
 | 
			
		||||
                <td> {{ trigger }}</td>
 | 
			
		||||
                  <td style="width:40%;"><strong>{{ $t('list.trigger') }}</strong></td>
 | 
			
		||||
                <td>
 | 
			
		||||
                    <span v-for="trigger in triggers" :key="trigger">
 | 
			
		||||
                        {{ $t('firefly.webhook_trigger_' + trigger) }}<br>
 | 
			
		||||
                    </span>
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th scope="row">{{ $t('list.response') }}</th>
 | 
			
		||||
                <td> {{ response }}</td>
 | 
			
		||||
                  <td style="width:40%;"><strong>{{ $t('list.response') }}</strong></td>
 | 
			
		||||
                <td>
 | 
			
		||||
                        <span v-for="response in responses" :key="response">
 | 
			
		||||
                            {{ $t('firefly.webhook_response_' + response) }}
 | 
			
		||||
                        </span>
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th scope="row">{{ $t('list.delivery') }}</th>
 | 
			
		||||
                <td> {{ delivery }}</td>
 | 
			
		||||
                  <td style="width:40%;"><strong>{{ $t('list.delivery') }}</strong></td>
 | 
			
		||||
                <td>
 | 
			
		||||
                        <span v-for="delivery in deliveries" :key="delivery">
 | 
			
		||||
                            {{ $t('firefly.webhook_delivery_' + delivery) }}
 | 
			
		||||
                        </span>
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              </tbody>
 | 
			
		||||
            </table>
 | 
			
		||||
@@ -86,12 +98,13 @@
 | 
			
		||||
            <table class="table table-hover" aria-label="A table">
 | 
			
		||||
              <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th scope="row" style="width:40%;">{{ $t('list.url') }}</th>
 | 
			
		||||
                  <td style="width:40%;"><strong>{{ $t('list.url') }}</strong></td>
 | 
			
		||||
                <td><input type="text" readonly class="form-control" :value=url></td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <td style="width:40%;"><strong>
 | 
			
		||||
                  {{ $t('list.secret') }}
 | 
			
		||||
                  </strong>
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <em style="cursor:pointer"
 | 
			
		||||
@@ -260,11 +273,14 @@ export default {
 | 
			
		||||
      secret: '',
 | 
			
		||||
      show_secret: false,
 | 
			
		||||
      trigger: '',
 | 
			
		||||
    triggers: [],
 | 
			
		||||
      loading: true,
 | 
			
		||||
      response: '',
 | 
			
		||||
    responses: [],
 | 
			
		||||
      message_content: '',
 | 
			
		||||
      message_attempts: [],
 | 
			
		||||
      delivery: '',
 | 
			
		||||
    deliveries: [],
 | 
			
		||||
      messages: [],
 | 
			
		||||
      active: false,
 | 
			
		||||
      edit_url: '#',
 | 
			
		||||
@@ -369,9 +385,9 @@ export default {
 | 
			
		||||
        this.title = response.data.data.attributes.title;
 | 
			
		||||
        this.url = response.data.data.attributes.url;
 | 
			
		||||
        this.secret = response.data.data.attributes.secret;
 | 
			
		||||
        this.trigger = this.$t('firefly.webhook_trigger_' + response.data.data.attributes.trigger);
 | 
			
		||||
        this.response = this.$t('firefly.webhook_response_' + response.data.data.attributes.response);
 | 
			
		||||
        this.delivery = this.$t('firefly.webhook_delivery_' + response.data.data.attributes.delivery);
 | 
			
		||||
        this.triggers = response.data.data.attributes.triggers;
 | 
			
		||||
        this.responses = response.data.data.attributes.responses;
 | 
			
		||||
        this.deliveries = response.data.data.attributes.deliveries;
 | 
			
		||||
 | 
			
		||||
        this.active = response.data.data.attributes.active;
 | 
			
		||||
        this.url = response.data.data.attributes.url;
 | 
			
		||||
 
 | 
			
		||||
@@ -107,12 +107,18 @@
 | 
			
		||||
        "multi_account_warning_withdrawal": "Keep in mind that the source account of subsequent splits will be overruled by whatever is defined in the first split of the withdrawal.",
 | 
			
		||||
        "multi_account_warning_deposit": "Keep in mind that the destination account of subsequent splits will be overruled by whatever is defined in the first split of the deposit.",
 | 
			
		||||
        "multi_account_warning_transfer": "Keep in mind that the source + destination account of subsequent splits will be overruled by whatever is defined in the first split of the transfer.",
 | 
			
		||||
        "webhook_trigger_ANY": "After any event",
 | 
			
		||||
        "webhook_trigger_STORE_TRANSACTION": "After transaction creation",
 | 
			
		||||
        "webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
 | 
			
		||||
        "webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
 | 
			
		||||
        "webhook_trigger_STORE_BUDGET": "After budget creation",
 | 
			
		||||
        "webhook_trigger_UPDATE_BUDGET": "After budget update",
 | 
			
		||||
        "webhook_trigger_DESTROY_BUDGET": "After budget delete",
 | 
			
		||||
        "webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
 | 
			
		||||
        "webhook_response_TRANSACTIONS": "Transaction details",
 | 
			
		||||
        "webhook_response_RELEVANT": "Relevant details",
 | 
			
		||||
        "webhook_response_ACCOUNTS": "Account details",
 | 
			
		||||
        "webhook_response_none_NONE": "No details",
 | 
			
		||||
        "webhook_response_NONE": "No details",
 | 
			
		||||
        "webhook_delivery_JSON": "JSON",
 | 
			
		||||
        "actions": "Actions",
 | 
			
		||||
        "meta_data": "Meta data",
 | 
			
		||||
 
 | 
			
		||||
@@ -107,12 +107,18 @@
 | 
			
		||||
        "multi_account_warning_withdrawal": "\u0636\u0639 \u0641\u064a \u0627\u0639\u062a\u0628\u0627\u0631\u0643 \u0623\u0646 \u062d\u0633\u0627\u0628 \u0627\u0644\u0645\u0635\u062f\u0631 \u0641\u064a \u0627\u0644\u062a\u0642\u0633\u064a\u0645\u0627\u062a \u0627\u0644\u0644\u0627\u062d\u0642\u0629 \u0633\u064a\u062a\u0645 \u062a\u062c\u0627\u0648\u0632\u0647 \u0628\u0645\u0627 \u0647\u0648 \u0645\u062d\u062f\u062f \u0641\u064a \u0623\u0648\u0644 \u062a\u0642\u0633\u064a\u0645 \u0644\u0644\u0633\u062d\u0628.",
 | 
			
		||||
        "multi_account_warning_deposit": "\u0636\u0639 \u0641\u064a \u0627\u0639\u062a\u0628\u0627\u0631\u0643 \u0623\u0646 \u062d\u0633\u0627\u0628 \u0627\u0644\u0648\u062c\u0647\u0629 \u0641\u064a \u0627\u0644\u062a\u0642\u0633\u064a\u0645\u0627\u062a \u0627\u0644\u0644\u0627\u062d\u0642\u0629 \u0633\u064a\u062a\u0645 \u062a\u062c\u0627\u0648\u0632\u0647 \u0628\u0645\u0627 \u0647\u0648 \u0645\u062d\u062f\u062f \u0641\u064a \u0623\u0648\u0644 \u062a\u0642\u0633\u064a\u0645 \u0644\u0644\u0625\u064a\u062f\u0627\u0639.",
 | 
			
		||||
        "multi_account_warning_transfer": "\u0636\u0639 \u0641\u064a \u0627\u0639\u062a\u0628\u0627\u0631\u0643 \u0623\u0646 \u062d\u0633\u0627\u0628 \u0627\u0644\u0645\u0635\u062f\u0631 + \u0627\u0644\u0648\u062c\u0647\u0629 \u0641\u064a \u0627\u0644\u062a\u0642\u0633\u064a\u0645\u0627\u062a \u0627\u0644\u0644\u0627\u062d\u0642\u0629 \u0633\u064a\u062a\u0645 \u062a\u062c\u0627\u0648\u0632\u0647\u0645\u0627 \u0628\u0645\u0627 \u0647\u0648 \u0645\u062d\u062f\u062f \u0641\u064a \u0623\u0648\u0644 \u062a\u0642\u0633\u064a\u0645 \u0644\u0644\u062a\u062d\u0648\u064a\u0644.",
 | 
			
		||||
        "webhook_trigger_ANY": "After any event",
 | 
			
		||||
        "webhook_trigger_STORE_TRANSACTION": "\u0628\u0639\u062f \u0625\u0646\u0634\u0627\u0621 \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629",
 | 
			
		||||
        "webhook_trigger_UPDATE_TRANSACTION": "\u0628\u0639\u062f \u062a\u062d\u062f\u064a\u062b \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629",
 | 
			
		||||
        "webhook_trigger_DESTROY_TRANSACTION": "\u0628\u0639\u062f \u062d\u0630\u0641 \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629",
 | 
			
		||||
        "webhook_trigger_STORE_BUDGET": "After budget creation",
 | 
			
		||||
        "webhook_trigger_UPDATE_BUDGET": "After budget update",
 | 
			
		||||
        "webhook_trigger_DESTROY_BUDGET": "After budget delete",
 | 
			
		||||
        "webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
 | 
			
		||||
        "webhook_response_TRANSACTIONS": "\u062a\u0641\u0627\u0635\u064a\u0644 \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629",
 | 
			
		||||
        "webhook_response_RELEVANT": "Relevant details",
 | 
			
		||||
        "webhook_response_ACCOUNTS": "\u062a\u0641\u0627\u0635\u064a\u0644 \u0627\u0644\u062d\u0633\u0627\u0628",
 | 
			
		||||
        "webhook_response_none_NONE": "\u0644\u0627 \u062a\u0648\u062c\u062f \u062a\u0641\u0627\u0635\u064a\u0644",
 | 
			
		||||
        "webhook_response_NONE": "No details",
 | 
			
		||||
        "webhook_delivery_JSON": "JSON",
 | 
			
		||||
        "actions": "\u0627\u0644\u0625\u062c\u0631\u0627\u0621\u0627\u062a",
 | 
			
		||||
        "meta_data": "\u0628\u064a\u0627\u0646\u0627\u062a \u0648\u0635\u0641\u064a\u0629",
 | 
			
		||||
 
 | 
			
		||||
@@ -107,12 +107,18 @@
 | 
			
		||||
        "multi_account_warning_withdrawal": "\u0418\u043c\u0430\u0439\u0442\u0435 \u043f\u0440\u0435\u0434\u0432\u0438\u0434, \u0447\u0435 \u0440\u0430\u0437\u0445\u043e\u0434\u043d\u0430 \u0441\u043c\u0435\u0442\u043a\u0430 \u043d\u0430 \u0441\u043b\u0435\u0434\u0432\u0430\u0449\u0438\u0442\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u043d\u0438\u044f \u0449\u0435 \u0431\u044a\u0434\u0435 \u0442\u0430\u0437\u0438 \u043a\u043e\u044f\u0442\u043e \u0435 \u0434\u0435\u0444\u0438\u043d\u0438\u0440\u0430\u043d\u0430 \u0432 \u043f\u044a\u0440\u0432\u0438\u044f \u0440\u0430\u0437\u0434\u0435\u043b \u043d\u0430 \u0442\u0435\u0433\u043b\u0435\u043d\u0435\u0442\u043e.",
 | 
			
		||||
        "multi_account_warning_deposit": "\u0418\u043c\u0430\u0439\u0442\u0435 \u043f\u0440\u0435\u0434\u0432\u0438\u0434, \u0447\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u043d\u0430\u0442\u0430 \u0441\u043c\u0435\u0442\u043a\u0430 \u043d\u0430 \u0441\u043b\u0435\u0434\u0432\u0430\u0449\u0438\u0442\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u043d\u0438\u044f \u0449\u0435 \u0431\u044a\u0434\u0435 \u0442\u0430\u0437\u0438 \u043a\u043e\u044f\u0442\u043e \u0435 \u0434\u0435\u0444\u0438\u043d\u0438\u0440\u0430\u043d\u0430 \u0432 \u043f\u044a\u0440\u0432\u0438\u044f \u0440\u0430\u0437\u0434\u0435\u043b \u043d\u0430 \u0434\u0435\u043f\u043e\u0437\u0438\u0442\u0430.",
 | 
			
		||||
        "multi_account_warning_transfer": "\u0418\u043c\u0430\u0439\u0442\u0435 \u043f\u0440\u0435\u0434\u0432\u0438\u0434, \u0447\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u043d\u0430\u0442\u0430 + \u0440\u0430\u0437\u0445\u043e\u0434\u043d\u0430\u0442\u0430 \u0441\u043c\u0435\u0442\u043a\u0430 \u043d\u0430 \u0441\u043b\u0435\u0434\u0432\u0430\u0449\u0438\u0442\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u043d\u0438\u044f \u0449\u0435 \u0431\u044a\u0434\u0435 \u0442\u0430\u0437\u0438 \u043a\u043e\u044f\u0442\u043e \u0435 \u0434\u0435\u0444\u0438\u043d\u0438\u0440\u0430\u043d\u0430 \u0432 \u043f\u044a\u0440\u0432\u0438\u044f \u0440\u0430\u0437\u0434\u0435\u043b \u043d\u0430 \u043f\u0440\u0435\u0445\u0432\u044a\u0440\u043b\u044f\u043d\u0435\u0442\u043e.",
 | 
			
		||||
        "webhook_trigger_ANY": "After any event",
 | 
			
		||||
        "webhook_trigger_STORE_TRANSACTION": "After transaction creation",
 | 
			
		||||
        "webhook_trigger_UPDATE_TRANSACTION": "After transaction update",
 | 
			
		||||
        "webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
 | 
			
		||||
        "webhook_trigger_STORE_BUDGET": "After budget creation",
 | 
			
		||||
        "webhook_trigger_UPDATE_BUDGET": "After budget update",
 | 
			
		||||
        "webhook_trigger_DESTROY_BUDGET": "After budget delete",
 | 
			
		||||
        "webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
 | 
			
		||||
        "webhook_response_TRANSACTIONS": "Transaction details",
 | 
			
		||||
        "webhook_response_RELEVANT": "Relevant details",
 | 
			
		||||
        "webhook_response_ACCOUNTS": "Account details",
 | 
			
		||||
        "webhook_response_none_NONE": "No details",
 | 
			
		||||
        "webhook_response_NONE": "No details",
 | 
			
		||||
        "webhook_delivery_JSON": "JSON",
 | 
			
		||||
        "actions": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f",
 | 
			
		||||
        "meta_data": "\u041c\u0435\u0442\u0430 \u0434\u0430\u043d\u043d\u0438",
 | 
			
		||||
 
 | 
			
		||||
@@ -107,12 +107,18 @@
 | 
			
		||||
        "multi_account_warning_withdrawal": "Tingues en compte que el compte d'origen de divisions posteriors ser\u00e0 anul\u00b7lat pel que es troba definit a la primera divisi\u00f3 de la retirada.",
 | 
			
		||||
        "multi_account_warning_deposit": "Tingues en compte que el compte de dest\u00ed de divisions posteriors ser\u00e0 anul\u00b7lat pel que es troba definit a la primera divisi\u00f3 del dip\u00f2sit.",
 | 
			
		||||
        "multi_account_warning_transfer": "Tingues en compte que el compte d'origen + dest\u00ed de divisions posteriors ser\u00e0 anul\u00b7lat pel que es troba definit a la primera divisi\u00f3 de la transfer\u00e8ncia.",
 | 
			
		||||
        "webhook_trigger_ANY": "After any event",
 | 
			
		||||
        "webhook_trigger_STORE_TRANSACTION": "Despr\u00e9s de crear la transacci\u00f3",
 | 
			
		||||
        "webhook_trigger_UPDATE_TRANSACTION": "Despr\u00e9s d'actualitzar la transacci\u00f3",
 | 
			
		||||
        "webhook_trigger_DESTROY_TRANSACTION": "Despr\u00e9s d'eliminar la transacci\u00f3",
 | 
			
		||||
        "webhook_trigger_STORE_BUDGET": "After budget creation",
 | 
			
		||||
        "webhook_trigger_UPDATE_BUDGET": "After budget update",
 | 
			
		||||
        "webhook_trigger_DESTROY_BUDGET": "After budget delete",
 | 
			
		||||
        "webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
 | 
			
		||||
        "webhook_response_TRANSACTIONS": "Detalls de la transacci\u00f3",
 | 
			
		||||
        "webhook_response_RELEVANT": "Relevant details",
 | 
			
		||||
        "webhook_response_ACCOUNTS": "Detalls del compte",
 | 
			
		||||
        "webhook_response_none_NONE": "Sense detalls",
 | 
			
		||||
        "webhook_response_NONE": "No details",
 | 
			
		||||
        "webhook_delivery_JSON": "JSON",
 | 
			
		||||
        "actions": "Accions",
 | 
			
		||||
        "meta_data": "Meta dades",
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
        "welcome_back": "Jak to jde?",
 | 
			
		||||
        "flash_error": "Chyba!",
 | 
			
		||||
        "flash_warning": "Varov\u00e1n\u00ed!",
 | 
			
		||||
        "flash_success": "\u00dasp\u011b\u0161n\u011b dokon\u010deno!",
 | 
			
		||||
        "flash_success": "V\u00fdborn\u011b!",
 | 
			
		||||
        "close": "Zav\u0159\u00edt",
 | 
			
		||||
        "select_dest_account": "Please select or type a valid destination account name",
 | 
			
		||||
        "select_source_account": "Please select or type a valid source account name",
 | 
			
		||||
@@ -107,12 +107,18 @@
 | 
			
		||||
        "multi_account_warning_withdrawal": "Zdrojov\u00fd \u00fa\u010det cel\u00e9 transakce je ovl\u00e1dan\u00fd prvn\u00edm rozd\u011blen\u00edm.",
 | 
			
		||||
        "multi_account_warning_deposit": "C\u00edlov\u00fd \u00fa\u010del v\u0161ech n\u00e1sleduj\u00edc\u00edch rozd\u011blen\u00ed je ovl\u00e1dan\u00fd c\u00edlov\u00fdm \u00fa\u010dtem prvn\u00edho rozd\u011blen\u00ed transakce.",
 | 
			
		||||
        "multi_account_warning_transfer": "Zdrojov\u00fd i c\u00edlov\u00fd \u00fa\u010det v\u0161ech n\u00e1sleduj\u00edc\u00edch rozd\u011blen\u00ed jsou ovl\u00e1d\u00e1ny zdrojov\u00fdm a c\u00edlov\u00fdm \u00fa\u010dtem prvn\u00edho rozd\u011blen\u00ed transakce.",
 | 
			
		||||
        "webhook_trigger_ANY": "After any event",
 | 
			
		||||
        "webhook_trigger_STORE_TRANSACTION": "Po vytvo\u0159en\u00ed transakce",
 | 
			
		||||
        "webhook_trigger_UPDATE_TRANSACTION": "Po aktualizaci transakce",
 | 
			
		||||
        "webhook_trigger_DESTROY_TRANSACTION": "Po odstran\u011bn\u00ed transakce",
 | 
			
		||||
        "webhook_trigger_STORE_BUDGET": "After budget creation",
 | 
			
		||||
        "webhook_trigger_UPDATE_BUDGET": "After budget update",
 | 
			
		||||
        "webhook_trigger_DESTROY_BUDGET": "After budget delete",
 | 
			
		||||
        "webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
 | 
			
		||||
        "webhook_response_TRANSACTIONS": "Podrobnosti transakce",
 | 
			
		||||
        "webhook_response_RELEVANT": "Relevant details",
 | 
			
		||||
        "webhook_response_ACCOUNTS": "Podrobnosti \u00fa\u010dtu",
 | 
			
		||||
        "webhook_response_none_NONE": "\u017d\u00e1dn\u00e9 detaily",
 | 
			
		||||
        "webhook_response_NONE": "No details",
 | 
			
		||||
        "webhook_delivery_JSON": "JSON",
 | 
			
		||||
        "actions": "Akce",
 | 
			
		||||
        "meta_data": "Metadata",
 | 
			
		||||
 
 | 
			
		||||
@@ -107,12 +107,18 @@
 | 
			
		||||
        "multi_account_warning_withdrawal": "Husk, at kildekontoen for efterf\u00f8lgende opdelinger vil blive overstyret af hvad der er defineret i den f\u00f8rste opdeling af tilbagetr\u00e6kningen.",
 | 
			
		||||
        "multi_account_warning_deposit": "Husk, at destinationskontoen for efterf\u00f8lgende opdelinger vil blive tilsidesat af hvad der er defineret i den f\u00f8rste opsplitning af depositummet.",
 | 
			
		||||
        "multi_account_warning_transfer": "Husk p\u00e5, at kilden + destination konto for efterf\u00f8lgende opdelinger vil blive overstyret af hvad der er defineret i den f\u00f8rste opdeling af overf\u00f8rslen.",
 | 
			
		||||
        "webhook_trigger_ANY": "After any event",
 | 
			
		||||
        "webhook_trigger_STORE_TRANSACTION": "Efter oprettelse af transaktion",
 | 
			
		||||
        "webhook_trigger_UPDATE_TRANSACTION": "Efter opdatering af transaktion",
 | 
			
		||||
        "webhook_trigger_DESTROY_TRANSACTION": "Efter sletning af transaktion",
 | 
			
		||||
        "webhook_trigger_STORE_BUDGET": "After budget creation",
 | 
			
		||||
        "webhook_trigger_UPDATE_BUDGET": "After budget update",
 | 
			
		||||
        "webhook_trigger_DESTROY_BUDGET": "After budget delete",
 | 
			
		||||
        "webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
 | 
			
		||||
        "webhook_response_TRANSACTIONS": "Transaktionsdetaljer",
 | 
			
		||||
        "webhook_response_RELEVANT": "Relevant details",
 | 
			
		||||
        "webhook_response_ACCOUNTS": "Kontodetaljer",
 | 
			
		||||
        "webhook_response_none_NONE": "Ingen detaljer",
 | 
			
		||||
        "webhook_response_NONE": "No details",
 | 
			
		||||
        "webhook_delivery_JSON": "JSON",
 | 
			
		||||
        "actions": "Handlinger",
 | 
			
		||||
        "meta_data": "Meta data",
 | 
			
		||||
 
 | 
			
		||||
@@ -107,12 +107,18 @@
 | 
			
		||||
        "multi_account_warning_withdrawal": "Bedenken Sie, dass das Quellkonto nachfolgender Aufteilungen von dem, was in der ersten Aufteilung der Abhebung definiert ist, au\u00dfer Kraft gesetzt wird.",
 | 
			
		||||
        "multi_account_warning_deposit": "Bedenken Sie, dass das Zielkonto nachfolgender Aufteilungen von dem, was in der ersten Aufteilung der Einnahmen definiert ist, au\u00dfer Kraft gesetzt wird.",
 | 
			
		||||
        "multi_account_warning_transfer": "Bedenken Sie, dass das Quell- und Zielkonto nachfolgender Aufteilungen durch das, was in der ersten Aufteilung der \u00dcbertragung definiert ist, au\u00dfer Kraft gesetzt wird.",
 | 
			
		||||
        "webhook_trigger_ANY": "After any event",
 | 
			
		||||
        "webhook_trigger_STORE_TRANSACTION": "Nach Erstellen einer Buchung",
 | 
			
		||||
        "webhook_trigger_UPDATE_TRANSACTION": "Nach Aktualisierung einer Buchung",
 | 
			
		||||
        "webhook_trigger_DESTROY_TRANSACTION": "Nach dem L\u00f6schen einer Buchung",
 | 
			
		||||
        "webhook_trigger_STORE_BUDGET": "Nach der Erstellung des Budgets",
 | 
			
		||||
        "webhook_trigger_UPDATE_BUDGET": "Nach der Aktualisierung des Budgets",
 | 
			
		||||
        "webhook_trigger_DESTROY_BUDGET": "Nach dem L\u00f6schen des Budgets",
 | 
			
		||||
        "webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "Nach dem \u00c4ndern des budgetierten Betrags",
 | 
			
		||||
        "webhook_response_TRANSACTIONS": "Buchungsdetails",
 | 
			
		||||
        "webhook_response_RELEVANT": "Relevant details",
 | 
			
		||||
        "webhook_response_ACCOUNTS": "Kontodetails",
 | 
			
		||||
        "webhook_response_none_NONE": "Keine Daten",
 | 
			
		||||
        "webhook_response_NONE": "Keine Details",
 | 
			
		||||
        "webhook_delivery_JSON": "JSON",
 | 
			
		||||
        "actions": "Aktionen",
 | 
			
		||||
        "meta_data": "Metadaten",
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user