mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 02:36:28 +00:00 
			
		
		
		
	Compare commits
	
		
			218 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | fa3343f437 | ||
|  | c5b8a951d2 | ||
|  | 20b1fc05cb | ||
|  | ab441d7d0c | ||
|  | b621d14bdf | ||
|  | 94730e998b | ||
|  | 23c2f76e52 | ||
|  | 40196d48b2 | ||
|  | ef3c2eb701 | ||
|  | 4f994a2795 | ||
|  | 807e575dac | ||
|  | 04b1465eb7 | ||
|  | 6f79ab2a70 | ||
|  | c1469f016e | ||
|  | 373ec934df | ||
|  | abd34ba1dc | ||
|  | 89b7596caf | ||
|  | 3543548ba0 | ||
|  | a932965908 | ||
|  | 9056126328 | ||
|  | 0e85a5123d | ||
|  | d3f19db42d | ||
|  | 9894d16d26 | ||
|  | 0b069bcb58 | ||
|  | 36d54c3fac | ||
|  | 530e48f16e | ||
|  | 9763f9f922 | ||
|  | bbd4d7a8ef | ||
|  | bafe2ece8c | ||
|  | b2449eb726 | ||
|  | 353c345d9a | ||
|  | e2461ba839 | ||
|  | e199fdd2b6 | ||
|  | 592a89d6a5 | ||
|  | 7a7b629225 | ||
|  | 21a4774561 | ||
|  | 5be97bb18f | ||
|  | a12ff10550 | ||
|  | c808294ca1 | ||
|  | 9dcf264658 | ||
|  | e0bbd3a810 | ||
|  | 2886a22f4e | ||
|  | 6f783f7662 | ||
|  | 992fdc9e27 | ||
|  | 73529c31e0 | ||
|  | d9fc91a432 | ||
|  | 6a432e7931 | ||
|  | 26f7575ba2 | ||
|  | b1c1bbd6c0 | ||
|  | d40643af54 | ||
|  | 11cfefd908 | ||
|  | ff2b2eec9b | ||
|  | 4f1187bf6e | ||
|  | 642539e410 | ||
|  | 1f774e0d71 | ||
|  | 1da19cde6f | ||
|  | ac5973833e | ||
|  | 40b0e31d27 | ||
|  | f56f8d72df | ||
|  | b47bb13558 | ||
|  | b4133b6512 | ||
|  | fb20095502 | ||
|  | cfc23c4cb9 | ||
|  | 44701f1633 | ||
|  | fadf799b9c | ||
|  | c22a9784ee | ||
|  | a6512d3d74 | ||
|  | cea2ca7532 | ||
|  | e322069bb4 | ||
|  | da7a976c4e | ||
|  | 300dba7257 | ||
|  | ec59f80fe3 | ||
|  | 37aa5bcc60 | ||
|  | e50d2cb481 | ||
|  | 468709c092 | ||
|  | 11ced9216c | ||
|  | cfa9151f45 | ||
|  | 6577224a55 | ||
|  | b4209f2e72 | ||
|  | f045e4ea69 | ||
|  | 4a37152aea | ||
|  | 2907db1380 | ||
|  | 80a35692c5 | ||
|  | 1a6fe5ca3c | ||
|  | 13ec3493bd | ||
|  | bf55e8df72 | ||
|  | 775504d812 | ||
|  | 8d6c45b68e | ||
|  | db9094956e | ||
|  | 25d02e92f4 | ||
|  | 81cb0a38f3 | ||
|  | e07a9d6162 | ||
|  | 2ccbc16dfa | ||
|  | e00addc0b0 | ||
|  | da7a2cf0c0 | ||
|  | 2368788405 | ||
|  | f603415931 | ||
|  | 523fa42998 | ||
|  | e449395f3f | ||
|  | d8d8002f1e | ||
|  | 2570ca9573 | ||
|  | e5fdc2cbfd | ||
|  | 0349cdbc1b | ||
|  | 122f0309a6 | ||
|  | 09bff5ea4e | ||
|  | 7ea112c5e7 | ||
|  | 44df07a5f5 | ||
|  | 66b0d9d309 | ||
|  | 6ac3d3e62c | ||
|  | 925450f84c | ||
|  | 62f59c6a19 | ||
|  | 7db21612a0 | ||
|  | 2c0da2cf26 | ||
|  | 79484cc194 | ||
|  | 6f18748c72 | ||
|  | 577824930f | ||
|  | d614519ee7 | ||
|  | ae31041f7f | ||
|  | 62c4d0cf86 | ||
|  | c2ddabbad2 | ||
|  | 458402aaff | ||
|  | 5c81e98218 | ||
|  | 37a46b02f4 | ||
|  | b3e1ecdd02 | ||
|  | 1780e6dc61 | ||
|  | 50f346d092 | ||
|  | ccc851090a | ||
|  | 4605d84cc8 | ||
|  | 8c7ab50325 | ||
|  | 908539836b | ||
|  | 9f71cf966c | ||
|  | 02ed47c578 | ||
|  | 1ddbaf0884 | ||
|  | d3ed8c6f0f | ||
|  | 4f1ac2ac6f | ||
|  | 1e733f4c8b | ||
|  | 8e2546da9d | ||
|  | 3a8162d3c5 | ||
|  | f7ceb75316 | ||
|  | 744e193faa | ||
|  | 12b0e11592 | ||
|  | 717f3a9e3d | ||
|  | b9f0682f04 | ||
|  | 8792465fd5 | ||
|  | 6fbf9a119d | ||
|  | 0dfa21a92e | ||
|  | 136fe8e8eb | ||
|  | d510c4e31d | ||
|  | c066bcc4ce | ||
|  | c9e7ae1f08 | ||
|  | 6a9b4f4d55 | ||
|  | 2b5054b905 | ||
|  | 0a45a2485b | ||
|  | fcc0294d07 | ||
|  | ad981c2bf0 | ||
|  | 75a32b2f94 | ||
|  | 70b60f756b | ||
|  | 0d2ae8ae23 | ||
|  | 8043c86942 | ||
|  | 4c30a7bc55 | ||
|  | f615b9c252 | ||
|  | c19b36a391 | ||
|  | 935634e487 | ||
|  | 2c3f032a2b | ||
|  | 7e62b75b12 | ||
|  | de57ab0874 | ||
|  | 6fb4aaecd3 | ||
|  | 45fdbf5a11 | ||
|  | c6615a7b17 | ||
|  | 0efb3d2dcf | ||
|  | b8a58f83ee | ||
|  | 110228e65e | ||
|  | 8ad27e0eda | ||
|  | 2e0d90c685 | ||
|  | bd2ecb13b8 | ||
|  | 5725570dbb | ||
|  | 11f77685e4 | ||
|  | 0521c46d27 | ||
|  | 50d6225590 | ||
|  | df55f7de79 | ||
|  | 5152ae9622 | ||
|  | 075d0da63d | ||
|  | d804df2a2f | ||
|  | ff0f8beb81 | ||
|  | c00be92f97 | ||
|  | 88f6221424 | ||
|  | f9463e02a2 | ||
|  | 25a23801be | ||
|  | fe7bb02dc5 | ||
|  | 68edcfc4e8 | ||
|  | 5f8a24a684 | ||
|  | 0a5d62605a | ||
|  | 1873be8d95 | ||
|  | 01892c3828 | ||
|  | b87e60c72f | ||
|  | 3a083f88b5 | ||
|  | 566bb2f097 | ||
|  | 1ba7847d84 | ||
|  | c32044a8eb | ||
|  | 72a2d417af | ||
|  | 09c18d6d44 | ||
|  | 84ae6a633e | ||
|  | 82749cea07 | ||
|  | 23aa0e3ba3 | ||
|  | 8be27a2201 | ||
|  | ff98f3cc3e | ||
|  | 01c4d25646 | ||
|  | 292b9ac9d0 | ||
|  | 6bdae03961 | ||
|  | 7426c6aac3 | ||
|  | 211526c032 | ||
|  | e6fe08dd61 | ||
|  | 7bba67130a | ||
|  | 7186d8ddfd | ||
|  | 1a6bc6decd | ||
|  | 5b11c86113 | ||
|  | 98b95ab891 | ||
|  | c3068d10bf | 
							
								
								
									
										175
									
								
								.ci/php-cs-fixer/composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										175
									
								
								.ci/php-cs-fixer/composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -226,21 +226,22 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "friendsofphp/php-cs-fixer", | ||||
|             "version": "v3.45.0", | ||||
|             "version": "v3.49.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", | ||||
|                 "reference": "c0daa33cb2533cd73f48dde1c70c2afa3e7953b5" | ||||
|                 "reference": "8742f7aa6f72a399688b65e4f58992c2d4681fc2" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/c0daa33cb2533cd73f48dde1c70c2afa3e7953b5", | ||||
|                 "reference": "c0daa33cb2533cd73f48dde1c70c2afa3e7953b5", | ||||
|                 "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/8742f7aa6f72a399688b65e4f58992c2d4681fc2", | ||||
|                 "reference": "8742f7aa6f72a399688b65e4f58992c2d4681fc2", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "composer/semver": "^3.4", | ||||
|                 "composer/xdebug-handler": "^3.0.3", | ||||
|                 "ext-filter": "*", | ||||
|                 "ext-json": "*", | ||||
|                 "ext-tokenizer": "*", | ||||
|                 "php": "^7.4 || ^8.0", | ||||
| @@ -304,7 +305,7 @@ | ||||
|             ], | ||||
|             "support": { | ||||
|                 "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", | ||||
|                 "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.45.0" | ||||
|                 "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.49.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -312,7 +313,7 @@ | ||||
|                     "type": "github" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-12-30T02:07:07+00:00" | ||||
|             "time": "2024-02-02T00:41:40+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "psr/container", | ||||
| @@ -536,16 +537,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/console", | ||||
|             "version": "v7.0.2", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/console.git", | ||||
|                 "reference": "f8587c4cdc5acad67af71c37db34ef03af91e59c" | ||||
|                 "reference": "c5010d50f1ee4b25cfa0201d9915cf1b14071456" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/console/zipball/f8587c4cdc5acad67af71c37db34ef03af91e59c", | ||||
|                 "reference": "f8587c4cdc5acad67af71c37db34ef03af91e59c", | ||||
|                 "url": "https://api.github.com/repos/symfony/console/zipball/c5010d50f1ee4b25cfa0201d9915cf1b14071456", | ||||
|                 "reference": "c5010d50f1ee4b25cfa0201d9915cf1b14071456", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -609,7 +610,7 @@ | ||||
|                 "terminal" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/console/tree/v7.0.2" | ||||
|                 "source": "https://github.com/symfony/console/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -625,7 +626,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-12-10T16:54:46+00:00" | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/deprecation-contracts", | ||||
| @@ -696,16 +697,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/event-dispatcher", | ||||
|             "version": "v7.0.2", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/event-dispatcher.git", | ||||
|                 "reference": "098b62ae81fdd6cbf941f355059f617db28f4f9a" | ||||
|                 "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/098b62ae81fdd6cbf941f355059f617db28f4f9a", | ||||
|                 "reference": "098b62ae81fdd6cbf941f355059f617db28f4f9a", | ||||
|                 "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/834c28d533dd0636f910909d01b9ff45cc094b5e", | ||||
|                 "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -756,7 +757,7 @@ | ||||
|             "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.2" | ||||
|                 "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -772,7 +773,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-12-27T22:24:19+00:00" | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/event-dispatcher-contracts", | ||||
| @@ -852,16 +853,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/filesystem", | ||||
|             "version": "v7.0.0", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/filesystem.git", | ||||
|                 "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7" | ||||
|                 "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/filesystem/zipball/7da8ea2362a283771478c5f7729cfcb43a76b8b7", | ||||
|                 "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7", | ||||
|                 "url": "https://api.github.com/repos/symfony/filesystem/zipball/2890e3a825bc0c0558526c04499c13f83e1b6b12", | ||||
|                 "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -895,7 +896,7 @@ | ||||
|             "description": "Provides basic utilities for the filesystem", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/filesystem/tree/v7.0.0" | ||||
|                 "source": "https://github.com/symfony/filesystem/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -911,7 +912,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-07-27T06:33:22+00:00" | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/finder", | ||||
| @@ -1046,16 +1047,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/polyfill-ctype", | ||||
|             "version": "v1.28.0", | ||||
|             "version": "v1.29.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/polyfill-ctype.git", | ||||
|                 "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" | ||||
|                 "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", | ||||
|                 "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", | ||||
|                 "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1069,9 +1070,6 @@ | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "1.28-dev" | ||||
|                 }, | ||||
|                 "thanks": { | ||||
|                     "name": "symfony/polyfill", | ||||
|                     "url": "https://github.com/symfony/polyfill" | ||||
| @@ -1108,7 +1106,7 @@ | ||||
|                 "portable" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" | ||||
|                 "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1124,20 +1122,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-01-26T09:26:14+00:00" | ||||
|             "time": "2024-01-29T20:11:03+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/polyfill-intl-grapheme", | ||||
|             "version": "v1.28.0", | ||||
|             "version": "v1.29.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/polyfill-intl-grapheme.git", | ||||
|                 "reference": "875e90aeea2777b6f135677f618529449334a612" | ||||
|                 "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", | ||||
|                 "reference": "875e90aeea2777b6f135677f618529449334a612", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", | ||||
|                 "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1148,9 +1146,6 @@ | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "1.28-dev" | ||||
|                 }, | ||||
|                 "thanks": { | ||||
|                     "name": "symfony/polyfill", | ||||
|                     "url": "https://github.com/symfony/polyfill" | ||||
| @@ -1189,7 +1184,7 @@ | ||||
|                 "shim" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" | ||||
|                 "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1205,20 +1200,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-01-26T09:26:14+00:00" | ||||
|             "time": "2024-01-29T20:11:03+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/polyfill-intl-normalizer", | ||||
|             "version": "v1.28.0", | ||||
|             "version": "v1.29.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/polyfill-intl-normalizer.git", | ||||
|                 "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" | ||||
|                 "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", | ||||
|                 "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", | ||||
|                 "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1229,9 +1224,6 @@ | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "1.28-dev" | ||||
|                 }, | ||||
|                 "thanks": { | ||||
|                     "name": "symfony/polyfill", | ||||
|                     "url": "https://github.com/symfony/polyfill" | ||||
| @@ -1273,7 +1265,7 @@ | ||||
|                 "shim" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" | ||||
|                 "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1289,20 +1281,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-01-26T09:26:14+00:00" | ||||
|             "time": "2024-01-29T20:11:03+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/polyfill-mbstring", | ||||
|             "version": "v1.28.0", | ||||
|             "version": "v1.29.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/polyfill-mbstring.git", | ||||
|                 "reference": "42292d99c55abe617799667f454222c54c60e229" | ||||
|                 "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", | ||||
|                 "reference": "42292d99c55abe617799667f454222c54c60e229", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", | ||||
|                 "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1316,9 +1308,6 @@ | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "1.28-dev" | ||||
|                 }, | ||||
|                 "thanks": { | ||||
|                     "name": "symfony/polyfill", | ||||
|                     "url": "https://github.com/symfony/polyfill" | ||||
| @@ -1356,7 +1345,7 @@ | ||||
|                 "shim" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" | ||||
|                 "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1372,20 +1361,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-07-28T09:04:16+00:00" | ||||
|             "time": "2024-01-29T20:11:03+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/polyfill-php80", | ||||
|             "version": "v1.28.0", | ||||
|             "version": "v1.29.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/polyfill-php80.git", | ||||
|                 "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" | ||||
|                 "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", | ||||
|                 "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", | ||||
|                 "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1393,9 +1382,6 @@ | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "1.28-dev" | ||||
|                 }, | ||||
|                 "thanks": { | ||||
|                     "name": "symfony/polyfill", | ||||
|                     "url": "https://github.com/symfony/polyfill" | ||||
| @@ -1439,7 +1425,7 @@ | ||||
|                 "shim" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" | ||||
|                 "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1455,20 +1441,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-01-26T09:26:14+00:00" | ||||
|             "time": "2024-01-29T20:11:03+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/polyfill-php81", | ||||
|             "version": "v1.28.0", | ||||
|             "version": "v1.29.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/polyfill-php81.git", | ||||
|                 "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" | ||||
|                 "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", | ||||
|                 "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", | ||||
|                 "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", | ||||
|                 "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1476,9 +1462,6 @@ | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-main": "1.28-dev" | ||||
|                 }, | ||||
|                 "thanks": { | ||||
|                     "name": "symfony/polyfill", | ||||
|                     "url": "https://github.com/symfony/polyfill" | ||||
| @@ -1518,7 +1501,7 @@ | ||||
|                 "shim" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" | ||||
|                 "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1534,20 +1517,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-01-26T09:26:14+00:00" | ||||
|             "time": "2024-01-29T20:11:03+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/process", | ||||
|             "version": "v7.0.2", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/process.git", | ||||
|                 "reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a" | ||||
|                 "reference": "937a195147e0c27b2759ade834169ed006d0bc74" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/process/zipball/acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a", | ||||
|                 "reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a", | ||||
|                 "url": "https://api.github.com/repos/symfony/process/zipball/937a195147e0c27b2759ade834169ed006d0bc74", | ||||
|                 "reference": "937a195147e0c27b2759ade834169ed006d0bc74", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1579,7 +1562,7 @@ | ||||
|             "description": "Executes commands in sub-processes", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/process/tree/v7.0.2" | ||||
|                 "source": "https://github.com/symfony/process/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1595,7 +1578,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-12-24T09:15:37+00:00" | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/service-contracts", | ||||
| @@ -1681,16 +1664,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/stopwatch", | ||||
|             "version": "v7.0.0", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/stopwatch.git", | ||||
|                 "reference": "7bbfa3dd564a0ce12eb4acaaa46823c740f9cb7a" | ||||
|                 "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/stopwatch/zipball/7bbfa3dd564a0ce12eb4acaaa46823c740f9cb7a", | ||||
|                 "reference": "7bbfa3dd564a0ce12eb4acaaa46823c740f9cb7a", | ||||
|                 "url": "https://api.github.com/repos/symfony/stopwatch/zipball/983900d6fddf2b0cbaacacbbad07610854bd8112", | ||||
|                 "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1723,7 +1706,7 @@ | ||||
|             "description": "Provides a way to profile code", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/stopwatch/tree/v7.0.0" | ||||
|                 "source": "https://github.com/symfony/stopwatch/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1739,20 +1722,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-07-05T13:06:06+00:00" | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/string", | ||||
|             "version": "v7.0.2", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/string.git", | ||||
|                 "reference": "cc78f14f91f5e53b42044d0620961c48028ff9f5" | ||||
|                 "reference": "524aac4a280b90a4420d8d6a040718d0586505ac" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/string/zipball/cc78f14f91f5e53b42044d0620961c48028ff9f5", | ||||
|                 "reference": "cc78f14f91f5e53b42044d0620961c48028ff9f5", | ||||
|                 "url": "https://api.github.com/repos/symfony/string/zipball/524aac4a280b90a4420d8d6a040718d0586505ac", | ||||
|                 "reference": "524aac4a280b90a4420d8d6a040718d0586505ac", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -1809,7 +1792,7 @@ | ||||
|                 "utf8" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/string/tree/v7.0.2" | ||||
|                 "source": "https://github.com/symfony/string/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -1825,7 +1808,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-12-10T16:54:46+00:00" | ||||
|             "time": "2024-01-29T15:41:16+00:00" | ||||
|         } | ||||
|     ], | ||||
|     "packages-dev": [], | ||||
|   | ||||
| @@ -29,12 +29,12 @@ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) | ||||
| # cp .ci/.env.ci .env | ||||
|  | ||||
| OUTPUT_FORMAT=txt | ||||
| EXTRA_PARAMS="-v" | ||||
| EXTRA_PARAMS="" | ||||
|  | ||||
| if [[ $GITHUB_ACTIONS = "true" ]] | ||||
| then | ||||
|     OUTPUT_FORMAT=gitlab | ||||
|     EXTRA_PARAMS="--diff --dry-run" | ||||
|     OUTPUT_FORMAT=txt | ||||
|     EXTRA_PARAMS="" | ||||
| fi | ||||
|  | ||||
| # clean up php code | ||||
|   | ||||
							
								
								
									
										48
									
								
								.ci/phpmd/composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										48
									
								
								.ci/phpmd/composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -395,16 +395,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/config", | ||||
|             "version": "v7.0.0", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/config.git", | ||||
|                 "reference": "8789646600f4e7e451dde9e1dc81cfa429f3857a" | ||||
|                 "reference": "86a5027869ca3d6bdecae6d5d6c2f77c8f2c1d16" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/config/zipball/8789646600f4e7e451dde9e1dc81cfa429f3857a", | ||||
|                 "reference": "8789646600f4e7e451dde9e1dc81cfa429f3857a", | ||||
|                 "url": "https://api.github.com/repos/symfony/config/zipball/86a5027869ca3d6bdecae6d5d6c2f77c8f2c1d16", | ||||
|                 "reference": "86a5027869ca3d6bdecae6d5d6c2f77c8f2c1d16", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -450,7 +450,7 @@ | ||||
|             "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/config/tree/v7.0.0" | ||||
|                 "source": "https://github.com/symfony/config/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -466,20 +466,20 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-11-09T08:30:23+00:00" | ||||
|             "time": "2024-01-30T08:34:29+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/dependency-injection", | ||||
|             "version": "v7.0.2", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/dependency-injection.git", | ||||
|                 "reference": "bd25ef7c937b9da12510bdc4f1c66728f19620e3" | ||||
|                 "reference": "e915c6684b8e3ae90a4441f6823ebbb40edf0b92" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/bd25ef7c937b9da12510bdc4f1c66728f19620e3", | ||||
|                 "reference": "bd25ef7c937b9da12510bdc4f1c66728f19620e3", | ||||
|                 "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e915c6684b8e3ae90a4441f6823ebbb40edf0b92", | ||||
|                 "reference": "e915c6684b8e3ae90a4441f6823ebbb40edf0b92", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -530,7 +530,7 @@ | ||||
|             "description": "Allows you to standardize and centralize the way objects are constructed in your application", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/dependency-injection/tree/v7.0.2" | ||||
|                 "source": "https://github.com/symfony/dependency-injection/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -546,7 +546,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-12-28T19:18:20+00:00" | ||||
|             "time": "2024-01-30T08:34:29+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/deprecation-contracts", | ||||
| @@ -617,16 +617,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/filesystem", | ||||
|             "version": "v7.0.0", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/filesystem.git", | ||||
|                 "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7" | ||||
|                 "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/filesystem/zipball/7da8ea2362a283771478c5f7729cfcb43a76b8b7", | ||||
|                 "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7", | ||||
|                 "url": "https://api.github.com/repos/symfony/filesystem/zipball/2890e3a825bc0c0558526c04499c13f83e1b6b12", | ||||
|                 "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -660,7 +660,7 @@ | ||||
|             "description": "Provides basic utilities for the filesystem", | ||||
|             "homepage": "https://symfony.com", | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/filesystem/tree/v7.0.0" | ||||
|                 "source": "https://github.com/symfony/filesystem/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -676,7 +676,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-07-27T06:33:22+00:00" | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/polyfill-ctype", | ||||
| @@ -927,16 +927,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "symfony/var-exporter", | ||||
|             "version": "v7.0.2", | ||||
|             "version": "v7.0.3", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/symfony/var-exporter.git", | ||||
|                 "reference": "345c62fefe92243c3a06fc0cc65f2ec1a47e0764" | ||||
|                 "reference": "1fb79308cb5fc2b44bff6e8af10a5af6812e05b8" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/symfony/var-exporter/zipball/345c62fefe92243c3a06fc0cc65f2ec1a47e0764", | ||||
|                 "reference": "345c62fefe92243c3a06fc0cc65f2ec1a47e0764", | ||||
|                 "url": "https://api.github.com/repos/symfony/var-exporter/zipball/1fb79308cb5fc2b44bff6e8af10a5af6812e05b8", | ||||
|                 "reference": "1fb79308cb5fc2b44bff6e8af10a5af6812e05b8", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -981,7 +981,7 @@ | ||||
|                 "serialize" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "source": "https://github.com/symfony/var-exporter/tree/v7.0.2" | ||||
|                 "source": "https://github.com/symfony/var-exporter/tree/v7.0.3" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -997,7 +997,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2023-12-27T08:42:13+00:00" | ||||
|             "time": "2024-01-23T15:02:46+00:00" | ||||
|         } | ||||
|     ], | ||||
|     "aliases": [], | ||||
|   | ||||
							
								
								
									
										15
									
								
								.env.example
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								.env.example
									
									
									
									
									
								
							| @@ -78,7 +78,7 @@ PAPERTRAIL_HOST= | ||||
| PAPERTRAIL_PORT= | ||||
|  | ||||
| # Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III | ||||
| # For other database types, please see the FAQ: https://docs.firefly-iii.org/firefly-iii/faq/self-hosted/#i-want-to-use-sqlite | ||||
| # For other database types, please see the FAQ: https://docs.firefly-iii.org/references/faq/install/#i-want-to-use-sqlite | ||||
| # If you use Docker or similar, you can set these variables from a file by appending them with _FILE | ||||
| # Use "pgsql" for PostgreSQL | ||||
| # Use "mysql" for MySQL and MariaDB. | ||||
| @@ -122,7 +122,7 @@ SESSION_DRIVER=file | ||||
| # If you use Docker or similar, you can set REDIS_HOST_FILE, REDIS_PASSWORD_FILE or | ||||
| # REDIS_PORT_FILE to set the value from a file instead of from an environment variable | ||||
|  | ||||
| # can be tcp, unix or http | ||||
| # can be tcp or unix. http is not supported | ||||
| REDIS_SCHEME=tcp | ||||
|  | ||||
| # use only when using 'unix' for REDIS_SCHEME. Leave empty otherwise. | ||||
| @@ -150,7 +150,7 @@ COOKIE_SECURE=false | ||||
| COOKIE_SAMESITE=lax | ||||
|  | ||||
| # If you want Firefly III to email you, update these settings | ||||
| # For instructions, see: https://docs.firefly-iii.org/firefly-iii/advanced-installation/email/#email | ||||
| # For instructions, see: https://docs.firefly-iii.org/how-to/firefly-iii/advanced/notifications/#email | ||||
| # If you use Docker or similar, you can set these variables from a file by appending them with _FILE | ||||
| MAIL_MAILER=log | ||||
| MAIL_HOST=null | ||||
| @@ -214,7 +214,7 @@ VALID_URL_PROTOCOLS= | ||||
| # - 'web' (default, uses built in DB) | ||||
| # - 'remote_user_guard' for Authelia etc | ||||
| # Read more about these settings in the documentation. | ||||
| # https://docs.firefly-iii.org/firefly-iii/advanced-installation/authentication | ||||
| # https://docs.firefly-iii.org/how-to/firefly-iii/advanced/authentication/ | ||||
| # | ||||
| # LDAP is no longer supported :( | ||||
| # | ||||
| @@ -269,7 +269,7 @@ ALLOW_WEBHOOKS=false | ||||
| # 1. Set this token to any 32-character value (this is important!). | ||||
| # 2. Use this token in the cron URL instead of a user's command line token that you can find in /profile | ||||
| # | ||||
| # For more info: https://docs.firefly-iii.org/firefly-iii/advanced-installation/cron/ | ||||
| # For more info: https://docs.firefly-iii.org/how-to/firefly-iii/advanced/cron/ | ||||
| # | ||||
| # You can set this variable from a file by appending it with _FILE | ||||
| # | ||||
| @@ -324,6 +324,11 @@ PUSHER_SECRET= | ||||
| PUSHER_ID= | ||||
| DEMO_USERNAME= | ||||
| DEMO_PASSWORD= | ||||
|  | ||||
| # | ||||
| # The v2 layout is very experimental. If it breaks you get to keep both parts. | ||||
| # Be wary of data loss. | ||||
| # | ||||
| FIREFLY_III_LAYOUT=v1 | ||||
|  | ||||
| # | ||||
|   | ||||
							
								
								
									
										16
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,11 +1,16 @@ | ||||
| version: 2 | ||||
| updates: | ||||
|  | ||||
|   # Check for updates to GitHub Actions every week | ||||
|   - package-ecosystem: "github-actions" | ||||
|     directory: "/" | ||||
|     schedule: | ||||
|       interval: "weekly" | ||||
|  | ||||
|   # composer updates | ||||
|   - package-ecosystem: "composer" | ||||
|     directory: "/" # Location of package manifests | ||||
|     target-branch: develop | ||||
|     labels: [ "bug" ] | ||||
|     versioning-strategy: increase | ||||
|     schedule: | ||||
|       interval: "weekly" | ||||
| @@ -14,15 +19,6 @@ updates: | ||||
|   - package-ecosystem: "npm" | ||||
|     directory: "/" | ||||
|     target-branch: develop | ||||
|     labels: [ "bug" ] | ||||
|     versioning-strategy: increase | ||||
|     schedule: | ||||
|       interval: "weekly" | ||||
|  | ||||
|   - package-ecosystem: "github-actions" | ||||
|     directory: "/" | ||||
|     target-branch: develop | ||||
|     labels: [ "bug" ] | ||||
|     versioning-strategy: increase | ||||
|     schedule: | ||||
|       interval: "weekly" | ||||
|   | ||||
							
								
								
									
										82
									
								
								.github/label-actions.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										82
									
								
								.github/label-actions.yml
									
									
									
									
										vendored
									
									
								
							| @@ -5,7 +5,9 @@ feature: | ||||
|   issues: | ||||
|     # Post a comment, `{issue-author}` is an optional placeholder | ||||
|     comment: | | ||||
|       Hi there! This is an automatic reply. `Share and enjoy` | ||||
|       Hi there!  | ||||
|  | ||||
|       This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|       This issue has been marked as a feature request. The requested (new) feature will become a part of Firefly III or the data importer in due course. | ||||
|  | ||||
| @@ -13,11 +15,29 @@ feature: | ||||
|  | ||||
|       Thank you for your contributions. | ||||
|  | ||||
| epic: | ||||
|   issues: | ||||
|     # Post a comment, `{issue-author}` is an optional placeholder | ||||
|     comment: | | ||||
|       Hi there!  | ||||
|  | ||||
|       This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|       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.  | ||||
|  | ||||
|       If you are merely interested in this epic's progress, you can subscribe to this issue to get updates. | ||||
|  | ||||
|       Thank you for your contributions. | ||||
|  | ||||
| enhancement: | ||||
|   issues: | ||||
|     # Post a comment, `{issue-author}` is an optional placeholder | ||||
|     comment: | | ||||
|       Hi there! This is an automatic reply. `Share and enjoy` | ||||
|       Hi there!  | ||||
|  | ||||
|       This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|       This issue has been marked as an enhancement. The requested enhancement to an existing feature will become a part of Firefly III or the data importer in due course. | ||||
|  | ||||
| @@ -25,13 +45,67 @@ enhancement: | ||||
|  | ||||
|       Thank you for your contributions. | ||||
|  | ||||
| # The `solved` label is added to discussions | ||||
| triage: | ||||
|   issues: | ||||
|     # Post a comment, `{issue-author}` is an optional placeholder | ||||
|     comment: | | ||||
|       Hi there! This is an automatic reply. `Share and enjoy` | ||||
|       Hi there!  | ||||
|  | ||||
|       This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|       This issue has been marked as being in triage. The root cause is not known yet, or the issue needs more investigation. You can help by sharing debug information (from `/debug`) if you also have this issue or when you haven't already done so. | ||||
|  | ||||
|       Thank you for your contributions. | ||||
|  | ||||
| needs-moar-debug: | ||||
|   issues: | ||||
|     comment: | | ||||
|       Hi there!  | ||||
|  | ||||
|       This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|       To learn more about this issue, please make sure you share at least: | ||||
|  | ||||
|       1. The table you can find on the `/debug` page | ||||
|       2. Firefly III version | ||||
|       2. Docker, self-hosted, or hosted by a third party? | ||||
|       3. Operating system and browser | ||||
|  | ||||
|       Thank you for your contributions. | ||||
|     unlabel: needs-moar-debug | ||||
|  | ||||
|  | ||||
| needs-moar-logs: | ||||
|   issues: | ||||
|     comment: | | ||||
|       Hi there!  | ||||
|  | ||||
|       This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|       To learn more about this issue, please share the relevant log files from your Firefly III or data importer installation. | ||||
|  | ||||
|       The relevant instructions can be found in the documentation: [How to debug Firefly III?](https://docs.firefly-iii.org/how-to/general/debug/) Once debug mode is activated per these instructions, you can repeat your action and find the logs, depending on your method of installation. All is explained on the page. | ||||
|  | ||||
|       Please share the relevant log lines in your issue, either inline or as an attachment. If you feel the logs contain sensitive information, you may also send them to [james@firefly-iii.org](mailto:james@firefly-iii.org). Without these logs, it may not be possible to properly investigate this issue. | ||||
|  | ||||
|       Thank you for your contributions. | ||||
|     unlabel: needs-moar-logs | ||||
|  | ||||
| v2-layout-issue: | ||||
|   issues: | ||||
|     comment: | | ||||
|       Hi there!  | ||||
|  | ||||
|       This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|       It seems your issue is about the new v2-layout that is currently in development for Firefly III. | ||||
|  | ||||
|       These issues are collected in [a GitHub discussion](https://github.com/firefly-iii/firefly-iii/issues/8361). | ||||
|  | ||||
|       Please note that the v2 layout is still very much in development. | ||||
|  | ||||
|       Thank you for your contributions. | ||||
|     close: true | ||||
|     close-reason: completed | ||||
|     lock: false | ||||
|     unlabel: v2-layout-issue | ||||
|   | ||||
							
								
								
									
										14
									
								
								.github/workflows/cleanup.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/cleanup.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,6 +2,9 @@ | ||||
|  | ||||
| name: "Chore - Prune old builds" | ||||
|  | ||||
| permissions: | ||||
|   actions: write | ||||
|  | ||||
| on: | ||||
|   schedule: | ||||
|     - cron: '0 0 * * *' | ||||
| @@ -12,9 +15,9 @@ jobs: | ||||
|     timeout-minutes: 10 | ||||
|     steps: | ||||
|       - name: Prune cancelled/skipped runs | ||||
|         uses: actions/github-script@v6 | ||||
|         uses: actions/github-script@v7 | ||||
|         with: | ||||
|           github-token: ${{ secrets.GH_ACTIONS_PERSONAL_ACCESS_TOKEN }} | ||||
|           github-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           script: | | ||||
|             const cancelled = await github.rest.actions.listWorkflowRunsForRepo({ | ||||
|               owner: context.repo.owner, | ||||
| @@ -42,9 +45,9 @@ jobs: | ||||
|             } | ||||
|  | ||||
|       - name: Prune runs older than 3 days | ||||
|         uses: actions/github-script@v6 | ||||
|         uses: actions/github-script@v7 | ||||
|         with: | ||||
|           github-token: ${{ secrets.GH_ACTIONS_PERSONAL_ACCESS_TOKEN }} | ||||
|           github-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           script: | | ||||
|             const days_to_expiration = 3; | ||||
|             const ms_in_day = 86400000; | ||||
| @@ -56,10 +59,13 @@ jobs: | ||||
|  | ||||
|             const workflows = [ | ||||
|               'cleanup.yml', | ||||
|               'close-duplicates.yml', | ||||
|               'closed-issues.yml', | ||||
|               'debug-info-actions.yml', | ||||
|               'depsreview.yml', | ||||
|               'label-actions.yml', | ||||
|               'lock.yml', | ||||
|               'release.yml', | ||||
|               'sonarcloud.yml', | ||||
|               'stale.yml' | ||||
|             ] | ||||
|   | ||||
							
								
								
									
										39
									
								
								.github/workflows/close-duplicates.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								.github/workflows/close-duplicates.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| name: "Issues - Command to close duplicate issues" | ||||
|  | ||||
| # the workflow to execute on is comments that are newly created | ||||
| on: | ||||
|   issue_comment: | ||||
|     types: [created] | ||||
|  | ||||
| permissions: | ||||
|   issues: write | ||||
|   checks: read | ||||
|  | ||||
| jobs: | ||||
|   close_duplicates: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: github/command@v1.1.0 | ||||
|         id: command | ||||
|         with: | ||||
|           allowed_contexts: "issue" | ||||
|           command: ".duplicate" | ||||
|       - name: reply | ||||
|         if: ${{ steps.command.outputs.continue == 'true' }} | ||||
|         run: | | ||||
|  | ||||
|           ISSUE_TITLE=$(gh issue view ${{ steps.command.outputs.params }} --json title --jq '.title') | ||||
|  | ||||
|           gh issue comment "$NUMBER" --body "Hi there! | ||||
|  | ||||
|           This is an automatic reply. \`Share and enjoy\`. | ||||
|  | ||||
|           Your issue is probably a duplicate of issue <span>#</span>${{ steps.command.outputs.params }}: [$ISSUE_TITLE](https://github.com/firefly-iii/firefly-iii/issues/${{ steps.command.outputs.params }}). Please refer to issue #${{ steps.command.outputs.params }} for support. | ||||
|  | ||||
|           You can close this issue now. If you believe this is not in fact a duplicate, please reply and let us know. Otherwise, this issue will be automatically closed in a few days time. | ||||
|  | ||||
|           Thank you for your contributions." | ||||
|         env: | ||||
|           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|           GH_REPO: ${{ github.repository }} | ||||
|           NUMBER: ${{ github.event.issue.number }} | ||||
							
								
								
									
										3
									
								
								.github/workflows/closed-issues.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/closed-issues.yml
									
									
									
									
										vendored
									
									
								
							| @@ -23,6 +23,3 @@ jobs: | ||||
|  | ||||
|             Thank you for your contributions. | ||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|       - uses: OSDKDev/lock-issues@v1.1 | ||||
|         with: | ||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|   | ||||
							
								
								
									
										32
									
								
								.github/workflows/debug-info-actions.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/debug-info-actions.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| name: 'Issues - Respond to hidden commands' | ||||
|  | ||||
| # the workflow to execute on is comments that are newly created | ||||
| on: | ||||
|   issues: | ||||
|     types: [opened, edited] | ||||
|   issue_comment: | ||||
|     types: [created] | ||||
|  | ||||
| # permissions needed for reacting to IssueOps commands on issues and PRs | ||||
| permissions: | ||||
|   contents: read | ||||
|   pull-requests: write | ||||
|   issues: write | ||||
|   checks: read | ||||
|  | ||||
| jobs: | ||||
|   respond: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - run: | | ||||
|           ISSUE_BODY=$(gh issue view $NUMBER --json body) | ||||
|           if [[ $ISSUE_BODY == *".eOxNZAmyGz6CXMyf"* ]]; then | ||||
|             gh issue comment "$NUMBER" --body "$V2_ISSUE_REPLY_BODY" | ||||
|             gh issue close "$NUMBER" --reason completed | ||||
|           fi | ||||
|         env: | ||||
|           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|           GH_REPO: ${{ github.repository }} | ||||
|           NUMBER: ${{ github.event.issue.number }} | ||||
|           V2_ISSUE_REPLY_BODY: ${{ secrets.V2_ISSUE_REPLY_BODY }} | ||||
|           LABELS: v2-layout-issue | ||||
							
								
								
									
										4
									
								
								.github/workflows/depsreview.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/depsreview.yml
									
									
									
									
										vendored
									
									
								
							| @@ -9,8 +9,8 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: 'Checkout repository' | ||||
|         uses: actions/checkout@v3 | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: 'Dependency review' | ||||
|         uses: actions/dependency-review-action@v3 | ||||
|         uses: actions/dependency-review-action@v4 | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/label-actions.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/label-actions.yml
									
									
									
									
										vendored
									
									
								
							| @@ -18,4 +18,4 @@ jobs: | ||||
|   action: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: dessant/label-actions@v3 | ||||
|       - uses: dessant/label-actions@v4 | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							| @@ -15,5 +15,5 @@ jobs: | ||||
|       - uses: JC5/lock-threads@main | ||||
|         with: | ||||
|           github-token: ${{ github.token }} | ||||
|           issue-inactive-days: 90 | ||||
|           pr-inactive-days: 90 | ||||
|           issue-inactive-days: 7 | ||||
|           pr-inactive-days: 7 | ||||
|   | ||||
							
								
								
									
										173
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| name: 'Code - Create new release' | ||||
|  | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|     inputs: | ||||
|       version: | ||||
|         description: 'Version to release' | ||||
|         required: true | ||||
|         default: 'develop' | ||||
|   schedule: | ||||
|     - cron:  '15 0 * * MON,THU' | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - name: Switch branch | ||||
|         run: | | ||||
|           if [[ "develop" == "$version" ]]; then | ||||
|             git checkout --track origin/develop | ||||
|             git pull | ||||
|           else | ||||
|             git config user.name github-actions | ||||
|             git config user.email 41898282+github-actions[bot]@users.noreply.github.com | ||||
|             git checkout --track origin/develop | ||||
|             git pull | ||||
|             git checkout main | ||||
|             git merge develop | ||||
|           fi | ||||
|         env: | ||||
|           version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }} | ||||
|       - name: Setup PHP | ||||
|         uses: shivammathur/setup-php@v2 | ||||
|         with: | ||||
|           php-version: '8.3' | ||||
|           extensions: mbstring, intl, zip, bcmath | ||||
|       - name: crowdin action | ||||
|         uses: crowdin/github-action@v1 | ||||
|         with: | ||||
|           upload_sources: true | ||||
|           download_translations: true | ||||
|           push_translations: false | ||||
|           push_sources: false | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ github.token }} | ||||
|           CROWDIN_PROJECT_NR: ${{ secrets.CROWDIN_PROJECT_NR }} | ||||
|           CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }} | ||||
|       - name: Cleanup translations | ||||
|         id: cleanup-transactions | ||||
|         uses: JC5/firefly-iii-dev@v32 | ||||
|         with: | ||||
|           action: 'ff3:crowdin-warning' | ||||
|           output: '' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: '' | ||||
|       - name: Cleanup changelog | ||||
|         id: cleanup-changelog | ||||
|         uses: JC5/firefly-iii-dev@v32 | ||||
|         with: | ||||
|           action: 'ff3:changelog' | ||||
|           output: '' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: ${{ secrets.CHANGELOG_TOKEN }} | ||||
|       - name: Extract changelog | ||||
|         id: extract-changelog | ||||
|         uses: JC5/firefly-iii-dev@v32 | ||||
|         with: | ||||
|           action: 'ff3:extract-changelog' | ||||
|           output: 'output' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: "" | ||||
|       - name: Replace version | ||||
|         id: replace-version | ||||
|         uses: JC5/firefly-iii-dev@v32 | ||||
|         with: | ||||
|           action: 'ff3:version' | ||||
|           output: '' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: "" | ||||
|           FF_III_VERSION: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }} | ||||
|       - name: Generate JSON v1 | ||||
|         id: json-v1 | ||||
|         uses: JC5/firefly-iii-dev@v32 | ||||
|         with: | ||||
|           action: 'ff3:json-translations v1' | ||||
|           output: '' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: '' | ||||
|       - name: Generate JSON v2 | ||||
|         id: json-v2 | ||||
|         uses: JC5/firefly-iii-dev@v32 | ||||
|         with: | ||||
|           action: 'ff3:json-translations v2' | ||||
|           output: '' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: '' | ||||
|       - name: Code cleanup | ||||
|         id: code-cleanup | ||||
|         uses: JC5/firefly-iii-dev@v32 | ||||
|         with: | ||||
|           action: 'ff3:code' | ||||
|           output: '' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: '' | ||||
|       - name: Build new JS | ||||
|         run: | | ||||
|           npm upgrade | ||||
|           npm run build | ||||
|       - name: Build old JS | ||||
|         id: old-js | ||||
|         uses: JC5/firefly-iii-dev@v32 | ||||
|         with: | ||||
|           action: 'ff3:old-js' | ||||
|           output: '' | ||||
|         env: | ||||
|           FIREFLY_III_ROOT: /github/workspace | ||||
|           GH_TOKEN: '' | ||||
|       - name: Run CI | ||||
|         run: | | ||||
|           rm -rf vendor composer.lock | ||||
|           composer validate --strict | ||||
|           composer update --no-dev --no-scripts --no-plugins -q | ||||
|           sudo chown -R runner:docker resources/lang | ||||
|           .ci/phpcs.sh | ||||
|       - name: Release | ||||
|         run: | | ||||
|           sudo timedatectl set-timezone Europe/Amsterdam | ||||
|           git config user.name github-actions | ||||
|           git config user.email 41898282+github-actions[bot]@users.noreply.github.com | ||||
|           git config advice.addIgnoredFile false | ||||
|  | ||||
|           if [[ "develop" == "$version" ]]; then | ||||
|             [[ -z $(git status --untracked-files=normal --porcelain) ]] && echo "this branch is clean, no need to push..." && exit 0; | ||||
|           fi | ||||
|  | ||||
|           git add -A | ||||
|           if test -f "output.txt"; then | ||||
|             git reset output.txt | ||||
|           fi | ||||
|           git commit -m "Auto commit for release '$version' on $(date +'%Y-%m-%d')" || true | ||||
|           git push | ||||
|  | ||||
|           if [[ "develop" == "$version" ]]; then | ||||
|             echo "Create nightly release." | ||||
|             git tag -a $version-$(date +'%Y%m%d') -m "Nightly development release '$version' on $(date +'%Y-%m-%d')" | ||||
|             git push origin $version-$(date +'%Y%m%d') | ||||
|             gh release create $version-$(date +'%Y%m%d') -p --verify-tag \ | ||||
|               -t "Development release for $(date +'%Y-%m-%d')" \ | ||||
|               -n "Bi-weekly development release of Firefly III with the latest fixes, translations and features. This release was created on **$(date +'%Y-%m-%d')** and may contain bugs. Use at your own risk. Docker users can find this release under the \`develop\` tag." | ||||
|           else | ||||
|             echo "Create default release." | ||||
|             git tag -a $version -m "Here be changelog" | ||||
|             git push origin $version | ||||
|             gh release create $version -F output.txt -t "$version" --verify-tag | ||||
|             rm output.txt | ||||
|             git checkout develop | ||||
|             git merge main | ||||
|             git push | ||||
|           fi | ||||
|         env: | ||||
|           GH_TOKEN: ${{ github.token }} | ||||
|           version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }} | ||||
							
								
								
									
										14
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| name: "Issues - Close stale issues" | ||||
| name: "Issues - Mark and close stale issues" | ||||
| on: | ||||
|   schedule: | ||||
|     - cron: "30 1 * * *" | ||||
| @@ -14,21 +14,25 @@ jobs: | ||||
|       pull-requests: write  # for actions/stale to close stale PRs | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/stale@v6 | ||||
|       - uses: actions/stale@v9 | ||||
|         with: | ||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           stale-issue-message: > | ||||
|             Hi there! This is an automatic reply. `Share and enjoy` | ||||
|             Hi there!  | ||||
|              | ||||
|             This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|             This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. | ||||
|  | ||||
|             Thank you for your contributions. | ||||
|           stale-pr-message: > | ||||
|             Hi there! This is an automatic reply. `Share and enjoy` | ||||
|             Hi there!  | ||||
|              | ||||
|             This is an automatic reply. `Share and enjoy` | ||||
|  | ||||
|             This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. | ||||
|  | ||||
|             Thank you for your contributions. | ||||
|           days-before-stale: 14 | ||||
|           days-before-close: 7 | ||||
|           exempt-issue-labels: 'enhancement,feature,bug,announcement,epic' | ||||
|           exempt-issue-labels: 'enhancement,feature,bug,announcement,epic,triage' | ||||
|   | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| /node_modules | ||||
| /storage/*.key | ||||
| /vendor | ||||
| public/hot | ||||
| npm-debug.log | ||||
| yarn-error.log | ||||
| .env | ||||
|   | ||||
| @@ -45,6 +45,7 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface; | ||||
| use FireflyIII\Services\Internal\Destroy\AccountDestroyService; | ||||
| use FireflyIII\Services\Internal\Destroy\JournalDestroyService; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| 
 | ||||
| /** | ||||
|  * Class DestroyController | ||||
| @@ -175,12 +176,14 @@ class DestroyController extends Controller | ||||
|             $count = $account->transactions()->count(); | ||||
|             if (true === $this->unused && 0 === $count) { | ||||
|                 app('log')->info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name)); | ||||
|                 Log::channel('audit')->info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name)); | ||||
|                 $service->destroy($account, null); | ||||
| 
 | ||||
|                 continue; | ||||
|             } | ||||
|             if (false === $this->unused) { | ||||
|                 app('log')->info(sprintf('Deleting account #%d "%s"', $account->id, $account->name)); | ||||
|                 Log::channel('audit')->warning(sprintf('Deleted account #%d "%s"', $account->id, $account->name)); | ||||
|                 $service->destroy($account, null); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -67,7 +67,7 @@ class DestroyController extends Controller | ||||
|     public function destroy(Attachment $attachment): JsonResponse | ||||
|     { | ||||
|         if(true === auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException(); | ||||
|         } | ||||
|   | ||||
| @@ -76,7 +76,7 @@ class ShowController extends Controller | ||||
|     public function download(Attachment $attachment): LaravelResponse | ||||
|     { | ||||
|         if(true === auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException(); | ||||
|         } | ||||
| @@ -124,7 +124,7 @@ class ShowController extends Controller | ||||
|     public function index(): JsonResponse | ||||
|     { | ||||
|         if(true === auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException(); | ||||
|         } | ||||
| @@ -162,7 +162,7 @@ class ShowController extends Controller | ||||
|     public function show(Attachment $attachment): JsonResponse | ||||
|     { | ||||
|         if(true === auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException(); | ||||
|         } | ||||
|   | ||||
| @@ -75,7 +75,7 @@ class StoreController extends Controller | ||||
|     public function store(StoreRequest $request): JsonResponse | ||||
|     { | ||||
|         if(true === auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException(); | ||||
|         } | ||||
| @@ -99,7 +99,7 @@ class StoreController extends Controller | ||||
|     public function upload(Request $request, Attachment $attachment): JsonResponse | ||||
|     { | ||||
|         if(true === auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException(); | ||||
|         } | ||||
|   | ||||
| @@ -70,7 +70,7 @@ class UpdateController extends Controller | ||||
|     public function update(UpdateRequest $request, Attachment $attachment): JsonResponse | ||||
|     { | ||||
|         if(true === auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('Demo user tries to access attachment API in %s', __METHOD__)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException(); | ||||
|         } | ||||
|   | ||||
| @@ -147,7 +147,7 @@ class TriggerController extends Controller | ||||
|             // add a range:
 | ||||
|             $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); | ||||
|         } | ||||
|         if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { | ||||
|         if (array_key_exists('accounts', $parameters) && is_array($parameters['accounts']) && count($parameters['accounts']) > 0) { | ||||
|             $ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -71,7 +71,7 @@ class AttemptController extends Controller | ||||
|             throw new FireflyException('200040: Webhook and webhook message are no match'); | ||||
|         } | ||||
|         if(false === config('firefly.allow_webhooks')) { | ||||
|             Log::channel('audit')->info(sprintf('User lists webhook attempts of webhook #%d and message #%d, but webhooks are DISABLED.', $webhook->id, $message->id)); | ||||
|             Log::channel('audit')->warning(sprintf('User lists webhook attempts of webhook #%d and message #%d, but webhooks are DISABLED.', $webhook->id, $message->id)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException('Webhooks are not enabled.'); | ||||
|         } | ||||
| @@ -115,7 +115,7 @@ class AttemptController extends Controller | ||||
|         } | ||||
| 
 | ||||
|         if(false === config('firefly.allow_webhooks')) { | ||||
|             Log::channel('audit')->info(sprintf('User views single webhook attempt #%d of webhook #%d and message #%d, but webhooks are DISABLED', $attempt->id, $webhook->id, $message->id)); | ||||
|             Log::channel('audit')->warning(sprintf('User views single webhook attempt #%d of webhook #%d and message #%d, but webhooks are DISABLED', $attempt->id, $webhook->id, $message->id)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException('Webhooks are not enabled.'); | ||||
|         } | ||||
|   | ||||
| @@ -63,7 +63,7 @@ class DestroyController extends Controller | ||||
|     public function destroy(Webhook $webhook): JsonResponse | ||||
|     { | ||||
|         if(false === config('firefly.allow_webhooks')) { | ||||
|             Log::channel('audit')->info(sprintf('User tries to destroy webhook #%d. but webhooks are DISABLED.', $webhook->id)); | ||||
|             Log::channel('audit')->warning(sprintf('User tries to destroy webhook #%d. but webhooks are DISABLED.', $webhook->id)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException('Webhooks are not enabled.'); | ||||
|         } | ||||
| @@ -93,7 +93,7 @@ class DestroyController extends Controller | ||||
|         } | ||||
| 
 | ||||
|         if (false === config('firefly.allow_webhooks')) { | ||||
|             Log::channel('audit')->info(sprintf('User tries to destroy webhook #%d, message #%d, attempt #%d, but webhooks are DISABLED.', $webhook->id, $message->id, $attempt->id)); | ||||
|             Log::channel('audit')->warning(sprintf('User tries to destroy webhook #%d, message #%d, attempt #%d, but webhooks are DISABLED.', $webhook->id, $message->id, $attempt->id)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException('Webhooks are not enabled.'); | ||||
|         } | ||||
| @@ -121,7 +121,7 @@ class DestroyController extends Controller | ||||
|         } | ||||
| 
 | ||||
|         if(false === config('firefly.allow_webhooks')) { | ||||
|             Log::channel('audit')->info(sprintf('User tries to destroy webhook #%d, message #%d, but webhooks are DISABLED.', $webhook->id, $message->id)); | ||||
|             Log::channel('audit')->warning(sprintf('User tries to destroy webhook #%d, message #%d, but webhooks are DISABLED.', $webhook->id, $message->id)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException('Webhooks are not enabled.'); | ||||
|         } | ||||
|   | ||||
| @@ -67,7 +67,7 @@ class MessageController extends Controller | ||||
|     public function index(Webhook $webhook): JsonResponse | ||||
|     { | ||||
|         if(false === config('firefly.allow_webhooks')) { | ||||
|             Log::channel('audit')->info(sprintf('User tries to view messages of webhook #%d, but webhooks are DISABLED.', $webhook->id)); | ||||
|             Log::channel('audit')->warning(sprintf('User tries to view messages of webhook #%d, but webhooks are DISABLED.', $webhook->id)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException('Webhooks are not enabled.'); | ||||
|         } | ||||
| @@ -107,7 +107,7 @@ class MessageController extends Controller | ||||
|             throw new FireflyException('200040: Webhook and webhook message are no match'); | ||||
|         } | ||||
|         if(false === config('firefly.allow_webhooks')) { | ||||
|             Log::channel('audit')->info(sprintf('User tries to view message #%d of webhook #%d, but webhooks are DISABLED.', $message->id, $webhook->id)); | ||||
|             Log::channel('audit')->warning(sprintf('User tries to view message #%d of webhook #%d, but webhooks are DISABLED.', $message->id, $webhook->id)); | ||||
| 
 | ||||
|             throw new NotFoundHttpException('Webhooks are not enabled.'); | ||||
|         } | ||||
|   | ||||
| @@ -28,6 +28,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -59,9 +60,7 @@ class MoveTransactionsRequest extends FormRequest | ||||
| 
 | ||||
|     /** | ||||
|      * Configure the validator instance with special rules for after the basic validation rules. | ||||
|      * | ||||
|      * @param validator $validator | ||||
|      *                             TODO this is duplicate | ||||
|      * TODO this is duplicate. | ||||
|      */ | ||||
|     public function withValidator(Validator $validator): void | ||||
|     { | ||||
| @@ -74,6 +73,9 @@ class MoveTransactionsRequest extends FormRequest | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function validateMove(Validator $validator): void | ||||
| @@ -81,12 +83,12 @@ class MoveTransactionsRequest extends FormRequest | ||||
|         $data                = $validator->getData(); | ||||
|         $repository          = app(AccountRepositoryInterface::class); | ||||
|         $repository->setUser(auth()->user()); | ||||
|         $original            = $repository->find((int)$data['original_account']); | ||||
|         $destination         = $repository->find((int)$data['destination_account']); | ||||
|         $original            = $repository->find((int) $data['original_account']); | ||||
|         $destination         = $repository->find((int) $data['destination_account']); | ||||
| 
 | ||||
|         // not the same type:
 | ||||
|         if ($original->accountType->type !== $destination->accountType->type) { | ||||
|             $validator->errors()->add('title', (string)trans('validation.same_account_type')); | ||||
|             $validator->errors()->add('title', (string) trans('validation.same_account_type')); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| @@ -96,7 +98,7 @@ class MoveTransactionsRequest extends FormRequest | ||||
| 
 | ||||
|         // check different scenario's.
 | ||||
|         if (null === $originalCurrency xor null === $destinationCurrency) { | ||||
|             $validator->errors()->add('title', (string)trans('validation.same_account_currency')); | ||||
|             $validator->errors()->add('title', (string) trans('validation.same_account_currency')); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| @@ -105,7 +107,7 @@ class MoveTransactionsRequest extends FormRequest | ||||
|             return; | ||||
|         } | ||||
|         if ($originalCurrency->code !== $destinationCurrency->code) { | ||||
|             $validator->errors()->add('title', (string)trans('validation.same_account_currency')); | ||||
|             $validator->errors()->add('title', (string) trans('validation.same_account_currency')); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,7 @@ use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\Validation\Api\Data\Bulk\ValidatesBulkTransactionQuery; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -72,5 +73,8 @@ class TransactionRequest extends FormRequest | ||||
|                 $this->validateTransactionQuery($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -71,7 +71,7 @@ class ExportRequest extends FormRequest | ||||
|     { | ||||
|         return [ | ||||
|             'type'     => 'in:csv', | ||||
|             'accounts' => 'min:1|max:65536', | ||||
|             'accounts' => 'min:1|max:32768', | ||||
|             'start'    => 'date|before:end', | ||||
|             'end'      => 'date|after:start', | ||||
|         ]; | ||||
|   | ||||
| @@ -101,7 +101,7 @@ class StoreRequest extends FormRequest | ||||
|             'type'                 => 'required|max:1024|min:1|'.sprintf('in:%s', $types), | ||||
|             'iban'                 => ['iban', 'nullable', new UniqueIban(null, $type)], | ||||
|             'bic'                  => 'bic|nullable', | ||||
|             'account_number'       => ['between:1,255', 'nullable', new UniqueAccountNumber(null, $type)], | ||||
|             'account_number'       => ['min:1', 'max:255', 'nullable', new UniqueAccountNumber(null, $type)], | ||||
|             'opening_balance'      => 'numeric|required_with:opening_balance_date|nullable', | ||||
|             'opening_balance_date' => 'date|required_with:opening_balance|nullable', | ||||
|             'virtual_balance'      => 'numeric|nullable', | ||||
| @@ -117,9 +117,9 @@ class StoreRequest extends FormRequest | ||||
|             'liability_amount'     => ['required_with:liability_start_date', new IsValidPositiveAmount()], | ||||
|             'liability_start_date' => 'required_with:liability_amount|date', | ||||
|             'liability_direction'  => 'nullable|required_if:type,liability|required_if:type,liabilities|in:credit,debit', | ||||
|             'interest'             => 'between:0,100|numeric', | ||||
|             'interest'             => 'min:0|max:100|numeric', | ||||
|             'interest_period'      => sprintf('nullable|in:%s', implode(',', config('firefly.interest_periods'))), | ||||
|             'notes'                => 'min:0|max:65536', | ||||
|             'notes'                => 'min:0|max:32768', | ||||
|         ]; | ||||
| 
 | ||||
|         return Location::requestRules($rules); | ||||
|   | ||||
| @@ -91,7 +91,7 @@ class UpdateRequest extends FormRequest | ||||
|             'type'                 => sprintf('in:%s', $types), | ||||
|             'iban'                 => ['iban', 'nullable', new UniqueIban($account, $this->convertString('type'))], | ||||
|             'bic'                  => 'bic|nullable', | ||||
|             'account_number'       => ['between:1,255', 'nullable', new UniqueAccountNumber($account, $this->convertString('type'))], | ||||
|             'account_number'       => ['min:1', 'max:255', 'nullable', new UniqueAccountNumber($account, $this->convertString('type'))], | ||||
|             'opening_balance'      => 'numeric|required_with:opening_balance_date|nullable', | ||||
|             'opening_balance_date' => 'date|required_with:opening_balance|nullable', | ||||
|             'virtual_balance'      => 'numeric|nullable', | ||||
| @@ -105,9 +105,9 @@ class UpdateRequest extends FormRequest | ||||
|             'monthly_payment_date' => 'date|nullable|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull', | ||||
|             'liability_type'       => 'required_if:type,liability|in:loan,debt,mortgage', | ||||
|             'liability_direction'  => 'required_if:type,liability|in:credit,debit', | ||||
|             'interest'             => 'required_if:type,liability|between:0,100|numeric', | ||||
|             'interest'             => 'required_if:type,liability|min:0|max:100|numeric', | ||||
|             'interest_period'      => 'required_if:type,liability|in:daily,monthly,yearly', | ||||
|             'notes'                => 'min:0|max:65536', | ||||
|             'notes'                => 'min:0|max:32768', | ||||
|         ]; | ||||
| 
 | ||||
|         return Location::requestRules($rules); | ||||
|   | ||||
| @@ -66,9 +66,9 @@ class StoreRequest extends FormRequest | ||||
|         $model  = $this->convertString('attachable_type'); | ||||
| 
 | ||||
|         return [ | ||||
|             'filename'        => 'required|between:1,255', | ||||
|             'title'           => 'between:1,255', | ||||
|             'notes'           => 'between:1,65000', | ||||
|             'filename'        => 'required|min:1|max:255', | ||||
|             'title'           => ['min:1', 'max:255'], | ||||
|             'notes'           => 'min:1|max:32768', | ||||
|             'attachable_type' => sprintf('required|in:%s', $models), | ||||
|             'attachable_id'   => ['required', 'numeric', new IsValidAttachmentModel($model)], | ||||
|         ]; | ||||
|   | ||||
| @@ -68,9 +68,9 @@ class UpdateRequest extends FormRequest | ||||
|         $model  = $this->convertString('attachable_type'); | ||||
| 
 | ||||
|         return [ | ||||
|             'filename'        => 'between:1,255', | ||||
|             'title'           => 'between:1,255', | ||||
|             'notes'           => 'between:1,65000', | ||||
|             'filename'        => ['min:1', 'max:255'], | ||||
|             'title'           => ['min:1', 'max:255'], | ||||
|             'notes'           => 'min:1|max:32768', | ||||
|             'attachable_type' => sprintf('in:%s', $models), | ||||
|             'attachable_id'   => ['numeric', new IsValidAttachmentModel($model)], | ||||
|         ]; | ||||
|   | ||||
| @@ -28,6 +28,7 @@ use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -87,5 +88,8 @@ class Request extends FormRequest | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -29,6 +29,7 @@ use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -72,7 +73,7 @@ class StoreRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name'           => 'between:1,255|uniqueObjectForUser:bills,name', | ||||
|             'name'           => 'min:1|max:255|uniqueObjectForUser:bills,name', | ||||
|             'amount_min'     => ['required', new IsValidPositiveAmount()], | ||||
|             'amount_max'     => ['required', new IsValidPositiveAmount()], | ||||
|             'currency_id'    => 'numeric|exists:transaction_currencies,id', | ||||
| @@ -81,9 +82,9 @@ class StoreRequest extends FormRequest | ||||
|             'end_date'       => 'date|after:date', | ||||
|             'extension_date' => 'date|after:date', | ||||
|             'repeat_freq'    => 'in:weekly,monthly,quarterly,half-year,yearly|required', | ||||
|             'skip'           => 'between:0,31', | ||||
|             'skip'           => 'min:0|max:31|numeric', | ||||
|             'active'         => [new IsBoolean()], | ||||
|             'notes'          => 'between:1,65536', | ||||
|             'notes'          => 'min:1|max:32768', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -103,5 +104,8 @@ class StoreRequest extends FormRequest | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,7 @@ use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -75,7 +76,7 @@ class UpdateRequest extends FormRequest | ||||
|         $bill = $this->route()->parameter('bill'); | ||||
| 
 | ||||
|         return [ | ||||
|             'name'           => sprintf('between:1,255|uniqueObjectForUser:bills,name,%d', $bill->id), | ||||
|             'name'           => sprintf('min:1|max:255|uniqueObjectForUser:bills,name,%d', $bill->id), | ||||
|             'amount_min'     => ['nullable', new IsValidPositiveAmount()], | ||||
|             'amount_max'     => ['nullable', new IsValidPositiveAmount()], | ||||
|             'currency_id'    => 'numeric|exists:transaction_currencies,id', | ||||
| @@ -84,9 +85,9 @@ class UpdateRequest extends FormRequest | ||||
|             'end_date'       => 'date|after:date', | ||||
|             'extension_date' => 'date|after:date', | ||||
|             'repeat_freq'    => 'in:weekly,monthly,quarterly,half-year,yearly', | ||||
|             'skip'           => 'between:0,31', | ||||
|             'skip'           => 'min:0|max:31|numeric', | ||||
|             'active'         => [new IsBoolean()], | ||||
|             'notes'          => 'between:1,65536', | ||||
|             'notes'          => 'min:1|max:32768', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -108,5 +109,8 @@ class UpdateRequest extends FormRequest | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -29,6 +29,7 @@ use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -68,11 +69,11 @@ class StoreRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name'               => 'required|between:1,100|uniqueObjectForUser:budgets,name', | ||||
|             'name'               => 'required|min:1|max:255|uniqueObjectForUser:budgets,name', | ||||
|             'active'             => [new IsBoolean()], | ||||
|             'currency_id'        => 'exists:transaction_currencies,id', | ||||
|             'currency_code'      => 'exists:transaction_currencies,code', | ||||
|             'notes'              => 'nullable|between:1,65536', | ||||
|             'notes'              => 'nullable|min:1|max:32768', | ||||
|             // auto budget info
 | ||||
|             'auto_budget_type'   => 'in:reset,rollover,adjusted,none', | ||||
|             'auto_budget_amount' => ['required_if:auto_budget_type,reset', 'required_if:auto_budget_type,rollover', 'required_if:auto_budget_type,adjusted', new IsValidPositiveAmount()], | ||||
| @@ -91,5 +92,8 @@ class StoreRequest extends FormRequest | ||||
|                 $this->validateAutoBudgetAmount($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,7 @@ use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\Validation\AutoBudget\ValidatesAutoBudgetRequest; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -81,9 +82,9 @@ class UpdateRequest extends FormRequest | ||||
|         $budget = $this->route()->parameter('budget'); | ||||
| 
 | ||||
|         return [ | ||||
|             'name'                      => sprintf('between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id), | ||||
|             'name'                      => sprintf('min:1|max:100|uniqueObjectForUser:budgets,name,%d', $budget->id), | ||||
|             'active'                    => [new IsBoolean()], | ||||
|             'notes'                     => 'nullable|between:1,65536', | ||||
|             'notes'                     => 'nullable|min:1|max:32768', | ||||
|             'auto_budget_type'          => 'in:reset,rollover,adjusted,none', | ||||
|             'auto_budget_currency_id'   => 'exists:transaction_currencies,id', | ||||
|             'auto_budget_currency_code' => 'exists:transaction_currencies,code', | ||||
| @@ -103,5 +104,8 @@ class UpdateRequest extends FormRequest | ||||
|                 $this->validateAutoBudgetAmount($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -28,6 +28,7 @@ use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -70,9 +71,7 @@ class UpdateRequest extends FormRequest | ||||
| 
 | ||||
|     /** | ||||
|      * Configure the validator instance with special rules for after the basic validation rules. | ||||
|      * | ||||
|      * @param Validator $validator | ||||
|      *                             TODO duplicate code | ||||
|      * TODO duplicate code. | ||||
|      */ | ||||
|     public function withValidator(Validator $validator): void | ||||
|     { | ||||
| @@ -84,10 +83,13 @@ class UpdateRequest extends FormRequest | ||||
|                     $start = new Carbon($data['start']); | ||||
|                     $end   = new Carbon($data['end']); | ||||
|                     if ($end->isBefore($start)) { | ||||
|                         $validator->errors()->add('end', (string)trans('validation.date_after')); | ||||
|                         $validator->errors()->add('end', (string) trans('validation.date_after')); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -52,7 +52,7 @@ class StoreRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name' => 'required|between:1,100|uniqueObjectForUser:categories,name', | ||||
|             'name' => 'required|min:1|max:100|uniqueObjectForUser:categories,name', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -58,7 +58,7 @@ class UpdateRequest extends FormRequest | ||||
|         $category = $this->route()->parameter('category'); | ||||
| 
 | ||||
|         return [ | ||||
|             'name' => sprintf('between:1,100|uniqueObjectForUser:categories,name,%d', $category->id), | ||||
|             'name' => sprintf('min:1|max:100|uniqueObjectForUser:categories,name,%d', $category->id), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -64,11 +64,11 @@ class StoreRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name'               => 'required|between:1,255|uniquePiggyBankForUser', | ||||
|             'name'               => 'required|min:1|max:255|uniquePiggyBankForUser', | ||||
|             'current_amount'     => ['nullable', new IsValidPositiveAmount()], | ||||
|             'account_id'         => 'required|numeric|belongsToUser:accounts,id', | ||||
|             'object_group_id'    => 'numeric|belongsToUser:object_groups,id', | ||||
|             'object_group_title' => 'between:1,255', | ||||
|             'object_group_title' => ['min:1', 'max:255'], | ||||
|             'target_amount'      => ['required', new IsValidPositiveAmount()], | ||||
|             'start_date'         => 'date|nullable', | ||||
|             'target_date'        => 'date|nullable|after:start_date', | ||||
|   | ||||
| @@ -69,7 +69,7 @@ class UpdateRequest extends FormRequest | ||||
|         $piggyBank = $this->route()->parameter('piggyBank'); | ||||
| 
 | ||||
|         return [ | ||||
|             'name'           => 'between:1,255|uniquePiggyBankForUser:'.$piggyBank->id, | ||||
|             'name'           => 'min:1|max:255|uniquePiggyBankForUser:'.$piggyBank->id, | ||||
|             'current_amount' => ['nullable', new LessThanPiggyTarget(), new IsValidPositiveAmount()], | ||||
|             'target_amount'  => ['nullable', new IsValidPositiveAmount()], | ||||
|             'start_date'     => 'date|nullable', | ||||
|   | ||||
| @@ -33,6 +33,7 @@ use FireflyIII\Validation\CurrencyValidation; | ||||
| use FireflyIII\Validation\RecurrenceValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -79,20 +80,20 @@ class StoreRequest extends FormRequest | ||||
|     { | ||||
|         return [ | ||||
|             'type'                                 => 'required|in:withdrawal,transfer,deposit', | ||||
|             'title'                                => 'required|between:1,255|uniqueObjectForUser:recurrences,title', | ||||
|             'description'                          => 'between:1,65000', | ||||
|             'title'                                => 'required|min:1|max:255|uniqueObjectForUser:recurrences,title', | ||||
|             'description'                          => 'min:1|max:32768', | ||||
|             'first_date'                           => 'required|date', | ||||
|             'apply_rules'                          => [new IsBoolean()], | ||||
|             'active'                               => [new IsBoolean()], | ||||
|             'repeat_until'                         => 'nullable|date', | ||||
|             'nr_of_repetitions'                    => 'nullable|numeric|between:1,31', | ||||
|             'nr_of_repetitions'                    => 'nullable|numeric|min:1|max:31', | ||||
| 
 | ||||
|             'repetitions.*.type'                   => 'required|in:daily,weekly,ndom,monthly,yearly', | ||||
|             'repetitions.*.moment'                 => 'between:0,10', | ||||
|             'repetitions.*.skip'                   => 'nullable|numeric|between:0,31', | ||||
|             'repetitions.*.moment'                 => 'min:0|max:10', | ||||
|             'repetitions.*.skip'                   => 'nullable|numeric|min:0|max:31', | ||||
|             'repetitions.*.weekend'                => 'numeric|min:1|max:4', | ||||
| 
 | ||||
|             'transactions.*.description'           => 'required|between:1,255', | ||||
|             'transactions.*.description'           => 'required|min:1|max:255', | ||||
|             'transactions.*.amount'                => ['required', new IsValidPositiveAmount()], | ||||
|             'transactions.*.foreign_amount'        => ['nullable', new IsValidPositiveAmount()], | ||||
|             'transactions.*.currency_id'           => 'nullable|numeric|exists:transaction_currencies,id', | ||||
| @@ -100,18 +101,18 @@ class StoreRequest extends FormRequest | ||||
|             'transactions.*.foreign_currency_id'   => 'nullable|numeric|exists:transaction_currencies,id', | ||||
|             'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code', | ||||
|             'transactions.*.source_id'             => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.source_name'           => 'between:1,255|nullable', | ||||
|             'transactions.*.source_name'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.destination_id'        => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.destination_name'      => 'between:1,255|nullable', | ||||
|             'transactions.*.destination_name'      => 'min:1|max:255|nullable', | ||||
| 
 | ||||
|             // new and updated fields:
 | ||||
|             'transactions.*.budget_id'             => ['nullable', 'mustExist:budgets,id', new BelongsUser()], | ||||
|             'transactions.*.budget_name'           => ['between:1,255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.budget_name'           => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.category_id'           => ['nullable', 'mustExist:categories,id', new BelongsUser()], | ||||
|             'transactions.*.category_name'         => 'between:1,255|nullable', | ||||
|             'transactions.*.category_name'         => 'min:1|max:255|nullable', | ||||
|             'transactions.*.piggy_bank_id'         => ['nullable', 'numeric', 'mustExist:piggy_banks,id', new BelongsUser()], | ||||
|             'transactions.*.piggy_bank_name'       => ['between:1,255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.tags'                  => 'nullable|between:1,255', | ||||
|             'transactions.*.piggy_bank_name'       => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.tags'                  => 'nullable|min:1|max:255', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -131,6 +132,9 @@ class StoreRequest extends FormRequest | ||||
|                 $this->validateAccountInformation($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -34,6 +34,7 @@ use FireflyIII\Validation\CurrencyValidation; | ||||
| use FireflyIII\Validation\RecurrenceValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -86,20 +87,20 @@ class UpdateRequest extends FormRequest | ||||
|         $recurrence = $this->route()->parameter('recurrence'); | ||||
| 
 | ||||
|         return [ | ||||
|             'title'                                => sprintf('between:1,255|uniqueObjectForUser:recurrences,title,%d', $recurrence->id), | ||||
|             'description'                          => 'between:1,65000', | ||||
|             'title'                                => sprintf('min:1|max:255|uniqueObjectForUser:recurrences,title,%d', $recurrence->id), | ||||
|             'description'                          => 'min:1|max:32768', | ||||
|             'first_date'                           => 'date', | ||||
|             'apply_rules'                          => [new IsBoolean()], | ||||
|             'active'                               => [new IsBoolean()], | ||||
|             'repeat_until'                         => 'nullable|date', | ||||
|             'nr_of_repetitions'                    => 'nullable|numeric|between:1,31', | ||||
|             'nr_of_repetitions'                    => 'nullable|numeric|min:1|max:31', | ||||
| 
 | ||||
|             'repetitions.*.type'                   => 'in:daily,weekly,ndom,monthly,yearly', | ||||
|             'repetitions.*.moment'                 => 'between:0,10', | ||||
|             'repetitions.*.skip'                   => 'nullable|numeric|between:0,31', | ||||
|             'repetitions.*.moment'                 => 'min:0|max:10|numeric', | ||||
|             'repetitions.*.skip'                   => 'nullable|numeric|min:0|max:31', | ||||
|             'repetitions.*.weekend'                => 'nullable|numeric|min:1|max:4', | ||||
| 
 | ||||
|             'transactions.*.description'           => 'between:1,255', | ||||
|             'transactions.*.description'           => ['min:1', 'max:255'], | ||||
|             'transactions.*.amount'                => [new IsValidPositiveAmount()], | ||||
|             'transactions.*.foreign_amount'        => ['nullable', new IsValidPositiveAmount()], | ||||
|             'transactions.*.currency_id'           => 'nullable|numeric|exists:transaction_currencies,id', | ||||
| @@ -107,18 +108,18 @@ class UpdateRequest extends FormRequest | ||||
|             'transactions.*.foreign_currency_id'   => 'nullable|numeric|exists:transaction_currencies,id', | ||||
|             'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code', | ||||
|             'transactions.*.source_id'             => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.source_name'           => 'between:1,255|nullable', | ||||
|             'transactions.*.source_name'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.destination_id'        => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.destination_name'      => 'between:1,255|nullable', | ||||
|             'transactions.*.destination_name'      => 'min:1|max:255|nullable', | ||||
| 
 | ||||
|             // new and updated fields:
 | ||||
|             'transactions.*.budget_id'             => ['nullable', 'mustExist:budgets,id', new BelongsUser()], | ||||
|             'transactions.*.budget_name'           => ['between:1,255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.budget_name'           => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.category_id'           => ['nullable', 'mustExist:categories,id', new BelongsUser()], | ||||
|             'transactions.*.category_name'         => 'between:1,255|nullable', | ||||
|             'transactions.*.category_name'         => 'min:1|max:255|nullable', | ||||
|             'transactions.*.piggy_bank_id'         => ['nullable', 'numeric', 'mustExist:piggy_banks,id', new BelongsUser()], | ||||
|             'transactions.*.piggy_bank_name'       => ['between:1,255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.tags'                  => 'nullable|between:1,255', | ||||
|             'transactions.*.piggy_bank_name'       => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.tags'                  => 'nullable|min:1|max:255', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -141,6 +142,9 @@ class UpdateRequest extends FormRequest | ||||
|                 $this->valUpdateAccountInfo($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -28,6 +28,7 @@ use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\Support\Request\GetRuleConfiguration; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -76,10 +77,10 @@ class StoreRequest extends FormRequest | ||||
|         $contextActions  = implode(',', config('firefly.context-rule-actions')); | ||||
| 
 | ||||
|         return [ | ||||
|             'title'                      => 'required|between:1,100|uniqueObjectForUser:rules,title', | ||||
|             'description'                => 'between:1,5000|nullable', | ||||
|             'title'                      => 'required|min:1|max:100|uniqueObjectForUser:rules,title', | ||||
|             'description'                => 'min:1|max:32768|nullable', | ||||
|             'rule_group_id'              => 'belongsToUser:rule_groups|required_without:rule_group_title', | ||||
|             'rule_group_title'           => 'nullable|between:1,255|required_without:rule_group_id|belongsToUser:rule_groups,title', | ||||
|             'rule_group_title'           => 'nullable|min:1|max:255|required_without:rule_group_id|belongsToUser:rule_groups,title', | ||||
|             'trigger'                    => 'required|in:store-journal,update-journal', | ||||
|             'triggers.*.type'            => 'required|in:'.implode(',', $validTriggers), | ||||
|             'triggers.*.value'           => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024', | ||||
| @@ -108,6 +109,9 @@ class StoreRequest extends FormRequest | ||||
|                 $this->atLeastOneActiveAction($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -29,6 +29,7 @@ use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\Support\Request\GetRuleConfiguration; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -85,10 +86,10 @@ class UpdateRequest extends FormRequest | ||||
|         $contextActions  = implode(',', config('firefly.context-rule-actions')); | ||||
| 
 | ||||
|         return [ | ||||
|             'title'                      => sprintf('between:1,100|uniqueObjectForUser:rules,title,%d', $rule->id), | ||||
|             'description'                => 'between:1,5000|nullable', | ||||
|             'title'                      => sprintf('min:1|max:100|uniqueObjectForUser:rules,title,%d', $rule->id), | ||||
|             'description'                => 'min:1|max:32768|nullable', | ||||
|             'rule_group_id'              => 'belongsToUser:rule_groups', | ||||
|             'rule_group_title'           => 'nullable|between:1,255|belongsToUser:rule_groups,title', | ||||
|             'rule_group_title'           => 'nullable|min:1|max:255|belongsToUser:rule_groups,title', | ||||
|             'trigger'                    => 'in:store-journal,update-journal', | ||||
|             'triggers.*.type'            => 'required|in:'.implode(',', $validTriggers), | ||||
|             'triggers.*.value'           => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024', | ||||
| @@ -101,7 +102,7 @@ class UpdateRequest extends FormRequest | ||||
|             'strict'                     => [new IsBoolean()], | ||||
|             'stop_processing'            => [new IsBoolean()], | ||||
|             'active'                     => [new IsBoolean()], | ||||
|             'order'                      => 'numeric|between:1,1337', | ||||
|             'order'                      => 'numeric|min:1|max:2048', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -118,6 +119,9 @@ class UpdateRequest extends FormRequest | ||||
|                 $this->atLeastOneValidAction($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -64,8 +64,8 @@ class StoreRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'title'       => 'required|between:1,100|uniqueObjectForUser:rule_groups,title', | ||||
|             'description' => 'between:1,5000|nullable', | ||||
|             'title'       => 'required|min:1|max:100|uniqueObjectForUser:rule_groups,title', | ||||
|             'description' => 'min:1|max:32768|nullable', | ||||
|             'active'      => [new IsBoolean()], | ||||
|         ]; | ||||
|     } | ||||
|   | ||||
| @@ -71,6 +71,10 @@ class TriggerRequest extends FormRequest | ||||
| 
 | ||||
|     private function getAccounts(): array | ||||
|     { | ||||
|         if(null === $this->get('accounts')) { | ||||
|             return []; | ||||
|         } | ||||
| 
 | ||||
|         return $this->get('accounts'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -62,8 +62,8 @@ class UpdateRequest extends FormRequest | ||||
|         $ruleGroup = $this->route()->parameter('ruleGroup'); | ||||
| 
 | ||||
|         return [ | ||||
|             'title'       => 'between:1,100|uniqueObjectForUser:rule_groups,title,'.$ruleGroup->id, | ||||
|             'description' => 'between:1,5000|nullable', | ||||
|             'title'       => 'min:1|max:100|uniqueObjectForUser:rule_groups,title,'.$ruleGroup->id, | ||||
|             'description' => 'min:1|max:32768|nullable', | ||||
|             'active'      => [new IsBoolean()], | ||||
|         ]; | ||||
|     } | ||||
|   | ||||
| @@ -60,7 +60,7 @@ class StoreRequest extends FormRequest | ||||
|     { | ||||
|         $rules = [ | ||||
|             'tag'         => 'required|min:1|uniqueObjectForUser:tags,tag|max:1024', | ||||
|             'description' => 'min:1|nullable|max:65536', | ||||
|             'description' => 'min:1|nullable|max:32768', | ||||
|             'date'        => 'date|nullable', | ||||
|         ]; | ||||
| 
 | ||||
|   | ||||
| @@ -66,7 +66,7 @@ class UpdateRequest extends FormRequest | ||||
|         // TODO check if uniqueObjectForUser is obsolete
 | ||||
|         $rules = [ | ||||
|             'tag'         => 'min:1|max:1024|uniqueObjectForUser:tags,tag,'.$tag->id, | ||||
|             'description' => 'min:1|nullable|max:65536', | ||||
|             'description' => 'min:1|nullable|max:32768', | ||||
|             'date'        => 'date|nullable', | ||||
|         ]; | ||||
| 
 | ||||
|   | ||||
| @@ -28,6 +28,7 @@ use FireflyIII\Rules\BelongsUser; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use FireflyIII\Rules\IsDateOrTime; | ||||
| use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Rules\IsValidZeroOrMoreAmount; | ||||
| use FireflyIII\Support\NullArrayObject; | ||||
| use FireflyIII\Support\Request\AppendsLocationData; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| @@ -36,6 +37,7 @@ use FireflyIII\Validation\CurrencyValidation; | ||||
| use FireflyIII\Validation\GroupValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -77,7 +79,7 @@ class StoreRequest extends FormRequest | ||||
| 
 | ||||
|         return [ | ||||
|             // basic fields for group:
 | ||||
|             'group_title'                            => 'between:1,1000|nullable', | ||||
|             'group_title'                            => 'min:1|max:1000|nullable', | ||||
|             'error_if_duplicate_hash'                => [new IsBoolean()], | ||||
|             'apply_rules'                            => [new IsBoolean()], | ||||
| 
 | ||||
| @@ -94,40 +96,40 @@ class StoreRequest extends FormRequest | ||||
| 
 | ||||
|             // amount
 | ||||
|             'transactions.*.amount'                  => ['required', new IsValidPositiveAmount()], | ||||
|             'transactions.*.foreign_amount'          => ['nullable', new IsValidPositiveAmount()], | ||||
|             'transactions.*.foreign_amount'          => ['nullable', new IsValidZeroOrMoreAmount()], | ||||
| 
 | ||||
|             // description
 | ||||
|             'transactions.*.description'             => 'nullable|between:1,1000', | ||||
|             'transactions.*.description'             => 'nullable|min:1|max:1000', | ||||
| 
 | ||||
|             // source of transaction
 | ||||
|             'transactions.*.source_id'               => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.source_name'             => 'between:1,255|nullable', | ||||
|             'transactions.*.source_iban'             => 'between:1,255|nullable|iban', | ||||
|             'transactions.*.source_number'           => 'between:1,255|nullable', | ||||
|             'transactions.*.source_bic'              => 'between:1,255|nullable|bic', | ||||
|             'transactions.*.source_name'             => 'min:1|max:255|nullable', | ||||
|             'transactions.*.source_iban'             => 'min:1|max:255|nullable|iban', | ||||
|             'transactions.*.source_number'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.source_bic'              => 'min:1|max:255|nullable|bic', | ||||
| 
 | ||||
|             // destination of transaction
 | ||||
|             'transactions.*.destination_id'          => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.destination_name'        => 'between:1,255|nullable', | ||||
|             'transactions.*.destination_iban'        => 'between:1,255|nullable|iban', | ||||
|             'transactions.*.destination_number'      => 'between:1,255|nullable', | ||||
|             'transactions.*.destination_bic'         => 'between:1,255|nullable|bic', | ||||
|             'transactions.*.destination_name'        => 'min:1|max:255|nullable', | ||||
|             'transactions.*.destination_iban'        => 'min:1|max:255|nullable|iban', | ||||
|             'transactions.*.destination_number'      => 'min:1|max:255|nullable', | ||||
|             'transactions.*.destination_bic'         => 'min:1|max:255|nullable|bic', | ||||
| 
 | ||||
|             // budget, category, bill and piggy
 | ||||
|             'transactions.*.budget_id'               => ['mustExist:budgets,id', new BelongsUser()], | ||||
|             'transactions.*.budget_name'             => ['between:1,255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.budget_name'             => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.category_id'             => ['mustExist:categories,id', new BelongsUser(), 'nullable'], | ||||
|             'transactions.*.category_name'           => 'between:1,255|nullable', | ||||
|             'transactions.*.category_name'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.bill_id'                 => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()], | ||||
|             'transactions.*.bill_name'               => ['between:1,255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.bill_name'               => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.piggy_bank_id'           => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUser()], | ||||
|             'transactions.*.piggy_bank_name'         => ['between:1,255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.piggy_bank_name'         => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
| 
 | ||||
|             // other interesting fields
 | ||||
|             'transactions.*.reconciled'              => [new IsBoolean()], | ||||
|             'transactions.*.notes'                   => 'min:1|max:50000|nullable', | ||||
|             'transactions.*.tags'                    => 'between:0,255', | ||||
|             'transactions.*.tags.*'                  => 'between:0,255', | ||||
|             'transactions.*.notes'                   => 'min:1|max:32768|nullable', | ||||
|             'transactions.*.tags'                    => 'min:0|max:255', | ||||
|             'transactions.*.tags.*'                  => 'min:0|max:255', | ||||
| 
 | ||||
|             // meta info fields
 | ||||
|             'transactions.*.internal_reference'      => 'min:1|max:255|nullable', | ||||
| @@ -190,6 +192,9 @@ class StoreRequest extends FormRequest | ||||
|                 $this->validateGroupDescription($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -30,11 +30,13 @@ use FireflyIII\Rules\BelongsUser; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use FireflyIII\Rules\IsDateOrTime; | ||||
| use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Rules\IsValidZeroOrMoreAmount; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\Validation\GroupValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -98,7 +100,7 @@ class UpdateRequest extends FormRequest | ||||
| 
 | ||||
|         return [ | ||||
|             // basic fields for group:
 | ||||
|             'group_title'                            => 'between:1,1000|nullable', | ||||
|             'group_title'                            => 'min:1|max:1000|nullable', | ||||
|             'apply_rules'                            => [new IsBoolean()], | ||||
| 
 | ||||
|             // transaction rules (in array for splits):
 | ||||
| @@ -116,33 +118,33 @@ class UpdateRequest extends FormRequest | ||||
|             'transactions.*.foreign_currency_code'   => 'nullable|min:3|max:51|exists:transaction_currencies,code', | ||||
| 
 | ||||
|             // amount
 | ||||
|             'transactions.*.amount'                  => ['nullable', new IsValidPositiveAmount()], | ||||
|             'transactions.*.foreign_amount'          => ['nullable', new IsValidPositiveAmount()], | ||||
|             'transactions.*.amount'                  => [new IsValidPositiveAmount()], | ||||
|             'transactions.*.foreign_amount'          => ['nullable', new IsValidZeroOrMoreAmount()], | ||||
| 
 | ||||
|             // description
 | ||||
|             'transactions.*.description'             => 'nullable|between:1,1000', | ||||
|             'transactions.*.description'             => 'nullable|min:1|max:1000', | ||||
| 
 | ||||
|             // source of transaction
 | ||||
|             'transactions.*.source_id'               => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.source_name'             => 'between:1,255|nullable', | ||||
|             'transactions.*.source_name'             => 'min:1|max:255|nullable', | ||||
| 
 | ||||
|             // destination of transaction
 | ||||
|             'transactions.*.destination_id'          => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.destination_name'        => 'between:1,255|nullable', | ||||
|             'transactions.*.destination_name'        => 'min:1|max:255|nullable', | ||||
| 
 | ||||
|             // budget, category, bill and piggy
 | ||||
|             'transactions.*.budget_id'               => ['mustExist:budgets,id', new BelongsUser(), 'nullable'], | ||||
|             'transactions.*.budget_name'             => ['between:1,255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.budget_name'             => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.category_id'             => ['mustExist:categories,id', new BelongsUser(), 'nullable'], | ||||
|             'transactions.*.category_name'           => 'between:1,255|nullable', | ||||
|             'transactions.*.category_name'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.bill_id'                 => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()], | ||||
|             'transactions.*.bill_name'               => ['between:1,255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.bill_name'               => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
| 
 | ||||
|             // other interesting fields
 | ||||
|             'transactions.*.reconciled'              => [new IsBoolean()], | ||||
|             'transactions.*.notes'                   => 'min:1|max:50000|nullable', | ||||
|             'transactions.*.tags'                    => 'between:0,255|nullable', | ||||
|             'transactions.*.tags.*'                  => 'between:0,255', | ||||
|             'transactions.*.notes'                   => 'min:1|max:32768|nullable', | ||||
|             'transactions.*.tags'                    => 'min:0|max:255|nullable', | ||||
|             'transactions.*.tags.*'                  => 'min:0|max:255', | ||||
| 
 | ||||
|             // meta info fields
 | ||||
|             'transactions.*.internal_reference'      => 'min:1|max:255|nullable', | ||||
| @@ -204,6 +206,9 @@ class UpdateRequest extends FormRequest | ||||
|                 $this->validateAccountInformationUpdate($validator, $transactionGroup); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
| @@ -66,10 +66,10 @@ class StoreRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name'           => 'required|between:1,255|unique:transaction_currencies,name', | ||||
|             'code'           => 'required|between:3,51|unique:transaction_currencies,code', | ||||
|             'symbol'         => 'required|between:1,51|unique:transaction_currencies,symbol', | ||||
|             'decimal_places' => 'between:0,20|numeric|min:0|max:12', | ||||
|             'name'           => 'required|min:1|max:255|unique:transaction_currencies,name', | ||||
|             'code'           => 'required|min:3|max:32|unique:transaction_currencies,code', | ||||
|             'symbol'         => 'required|min:1|max:32|unique:transaction_currencies,symbol', | ||||
|             'decimal_places' => 'numeric|min:0|max:12', | ||||
|             'enabled'        => [new IsBoolean()], | ||||
|             'default'        => [new IsBoolean()], | ||||
|         ]; | ||||
|   | ||||
| @@ -42,7 +42,7 @@ class UpdateRequest extends FormRequest | ||||
|      */ | ||||
|     public function getAll(): array | ||||
|     { | ||||
|         // return nothing that isn't explicitely in the array:
 | ||||
|         // return nothing that isn't explicitly in the array:
 | ||||
|         $fields = [ | ||||
|             'name'           => ['name', 'convertString'], | ||||
|             'code'           => ['code', 'convertString'], | ||||
| @@ -64,10 +64,10 @@ class UpdateRequest extends FormRequest | ||||
|         $currency = $this->route()->parameter('currency_code'); | ||||
| 
 | ||||
|         return [ | ||||
|             'name'           => sprintf('between:1,255|unique:transaction_currencies,name,%d', $currency->id), | ||||
|             'code'           => sprintf('between:3,51|unique:transaction_currencies,code,%d', $currency->id), | ||||
|             'symbol'         => sprintf('between:1,51|unique:transaction_currencies,symbol,%d', $currency->id), | ||||
|             'decimal_places' => 'between:0,20|numeric|min:0|max:12', | ||||
|             'name'           => sprintf('min:1|max:255|unique:transaction_currencies,name,%d', $currency->id), | ||||
|             'code'           => sprintf('min:3|max:32|unique:transaction_currencies,code,%d', $currency->id), | ||||
|             'symbol'         => sprintf('min:1|max:32|unique:transaction_currencies,symbol,%d', $currency->id), | ||||
|             'decimal_places' => 'numeric|min:0|max:12', | ||||
|             'enabled'        => [new IsBoolean()], | ||||
|             'default'        => [new IsBoolean()], | ||||
|         ]; | ||||
|   | ||||
| @@ -29,6 +29,7 @@ use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -63,7 +64,7 @@ class StoreRequest extends FormRequest | ||||
|             'link_type_name' => 'exists:link_types,name|required_without:link_type_id', | ||||
|             'inward_id'      => 'required|belongsToUser:transaction_journals,id|different:outward_id', | ||||
|             'outward_id'     => 'required|belongsToUser:transaction_journals,id|different:inward_id', | ||||
|             'notes'          => 'between:0,65000', | ||||
|             'notes'          => 'min:1|max:32768|nullable', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -77,6 +78,9 @@ class StoreRequest extends FormRequest | ||||
|                 $this->validateExistingLink($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function validateExistingLink(Validator $validator): void | ||||
|   | ||||
| @@ -29,6 +29,7 @@ use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -63,7 +64,7 @@ class UpdateRequest extends FormRequest | ||||
|             'link_type_name' => 'exists:link_types,name', | ||||
|             'inward_id'      => 'belongsToUser:transaction_journals,id|different:outward_id', | ||||
|             'outward_id'     => 'belongsToUser:transaction_journals,id|different:inward_id', | ||||
|             'notes'          => 'between:0,65000', | ||||
|             'notes'          => 'min:1|max:32768|nullable', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -77,6 +78,9 @@ class UpdateRequest extends FormRequest | ||||
|                 $this->validateUpdate($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function validateUpdate(Validator $validator): void | ||||
|   | ||||
| @@ -72,7 +72,7 @@ class CreateRequest extends FormRequest | ||||
|         $validProtocols = config('firefly.valid_url_protocols'); | ||||
| 
 | ||||
|         return [ | ||||
|             'title'    => 'required|between:1,512|uniqueObjectForUser:webhooks,title', | ||||
|             'title'    => 'required|min:1|max:255|uniqueObjectForUser:webhooks,title', | ||||
|             'active'   => [new IsBoolean()], | ||||
|             'trigger'  => sprintf('required|in:%s', $triggers), | ||||
|             'response' => sprintf('required|in:%s', $responses), | ||||
|   | ||||
| @@ -85,7 +85,7 @@ class UpdateRequest extends FormRequest | ||||
|         $webhook        = $this->route()->parameter('webhook'); | ||||
| 
 | ||||
|         return [ | ||||
|             'title'    => sprintf('between:1,512|uniqueObjectForUser:webhooks,title,%d', $webhook->id), | ||||
|             '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), | ||||
|   | ||||
| @@ -65,7 +65,7 @@ class UpdateRequest extends FormRequest | ||||
|             return ['value' => ['required', new IsBoolean()]]; | ||||
|         } | ||||
|         if ('configuration.permission_update_check' === $name) { | ||||
|             return ['value' => 'required|numeric|between:-1,1']; | ||||
|             return ['value' => 'required|numeric|min:-1|max:1']; | ||||
|         } | ||||
|         if ('configuration.last_update_check' === $name) { | ||||
|             return ['value' => 'required|numeric|min:464272080']; | ||||
|   | ||||
| @@ -29,6 +29,7 @@ use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -97,5 +98,8 @@ class UserUpdateRequest extends FormRequest | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -77,11 +77,7 @@ class BudgetController extends Controller | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param DateRequest $request | ||||
|      * | ||||
|      * TODO see autocomplete/accountcontroller | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function dashboard(DateRequest $request): JsonResponse | ||||
|     { | ||||
|   | ||||
							
								
								
									
										43
									
								
								app/Api/V2/Controllers/Model/Transaction/ShowController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/Api/V2/Controllers/Model/Transaction/ShowController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| <?php | ||||
| /* | ||||
|  * ShowController.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Controllers\Model\Transaction; | ||||
| 
 | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Models\TransactionGroup; | ||||
| use FireflyIII\Transformers\V2\TransactionGroupTransformer; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| 
 | ||||
| class ShowController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * TODO this endpoint is not yet reachable. | ||||
|      */ | ||||
|     public function show(TransactionGroup $transactionGroup): JsonResponse | ||||
|     { | ||||
|         $transformer = new TransactionGroupTransformer(); | ||||
|         $transformer->setParameters($this->parameters); | ||||
| 
 | ||||
|         return response()->api($this->jsonApiObject('transactions', $transactionGroup, $transformer))->header('Content-Type', self::CONTENT_TYPE); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,93 @@ | ||||
| <?php | ||||
| /* | ||||
|  * UpdateController.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Controllers\Model\Transaction; | ||||
| 
 | ||||
| use FireflyIII\Api\V2\Controllers\Controller; | ||||
| use FireflyIII\Api\V2\Request\Model\Transaction\UpdateRequest; | ||||
| use FireflyIII\Events\UpdatedTransactionGroup; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Models\TransactionGroup; | ||||
| use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; | ||||
| use FireflyIII\Transformers\V2\TransactionGroupTransformer; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| 
 | ||||
| class UpdateController extends Controller | ||||
| { | ||||
|     private TransactionGroupRepositoryInterface $groupRepository; | ||||
| 
 | ||||
|     /** | ||||
|      * TransactionController constructor. | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->groupRepository = app(TransactionGroupRepositoryInterface::class); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This endpoint is documented at: | ||||
|      * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/updateTransaction
 | ||||
|      * | ||||
|      * Update a transaction. | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse | ||||
|     { | ||||
|         app('log')->debug('Now in update routine for transaction group [v2]!'); | ||||
|         $data             = $request->getAll(); | ||||
|         $transactionGroup = $this->groupRepository->update($transactionGroup, $data); | ||||
|         $applyRules       = $data['apply_rules'] ?? true; | ||||
|         $fireWebhooks     = $data['fire_webhooks'] ?? true; | ||||
| 
 | ||||
|         event(new UpdatedTransactionGroup($transactionGroup, $applyRules, $fireWebhooks)); | ||||
|         app('preferences')->mark(); | ||||
| 
 | ||||
|         /** @var User $admin */ | ||||
|         $admin            = auth()->user(); | ||||
| 
 | ||||
|         // use new group collector:
 | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector        = app(GroupCollectorInterface::class); | ||||
|         $collector->setUser($admin)->setTransactionGroup($transactionGroup); | ||||
| 
 | ||||
|         $selectedGroup    = $collector->getGroups()->first(); | ||||
|         if (null === $selectedGroup) { | ||||
|             throw new FireflyException('200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.'); | ||||
|         } | ||||
| 
 | ||||
|         $transformer      = new TransactionGroupTransformer(); | ||||
|         $transformer->setParameters($this->parameters); | ||||
| 
 | ||||
|         return response()->api($this->jsonApiObject('transactions', $selectedGroup, $transformer))->header('Content-Type', self::CONTENT_TYPE); | ||||
|     } | ||||
| } | ||||
| @@ -28,6 +28,7 @@ use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -79,5 +80,8 @@ class BalanceChartRequest extends FormRequest | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -27,6 +27,7 @@ use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -77,5 +78,8 @@ class DashboardChartRequest extends FormRequest | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -28,6 +28,7 @@ use FireflyIII\Models\UserGroup; | ||||
| use FireflyIII\Rules\BelongsUserGroup; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use FireflyIII\Rules\IsDateOrTime; | ||||
| use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Support\NullArrayObject; | ||||
| use FireflyIII\Support\Request\AppendsLocationData; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| @@ -37,6 +38,7 @@ use FireflyIII\Validation\CurrencyValidation; | ||||
| use FireflyIII\Validation\GroupValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
| @@ -74,7 +76,6 @@ class StoreRequest extends FormRequest | ||||
|             'fire_webhooks'           => $this->boolean('fire_webhooks', true), | ||||
|             'transactions'            => $this->getTransactionData(), | ||||
|         ]; | ||||
|         // TODO include location and ability to process it.
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -91,81 +92,84 @@ class StoreRequest extends FormRequest | ||||
| 
 | ||||
|         return [ | ||||
|             // basic fields for group:
 | ||||
|             'group_title'                          => 'between:1,1000|nullable', | ||||
|             'error_if_duplicate_hash'              => [new IsBoolean()], | ||||
|             'apply_rules'                          => [new IsBoolean()], | ||||
|             'group_title'                            => 'min:1|max:1000|nullable', | ||||
|             'error_if_duplicate_hash'                => [new IsBoolean()], | ||||
|             'apply_rules'                            => [new IsBoolean()], | ||||
| 
 | ||||
|             // transaction rules (in array for splits):
 | ||||
|             'transactions.*.type'                  => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation', | ||||
|             'transactions.*.date'                  => ['required', new IsDateOrTime()], | ||||
|             'transactions.*.order'                 => 'numeric|min:0', | ||||
|             'transactions.*.type'                    => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation', | ||||
|             'transactions.*.date'                    => ['required', new IsDateOrTime()], | ||||
|             'transactions.*.order'                   => 'numeric|min:0', | ||||
| 
 | ||||
|             // currency info
 | ||||
|             'transactions.*.currency_id'           => 'numeric|exists:transaction_currencies,id|nullable', | ||||
|             'transactions.*.currency_code'         => 'min:3|max:51|exists:transaction_currencies,code|nullable', | ||||
|             'transactions.*.foreign_currency_id'   => 'numeric|exists:transaction_currencies,id|nullable', | ||||
|             'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable', | ||||
|             'transactions.*.currency_id'             => 'numeric|exists:transaction_currencies,id|nullable', | ||||
|             'transactions.*.currency_code'           => 'min:3|max:51|exists:transaction_currencies,code|nullable', | ||||
|             'transactions.*.foreign_currency_id'     => 'numeric|exists:transaction_currencies,id|nullable', | ||||
|             'transactions.*.foreign_currency_code'   => 'min:3|max:51|exists:transaction_currencies,code|nullable', | ||||
| 
 | ||||
|             // amount
 | ||||
|             'transactions.*.amount'                => 'required|numeric|gt:0|max:1000000000', | ||||
|             'transactions.*.foreign_amount'        => 'numeric|gt:0|max:1000000000', | ||||
|             'transactions.*.amount'                  => ['required', new IsValidPositiveAmount()], | ||||
|             'transactions.*.foreign_amount'          => ['nullable', new IsValidPositiveAmount()], | ||||
| 
 | ||||
|             // description
 | ||||
|             'transactions.*.description'           => 'nullable|between:1,1000', | ||||
|             'transactions.*.description'             => 'nullable|min:1|max:1000', | ||||
| 
 | ||||
|             // source of transaction
 | ||||
|             'transactions.*.source_id'             => ['numeric', 'nullable', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.source_name'           => 'between:1,255|nullable', | ||||
|             'transactions.*.source_iban'           => 'between:1,255|nullable|iban', | ||||
|             'transactions.*.source_number'         => 'between:1,255|nullable', | ||||
|             'transactions.*.source_bic'            => 'between:1,255|nullable|bic', | ||||
|             'transactions.*.source_id'               => ['numeric', 'nullable', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.source_name'             => 'min:1|max:255|nullable', | ||||
|             'transactions.*.source_iban'             => 'min:1|max:255|nullable|iban', | ||||
|             'transactions.*.source_number'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.source_bic'              => 'min:1|max:255|nullable|bic', | ||||
| 
 | ||||
|             // destination of transaction
 | ||||
|             'transactions.*.destination_id'        => ['numeric', 'nullable', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.destination_name'      => 'between:1,255|nullable', | ||||
|             'transactions.*.destination_iban'      => 'between:1,255|nullable|iban', | ||||
|             'transactions.*.destination_number'    => 'between:1,255|nullable', | ||||
|             'transactions.*.destination_bic'       => 'between:1,255|nullable|bic', | ||||
|             'transactions.*.destination_id'          => ['numeric', 'nullable', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.destination_name'        => 'min:1|max:255|nullable', | ||||
|             'transactions.*.destination_iban'        => 'min:1|max:255|nullable|iban', | ||||
|             'transactions.*.destination_number'      => 'min:1|max:255|nullable', | ||||
|             'transactions.*.destination_bic'         => 'min:1|max:255|nullable|bic', | ||||
| 
 | ||||
|             // budget, category, bill and piggy
 | ||||
|             'transactions.*.budget_id'             => ['mustExist:budgets,id', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.budget_name'           => ['between:1,255', 'nullable', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.category_id'           => ['mustExist:categories,id', new BelongsUserGroup($userGroup), 'nullable'], | ||||
|             'transactions.*.category_name'         => 'between:1,255|nullable', | ||||
|             'transactions.*.bill_id'               => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.bill_name'             => ['between:1,255', 'nullable', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.piggy_bank_id'         => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.piggy_bank_name'       => ['between:1,255', 'nullable', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.budget_id'               => ['mustExist:budgets,id', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.budget_name'             => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.category_id'             => ['mustExist:categories,id', new BelongsUserGroup($userGroup), 'nullable'], | ||||
|             'transactions.*.category_name'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.bill_id'                 => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.bill_name'               => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.piggy_bank_id'           => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUserGroup($userGroup)], | ||||
|             'transactions.*.piggy_bank_name'         => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)], | ||||
| 
 | ||||
|             // other interesting fields
 | ||||
|             'transactions.*.reconciled'            => [new IsBoolean()], | ||||
|             'transactions.*.notes'                 => 'min:1|max:50000|nullable', | ||||
|             'transactions.*.tags'                  => 'between:0,255', | ||||
|             'transactions.*.reconciled'              => [new IsBoolean()], | ||||
|             'transactions.*.notes'                   => 'min:1|max:32768|nullable', | ||||
|             'transactions.*.tags'                    => 'min:0|max:255', | ||||
|             'transactions.*.tags.*'                  => 'min:0|max:255', | ||||
| 
 | ||||
|             // meta info fields
 | ||||
|             'transactions.*.internal_reference'    => 'min:1|max:255|nullable', | ||||
|             'transactions.*.external_id'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.recurrence_id'         => 'min:1|max:255|nullable', | ||||
|             'transactions.*.bunq_payment_id'       => 'min:1|max:255|nullable', | ||||
|             'transactions.*.external_url'          => 'min:1|max:255|nullable|url', | ||||
|             'transactions.*.internal_reference'      => 'min:1|max:255|nullable', | ||||
|             'transactions.*.external_id'             => 'min:1|max:255|nullable', | ||||
|             'transactions.*.recurrence_id'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.bunq_payment_id'         => 'min:1|max:255|nullable', | ||||
|             'transactions.*.external_url'            => 'min:1|max:255|nullable|url', | ||||
| 
 | ||||
|             // SEPA fields:
 | ||||
|             'transactions.*.sepa_cc'               => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ct_op'            => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ct_id'            => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_db'               => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_country'          => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ep'               => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ci'               => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_batch_id'         => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_cc'                 => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ct_op'              => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ct_id'              => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_db'                 => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_country'            => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ep'                 => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ci'                 => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_batch_id'           => 'min:1|max:255|nullable', | ||||
| 
 | ||||
|             // dates
 | ||||
|             'transactions.*.interest_date'         => 'date|nullable', | ||||
|             'transactions.*.book_date'             => 'date|nullable', | ||||
|             'transactions.*.process_date'          => 'date|nullable', | ||||
|             'transactions.*.due_date'              => 'date|nullable', | ||||
|             'transactions.*.payment_date'          => 'date|nullable', | ||||
|             'transactions.*.invoice_date'          => 'date|nullable', | ||||
|             'transactions.*.interest_date'           => 'date|nullable', | ||||
|             'transactions.*.book_date'               => 'date|nullable', | ||||
|             'transactions.*.process_date'            => 'date|nullable', | ||||
|             'transactions.*.due_date'                => 'date|nullable', | ||||
|             'transactions.*.payment_date'            => 'date|nullable', | ||||
|             'transactions.*.invoice_date'            => 'date|nullable', | ||||
| 
 | ||||
|             // TODO include location and ability to process it.
 | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -208,6 +212,9 @@ class StoreRequest extends FormRequest | ||||
|                 $this->validateGroupDescription($validator); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -222,7 +229,7 @@ class StoreRequest extends FormRequest | ||||
|          */ | ||||
|         foreach ($this->get('transactions') as $transaction) { | ||||
|             $object   = new NullArrayObject($transaction); | ||||
|             $return[] = [ | ||||
|             $result   = [ | ||||
|                 'type'                  => $this->clearString($object['type']), | ||||
|                 'date'                  => $this->dateFromValue($object['date']), | ||||
|                 'order'                 => $this->integerFromValue((string)$object['order']), | ||||
| @@ -300,6 +307,8 @@ class StoreRequest extends FormRequest | ||||
|                 'payment_date'          => $this->dateFromValue($object['payment_date']), | ||||
|                 'invoice_date'          => $this->dateFromValue($object['invoice_date']), | ||||
|             ]; | ||||
|             $result   = $this->addFromromTransactionStore($transaction, $result); | ||||
|             $return[] = $result; | ||||
|         } | ||||
| 
 | ||||
|         return $return; | ||||
|   | ||||
							
								
								
									
										366
									
								
								app/Api/V2/Request/Model/Transaction/UpdateRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								app/Api/V2/Request/Model/Transaction/UpdateRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,366 @@ | ||||
| <?php | ||||
| /* | ||||
|  * UpdateRequest.php | ||||
|  * Copyright (c) 2024 james@firefly-iii.org. | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see https://www.gnu.org/licenses/. | ||||
|  */ | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace FireflyIII\Api\V2\Request\Model\Transaction; | ||||
| 
 | ||||
| use FireflyIII\Api\V1\Requests\Models\AvailableBudget\Request; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Models\TransactionGroup; | ||||
| use FireflyIII\Rules\BelongsUser; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use FireflyIII\Rules\IsDateOrTime; | ||||
| use FireflyIII\Rules\IsValidPositiveAmount; | ||||
| use FireflyIII\Rules\IsValidZeroOrMoreAmount; | ||||
| use FireflyIII\Support\Request\ChecksLogin; | ||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | ||||
| use FireflyIII\Validation\GroupValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use Illuminate\Validation\Validator; | ||||
| 
 | ||||
| /** | ||||
|  * Class UpdateRequest | ||||
|  * | ||||
|  * TODO it's the same as the v1 | ||||
|  */ | ||||
| class UpdateRequest extends Request | ||||
| { | ||||
|     use ChecksLogin; | ||||
|     use ConvertsDataTypes; | ||||
|     use GroupValidation; | ||||
|     use TransactionValidation; | ||||
| 
 | ||||
|     private array $arrayFields; | ||||
|     private array $booleanFields; | ||||
|     private array $dateFields; | ||||
|     private array $floatFields; | ||||
|     private array $integerFields; | ||||
|     private array $stringFields; | ||||
|     private array $textareaFields; | ||||
| 
 | ||||
|     /** | ||||
|      * Get all data. Is pretty complex because of all the ??-statements. | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function getAll(): array | ||||
|     { | ||||
|         app('log')->debug(sprintf('Now in %s', __METHOD__)); | ||||
|         $this->integerFields  = ['order', 'currency_id', 'foreign_currency_id', 'transaction_journal_id', 'source_id', 'destination_id', 'budget_id', 'category_id', 'bill_id', 'recurrence_id']; | ||||
|         $this->dateFields     = ['date', 'interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date']; | ||||
|         $this->textareaFields = ['notes']; | ||||
|         // not really floats, for validation.
 | ||||
|         $this->floatFields    = ['amount', 'foreign_amount']; | ||||
|         $this->stringFields   = ['type', 'currency_code', 'foreign_currency_code', 'description', 'source_name', 'source_iban', 'source_number', 'source_bic', 'destination_name', 'destination_iban', 'destination_number', 'destination_bic', 'budget_name', 'category_name', 'bill_name', 'internal_reference', 'external_id', 'bunq_payment_id', 'sepa_cc', 'sepa_ct_op', 'sepa_ct_id', 'sepa_db', 'sepa_country', 'sepa_ep', 'sepa_ci', 'sepa_batch_id', 'external_url']; | ||||
|         $this->booleanFields  = ['reconciled']; | ||||
|         $this->arrayFields    = ['tags']; | ||||
|         $data                 = []; | ||||
|         if ($this->has('transactions')) { | ||||
|             $data['transactions'] = $this->getTransactionData(); | ||||
|         } | ||||
|         if ($this->has('apply_rules')) { | ||||
|             $data['apply_rules'] = $this->boolean('apply_rules', true); | ||||
|         } | ||||
|         if ($this->has('fire_webhooks')) { | ||||
|             $data['fire_webhooks'] = $this->boolean('fire_webhooks', true); | ||||
|         } | ||||
|         if ($this->has('group_title')) { | ||||
|             $data['group_title'] = $this->convertString('group_title'); | ||||
|         } | ||||
| 
 | ||||
|         return $data; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * The rules that the incoming request must be matched against. | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         app('log')->debug(sprintf('Now in %s', __METHOD__)); | ||||
|         $validProtocols = config('firefly.valid_url_protocols'); | ||||
| 
 | ||||
|         return [ | ||||
|             // basic fields for group:
 | ||||
|             'group_title'                            => 'min:1|max:1000|nullable', | ||||
|             'apply_rules'                            => [new IsBoolean()], | ||||
| 
 | ||||
|             // transaction rules (in array for splits):
 | ||||
|             'transactions.*.type'                    => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation', | ||||
|             'transactions.*.date'                    => [new IsDateOrTime()], | ||||
|             'transactions.*.order'                   => 'numeric|min:0', | ||||
| 
 | ||||
|             // group id:
 | ||||
|             'transactions.*.transaction_journal_id'  => ['nullable', 'numeric', new BelongsUser()], | ||||
| 
 | ||||
|             // currency info
 | ||||
|             'transactions.*.currency_id'             => 'numeric|exists:transaction_currencies,id|nullable', | ||||
|             'transactions.*.currency_code'           => 'min:3|max:51|exists:transaction_currencies,code|nullable', | ||||
|             'transactions.*.foreign_currency_id'     => 'nullable|numeric|exists:transaction_currencies,id', | ||||
|             'transactions.*.foreign_currency_code'   => 'nullable|min:3|max:51|exists:transaction_currencies,code', | ||||
| 
 | ||||
|             // amount
 | ||||
|             'transactions.*.amount'                  => ['nullable', new IsValidPositiveAmount()], | ||||
|             'transactions.*.foreign_amount'          => ['nullable', new IsValidZeroOrMoreAmount()], | ||||
| 
 | ||||
|             // description
 | ||||
|             'transactions.*.description'             => 'nullable|min:1|max:1000', | ||||
| 
 | ||||
|             // source of transaction
 | ||||
|             'transactions.*.source_id'               => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.source_name'             => 'min:1|max:255|nullable', | ||||
| 
 | ||||
|             // destination of transaction
 | ||||
|             'transactions.*.destination_id'          => ['numeric', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.destination_name'        => 'min:1|max:255|nullable', | ||||
| 
 | ||||
|             // budget, category, bill and piggy
 | ||||
|             'transactions.*.budget_id'               => ['mustExist:budgets,id', new BelongsUser(), 'nullable'], | ||||
|             'transactions.*.budget_name'             => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
|             'transactions.*.category_id'             => ['mustExist:categories,id', new BelongsUser(), 'nullable'], | ||||
|             'transactions.*.category_name'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.bill_id'                 => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()], | ||||
|             'transactions.*.bill_name'               => ['min:1', 'max:255', 'nullable', new BelongsUser()], | ||||
| 
 | ||||
|             // other interesting fields
 | ||||
|             'transactions.*.reconciled'              => [new IsBoolean()], | ||||
|             'transactions.*.notes'                   => 'min:1|max:32768|nullable', | ||||
|             'transactions.*.tags'                    => 'min:0|max:255|nullable', | ||||
|             'transactions.*.tags.*'                  => 'min:0|max:255', | ||||
| 
 | ||||
|             // meta info fields
 | ||||
|             'transactions.*.internal_reference'      => 'min:1|max:255|nullable', | ||||
|             'transactions.*.external_id'             => 'min:1|max:255|nullable', | ||||
|             'transactions.*.recurrence_id'           => 'min:1|max:255|nullable', | ||||
|             'transactions.*.bunq_payment_id'         => 'min:1|max:255|nullable', | ||||
|             'transactions.*.external_url'            => sprintf('min:1|max:255|nullable|url:%s', $validProtocols), | ||||
| 
 | ||||
|             // SEPA fields:
 | ||||
|             'transactions.*.sepa_cc'                 => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ct_op'              => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ct_id'              => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_db'                 => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_country'            => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ep'                 => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_ci'                 => 'min:1|max:255|nullable', | ||||
|             'transactions.*.sepa_batch_id'           => 'min:1|max:255|nullable', | ||||
| 
 | ||||
|             // dates
 | ||||
|             'transactions.*.interest_date'           => 'date|nullable', | ||||
|             'transactions.*.book_date'               => 'date|nullable', | ||||
|             'transactions.*.process_date'            => 'date|nullable', | ||||
|             'transactions.*.due_date'                => 'date|nullable', | ||||
|             'transactions.*.payment_date'            => 'date|nullable', | ||||
|             'transactions.*.invoice_date'            => 'date|nullable', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Configure the validator instance. | ||||
|      */ | ||||
|     public function withValidator(Validator $validator): void | ||||
|     { | ||||
|         app('log')->debug('Now in withValidator'); | ||||
| 
 | ||||
|         /** @var TransactionGroup $transactionGroup */ | ||||
|         $transactionGroup = $this->route()->parameter('userGroupTransaction'); | ||||
|         $validator->after( | ||||
|             function (Validator $validator) use ($transactionGroup): void { | ||||
|                 // if more than one, verify that there are journal ID's present.
 | ||||
|                 $this->validateJournalIds($validator, $transactionGroup); | ||||
| 
 | ||||
|                 // all transaction types must be equal:
 | ||||
|                 $this->validateTransactionTypesForUpdate($validator); | ||||
| 
 | ||||
|                 // user wants to update a reconciled transaction.
 | ||||
|                 // source, destination, amount + foreign_amount cannot be changed
 | ||||
|                 // and must be omitted from the request.
 | ||||
|                 $this->preventUpdateReconciled($validator, $transactionGroup); | ||||
| 
 | ||||
|                 // validate source/destination is equal, depending on the transaction journal type.
 | ||||
|                 $this->validateEqualAccountsForUpdate($validator, $transactionGroup); | ||||
| 
 | ||||
|                 // see method:
 | ||||
|                 // $this->preventNoAccountInfo($validator, );
 | ||||
| 
 | ||||
|                 // validate that the currency fits the source and/or destination account.
 | ||||
|                 // validate all account info
 | ||||
|                 $this->validateAccountInformationUpdate($validator, $transactionGroup); | ||||
|             } | ||||
|         ); | ||||
|         if($validator->fails()) { | ||||
|             Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get transaction data. | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function getTransactionData(): array | ||||
|     { | ||||
|         app('log')->debug(sprintf('Now in %s', __METHOD__)); | ||||
|         $return       = []; | ||||
| 
 | ||||
|         /** @var null|array $transactions */ | ||||
|         $transactions = $this->get('transactions'); | ||||
| 
 | ||||
|         if (!is_countable($transactions)) { | ||||
|             return $return; | ||||
|         } | ||||
| 
 | ||||
|         /** @var null|array $transaction */ | ||||
|         foreach ($transactions as $transaction) { | ||||
|             if (!is_array($transaction)) { | ||||
|                 throw new FireflyException('Invalid data submitted: transaction is not array.'); | ||||
|             } | ||||
|             // default response is to update nothing in the transaction:
 | ||||
|             $current  = []; | ||||
|             $current  = $this->getIntegerData($current, $transaction); | ||||
|             $current  = $this->getStringData($current, $transaction); | ||||
|             $current  = $this->getNlStringData($current, $transaction); | ||||
|             $current  = $this->getDateData($current, $transaction); | ||||
|             $current  = $this->getBooleanData($current, $transaction); | ||||
|             $current  = $this->getArrayData($current, $transaction); | ||||
|             $current  = $this->getFloatData($current, $transaction); | ||||
|             $return[] = $current; | ||||
|         } | ||||
| 
 | ||||
|         return $return; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * For each field, add it to the array if a reference is present in the request: | ||||
|      * | ||||
|      * @param array<string, string> $current | ||||
|      * @param array<string, mixed>  $transaction | ||||
|      */ | ||||
|     private function getIntegerData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->integerFields as $fieldName) { | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 $current[$fieldName] = $this->integerFromValue((string) $transaction[$fieldName]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $current; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param array<string, string> $current | ||||
|      * @param array<string, mixed>  $transaction | ||||
|      */ | ||||
|     private function getStringData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->stringFields as $fieldName) { | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 $current[$fieldName] = $this->clearString((string) $transaction[$fieldName]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $current; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param array<string, string> $current | ||||
|      * @param array<string, mixed>  $transaction | ||||
|      */ | ||||
|     private function getNlStringData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->textareaFields as $fieldName) { | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 $current[$fieldName] = $this->clearStringKeepNewlines((string) $transaction[$fieldName]); // keep newlines
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $current; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param array<string, string> $current | ||||
|      * @param array<string, mixed>  $transaction | ||||
|      */ | ||||
|     private function getDateData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->dateFields as $fieldName) { | ||||
|             app('log')->debug(sprintf('Now at date field %s', $fieldName)); | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 app('log')->debug(sprintf('New value: "%s"', (string) $transaction[$fieldName])); | ||||
|                 $current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $current; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param array<string, string> $current | ||||
|      * @param array<string, mixed>  $transaction | ||||
|      */ | ||||
|     private function getBooleanData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->booleanFields as $fieldName) { | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 $current[$fieldName] = $this->convertBoolean((string) $transaction[$fieldName]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $current; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param array<string, string> $current | ||||
|      * @param array<string, mixed>  $transaction | ||||
|      */ | ||||
|     private function getArrayData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->arrayFields as $fieldName) { | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 $current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $current; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param array<string, string> $current | ||||
|      * @param array<string, mixed>  $transaction | ||||
|      */ | ||||
|     private function getFloatData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->floatFields as $fieldName) { | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 $value = $transaction[$fieldName]; | ||||
|                 if (is_float($value)) { | ||||
|                     $current[$fieldName] = sprintf('%.12f', $value); | ||||
|                 } | ||||
|                 if (!is_float($value)) { | ||||
|                     $current[$fieldName] = (string) $value; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $current; | ||||
|     } | ||||
| } | ||||
| @@ -49,7 +49,7 @@ class StoreRequest extends FormRequest | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'title' => 'unique:user_groups,title|required|min:2|max:255', | ||||
|             'title' => 'unique:user_groups,title|required|min:1|max:255', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -53,7 +53,7 @@ class UpdateRequest extends FormRequest | ||||
|         $userGroup = $this->route()->parameter('userGroup'); | ||||
| 
 | ||||
|         return [ | ||||
|             'title' => sprintf('required|min:2|max:255|unique:user_groups,title,%d', $userGroup->id), | ||||
|             'title' => sprintf('required|min:1|max:255|unique:user_groups,title,%d', $userGroup->id), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -71,7 +71,6 @@ class EnableCurrencies extends Command | ||||
|         $found           = [$defaultCurrency->id]; | ||||
| 
 | ||||
|         // get all meta entries
 | ||||
|         /** @var Collection $meta */ | ||||
|         $meta            = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id') | ||||
|             ->where('accounts.user_group_id', $userGroup->id) | ||||
|             ->where('account_meta.name', 'currency_id')->groupBy('data')->get(['data']) | ||||
| @@ -108,6 +107,12 @@ class EnableCurrencies extends Command | ||||
|             $found[] = $entry->transaction_currency_id; | ||||
|         } | ||||
| 
 | ||||
|         // also get all currencies already enabled.
 | ||||
|         $alreadyEnabled  = $userGroup->currencies()->get(['transaction_currencies.id'])->pluck('id')->toArray(); | ||||
|         foreach ($alreadyEnabled as $currencyId) { | ||||
|             $found[] = $currencyId; | ||||
|         } | ||||
| 
 | ||||
|         $found           = array_values(array_unique($found)); | ||||
|         $found           = array_values( | ||||
|             array_filter( | ||||
|   | ||||
| @@ -216,6 +216,7 @@ class Handler extends ExceptionHandler | ||||
|             'json'         => request()->acceptsJson(), | ||||
|             'method'       => request()->method(), | ||||
|             'headers'      => $headers, | ||||
|             'post'         => 'POST' === request()->method() ? json_encode(request()->all()) : '', | ||||
|         ]; | ||||
| 
 | ||||
|         // create job that will mail.
 | ||||
|   | ||||
| @@ -28,6 +28,7 @@ use Carbon\Carbon; | ||||
| use FireflyIII\Exceptions\DuplicateTransactionException; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Location; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| @@ -49,6 +50,8 @@ use Illuminate\Support\Collection; | ||||
| 
 | ||||
| /** | ||||
|  * Class TransactionJournalFactory | ||||
|  * | ||||
|  * @SuppressWarnings(PHPMD.CouplingBetweenObjects) | ||||
|  */ | ||||
| class TransactionJournalFactory | ||||
| { | ||||
| @@ -318,10 +321,23 @@ class TransactionJournalFactory | ||||
|         $this->storePiggyEvent($journal, $row); | ||||
|         $this->storeTags($journal, $row['tags']); | ||||
|         $this->storeMetaFields($journal, $row); | ||||
|         $this->storeLocation($journal, $row); | ||||
| 
 | ||||
|         return $journal; | ||||
|     } | ||||
| 
 | ||||
|     private function storeLocation(TransactionJournal $journal, NullArrayObject $data): void | ||||
|     { | ||||
|         if (true === $data['store_location']) { | ||||
|             $location             = new Location(); | ||||
|             $location->longitude  = $data['longitude']; | ||||
|             $location->latitude   = $data['latitude']; | ||||
|             $location->zoom_level = $data['zoom_level']; | ||||
|             $location->locatable()->associate($journal); | ||||
|             $location->save(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function hashArray(NullArrayObject $row): string | ||||
|     { | ||||
|         $dataRow = $row->getArrayCopy(); | ||||
| @@ -360,16 +376,13 @@ class TransactionJournalFactory | ||||
|             ->where('transaction_journals.user_id', $this->user->id) | ||||
|             ->where('data', json_encode($hash, JSON_THROW_ON_ERROR)) | ||||
|             ->with(['transactionJournal', 'transactionJournal.transactionGroup']) | ||||
|             ->first() | ||||
|             ->first(['journal_meta.*']) | ||||
|         ; | ||||
|         if (null !== $result) { | ||||
|             app('log')->warning(sprintf('Found a duplicate in errorIfDuplicate because hash %s is not unique!', $hash)); | ||||
|             $journal = $result->transactionJournal()->withTrashed()->first(); | ||||
|             $group   = $journal?->transactionGroup()->withTrashed()->first(); | ||||
|             $groupId = $group?->id; | ||||
|             if (null === $group) { | ||||
|                 $groupId = 0; | ||||
|             } | ||||
|             $groupId = (int) $group?->id; | ||||
| 
 | ||||
|             throw new DuplicateTransactionException(sprintf('Duplicate of transaction #%d.', $groupId)); | ||||
|         } | ||||
|   | ||||
| @@ -182,7 +182,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function excludeInternalReference(string $internalReference): GroupCollectorInterface | ||||
|     { | ||||
|         $internalReference = (string) json_encode($internalReference); | ||||
|         $internalReference = (string)json_encode($internalReference); | ||||
|         $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -203,7 +203,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function externalIdContains(string $externalId): GroupCollectorInterface | ||||
|     { | ||||
|         $externalId = (string) json_encode($externalId); | ||||
|         $externalId = (string)json_encode($externalId); | ||||
|         $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -215,7 +215,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function externalIdDoesNotContain(string $externalId): GroupCollectorInterface | ||||
|     { | ||||
|         $externalId = (string) json_encode($externalId); | ||||
|         $externalId = (string)json_encode($externalId); | ||||
|         $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -227,7 +227,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function externalIdDoesNotEnd(string $externalId): GroupCollectorInterface | ||||
|     { | ||||
|         $externalId = (string) json_encode($externalId); | ||||
|         $externalId = (string)json_encode($externalId); | ||||
|         $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -239,7 +239,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function externalIdDoesNotStart(string $externalId): GroupCollectorInterface | ||||
|     { | ||||
|         $externalId = (string) json_encode($externalId); | ||||
|         $externalId = (string)json_encode($externalId); | ||||
|         $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -251,7 +251,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function externalIdEnds(string $externalId): GroupCollectorInterface | ||||
|     { | ||||
|         $externalId = (string) json_encode($externalId); | ||||
|         $externalId = (string)json_encode($externalId); | ||||
|         $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -263,7 +263,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function externalIdStarts(string $externalId): GroupCollectorInterface | ||||
|     { | ||||
|         $externalId = (string) json_encode($externalId); | ||||
|         $externalId = (string)json_encode($externalId); | ||||
|         $externalId = str_replace('\\', '\\\\', trim($externalId, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -276,7 +276,7 @@ trait MetaCollection | ||||
|     public function externalUrlContains(string $url): GroupCollectorInterface | ||||
|     { | ||||
|         $this->joinMetaDataTables(); | ||||
|         $url = (string) json_encode($url); | ||||
|         $url = (string)json_encode($url); | ||||
|         $url = str_replace('\\', '\\\\', trim($url, '"')); | ||||
|         $this->query->where('journal_meta.name', '=', 'external_url'); | ||||
|         $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $url)); | ||||
| @@ -287,7 +287,7 @@ trait MetaCollection | ||||
|     public function externalUrlDoesNotContain(string $url): GroupCollectorInterface | ||||
|     { | ||||
|         $this->joinMetaDataTables(); | ||||
|         $url = (string) json_encode($url); | ||||
|         $url = (string)json_encode($url); | ||||
|         $url = str_replace('\\', '\\\\', trim($url, '"')); | ||||
|         $this->query->where('journal_meta.name', '=', 'external_url'); | ||||
|         $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s%%', $url)); | ||||
| @@ -298,7 +298,7 @@ trait MetaCollection | ||||
|     public function externalUrlDoesNotEnd(string $url): GroupCollectorInterface | ||||
|     { | ||||
|         $this->joinMetaDataTables(); | ||||
|         $url = (string) json_encode($url); | ||||
|         $url = (string)json_encode($url); | ||||
|         $url = str_replace('\\', '\\\\', ltrim($url, '"')); | ||||
|         $this->query->where('journal_meta.name', '=', 'external_url'); | ||||
|         $this->query->where('journal_meta.data', 'NOT LIKE', sprintf('%%%s', $url)); | ||||
| @@ -309,7 +309,7 @@ trait MetaCollection | ||||
|     public function externalUrlDoesNotStart(string $url): GroupCollectorInterface | ||||
|     { | ||||
|         $this->joinMetaDataTables(); | ||||
|         $url = (string) json_encode($url); | ||||
|         $url = (string)json_encode($url); | ||||
|         $url = str_replace('\\', '\\\\', rtrim($url, '"')); | ||||
|         // var_dump($url);
 | ||||
| 
 | ||||
| @@ -322,7 +322,7 @@ trait MetaCollection | ||||
|     public function externalUrlEnds(string $url): GroupCollectorInterface | ||||
|     { | ||||
|         $this->joinMetaDataTables(); | ||||
|         $url = (string) json_encode($url); | ||||
|         $url = (string)json_encode($url); | ||||
|         $url = str_replace('\\', '\\\\', ltrim($url, '"')); | ||||
|         $this->query->where('journal_meta.name', '=', 'external_url'); | ||||
|         $this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s', $url)); | ||||
| @@ -333,7 +333,7 @@ trait MetaCollection | ||||
|     public function externalUrlStarts(string $url): GroupCollectorInterface | ||||
|     { | ||||
|         $this->joinMetaDataTables(); | ||||
|         $url = (string) json_encode($url); | ||||
|         $url = (string)json_encode($url); | ||||
|         $url = str_replace('\\', '\\\\', rtrim($url, '"')); | ||||
|         // var_dump($url);
 | ||||
| 
 | ||||
| @@ -371,7 +371,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function internalReferenceContains(string $internalReference): GroupCollectorInterface | ||||
|     { | ||||
|         $internalReference = (string) json_encode($internalReference); | ||||
|         $internalReference = (string)json_encode($internalReference); | ||||
|         $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); | ||||
|         // var_dump($internalReference);
 | ||||
|         // exit;
 | ||||
| @@ -385,7 +385,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function internalReferenceDoesNotContain(string $internalReference): GroupCollectorInterface | ||||
|     { | ||||
|         $internalReference = (string) json_encode($internalReference); | ||||
|         $internalReference = (string)json_encode($internalReference); | ||||
|         $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -397,7 +397,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function internalReferenceDoesNotEnd(string $internalReference): GroupCollectorInterface | ||||
|     { | ||||
|         $internalReference = (string) json_encode($internalReference); | ||||
|         $internalReference = (string)json_encode($internalReference); | ||||
|         $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -409,7 +409,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function internalReferenceDoesNotStart(string $internalReference): GroupCollectorInterface | ||||
|     { | ||||
|         $internalReference = (string) json_encode($internalReference); | ||||
|         $internalReference = (string)json_encode($internalReference); | ||||
|         $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -421,7 +421,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function internalReferenceEnds(string $internalReference): GroupCollectorInterface | ||||
|     { | ||||
|         $internalReference = (string) json_encode($internalReference); | ||||
|         $internalReference = (string)json_encode($internalReference); | ||||
|         $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -433,7 +433,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function internalReferenceStarts(string $internalReference): GroupCollectorInterface | ||||
|     { | ||||
|         $internalReference = (string) json_encode($internalReference); | ||||
|         $internalReference = (string)json_encode($internalReference); | ||||
|         $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -629,7 +629,7 @@ trait MetaCollection | ||||
| 
 | ||||
|     public function setInternalReference(string $internalReference): GroupCollectorInterface | ||||
|     { | ||||
|         $internalReference = (string) json_encode($internalReference); | ||||
|         $internalReference = (string)json_encode($internalReference); | ||||
|         $internalReference = str_replace('\\', '\\\\', trim($internalReference, '"')); | ||||
| 
 | ||||
|         $this->joinMetaDataTables(); | ||||
| @@ -682,6 +682,7 @@ trait MetaCollection | ||||
|         $list                = $tags->pluck('tag')->toArray(); | ||||
|         $list                = array_map('strtolower', $list); | ||||
|         $filter              = static function (array $object) use ($list): bool|array { | ||||
|             $includedJournals       = []; | ||||
|             $return                 = $object; | ||||
|             unset($return['transactions']); | ||||
|             $return['transactions'] = []; | ||||
| @@ -701,7 +702,12 @@ trait MetaCollection | ||||
|                     if (in_array(strtolower($tag['name']), $list, true)) { | ||||
|                         app('log')->debug(sprintf('Transaction has tag "%s" so count++.', $tag['name'])); | ||||
|                         ++$foundTagCount; | ||||
|                         $return['transactions'][] = $transaction; | ||||
|                         $journalId = $transaction['transaction_journal_id']; | ||||
|                         // #8377 prevent adding a transaction twice when multiple tag searches find this transaction
 | ||||
|                         if (!in_array($journalId, $includedJournals, true)) { | ||||
|                             $includedJournals[]       = $journalId; | ||||
|                             $return['transactions'][] = $transaction; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -462,7 +462,7 @@ trait TimeCollection | ||||
|      */ | ||||
|     public function setBefore(Carbon $date): GroupCollectorInterface | ||||
|     { | ||||
|         $beforeStr = $date->format('Y-m-d 00:00:00'); | ||||
|         $beforeStr = $date->format('Y-m-d 23:59:59'); | ||||
|         $this->query->where('transaction_journals.date', '<=', $beforeStr); | ||||
| 
 | ||||
|         return $this; | ||||
|   | ||||
| @@ -25,7 +25,6 @@ namespace FireflyIII\Helpers\Collector; | ||||
| 
 | ||||
| use Carbon\Carbon; | ||||
| use Carbon\Exceptions\InvalidFormatException; | ||||
| use Exception; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Collector\Extensions\AccountCollection; | ||||
| use FireflyIII\Helpers\Collector\Extensions\AmountCollection; | ||||
| @@ -106,6 +105,8 @@ class GroupCollector implements GroupCollectorInterface | ||||
|             'transaction_groups.created_at as created_at', | ||||
|             'transaction_groups.updated_at as updated_at', | ||||
|             'transaction_groups.title as transaction_group_title', | ||||
|             'transaction_groups.created_at as group_created_at', | ||||
|             'transaction_groups.updated_at as group_updated_at', | ||||
| 
 | ||||
|             // journal
 | ||||
|             'transaction_journals.id as transaction_journal_id', | ||||
| @@ -702,6 +703,8 @@ class GroupCollector implements GroupCollectorInterface | ||||
|                     'user_group_id'    => $augumentedJournal->user_group_id, | ||||
|                     // Field transaction_group_title was added by the query.
 | ||||
|                     'title'            => $augumentedJournal->transaction_group_title, // @phpstan-ignore-line
 | ||||
|                     'created_at'       => new Carbon($augumentedJournal->group_created_at, config('app.timezone')), | ||||
|                     'updated_at'       => new Carbon($augumentedJournal->group_updated_at, config('app.timezone')), | ||||
|                     'transaction_type' => $parsedGroup['transaction_type_type'], | ||||
|                     'count'            => 1, | ||||
|                     'sums'             => [], | ||||
| @@ -928,7 +931,6 @@ class GroupCollector implements GroupCollectorInterface | ||||
|     private function postFilterCollection(Collection $collection): Collection | ||||
|     { | ||||
|         $currentCollection = $collection; | ||||
| 
 | ||||
|         app('log')->debug(sprintf('GroupCollector: postFilterCollection has %d filter(s) and %d transaction(s).', count($this->postFilters), count($currentCollection))); | ||||
| 
 | ||||
|         /** | ||||
| @@ -951,11 +953,11 @@ class GroupCollector implements GroupCollectorInterface | ||||
|                     continue; | ||||
|                 } | ||||
|                 // if the result is a bool, use the unedited results.
 | ||||
|                 if(true === $result) { | ||||
|                 if (true === $result) { | ||||
|                     $nextCollection->push($item); | ||||
|                 } | ||||
|                 // if the result is an array, the filter has changed what's being returned.
 | ||||
|                 if(is_array($result)) { | ||||
|                 if (is_array($result)) { | ||||
|                     $nextCollection->push($result); | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -161,7 +161,7 @@ class CreateController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($account, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string) trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -187,7 +187,7 @@ class EditController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($account, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string) trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -78,7 +78,7 @@ class LoginController extends Controller | ||||
|     public function login(Request $request): JsonResponse|RedirectResponse | ||||
|     { | ||||
|         Log::channel('audit')->info(sprintf('User is trying to login using "%s"', $request->get($this->username()))); | ||||
|         app('log')->info('User is trying to login.'); | ||||
|         app('log')->debug('User is trying to login.'); | ||||
| 
 | ||||
|         $this->validateLogin($request); | ||||
|         app('log')->debug('Login data is present.'); | ||||
| @@ -88,7 +88,7 @@ class LoginController extends Controller | ||||
|         // the login attempts for this application. We'll key this by the username and
 | ||||
|         // the IP address of the client making these requests into this application.
 | ||||
|         if ($this->hasTooManyLoginAttempts($request)) { | ||||
|             Log::channel('audit')->info(sprintf('Login for user "%s" was locked out.', $request->get($this->username()))); | ||||
|             Log::channel('audit')->warning(sprintf('Login for user "%s" was locked out.', $request->get($this->username()))); | ||||
|             app('log')->error(sprintf('Login for user "%s" was locked out.', $request->get($this->username()))); | ||||
|             $this->fireLockoutEvent($request); | ||||
| 
 | ||||
| @@ -114,7 +114,7 @@ class LoginController extends Controller | ||||
|         // to login and redirect the user back to the login form. Of course, when this
 | ||||
|         // user surpasses their maximum number of attempts they will get locked out.
 | ||||
|         $this->incrementLoginAttempts($request); | ||||
|         Log::channel('audit')->info(sprintf('Login failed. Attempt for user "%s" failed.', $request->get($this->username()))); | ||||
|         Log::channel('audit')->warning(sprintf('Login failed. Attempt for user "%s" failed.', $request->get($this->username()))); | ||||
| 
 | ||||
|         $this->sendFailedLoginResponse($request); | ||||
| 
 | ||||
|   | ||||
| @@ -116,7 +116,7 @@ class CreateController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($bill, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -128,7 +128,7 @@ class EditController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($bill, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string) trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -202,7 +202,9 @@ class BudgetLimitController extends Controller | ||||
|         if ('' === $amount) { | ||||
|             $amount = '0'; | ||||
|         } | ||||
| 
 | ||||
|         if ((int)$amount > 268435456) { // 268 million, intentional integer
 | ||||
|             $amount = '268435456'; | ||||
|         } | ||||
|         // sanity check on amount:
 | ||||
|         if (0 === bccomp($amount, '0')) { | ||||
|             $budgetId = $budgetLimit->budget_id; | ||||
| @@ -217,9 +219,7 @@ class BudgetLimitController extends Controller | ||||
| 
 | ||||
|             return response()->json($array); | ||||
|         } | ||||
|         if ((int)$amount > 268435456) { // 268 million, intentional integer
 | ||||
|             $amount = '268435456'; | ||||
|         } | ||||
| 
 | ||||
|         if (-1 === bccomp($amount, '0')) { | ||||
|             $amount = bcmul($amount, '-1'); | ||||
|         } | ||||
|   | ||||
| @@ -127,7 +127,7 @@ class CreateController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($budget, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -137,7 +137,7 @@ class EditController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($budget, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -100,7 +100,7 @@ class CreateController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($category, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -104,7 +104,7 @@ class EditController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($category, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -108,7 +108,7 @@ class ReportController extends Controller | ||||
|                     continue; | ||||
|                 } | ||||
|                 $currencyId                                = $netWorthItem['currency_id']; | ||||
|                 $label                                     = $current->isoFormat((string) trans('config.month_and_day_js', [], $locale)); | ||||
|                 $label                                     = $current->isoFormat((string)trans('config.month_and_day_js', [], $locale)); | ||||
|                 if (!array_key_exists($currencyId, $chartData)) { | ||||
|                     $chartData[$currencyId] = [ | ||||
|                         'label'           => 'Net worth in '.$netWorthItem['currency_name'], | ||||
| @@ -145,6 +145,7 @@ class ReportController extends Controller | ||||
|         if ($cache->has()) { | ||||
|             return response()->json($cache->get()); | ||||
|         } | ||||
| 
 | ||||
|         app('log')->debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray()); | ||||
|         $format         = app('navigation')->preferredCarbonFormat($start, $end); | ||||
|         $titleFormat    = app('navigation')->preferredCarbonLocalizedFormat($start, $end); | ||||
| @@ -164,13 +165,13 @@ class ReportController extends Controller | ||||
|         /** @var array $journal */ | ||||
|         foreach ($journals as $journal) { | ||||
|             $period                           = $journal['date']->format($format); | ||||
|             $currencyId                       = (int) $journal['currency_id']; | ||||
|             $currencyId                       = (int)$journal['currency_id']; | ||||
|             $data[$currencyId]          ??= [ | ||||
|                 'currency_id'             => $currencyId, | ||||
|                 'currency_symbol'         => $journal['currency_symbol'], | ||||
|                 'currency_code'           => $journal['currency_code'], | ||||
|                 'currency_name'           => $journal['currency_name'], | ||||
|                 'currency_decimal_places' => (int) $journal['currency_decimal_places'], | ||||
|                 'currency_decimal_places' => (int)$journal['currency_decimal_places'], | ||||
|             ]; | ||||
|             $data[$currencyId][$period] ??= [ | ||||
|                 'period' => $period, | ||||
| @@ -193,7 +194,7 @@ class ReportController extends Controller | ||||
|         /** @var array $currency */ | ||||
|         foreach ($data as $currency) { | ||||
|             $income       = [ | ||||
|                 'label'           => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), | ||||
|                 'label'           => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), | ||||
|                 'type'            => 'bar', | ||||
|                 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green
 | ||||
|                 'currency_id'     => $currency['currency_id'], | ||||
| @@ -202,7 +203,7 @@ class ReportController extends Controller | ||||
|                 'entries'         => [], | ||||
|             ]; | ||||
|             $expense      = [ | ||||
|                 'label'           => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), | ||||
|                 'label'           => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), | ||||
|                 'type'            => 'bar', | ||||
|                 'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red
 | ||||
|                 'currency_id'     => $currency['currency_id'], | ||||
| @@ -212,7 +213,13 @@ class ReportController extends Controller | ||||
|             ]; | ||||
|             // loop all possible periods between $start and $end
 | ||||
|             $currentStart = clone $start; | ||||
|             while ($currentStart <= $end) { | ||||
|             $currentEnd   = clone $end; | ||||
| 
 | ||||
|             // #8374. Sloppy fix for yearly charts. Not really interested in a better fix with v2 layout and all.
 | ||||
|             if ('1Y' === $preferredRange) { | ||||
|                 $currentEnd = app('navigation')->endOfPeriod($currentEnd, $preferredRange); | ||||
|             } | ||||
|             while ($currentStart <= $currentEnd) { | ||||
|                 $key                        = $currentStart->format($format); | ||||
|                 $title                      = $currentStart->isoFormat($titleFormat); | ||||
|                 $income['entries'][$title]  = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); | ||||
|   | ||||
| @@ -30,6 +30,7 @@ use FireflyIII\Http\Middleware\IsDemoUser; | ||||
| use FireflyIII\Repositories\Journal\JournalRepositoryInterface; | ||||
| use FireflyIII\Support\Export\ExportDataGenerator; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Illuminate\Http\Response as LaravelResponse; | ||||
| use Illuminate\View\View; | ||||
| 
 | ||||
| @@ -63,8 +64,14 @@ class IndexController extends Controller | ||||
|     /** | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function export(): LaravelResponse | ||||
|     public function export(): LaravelResponse|RedirectResponse | ||||
|     { | ||||
|         if(auth()->user()->hasRole('demo')) { | ||||
|             session()->flash('info', (string) trans('firefly.demo_user_export')); | ||||
| 
 | ||||
|             return redirect(route('export.index')); | ||||
|         } | ||||
| 
 | ||||
|         /** @var ExportDataGenerator $generator */ | ||||
|         $generator = app(ExportDataGenerator::class); | ||||
|         $generator->setUser(auth()->user()); | ||||
|   | ||||
| @@ -36,6 +36,7 @@ use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; | ||||
| use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; | ||||
| use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; | ||||
| use FireflyIII\Support\CacheProperties; | ||||
| use FireflyIII\Support\Http\Controllers\DateCalculation; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| 
 | ||||
| /** | ||||
| @@ -43,6 +44,8 @@ use Illuminate\Http\JsonResponse; | ||||
|  */ | ||||
| class BoxController extends Controller | ||||
| { | ||||
|     use DateCalculation; | ||||
| 
 | ||||
|     /** | ||||
|      * This box has three types of info to display: | ||||
|      * 0) If the user has available amount this period and has overspent: overspent box. | ||||
| @@ -122,12 +125,12 @@ class BoxController extends Controller | ||||
|             // calculate with available budget.
 | ||||
|             $leftToSpendAmount = bcadd($totalAvailableSum, $spentAmount); | ||||
|             app('log')->debug(sprintf('So left to spend is %s', $leftToSpendAmount)); | ||||
|             if (1 === bccomp($leftToSpendAmount, '0')) { | ||||
|                 app('log')->debug('Left to spend is positive!'); | ||||
|             if (bccomp($leftToSpendAmount, '0') >= 0) { | ||||
|                 app('log')->debug('Left to spend is positive or zero!'); | ||||
|                 $boxTitle         = (string)trans('firefly.left_to_spend'); | ||||
|                 $days             = $today->diffInDays($end) + 1; | ||||
|                 $activeDaysLeft   = $this->activeDaysLeft($start, $end);   // see method description.
 | ||||
|                 $display          = 1; // not overspent
 | ||||
|                 $leftPerDayAmount = bcdiv($leftToSpendAmount, (string)$days); | ||||
|                 $leftPerDayAmount = bcdiv($leftToSpendAmount, (string)$activeDaysLeft); | ||||
|                 app('log')->debug(sprintf('Left to spend per day is %s', $leftPerDayAmount)); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -107,7 +107,7 @@ class CreateController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($piggyBank, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -127,7 +127,7 @@ class EditController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($piggyBank, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -231,6 +231,7 @@ class CreateController extends Controller | ||||
| 
 | ||||
|             return redirect(route('recurring.create'))->withInput(); | ||||
|         } | ||||
|         Log::channel('audit')->info('Stored new recurrence.', $data); | ||||
| 
 | ||||
|         $request->session()->flash('success', (string)trans('firefly.stored_new_recurrence', ['title' => $recurrence->title])); | ||||
|         app('preferences')->mark(); | ||||
| @@ -242,7 +243,7 @@ class CreateController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($recurrence, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -172,6 +172,7 @@ class EditController extends Controller | ||||
|         $this->recurring->update($recurrence, $data); | ||||
| 
 | ||||
|         $request->session()->flash('success', (string)trans('firefly.updated_recurrence', ['title' => $recurrence->title])); | ||||
|         Log::channel('audit')->info(sprintf('Updated recurrence #%d.', $recurrence->id), $data); | ||||
| 
 | ||||
|         // store new attachment(s):
 | ||||
|         /** @var null|array $files */ | ||||
| @@ -180,7 +181,7 @@ class EditController extends Controller | ||||
|             $this->attachments->saveAttachmentsForModel($recurrence, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -136,9 +136,13 @@ class SelectController extends Controller | ||||
|         } | ||||
| 
 | ||||
|         foreach ($textTriggers as $textTrigger) { | ||||
|             $needsContext             = config(sprintf('search.operators.%s.needs_context', $textTrigger['type'])) ?? true; | ||||
|             $trigger                  = new RuleTrigger(); | ||||
|             $trigger->trigger_type    = $textTrigger['type']; | ||||
|             $trigger->trigger_value   = $textTrigger['value']; | ||||
|             if(false === $needsContext) { | ||||
|                 $trigger->trigger_value = 'true'; | ||||
|             } | ||||
|             $trigger->stop_processing = $textTrigger['stop_processing']; | ||||
|             if ($textTrigger['prohibited']) { | ||||
|                 $trigger->trigger_type = sprintf('-%s', $textTrigger['type']); | ||||
|   | ||||
| @@ -305,7 +305,7 @@ class TagController extends Controller | ||||
|             $this->attachmentsHelper->saveAttachmentsForModel($result, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
| @@ -340,7 +340,7 @@ class TagController extends Controller | ||||
|             $this->attachmentsHelper->saveAttachmentsForModel($tag, $files); | ||||
|         } | ||||
|         if (null !== $files && auth()->user()->hasRole('demo')) { | ||||
|             Log::channel('audit')->info(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             Log::channel('audit')->warning(sprintf('The demo user is trying to upload attachments in %s.', __METHOD__)); | ||||
|             session()->flash('info', (string)trans('firefly.no_att_demo_user')); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -50,7 +50,7 @@ class CreateController extends Controller | ||||
| 
 | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 app('view')->share('title', (string)trans('firefly.transactions')); | ||||
|                 app('view')->share('title', (string) trans('firefly.transactions')); | ||||
|                 app('view')->share('mainTitleIcon', 'fa-exchange'); | ||||
|                 $this->repository = app(TransactionGroupRepositoryInterface::class); | ||||
| 
 | ||||
| @@ -61,7 +61,7 @@ class CreateController extends Controller | ||||
| 
 | ||||
|     public function cloneGroup(Request $request): JsonResponse | ||||
|     { | ||||
|         $groupId = (int)$request->get('id'); | ||||
|         $groupId = (int) $request->get('id'); | ||||
|         if (0 !== $groupId) { | ||||
|             $group = $this->repository->find($groupId); | ||||
|             if (null !== $group) { | ||||
| @@ -101,23 +101,43 @@ class CreateController extends Controller | ||||
|     { | ||||
|         app('preferences')->mark(); | ||||
| 
 | ||||
|         $sourceId             = (int)request()->get('source'); | ||||
|         $destinationId        = (int)request()->get('destination'); | ||||
|         $sourceId                   = (int) request()->get('source'); | ||||
|         $destinationId              = (int) request()->get('destination'); | ||||
| 
 | ||||
|         /** @var AccountRepositoryInterface $accountRepository */ | ||||
|         $accountRepository    = app(AccountRepositoryInterface::class); | ||||
|         $cash                 = $accountRepository->getCashAccount(); | ||||
|         $preFilled            = session()->has('preFilled') ? session('preFilled') : []; | ||||
|         $subTitle             = (string)trans(sprintf('breadcrumbs.create_%s', strtolower((string)$objectType))); | ||||
|         $subTitleIcon         = 'fa-plus'; | ||||
|         $optionalFields       = app('preferences')->get('transaction_journal_optional_fields', [])->data; | ||||
|         $allowedOpposingTypes = config('firefly.allowed_opposing_types'); | ||||
|         $accountToTypes       = config('firefly.account_to_transaction'); | ||||
|         $defaultCurrency      = app('amount')->getDefaultCurrency(); | ||||
|         $previousUrl          = $this->rememberPreviousUrl('transactions.create.url'); | ||||
|         $parts                = parse_url($previousUrl); | ||||
|         $search               = sprintf('?%s', $parts['query'] ?? ''); | ||||
|         $previousUrl          = str_replace($search, '', $previousUrl); | ||||
|         $accountRepository          = app(AccountRepositoryInterface::class); | ||||
|         $cash                       = $accountRepository->getCashAccount(); | ||||
|         $preFilled                  = session()->has('preFilled') ? session('preFilled') : []; | ||||
|         $subTitle                   = (string) trans(sprintf('breadcrumbs.create_%s', strtolower((string) $objectType))); | ||||
|         $subTitleIcon               = 'fa-plus'; | ||||
|         $optionalFields             = app('preferences')->get('transaction_journal_optional_fields', [])->data; | ||||
|         $allowedOpposingTypes       = config('firefly.allowed_opposing_types'); | ||||
|         $accountToTypes             = config('firefly.account_to_transaction'); | ||||
|         $defaultCurrency            = app('amount')->getDefaultCurrency(); | ||||
|         $previousUrl                = $this->rememberPreviousUrl('transactions.create.url'); | ||||
|         $parts                      = parse_url($previousUrl); | ||||
|         $search                     = sprintf('?%s', $parts['query'] ?? ''); | ||||
|         $previousUrl                = str_replace($search, '', $previousUrl); | ||||
|         if (!is_array($optionalFields)) { | ||||
|             $optionalFields = []; | ||||
|         } | ||||
|         // not really a fan of this, but meh.
 | ||||
|         $optionalDateFields         = [ | ||||
|             'interest_date' => $optionalFields['interest_date'] ?? false, | ||||
|             'book_date'     => $optionalFields['book_date'] ?? false, | ||||
|             'process_date'  => $optionalFields['process_date'] ?? false, | ||||
|             'due_date'      => $optionalFields['due_date'] ?? false, | ||||
|             'payment_date'  => $optionalFields['payment_date'] ?? false, | ||||
|             'invoice_date'  => $optionalFields['invoice_date'] ?? false, | ||||
|         ]; | ||||
|         $optionalFields['external_url'] ??= false; | ||||
|         $optionalFields['location']     ??= false; | ||||
|         $optionalFields['location'] = $optionalFields['location'] && true === config('firefly.enable_external_map'); | ||||
| 
 | ||||
|         // map info:
 | ||||
|         $longitude                  = config('firefly.default_location.longitude'); | ||||
|         $latitude                   = config('firefly.default_location.latitude'); | ||||
|         $zoomLevel                  = config('firefly.default_location.zoom_level'); | ||||
| 
 | ||||
|         session()->put('preFilled', $preFilled); | ||||
| 
 | ||||
| @@ -126,7 +146,11 @@ class CreateController extends Controller | ||||
|             compact( | ||||
|                 'subTitleIcon', | ||||
|                 'cash', | ||||
|                 'longitude', | ||||
|                 'latitude', | ||||
|                 'zoomLevel', | ||||
|                 'objectType', | ||||
|                 'optionalDateFields', | ||||
|                 'subTitle', | ||||
|                 'defaultCurrency', | ||||
|                 'previousUrl', | ||||
|   | ||||
| @@ -51,7 +51,7 @@ class EditController extends Controller | ||||
|         // translations:
 | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 app('view')->share('title', (string)trans('firefly.transactions')); | ||||
|                 app('view')->share('title', (string) trans('firefly.transactions')); | ||||
|                 app('view')->share('mainTitleIcon', 'fa-exchange'); | ||||
| 
 | ||||
|                 $this->repository = app(JournalRepositoryInterface::class); | ||||
| @@ -73,18 +73,43 @@ class EditController extends Controller | ||||
|         } | ||||
| 
 | ||||
|         /** @var AccountRepositoryInterface $repository */ | ||||
|         $repository           = app(AccountRepositoryInterface::class); | ||||
|         $allowedOpposingTypes = config('firefly.allowed_opposing_types'); | ||||
|         $accountToTypes       = config('firefly.account_to_transaction'); | ||||
|         $expectedSourceTypes  = config('firefly.expected_source_types'); | ||||
|         $allowedSourceDests   = config('firefly.source_dests'); | ||||
|         $repository                 = app(AccountRepositoryInterface::class); | ||||
|         $allowedOpposingTypes       = config('firefly.allowed_opposing_types'); | ||||
|         $accountToTypes             = config('firefly.account_to_transaction'); | ||||
|         $expectedSourceTypes        = config('firefly.expected_source_types'); | ||||
|         $allowedSourceDests         = config('firefly.source_dests'); | ||||
|         $title                      = $transactionGroup->transactionJournals()->count() > 1 ? $transactionGroup->title : $transactionGroup->transactionJournals()->first()->description; | ||||
|         $subTitle                   = (string) trans('firefly.edit_transaction_title', ['description' => $title]); | ||||
|         $subTitleIcon               = 'fa-plus'; | ||||
|         $defaultCurrency            = app('amount')->getDefaultCurrency(); | ||||
|         $cash                       = $repository->getCashAccount(); | ||||
|         $previousUrl                = $this->rememberPreviousUrl('transactions.edit.url'); | ||||
|         $parts                      = parse_url($previousUrl); | ||||
|         $search                     = sprintf('?%s', $parts['query'] ?? ''); | ||||
|         $previousUrl                = str_replace($search, '', $previousUrl); | ||||
| 
 | ||||
|         $defaultCurrency      = app('amount')->getDefaultCurrency(); | ||||
|         $cash                 = $repository->getCashAccount(); | ||||
|         $previousUrl          = $this->rememberPreviousUrl('transactions.edit.url'); | ||||
|         $parts                = parse_url($previousUrl); | ||||
|         $search               = sprintf('?%s', $parts['query'] ?? ''); | ||||
|         $previousUrl          = str_replace($search, '', $previousUrl); | ||||
|         // settings necessary for v2
 | ||||
|         $optionalFields             = app('preferences')->get('transaction_journal_optional_fields', [])->data; | ||||
|         if (!is_array($optionalFields)) { | ||||
|             $optionalFields = []; | ||||
|         } | ||||
|         // not really a fan of this, but meh.
 | ||||
|         $optionalDateFields         = [ | ||||
|             'interest_date' => $optionalFields['interest_date'] ?? false, | ||||
|             'book_date'     => $optionalFields['book_date'] ?? false, | ||||
|             'process_date'  => $optionalFields['process_date'] ?? false, | ||||
|             'due_date'      => $optionalFields['due_date'] ?? false, | ||||
|             'payment_date'  => $optionalFields['payment_date'] ?? false, | ||||
|             'invoice_date'  => $optionalFields['invoice_date'] ?? false, | ||||
|         ]; | ||||
|         $optionalFields['external_url'] ??= false; | ||||
|         $optionalFields['location']     ??= false; | ||||
|         $optionalFields['location'] = $optionalFields['location'] && true === config('firefly.enable_external_map'); | ||||
| 
 | ||||
|         // map info voor v2:
 | ||||
|         $longitude                  = config('firefly.default_location.longitude'); | ||||
|         $latitude                   = config('firefly.default_location.latitude'); | ||||
|         $zoomLevel                  = config('firefly.default_location.zoom_level'); | ||||
| 
 | ||||
|         return view( | ||||
|             'transactions.edit', | ||||
| @@ -92,6 +117,13 @@ class EditController extends Controller | ||||
|                 'cash', | ||||
|                 'allowedSourceDests', | ||||
|                 'expectedSourceTypes', | ||||
|                 'optionalDateFields', | ||||
|                 'longitude', | ||||
|                 'latitude', | ||||
|                 'zoomLevel', | ||||
|                 'optionalFields', | ||||
|                 'subTitle', | ||||
|                 'subTitleIcon', | ||||
|                 'transactionGroup', | ||||
|                 'allowedOpposingTypes', | ||||
|                 'accountToTypes', | ||||
|   | ||||
| @@ -75,45 +75,52 @@ class IndexController extends Controller | ||||
|             $objectType = 'transfer'; | ||||
|         } | ||||
| 
 | ||||
|         $subTitleIcon  = config('firefly.transactionIconsByType.'.$objectType); | ||||
|         $types         = config('firefly.transactionTypesByType.'.$objectType); | ||||
|         $page          = (int)$request->get('page'); | ||||
|         $pageSize      = (int)app('preferences')->get('listPageSize', 50)->data; | ||||
|         if (null === $start) { | ||||
|             $start = session('start'); | ||||
|             $end   = session('end'); | ||||
|         // add a split for the (future) v2 release.
 | ||||
|         $periods      = []; | ||||
|         $groups       = []; | ||||
|         $subTitle     = 'TODO page subtitle in v2'; | ||||
| 
 | ||||
|         $subTitleIcon = config('firefly.transactionIconsByType.'.$objectType); | ||||
|         $types        = config('firefly.transactionTypesByType.'.$objectType); | ||||
|         $page         = (int)$request->get('page'); | ||||
|         $pageSize     = (int)app('preferences')->get('listPageSize', 50)->data; | ||||
| 
 | ||||
|         if ('v2' !== (string)config('firefly.layout')) { | ||||
|             if (null === $start) { | ||||
|                 $start = session('start'); | ||||
|                 $end   = session('end'); | ||||
|             } | ||||
|             if (null === $end) { | ||||
|                 // get last transaction ever?
 | ||||
|                 $last = $this->repository->getLast(); | ||||
|                 $end  = null !== $last ? $last->date : session('end'); | ||||
|             } | ||||
| 
 | ||||
|             [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; | ||||
|             $startStr      = $start->isoFormat($this->monthAndDayFormat); | ||||
|             $endStr        = $end->isoFormat($this->monthAndDayFormat); | ||||
|             $subTitle      = (string)trans(sprintf('firefly.title_%s_between', $objectType), ['start' => $startStr, 'end' => $endStr]); | ||||
|             $path          = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]); | ||||
|             $firstJournal  = $this->repository->firstNull(); | ||||
|             $startPeriod   = null === $firstJournal ? new Carbon() : $firstJournal->date; | ||||
|             $endPeriod     = clone $end; | ||||
|             $periods       = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod); | ||||
| 
 | ||||
|             /** @var GroupCollectorInterface $collector */ | ||||
|             $collector     = app(GroupCollectorInterface::class); | ||||
| 
 | ||||
|             $collector->setRange($start, $end) | ||||
|                 ->setTypes($types) | ||||
|                 ->setLimit($pageSize) | ||||
|                 ->setPage($page) | ||||
|                 ->withBudgetInformation() | ||||
|                 ->withCategoryInformation() | ||||
|                 ->withAccountInformation() | ||||
|                 ->withAttachmentInformation() | ||||
|             ; | ||||
|             $groups        = $collector->getPaginatedGroups(); | ||||
|             $groups->setPath($path); | ||||
|         } | ||||
|         if (null === $end) { | ||||
|             // get last transaction ever?
 | ||||
|             $last = $this->repository->getLast(); | ||||
|             $end  = null !== $last ? $last->date : session('end'); | ||||
|         } | ||||
| 
 | ||||
|         [$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; | ||||
|         $path          = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]); | ||||
|         $startStr      = $start->isoFormat($this->monthAndDayFormat); | ||||
|         $endStr        = $end->isoFormat($this->monthAndDayFormat); | ||||
|         $subTitle      = (string)trans(sprintf('firefly.title_%s_between', $objectType), ['start' => $startStr, 'end' => $endStr]); | ||||
| 
 | ||||
|         $firstJournal  = $this->repository->firstNull(); | ||||
|         $startPeriod   = null === $firstJournal ? new Carbon() : $firstJournal->date; | ||||
|         $endPeriod     = clone $end; | ||||
|         $periods       = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod); | ||||
| 
 | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector     = app(GroupCollectorInterface::class); | ||||
| 
 | ||||
|         $collector->setRange($start, $end) | ||||
|             ->setTypes($types) | ||||
|             ->setLimit($pageSize) | ||||
|             ->setPage($page) | ||||
|             ->withBudgetInformation() | ||||
|             ->withCategoryInformation() | ||||
|             ->withAccountInformation() | ||||
|             ->withAttachmentInformation() | ||||
|         ; | ||||
|         $groups        = $collector->getPaginatedGroups(); | ||||
|         $groups->setPath($path); | ||||
| 
 | ||||
|         return view('transactions.index', compact('subTitle', 'objectType', 'subTitleIcon', 'groups', 'periods', 'start', 'end')); | ||||
|     } | ||||
|   | ||||
| @@ -105,7 +105,7 @@ class CreateController extends Controller | ||||
|         $data            = $request->getCurrencyData(); | ||||
|         if (!$this->userRepository->hasRole($user, 'owner')) { | ||||
|             app('log')->error('User '.auth()->user()->id.' is not admin, but tried to store a currency.'); | ||||
|             Log::channel('audit')->info('Tried to create (POST) currency without admin rights.', $data); | ||||
|             Log::channel('audit')->warning('Tried to create (POST) currency without admin rights.', $data); | ||||
| 
 | ||||
|             return redirect($this->getPreviousUrl('currencies.create.url'))->withInput(); | ||||
|         } | ||||
| @@ -116,7 +116,7 @@ class CreateController extends Controller | ||||
|             $currency = $this->repository->store($data); | ||||
|         } catch (FireflyException $e) { | ||||
|             app('log')->error($e->getMessage()); | ||||
|             Log::channel('audit')->info('Could not store (POST) currency without admin rights.', $data); | ||||
|             Log::channel('audit')->warning('Could not store (POST) currency without admin rights.', $data); | ||||
|             $request->session()->flash('error', (string)trans('firefly.could_not_store_currency')); | ||||
|             $currency = null; | ||||
|         } | ||||
|   | ||||
| @@ -74,7 +74,7 @@ class DeleteController extends Controller | ||||
|         $user     = auth()->user(); | ||||
|         if (!$this->userRepository->hasRole($user, 'owner')) { | ||||
|             $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); | ||||
|             Log::channel('audit')->info(sprintf('Tried to visit page to delete currency %s but is not site owner.', $currency->code)); | ||||
|             Log::channel('audit')->warning(sprintf('Tried to visit page to delete currency %s but is not site owner.', $currency->code)); | ||||
| 
 | ||||
|             return redirect(route('currencies.index')); | ||||
|         } | ||||
| @@ -83,7 +83,7 @@ class DeleteController extends Controller | ||||
|             $location = $this->repository->currencyInUseAt($currency); | ||||
|             $message  = (string)trans(sprintf('firefly.cannot_disable_currency_%s', $location), ['name' => e($currency->name)]); | ||||
|             $request->session()->flash('error', $message); | ||||
|             Log::channel('audit')->info(sprintf('Tried to visit page to delete currency %s but currency is in use.', $currency->code)); | ||||
|             Log::channel('audit')->warning(sprintf('Tried to visit page to delete currency %s but currency is in use.', $currency->code)); | ||||
| 
 | ||||
|             return redirect(route('currencies.index')); | ||||
|         } | ||||
| @@ -107,7 +107,7 @@ class DeleteController extends Controller | ||||
|         $user = auth()->user(); | ||||
|         if (!$this->userRepository->hasRole($user, 'owner')) { | ||||
|             $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))])); | ||||
|             Log::channel('audit')->info(sprintf('Tried to delete currency %s but is not site owner.', $currency->code)); | ||||
|             Log::channel('audit')->warning(sprintf('Tried to delete currency %s but is not site owner.', $currency->code)); | ||||
| 
 | ||||
|             return redirect(route('currencies.index')); | ||||
|         } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user