mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 18:54:58 +00:00 
			
		
		
		
	Compare commits
	
		
			460 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 326bef66ee | ||
|  | 67ba8cee09 | ||
|  | 08107f7103 | ||
|  | f5c075936f | ||
|  | d493820cc8 | ||
|  | af4a006192 | ||
|  | 946e3fb540 | ||
|  | b19338e2f3 | ||
|  | d4a44e6089 | ||
|  | c90c181785 | ||
|  | b75178a184 | ||
|  | a77187135f | ||
|  | 61e24a41a2 | ||
|  | 07c7bf1d49 | ||
|  | d4cb72cb77 | ||
|  | 948cdf7e5e | ||
|  | e93292d98c | ||
|  | 7105d34fb3 | ||
|  | 29847c9711 | ||
|  | 3db778a2b1 | ||
|  | 361f78542a | ||
|  | 15d0e04431 | ||
|  | b968ef416a | ||
|  | 6191362fe0 | ||
|  | 6d8fd79922 | ||
|  | 0227879a69 | ||
|  | e4d2b121c1 | ||
|  | f8941ab507 | ||
|  | f3ac8a5888 | ||
|  | 64ac3927ec | ||
|  | a558613189 | ||
|  | 7535ca9a26 | ||
|  | 03670e2dfe | ||
|  | be797f5353 | ||
|  | da38df6e4b | ||
|  | 2d53eb300a | ||
|  | 523ea78166 | ||
|  | 3b33730e48 | ||
|  | 351272cc27 | ||
|  | a293827739 | ||
|  | ead2cf92d3 | ||
|  | dea9e21382 | ||
|  | 69725eb8c3 | ||
|  | d31bb1f9b6 | ||
|  | b64979e45a | ||
|  | 3dd3d2cabb | ||
|  | 94e7c3527f | ||
|  | 1a71d22146 | ||
|  | fdc3b80f1f | ||
|  | b978de78e4 | ||
|  | 8b6bd296e1 | ||
|  | 4d075feed6 | ||
|  | e0e16489a1 | ||
|  | deeb4933fc | ||
|  | be8a0ddb3e | ||
|  | 49b1a6d844 | ||
|  | 3c1361e377 | ||
|  | a7ab5b7504 | ||
|  | 0c8229d689 | ||
|  | aebf45697f | ||
|  | d5d5530a8e | ||
|  | 8aa7776072 | ||
|  | f427267f5b | ||
|  | 3195dd0db0 | ||
|  | e67c62e6cd | ||
|  | d319a21b01 | ||
|  | f8963179c3 | ||
|  | d3cd657501 | ||
|  | 6c6bb2cd1e | ||
|  | 1771b3964e | ||
|  | 351c0ee2d7 | ||
|  | 1c9c380c8c | ||
|  | 24455bf980 | ||
|  | e21b40b46c | ||
|  | 29eabf9c8a | ||
|  | 60e20ceeb1 | ||
|  | 37b35661be | ||
|  | b54bb20617 | ||
|  | 19ce6b71e2 | ||
|  | b68663c977 | ||
|  | ac385e2647 | ||
|  | bc70174f44 | ||
|  | b8668b44a0 | ||
|  | 86a87cc951 | ||
|  | be58b1d2be | ||
|  | 61733f6553 | ||
|  | 15e772d9dc | ||
|  | ae7b81bf86 | ||
|  | 2c9ac0982a | ||
|  | 2df390065b | ||
|  | 247c9aebf3 | ||
|  | 0783500eaa | ||
|  | c2e542004d | ||
|  | 806ad918f0 | ||
|  | 84406a74c3 | ||
|  | 09990acaa2 | ||
|  | a73247ec8c | ||
|  | e98d43dd65 | ||
|  | dbd68cedc9 | ||
|  | e6e8200912 | ||
|  | 7b1380366b | ||
|  | 1eda806c17 | ||
|  | 782ecca6a9 | ||
|  | e6338705a7 | ||
|  | 09226e6f12 | ||
|  | 5ff1991cdc | ||
|  | 4ebb6de520 | ||
|  | 1ba7d65582 | ||
|  | 4e6063a4f8 | ||
|  | 85476f3549 | ||
|  | ef9714b7e0 | ||
|  | 8d20029557 | ||
|  | 0f04b44ca1 | ||
|  | 44ed45502e | ||
|  | 5bcafe1311 | ||
|  | 5a771ccc5f | ||
|  | c6145b4a3b | ||
|  | b20aeca849 | ||
|  | 793918f2f3 | ||
|  | 5d410e577e | ||
|  | 9ec786ec3a | ||
|  | e532b4d4fc | ||
|  | 58585d03c6 | ||
|  | a4f66b3d86 | ||
|  | c847621874 | ||
|  | 86f14885eb | ||
|  | 173e196bc8 | ||
|  | 7505db871f | ||
|  | 946dde8957 | ||
|  | 9a52cfbfbe | ||
|  | b248bd6d0c | ||
|  | 3fff9ad0a2 | ||
|  | a695a1bba2 | ||
|  | 91e384aae8 | ||
|  | 1ac95b6fa7 | ||
|  | 6757b6211d | ||
|  | 4dfb78837e | ||
|  | af50ad3db4 | ||
|  | 01cb94aabc | ||
|  | 5ffc5060b9 | ||
|  | 43d7c956a4 | ||
|  | 863b07951a | ||
|  | 2101717edb | ||
|  | e7ac1476c5 | ||
|  | 5d24c1fee1 | ||
|  | 53eb863f9d | ||
|  | 3c3ba637b5 | ||
|  | be8286b15c | ||
|  | 6f6087995d | ||
|  | 2ff8a35171 | ||
|  | 7f8ed7abb6 | ||
|  | 2ff0c37c12 | ||
|  | c593936b32 | ||
|  | c54204ede9 | ||
|  | d9132bbee3 | ||
|  | c1be98762e | ||
|  | c2733e2a8f | ||
|  | 722cf9b4fe | ||
|  | 01e31e73e0 | ||
|  | dba7d05296 | ||
|  | a8709a4a45 | ||
|  | 49b1435cba | ||
|  | 6a25b41952 | ||
|  | c561f99de6 | ||
|  | 9d9053d828 | ||
|  | c3c9a2f3c0 | ||
|  | 28465142e9 | ||
|  | 5473a618c9 | ||
|  | ac1a8d8053 | ||
|  | 15ae9203b6 | ||
|  | 9dfc2ae20b | ||
|  | 230de7cbdd | ||
|  | feaa003a52 | ||
|  | 295d01dc16 | ||
|  | cbf1fde45e | ||
|  | 6cc47287d3 | ||
|  | 4bbe728376 | ||
|  | 427a90ec85 | ||
|  | 55ddb26dac | ||
|  | 891777f079 | ||
|  | 1655286b67 | ||
|  | dd81636bf2 | ||
|  | 56a43a707d | ||
|  | c2f92c6e45 | ||
|  | 61bd2dc840 | ||
|  | f6a675f2e2 | ||
|  | 85b43055a7 | ||
|  | e63f7bcc70 | ||
|  | 384dd37430 | ||
|  | c6b336171c | ||
|  | d2f4399a1a | ||
|  | 02d1bc093c | ||
|  | 420e493987 | ||
|  | 0a15479bff | ||
|  | a9b76a3679 | ||
|  | d1a3cd9044 | ||
|  | 81735d59f8 | ||
|  | 1d8da7f9f0 | ||
|  | c5f0684030 | ||
|  | 25867adcb9 | ||
|  | d55694cd68 | ||
|  | 05f069d61e | ||
|  | 5f6e7ad138 | ||
|  | 61bc38921e | ||
|  | 94c660545d | ||
|  | 4d3907948d | ||
|  | e6442dd8af | ||
|  | 7905e0bd70 | ||
|  | f4b1da352d | ||
|  | 0d33348941 | ||
|  | c7c875e95f | ||
|  | 19d24b3e2a | ||
|  | 8fed6b6657 | ||
|  | b5eafa1910 | ||
|  | c15501937f | ||
|  | 4c743bd5b0 | ||
|  | 44289cbd95 | ||
|  | b2f1642cfe | ||
|  | 341ef0220c | ||
|  | 9df88115bc | ||
|  | c7273edb5e | ||
|  | c398aa2b69 | ||
|  | aa786eaaf3 | ||
|  | e58a5e12d6 | ||
|  | 12b3575c5c | ||
|  | 3ca186dc8f | ||
|  | 1535f596f6 | ||
|  | 2cc326caa1 | ||
|  | 43436ae942 | ||
|  | fbfd8475de | ||
|  | 405752f353 | ||
|  | 2af98b259a | ||
|  | 015242a666 | ||
|  | 54933fda2e | ||
|  | 852d057a47 | ||
|  | 1778f0b4f3 | ||
|  | 6daf083b3f | ||
|  | 4a7d9b130a | ||
|  | 4163efba55 | ||
|  | db5847b49b | ||
|  | 6829003f5e | ||
|  | 047927718e | ||
|  | 91deb22a3f | ||
|  | 1629ca0739 | ||
|  | 8b87204f10 | ||
|  | f920d90e3d | ||
|  | 1cb91282af | ||
|  | a6ce294131 | ||
|  | 34ceb69776 | ||
|  | eed68b5d95 | ||
|  | 79374c11ee | ||
|  | 404f9df6d3 | ||
|  | 4b716e35b9 | ||
|  | 166fc7a3e2 | ||
|  | 06afbc7a0a | ||
|  | 49a98de63a | ||
|  | 5b572c0da2 | ||
|  | 59014b1a87 | ||
|  | 4aec1f06e0 | ||
|  | 136af9625a | ||
|  | edac26f757 | ||
|  | ac54dd05bf | ||
|  | 7948058364 | ||
|  | f13a6f7bf9 | ||
|  | e1a5d143c5 | ||
|  | f98011cd0d | ||
|  | e7c10dec5c | ||
|  | ae2b28fdee | ||
|  | 8d3fc18ca6 | ||
|  | 61521cf478 | ||
|  | 06c5e4df2c | ||
|  | 9e4b7f8bb4 | ||
|  | 144bc29eb3 | ||
|  | 2c0d8b9cb3 | ||
|  | 511b1258ba | ||
|  | 8cfe0af502 | ||
|  | 54e3c7d729 | ||
|  | 628b493128 | ||
|  | 8c552b8fa8 | ||
|  | c864d904b0 | ||
|  | ecb61676ab | ||
|  | ee5a4caaab | ||
|  | 84bdd47109 | ||
|  | 5445752588 | ||
|  | bcfbdcf3f0 | ||
|  | 7e6cd77203 | ||
|  | 5e3d00ecde | ||
|  | 4dea3a5f55 | ||
|  | 7303d8b899 | ||
|  | fa0caa6518 | ||
|  | 26be90eff1 | ||
|  | db3c9fbd28 | ||
|  | 87b62e2e72 | ||
|  | 1e6224f041 | ||
|  | d6797b8428 | ||
|  | 76c89a8efa | ||
|  | 1797880488 | ||
|  | cdebde5a2b | ||
|  | 5ee1f322d0 | ||
|  | 87273e232a | ||
|  | 53b77e6bf9 | ||
|  | b8696bf8b5 | ||
|  | 8436a86536 | ||
|  | c598151c68 | ||
|  | 53938f3c12 | ||
|  | 0813464fe0 | ||
|  | 4436c9939e | ||
|  | 5e323b958f | ||
|  | 367f225f38 | ||
|  | a97d7058ff | ||
|  | 9d7fb0efe1 | ||
|  | 1e9596bba0 | ||
|  | c6d17e9bd7 | ||
|  | f76585dc9b | ||
|  | cf4f76f211 | ||
|  | 790e29f15e | ||
|  | 2107406d8b | ||
|  | b91a21a081 | ||
|  | 170e35aecc | ||
|  | 5701597e20 | ||
|  | d16389748f | ||
|  | ee844e970f | ||
|  | 62757812ff | ||
|  | c15b72b6b0 | ||
|  | 11e3834235 | ||
|  | affc198b03 | ||
|  | d5b315e3c8 | ||
|  | eb31ff4ebb | ||
|  | 9ab36d4598 | ||
|  | bf06f8eac3 | ||
|  | fa9befba11 | ||
|  | 3b0c061ec7 | ||
|  | d021be6a25 | ||
|  | 69ee691497 | ||
|  | 5a7152ceec | ||
|  | 7b216543fa | ||
|  | cd3f3fd781 | ||
|  | 4b0e46af2b | ||
|  | 470ab6340c | ||
|  | 9e54ebe6a9 | ||
|  | 7eb2451e3d | ||
|  | 6dba44ba71 | ||
|  | 64683647bd | ||
|  | 1f919b6d0e | ||
|  | 417a0d0f83 | ||
|  | 9b53576fc2 | ||
|  | 2a46756838 | ||
|  | cdf055065f | ||
|  | 2cee008e4a | ||
|  | d889e39aad | ||
|  | 72b9ecf07e | ||
|  | 14f4351920 | ||
|  | 12e81364a0 | ||
|  | d489244c00 | ||
|  | aceaf5f891 | ||
|  | 6129b9d25c | ||
|  | 75afe35e98 | ||
|  | 3246b7eb9f | ||
|  | c412ffc195 | ||
|  | 7c3b5fe714 | ||
|  | ae967c027b | ||
|  | 3e74fce885 | ||
|  | 2c7891bfb2 | ||
|  | 712ba00c9e | ||
|  | 74683b18f2 | ||
|  | d0d129d965 | ||
|  | 87d459c5ff | ||
|  | 5b591ff744 | ||
|  | 63f00cbb7b | ||
|  | c90b26c181 | ||
|  | c136c60eae | ||
|  | ba4c1d95a7 | ||
|  | 9a25d6a741 | ||
|  | 91c067ac9f | ||
|  | 24129ab69c | ||
|  | 2b6c3fd743 | ||
|  | bd2f064eeb | ||
|  | b0c9fc0792 | ||
|  | e02e747f1b | ||
|  | 64462812fc | ||
|  | 46382b0d21 | ||
|  | a7fffa5868 | ||
|  | 4334928fbd | ||
|  | a478d1b544 | ||
|  | 08b69dfbf6 | ||
|  | b631adbbf2 | ||
|  | fbd88d03c5 | ||
|  | 4982f6f919 | ||
|  | 6a6bf102b6 | ||
|  | 524f85b9c1 | ||
|  | 328c960950 | ||
|  | 208bece7ea | ||
|  | 16f918a294 | ||
|  | 9d90beb790 | ||
|  | d7e953d38c | ||
|  | 6967bb003e | ||
|  | f63e51fea2 | ||
|  | 33c73701d8 | ||
|  | 50b710b4f6 | ||
|  | d1325ffbd8 | ||
|  | bfc6a70c9f | ||
|  | a00f46faa9 | ||
|  | c475f05652 | ||
|  | d42b9ee017 | ||
|  | cccaae49a5 | ||
|  | 1dd3018cb2 | ||
|  | 7ea32046af | ||
|  | 9bb2f1cfd3 | ||
|  | fdffed636f | ||
|  | bde0732135 | ||
|  | d1d11ae717 | ||
|  | 309633069c | ||
|  | 2ece754927 | ||
|  | 6f98fc0dff | ||
|  | cd65971f5e | ||
|  | 81d7b7b6a1 | ||
|  | 92ba56ae2b | ||
|  | e1bcfb9ac9 | ||
|  | b398924706 | ||
|  | d6f46e23c5 | ||
|  | 8b4066a981 | ||
|  | f8a695d7e1 | ||
|  | 630b24716d | ||
|  | be41049e72 | ||
|  | b726e7d106 | ||
|  | 1058bcd31d | ||
|  | 8724ba05ca | ||
|  | 5145707b94 | ||
|  | 3746cb8c71 | ||
|  | 624f2d0bfd | ||
|  | 1c397ad0ad | ||
|  | a2c0f0666f | ||
|  | 72a4e86e2f | ||
|  | 5e8d94d16a | ||
|  | d92b741088 | ||
|  | 2a4107940f | ||
|  | 16d5282929 | ||
|  | f6ecb143fe | ||
|  | f375934b41 | ||
|  | 9f8bf6d495 | ||
|  | 9e9b55da8e | ||
|  | c1654f1d04 | ||
|  | c526c4ae1a | ||
|  | dcf1609c61 | ||
|  | dd84c7f966 | ||
|  | bccde86c0c | ||
|  | 2f17c45b3f | ||
|  | b6bab003cd | ||
|  | 27d7eb4832 | ||
|  | 774f7d88c2 | ||
|  | 086e4d5880 | ||
|  | 50f4bf568b | ||
|  | c218c70b1f | ||
|  | a939a5ba30 | ||
|  | 25be550e6d | ||
|  | 9331f8985a | ||
|  | 41e6c8f73e | ||
|  | a29f8d5849 | ||
|  | 9817c0807a | ||
|  | 4818baee39 | 
| @@ -16,3 +16,4 @@ sv_SE | ||||
| zh-hans_CN | ||||
| zh-hant_CN | ||||
| fi_FI | ||||
| vi_VN | ||||
|   | ||||
							
								
								
									
										62
									
								
								.env.example
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								.env.example
									
									
									
									
									
								
							| @@ -22,28 +22,30 @@ APP_KEY=SomeRandomStringOf32CharsExactly | ||||
| # If text is still in English, remember that not everything may have been translated. | ||||
| DEFAULT_LANGUAGE=en_US | ||||
|  | ||||
| # The locale defines how numbers are formatted. | ||||
| # by default this value is the same as whatever the language is. | ||||
| DEFAULT_LOCALE=equal | ||||
|  | ||||
| # Change this value to your preferred time zone. | ||||
| # Example: Europe/Amsterdam | ||||
| # For a list of supported time zones, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones | ||||
| TZ=Europe/Amsterdam | ||||
|  | ||||
| # This variable must match your installation's external address but keep in mind that | ||||
| # it's only used on the command line as a fallback value. | ||||
| APP_URL=http://localhost | ||||
|  | ||||
| # TRUSTED_PROXIES is a useful variable when using Docker and/or a reverse proxy. | ||||
| # Set it to ** and reverse proxies work just fine. | ||||
| TRUSTED_PROXIES= | ||||
|  | ||||
| # The log channel defines where your log entries go to. | ||||
| # - Docker + versions <= 4.8.1.8 and before: use "stdout" | ||||
| # - Docker + versions >  4.8.1.8: use "docker_out" | ||||
| # - For everything else, use 'daily' | ||||
|  | ||||
| # Several other options exist. You can use 'single' for one big fat error log (not recommended). | ||||
| # Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself. | ||||
| # A rotating log option is 'daily', creates 5 files that (surprise) rotate. | ||||
| # Default setting 'stack' will log to 'daily' and to 'stdout' at the same time. | ||||
|  | ||||
| # - Docker + versions <= 4.8.1.8 and before: use "stdout" | ||||
| # - Docker + versions >  4.8.1.8           : use "docker_out" | ||||
| # - Docker + versions >=  5.1.1            : use "stack" | ||||
| # - For everything else (als not Docker)   : use 'stack' | ||||
|  | ||||
| LOG_CHANNEL=stack | ||||
|  | ||||
| # Log level. You can set this from least severe to most severe: | ||||
| @@ -56,13 +58,24 @@ APP_LOG_LEVEL=notice | ||||
| # For other database types, please see the FAQ: https://docs.firefly-iii.org/support/faq | ||||
| # If you use Docker or similar, you can set these variables from a file by appending them with _FILE | ||||
| # Use "mysql" for MySQL and MariaDB. Use "sqlite" for SQLite. | ||||
| DB_CONNECTION=pgsql | ||||
| DB_HOST=firefly_iii_db | ||||
| DB_PORT=5432 | ||||
| DB_CONNECTION=mysql | ||||
| DB_HOST=fireflyiiidb | ||||
| DB_PORT=3306 | ||||
| DB_DATABASE=firefly | ||||
| DB_USERNAME=firefly | ||||
| DB_PASSWORD=secret_firefly_password | ||||
|  | ||||
| # MySQL supports SSL. You can configure it here. | ||||
| # If you use Docker or similar, you can set these variables from a file by appending them with _FILE | ||||
| MYSQL_USE_SSL=false | ||||
| MYSQL_SSL_VERIFY_SERVER_CERT=true | ||||
| # You need to set at least of these options | ||||
| MYSQL_SSL_CAPATH=/etc/ssl/certs/ | ||||
| MYSQL_SSL_CA= | ||||
| MYSQL_SSL_CERT= | ||||
| MYSQL_SSL_KEY= | ||||
| MYSQL_SSL_CIPHER= | ||||
|  | ||||
| # PostgreSQL supports SSL. You can configure it here. | ||||
| # If you use Docker or similar, you can set these variables from a file by appending them with _FILE | ||||
| PGSQL_SSL_MODE=prefer | ||||
| @@ -168,8 +181,16 @@ ADLDAP_PORT=389 | ||||
| ADLDAP_TIMEOUT=5 | ||||
| ADLDAP_BASEDN="" | ||||
| ADLDAP_FOLLOW_REFFERALS=false | ||||
|  | ||||
| # SSL/TLS settings | ||||
| ADLDAP_USE_SSL=false | ||||
| ADLDAP_USE_TLS=false | ||||
| ADLDAP_SSL_CACERTDIR= | ||||
| ADLDAP_SSL_CACERTFILE= | ||||
| ADLDAP_SSL_CERTFILE= | ||||
| ADLDAP_SSL_KEYFILE= | ||||
| ADLDAP_SSL_CIPHER_SUITE= | ||||
| ADLDAP_SSL_REQUIRE_CERT= | ||||
|  | ||||
| # You can set the following variables from a file by appending them with _FILE: | ||||
| ADLDAP_ADMIN_USERNAME= | ||||
| @@ -189,6 +210,7 @@ ADLDAP_AUTH_FIELD=distinguishedname | ||||
|  | ||||
| # Will allow SSO if your server provides an AUTH_USER field. | ||||
| # You can set the following variables from a file by appending them with _FILE: | ||||
| WINDOWS_SSO_ENABLED=false | ||||
| WINDOWS_SSO_DISCOVER=samaccountname | ||||
| WINDOWS_SSO_KEY=AUTH_USER | ||||
|  | ||||
| @@ -216,8 +238,9 @@ TRACKER_SITE_ID= | ||||
| TRACKER_URL= | ||||
|  | ||||
| # | ||||
| # Firefly III could (in the future) collect telemetry on how you use Firefly III. | ||||
| # In order to allow this, change the following variable to true: | ||||
| # Firefly III can collect telemetry on how you use Firefly III. This is opt-in. | ||||
| # In order to allow this, change the following variable to true. | ||||
| # To read more about this feature, go to this page: https://docs.firefly-iii.org/support/telemetry | ||||
| SEND_TELEMETRY=false | ||||
|  | ||||
| # You can fine tune the start-up of a Docker container by editing these environment variables. | ||||
| @@ -265,6 +288,17 @@ DEMO_USERNAME= | ||||
| DEMO_PASSWORD= | ||||
| USE_ENCRYPTION=false | ||||
| IS_SANDSTORM=false | ||||
| IS_DOCKER=false | ||||
| IS_HEROKU=false | ||||
| BUNQ_USE_SANDBOX=false | ||||
|  | ||||
| # | ||||
| # If you have trouble configuring your Firefly III installation, DON'T BOTHER setting this variable. | ||||
| # It won't work. It doesn't do ANYTHING. Don't believe the lies you read online. I'm not joking. | ||||
| # This configuration value WILL NOT HELP. | ||||
| # | ||||
| # This variable is ONLY used in some of the emails Firefly III sends around. Nowhere else. | ||||
| # So when configuring anything WEB related this variable doesn't do anything. Nothing | ||||
| # | ||||
| # If you're stuck I understand you get desperate but look SOMEWHERE ELSE. | ||||
| # | ||||
| APP_URL=http://localhost | ||||
|   | ||||
							
								
								
									
										12
									
								
								.github/ISSUE_TEMPLATE/Bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/ISSUE_TEMPLATE/Bug_report.md
									
									
									
									
										vendored
									
									
								
							| @@ -16,10 +16,10 @@ I am running Firefly III version x.x.x, and my problem is: | ||||
| <!-- Please add extra info here, such as OS, browser, and the output from the /debug page of your Firefly III installation (click the version at the bottom). --> | ||||
|  | ||||
| **Bonus points** | ||||
| <!-- Earn bonus points by checking the boxes --> | ||||
| <!-- Before you submit, verify the following please: --> | ||||
|  | ||||
| - [ ] Nobody reported this bug before | ||||
| - [ ] I have added a stack trace from my log files. | ||||
| - [ ] I have added a screenshot. | ||||
| - [ ] I was able to replicate it on the demo site https://demo.firefly-iii.org/ | ||||
| <!-- - [ ] I donated money (this is a joke :wink:)--> | ||||
| - I searched and nobody reported this bug before | ||||
| - I have added a stack trace from my log files <!-- (see https://bit.ly/FF3-get-debug-info) --> | ||||
| - I have added a screenshot. | ||||
| - I was able to replicate it on the demo site https://demo.firefly-iii.org/ | ||||
| <!-- - I donated money (this is a joke ;)--> | ||||
|   | ||||
							
								
								
									
										10
									
								
								.github/ISSUE_TEMPLATE/Custom.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/ISSUE_TEMPLATE/Custom.md
									
									
									
									
										vendored
									
									
								
							| @@ -16,8 +16,8 @@ I am running Firefly III version x.x.x. | ||||
|  | ||||
| <!-- Complete the following checklist for bonus points --> | ||||
|  | ||||
| - [ ] I have read the FAQ at https://bit.ly/FF3-FAQ | ||||
| - [ ] I added a screenshot | ||||
| - [ ] I added log files (see https://bit.ly/FF3-get-logs) | ||||
| - [ ] I was able to replicate the issue on the demo site. | ||||
| <!-- - [ ] I donated money (this is a joke :wink:)--> | ||||
| - I have read the FAQ at https://bit.ly/FF3-FAQ | ||||
| - I added a screenshot | ||||
| - I added log files <!-- (see https://bit.ly/FF3-get-debug-info) --> | ||||
| - I was able to replicate the issue on the demo site. | ||||
| <!-- - I donated money (this is a joke :wink:)--> | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/lock.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/lock.yml
									
									
									
									
										vendored
									
									
								
							| @@ -32,4 +32,4 @@ setLockReason: true | ||||
| #   daysUntilLock: 30 | ||||
|  | ||||
| # Repository to extend settings from | ||||
| # _extends: repo | ||||
| # _extends: repo | ||||
|   | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,5 @@ | ||||
| /node_modules | ||||
| /frontend/node_modules | ||||
| /public/hot | ||||
| /public/storage | ||||
| /storage/*.key | ||||
|   | ||||
| @@ -31,6 +31,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| use FireflyIII\Support\Http\Api\AccountFilter; | ||||
| use FireflyIII\Support\Http\Api\TransactionFilter; | ||||
| use FireflyIII\Transformers\AccountTransformer; | ||||
| use FireflyIII\Transformers\AttachmentTransformer; | ||||
| use FireflyIII\Transformers\PiggyBankTransformer; | ||||
| use FireflyIII\Transformers\TransactionGroupTransformer; | ||||
| use FireflyIII\User; | ||||
| @@ -73,6 +74,35 @@ class AccountController extends Controller | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     public function attachments(Account $account): JsonResponse | ||||
|     { | ||||
|         $manager    = $this->getManager(); | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $collection = $this->repository->getAttachments($account); | ||||
|  | ||||
|         $count       = $collection->count(); | ||||
|         $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|  | ||||
|         // make paginator: | ||||
|         $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $paginator->setPath(route('api.v1.accounts.attachments', [$account->id]) . $this->buildParams()); | ||||
|  | ||||
|         /** @var AttachmentTransformer $transformer */ | ||||
|         $transformer = app(AttachmentTransformer::class); | ||||
|         $transformer->setParameters($this->parameters); | ||||
|  | ||||
|         $resource = new FractalCollection($attachments, $transformer, 'attachments'); | ||||
|         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
| @@ -104,7 +134,7 @@ class AccountController extends Controller | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $types    = $this->mapAccountTypes($this->parameters->get('type')); | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of accounts. Count it and split it. | ||||
|         $collection = $this->repository->getAccountsByType($types); | ||||
| @@ -142,7 +172,7 @@ class AccountController extends Controller | ||||
|         $manager = $this->getManager(); | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         $collection = $this->repository->getPiggyBanks($account); | ||||
| @@ -218,7 +248,7 @@ class AccountController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, Account $account): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
| @@ -227,10 +257,8 @@ class AccountController extends Controller | ||||
|         if (null !== $limit && $limit > 0) { | ||||
|             $pageSize = $limit; | ||||
|         } | ||||
|  | ||||
|         $types   = $this->mapTransactionTypes($this->parameters->get('type')); | ||||
|         $manager = $this->getManager(); | ||||
|  | ||||
|         /** @var User $admin */ | ||||
|         $admin = auth()->user(); | ||||
|  | ||||
| @@ -240,7 +268,6 @@ class AccountController extends Controller | ||||
|         $collector->setUser($admin)->setAccounts(new Collection([$account])) | ||||
|                   ->withAPIInformation()->setLimit($pageSize)->setPage($this->parameters->get('page'))->setTypes($types); | ||||
|  | ||||
|         // set range if necessary: | ||||
|         if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { | ||||
|             $collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); | ||||
|         } | ||||
|   | ||||
| @@ -23,10 +23,12 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Controllers; | ||||
|  | ||||
| use FireflyIII\Api\V1\Middleware\ApiDemoUser; | ||||
| use FireflyIII\Api\V1\Requests\AttachmentStoreRequest; | ||||
| use FireflyIII\Api\V1\Requests\AttachmentUpdateRequest; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; | ||||
| use FireflyIII\Http\Middleware\IsDemoUser; | ||||
| use FireflyIII\Models\Attachment; | ||||
| use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; | ||||
| use FireflyIII\Transformers\AttachmentTransformer; | ||||
| @@ -58,6 +60,7 @@ class AttachmentController extends Controller | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->middleware(ApiDemoUser::class)->except(['delete', 'download', 'show', 'index']); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 /** @var User $user */ | ||||
| @@ -65,6 +68,7 @@ class AttachmentController extends Controller | ||||
|                 $this->repository = app(AttachmentRepositoryInterface::class); | ||||
|                 $this->repository->setUser($user); | ||||
|  | ||||
|  | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
| @@ -92,8 +96,8 @@ class AttachmentController extends Controller | ||||
|      * @param Attachment $attachment | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      * @return LaravelResponse | ||||
|      * @throws   FireflyException | ||||
|      * @return LaravelResponse | ||||
|      */ | ||||
|     public function download(Attachment $attachment): LaravelResponse | ||||
|     { | ||||
| @@ -108,7 +112,7 @@ class AttachmentController extends Controller | ||||
|             if ('' === $content) { | ||||
|                 throw new FireflyException('200002: File is empty (zero bytes).'); | ||||
|             } | ||||
|             $quoted  = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\')); | ||||
|             $quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\')); | ||||
|  | ||||
|             /** @var LaravelResponse $response */ | ||||
|             $response = response($content); | ||||
| @@ -139,7 +143,7 @@ class AttachmentController extends Controller | ||||
|         $manager = $this->getManager(); | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of accounts. Count it and split it. | ||||
|         $collection  = $this->repository->get(); | ||||
| @@ -184,8 +188,8 @@ class AttachmentController extends Controller | ||||
|      * | ||||
|      * @param AttachmentStoreRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(AttachmentStoreRequest $request): JsonResponse | ||||
|     { | ||||
|   | ||||
| @@ -92,7 +92,7 @@ class AvailableBudgetController extends Controller | ||||
|         $manager = $this->getManager(); | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         $start = $this->parameters->get('start'); | ||||
|         $end   = $this->parameters->get('end'); | ||||
|   | ||||
| @@ -85,7 +85,7 @@ class BillController extends Controller | ||||
|     public function attachments(Bill $bill): JsonResponse | ||||
|     { | ||||
|         $manager    = $this->getManager(); | ||||
|         $pageSize   = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $collection = $this->repository->getAttachments($bill); | ||||
|  | ||||
|         $count       = $collection->count(); | ||||
| @@ -130,7 +130,7 @@ class BillController extends Controller | ||||
|     { | ||||
|         $bills     = $this->repository->getBills(); | ||||
|         $manager   = $this->getManager(); | ||||
|         $pageSize  = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize  = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $count     = $bills->count(); | ||||
|         $bills     = $bills->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|         $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page')); | ||||
| @@ -158,7 +158,7 @@ class BillController extends Controller | ||||
|         $manager = $this->getManager(); | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         $collection = $this->repository->getRulesForBill($bill); | ||||
| @@ -206,8 +206,8 @@ class BillController extends Controller | ||||
|      * | ||||
|      * @param BillRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(BillRequest $request): JsonResponse | ||||
|     { | ||||
| @@ -235,7 +235,7 @@ class BillController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, Bill $bill): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
|   | ||||
| @@ -25,13 +25,15 @@ namespace FireflyIII\Api\V1\Controllers; | ||||
|  | ||||
| use Exception; | ||||
| use FireflyIII\Api\V1\Requests\BudgetLimitRequest; | ||||
| use FireflyIII\Api\V1\Requests\BudgetRequest; | ||||
| use FireflyIII\Api\V1\Requests\BudgetStoreRequest; | ||||
| use FireflyIII\Api\V1\Requests\BudgetUpdateRequest; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; | ||||
| use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; | ||||
| use FireflyIII\Support\Http\Api\TransactionFilter; | ||||
| use FireflyIII\Transformers\AttachmentTransformer; | ||||
| use FireflyIII\Transformers\BudgetLimitTransformer; | ||||
| use FireflyIII\Transformers\BudgetTransformer; | ||||
| use FireflyIII\Transformers\TransactionGroupTransformer; | ||||
| @@ -89,7 +91,7 @@ class BudgetController extends Controller | ||||
|     public function budgetLimits(Budget $budget): JsonResponse | ||||
|     { | ||||
|         $manager  = $this->getManager(); | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $this->parameters->set('budget_id', $budget->id); | ||||
|         $collection   = $this->blRepository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end')); | ||||
|         $count        = $collection->count(); | ||||
| @@ -108,6 +110,35 @@ class BudgetController extends Controller | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Budget $budget | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     public function attachments(Budget $budget): JsonResponse | ||||
|     { | ||||
|         $manager    = $this->getManager(); | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $collection = $this->repository->getAttachments($budget); | ||||
|  | ||||
|         $count       = $collection->count(); | ||||
|         $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|  | ||||
|         // make paginator: | ||||
|         $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $paginator->setPath(route('api.v1.budgets.attachments', [$budget->id]) . $this->buildParams()); | ||||
|  | ||||
|         /** @var AttachmentTransformer $transformer */ | ||||
|         $transformer = app(AttachmentTransformer::class); | ||||
|         $transformer->setParameters($this->parameters); | ||||
|  | ||||
|         $resource = new FractalCollection($attachments, $transformer, 'attachments'); | ||||
|         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
| @@ -134,7 +165,7 @@ class BudgetController extends Controller | ||||
|         $manager = $this->getManager(); | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         $collection = $this->repository->getBudgets(); | ||||
| @@ -179,13 +210,13 @@ class BudgetController extends Controller | ||||
|     /** | ||||
|      * Store a budget. | ||||
|      * | ||||
|      * @param BudgetRequest $request | ||||
|      * @param BudgetStoreRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(BudgetRequest $request): JsonResponse | ||||
|     public function store(BudgetStoreRequest $request): JsonResponse | ||||
|     { | ||||
|         $budget  = $this->repository->store($request->getAll()); | ||||
|         $manager = $this->getManager(); | ||||
| @@ -205,8 +236,8 @@ class BudgetController extends Controller | ||||
|      * @param BudgetLimitRequest $request | ||||
|      * @param Budget             $budget | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws Exception | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function storeBudgetLimit(BudgetLimitRequest $request, Budget $budget): JsonResponse | ||||
|     { | ||||
| @@ -236,7 +267,7 @@ class BudgetController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, Budget $budget): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // user can overrule page size with limit parameter. | ||||
|         $limit = $this->parameters->get('limit'); | ||||
| @@ -291,12 +322,12 @@ class BudgetController extends Controller | ||||
|     /** | ||||
|      * Update a budget. | ||||
|      * | ||||
|      * @param BudgetRequest $request | ||||
|      * @param Budget        $budget | ||||
|      * @param BudgetUpdateRequest $request | ||||
|      * @param Budget              $budget | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function update(BudgetRequest $request, Budget $budget): JsonResponse | ||||
|     public function update(BudgetUpdateRequest $request, Budget $budget): JsonResponse | ||||
|     { | ||||
|         $data    = $request->getAll(); | ||||
|         $budget  = $this->repository->update($budget, $data); | ||||
|   | ||||
| @@ -102,9 +102,9 @@ class BudgetLimitController extends Controller | ||||
|     public function index(Request $request): JsonResponse | ||||
|     { | ||||
|         $manager  = $this->getManager(); | ||||
|         $budgetId = (int)($request->get('budget_id') ?? 0); | ||||
|         $budgetId = (int) ($request->get('budget_id') ?? 0); | ||||
|         $budget   = $this->repository->findNull($budgetId); | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $this->parameters->set('budget_id', $budgetId); | ||||
|  | ||||
|         $collection = new Collection; | ||||
| @@ -156,16 +156,16 @@ class BudgetLimitController extends Controller | ||||
|      * | ||||
|      * @param BudgetLimitRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(BudgetLimitRequest $request): JsonResponse | ||||
|     { | ||||
|         $data   = $request->getAll(); | ||||
|         $budget = $this->repository->findNull($data['budget_id']); | ||||
|         if (null === $budget) { | ||||
|             throw new FireflyException('200004: Budget does not exist.'); | ||||
|             throw new FireflyException('200004: Budget does not exist.'); // @codeCoverageIgnore | ||||
|         } | ||||
|         $data['budget'] = $budget; | ||||
|         $budgetLimit    = $this->blRepository->storeBudgetLimit($data); | ||||
| @@ -191,7 +191,7 @@ class BudgetLimitController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, BudgetLimit $budgetLimit): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
|   | ||||
| @@ -29,6 +29,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Models\Category; | ||||
| use FireflyIII\Repositories\Category\CategoryRepositoryInterface; | ||||
| use FireflyIII\Support\Http\Api\TransactionFilter; | ||||
| use FireflyIII\Transformers\AttachmentTransformer; | ||||
| use FireflyIII\Transformers\CategoryTransformer; | ||||
| use FireflyIII\Transformers\TransactionGroupTransformer; | ||||
| use FireflyIII\User; | ||||
| @@ -71,6 +72,35 @@ class CategoryController extends Controller | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Category $category | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     public function attachments(Category $category): JsonResponse | ||||
|     { | ||||
|         $manager    = $this->getManager(); | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $collection = $this->repository->getAttachments($category); | ||||
|  | ||||
|         $count       = $collection->count(); | ||||
|         $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|  | ||||
|         // make paginator: | ||||
|         $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $paginator->setPath(route('api.v1.categories.attachments', [$category->id]) . $this->buildParams()); | ||||
|  | ||||
|         /** @var AttachmentTransformer $transformer */ | ||||
|         $transformer = app(AttachmentTransformer::class); | ||||
|         $transformer->setParameters($this->parameters); | ||||
|  | ||||
|         $resource = new FractalCollection($attachments, $transformer, 'attachments'); | ||||
|         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the specified resource from storage. | ||||
|      * | ||||
| @@ -97,7 +127,7 @@ class CategoryController extends Controller | ||||
|         $manager = $this->getManager(); | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         $collection = $this->repository->getCategories(); | ||||
| @@ -146,13 +176,13 @@ class CategoryController extends Controller | ||||
|      * | ||||
|      * @param CategoryRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(CategoryRequest $request): JsonResponse | ||||
|     { | ||||
|         $category = $this->repository->store($request->getAll()); | ||||
|         $manager = $this->getManager(); | ||||
|         $manager  = $this->getManager(); | ||||
|  | ||||
|         /** @var CategoryTransformer $transformer */ | ||||
|         $transformer = app(CategoryTransformer::class); | ||||
| @@ -175,7 +205,7 @@ class CategoryController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, Category $category): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
|   | ||||
| @@ -99,10 +99,10 @@ class AccountController extends Controller | ||||
|  | ||||
|         // loop the end balances. This is an array for each account ($expenses) | ||||
|         foreach ($endBalances as $accountId => $expenses) { | ||||
|             $accountId = (int)$accountId; | ||||
|             $accountId = (int) $accountId; | ||||
|             // loop each expense entry (each entry can be a different currency). | ||||
|             foreach ($expenses as $currencyId => $endAmount) { | ||||
|                 $currencyId = (int)$currencyId; | ||||
|                 $currencyId = (int) $currencyId; | ||||
|  | ||||
|                 // see if there is an accompanying start amount. | ||||
|                 // grab the difference and find the currency. | ||||
| @@ -114,7 +114,7 @@ class AccountController extends Controller | ||||
|                     $tempData[] = [ | ||||
|                         'name'        => $accountNames[$accountId], | ||||
|                         'difference'  => $diff, | ||||
|                         'diff_float'  => (float)$diff, | ||||
|                         'diff_float'  => (float) $diff, | ||||
|                         'currency_id' => $currencyId, | ||||
|                     ]; | ||||
|                 } | ||||
| @@ -247,10 +247,10 @@ class AccountController extends Controller | ||||
|  | ||||
|         // loop the end balances. This is an array for each account ($expenses) | ||||
|         foreach ($endBalances as $accountId => $expenses) { | ||||
|             $accountId = (int)$accountId; | ||||
|             $accountId = (int) $accountId; | ||||
|             // loop each expense entry (each entry can be a different currency). | ||||
|             foreach ($expenses as $currencyId => $endAmount) { | ||||
|                 $currencyId = (int)$currencyId; | ||||
|                 $currencyId = (int) $currencyId; | ||||
|  | ||||
|                 // see if there is an accompanying start amount. | ||||
|                 // grab the difference and find the currency. | ||||
| @@ -263,7 +263,7 @@ class AccountController extends Controller | ||||
|                         'name'        => $accountNames[$accountId], | ||||
|                         'difference'  => bcmul($diff, '-1'), | ||||
|                         //  For some reason this line is never covered in code coverage: | ||||
|                         'diff_float'  => ((float)$diff) * -1, // @codeCoverageIgnore | ||||
|                         'diff_float'  => ((float) $diff) * -1, // @codeCoverageIgnore | ||||
|                         'currency_id' => $currencyId, | ||||
|                     ]; | ||||
|                 } | ||||
|   | ||||
| @@ -83,9 +83,9 @@ class AvailableBudgetController extends Controller | ||||
|                 $spent = $spentInfo['amount']; | ||||
|             } | ||||
|         } | ||||
|         $left = bcadd($availableBudget->amount, (string)$spent); | ||||
|         $left = bcadd($availableBudget->amount, (string) $spent); | ||||
|         // left less than zero? Set to zero. | ||||
|         if (bccomp($left, '0') === -1) { | ||||
|         if (-1 === bccomp($left, '0')) { | ||||
|             $left = '0'; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -90,8 +90,8 @@ class CategoryController extends Controller | ||||
|         $tempData      = []; | ||||
|         $spentWith     = $this->opsRepository->listExpenses($start, $end); | ||||
|         $earnedWith    = $this->opsRepository->listIncome($start, $end); | ||||
|         $spentWithout  = $this->noCatRepository->listExpenses($start, $end); // refactored | ||||
|         $earnedWithout = $this->noCatRepository->listIncome($start, $end); // refactored | ||||
|         $spentWithout  = $this->noCatRepository->listExpenses($start, $end); | ||||
|         $earnedWithout = $this->noCatRepository->listIncome($start, $end); | ||||
|         $categories    = []; | ||||
|  | ||||
|  | ||||
| @@ -104,7 +104,7 @@ class CategoryController extends Controller | ||||
|                     // make data arrays if not yet present. | ||||
|                     $tempData[$inKey]  = $tempData[$inKey] ?? [ | ||||
|                             'currency_id'             => $currency['currency_id'], | ||||
|                             'label'                   => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), | ||||
|                             'label'                   => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), | ||||
|                             'currency_code'           => $currency['currency_code'], | ||||
|                             'currency_symbol'         => $currency['currency_symbol'], | ||||
|                             'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
| @@ -117,7 +117,7 @@ class CategoryController extends Controller | ||||
|                         ]; | ||||
|                     $tempData[$outKey] = $tempData[$outKey] ?? [ | ||||
|                             'currency_id'             => $currency['currency_id'], | ||||
|                             'label'                   => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), | ||||
|                             'label'                   => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), | ||||
|                             'currency_code'           => $currency['currency_code'], | ||||
|                             'currency_symbol'         => $currency['currency_symbol'], | ||||
|                             'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
| @@ -141,48 +141,48 @@ class CategoryController extends Controller | ||||
|             } | ||||
|         } | ||||
|  | ||||
| //        foreach ([] as $set) { | ||||
| //            foreach ($set as $currency) { | ||||
| //                $inKey        = sprintf('%d-i', $currency['currency_id']); | ||||
| //                $outKey       = sprintf('%d-e', $currency['currency_id']); | ||||
| //                $categories[] = (string)trans('firefly.no_category'); | ||||
| //                // make data arrays if not yet present. | ||||
| //                $tempData[$inKey]  = $tempData[$inKey] ?? [ | ||||
| //                        'currency_id'             => $currency['currency_id'], | ||||
| //                        'label'                   => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), | ||||
| //                        'currency_code'           => $currency['currency_code'], | ||||
| //                        'currency_symbol'         => $currency['currency_symbol'], | ||||
| //                        'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
| //                        'type'                    => 'bar', // line, area or bar | ||||
| //                        'yAxisID'                 => 0, // 0, 1, 2 | ||||
| //                        'entries'                 => [ | ||||
| //                            // per category: | ||||
| //                            // "category" => 5, | ||||
| //                        ], | ||||
| //                    ]; | ||||
| //                $tempData[$outKey] = $tempData[$outKey] ?? [ | ||||
| //                        'currency_id'             => $currency['currency_id'], | ||||
| //                        'label'                   => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), | ||||
| //                        'currency_code'           => $currency['currency_code'], | ||||
| //                        'currency_symbol'         => $currency['currency_symbol'], | ||||
| //                        'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
| //                        'type'                    => 'bar', // line, area or bar | ||||
| //                        'yAxisID'                 => 0, // 0, 1, 2 | ||||
| //                        'entries'                 => [ | ||||
| //                            // per category: | ||||
| //                            // "category" => 5, | ||||
| //                        ], | ||||
| //                    ]; | ||||
| //                foreach ($currency['transaction_journals'] as $journal) { | ||||
| //                    // is it expense or income? | ||||
| //                    $letter                                  = -1 === bccomp($journal['amount'], '0') ? 'e' : 'i'; | ||||
| //                    $currentKey                              = sprintf('%d-%s', $currency['currency_id'], $letter); | ||||
| //                    $name                                    = (string)trans('firefly.no_category'); | ||||
| //                    $tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0'; | ||||
| //                    $tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']); | ||||
| //                } | ||||
| //            } | ||||
| //        } | ||||
|         //        foreach ([] as $set) { | ||||
|         //            foreach ($set as $currency) { | ||||
|         //                $inKey        = sprintf('%d-i', $currency['currency_id']); | ||||
|         //                $outKey       = sprintf('%d-e', $currency['currency_id']); | ||||
|         //                $categories[] = (string)trans('firefly.no_category'); | ||||
|         //                // make data arrays if not yet present. | ||||
|         //                $tempData[$inKey]  = $tempData[$inKey] ?? [ | ||||
|         //                        'currency_id'             => $currency['currency_id'], | ||||
|         //                        'label'                   => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]), | ||||
|         //                        'currency_code'           => $currency['currency_code'], | ||||
|         //                        'currency_symbol'         => $currency['currency_symbol'], | ||||
|         //                        'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|         //                        'type'                    => 'bar', // line, area or bar | ||||
|         //                        'yAxisID'                 => 0, // 0, 1, 2 | ||||
|         //                        'entries'                 => [ | ||||
|         //                            // per category: | ||||
|         //                            // "category" => 5, | ||||
|         //                        ], | ||||
|         //                    ]; | ||||
|         //                $tempData[$outKey] = $tempData[$outKey] ?? [ | ||||
|         //                        'currency_id'             => $currency['currency_id'], | ||||
|         //                        'label'                   => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), | ||||
|         //                        'currency_code'           => $currency['currency_code'], | ||||
|         //                        'currency_symbol'         => $currency['currency_symbol'], | ||||
|         //                        'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|         //                        'type'                    => 'bar', // line, area or bar | ||||
|         //                        'yAxisID'                 => 0, // 0, 1, 2 | ||||
|         //                        'entries'                 => [ | ||||
|         //                            // per category: | ||||
|         //                            // "category" => 5, | ||||
|         //                        ], | ||||
|         //                    ]; | ||||
|         //                foreach ($currency['transaction_journals'] as $journal) { | ||||
|         //                    // is it expense or income? | ||||
|         //                    $letter                                  = -1 === bccomp($journal['amount'], '0') ? 'e' : 'i'; | ||||
|         //                    $currentKey                              = sprintf('%d-%s', $currency['currency_id'], $letter); | ||||
|         //                    $name                                    = (string)trans('firefly.no_category'); | ||||
|         //                    $tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0'; | ||||
|         //                    $tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']); | ||||
|         //                } | ||||
|         //            } | ||||
|         //        } | ||||
|  | ||||
|         // re-sort every spent array and add 0 for missing entries. | ||||
|         foreach ($tempData as $index => $set) { | ||||
|   | ||||
| @@ -51,7 +51,6 @@ class ConfigurationController extends Controller | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 /** @noinspection UnusedConstructorDependenciesInspection */ | ||||
|                 $this->repository = app(UserRepositoryInterface::class); | ||||
|                 /** @var User $admin */ | ||||
|                 $admin = auth()->user(); | ||||
| @@ -109,13 +108,12 @@ class ConfigurationController extends Controller | ||||
|         $lastCheck = app('fireflyconfig')->get('last_update_check'); | ||||
|         /** @var Configuration $singleUser */ | ||||
|         $singleUser = app('fireflyconfig')->get('single_user_mode'); | ||||
|         $data       = [ | ||||
|  | ||||
|         return [ | ||||
|             'is_demo_site'            => null === $isDemoSite ? null : $isDemoSite->data, | ||||
|             'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data, | ||||
|             'last_update_check'       => null === $lastCheck ? null : (int)$lastCheck->data, | ||||
|             'permission_update_check' => null === $updateCheck ? null : (int) $updateCheck->data, | ||||
|             'last_update_check'       => null === $lastCheck ? null : (int) $lastCheck->data, | ||||
|             'single_user_mode'        => null === $singleUser ? null : $singleUser->data, | ||||
|         ]; | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -104,7 +104,7 @@ class Controller extends BaseController | ||||
|     private function getParameters(): ParameterBag | ||||
|     { | ||||
|         $bag  = new ParameterBag; | ||||
|         $page = (int)request()->get('page'); | ||||
|         $page = (int) request()->get('page'); | ||||
|         if (0 === $page) { | ||||
|             $page = 1; | ||||
|         } | ||||
| @@ -131,7 +131,7 @@ class Controller extends BaseController | ||||
|         foreach ($integers as $integer) { | ||||
|             $value = request()->query->get($integer); | ||||
|             if (null !== $value) { | ||||
|                 $bag->set($integer, (int)$value); | ||||
|                 $bag->set($integer, (int) $value); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -116,7 +116,7 @@ class CurrencyController extends Controller | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $types    = $this->mapAccountTypes($this->parameters->get('type')); | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of accounts. Count it and split it. | ||||
|         /** @var AccountRepositoryInterface $accountRepository */ | ||||
| @@ -126,7 +126,7 @@ class CurrencyController extends Controller | ||||
|         // filter list on currency preference: | ||||
|         $collection = $unfiltered->filter( | ||||
|             static function (Account $account) use ($currency, $accountRepository) { | ||||
|                 $currencyId = (int)$accountRepository->getMetaValue($account, 'currency_id'); | ||||
|                 $currencyId = (int) $accountRepository->getMetaValue($account, 'currency_id'); | ||||
|  | ||||
|                 return $currencyId === $currency->id; | ||||
|             } | ||||
| @@ -165,7 +165,7 @@ class CurrencyController extends Controller | ||||
|  | ||||
|         $manager = $this->getManager(); | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of available budgets. Count it and split it. | ||||
|  | ||||
| @@ -209,7 +209,7 @@ class CurrencyController extends Controller | ||||
|  | ||||
|         /** @var BillRepositoryInterface $repository */ | ||||
|         $repository = app(BillRepositoryInterface::class); | ||||
|         $pageSize   = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $unfiltered = $repository->getBills(); | ||||
|  | ||||
|         // filter and paginate list: | ||||
| @@ -249,7 +249,7 @@ class CurrencyController extends Controller | ||||
|         $blRepository = app(BudgetLimitRepositoryInterface::class); | ||||
|  | ||||
|         $manager      = $this->getManager(); | ||||
|         $pageSize     = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize     = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $collection   = $blRepository->getAllBudgetLimitsByCurrency($currency, $this->parameters->get('start'), $this->parameters->get('end')); | ||||
|         $count        = $collection->count(); | ||||
|         $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
| @@ -278,7 +278,7 @@ class CurrencyController extends Controller | ||||
|     { | ||||
|         // create some objects: | ||||
|         $manager    = $this->getManager(); | ||||
|         $pageSize   = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $collection = $this->repository->getExchangeRates($currency); | ||||
|  | ||||
|  | ||||
| @@ -302,9 +302,9 @@ class CurrencyController extends Controller | ||||
|      * | ||||
|      * @param TransactionCurrency $currency | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @codeCoverageIgnore | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function delete(TransactionCurrency $currency): JsonResponse | ||||
|     { | ||||
| @@ -391,7 +391,7 @@ class CurrencyController extends Controller | ||||
|      */ | ||||
|     public function index(): JsonResponse | ||||
|     { | ||||
|         $pageSize   = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $collection = $this->repository->getAll(); | ||||
|         $count      = $collection->count(); | ||||
|         // slice them: | ||||
| @@ -455,7 +455,7 @@ class CurrencyController extends Controller | ||||
|     { | ||||
|         $manager = $this->getManager(); | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         /** @var RecurringRepositoryInterface $repository */ | ||||
| @@ -506,7 +506,7 @@ class CurrencyController extends Controller | ||||
|     public function rules(TransactionCurrency $currency): JsonResponse | ||||
|     { | ||||
|         $manager  = $this->getManager(); | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         /** @var RuleRepositoryInterface $repository */ | ||||
| @@ -567,13 +567,33 @@ class CurrencyController extends Controller | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Show a currency. | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     public function showDefault(): JsonResponse | ||||
|     { | ||||
|         $manager  = $this->getManager(); | ||||
|         $currency = app('amount')->getDefaultCurrencyByUser(auth()->user()); | ||||
|  | ||||
|         /** @var CurrencyTransformer $transformer */ | ||||
|         $transformer = app(CurrencyTransformer::class); | ||||
|         $transformer->setParameters($this->parameters); | ||||
|  | ||||
|         $resource = new Item($currency, $transformer, 'currencies'); | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store new currency. | ||||
|      * | ||||
|      * @param CurrencyRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(CurrencyRequest $request): JsonResponse | ||||
|     { | ||||
| @@ -607,7 +627,7 @@ class CurrencyController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, TransactionCurrency $currency): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
|   | ||||
| @@ -69,8 +69,8 @@ class CurrencyExchangeRateController extends Controller | ||||
|      * | ||||
|      * @param Request $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function index(Request $request): JsonResponse | ||||
|     { | ||||
|   | ||||
| @@ -40,6 +40,9 @@ use League\Fractal\Resource\Item; | ||||
|  | ||||
| /** | ||||
|  * Class ImportController | ||||
|  * | ||||
|  * @deprecated | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class ImportController extends Controller | ||||
| { | ||||
| @@ -75,7 +78,7 @@ class ImportController extends Controller | ||||
|     { | ||||
|         // create some objects: | ||||
|         $manager  = $this->getManager(); | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of accounts. Count it and split it. | ||||
|         $collection = $this->repository->get(); | ||||
| @@ -125,7 +128,7 @@ class ImportController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, ImportJob $importJob): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
|   | ||||
| @@ -79,9 +79,9 @@ class LinkTypeController extends Controller | ||||
|      * | ||||
|      * @param LinkType $linkType | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @codeCoverageIgnore | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function delete(LinkType $linkType): JsonResponse | ||||
|     { | ||||
| @@ -103,7 +103,7 @@ class LinkTypeController extends Controller | ||||
|     { | ||||
|         // create some objects: | ||||
|         $manager  = $this->getManager(); | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of accounts. Count it and split it. | ||||
|         $collection = $this->repository->get(); | ||||
| @@ -151,8 +151,8 @@ class LinkTypeController extends Controller | ||||
|      * | ||||
|      * @param LinkTypeRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(LinkTypeRequest $request): JsonResponse | ||||
|     { | ||||
| @@ -187,7 +187,7 @@ class LinkTypeController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, LinkType $linkType): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
| @@ -241,8 +241,8 @@ class LinkTypeController extends Controller | ||||
|      * @param LinkTypeRequest $request | ||||
|      * @param LinkType        $linkType | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function update(LinkTypeRequest $request, LinkType $linkType): JsonResponse | ||||
|     { | ||||
|   | ||||
| @@ -27,6 +27,7 @@ use FireflyIII\Api\V1\Requests\PiggyBankRequest; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Models\PiggyBank; | ||||
| use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; | ||||
| use FireflyIII\Transformers\AttachmentTransformer; | ||||
| use FireflyIII\Transformers\PiggyBankEventTransformer; | ||||
| use FireflyIII\Transformers\PiggyBankTransformer; | ||||
| use FireflyIII\User; | ||||
| @@ -83,6 +84,36 @@ class PiggyBankController extends Controller | ||||
|         return response()->json([], 204); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param PiggyBank $piggyBank | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     public function attachments(PiggyBank $piggyBank): JsonResponse | ||||
|     { | ||||
|         $manager    = $this->getManager(); | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $collection = $this->repository->getAttachments($piggyBank); | ||||
|  | ||||
|         $count       = $collection->count(); | ||||
|         $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|  | ||||
|         // make paginator: | ||||
|         $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $paginator->setPath(route('api.v1.piggy_banks.attachments', [$piggyBank->id]) . $this->buildParams()); | ||||
|  | ||||
|         /** @var AttachmentTransformer $transformer */ | ||||
|         $transformer = app(AttachmentTransformer::class); | ||||
|         $transformer->setParameters($this->parameters); | ||||
|  | ||||
|         $resource = new FractalCollection($attachments, $transformer, 'attachments'); | ||||
|         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * List all of them. | ||||
|      * | ||||
| @@ -93,7 +124,7 @@ class PiggyBankController extends Controller | ||||
|     { | ||||
|         $manager = $this->getManager(); | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         $collection = $this->repository->getPiggyBanks(); | ||||
| @@ -126,7 +157,7 @@ class PiggyBankController extends Controller | ||||
|     public function piggyBankEvents(PiggyBank $piggyBank): JsonResponse | ||||
|     { | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $manager  = $this->getManager(); | ||||
|  | ||||
|         $collection = $this->repository->getEvents($piggyBank); | ||||
| @@ -175,13 +206,13 @@ class PiggyBankController extends Controller | ||||
|      * | ||||
|      * @param PiggyBankRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(PiggyBankRequest $request): JsonResponse | ||||
|     { | ||||
|         $piggyBank = $this->repository->store($request->getAll()); | ||||
|         $manager = $this->getManager(); | ||||
|         $manager   = $this->getManager(); | ||||
|  | ||||
|         /** @var PiggyBankTransformer $transformer */ | ||||
|         $transformer = app(PiggyBankTransformer::class); | ||||
|   | ||||
| @@ -147,10 +147,10 @@ class PreferenceController extends Controller | ||||
|                 $newValue = explode(',', $data['data']); | ||||
|                 break; | ||||
|             case 'listPageSize': | ||||
|                 $newValue = (int)$data['data']; | ||||
|                 $newValue = (int) $data['data']; | ||||
|                 break; | ||||
|             case 'customFiscalYear': | ||||
|                 $newValue = 1 === (int)$data['data']; | ||||
|                 $newValue = 1 === (int) $data['data']; | ||||
|                 break; | ||||
|         } | ||||
|         $result = app('preferences')->set($preference->name, $newValue); | ||||
|   | ||||
| @@ -99,7 +99,7 @@ class RecurrenceController extends Controller | ||||
|         $manager = $this->getManager(); | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         $collection = $this->repository->getAll(); | ||||
| @@ -149,8 +149,8 @@ class RecurrenceController extends Controller | ||||
|      * | ||||
|      * @param RecurrenceStoreRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(RecurrenceStoreRequest $request): JsonResponse | ||||
|     { | ||||
| @@ -178,7 +178,7 @@ class RecurrenceController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, Recurrence $recurrence): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
| @@ -224,9 +224,9 @@ class RecurrenceController extends Controller | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @codeCoverageIgnore | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function trigger(): JsonResponse | ||||
|     { | ||||
|   | ||||
| @@ -105,7 +105,7 @@ class RuleController extends Controller | ||||
|         $manager = $this->getManager(); | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         $collection = $this->ruleRepository->getAll(); | ||||
| @@ -214,12 +214,12 @@ class RuleController extends Controller | ||||
|      * @param RuleTestRequest $request | ||||
|      * @param Rule            $rule | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function testRule(RuleTestRequest $request, Rule $rule): JsonResponse | ||||
|     { | ||||
|         $pageSize   = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $parameters = $request->getTestParameters(); | ||||
|         /** @var Rule $rule */ | ||||
|         Log::debug(sprintf('Now testing rule #%d, "%s"', $rule->id, $rule->title)); | ||||
|   | ||||
| @@ -106,7 +106,7 @@ class RuleGroupController extends Controller | ||||
|     { | ||||
|         $manager = $this->getManager(); | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of rule groups. Count it and split it. | ||||
|         $collection = $this->ruleGroupRepository->get(); | ||||
| @@ -177,7 +177,7 @@ class RuleGroupController extends Controller | ||||
|     { | ||||
|         $manager = $this->getManager(); | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         $collection = $this->ruleGroupRepository->getRules($group); | ||||
| @@ -246,13 +246,13 @@ class RuleGroupController extends Controller | ||||
|      * @param RuleGroupTestRequest $request | ||||
|      * @param RuleGroup            $group | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function testGroup(RuleGroupTestRequest $request, RuleGroup $group): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         Log::debug('Now in testGroup()'); | ||||
|         /** @var Collection $rules */ | ||||
|         $rules = $this->ruleGroupRepository->getActiveRules($group); | ||||
| @@ -305,8 +305,8 @@ class RuleGroupController extends Controller | ||||
|      * @param RuleGroupTriggerRequest $request | ||||
|      * @param RuleGroup               $group | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws Exception | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function triggerGroup(RuleGroupTriggerRequest $request, RuleGroup $group): JsonResponse | ||||
|     { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * AccountController.php | ||||
|  * Copyright (c) 2019 james@firefly-iii.org | ||||
| @@ -21,7 +22,6 @@ | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Controllers\Search; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Api\V1\Controllers\Controller; | ||||
| use FireflyIII\Support\Http\Api\AccountFilter; | ||||
| use FireflyIII\Support\Search\AccountSearch; | ||||
| @@ -97,5 +97,4 @@ class AccountController extends Controller | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * TransactionController.php | ||||
|  * Copyright (c) 2019 james@firefly-iii.org | ||||
| @@ -21,13 +22,8 @@ | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Controllers\Search; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Api\V1\Controllers\Controller; | ||||
| use FireflyIII\Support\Search\SearchInterface; | ||||
| use FireflyIII\Support\Search\TransactionSearch; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Http\Response; | ||||
|  | ||||
| /** | ||||
|  * Class TransactionController | ||||
| @@ -35,13 +31,13 @@ use Illuminate\Http\Response; | ||||
| class TransactionController extends Controller | ||||
| { | ||||
|     /** @var string */ | ||||
|     const SEARCH_ALL = 'all'; | ||||
|     public const SEARCH_ALL = 'all'; | ||||
|     /** @var string */ | ||||
|     const SEARCH_DESCRIPTION = 'description'; | ||||
|     public const SEARCH_DESCRIPTION = 'description'; | ||||
|     /** @var string */ | ||||
|     const SEARCH_NOTES = 'notes'; | ||||
|     public const SEARCH_NOTES = 'notes'; | ||||
|     /** @var string */ | ||||
|     const SEARCH_ACCOUNTS = 'accounts'; | ||||
|     public const SEARCH_ACCOUNTS = 'accounts'; | ||||
|     /** @var array */ | ||||
|     private $validFields; | ||||
|  | ||||
| @@ -59,11 +55,10 @@ class TransactionController extends Controller | ||||
|     /** | ||||
|      * @param Request $request | ||||
|      * | ||||
|      * @return JsonResponse|Response | ||||
|      * @return void | ||||
|      */ | ||||
|     public function search(Request $request) | ||||
|     public function search(Request $request): void | ||||
|     { | ||||
|         die('the route is present but nobody\'s home.'); | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * TransferController.php | ||||
|  * Copyright (c) 2019 james@firefly-iii.org | ||||
| @@ -21,16 +22,13 @@ | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Controllers\Search; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Api\V1\Controllers\Controller; | ||||
| use FireflyIII\Api\V1\Requests\Search\TransferRequest; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Support\Search\TransferSearch; | ||||
| use FireflyIII\Transformers\TransactionGroupTransformer; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Http\Response; | ||||
| use League\Fractal\Pagination\IlluminatePaginatorAdapter; | ||||
| use League\Fractal\Resource\Collection as FractalCollection; | ||||
| @@ -41,10 +39,9 @@ use League\Fractal\Resource\Collection as FractalCollection; | ||||
| class TransferController extends Controller | ||||
| { | ||||
|     /** | ||||
|      * @param Request $request | ||||
|      * @param TransferRequest $request | ||||
|      * | ||||
|      * @return JsonResponse|Response | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     public function search(TransferRequest $request) | ||||
|     { | ||||
| @@ -82,7 +79,7 @@ class TransferController extends Controller | ||||
|         } | ||||
|  | ||||
|         // collector to return results. | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $manager  = $this->getManager(); | ||||
|         /** @var User $admin */ | ||||
|         $admin = auth()->user(); | ||||
| @@ -113,4 +110,4 @@ class TransferController extends Controller | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,6 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Controllers; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Exception; | ||||
| use FireflyIII\Api\V1\Requests\DateRequest; | ||||
| @@ -42,7 +41,6 @@ use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; | ||||
| use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Class SummaryController | ||||
| @@ -98,8 +96,8 @@ class SummaryController extends Controller | ||||
|     /** | ||||
|      * @param DateRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws Exception | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function basic(DateRequest $request): JsonResponse | ||||
|     { | ||||
| @@ -125,7 +123,6 @@ class SummaryController extends Controller | ||||
|         } | ||||
|  | ||||
|         return response()->json($return); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -152,25 +149,6 @@ class SummaryController extends Controller | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method will scroll through the results of the spentInPeriodMc() array and return the correct info. | ||||
|      * | ||||
|      * @param array               $spentInfo | ||||
|      * @param TransactionCurrency $currency | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private function findInSpentArray(array $spentInfo, TransactionCurrency $currency): string | ||||
|     { | ||||
|         foreach ($spentInfo as $array) { | ||||
|             if ($array['currency_id'] === $currency->id) { | ||||
|                 return (string)$array['amount']; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return '0'; // @codeCoverageIgnore | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
| @@ -198,8 +176,7 @@ class SummaryController extends Controller | ||||
|         $set = $collector->getExtractedJournals(); | ||||
|         /** @var array $transactionJournal */ | ||||
|         foreach ($set as $transactionJournal) { | ||||
|  | ||||
|             $currencyId           = (int)$transactionJournal['currency_id']; | ||||
|             $currencyId           = (int) $transactionJournal['currency_id']; | ||||
|             $incomes[$currencyId] = $incomes[$currencyId] ?? '0'; | ||||
|             $incomes[$currencyId] = bcadd($incomes[$currencyId], bcmul($transactionJournal['amount'], '-1')); | ||||
|             $sums[$currencyId]    = $sums[$currencyId] ?? '0'; | ||||
| @@ -221,7 +198,7 @@ class SummaryController extends Controller | ||||
|  | ||||
|         /** @var array $transactionJournal */ | ||||
|         foreach ($set as $transactionJournal) { | ||||
|             $currencyId            = (int)$transactionJournal['currency_id']; | ||||
|             $currencyId            = (int) $transactionJournal['currency_id']; | ||||
|             $expenses[$currencyId] = $expenses[$currencyId] ?? '0'; | ||||
|             $expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']); | ||||
|             $sums[$currencyId]     = $sums[$currencyId] ?? '0'; | ||||
| @@ -295,7 +272,7 @@ class SummaryController extends Controller | ||||
|         $return       = []; | ||||
|         foreach ($paidAmount as $currencyId => $amount) { | ||||
|             $amount   = bcmul($amount, '-1'); | ||||
|             $currency = $this->currencyRepos->findNull((int)$currencyId); | ||||
|             $currency = $this->currencyRepos->findNull((int) $currencyId); | ||||
|             if (null === $currency) { | ||||
|                 continue; | ||||
|             } | ||||
| @@ -315,7 +292,7 @@ class SummaryController extends Controller | ||||
|  | ||||
|         foreach ($unpaidAmount as $currencyId => $amount) { | ||||
|             $amount   = bcmul($amount, '-1'); | ||||
|             $currency = $this->currencyRepos->findNull((int)$currencyId); | ||||
|             $currency = $this->currencyRepos->findNull((int) $currencyId); | ||||
|             if (null === $currency) { | ||||
|                 continue; | ||||
|             } | ||||
| @@ -340,8 +317,8 @@ class SummaryController extends Controller | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return array | ||||
|      * @throws Exception | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getLeftToSpendInfo(Carbon $start, Carbon $end): array | ||||
|     { | ||||
| @@ -360,7 +337,7 @@ class SummaryController extends Controller | ||||
|             $days   = $today->diffInDays($end) + 1; | ||||
|             $perDay = '0'; | ||||
|             if (0 !== $days && bccomp($leftToSpend, '0') > -1) { | ||||
|                 $perDay = bcdiv($leftToSpend, (string)$days); | ||||
|                 $perDay = bcdiv($leftToSpend, (string) $days); | ||||
|             } | ||||
|  | ||||
|             $return[] = [ | ||||
| @@ -373,14 +350,18 @@ class SummaryController extends Controller | ||||
|                 'currency_decimal_places' => $row['currency_decimal_places'], | ||||
|                 'value_parsed'            => app('amount')->formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $leftToSpend, false), | ||||
|                 'local_icon'              => 'money', | ||||
|                 'sub_title'               => (string)trans( | ||||
|                     'firefly.box_spend_per_day', ['amount' => app('amount')->formatFlat( | ||||
|                     $row['currency_symbol'], $row['currency_decimal_places'], $perDay, false | ||||
|                 )] | ||||
|                 'sub_title'               => (string) trans( | ||||
|                     'firefly.box_spend_per_day', | ||||
|                     ['amount' => app('amount')->formatFlat( | ||||
|                         $row['currency_symbol'], | ||||
|                         $row['currency_decimal_places'], | ||||
|                         $perDay, | ||||
|                         false | ||||
|                     )] | ||||
|                 ), | ||||
|             ]; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
| @@ -443,5 +424,4 @@ class SummaryController extends Controller | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -25,12 +25,13 @@ namespace FireflyIII\Api\V1\Controllers; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Api\V1\Requests\DateRequest; | ||||
| use FireflyIII\Api\V1\Requests\TagUpdateRequest; | ||||
| use FireflyIII\Api\V1\Requests\TagStoreRequest; | ||||
| use FireflyIII\Api\V1\Requests\TagUpdateRequest; | ||||
| use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Models\Tag; | ||||
| use FireflyIII\Repositories\Tag\TagRepositoryInterface; | ||||
| use FireflyIII\Support\Http\Api\TransactionFilter; | ||||
| use FireflyIII\Transformers\AttachmentTransformer; | ||||
| use FireflyIII\Transformers\TagTransformer; | ||||
| use FireflyIII\Transformers\TransactionGroupTransformer; | ||||
| use FireflyIII\User; | ||||
| @@ -117,7 +118,7 @@ class TagController extends Controller | ||||
|     { | ||||
|         $manager = $this->getManager(); | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|  | ||||
|         // get list of budgets. Count it and split it. | ||||
|         $collection = $this->repository->get(); | ||||
| @@ -138,6 +139,36 @@ class TagController extends Controller | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Tag $tag | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     public function attachments(Tag $tag): JsonResponse | ||||
|     { | ||||
|         $manager    = $this->getManager(); | ||||
|         $pageSize   = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $collection = $this->repository->getAttachments($tag); | ||||
|  | ||||
|         $count       = $collection->count(); | ||||
|         $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); | ||||
|  | ||||
|         // make paginator: | ||||
|         $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); | ||||
|         $paginator->setPath(route('api.v1.tags.attachments', [$tag->id]) . $this->buildParams()); | ||||
|  | ||||
|         /** @var AttachmentTransformer $transformer */ | ||||
|         $transformer = app(AttachmentTransformer::class); | ||||
|         $transformer->setParameters($this->parameters); | ||||
|  | ||||
|         $resource = new FractalCollection($attachments, $transformer, 'attachments'); | ||||
|         $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * List single resource. | ||||
|      * | ||||
| @@ -190,7 +221,7 @@ class TagController extends Controller | ||||
|      */ | ||||
|     public function transactions(Request $request, Tag $tag): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
| @@ -236,7 +267,7 @@ class TagController extends Controller | ||||
|      * Update a rule. | ||||
|      * | ||||
|      * @param TagUpdateRequest $request | ||||
|      * @param Tag        $tag | ||||
|      * @param Tag              $tag | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
| @@ -288,8 +319,8 @@ class TagController extends Controller | ||||
|         ]; | ||||
|         /** @var Tag $tag */ | ||||
|         foreach ($tags as $tag) { | ||||
|             $earned = (float)$this->repository->earnedInPeriod($tag, $start, $end); | ||||
|             $spent  = (float)$this->repository->spentInPeriod($tag, $start, $end); | ||||
|             $earned = (float) $this->repository->earnedInPeriod($tag, $start, $end); | ||||
|             $spent  = (float) $this->repository->spentInPeriod($tag, $start, $end); | ||||
|             $size   = ($spent * -1) + $earned; | ||||
|             $min    = $min ?? $size; | ||||
|             if ($size > 0) { | ||||
|   | ||||
| @@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\TransactionUpdateRequest; | ||||
| use FireflyIII\Events\StoredTransactionGroup; | ||||
| use FireflyIII\Events\UpdatedTransactionGroup; | ||||
| use FireflyIII\Exceptions\DuplicateTransactionException; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Models\TransactionGroup; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| @@ -109,7 +110,6 @@ class TransactionController extends Controller | ||||
|         $resource = new FractalCollection($attachments, $transformer, 'attachments'); | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -152,7 +152,7 @@ class TransactionController extends Controller | ||||
|      */ | ||||
|     public function index(Request $request): JsonResponse | ||||
|     { | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $type     = $request->get('type') ?? 'default'; | ||||
|         $this->parameters->set('type', $type); | ||||
|  | ||||
| @@ -214,7 +214,6 @@ class TransactionController extends Controller | ||||
|         $resource = new FractalCollection($events, $transformer, 'piggy_bank_events'); | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -270,6 +269,7 @@ class TransactionController extends Controller | ||||
|      * | ||||
|      * @param TransactionStoreRequest $request | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(TransactionStoreRequest $request): JsonResponse | ||||
| @@ -284,7 +284,7 @@ class TransactionController extends Controller | ||||
|         try { | ||||
|             $transactionGroup = $this->groupRepository->store($data); | ||||
|         } catch (DuplicateTransactionException $e) { | ||||
|             Log::warning('Caught a duplicate. Return error message.'); | ||||
|             Log::warning('Caught a duplicate transaction. Return error message.'); | ||||
|             // return bad validation message. | ||||
|             // TODO use Laravel's internal validation thing to do this. | ||||
|             $response = [ | ||||
| @@ -294,10 +294,23 @@ class TransactionController extends Controller | ||||
|                 ], | ||||
|             ]; | ||||
|  | ||||
|             return response()->json($response, 422); | ||||
|         } catch (FireflyException $e) { | ||||
|             Log::warning('Caught an exception. Return error message.'); | ||||
|             Log::error($e->getMessage()); | ||||
|             // return bad validation message. | ||||
|             // TODO use Laravel's internal validation thing to do this. | ||||
|             $response = [ | ||||
|                 'message' => 'The given data was invalid.', | ||||
|                 'errors'  => [ | ||||
|                     'transactions.0.description' => [sprintf('Internal exception: %s', $e->getMessage())], | ||||
|                 ], | ||||
|             ]; | ||||
|  | ||||
|             return response()->json($response, 422); | ||||
|         } | ||||
|         app('preferences')->mark(); | ||||
|         event(new StoredTransactionGroup($transactionGroup)); | ||||
|         event(new StoredTransactionGroup($transactionGroup, $data['apply_rules'] ?? true)); | ||||
|  | ||||
|         $manager = $this->getManager(); | ||||
|         /** @var User $admin */ | ||||
| @@ -314,7 +327,7 @@ class TransactionController extends Controller | ||||
|  | ||||
|         $selectedGroup = $collector->getGroups()->first(); | ||||
|         if (null === $selectedGroup) { | ||||
|             throw new NotFoundHttpException(); // @codeCoverageIgnore | ||||
|             throw new FireflyException('Cannot find transaction. Possibly, a rule deleted this transaction after its creation.'); | ||||
|         } | ||||
|         /** @var TransactionGroupTransformer $transformer */ | ||||
|         $transformer = app(TransactionGroupTransformer::class); | ||||
| @@ -341,7 +354,7 @@ class TransactionController extends Controller | ||||
|         $manager          = $this->getManager(); | ||||
|  | ||||
|         app('preferences')->mark(); | ||||
|         event(new UpdatedTransactionGroup($transactionGroup)); | ||||
|         event(new UpdatedTransactionGroup($transactionGroup, $data['apply_rules'] ?? true)); | ||||
|  | ||||
|         /** @var User $admin */ | ||||
|         $admin = auth()->user(); | ||||
| @@ -365,6 +378,5 @@ class TransactionController extends Controller | ||||
|         $resource = new Item($selectedGroup, $transformer, 'transactions'); | ||||
|  | ||||
|         return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -105,7 +105,7 @@ class TransactionLinkController extends Controller | ||||
|         $name = $request->get('name'); | ||||
|  | ||||
|         // types to get, page size: | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $linkType = $this->repository->findByName($name); | ||||
|  | ||||
|         // get list of transaction links. Count it and split it. | ||||
| @@ -155,8 +155,8 @@ class TransactionLinkController extends Controller | ||||
|      * | ||||
|      * @param TransactionLinkRequest $request | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function store(TransactionLinkRequest $request): JsonResponse | ||||
|     { | ||||
| @@ -186,8 +186,8 @@ class TransactionLinkController extends Controller | ||||
|      * @param TransactionLinkRequest $request | ||||
|      * @param TransactionJournalLink $journalLink | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function update(TransactionLinkRequest $request, TransactionJournalLink $journalLink): JsonResponse | ||||
|     { | ||||
|   | ||||
| @@ -70,9 +70,9 @@ class UserController extends Controller | ||||
|      * | ||||
|      * @param User $user | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * @throws FireflyException | ||||
|      * @codeCoverageIgnore | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function delete(User $user): JsonResponse | ||||
|     { | ||||
| @@ -95,7 +95,7 @@ class UserController extends Controller | ||||
|     public function index(): JsonResponse | ||||
|     { | ||||
|         // user preferences | ||||
|         $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; | ||||
|         $manager  = $this->getManager(); | ||||
|  | ||||
|         // build collection | ||||
|   | ||||
							
								
								
									
										61
									
								
								app/Api/V1/Middleware/ApiDemoUser.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								app/Api/V1/Middleware/ApiDemoUser.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ApiDemoUser.php | ||||
|  * Copyright (c) 2019 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Middleware; | ||||
|  | ||||
| use Closure; | ||||
| use FireflyIII\Repositories\User\UserRepositoryInterface; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Http\Request; | ||||
|  | ||||
| /** | ||||
|  * Class ApiDemoUser. | ||||
|  */ | ||||
| class ApiDemoUser | ||||
| { | ||||
|     /** | ||||
|      * Handle an incoming request. | ||||
|      * | ||||
|      * @param Request $request | ||||
|      * @param Closure $next | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function handle(Request $request, Closure $next) | ||||
|     { | ||||
|         /** @var User $user */ | ||||
|         $user = $request->user(); | ||||
|  | ||||
|         if (null === $user) { | ||||
|             return $next($request); | ||||
|         } | ||||
|  | ||||
|         /** @var UserRepositoryInterface $repository */ | ||||
|         $repository = app(UserRepositoryInterface::class); | ||||
|  | ||||
|         if ($repository->hasRole($user, 'demo')) { | ||||
|             return response('', 403); | ||||
|         } | ||||
|  | ||||
|         return $next($request); | ||||
|     } | ||||
| } | ||||
| @@ -126,7 +126,7 @@ class AccountStoreRequest extends Request | ||||
|             'interest_period'      => 'required_if:type,liability|in:daily,monthly,yearly', | ||||
|             'notes'                => 'min:0|max:65536', | ||||
|         ]; | ||||
|         $rules = Location::requestRules($rules); | ||||
|         $rules          = Location::requestRules($rules); | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
|   | ||||
| @@ -105,7 +105,7 @@ class AccountUpdateRequest extends Request | ||||
|         $types          = implode(',', array_keys(config('firefly.subTitlesByIdentifier'))); | ||||
|         $ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes'))); | ||||
|  | ||||
|         $rules          = [ | ||||
|         $rules = [ | ||||
|             'name'                 => sprintf('min:1|uniqueAccountForUser:%d', $account->id), | ||||
|             'type'                 => sprintf('in:%s', $types), | ||||
|             'iban'                 => 'iban|nullable', | ||||
|   | ||||
| @@ -23,9 +23,6 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Requests; | ||||
|  | ||||
| use FireflyIII\Models\Bill; | ||||
| use FireflyIII\Models\ImportJob; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use FireflyIII\Rules\IsValidAttachmentModel; | ||||
|  | ||||
| /** | ||||
| @@ -69,14 +66,14 @@ class AttachmentStoreRequest extends Request | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         $models = implode( | ||||
|             ',', | ||||
|             [ | ||||
|                 str_replace('FireflyIII\\Models\\', '', Bill::class), | ||||
|                 str_replace('FireflyIII\\Models\\', '', ImportJob::class), | ||||
|                 str_replace('FireflyIII\\Models\\', '', TransactionJournal::class), | ||||
|             ] | ||||
|         $models = config('firefly.valid_attachment_models'); | ||||
|         $models = array_map( | ||||
|  | ||||
|             static function (string $className) { | ||||
|                 return str_replace('FireflyIII\\Models\\', '', $className); | ||||
|             }, $models | ||||
|         ); | ||||
|         $models = implode(',', $models); | ||||
|         $model  = $this->string('attachable_type'); | ||||
|  | ||||
|         return [ | ||||
|   | ||||
| @@ -64,15 +64,13 @@ class AvailableBudgetRequest extends Request | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         $rules = [ | ||||
|         return [ | ||||
|             'currency_id'   => 'numeric|exists:transaction_currencies,id', | ||||
|             'currency_code' => 'min:3|max:3|exists:transaction_currencies,code', | ||||
|             'amount'        => 'required|numeric|more:0', | ||||
|             'start'         => 'required|date|before:end', | ||||
|             'end'           => 'required|date|after:start', | ||||
|         ]; | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -60,7 +60,7 @@ class BillRequest extends Request | ||||
|             $active = $this->boolean('active'); | ||||
|         } | ||||
|  | ||||
|         $data = [ | ||||
|         return [ | ||||
|             'name'          => $this->string('name'), | ||||
|             'amount_min'    => $this->string('amount_min'), | ||||
|             'amount_max'    => $this->string('amount_max'), | ||||
| @@ -72,8 +72,6 @@ class BillRequest extends Request | ||||
|             'active'        => $active, | ||||
|             'notes'         => $this->nlString('notes'), | ||||
|         ]; | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -121,10 +119,10 @@ class BillRequest extends Request | ||||
|         $validator->after( | ||||
|             static function (Validator $validator) { | ||||
|                 $data = $validator->getData(); | ||||
|                 $min  = (float)($data['amount_min'] ?? 0); | ||||
|                 $max  = (float)($data['amount_max'] ?? 0); | ||||
|                 $min  = (float) ($data['amount_min'] ?? 0); | ||||
|                 $max  = (float) ($data['amount_max'] ?? 0); | ||||
|                 if ($min > $max) { | ||||
|                     $validator->errors()->add('amount_min', (string)trans('validation.amount_min_over_max')); | ||||
|                     $validator->errors()->add('amount_min', (string) trans('validation.amount_min_over_max')); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|   | ||||
| @@ -1,91 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BudgetRequest.php | ||||
|  * Copyright (c) 2019 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Requests; | ||||
|  | ||||
| use FireflyIII\Models\Budget; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
|  | ||||
| /** | ||||
|  * Class BudgetRequest | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  * TODO AFTER 4.8,0: split this into two request classes. | ||||
|  */ | ||||
| class BudgetRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Authorize logged in users. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize(): bool | ||||
|     { | ||||
|         // Only allow authenticated users | ||||
|         return auth()->check(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get all data from the request. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getAll(): array | ||||
|     { | ||||
|         $active = true; | ||||
|         if (null !== $this->get('active')) { | ||||
|             $active = $this->boolean('active'); | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|             'name'   => $this->string('name'), | ||||
|             'active' => $active, | ||||
|             'order'  => 0, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The rules that the incoming request must be matched against. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         $rules = [ | ||||
|             'name'   => 'required|between:1,100|uniqueObjectForUser:budgets,name', | ||||
|             'active' => [new IsBoolean], | ||||
|         ]; | ||||
|         switch ($this->method()) { | ||||
|             default: | ||||
|                 break; | ||||
|             case 'PUT': | ||||
|             case 'PATCH': | ||||
|                 /** @var Budget $budget */ | ||||
|                 $budget        = $this->route()->parameter('budget'); | ||||
|                 $rules['name'] = sprintf('required|between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										106
									
								
								app/Api/V1/Requests/BudgetStoreRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								app/Api/V1/Requests/BudgetStoreRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BudgetStoreRequest.php | ||||
|  * Copyright (c) 2019 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Requests; | ||||
|  | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use Illuminate\Validation\Validator; | ||||
|  | ||||
| /** | ||||
|  * Class BudgetStoreRequest | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class BudgetStoreRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Authorize logged in users. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize(): bool | ||||
|     { | ||||
|         // Only allow authenticated users | ||||
|         return auth()->check(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get all data from the request. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getAll(): array | ||||
|     { | ||||
|         $active = true; | ||||
|         if (null !== $this->get('active')) { | ||||
|             $active = $this->boolean('active'); | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|             'name'                      => $this->string('name'), | ||||
|             'active'                    => $active, | ||||
|             'order'                     => 0, | ||||
|             'auto_budget_type'          => $this->string('auto_budget_type'), | ||||
|             'transaction_currency_id'   => $this->integer('auto_budget_currency_id'), | ||||
|             'transaction_currency_code' => $this->string('auto_budget_currency_code'), | ||||
|             'auto_budget_amount'        => $this->string('auto_budget_amount'), | ||||
|             'auto_budget_period'        => $this->string('auto_budget_period'), | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The rules that the incoming request must be matched against. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'name'                      => 'required|between:1,100|uniqueObjectForUser:budgets,name', | ||||
|             'active'                    => [new IsBoolean], | ||||
|             'auto_budget_type'          => 'in:reset,rollover,none', | ||||
|             'auto_budget_currency_id'   => 'exists:transaction_currencies,id', | ||||
|             'auto_budget_currency_code' => 'exists:transaction_currencies,code', | ||||
|             'auto_budget_amount'        => 'min:0|max:1000000000', | ||||
|             'auto_budget_period'        => 'in:daily,weekly,monthly,quarterly,half_year,yearly', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Configure the validator instance with special rules for after the basic validation rules. | ||||
|      * | ||||
|      * @param Validator $validator | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function withValidator(Validator $validator): void | ||||
|     { | ||||
|         $validator->after( | ||||
|             function (Validator $validator) { | ||||
|                 // validate all account info | ||||
|                 $this->validateAutoBudgetAmount($validator); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										107
									
								
								app/Api/V1/Requests/BudgetUpdateRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								app/Api/V1/Requests/BudgetUpdateRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| <?php | ||||
| /** | ||||
|  * BudgetUpdateRequest.php | ||||
|  * Copyright (c) 2019 james@firefly-iii.org | ||||
|  * | ||||
|  * This file is part of Firefly III (https://github.com/firefly-iii). | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as | ||||
|  * published by the Free Software Foundation, either version 3 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Requests; | ||||
|  | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use Illuminate\Validation\Validator; | ||||
|  | ||||
| /** | ||||
|  * Class BudgetUpdateRequest | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class BudgetUpdateRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Authorize logged in users. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize(): bool | ||||
|     { | ||||
|         // Only allow authenticated users | ||||
|         return auth()->check(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get all data from the request. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getAll(): array | ||||
|     { | ||||
|         $active = true; | ||||
|         if (null !== $this->get('active')) { | ||||
|             $active = $this->boolean('active'); | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|             'name'                      => $this->string('name'), | ||||
|             'active'                    => $active, | ||||
|             'order'                     => 0, | ||||
|             'auto_budget_type'          => $this->string('auto_budget_type'), | ||||
|             'transaction_currency_id'   => $this->integer('auto_budget_currency_id'), | ||||
|             'transaction_currency_code' => $this->string('auto_budget_currency_code'), | ||||
|             'auto_budget_amount'        => $this->string('auto_budget_amount'), | ||||
|             'auto_budget_period'        => $this->string('auto_budget_period'), | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The rules that the incoming request must be matched against. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         $budget = $this->route()->parameter('budget'); | ||||
|  | ||||
|         return [ | ||||
|             'name'                      => sprintf('required|between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id), | ||||
|             'active'                    => [new IsBoolean], | ||||
|             'auto_budget_type'          => 'in:reset,rollover,none', | ||||
|             'auto_budget_currency_id'   => 'exists:transaction_currencies,id', | ||||
|             'auto_budget_currency_code' => 'exists:transaction_currencies,code', | ||||
|             'auto_budget_amount'        => 'min:0|max:1000000000', | ||||
|             'auto_budget_period'        => 'in:daily,weekly,monthly,quarterly,half_year,yearly', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Configure the validator instance with special rules for after the basic validation rules. | ||||
|      * | ||||
|      * @param Validator $validator | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function withValidator(Validator $validator): void | ||||
|     { | ||||
|         $validator->after( | ||||
|             function (Validator $validator) { | ||||
|                 // validate all account info | ||||
|                 $this->validateAutoBudgetAmount($validator); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Rules\BelongsUser; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use FireflyIII\Validation\CurrencyValidation; | ||||
| use FireflyIII\Validation\RecurrenceValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Validation\Validator; | ||||
| @@ -35,7 +36,7 @@ use Illuminate\Validation\Validator; | ||||
|  */ | ||||
| class RecurrenceStoreRequest extends Request | ||||
| { | ||||
|     use RecurrenceValidation, TransactionValidation; | ||||
|     use RecurrenceValidation, TransactionValidation, CurrencyValidation; | ||||
|  | ||||
|     /** | ||||
|      * Authorize logged in users. | ||||
| @@ -63,7 +64,8 @@ class RecurrenceStoreRequest extends Request | ||||
|         if (null !== $this->get('apply_rules')) { | ||||
|             $applyRules = $this->boolean('apply_rules'); | ||||
|         } | ||||
|         $return = [ | ||||
|  | ||||
|         return [ | ||||
|             'recurrence'   => [ | ||||
|                 'type'         => $this->string('type'), | ||||
|                 'title'        => $this->string('title'), | ||||
| @@ -77,8 +79,6 @@ class RecurrenceStoreRequest extends Request | ||||
|             'transactions' => $this->getTransactionData(), | ||||
|             'repetitions'  => $this->getRepetitionData(), | ||||
|         ]; | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -168,8 +168,8 @@ class RecurrenceStoreRequest extends Request | ||||
|             $return[] = [ | ||||
|                 'type'    => $repetition['type'], | ||||
|                 'moment'  => $repetition['moment'], | ||||
|                 'skip'    => (int)$repetition['skip'], | ||||
|                 'weekend' => (int)$repetition['weekend'], | ||||
|                 'skip'    => (int) $repetition['skip'], | ||||
|                 'weekend' => (int) $repetition['weekend'], | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
| @@ -193,29 +193,7 @@ class RecurrenceStoreRequest extends Request | ||||
|         } | ||||
|         /** @var array $transaction */ | ||||
|         foreach ($transactions as $transaction) { | ||||
|             $return[] = [ | ||||
|                 'amount'                => $transaction['amount'], | ||||
|                 'currency_id'           => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null, | ||||
|                 'currency_code'         => $transaction['currency_code'] ?? null, | ||||
|                 'foreign_amount'        => $transaction['foreign_amount'] ?? null, | ||||
|                 'foreign_currency_id'   => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null, | ||||
|                 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, | ||||
|                 'source_id'             => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null, | ||||
|                 'source_name'           => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null, | ||||
|                 'destination_id'        => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null, | ||||
|                 'destination_name'      => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null, | ||||
|                 'description'           => $transaction['description'], | ||||
|                 'type'                  => $this->string('type'), | ||||
|  | ||||
|                 // new and updated fields: | ||||
|                 'piggy_bank_id'         => isset($transaction['piggy_bank_id']) ? (int)$transaction['piggy_bank_id'] : null, | ||||
|                 'piggy_bank_name'       => $transaction['piggy_bank_name'] ?? null, | ||||
|                 'tags'                  => $transaction['tags'] ?? [], | ||||
|                 'budget_id'             => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null, | ||||
|                 'budget_name'           => $transaction['budget_name'] ?? null, | ||||
|                 'category_id'           => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null, | ||||
|                 'category_name'         => $transaction['category_name'] ?? null, | ||||
|             ]; | ||||
|             $return[] = $this->getSingleRecurrenceData($transaction); | ||||
|         } | ||||
|  | ||||
|         return $return; | ||||
|   | ||||
| @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests; | ||||
| use FireflyIII\Models\Recurrence; | ||||
| use FireflyIII\Rules\BelongsUser; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use FireflyIII\Validation\CurrencyValidation; | ||||
| use FireflyIII\Validation\RecurrenceValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Validation\Validator; | ||||
| @@ -35,7 +36,7 @@ use Illuminate\Validation\Validator; | ||||
|  */ | ||||
| class RecurrenceUpdateRequest extends Request | ||||
| { | ||||
|     use RecurrenceValidation, TransactionValidation; | ||||
|     use RecurrenceValidation, TransactionValidation, CurrencyValidation; | ||||
|  | ||||
|     /** | ||||
|      * Authorize logged in users. | ||||
| @@ -63,7 +64,8 @@ class RecurrenceUpdateRequest extends Request | ||||
|         if (null !== $this->get('apply_rules')) { | ||||
|             $applyRules = $this->boolean('apply_rules'); | ||||
|         } | ||||
|         $return = [ | ||||
|  | ||||
|         return [ | ||||
|             'recurrence'   => [ | ||||
|                 'type'              => $this->nullableString('type'), | ||||
|                 'title'             => $this->nullableString('title'), | ||||
| @@ -78,8 +80,6 @@ class RecurrenceUpdateRequest extends Request | ||||
|             'transactions' => $this->getTransactionData(), | ||||
|             'repetitions'  => $this->getRepetitionData(), | ||||
|         ]; | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -141,7 +141,7 @@ class RecurrenceUpdateRequest extends Request | ||||
|     { | ||||
|         $validator->after( | ||||
|             function (Validator $validator) { | ||||
|                 $this->validateOneRecurrenceTransactionUpdate($validator); | ||||
|                 $this->validateOneRecurrenceTransaction($validator); | ||||
|                 $this->validateOneRepetitionUpdate($validator); | ||||
|                 $this->validateRecurrenceRepetition($validator); | ||||
|                 $this->validateRepetitionMoment($validator); | ||||
| @@ -170,8 +170,8 @@ class RecurrenceUpdateRequest extends Request | ||||
|             $return[] = [ | ||||
|                 'type'    => $repetition['type'], | ||||
|                 'moment'  => $repetition['moment'], | ||||
|                 'skip'    => (int)$repetition['skip'], | ||||
|                 'weekend' => (int)$repetition['weekend'], | ||||
|                 'skip'    => (int) $repetition['skip'], | ||||
|                 'weekend' => (int) $repetition['weekend'], | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
| @@ -195,29 +195,7 @@ class RecurrenceUpdateRequest extends Request | ||||
|         } | ||||
|         /** @var array $transaction */ | ||||
|         foreach ($transactions as $transaction) { | ||||
|             $return[] = [ | ||||
|                 'amount'                => $transaction['amount'], | ||||
|                 'currency_id'           => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null, | ||||
|                 'currency_code'         => $transaction['currency_code'] ?? null, | ||||
|                 'foreign_amount'        => $transaction['foreign_amount'] ?? null, | ||||
|                 'foreign_currency_id'   => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null, | ||||
|                 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, | ||||
|                 'source_id'             => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null, | ||||
|                 'source_name'           => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null, | ||||
|                 'destination_id'        => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null, | ||||
|                 'destination_name'      => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null, | ||||
|                 'description'           => $transaction['description'], | ||||
|                 'type'                  => $this->string('type'), | ||||
|  | ||||
|                 // new and updated fields: | ||||
|                 'piggy_bank_id'         => isset($transaction['piggy_bank_id']) ? (int)$transaction['piggy_bank_id'] : null, | ||||
|                 'piggy_bank_name'       => $transaction['piggy_bank_name'] ?? null, | ||||
|                 'tags'                  => $transaction['tags'] ?? [], | ||||
|                 'budget_id'             => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null, | ||||
|                 'budget_name'           => $transaction['budget_name'] ?? null, | ||||
|                 'category_id'           => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null, | ||||
|                 'category_name'         => $transaction['category_name'] ?? null, | ||||
|             ]; | ||||
|             $return[] = $this->getSingleRecurrenceData($transaction); | ||||
|         } | ||||
|  | ||||
|         return $return; | ||||
|   | ||||
| @@ -34,4 +34,36 @@ use FireflyIII\Http\Requests\Request as FireflyIIIRequest; | ||||
|  */ | ||||
| class Request extends FireflyIIIRequest | ||||
| { | ||||
|     /** | ||||
|      * @param array $transaction | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function getSingleRecurrenceData(array $transaction): array | ||||
|     { | ||||
|         return [ | ||||
|             'amount'                => $transaction['amount'], | ||||
|             'currency_id'           => isset($transaction['currency_id']) ? (int) $transaction['currency_id'] : null, | ||||
|             'currency_code'         => $transaction['currency_code'] ?? null, | ||||
|             'foreign_amount'        => $transaction['foreign_amount'] ?? null, | ||||
|             'foreign_currency_id'   => isset($transaction['foreign_currency_id']) ? (int) $transaction['foreign_currency_id'] : null, | ||||
|             'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, | ||||
|             'source_id'             => isset($transaction['source_id']) ? (int) $transaction['source_id'] : null, | ||||
|             'source_name'           => isset($transaction['source_name']) ? (string) $transaction['source_name'] : null, | ||||
|             'destination_id'        => isset($transaction['destination_id']) ? (int) $transaction['destination_id'] : null, | ||||
|             'destination_name'      => isset($transaction['destination_name']) ? (string) $transaction['destination_name'] : null, | ||||
|             'description'           => $transaction['description'], | ||||
|             'type'                  => $this->string('type'), | ||||
|  | ||||
|             // new and updated fields: | ||||
|             'piggy_bank_id'         => isset($transaction['piggy_bank_id']) ? (int) $transaction['piggy_bank_id'] : null, | ||||
|             'piggy_bank_name'       => $transaction['piggy_bank_name'] ?? null, | ||||
|             'tags'                  => $transaction['tags'] ?? [], | ||||
|             'budget_id'             => isset($transaction['budget_id']) ? (int) $transaction['budget_id'] : null, | ||||
|             'budget_name'           => $transaction['budget_name'] ?? null, | ||||
|             'category_id'           => isset($transaction['category_id']) ? (int) $transaction['category_id'] : null, | ||||
|             'category_name'         => $transaction['category_name'] ?? null, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -54,7 +54,7 @@ class RuleGroupTestRequest extends Request | ||||
|      */ | ||||
|     public function getTestParameters(): array | ||||
|     { | ||||
|         $return = [ | ||||
|         return [ | ||||
|             'page'          => $this->getPage(), | ||||
|             'start_date'    => $this->getDate('start_date'), | ||||
|             'end_date'      => $this->getDate('end_date'), | ||||
| @@ -62,9 +62,6 @@ class RuleGroupTestRequest extends Request | ||||
|             'trigger_limit' => $this->getTriggerLimit(), | ||||
|             'accounts'      => $this->getAccounts(), | ||||
|         ]; | ||||
|  | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -80,7 +77,7 @@ class RuleGroupTestRequest extends Request | ||||
|      */ | ||||
|     private function getAccounts(): Collection | ||||
|     { | ||||
|         $accountList = '' === (string)$this->query('accounts') ? [] : explode(',', $this->query('accounts')); | ||||
|         $accountList = '' === (string) $this->query('accounts') ? [] : explode(',', $this->query('accounts')); | ||||
|         $accounts    = new Collection; | ||||
|  | ||||
|         /** @var AccountRepositoryInterface $accountRepository */ | ||||
| @@ -88,7 +85,7 @@ class RuleGroupTestRequest extends Request | ||||
|  | ||||
|         foreach ($accountList as $accountId) { | ||||
|             Log::debug(sprintf('Searching for asset account with id "%s"', $accountId)); | ||||
|             $account = $accountRepository->findNull((int)$accountId); | ||||
|             $account = $accountRepository->findNull((int) $accountId); | ||||
|             if ($this->validAccount($account)) { | ||||
|                 /** @noinspection NullPointerExceptionInspection */ | ||||
|                 Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name)); | ||||
| @@ -117,7 +114,7 @@ class RuleGroupTestRequest extends Request | ||||
|      */ | ||||
|     private function getPage(): int | ||||
|     { | ||||
|         return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page'); | ||||
|         return 0 === (int) $this->query('page') ? 1 : (int) $this->query('page'); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -126,7 +123,7 @@ class RuleGroupTestRequest extends Request | ||||
|      */ | ||||
|     private function getSearchLimit(): int | ||||
|     { | ||||
|         return 0 === (int)$this->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$this->query('search_limit'); | ||||
|         return 0 === (int) $this->query('search_limit') ? (int) config('firefly.test-triggers.limit') : (int) $this->query('search_limit'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -134,7 +131,7 @@ class RuleGroupTestRequest extends Request | ||||
|      */ | ||||
|     private function getTriggerLimit(): int | ||||
|     { | ||||
|         return 0 === (int)$this->query('triggered_limit') ? (int)config('firefly.test-triggers.range') : (int)$this->query('triggered_limit'); | ||||
|         return 0 === (int) $this->query('triggered_limit') ? (int) config('firefly.test-triggers.range') : (int) $this->query('triggered_limit'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -54,14 +54,11 @@ class RuleGroupTriggerRequest extends Request | ||||
|      */ | ||||
|     public function getTriggerParameters(): array | ||||
|     { | ||||
|         $return = [ | ||||
|         return [ | ||||
|             'start_date' => $this->getDate('start_date'), | ||||
|             'end_date'   => $this->getDate('end_date'), | ||||
|             'accounts'   => $this->getAccounts(), | ||||
|         ]; | ||||
|  | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -80,7 +77,7 @@ class RuleGroupTriggerRequest extends Request | ||||
|      */ | ||||
|     private function getAccounts(): Collection | ||||
|     { | ||||
|         $accountList = '' === (string)$this->query('accounts') ? [] : explode(',', $this->query('accounts')); | ||||
|         $accountList = '' === (string) $this->query('accounts') ? [] : explode(',', $this->query('accounts')); | ||||
|         $accounts    = new Collection; | ||||
|  | ||||
|         /** @var AccountRepositoryInterface $accountRepository */ | ||||
| @@ -88,7 +85,7 @@ class RuleGroupTriggerRequest extends Request | ||||
|  | ||||
|         foreach ($accountList as $accountId) { | ||||
|             Log::debug(sprintf('Searching for asset account with id "%s"', $accountId)); | ||||
|             $account = $accountRepository->findNull((int)$accountId); | ||||
|             $account = $accountRepository->findNull((int) $accountId); | ||||
|             if ($this->validAccount($account)) { | ||||
|                 /** @noinspection NullPointerExceptionInspection */ | ||||
|                 Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name)); | ||||
|   | ||||
| @@ -65,7 +65,7 @@ class RuleStoreRequest extends Request | ||||
|             $stopProcessing = $this->boolean('stop_processing'); | ||||
|         } | ||||
|  | ||||
|         $data = [ | ||||
|         return [ | ||||
|             'title'            => $this->string('title'), | ||||
|             'description'      => $this->string('description'), | ||||
|             'rule_group_id'    => $this->integer('rule_group_id'), | ||||
| @@ -77,8 +77,6 @@ class RuleStoreRequest extends Request | ||||
|             'triggers'         => $this->getRuleTriggers(), | ||||
|             'actions'          => $this->getRuleActions(), | ||||
|         ]; | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -94,7 +92,8 @@ class RuleStoreRequest extends Request | ||||
|         // some triggers and actions require text: | ||||
|         $contextTriggers = implode(',', config('firefly.context-rule-triggers')); | ||||
|         $contextActions  = implode(',', config('firefly.context-rule-actions')); | ||||
|         $rules           = [ | ||||
|  | ||||
|         return [ | ||||
|             'title'                      => 'required|between:1,100|uniqueObjectForUser:rules,title', | ||||
|             'description'                => 'between:1,5000|nullable', | ||||
|             'rule_group_id'              => 'required|belongsToUser:rule_groups|required_without:rule_group_title', | ||||
| @@ -112,8 +111,6 @@ class RuleStoreRequest extends Request | ||||
|             'stop_processing'            => [new IsBoolean], | ||||
|             'active'                     => [new IsBoolean], | ||||
|         ]; | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -144,7 +141,7 @@ class RuleStoreRequest extends Request | ||||
|         $actions = $data['actions'] ?? []; | ||||
|         // need at least one trigger | ||||
|         if (0 === count($actions)) { | ||||
|             $validator->errors()->add('title', (string)trans('validation.at_least_one_action')); | ||||
|             $validator->errors()->add('title', (string) trans('validation.at_least_one_action')); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -159,7 +156,7 @@ class RuleStoreRequest extends Request | ||||
|         $triggers = $data['triggers'] ?? []; | ||||
|         // need at least one trigger | ||||
|         if (0 === count($triggers)) { | ||||
|             $validator->errors()->add('title', (string)trans('validation.at_least_one_trigger')); | ||||
|             $validator->errors()->add('title', (string) trans('validation.at_least_one_trigger')); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -175,8 +172,8 @@ class RuleStoreRequest extends Request | ||||
|                 $return[] = [ | ||||
|                     'type'            => $action['type'], | ||||
|                     'value'           => $action['value'], | ||||
|                     'active'          => $this->convertBoolean((string)($action['active'] ?? 'false')), | ||||
|                     'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')), | ||||
|                     'active'          => $this->convertBoolean((string) ($action['active'] ?? 'false')), | ||||
|                     'stop_processing' => $this->convertBoolean((string) ($action['stop_processing'] ?? 'false')), | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
| @@ -196,8 +193,8 @@ class RuleStoreRequest extends Request | ||||
|                 $return[] = [ | ||||
|                     'type'            => $trigger['type'], | ||||
|                     'value'           => $trigger['value'], | ||||
|                     'active'          => $this->convertBoolean((string)($trigger['active'] ?? 'false')), | ||||
|                     'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')), | ||||
|                     'active'          => $this->convertBoolean((string) ($trigger['active'] ?? 'false')), | ||||
|                     'stop_processing' => $this->convertBoolean((string) ($trigger['stop_processing'] ?? 'false')), | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -54,7 +54,7 @@ class RuleTestRequest extends Request | ||||
|      */ | ||||
|     public function getTestParameters(): array | ||||
|     { | ||||
|         $return = [ | ||||
|         return [ | ||||
|             'page'          => $this->getPage(), | ||||
|             'start_date'    => $this->getDate('start_date'), | ||||
|             'end_date'      => $this->getDate('end_date'), | ||||
| @@ -62,9 +62,6 @@ class RuleTestRequest extends Request | ||||
|             'trigger_limit' => $this->getTriggerLimit(), | ||||
|             'accounts'      => $this->getAccounts(), | ||||
|         ]; | ||||
|  | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -80,7 +77,7 @@ class RuleTestRequest extends Request | ||||
|      */ | ||||
|     private function getAccounts(): Collection | ||||
|     { | ||||
|         $accountList = '' === (string)$this->query('accounts') ? [] : explode(',', $this->query('accounts')); | ||||
|         $accountList = '' === (string) $this->query('accounts') ? [] : explode(',', $this->query('accounts')); | ||||
|         $accounts    = new Collection; | ||||
|  | ||||
|         /** @var AccountRepositoryInterface $accountRepository */ | ||||
| @@ -88,7 +85,7 @@ class RuleTestRequest extends Request | ||||
|  | ||||
|         foreach ($accountList as $accountId) { | ||||
|             Log::debug(sprintf('Searching for asset account with id "%s"', $accountId)); | ||||
|             $account = $accountRepository->findNull((int)$accountId); | ||||
|             $account = $accountRepository->findNull((int) $accountId); | ||||
|             if ($this->validAccount($account)) { | ||||
|                 /** @noinspection NullPointerExceptionInspection */ | ||||
|                 Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name)); | ||||
| @@ -117,7 +114,7 @@ class RuleTestRequest extends Request | ||||
|      */ | ||||
|     private function getPage(): int | ||||
|     { | ||||
|         return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page'); | ||||
|         return 0 === (int) $this->query('page') ? 1 : (int) $this->query('page'); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -126,7 +123,7 @@ class RuleTestRequest extends Request | ||||
|      */ | ||||
|     private function getSearchLimit(): int | ||||
|     { | ||||
|         return 0 === (int)$this->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$this->query('search_limit'); | ||||
|         return 0 === (int) $this->query('search_limit') ? (int) config('firefly.test-triggers.limit') : (int) $this->query('search_limit'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -134,7 +131,7 @@ class RuleTestRequest extends Request | ||||
|      */ | ||||
|     private function getTriggerLimit(): int | ||||
|     { | ||||
|         return 0 === (int)$this->query('triggered_limit') ? (int)config('firefly.test-triggers.range') : (int)$this->query('triggered_limit'); | ||||
|         return 0 === (int) $this->query('triggered_limit') ? (int) config('firefly.test-triggers.range') : (int) $this->query('triggered_limit'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -53,14 +53,11 @@ class RuleTriggerRequest extends Request | ||||
|      */ | ||||
|     public function getTriggerParameters(): array | ||||
|     { | ||||
|         $return = [ | ||||
|         return [ | ||||
|             'start_date' => $this->getDate('start_date'), | ||||
|             'end_date'   => $this->getDate('end_date'), | ||||
|             'accounts'   => $this->getAccounts(), | ||||
|         ]; | ||||
|  | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -79,7 +76,7 @@ class RuleTriggerRequest extends Request | ||||
|      */ | ||||
|     private function getAccounts(): Collection | ||||
|     { | ||||
|         $accountList = '' === (string)$this->query('accounts') ? [] : explode(',', $this->query('accounts')); | ||||
|         $accountList = '' === (string) $this->query('accounts') ? [] : explode(',', $this->query('accounts')); | ||||
|         $accounts    = new Collection; | ||||
|  | ||||
|         /** @var AccountRepositoryInterface $accountRepository */ | ||||
| @@ -87,7 +84,7 @@ class RuleTriggerRequest extends Request | ||||
|  | ||||
|         foreach ($accountList as $accountId) { | ||||
|             Log::debug(sprintf('Searching for asset account with id "%s"', $accountId)); | ||||
|             $account = $accountRepository->findNull((int)$accountId); | ||||
|             $account = $accountRepository->findNull((int) $accountId); | ||||
|             if ($this->validAccount($account)) { | ||||
|                 /** @noinspection NullPointerExceptionInspection */ | ||||
|                 Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name)); | ||||
|   | ||||
| @@ -65,7 +65,7 @@ class RuleUpdateRequest extends Request | ||||
|             $stopProcessing = $this->boolean('stop_processing'); | ||||
|         } | ||||
|  | ||||
|         $data = [ | ||||
|         return [ | ||||
|             'title'            => $this->nullableString('title'), | ||||
|             'description'      => $this->nullableString('description'), | ||||
|             'rule_group_id'    => $this->nullableInteger('rule_group_id'), | ||||
| @@ -77,8 +77,6 @@ class RuleUpdateRequest extends Request | ||||
|             'triggers'         => $this->getRuleTriggers(), | ||||
|             'actions'          => $this->getRuleActions(), | ||||
|         ]; | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -95,7 +93,8 @@ class RuleUpdateRequest extends Request | ||||
|         // some triggers and actions require text: | ||||
|         $contextTriggers = implode(',', config('firefly.context-rule-triggers')); | ||||
|         $contextActions  = implode(',', config('firefly.context-rule-actions')); | ||||
|         $rules           = [ | ||||
|  | ||||
|         return [ | ||||
|             'title'                      => sprintf('between:1,100|uniqueObjectForUser:rules,title,%d', $rule->id), | ||||
|             'description'                => 'between:1,5000|nullable', | ||||
|             'rule_group_id'              => 'belongsToUser:rule_groups', | ||||
| @@ -113,8 +112,6 @@ class RuleUpdateRequest extends Request | ||||
|             'stop_processing'            => [new IsBoolean], | ||||
|             'active'                     => [new IsBoolean], | ||||
|         ]; | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -145,7 +142,7 @@ class RuleUpdateRequest extends Request | ||||
|         $actions = $data['actions'] ?? null; | ||||
|         // need at least one action | ||||
|         if (is_array($actions) && 0 === count($actions)) { | ||||
|             $validator->errors()->add('title', (string)trans('validation.at_least_one_action')); | ||||
|             $validator->errors()->add('title', (string) trans('validation.at_least_one_action')); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -160,7 +157,7 @@ class RuleUpdateRequest extends Request | ||||
|         $triggers = $data['triggers'] ?? null; | ||||
|         // need at least one trigger | ||||
|         if (is_array($triggers) && 0 === count($triggers)) { | ||||
|             $validator->errors()->add('title', (string)trans('validation.at_least_one_trigger')); | ||||
|             $validator->errors()->add('title', (string) trans('validation.at_least_one_trigger')); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -179,8 +176,8 @@ class RuleUpdateRequest extends Request | ||||
|                 $return[] = [ | ||||
|                     'type'            => $action['type'], | ||||
|                     'value'           => $action['value'], | ||||
|                     'active'          => $this->convertBoolean((string)($action['active'] ?? 'false')), | ||||
|                     'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')), | ||||
|                     'active'          => $this->convertBoolean((string) ($action['active'] ?? 'false')), | ||||
|                     'stop_processing' => $this->convertBoolean((string) ($action['stop_processing'] ?? 'false')), | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
| @@ -203,8 +200,8 @@ class RuleUpdateRequest extends Request | ||||
|                 $return[] = [ | ||||
|                     'type'            => $trigger['type'], | ||||
|                     'value'           => $trigger['value'], | ||||
|                     'active'          => $this->convertBoolean((string)($trigger['active'] ?? 'false')), | ||||
|                     'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')), | ||||
|                     'active'          => $this->convertBoolean((string) ($trigger['active'] ?? 'false')), | ||||
|                     'stop_processing' => $this->convertBoolean((string) ($trigger['stop_processing'] ?? 'false')), | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * TransferRequest.php | ||||
|  * Copyright (c) 2019 james@firefly-iii.org | ||||
| @@ -21,7 +22,6 @@ | ||||
|  | ||||
| namespace FireflyIII\Api\V1\Requests\Search; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Api\V1\Requests\Request; | ||||
| use FireflyIII\Rules\IsTransferAccount; | ||||
|  | ||||
| @@ -54,5 +54,4 @@ class TransferRequest extends Request | ||||
|             'date'        => 'required|date', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -110,8 +110,8 @@ class TransactionLinkRequest extends Request | ||||
|         $journalRepos->setUser($user); | ||||
|  | ||||
|         $data      = $validator->getData(); | ||||
|         $inwardId  = (int)($data['inward_id'] ?? 0); | ||||
|         $outwardId = (int)($data['outward_id'] ?? 0); | ||||
|         $inwardId  = (int) ($data['inward_id'] ?? 0); | ||||
|         $outwardId = (int) ($data['outward_id'] ?? 0); | ||||
|         $inward    = $journalRepos->findNull($inwardId); | ||||
|         $outward   = $journalRepos->findNull($outwardId); | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,8 @@ use FireflyIII\Rules\BelongsUser; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use FireflyIII\Rules\IsDateOrTime; | ||||
| use FireflyIII\Support\NullArrayObject; | ||||
| use FireflyIII\Validation\CurrencyValidation; | ||||
| use FireflyIII\Validation\GroupValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Validation\Validator; | ||||
| use Log; | ||||
| @@ -37,7 +39,7 @@ use Log; | ||||
|  */ | ||||
| class TransactionStoreRequest extends Request | ||||
| { | ||||
|     use TransactionValidation; | ||||
|     use TransactionValidation, GroupValidation, CurrencyValidation; | ||||
|  | ||||
|     /** | ||||
|      * Authorize logged in users. | ||||
| @@ -47,6 +49,7 @@ class TransactionStoreRequest extends Request | ||||
|     public function authorize(): bool | ||||
|     { | ||||
|         Log::debug('Authorize TransactionStoreRequest'); | ||||
|  | ||||
|         // Only allow authenticated users | ||||
|         return auth()->check(); | ||||
|     } | ||||
| @@ -59,13 +62,13 @@ class TransactionStoreRequest extends Request | ||||
|     public function getAll(): array | ||||
|     { | ||||
|         Log::debug('get all data in TransactionStoreRequest'); | ||||
|         $data = [ | ||||
|  | ||||
|         return [ | ||||
|             'group_title'             => $this->string('group_title'), | ||||
|             'error_if_duplicate_hash' => $this->boolean('error_if_duplicate_hash'), | ||||
|             'apply_rules'             => $this->boolean('apply_rules', true), | ||||
|             'transactions'            => $this->getTransactionData(), | ||||
|         ]; | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -76,10 +79,12 @@ class TransactionStoreRequest extends Request | ||||
|     public function rules(): array | ||||
|     { | ||||
|         Log::debug('Collect rules of TransactionStoreRequest'); | ||||
|         $rules = [ | ||||
|  | ||||
|         return [ | ||||
|             // basic fields for group: | ||||
|             'group_title'                          => 'between:1,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', | ||||
| @@ -153,8 +158,6 @@ class TransactionStoreRequest extends Request | ||||
|             'transactions.*.invoice_date'          => 'date|nullable', | ||||
|         ]; | ||||
|  | ||||
|         return $rules; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -211,63 +214,63 @@ class TransactionStoreRequest extends Request | ||||
|             $return[] = [ | ||||
|                 'type'  => $this->stringFromValue($object['type']), | ||||
|                 'date'  => $this->dateFromValue($object['date']), | ||||
|                 'order' => $this->integerFromValue((string)$object['order']), | ||||
|                 'order' => $this->integerFromValue((string) $object['order']), | ||||
|  | ||||
|                 'currency_id'           => $this->integerFromValue((string)$object['currency_id']), | ||||
|                 'currency_id'           => $this->integerFromValue((string) $object['currency_id']), | ||||
|                 'currency_code'         => $this->stringFromValue($object['currency_code']), | ||||
|  | ||||
|                 // foreign currency info: | ||||
|                 'foreign_currency_id'   => $this->integerFromValue((string)$object['foreign_currency_id']), | ||||
|                 'foreign_currency_id'   => $this->integerFromValue((string) $object['foreign_currency_id']), | ||||
|                 'foreign_currency_code' => $this->stringFromValue($object['foreign_currency_code']), | ||||
|  | ||||
|                 // amount and foreign amount. Cannot be 0. | ||||
|                 'amount'                => $this->stringFromValue((string)$object['amount']), | ||||
|                 'foreign_amount'        => $this->stringFromValue((string)$object['foreign_amount']), | ||||
|                 'amount'                => $this->stringFromValue((string) $object['amount']), | ||||
|                 'foreign_amount'        => $this->stringFromValue((string) $object['foreign_amount']), | ||||
|  | ||||
|                 // description. | ||||
|                 'description'           => $this->stringFromValue($object['description']), | ||||
|  | ||||
|                 // source of transaction. If everything is null, assume cash account. | ||||
|                 'source_id'             => $this->integerFromValue((string)$object['source_id']), | ||||
|                 'source_id'             => $this->integerFromValue((string) $object['source_id']), | ||||
|                 'source_name'           => $this->stringFromValue($object['source_name']), | ||||
|                 'source_iban'           => $this->stringFromValue($object['source_iban']), | ||||
|                 'source_number'         => $this->stringFromValue($object['source_number']), | ||||
|                 'source_bic'            => $this->stringFromValue($object['source_bic']), | ||||
|  | ||||
|                 // destination of transaction. If everything is null, assume cash account. | ||||
|                 'destination_id'        => $this->integerFromValue((string)$object['destination_id']), | ||||
|                 'destination_id'        => $this->integerFromValue((string) $object['destination_id']), | ||||
|                 'destination_name'      => $this->stringFromValue($object['destination_name']), | ||||
|                 'destination_iban'      => $this->stringFromValue($object['destination_iban']), | ||||
|                 'destination_number'    => $this->stringFromValue($object['destination_number']), | ||||
|                 'destination_bic'       => $this->stringFromValue($object['destination_bic']), | ||||
|  | ||||
|                 // budget info | ||||
|                 'budget_id'             => $this->integerFromValue((string)$object['budget_id']), | ||||
|                 'budget_id'             => $this->integerFromValue((string) $object['budget_id']), | ||||
|                 'budget_name'           => $this->stringFromValue($object['budget_name']), | ||||
|  | ||||
|                 // category info | ||||
|                 'category_id'           => $this->integerFromValue((string)$object['category_id']), | ||||
|                 'category_id'           => $this->integerFromValue((string) $object['category_id']), | ||||
|                 'category_name'         => $this->stringFromValue($object['category_name']), | ||||
|  | ||||
|                 // journal bill reference. Optional. Will only work for withdrawals | ||||
|                 'bill_id'               => $this->integerFromValue((string)$object['bill_id']), | ||||
|                 'bill_id'               => $this->integerFromValue((string) $object['bill_id']), | ||||
|                 'bill_name'             => $this->stringFromValue($object['bill_name']), | ||||
|  | ||||
|                 // piggy bank reference. Optional. Will only work for transfers | ||||
|                 'piggy_bank_id'         => $this->integerFromValue((string)$object['piggy_bank_id']), | ||||
|                 'piggy_bank_id'         => $this->integerFromValue((string) $object['piggy_bank_id']), | ||||
|                 'piggy_bank_name'       => $this->stringFromValue($object['piggy_bank_name']), | ||||
|  | ||||
|                 // some other interesting properties | ||||
|                 'reconciled'            => $this->convertBoolean((string)$object['reconciled']), | ||||
|                 'reconciled'            => $this->convertBoolean((string) $object['reconciled']), | ||||
|                 'notes'                 => $this->nlStringFromValue($object['notes']), | ||||
|                 'tags'                  => $this->arrayFromValue($object['tags']), | ||||
|  | ||||
|                 // all custom fields: | ||||
|                 'internal_reference'    => $this->stringFromValue((string)$object['internal_reference']), | ||||
|                 'external_id'           => $this->stringFromValue((string)$object['external_id']), | ||||
|                 'internal_reference'    => $this->stringFromValue((string) $object['internal_reference']), | ||||
|                 'external_id'           => $this->stringFromValue((string) $object['external_id']), | ||||
|                 'original_source'       => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')), | ||||
|                 'recurrence_id'         => $this->integerFromValue($object['recurrence_id']), | ||||
|                 'bunq_payment_id'       => $this->stringFromValue((string)$object['bunq_payment_id']), | ||||
|                 'bunq_payment_id'       => $this->stringFromValue((string) $object['bunq_payment_id']), | ||||
|  | ||||
|                 'sepa_cc'       => $this->stringFromValue($object['sepa_cc']), | ||||
|                 'sepa_ct_op'    => $this->stringFromValue($object['sepa_ct_op']), | ||||
|   | ||||
| @@ -28,6 +28,7 @@ use FireflyIII\Models\TransactionGroup; | ||||
| use FireflyIII\Rules\BelongsUser; | ||||
| use FireflyIII\Rules\IsBoolean; | ||||
| use FireflyIII\Rules\IsDateOrTime; | ||||
| use FireflyIII\Validation\GroupValidation; | ||||
| use FireflyIII\Validation\TransactionValidation; | ||||
| use Illuminate\Validation\Validator; | ||||
| use Log; | ||||
| @@ -37,7 +38,7 @@ use Log; | ||||
|  */ | ||||
| class TransactionUpdateRequest extends Request | ||||
| { | ||||
|     use TransactionValidation; | ||||
|     use TransactionValidation, GroupValidation; | ||||
|  | ||||
|     /** @var array Array values. */ | ||||
|     private $arrayFields; | ||||
| @@ -138,6 +139,7 @@ class TransactionUpdateRequest extends Request | ||||
|  | ||||
|         $data = [ | ||||
|             'transactions' => $this->getTransactionData(), | ||||
|             'apply_rules'  => $this->boolean('apply_rules', true), | ||||
|         ]; | ||||
|         if ($this->has('group_title')) { | ||||
|             $data['group_title'] = $this->string('group_title'); | ||||
| @@ -153,9 +155,10 @@ class TransactionUpdateRequest extends Request | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         $rules = [ | ||||
|         return [ | ||||
|             // basic fields for group: | ||||
|             'group_title'                          => 'between:1,1000', | ||||
|             'apply_rules'                          => [new IsBoolean], | ||||
|  | ||||
|             // transaction rules (in array for splits): | ||||
|             'transactions.*.type'                  => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation', | ||||
| @@ -220,8 +223,6 @@ class TransactionUpdateRequest extends Request | ||||
|             'transactions.*.payment_date'          => 'date|nullable', | ||||
|             'transactions.*.invoice_date'          => 'date|nullable', | ||||
|         ]; | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -277,6 +278,111 @@ class TransactionUpdateRequest extends Request | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $current | ||||
|      * @param array $transaction | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     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 $current | ||||
|      * @param array $transaction | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     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 $current | ||||
|      * @param array $transaction | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getDateData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->dateFields as $fieldName) { | ||||
|             Log::debug(sprintf('Now at date field %s', $fieldName)); | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 $current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]); | ||||
|                 Log::debug(sprintf('New value: "%s"', (string) $transaction[$fieldName])); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $current; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * For each field, add it to the array if a reference is present in the request: | ||||
|      * | ||||
|      * @param array $current | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     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 $current | ||||
|      * @param array $transaction | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getNlStringData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->textareaFields as $fieldName) { | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 $current[$fieldName] = $this->nlStringFromValue((string) $transaction[$fieldName]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $current; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $current | ||||
|      * @param array $transaction | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getStringData(array $current, array $transaction): array | ||||
|     { | ||||
|         foreach ($this->stringFields as $fieldName) { | ||||
|             if (array_key_exists($fieldName, $transaction)) { | ||||
|                 $current[$fieldName] = $this->stringFromValue((string) $transaction[$fieldName]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $current; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get transaction data. | ||||
|      * | ||||
| @@ -290,48 +396,15 @@ class TransactionUpdateRequest extends Request | ||||
|          * @var int   $index | ||||
|          * @var array $transaction | ||||
|          */ | ||||
|         foreach ($this->get('transactions') as $index => $transaction) { | ||||
|         foreach ($this->get('transactions') as $transaction) { | ||||
|             // default response is to update nothing in the transaction: | ||||
|             $current = []; | ||||
|  | ||||
|             // for each field, add it to the array if a reference is present in the request: | ||||
|             foreach ($this->integerFields as $fieldName) { | ||||
|                 if (array_key_exists($fieldName, $transaction)) { | ||||
|                     $current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach ($this->stringFields as $fieldName) { | ||||
|                 if (array_key_exists($fieldName, $transaction)) { | ||||
|                     $current[$fieldName] = $this->stringFromValue((string)$transaction[$fieldName]); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach ($this->textareaFields as $fieldName) { | ||||
|                 if (array_key_exists($fieldName, $transaction)) { | ||||
|                     $current[$fieldName] = $this->nlStringFromValue((string)$transaction[$fieldName]); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach ($this->dateFields as $fieldName) { | ||||
|                 Log::debug(sprintf('Now at date field %s', $fieldName)); | ||||
|                 if (array_key_exists($fieldName, $transaction)) { | ||||
|                     $current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]); | ||||
|                     Log::debug(sprintf('New value: "%s"', (string)$transaction[$fieldName])); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach ($this->booleanFields as $fieldName) { | ||||
|                 if (array_key_exists($fieldName, $transaction)) { | ||||
|                     $current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             foreach ($this->arrayFields as $fieldName) { | ||||
|                 if (array_key_exists($fieldName, $transaction)) { | ||||
|                     $current[$fieldName] = $this->arrayFromValue($transaction[$fieldName]); | ||||
|                 } | ||||
|             } | ||||
|             $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); | ||||
|             $return[] = $current; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -69,14 +69,13 @@ class UserStoreRequest extends Request | ||||
|         if (null !== $this->get('blocked')) { | ||||
|             $blocked = $this->boolean('blocked'); | ||||
|         } | ||||
|         $data = [ | ||||
|  | ||||
|         return [ | ||||
|             'email'        => $this->string('email'), | ||||
|             'blocked'      => $blocked, | ||||
|             'blocked_code' => $this->string('blocked_code'), | ||||
|             'role'         => $this->string('role'), | ||||
|         ]; | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -69,14 +69,13 @@ class UserUpdateRequest extends Request | ||||
|         if (null !== $this->get('blocked')) { | ||||
|             $blocked = $this->boolean('blocked'); | ||||
|         } | ||||
|         $data = [ | ||||
|  | ||||
|         return [ | ||||
|             'email'        => $this->string('email'), | ||||
|             'blocked'      => $blocked, | ||||
|             'blocked_code' => $this->string('blocked_code'), | ||||
|             'role'         => $this->string('role'), | ||||
|         ]; | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -86,15 +85,14 @@ class UserUpdateRequest extends Request | ||||
|      */ | ||||
|     public function rules(): array | ||||
|     { | ||||
|         $user  = $this->route()->parameter('user'); | ||||
|         $rules = [ | ||||
|         $user = $this->route()->parameter('user'); | ||||
|  | ||||
|         return [ | ||||
|             'email'        => sprintf('email|unique:users,email,%d', $user->id), | ||||
|             'blocked'      => [new IsBoolean], | ||||
|             'blocked_code' => 'in:email_changed', | ||||
|             'role'         => 'in:owner,demo,', | ||||
|         ]; | ||||
|  | ||||
|         return $rules; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,7 @@ use Schema; | ||||
|  | ||||
| /** | ||||
|  * Class CorrectDatabase | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class CorrectDatabase extends Command | ||||
| @@ -73,7 +74,7 @@ class CorrectDatabase extends Command | ||||
|             'firefly-iii:fix-ob-currencies', | ||||
|             'firefly-iii:fix-long-descriptions', | ||||
|             'firefly-iii:fix-recurring-transactions', | ||||
|             'firefly-iii:restore-oauth-keys' | ||||
|             'firefly-iii:restore-oauth-keys', | ||||
|         ]; | ||||
|         foreach ($commands as $command) { | ||||
|             $this->line(sprintf('Now executing %s', $command)); | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * CorrectOpeningBalanceCurrencies.php | ||||
|  * Copyright (c) 2020 james@firefly-iii.org | ||||
| @@ -111,15 +112,12 @@ class CorrectOpeningBalanceCurrencies extends Command | ||||
|      */ | ||||
|     private function getAccount(TransactionJournal $journal): ?Account | ||||
|     { | ||||
|         $excluded     = []; | ||||
|         $transactions = $journal->transactions()->with(['account', 'account.accountType'])->get(); | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($transactions as $transaction) { | ||||
|             $account = $transaction->account; | ||||
|             if (null !== $account) { | ||||
|                 if (AccountType::INITIAL_BALANCE !== $account->accountType->type) { | ||||
|                     return $account; | ||||
|                 } | ||||
|             if ((null !== $account) && AccountType::INITIAL_BALANCE !== $account->accountType->type) { | ||||
|                 return $account; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -50,8 +50,8 @@ class CreateAccessTokens extends Command | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      * @throws Exception | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|   | ||||
| @@ -47,7 +47,7 @@ class CreateLinkTypes extends Command | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|   | ||||
| @@ -25,7 +25,6 @@ namespace FireflyIII\Console\Commands\Correction; | ||||
|  | ||||
| use Exception; | ||||
| use FireflyIII\Models\TransactionGroup; | ||||
| use FireflyIII\Models\TransactionJournal; | ||||
| use Illuminate\Console\Command; | ||||
| use Log; | ||||
|  | ||||
| @@ -50,16 +49,17 @@ class DeleteEmptyGroups extends Command | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @throws Exception; | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|         Log::debug(sprintf('Now in %s', __METHOD__)); | ||||
|         $start  = microtime(true); | ||||
|         $groupIds  = | ||||
|             TransactionGroup | ||||
|             ::leftJoin('transaction_journals','transaction_groups.id','=','transaction_journals.transaction_group_id') | ||||
|         $start = microtime(true); | ||||
|         $groupIds | ||||
|                = TransactionGroup | ||||
|             ::leftJoin('transaction_journals', 'transaction_groups.id', '=', 'transaction_journals.transaction_group_id') | ||||
|             ->whereNull('transaction_journals.id')->get(['transaction_groups.id'])->pluck('id')->toArray(); | ||||
|  | ||||
|         $total = count($groupIds); | ||||
|   | ||||
| @@ -58,7 +58,6 @@ class DeleteEmptyJournals extends Command | ||||
|         $this->deleteUnevenJournals(); | ||||
|         $this->deleteEmptyJournals(); | ||||
|  | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
| @@ -101,18 +100,18 @@ class DeleteEmptyJournals extends Command | ||||
|             ->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']); | ||||
|         $total = 0; | ||||
|         foreach ($set as $row) { | ||||
|             $count = (int)$row->the_count; | ||||
|             $count = (int) $row->the_count; | ||||
|             if (1 === $count % 2) { | ||||
|                 // uneven number, delete journal and transactions: | ||||
|                 try { | ||||
|                     TransactionJournal::find((int)$row->transaction_journal_id)->delete(); | ||||
|                     TransactionJournal::find((int) $row->transaction_journal_id)->delete(); | ||||
|                     // @codeCoverageIgnoreStart | ||||
|                 } catch (Exception $e) { | ||||
|                     Log::info(sprintf('Could not delete journal: %s', $e->getMessage())); | ||||
|                 } | ||||
|                 // @codeCoverageIgnoreEnd | ||||
|  | ||||
|                 Transaction::where('transaction_journal_id', (int)$row->transaction_journal_id)->delete(); | ||||
|                 Transaction::where('transaction_journal_id', (int) $row->transaction_journal_id)->delete(); | ||||
|                 $this->info(sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id)); | ||||
|                 $total++; | ||||
|             } | ||||
|   | ||||
| @@ -51,8 +51,8 @@ class DeleteOrphanedTransactions extends Command | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      * @throws Exception | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
| @@ -79,7 +79,7 @@ class DeleteOrphanedTransactions extends Command | ||||
|         /** @var Transaction $transaction */ | ||||
|         foreach ($set as $transaction) { | ||||
|             // delete journals | ||||
|             $journal = TransactionJournal::find((int)$transaction->transaction_journal_id); | ||||
|             $journal = TransactionJournal::find((int) $transaction->transaction_journal_id); | ||||
|             if ($journal) { | ||||
|                 try { | ||||
|                     $journal->delete(); | ||||
| @@ -89,10 +89,13 @@ class DeleteOrphanedTransactions extends Command | ||||
|                 } | ||||
|                 // @codeCoverageIgnoreEnd | ||||
|             } | ||||
|             Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete(); | ||||
|             Transaction::where('transaction_journal_id', (int) $transaction->transaction_journal_id)->delete(); | ||||
|             $this->line( | ||||
|                 sprintf('Deleted transaction journal #%d because account #%d was already deleted.', | ||||
|                         $transaction->transaction_journal_id, $transaction->account_id) | ||||
|                 sprintf( | ||||
|                     'Deleted transaction journal #%d because account #%d was already deleted.', | ||||
|                     $transaction->transaction_journal_id, | ||||
|                     $transaction->account_id | ||||
|                 ) | ||||
|             ); | ||||
|             $count++; | ||||
|         } | ||||
| @@ -120,7 +123,7 @@ class DeleteOrphanedTransactions extends Command | ||||
|             ); | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $transaction = Transaction::find((int)$entry->transaction_id); | ||||
|             $transaction = Transaction::find((int) $entry->transaction_id); | ||||
|             $transaction->delete(); | ||||
|             $this->info( | ||||
|                 sprintf( | ||||
| @@ -134,6 +137,5 @@ class DeleteOrphanedTransactions extends Command | ||||
|         if (0 === $count) { | ||||
|             $this->info('No orphaned transactions.'); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -49,6 +49,7 @@ class DeleteZeroAmount extends Command | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|   | ||||
| @@ -52,7 +52,7 @@ class EnableCurrencies extends Command | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
| @@ -62,28 +62,28 @@ class EnableCurrencies extends Command | ||||
|         /** @var Collection $meta */ | ||||
|         $meta = AccountMeta::where('name', 'currency_id')->groupBy('data')->get(['data']); | ||||
|         foreach ($meta as $entry) { | ||||
|             $found[] = (int)$entry->data; | ||||
|             $found[] = (int) $entry->data; | ||||
|         } | ||||
|  | ||||
|         // get all from journals: | ||||
|         /** @var Collection $journals */ | ||||
|         $journals = TransactionJournal::groupBy('transaction_currency_id')->get(['transaction_currency_id']); | ||||
|         foreach ($journals as $entry) { | ||||
|             $found[] = (int)$entry->transaction_currency_id; | ||||
|             $found[] = (int) $entry->transaction_currency_id; | ||||
|         } | ||||
|  | ||||
|         // get all from transactions | ||||
|         /** @var Collection $transactions */ | ||||
|         $transactions = Transaction::groupBy('transaction_currency_id')->get(['transaction_currency_id']); | ||||
|         foreach ($transactions as $entry) { | ||||
|             $found[] = (int)$entry->transaction_currency_id; | ||||
|             $found[] = (int) $entry->transaction_currency_id; | ||||
|         } | ||||
|  | ||||
|         // get all from budget limits | ||||
|         /** @var Collection $limits */ | ||||
|         $limits = BudgetLimit::groupBy('transaction_currency_id')->get(['transaction_currency_id']); | ||||
|         foreach ($limits as $entry) { | ||||
|             $found[] = (int)$entry->transaction_currency_id; | ||||
|             $found[] = (int) $entry->transaction_currency_id; | ||||
|         } | ||||
|  | ||||
|         $found = array_unique($found); | ||||
|   | ||||
| @@ -49,20 +49,20 @@ class FixAccountTypes extends Command | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly-iii:fix-account-types'; | ||||
|     /** @var int */ | ||||
|     private $count; | ||||
|     /** @var array */ | ||||
|     private $expected; | ||||
|     /** @var AccountFactory */ | ||||
|     private $factory; | ||||
|     /** @var array */ | ||||
|     private $fixable; | ||||
|     /** @var int */ | ||||
|     private $count; | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      * @throws FireflyException | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
| @@ -109,23 +109,12 @@ class FixAccountTypes extends Command | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->count = 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      * @param string $type | ||||
|      * @param Transaction $source | ||||
|      * @param Transaction $dest | ||||
|      * @param string             $type | ||||
|      * @param Transaction        $source | ||||
|      * @param Transaction        $dest | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function fixJournal(TransactionJournal $journal, string $type, Transaction $source, Transaction $dest): void | ||||
| @@ -167,9 +156,12 @@ class FixAccountTypes extends Command | ||||
|                 $dest->save(); | ||||
|                 $this->info( | ||||
|                     sprintf( | ||||
|                         'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id, | ||||
|                         $oldDest->id, $oldDest->name, | ||||
|                         $result->id, $result->name | ||||
|                         'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', | ||||
|                         $journal->id, | ||||
|                         $oldDest->id, | ||||
|                         $oldDest->name, | ||||
|                         $result->id, | ||||
|                         $result->name | ||||
|                     ) | ||||
|                 ); | ||||
|                 $this->inspectJournal($journal); | ||||
| @@ -184,9 +176,12 @@ class FixAccountTypes extends Command | ||||
|                 $source->save(); | ||||
|                 $this->info( | ||||
|                     sprintf( | ||||
|                         'Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', $journal->id, | ||||
|                         $oldSource->id, $oldSource->name, | ||||
|                         $result->id, $result->name | ||||
|                         'Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', | ||||
|                         $journal->id, | ||||
|                         $oldSource->id, | ||||
|                         $oldSource->name, | ||||
|                         $result->id, | ||||
|                         $result->name | ||||
|                     ) | ||||
|                 ); | ||||
|                 $this->inspectJournal($journal); | ||||
| @@ -198,7 +193,6 @@ class FixAccountTypes extends Command | ||||
|                 break; | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -274,4 +268,15 @@ class FixAccountTypes extends Command | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->count = 0; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * FixLongDescriptions.php | ||||
|  * Copyright (c) 2020 james@firefly-iii.org | ||||
| @@ -51,7 +52,7 @@ class FixLongDescriptions extends Command | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|         $start = microtime(true); | ||||
|         $start    = microtime(true); | ||||
|         $journals = TransactionJournal::get(['id', 'description']); | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         foreach ($journals as $journal) { | ||||
| @@ -65,7 +66,7 @@ class FixLongDescriptions extends Command | ||||
|         $groups = TransactionGroup::get(['id', 'title']); | ||||
|         /** @var TransactionGroup $group */ | ||||
|         foreach ($groups as $group) { | ||||
|             if (strlen($group->title) > self::MAX_LENGTH) { | ||||
|             if (strlen((string)$group->title) > self::MAX_LENGTH) { | ||||
|                 $group->title = substr($group->title, 0, self::MAX_LENGTH); | ||||
|                 $group->save(); | ||||
|                 $this->line(sprintf('Truncated description of transaction group #%d', $group->id)); | ||||
| @@ -73,6 +74,7 @@ class FixLongDescriptions extends Command | ||||
|         } | ||||
|         $end = round(microtime(true) - $start, 2); | ||||
|         $this->info(sprintf('Verified all transaction group and journal title lengths in %s seconds.', $end)); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -54,7 +54,7 @@ class FixPiggies extends Command | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * FixRecurringTransactions.php | ||||
|  * Copyright (c) 2020 james@firefly-iii.org | ||||
| @@ -51,16 +52,6 @@ class FixRecurringTransactions extends Command | ||||
|     /** @var UserRepositoryInterface */ | ||||
|     private $userRepos; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|   | ||||
| @@ -63,8 +63,8 @@ class FixUnevenAmount extends Command | ||||
|                       ->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]); | ||||
|         /** @var stdClass $entry */ | ||||
|         foreach ($journals as $entry) { | ||||
|             if (0 !== bccomp((string)$entry->the_sum, '0')) { | ||||
|                 $this->fixJournal((int)$entry->transaction_journal_id); | ||||
|             if (0 !== bccomp((string) $entry->the_sum, '0')) { | ||||
|                 $this->fixJournal((int) $entry->transaction_journal_id); | ||||
|                 $count++; | ||||
|             } | ||||
|         } | ||||
| @@ -94,7 +94,8 @@ class FixUnevenAmount extends Command | ||||
|         if (null === $source) { | ||||
|             $this->error( | ||||
|                 sprintf( | ||||
|                     'Journal #%d ("%s") has no source transaction. It will be deleted to maintain database consistency.', $journal->id ?? 0, | ||||
|                     'Journal #%d ("%s") has no source transaction. It will be deleted to maintain database consistency.', | ||||
|                     $journal->id ?? 0, | ||||
|                     $journal->description ?? '' | ||||
|                 ) | ||||
|             ); | ||||
| @@ -104,7 +105,7 @@ class FixUnevenAmount extends Command | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $amount = bcmul('-1', (string)$source->amount); | ||||
|         $amount = bcmul('-1', (string) $source->amount); | ||||
|  | ||||
|         // fix amount of destination: | ||||
|         /** @var Transaction $destination */ | ||||
| @@ -113,7 +114,8 @@ class FixUnevenAmount extends Command | ||||
|         if (null === $destination) { | ||||
|             $this->error( | ||||
|                 sprintf( | ||||
|                     'Journal #%d ("%s") has no destination transaction. It will be deleted to maintain database consistency.', $journal->id ?? 0, | ||||
|                     'Journal #%d ("%s") has no destination transaction. It will be deleted to maintain database consistency.', | ||||
|                     $journal->id ?? 0, | ||||
|                     $journal->description ?? '' | ||||
|                 ) | ||||
|             ); | ||||
|   | ||||
| @@ -48,7 +48,7 @@ class RemoveBills extends Command | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -30,6 +30,7 @@ use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Models\Preference; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Contracts\Encryption\DecryptException; | ||||
| use JsonException; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
| @@ -54,8 +55,8 @@ class DecryptDatabase extends Command | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      * @throws FireflyException | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
| @@ -92,11 +93,15 @@ class DecryptDatabase extends Command | ||||
|                     // A separate routine for preferences: | ||||
|                     if ('preferences' === $table) { | ||||
|                         // try to json_decrypt the value. | ||||
|                         $value = json_decode($value, true) ?? $value; | ||||
|                         try { | ||||
|                             $value = json_decode($value, true, 512, JSON_THROW_ON_ERROR) ?? $value; | ||||
|                         } catch(JsonException $e) { | ||||
|                             Log::error($e->getMessage()); | ||||
|                         } | ||||
|                         Log::debug(sprintf('Decrypted field "%s" "%s" to "%s" in table "%s" (row #%d)', $field, $original, print_r($value, true), $table, $id)); | ||||
|  | ||||
|                         /** @var Preference $object */ | ||||
|                         $object = Preference::find((int)$id); | ||||
|                         $object = Preference::find((int) $id); | ||||
|                         if (null !== $object) { | ||||
|                             $object->data = $value; | ||||
|                             $object->save(); | ||||
| @@ -131,7 +136,7 @@ class DecryptDatabase extends Command | ||||
|         $configName = sprintf('is_decrypted_%s', $table); | ||||
|         $configVar  = app('fireflyconfig')->get($configName, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
| @@ -142,8 +147,9 @@ class DecryptDatabase extends Command | ||||
|      * Tries to decrypt data. Will only throw an exception when the MAC is invalid. | ||||
|      * | ||||
|      * @param $value | ||||
|      * @return string | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      * @return string | ||||
|      */ | ||||
|     private function tryDecrypt($value) | ||||
|     { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * ExportData.php | ||||
|  * Copyright (c) 2020 james@firefly-iii.org | ||||
| @@ -22,6 +23,7 @@ | ||||
| namespace FireflyIII\Console\Commands\Export; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Exception; | ||||
| use FireflyIII\Console\Commands\VerifiesAccessToken; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Models\AccountType; | ||||
| @@ -32,6 +34,7 @@ use FireflyIII\User; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Support\Collection; | ||||
| use InvalidArgumentException; | ||||
| use League\Csv\CannotInsertRecord; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
| @@ -76,21 +79,12 @@ class ExportData extends Command | ||||
|     /** @var User */ | ||||
|     private $user; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      * @throws FireflyException | ||||
|      * @throws CannotInsertRecord | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
| @@ -142,8 +136,12 @@ class ExportData extends Command | ||||
|             $this->exportData($options, $data); | ||||
|         } catch (FireflyException $e) { | ||||
|             $this->error(sprintf('Could not store data: %s', $e->getMessage())); | ||||
|  | ||||
|             app('telemetry')->feature('system.command.errored', $this->signature); | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
|         app('telemetry')->feature('system.command.executed', $this->signature); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
| @@ -171,8 +169,8 @@ class ExportData extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      * @throws FireflyException | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function getAccountsParameter(): Collection | ||||
|     { | ||||
| @@ -180,7 +178,7 @@ class ExportData extends Command | ||||
|         $accounts    = new Collection; | ||||
|         $accountList = $this->option('accounts'); | ||||
|         $types       = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; | ||||
|         if (null !== $accountList && '' !== (string)$accountList) { | ||||
|         if (null !== $accountList && '' !== (string) $accountList) { | ||||
|             $accountIds = explode(',', $accountList); | ||||
|             $accounts   = $this->accountRepository->getAccountsById($accountIds); | ||||
|         } | ||||
| @@ -204,8 +202,9 @@ class ExportData extends Command | ||||
|     /** | ||||
|      * @param string $field | ||||
|      * | ||||
|      * @return Carbon | ||||
|      * @throws FireflyException | ||||
|      * @throws Exception | ||||
|      * @return Carbon | ||||
|      */ | ||||
|     private function getDateParameter(string $field): Carbon | ||||
|     { | ||||
| @@ -239,12 +238,13 @@ class ExportData extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      * @throws FireflyException | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private function getExportDirectory(): string | ||||
|     { | ||||
|         $directory = $this->option('export_directory'); | ||||
|         $directory = (string) $this->option('export_directory'); | ||||
|         if (null === $directory) { | ||||
|             $directory = './'; | ||||
|         } | ||||
| @@ -256,8 +256,8 @@ class ExportData extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      * @throws FireflyException | ||||
|      * @return array | ||||
|      */ | ||||
|     private function parseOptions(): array | ||||
|     { | ||||
|   | ||||
| @@ -39,6 +39,9 @@ use Log; | ||||
|  | ||||
| /** | ||||
|  * Class CreateCSVImport. | ||||
|  * | ||||
|  * @deprecated | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class CreateCSVImport extends Command | ||||
| { | ||||
| @@ -60,273 +63,14 @@ class CreateCSVImport extends Command | ||||
|                             {configuration? : The configuration file to use for the import.} | ||||
|                             {--user=1 : The user ID that the import should import for.} | ||||
|                             {--token= : The user\'s access token.}'; | ||||
|     /** @var UserRepositoryInterface */ | ||||
|     private $userRepository; | ||||
|     /** @var ImportJobRepositoryInterface */ | ||||
|     private $importRepository; | ||||
|     /** @var ImportJob */ | ||||
|     private $importJob; | ||||
|  | ||||
|     /** | ||||
|      * Run the command. | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|         $this->stupidLaravel(); | ||||
|         // @codeCoverageIgnoreStart | ||||
|         if (!$this->verifyAccessToken()) { | ||||
|             $this->errorLine('Invalid access token.'); | ||||
|  | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
|         if (!$this->validArguments()) { | ||||
|             $this->errorLine('Invalid arguments.'); | ||||
|  | ||||
|             return 1; | ||||
|         } | ||||
|         // @codeCoverageIgnoreEnd | ||||
|         /** @var User $user */ | ||||
|         $user          = $this->userRepository->findNull((int)$this->option('user')); | ||||
|         $file          = (string)$this->argument('file'); | ||||
|         $configuration = (string)$this->argument('configuration'); | ||||
|  | ||||
|         $this->importRepository->setUser($user); | ||||
|  | ||||
|         $configurationData = json_decode(file_get_contents($configuration), true); | ||||
|         $this->importJob   = $this->importRepository->create('file'); | ||||
|  | ||||
|  | ||||
|         // inform user (and log it) | ||||
|         $this->infoLine(sprintf('Import file        : %s', $file)); | ||||
|         $this->infoLine(sprintf('Configuration file : %s', $configuration)); | ||||
|         $this->infoLine(sprintf('User               : #%d (%s)', $user->id, $user->email)); | ||||
|         $this->infoLine(sprintf('Job                : %s', $this->importJob->key)); | ||||
|  | ||||
|         try { | ||||
|             $this->storeFile($file); | ||||
|         } catch (FireflyException $e) { | ||||
|             $this->errorLine($e->getMessage()); | ||||
|  | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
|         // job is ready to go | ||||
|         $this->importRepository->setConfiguration($this->importJob, $configurationData); | ||||
|         $this->importRepository->setStatus($this->importJob, 'ready_to_run'); | ||||
|  | ||||
|         $this->infoLine('The import routine has started. The process is not visible. Please wait.'); | ||||
|         Log::debug('Go for import!'); | ||||
|  | ||||
|  | ||||
|         // keep repeating this call until job lands on "provider_finished" | ||||
|         try { | ||||
|             $this->processFile(); | ||||
|         } catch (FireflyException $e) { | ||||
|             $this->errorLine($e->getMessage()); | ||||
|  | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
|         // then store data: | ||||
|         try { | ||||
|             $this->storeData(); | ||||
|         } catch (FireflyException $e) { | ||||
|             $this->errorLine($e->getMessage()); | ||||
|  | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
|         // give feedback: | ||||
|         $this->giveFeedback(); | ||||
|  | ||||
|         // clear cache for user: | ||||
|         app('preferences')->setForUser($user, 'lastActivity', microtime()); | ||||
|  | ||||
|         return 0; | ||||
|         $this->error('This command is disabled.'); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->userRepository   = app(UserRepositoryInterface::class); | ||||
|         $this->importRepository = app(ImportJobRepositoryInterface::class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $message | ||||
|      * @param array|null $data | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function errorLine(string $message, array $data = null): void | ||||
|     { | ||||
|         Log::error($message, $data ?? []); | ||||
|         $this->error($message); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $message | ||||
|      * @param array $data | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function infoLine(string $message, array $data = null): void | ||||
|     { | ||||
|         Log::info($message, $data ?? []); | ||||
|         $this->line($message); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Verify user inserts correct arguments. | ||||
|      * | ||||
|      * @noinspection MultipleReturnStatementsInspection | ||||
|      * @return bool | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function validArguments(): bool | ||||
|     { | ||||
|         $file          = (string)$this->argument('file'); | ||||
|         $configuration = (string)$this->argument('configuration'); | ||||
|         $cwd           = getcwd(); | ||||
|         $enabled       = (bool)config('import.enabled.file'); | ||||
|  | ||||
|         if (false === $enabled) { | ||||
|             $this->errorLine('CSV Provider is not enabled.'); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!file_exists($file)) { | ||||
|             $this->errorLine(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd)); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!file_exists($configuration)) { | ||||
|             $this->errorLine(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd)); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $configurationData = json_decode(file_get_contents($configuration), true); | ||||
|         if (null === $configurationData) { | ||||
|             $this->errorLine(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd)); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store the supplied file as an attachment to this job. | ||||
|      * | ||||
|      * @param string $file | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function storeFile(string $file): void | ||||
|     { | ||||
|         // store file as attachment. | ||||
|         if ('' !== $file) { | ||||
|             $messages = $this->importRepository->storeCLIUpload($this->importJob, 'import_file', $file); | ||||
|             if ($messages->count() > 0) { | ||||
|                 throw new FireflyException($messages->first()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Keep repeating import call until job lands on "provider_finished". | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function processFile(): void | ||||
|     { | ||||
|         $className = config('import.routine.file'); | ||||
|         $valid     = ['provider_finished']; | ||||
|         $count     = 0; | ||||
|  | ||||
|         while (!in_array($this->importJob->status, $valid, true) && $count < 6) { | ||||
|             Log::debug(sprintf('Now in loop #%d.', $count + 1)); | ||||
|             /** @var RoutineInterface $routine */ | ||||
|             $routine = app($className); | ||||
|             $routine->setImportJob($this->importJob); | ||||
|             try { | ||||
|                 $routine->run(); | ||||
|             } catch (FireflyException|Exception $e) { | ||||
|                 $message = 'The import routine crashed: ' . $e->getMessage(); | ||||
|                 Log::error($message); | ||||
|                 Log::error($e->getTraceAsString()); | ||||
|  | ||||
|                 // set job errored out: | ||||
|                 $this->importRepository->setStatus($this->importJob, 'error'); | ||||
|                 throw new FireflyException($message); | ||||
|             } | ||||
|             $count++; | ||||
|         } | ||||
|         $this->importRepository->setStatus($this->importJob, 'provider_finished'); | ||||
|         $this->importJob->status = 'provider_finished'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function storeData(): void | ||||
|     { | ||||
|         if ('provider_finished' === $this->importJob->status) { | ||||
|             $this->infoLine('Import has finished. Please wait for storage of data.'); | ||||
|             // set job to be storing data: | ||||
|             $this->importRepository->setStatus($this->importJob, 'storing_data'); | ||||
|  | ||||
|             /** @var ImportArrayStorage $storage */ | ||||
|             $storage = app(ImportArrayStorage::class); | ||||
|             $storage->setImportJob($this->importJob); | ||||
|  | ||||
|             try { | ||||
|                 $storage->store(); | ||||
|             } catch (FireflyException|Exception $e) { | ||||
|                 $message = 'The import routine crashed: ' . $e->getMessage(); | ||||
|                 Log::error($message); | ||||
|                 Log::error($e->getTraceAsString()); | ||||
|  | ||||
|                 // set job errored out: | ||||
|                 $this->importRepository->setStatus($this->importJob, 'error'); | ||||
|                 throw new FireflyException($message); | ||||
|  | ||||
|             } | ||||
|             // set storage to be finished: | ||||
|             $this->importRepository->setStatus($this->importJob, 'storage_finished'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private function giveFeedback(): void | ||||
|     { | ||||
|         $this->infoLine('Job has finished.'); | ||||
|  | ||||
|  | ||||
|         if (null !== $this->importJob->tag) { | ||||
|             $this->infoLine(sprintf('%d transaction(s) have been imported.', $this->importJob->tag->transactionJournals->count())); | ||||
|             $this->infoLine(sprintf('You can find your transactions under tag "%s"', $this->importJob->tag->tag)); | ||||
|         } | ||||
|  | ||||
|         if (null === $this->importJob->tag) { | ||||
|             $this->errorLine('No transactions have been imported :(.'); | ||||
|         } | ||||
|         if (count($this->importJob->errors) > 0) { | ||||
|             $this->infoLine(sprintf('%d error(s) occurred:', count($this->importJob->errors))); | ||||
|             foreach ($this->importJob->errors as $err) { | ||||
|                 $this->errorLine('- ' . $err); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,7 @@ use Schema; | ||||
|  | ||||
| /** | ||||
|  * Class ReportIntegrity | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class ReportIntegrity extends Command | ||||
|   | ||||
| @@ -69,7 +69,7 @@ class ReportSum extends Command | ||||
|  | ||||
|         /** @var User $user */ | ||||
|         foreach ($userRepository->all() as $user) { | ||||
|             $sum = (string)$user->transactions()->sum('amount'); | ||||
|             $sum = (string) $user->transactions()->sum('amount'); | ||||
|             if (0 !== bccomp($sum, '0')) { | ||||
|                 $message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum); | ||||
|                 $this->error($message); | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * RestoreOAuthKeys.php | ||||
|  * Copyright (c) 2020 james@firefly-iii.org | ||||
| @@ -21,10 +22,9 @@ | ||||
|  | ||||
| namespace FireflyIII\Console\Commands\Integrity; | ||||
|  | ||||
| use Artisan; | ||||
| use Crypt; | ||||
| use FireflyIII\Support\System\OAuthKeys; | ||||
| use Illuminate\Console\Command; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class RestoreOAuthKeys | ||||
| @@ -93,7 +93,9 @@ class RestoreOAuthKeys extends Command | ||||
|      */ | ||||
|     private function restoreOAuthKeys(): void | ||||
|     { | ||||
|         Log::debug('Going to restoreOAuthKeys()'); | ||||
|         if (!$this->keysInDatabase() && !$this->keysOnDrive()) { | ||||
|             Log::debug('Keys are not in DB and keys are not on the drive.'); | ||||
|             $this->generateKeys(); | ||||
|             $this->storeKeysInDB(); | ||||
|             $this->line('Generated and stored new keys.'); | ||||
| @@ -101,12 +103,14 @@ class RestoreOAuthKeys extends Command | ||||
|             return; | ||||
|         } | ||||
|         if ($this->keysInDatabase() && !$this->keysOnDrive()) { | ||||
|             Log::debug('Keys are in DB and keys are not on the drive. Restore.'); | ||||
|             $this->restoreKeysFromDB(); | ||||
|             $this->line('Restored OAuth keys from database.'); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|         if (!$this->keysInDatabase() && $this->keysOnDrive()) { | ||||
|             Log::debug('Keys are not in DB and keys are on the drive. Save in DB.'); | ||||
|             $this->storeKeysInDB(); | ||||
|             $this->line('Stored OAuth keys in database.'); | ||||
|  | ||||
| @@ -122,4 +126,4 @@ class RestoreOAuthKeys extends Command | ||||
|     { | ||||
|         OAuthKeys::storeKeysInDB(); | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -63,8 +63,7 @@ class ScanAttachments extends Command | ||||
|         $disk        = Storage::disk('upload'); | ||||
|         /** @var Attachment $attachment */ | ||||
|         foreach ($attachments as $attachment) { | ||||
|             $fileName         = $attachment->fileName(); | ||||
|             $decryptedContent = ''; | ||||
|             $fileName = $attachment->fileName(); | ||||
|             try { | ||||
|                 $encryptedContent = $disk->get($fileName); | ||||
|             } catch (FileNotFoundException $e) { | ||||
| @@ -87,6 +86,7 @@ class ScanAttachments extends Command | ||||
|             $this->line(sprintf('Fixed attachment #%d', $attachment->id)); | ||||
|         } | ||||
|  | ||||
|         app('telemetry')->feature('system.command.executed', $this->signature); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /** | ||||
|  * SetLatestVersion.php | ||||
| @@ -25,6 +25,9 @@ namespace FireflyIII\Console\Commands; | ||||
|  | ||||
| use Illuminate\Console\Command; | ||||
|  | ||||
| /** | ||||
|  * Class SetLatestVersion | ||||
|  */ | ||||
| class SetLatestVersion extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -40,32 +43,24 @@ class SetLatestVersion extends Command | ||||
|      */ | ||||
|     protected $signature = 'firefly-iii:set-latest-version {--james-is-cool}'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle() | ||||
|     public function handle(): int | ||||
|     { | ||||
|         if (!$this->option('james-is-cool')) { | ||||
|             $this->error('Am too!'); | ||||
|  | ||||
|             return; | ||||
|             return 0; | ||||
|         } | ||||
|         app('fireflyconfig')->set('db_version', config('firefly.db_version')); | ||||
|         app('fireflyconfig')->set('ff3_version', config('firefly.version')); | ||||
|         $this->line('Updated version.'); | ||||
|  | ||||
|         app('telemetry')->feature('system.command.executed', $this->signature); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -68,35 +68,32 @@ class ApplyRules extends Command | ||||
|                             {--all_rules : If set, will overrule both settings and simply apply ALL of your rules.} | ||||
|                             {--start_date= : The date of the earliest transaction to be included (inclusive). If omitted, will be your very first transaction ever. Format: YYYY-MM-DD} | ||||
|                             {--end_date= : The date of the latest transaction to be included (inclusive). If omitted, will be your latest transaction ever. Format: YYYY-MM-DD}'; | ||||
|  | ||||
|     /** @var Collection */ | ||||
|     private $accounts; | ||||
|     /** @var array */ | ||||
|     private $acceptedAccounts; | ||||
|     /** @var Collection */ | ||||
|     private $accounts; | ||||
|     /** @var bool */ | ||||
|     private $allRules; | ||||
|     /** @var Carbon */ | ||||
|     private $endDate; | ||||
|     /** @var Collection */ | ||||
|     private $groups; | ||||
|     /** @var RuleGroupRepositoryInterface */ | ||||
|     private $ruleGroupRepository; | ||||
|     /** @var array */ | ||||
|     private $ruleGroupSelection; | ||||
|     /** @var RuleRepositoryInterface */ | ||||
|     private $ruleRepository; | ||||
|     /** @var array */ | ||||
|     private $ruleSelection; | ||||
|     /** @var Carbon */ | ||||
|     private $startDate; | ||||
|     /** @var Collection */ | ||||
|     private $groups; | ||||
|     /** @var bool */ | ||||
|     private $allRules; | ||||
|  | ||||
|     /** @var RuleRepositoryInterface */ | ||||
|     private $ruleRepository; | ||||
|  | ||||
|     /** @var RuleGroupRepositoryInterface */ | ||||
|     private $ruleGroupRepository; | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      * @throws FireflyException | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
| @@ -115,6 +112,7 @@ class ApplyRules extends Command | ||||
|  | ||||
|         $result = $this->verifyInput(); | ||||
|         if (false === $result) { | ||||
|             app('telemetry')->feature('system.command.errored', $this->signature); | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
| @@ -132,6 +130,9 @@ class ApplyRules extends Command | ||||
|             $this->warn('    --rules=1,2,...'); | ||||
|             $this->warn('    --rule_groups=1,2,...'); | ||||
|             $this->warn('    --all_rules'); | ||||
|  | ||||
|             app('telemetry')->feature('system.command.errored', $this->signature); | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
| @@ -166,9 +167,53 @@ class ApplyRules extends Command | ||||
|         $this->line(''); | ||||
|         $this->line('Done!'); | ||||
|  | ||||
|         app('telemetry')->feature('system.command.executed', $this->signature); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getRulesToApply(): array | ||||
|     { | ||||
|         $rulesToApply = []; | ||||
|         /** @var RuleGroup $group */ | ||||
|         foreach ($this->groups as $group) { | ||||
|             $rules = $this->ruleGroupRepository->getActiveStoreRules($group); | ||||
|             /** @var Rule $rule */ | ||||
|             foreach ($rules as $rule) { | ||||
|                 // if in rule selection, or group in selection or all rules, it's included. | ||||
|                 $test = $this->includeRule($rule, $group); | ||||
|                 if (true === $test) { | ||||
|                     Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title)); | ||||
|                     $rulesToApply[] = $rule->id; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $rulesToApply; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      */ | ||||
|     private function grabAllRules(): void | ||||
|     { | ||||
|         $this->groups = $this->ruleGroupRepository->getActiveGroups(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Rule      $rule | ||||
|      * @param RuleGroup $group | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function includeRule(Rule $rule, RuleGroup $group): bool | ||||
|     { | ||||
|         return in_array($group->id, $this->ruleGroupSelection, true) | ||||
|                || in_array($rule->id, $this->ruleSelection, true) | ||||
|                || $this->allRules; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
| @@ -189,8 +234,8 @@ class ApplyRules extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      * @throws FireflyException | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function verifyInput(): bool | ||||
|     { | ||||
| @@ -212,8 +257,8 @@ class ApplyRules extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      * @throws FireflyException | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function verifyInputAccounts(): bool | ||||
|     { | ||||
| @@ -240,7 +285,7 @@ class ApplyRules extends Command | ||||
|  | ||||
|  | ||||
|         foreach ($accountList as $accountId) { | ||||
|             $accountId = (int)$accountId; | ||||
|             $accountId = (int) $accountId; | ||||
|             $account   = $accountRepository->findNull($accountId); | ||||
|             if (null !== $account && in_array($account->accountType->type, $this->acceptedAccounts, true)) { | ||||
|                 $finalList->push($account); | ||||
| @@ -258,66 +303,6 @@ class ApplyRules extends Command | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function verifyInputRuleGroups(): bool | ||||
|     { | ||||
|         $ruleGroupString = $this->option('rule_groups'); | ||||
|         if (null === $ruleGroupString || '' === $ruleGroupString) { | ||||
|             // can be empty. | ||||
|             return true; | ||||
|         } | ||||
|         $ruleGroupList = explode(',', $ruleGroupString); | ||||
|         // @codeCoverageIgnoreStart | ||||
|         if (0 === count($ruleGroupList)) { | ||||
|             // can be empty. | ||||
|             return true; | ||||
|         } | ||||
|         // @codeCoverageIgnoreEnd | ||||
|         foreach ($ruleGroupList as $ruleGroupId) { | ||||
|             $ruleGroup = $this->ruleGroupRepository->find((int)$ruleGroupId); | ||||
|             if ($ruleGroup->active) { | ||||
|                 $this->ruleGroupSelection[] = $ruleGroup->id; | ||||
|             } | ||||
|             if (false === $ruleGroup->active) { | ||||
|                 $this->warn(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function verifyInputRules(): bool | ||||
|     { | ||||
|         $ruleString = $this->option('rules'); | ||||
|         if (null === $ruleString || '' === $ruleString) { | ||||
|             // can be empty. | ||||
|             return true; | ||||
|         } | ||||
|         $ruleList = explode(',', $ruleString); | ||||
|  | ||||
|         // @codeCoverageIgnoreStart | ||||
|         if (0 === count($ruleList)) { | ||||
|             // can be empty. | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|         // @codeCoverageIgnoreEnd | ||||
|  | ||||
|         foreach ($ruleList as $ruleId) { | ||||
|             $rule = $this->ruleRepository->find((int)$ruleId); | ||||
|             if (null !== $rule && $rule->active) { | ||||
|                 $this->ruleSelection[] = $rule->id; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
| @@ -355,44 +340,62 @@ class ApplyRules extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      */ | ||||
|     private function grabAllRules(): void | ||||
|     { | ||||
|         $this->groups = $this->ruleGroupRepository->getActiveGroups(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Rule $rule | ||||
|      * @param RuleGroup $group | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function includeRule(Rule $rule, RuleGroup $group): bool | ||||
|     private function verifyInputRuleGroups(): bool | ||||
|     { | ||||
|         return in_array($group->id, $this->ruleGroupSelection, true) || | ||||
|                in_array($rule->id, $this->ruleSelection, true) || | ||||
|                $this->allRules; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getRulesToApply(): array | ||||
|     { | ||||
|         $rulesToApply = []; | ||||
|         /** @var RuleGroup $group */ | ||||
|         foreach ($this->groups as $group) { | ||||
|             $rules = $this->ruleGroupRepository->getActiveStoreRules($group); | ||||
|             /** @var Rule $rule */ | ||||
|             foreach ($rules as $rule) { | ||||
|                 // if in rule selection, or group in selection or all rules, it's included. | ||||
|                 $test = $this->includeRule($rule, $group); | ||||
|                 if (true === $test) { | ||||
|                     Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title)); | ||||
|                     $rulesToApply[] = $rule->id; | ||||
|                 } | ||||
|         $ruleGroupString = $this->option('rule_groups'); | ||||
|         if (null === $ruleGroupString || '' === $ruleGroupString) { | ||||
|             // can be empty. | ||||
|             return true; | ||||
|         } | ||||
|         $ruleGroupList = explode(',', $ruleGroupString); | ||||
|         // @codeCoverageIgnoreStart | ||||
|         if (0 === count($ruleGroupList)) { | ||||
|             // can be empty. | ||||
|             return true; | ||||
|         } | ||||
|         // @codeCoverageIgnoreEnd | ||||
|         foreach ($ruleGroupList as $ruleGroupId) { | ||||
|             $ruleGroup = $this->ruleGroupRepository->find((int) $ruleGroupId); | ||||
|             if ($ruleGroup->active) { | ||||
|                 $this->ruleGroupSelection[] = $ruleGroup->id; | ||||
|             } | ||||
|             if (false === $ruleGroup->active) { | ||||
|                 $this->warn(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $rulesToApply; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function verifyInputRules(): bool | ||||
|     { | ||||
|         $ruleString = $this->option('rules'); | ||||
|         if (null === $ruleString || '' === $ruleString) { | ||||
|             // can be empty. | ||||
|             return true; | ||||
|         } | ||||
|         $ruleList = explode(',', $ruleString); | ||||
|  | ||||
|         // @codeCoverageIgnoreStart | ||||
|         if (0 === count($ruleList)) { | ||||
|             // can be empty. | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|         // @codeCoverageIgnoreEnd | ||||
|  | ||||
|         foreach ($ruleList as $ruleId) { | ||||
|             $rule = $this->ruleRepository->find((int) $ruleId); | ||||
|             if (null !== $rule && $rule->active) { | ||||
|                 $this->ruleSelection[] = $rule->id; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -27,9 +27,12 @@ namespace FireflyIII\Console\Commands\Tools; | ||||
| use Carbon\Carbon; | ||||
| use Exception; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Support\Cronjobs\AutoBudgetCronjob; | ||||
| use FireflyIII\Support\Cronjobs\RecurringCronjob; | ||||
| use FireflyIII\Support\Cronjobs\TelemetryCronjob; | ||||
| use Illuminate\Console\Command; | ||||
| use InvalidArgumentException; | ||||
| use Log; | ||||
|  | ||||
| /** | ||||
|  * Class Cron | ||||
| @@ -56,45 +59,137 @@ class Cron extends Command | ||||
|  | ||||
|     /** | ||||
|      * @return int | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
|         $date = null; | ||||
|         try { | ||||
|             $date = new Carbon($this->option('date')); | ||||
|         } catch (InvalidArgumentException $e) { | ||||
|         } catch (InvalidArgumentException|Exception $e) { | ||||
|             $this->error(sprintf('"%s" is not a valid date', $this->option('date'))); | ||||
|             $e->getMessage(); | ||||
|         } | ||||
|         $force = (bool) $this->option('force'); | ||||
|  | ||||
|         /* | ||||
|          * Fire recurring transaction cron job. | ||||
|          */ | ||||
|         try { | ||||
|             $this->recurringCronJob($force, $date); | ||||
|         } catch (FireflyException $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|             Log::error($e->getTraceAsString()); | ||||
|             $this->error($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Fire auto-budget cron job: | ||||
|          */ | ||||
|         try { | ||||
|             $this->autoBudgetCronJob($force, $date); | ||||
|         } catch (FireflyException $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|             Log::error($e->getTraceAsString()); | ||||
|             $this->error($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         /* | ||||
|          * Fire telemetry cron job | ||||
|          */ | ||||
|         try { | ||||
|             $this->telemetryCronJob($force, $date); | ||||
|         } catch (FireflyException $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|             Log::error($e->getTraceAsString()); | ||||
|             $this->error($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         $this->info('More feedback on the cron jobs can be found in the log files.'); | ||||
|  | ||||
|         app('telemetry')->feature('system.command.executed', $this->signature); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param bool        $force | ||||
|      * @param Carbon|null $date | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     private function autoBudgetCronJob(bool $force, ?Carbon $date): void | ||||
|     { | ||||
|         $autoBudget = new AutoBudgetCronjob; | ||||
|         $autoBudget->setForce($force); | ||||
|         // set date in cron job: | ||||
|         if (null !== $date) { | ||||
|             $autoBudget->setDate($date); | ||||
|         } | ||||
|  | ||||
|         $result = $autoBudget->fire(); | ||||
|  | ||||
|         if (false === $result) { | ||||
|             $this->line('The auto budget cron job did not fire.'); | ||||
|         } | ||||
|         if (true === $result) { | ||||
|             $this->line('The auto budget cron job fired successfully.'); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param bool        $force | ||||
|      * @param Carbon|null $date | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function recurringCronJob(bool $force, ?Carbon $date): void | ||||
|     { | ||||
|         $recurring = new RecurringCronjob; | ||||
|         $recurring->setForce($this->option('force')); | ||||
|         $recurring->setForce($force); | ||||
|  | ||||
|         // set date in cron job: | ||||
|         if (null !== $date) { | ||||
|             $recurring->setDate($date); | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             $result = $recurring->fire(); | ||||
|         } catch (FireflyException $e) { | ||||
|             $this->error($e->getMessage()); | ||||
|         $result = $recurring->fire(); | ||||
|  | ||||
|             return 0; | ||||
|         } | ||||
|         if (false === $result) { | ||||
|             $this->line('The recurring transaction cron job did not fire.'); | ||||
|         } | ||||
|         if (true === $result) { | ||||
|             $this->line('The recurring transaction cron job fired successfully.'); | ||||
|         } | ||||
|  | ||||
|         $this->info('More feedback on the cron jobs can be found in the log files.'); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param bool        $force | ||||
|      * @param Carbon|null $date | ||||
|      */ | ||||
|     private function telemetryCronJob(bool $force, ?Carbon $date): void | ||||
|     { | ||||
|         if (false === config('firefly.send_telemetry') || false === config('firefly.feature_flags.telemetry')) { | ||||
|             // if not configured to do anything with telemetry, do nothing. | ||||
|             return; | ||||
|         } | ||||
|         $telemetry = new TelemetryCronJob; | ||||
|         $telemetry->setForce($force); | ||||
|  | ||||
|         // set date in cron job: | ||||
|         if (null !== $date) { | ||||
|             $telemetry->setDate($date); | ||||
|         } | ||||
|  | ||||
|         $result = $telemetry->fire(); | ||||
|  | ||||
|         if (false === $result) { | ||||
|             $this->line('The telemetry cron job did not fire.'); | ||||
|         } | ||||
|         if (true === $result) { | ||||
|             $this->line('The telemetry cron job fired successfully.'); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -54,10 +54,10 @@ class AccountCurrencies extends Command | ||||
|     protected $signature = 'firefly-iii:account-currencies {--F|force : Force the execution of this command.}'; | ||||
|     /** @var AccountRepositoryInterface */ | ||||
|     private $accountRepos; | ||||
|     /** @var UserRepositoryInterface */ | ||||
|     private $userRepos; | ||||
|     /** @var int */ | ||||
|     private $count; | ||||
|     /** @var UserRepositoryInterface */ | ||||
|     private $userRepos; | ||||
|  | ||||
|     /** | ||||
|      * Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account. | ||||
| @@ -87,9 +87,31 @@ class AccountCurrencies extends Command | ||||
|         $this->info(sprintf('Verified and fixed account currencies in %s seconds.', $end)); | ||||
|         $this->markAsExecuted(); | ||||
|  | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isExecuted(): bool | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private function markAsExecuted(): void | ||||
|     { | ||||
|         app('fireflyconfig')->set(self::CONFIG_NAME, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
| @@ -105,29 +127,7 @@ class AccountCurrencies extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isExecuted(): bool | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private function markAsExecuted(): void | ||||
|     { | ||||
|         app('fireflyconfig')->set(self::CONFIG_NAME, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * @param Account             $account | ||||
|      * @param TransactionCurrency $currency | ||||
|      */ | ||||
|     private function updateAccount(Account $account, TransactionCurrency $currency): void | ||||
| @@ -135,13 +135,13 @@ class AccountCurrencies extends Command | ||||
|         Log::debug(sprintf('Now in updateAccount(%d, %s)', $account->id, $currency->code)); | ||||
|         $this->accountRepos->setUser($account->user); | ||||
|  | ||||
|         $accountCurrency = (int)$this->accountRepos->getMetaValue($account, 'currency_id'); | ||||
|         $accountCurrency = (int) $this->accountRepos->getMetaValue($account, 'currency_id'); | ||||
|         Log::debug(sprintf('Account currency is #%d', $accountCurrency)); | ||||
|  | ||||
|         $openingBalance  = $this->accountRepos->getOpeningBalance($account); | ||||
|         $obCurrency      = 0; | ||||
|         $openingBalance = $this->accountRepos->getOpeningBalance($account); | ||||
|         $obCurrency     = 0; | ||||
|         if (null !== $openingBalance) { | ||||
|             $obCurrency = (int)$openingBalance->transaction_currency_id; | ||||
|             $obCurrency = (int) $openingBalance->transaction_currency_id; | ||||
|             Log::debug('Account has opening balance.'); | ||||
|         } | ||||
|         Log::debug(sprintf('Account OB currency is #%d.', $obCurrency)); | ||||
| @@ -178,7 +178,8 @@ class AccountCurrencies extends Command | ||||
|                 static function (Transaction $transaction) use ($accountCurrency) { | ||||
|                     $transaction->transaction_currency_id = $accountCurrency; | ||||
|                     $transaction->save(); | ||||
|                 }); | ||||
|                 } | ||||
|             ); | ||||
|             $this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name)); | ||||
|             $this->count++; | ||||
|  | ||||
| @@ -194,7 +195,7 @@ class AccountCurrencies extends Command | ||||
|     { | ||||
|         Log::debug('Now in updateAccountCurrencies()'); | ||||
|         $users               = $this->userRepos->all(); | ||||
|         $defaultCurrencyCode = (string)config('firefly.default_currency', 'EUR'); | ||||
|         $defaultCurrencyCode = (string) config('firefly.default_currency', 'EUR'); | ||||
|         Log::debug(sprintf('Default currency is %s', $defaultCurrencyCode)); | ||||
|         foreach ($users as $user) { | ||||
|             $this->updateCurrenciesForUser($user, $defaultCurrencyCode); | ||||
| @@ -202,7 +203,7 @@ class AccountCurrencies extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param User $user | ||||
|      * @param User   $user | ||||
|      * @param string $systemCurrencyCode | ||||
|      */ | ||||
|     private function updateCurrenciesForUser(User $user, string $systemCurrencyCode): void | ||||
|   | ||||
| @@ -111,9 +111,9 @@ class BackToJournals extends Command | ||||
|         $chunks       = array_chunk($transactions, 500); | ||||
|  | ||||
|         foreach ($chunks as $chunk) { | ||||
|             $set   = DB::table('transactions') | ||||
|                        ->whereIn('transactions.id', $chunk) | ||||
|                        ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); | ||||
|             $set = DB::table('transactions') | ||||
|                      ->whereIn('transactions.id', $chunk) | ||||
|                      ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); | ||||
|             /** @noinspection SlowArrayOperationsInLoopInspection */ | ||||
|             $array = array_merge($array, $set); | ||||
|         } | ||||
| @@ -128,7 +128,7 @@ class BackToJournals extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
| @@ -141,7 +141,7 @@ class BackToJournals extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(MigrateToGroups::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
| @@ -212,7 +212,7 @@ class BackToJournals extends Command | ||||
|         // both have a budget, but they don't match. | ||||
|         if (null !== $budget && null !== $journalBudget && $budget->id !== $journalBudget->id) { | ||||
|             // sync to journal: | ||||
|             $journal->budgets()->sync([(int)$budget->id]); | ||||
|             $journal->budgets()->sync([(int) $budget->id]); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
| @@ -220,7 +220,7 @@ class BackToJournals extends Command | ||||
|         // transaction has a budget, but the journal doesn't. | ||||
|         if (null !== $budget && null === $journalBudget) { | ||||
|             // sync to journal: | ||||
|             $journal->budgets()->sync([(int)$budget->id]); | ||||
|             $journal->budgets()->sync([(int) $budget->id]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -271,12 +271,12 @@ class BackToJournals extends Command | ||||
|         // both have a category, but they don't match. | ||||
|         if (null !== $category && null !== $journalCategory && $category->id !== $journalCategory->id) { | ||||
|             // sync to journal: | ||||
|             $journal->categories()->sync([(int)$category->id]); | ||||
|             $journal->categories()->sync([(int) $category->id]); | ||||
|         } | ||||
|  | ||||
|         // transaction has a category, but the journal doesn't. | ||||
|         if (null !== $category && null === $journalCategory) { | ||||
|             $journal->categories()->sync([(int)$category->id]); | ||||
|             $journal->categories()->sync([(int) $category->id]); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -102,7 +102,7 @@ class BudgetLimitCurrency extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|   | ||||
| @@ -101,7 +101,7 @@ class CCLiabilities extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|   | ||||
| @@ -71,7 +71,7 @@ class MigrateAttachments extends Command | ||||
|         foreach ($attachments as $att) { | ||||
|  | ||||
|             // move description: | ||||
|             $description = (string)$att->description; | ||||
|             $description = (string) $att->description; | ||||
|             if ('' !== $description) { | ||||
|  | ||||
|                 // find or create note: | ||||
| @@ -111,7 +111,7 @@ class MigrateAttachments extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|   | ||||
| @@ -110,7 +110,7 @@ class MigrateJournalNotes extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * MigrateRecurrenceMeta.php | ||||
|  * Copyright (c) 2020 james@firefly-iii.org | ||||
| @@ -81,7 +82,7 @@ class MigrateRecurrenceMeta extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
| @@ -103,7 +104,7 @@ class MigrateRecurrenceMeta extends Command | ||||
|      */ | ||||
|     private function migrateEntry(RecurrenceMeta $meta): int | ||||
|     { | ||||
|         $recurrence       = $meta->recurrence; | ||||
|         $recurrence = $meta->recurrence; | ||||
|         if (null === $recurrence) { | ||||
|             return 0; | ||||
|         } | ||||
| @@ -115,7 +116,7 @@ class MigrateRecurrenceMeta extends Command | ||||
|  | ||||
|         if ('tags' === $meta->name) { | ||||
|             $array = explode(',', $meta->value); | ||||
|             $value = json_encode($array); | ||||
|             $value = json_encode($array, JSON_THROW_ON_ERROR, 512); | ||||
|         } | ||||
|  | ||||
|         RecurrenceTransactionMeta::create( | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
| /** | ||||
|  * MigrateTagLocations.php | ||||
|  * Copyright (c) 2020 james@firefly-iii.org | ||||
| @@ -84,7 +85,7 @@ class MigrateTagLocations extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|   | ||||
| @@ -59,21 +59,21 @@ class MigrateToGroups extends Command | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly-iii:migrate-to-groups {--F|force : Force the migration, even if it fired before.}'; | ||||
|     /** @var JournalCLIRepositoryInterface */ | ||||
|     private $cliRepository; | ||||
|     private $count; | ||||
|     /** @var TransactionGroupFactory */ | ||||
|     private $groupFactory; | ||||
|     /** @var JournalRepositoryInterface */ | ||||
|     private $journalRepository; | ||||
|     /** @var JournalCLIRepositoryInterface */ | ||||
|     private $cliRepository; | ||||
|     /** @var JournalDestroyService */ | ||||
|     private $service; | ||||
|     private $count; | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      * @throws Exception | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
| @@ -112,29 +112,12 @@ class MigrateToGroups extends Command | ||||
|  | ||||
|         $this->markAsMigrated(); | ||||
|  | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->count             = 0; | ||||
|         $this->journalRepository = app(JournalRepositoryInterface::class); | ||||
|         $this->service           = app(JournalDestroyService::class); | ||||
|         $this->groupFactory      = app(TransactionGroupFactory::class); | ||||
|         $this->cliRepository     = app(JournalCLIRepositoryInterface::class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      * @param Transaction $transaction | ||||
|      * @param Transaction        $transaction | ||||
|      * | ||||
|      * @return Transaction|null | ||||
|      */ | ||||
| @@ -142,7 +125,7 @@ class MigrateToGroups extends Command | ||||
|     { | ||||
|         $set = $journal->transactions->filter( | ||||
|             static function (Transaction $subject) use ($transaction) { | ||||
|                 $amount     = (float)$transaction->amount * -1 === (float)$subject->amount; | ||||
|                 $amount     = (float) $transaction->amount * -1 === (float) $subject->amount; | ||||
|                 $identifier = $transaction->identifier === $subject->identifier; | ||||
|                 Log::debug(sprintf('Amount the same? %s', var_export($amount, true))); | ||||
|                 Log::debug(sprintf('ID the same?     %s', var_export($identifier, true))); | ||||
| @@ -168,6 +151,72 @@ class MigrateToGroups extends Command | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Transaction $left | ||||
|      * @param Transaction $right | ||||
|      * | ||||
|      * @return int|null | ||||
|      */ | ||||
|     private function getTransactionBudget(Transaction $left, Transaction $right): ?int | ||||
|     { | ||||
|         Log::debug('Now in getTransactionBudget()'); | ||||
|  | ||||
|         // try to get a budget ID from the left transaction: | ||||
|         /** @var Budget $budget */ | ||||
|         $budget = $left->budgets()->first(); | ||||
|         if (null !== $budget) { | ||||
|             Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); | ||||
|  | ||||
|             return (int) $budget->id; | ||||
|         } | ||||
|  | ||||
|         // try to get a budget ID from the right transaction: | ||||
|         /** @var Budget $budget */ | ||||
|         $budget = $right->budgets()->first(); | ||||
|         if (null !== $budget) { | ||||
|             Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); | ||||
|  | ||||
|             return (int) $budget->id; | ||||
|         } | ||||
|         Log::debug('Neither left or right have a budget, return NULL'); | ||||
|  | ||||
|         // if all fails, return NULL. | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Transaction $left | ||||
|      * @param Transaction $right | ||||
|      * | ||||
|      * @return int|null | ||||
|      */ | ||||
|     private function getTransactionCategory(Transaction $left, Transaction $right): ?int | ||||
|     { | ||||
|         Log::debug('Now in getTransactionCategory()'); | ||||
|  | ||||
|         // try to get a category ID from the left transaction: | ||||
|         /** @var Category $category */ | ||||
|         $category = $left->categories()->first(); | ||||
|         if (null !== $category) { | ||||
|             Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); | ||||
|  | ||||
|             return (int) $category->id; | ||||
|         } | ||||
|  | ||||
|         // try to get a category ID from the left transaction: | ||||
|         /** @var Category $category */ | ||||
|         $category = $right->categories()->first(); | ||||
|         if (null !== $category) { | ||||
|             Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); | ||||
|  | ||||
|             return (int) $category->id; | ||||
|         } | ||||
|         Log::debug('Neither left or right have a category, return NULL'); | ||||
|  | ||||
|         // if all fails, return NULL. | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $array | ||||
|      */ | ||||
| @@ -192,7 +241,7 @@ class MigrateToGroups extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
| @@ -302,7 +351,8 @@ class MigrateToGroups extends Command | ||||
|                 $this->error( | ||||
|                     sprintf( | ||||
|                         'Journal #%d has no opposing transaction for transaction #%d. Cannot upgrade this entry.', | ||||
|                         $journal->id, $transaction->id | ||||
|                         $journal->id, | ||||
|                         $transaction->id | ||||
|                     ) | ||||
|                 ); | ||||
|                 continue; | ||||
| @@ -365,81 +415,23 @@ class MigrateToGroups extends Command | ||||
|  | ||||
|         // report on result: | ||||
|         Log::debug( | ||||
|             sprintf('Migrated journal #%d into group #%d with these journals: #%s', | ||||
|                     $journal->id, $group->id, implode(', #', $group->transactionJournals->pluck('id')->toArray())) | ||||
|             sprintf( | ||||
|                 'Migrated journal #%d into group #%d with these journals: #%s', | ||||
|                 $journal->id, | ||||
|                 $group->id, | ||||
|                 implode(', #', $group->transactionJournals->pluck('id')->toArray()) | ||||
|             ) | ||||
|         ); | ||||
|         $this->line( | ||||
|             sprintf('Migrated journal #%d into group #%d with these journals: #%s', | ||||
|                     $journal->id, $group->id, implode(', #', $group->transactionJournals->pluck('id')->toArray())) | ||||
|             sprintf( | ||||
|                 'Migrated journal #%d into group #%d with these journals: #%s', | ||||
|                 $journal->id, | ||||
|                 $group->id, | ||||
|                 implode(', #', $group->transactionJournals->pluck('id')->toArray()) | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Transaction $left | ||||
|      * @param Transaction $right | ||||
|      * | ||||
|      * @return int|null | ||||
|      */ | ||||
|     private function getTransactionBudget(Transaction $left, Transaction $right): ?int | ||||
|     { | ||||
|         Log::debug('Now in getTransactionBudget()'); | ||||
|  | ||||
|         // try to get a budget ID from the left transaction: | ||||
|         /** @var Budget $budget */ | ||||
|         $budget = $left->budgets()->first(); | ||||
|         if (null !== $budget) { | ||||
|             Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); | ||||
|  | ||||
|             return (int)$budget->id; | ||||
|         } | ||||
|  | ||||
|         // try to get a budget ID from the right transaction: | ||||
|         /** @var Budget $budget */ | ||||
|         $budget = $right->budgets()->first(); | ||||
|         if (null !== $budget) { | ||||
|             Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); | ||||
|  | ||||
|             return (int)$budget->id; | ||||
|         } | ||||
|         Log::debug('Neither left or right have a budget, return NULL'); | ||||
|  | ||||
|         // if all fails, return NULL. | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Transaction $left | ||||
|      * @param Transaction $right | ||||
|      * | ||||
|      * @return int|null | ||||
|      */ | ||||
|     private function getTransactionCategory(Transaction $left, Transaction $right): ?int | ||||
|     { | ||||
|         Log::debug('Now in getTransactionCategory()'); | ||||
|  | ||||
|         // try to get a category ID from the left transaction: | ||||
|         /** @var Category $category */ | ||||
|         $category = $left->categories()->first(); | ||||
|         if (null !== $category) { | ||||
|             Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); | ||||
|  | ||||
|             return (int)$category->id; | ||||
|         } | ||||
|  | ||||
|         // try to get a category ID from the left transaction: | ||||
|         /** @var Category $category */ | ||||
|         $category = $right->categories()->first(); | ||||
|         if (null !== $category) { | ||||
|             Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); | ||||
|  | ||||
|             return (int)$category->id; | ||||
|         } | ||||
|         Log::debug('Neither left or right have a category, return NULL'); | ||||
|  | ||||
|         // if all fails, return NULL. | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
| @@ -448,4 +440,19 @@ class MigrateToGroups extends Command | ||||
|         app('fireflyconfig')->set(self::CONFIG_NAME, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->count             = 0; | ||||
|         $this->journalRepository = app(JournalRepositoryInterface::class); | ||||
|         $this->service           = app(JournalDestroyService::class); | ||||
|         $this->groupFactory      = app(TransactionGroupFactory::class); | ||||
|         $this->cliRepository     = app(JournalCLIRepositoryInterface::class); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -53,22 +53,21 @@ class MigrateToRules extends Command | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly-iii:bills-to-rules {--F|force : Force the execution of this command.}'; | ||||
|  | ||||
|     /** @var UserRepositoryInterface */ | ||||
|     private $userRepository; | ||||
|     /** @var RuleGroupRepositoryInterface */ | ||||
|     private $ruleGroupRepository; | ||||
|     /** @var BillRepositoryInterface */ | ||||
|     private $billRepository; | ||||
|     private $count; | ||||
|     /** @var RuleGroupRepositoryInterface */ | ||||
|     private $ruleGroupRepository; | ||||
|     /** @var RuleRepositoryInterface */ | ||||
|     private $ruleRepository; | ||||
|     private $count; | ||||
|     /** @var UserRepositoryInterface */ | ||||
|     private $userRepository; | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return int | ||||
|      * @throws FireflyException | ||||
|      * @return int | ||||
|      */ | ||||
|     public function handle(): int | ||||
|     { | ||||
| @@ -103,22 +102,6 @@ class MigrateToRules extends Command | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->count               = 0; | ||||
|         $this->userRepository      = app(UserRepositoryInterface::class); | ||||
|         $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); | ||||
|         $this->billRepository      = app(BillRepositoryInterface::class); | ||||
|         $this->ruleRepository      = app(RuleRepositoryInterface::class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
| @@ -126,7 +109,7 @@ class MigrateToRules extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
| @@ -141,45 +124,9 @@ class MigrateToRules extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Migrate bills to new rule structure for a specific user. | ||||
|      * | ||||
|      * @param User $user | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function migrateUser(User $user): void | ||||
|     { | ||||
|         $this->ruleGroupRepository->setUser($user); | ||||
|         $this->billRepository->setUser($user); | ||||
|         $this->ruleRepository->setUser($user); | ||||
|  | ||||
|         /** @var Preference $lang */ | ||||
|         $lang       = app('preferences')->getForUser($user, 'language', 'en_US'); | ||||
|         $groupTitle = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data); | ||||
|         $ruleGroup  = $this->ruleGroupRepository->findByTitle($groupTitle); | ||||
|         //$currency   = $this->getCurrency($user); | ||||
|  | ||||
|         if (null === $ruleGroup) { | ||||
|             $ruleGroup = $this->ruleGroupRepository->store( | ||||
|                 [ | ||||
|                     'title'       => (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data), | ||||
|                     'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $lang->data), | ||||
|                     'active'      => true, | ||||
|                 ] | ||||
|             ); | ||||
|         } | ||||
|         $bills = $this->billRepository->getBills(); | ||||
|  | ||||
|         /** @var Bill $bill */ | ||||
|         foreach ($bills as $bill) { | ||||
|             $this->migrateBill($ruleGroup, $bill, $lang); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param RuleGroup $ruleGroup | ||||
|      * @param Bill $bill | ||||
|      * @throws FireflyException | ||||
|      * @param RuleGroup  $ruleGroup | ||||
|      * @param Bill       $bill | ||||
|      * @param Preference $language | ||||
|      */ | ||||
|     private function migrateBill(RuleGroup $ruleGroup, Bill $bill, Preference $language): void | ||||
|     { | ||||
| @@ -194,8 +141,8 @@ class MigrateToRules extends Command | ||||
|             'active'          => true, | ||||
|             'strict'          => false, | ||||
|             'stop_processing' => false, // field is no longer used. | ||||
|             'title'           => (string)trans('firefly.rule_for_bill_title', ['name' => $bill->name], $language->data), | ||||
|             'description'     => (string)trans('firefly.rule_for_bill_description', ['name' => $bill->name], $language->data), | ||||
|             'title'           => (string) trans('firefly.rule_for_bill_title', ['name' => $bill->name], $language->data), | ||||
|             'description'     => (string) trans('firefly.rule_for_bill_description', ['name' => $bill->name], $language->data), | ||||
|             'trigger'         => 'store-journal', | ||||
|             'triggers'        => [ | ||||
|                 [ | ||||
| @@ -246,4 +193,57 @@ class MigrateToRules extends Command | ||||
|         $this->billRepository->update($bill, $newBillData); | ||||
|         $this->count++; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Migrate bills to new rule structure for a specific user. | ||||
|      * | ||||
|      * @param User $user | ||||
|      * | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function migrateUser(User $user): void | ||||
|     { | ||||
|         $this->ruleGroupRepository->setUser($user); | ||||
|         $this->billRepository->setUser($user); | ||||
|         $this->ruleRepository->setUser($user); | ||||
|  | ||||
|         /** @var Preference $lang */ | ||||
|         $lang       = app('preferences')->getForUser($user, 'language', 'en_US'); | ||||
|         $groupTitle = (string) trans('firefly.rulegroup_for_bills_title', [], $lang->data); | ||||
|         $ruleGroup  = $this->ruleGroupRepository->findByTitle($groupTitle); | ||||
|         //$currency   = $this->getCurrency($user); | ||||
|  | ||||
|         if (null === $ruleGroup) { | ||||
|             $ruleGroup = $this->ruleGroupRepository->store( | ||||
|                 [ | ||||
|                     'title'       => (string) trans('firefly.rulegroup_for_bills_title', [], $lang->data), | ||||
|                     'description' => (string) trans('firefly.rulegroup_for_bills_description', [], $lang->data), | ||||
|                     'active'      => true, | ||||
|                 ] | ||||
|             ); | ||||
|         } | ||||
|         $bills = $this->billRepository->getBills(); | ||||
|  | ||||
|         /** @var Bill $bill */ | ||||
|         foreach ($bills as $bill) { | ||||
|             $this->migrateBill($ruleGroup, $bill, $lang); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->count               = 0; | ||||
|         $this->userRepository      = app(UserRepositoryInterface::class); | ||||
|         $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); | ||||
|         $this->billRepository      = app(BillRepositoryInterface::class); | ||||
|         $this->ruleRepository      = app(RuleRepositoryInterface::class); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -40,7 +40,6 @@ use Illuminate\Console\Command; | ||||
|  */ | ||||
| class OtherCurrenciesCorrections extends Command | ||||
| { | ||||
|  | ||||
|     public const CONFIG_NAME = '480_other_currencies'; | ||||
|     /** | ||||
|      * The console command description. | ||||
| @@ -58,14 +57,14 @@ class OtherCurrenciesCorrections extends Command | ||||
|     private $accountCurrencies; | ||||
|     /** @var AccountRepositoryInterface */ | ||||
|     private $accountRepos; | ||||
|     /** @var CurrencyRepositoryInterface */ | ||||
|     private $currencyRepos; | ||||
|     /** @var JournalRepositoryInterface */ | ||||
|     private $journalRepos; | ||||
|     /** @var JournalCLIRepositoryInterface */ | ||||
|     private $cliRepos; | ||||
|     /** @var int */ | ||||
|     private $count; | ||||
|     /** @var CurrencyRepositoryInterface */ | ||||
|     private $currencyRepos; | ||||
|     /** @var JournalRepositoryInterface */ | ||||
|     private $journalRepos; | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
| @@ -94,23 +93,6 @@ class OtherCurrenciesCorrections extends Command | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->count             = 0; | ||||
|         $this->accountCurrencies = []; | ||||
|         $this->accountRepos      = app(AccountRepositoryInterface::class); | ||||
|         $this->currencyRepos     = app(CurrencyRepositoryInterface::class); | ||||
|         $this->journalRepos      = app(JournalRepositoryInterface::class); | ||||
|         $this->cliRepos          = app(JournalCLIRepositoryInterface::class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Account $account | ||||
|      * | ||||
| @@ -136,110 +118,6 @@ class OtherCurrenciesCorrections extends Command | ||||
|         $this->accountCurrencies[$accountId] = $currency; | ||||
|  | ||||
|         return $currency; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isExecuted(): bool | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private function markAsExecuted(): void | ||||
|     { | ||||
|         app('fireflyconfig')->set(self::CONFIG_NAME, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      */ | ||||
|     private function updateJournalCurrency(TransactionJournal $journal): void | ||||
|     { | ||||
|         $this->accountRepos->setUser($journal->user); | ||||
|         $this->journalRepos->setUser($journal->user); | ||||
|         $this->currencyRepos->setUser($journal->user); | ||||
|         $this->cliRepos->setUser($journal->user); | ||||
|  | ||||
|         $leadTransaction = $this->getLeadTransaction($journal); | ||||
|  | ||||
|         if (null === $leadTransaction) { | ||||
|             // @codeCoverageIgnoreStart | ||||
|             $this->error(sprintf('Could not reliably determine which transaction is in the lead for transaction journal #%d.', $journal->id)); | ||||
|  | ||||
|             return; | ||||
|             // @codeCoverageIgnoreEnd | ||||
|         } | ||||
|  | ||||
|         /** @var Account $account */ | ||||
|         $account  = $leadTransaction->account; | ||||
|         $currency = $this->getCurrency($account); | ||||
|         if (null === $currency) { | ||||
|             // @codeCoverageIgnoreStart | ||||
|             $this->error(sprintf('Account #%d ("%s") has no currency preference, so transaction journal #%d can\'t be corrected', | ||||
|                                  $account->id, $account->name, $journal->id)); | ||||
|             $this->count++; | ||||
|  | ||||
|             return; | ||||
|             // @codeCoverageIgnoreEnd | ||||
|         } | ||||
|         // fix each transaction: | ||||
|         $journal->transactions->each( | ||||
|             static function (Transaction $transaction) use ($currency) { | ||||
|                 if (null === $transaction->transaction_currency_id) { | ||||
|                     $transaction->transaction_currency_id = $currency->id; | ||||
|                     $transaction->save(); | ||||
|                 } | ||||
|  | ||||
|                 // when mismatch in transaction: | ||||
|                 if (!((int)$transaction->transaction_currency_id === (int)$currency->id)) { | ||||
|                     $transaction->foreign_currency_id     = (int)$transaction->transaction_currency_id; | ||||
|                     $transaction->foreign_amount          = $transaction->amount; | ||||
|                     $transaction->transaction_currency_id = $currency->id; | ||||
|                     $transaction->save(); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         // also update the journal, of course: | ||||
|         $journal->transaction_currency_id = $currency->id; | ||||
|         $this->count++; | ||||
|         $journal->save(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for | ||||
|      * the accounts they are linked to. | ||||
|      * | ||||
|      * Both source and destination must match the respective currency preference of the related asset account. | ||||
|      * So FF3 must verify all transactions. | ||||
|      * | ||||
|      */ | ||||
|     private function updateOtherJournalsCurrencies(): void | ||||
|     { | ||||
|         $set = | ||||
|             $this->cliRepos->getAllJournals( | ||||
|                 [ | ||||
|                     TransactionType::WITHDRAWAL, | ||||
|                     TransactionType::DEPOSIT, | ||||
|                     TransactionType::OPENING_BALANCE, | ||||
|                     TransactionType::RECONCILIATION, | ||||
|                 ] | ||||
|             ); | ||||
|  | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         foreach ($set as $journal) { | ||||
|             $this->updateJournalCurrency($journal); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -281,4 +159,129 @@ class OtherCurrenciesCorrections extends Command | ||||
|  | ||||
|         return $lead; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isExecuted(): bool | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private function markAsExecuted(): void | ||||
|     { | ||||
|         app('fireflyconfig')->set(self::CONFIG_NAME, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->count             = 0; | ||||
|         $this->accountCurrencies = []; | ||||
|         $this->accountRepos      = app(AccountRepositoryInterface::class); | ||||
|         $this->currencyRepos     = app(CurrencyRepositoryInterface::class); | ||||
|         $this->journalRepos      = app(JournalRepositoryInterface::class); | ||||
|         $this->cliRepos          = app(JournalCLIRepositoryInterface::class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $journal | ||||
|      */ | ||||
|     private function updateJournalCurrency(TransactionJournal $journal): void | ||||
|     { | ||||
|         $this->accountRepos->setUser($journal->user); | ||||
|         $this->journalRepos->setUser($journal->user); | ||||
|         $this->currencyRepos->setUser($journal->user); | ||||
|         $this->cliRepos->setUser($journal->user); | ||||
|  | ||||
|         $leadTransaction = $this->getLeadTransaction($journal); | ||||
|  | ||||
|         if (null === $leadTransaction) { | ||||
|             // @codeCoverageIgnoreStart | ||||
|             $this->error(sprintf('Could not reliably determine which transaction is in the lead for transaction journal #%d.', $journal->id)); | ||||
|  | ||||
|             return; | ||||
|             // @codeCoverageIgnoreEnd | ||||
|         } | ||||
|  | ||||
|         /** @var Account $account */ | ||||
|         $account  = $leadTransaction->account; | ||||
|         $currency = $this->getCurrency($account); | ||||
|         if (null === $currency) { | ||||
|             // @codeCoverageIgnoreStart | ||||
|             $this->error( | ||||
|                 sprintf( | ||||
|                     'Account #%d ("%s") has no currency preference, so transaction journal #%d can\'t be corrected', | ||||
|                     $account->id, | ||||
|                     $account->name, | ||||
|                     $journal->id | ||||
|                 ) | ||||
|             ); | ||||
|             $this->count++; | ||||
|  | ||||
|             return; | ||||
|             // @codeCoverageIgnoreEnd | ||||
|         } | ||||
|         // fix each transaction: | ||||
|         $journal->transactions->each( | ||||
|             static function (Transaction $transaction) use ($currency) { | ||||
|                 if (null === $transaction->transaction_currency_id) { | ||||
|                     $transaction->transaction_currency_id = $currency->id; | ||||
|                     $transaction->save(); | ||||
|                 } | ||||
|  | ||||
|                 // when mismatch in transaction: | ||||
|                 if (!((int) $transaction->transaction_currency_id === (int) $currency->id)) { | ||||
|                     $transaction->foreign_currency_id     = (int) $transaction->transaction_currency_id; | ||||
|                     $transaction->foreign_amount          = $transaction->amount; | ||||
|                     $transaction->transaction_currency_id = $currency->id; | ||||
|                     $transaction->save(); | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
|         // also update the journal, of course: | ||||
|         $journal->transaction_currency_id = $currency->id; | ||||
|         $this->count++; | ||||
|         $journal->save(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for | ||||
|      * the accounts they are linked to. | ||||
|      * | ||||
|      * Both source and destination must match the respective currency preference of the related asset account. | ||||
|      * So FF3 must verify all transactions. | ||||
|      * | ||||
|      */ | ||||
|     private function updateOtherJournalsCurrencies(): void | ||||
|     { | ||||
|         $set | ||||
|             = $this->cliRepos->getAllJournals( | ||||
|             [ | ||||
|                 TransactionType::WITHDRAWAL, | ||||
|                 TransactionType::DEPOSIT, | ||||
|                 TransactionType::OPENING_BALANCE, | ||||
|                 TransactionType::RECONCILIATION, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         /** @var TransactionJournal $journal */ | ||||
|         foreach ($set as $journal) { | ||||
|             $this->updateJournalCurrency($journal); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -76,7 +76,7 @@ class RenameAccountMeta extends Command | ||||
|             $count += AccountMeta::where('name', $old)->update(['name' => $new]); | ||||
|  | ||||
|             // delete empty entries while we're at it. | ||||
|             AccountMeta::where('name', $new)->where('data','""')->delete(); | ||||
|             AccountMeta::where('name', $new)->where('data', '""')->delete(); | ||||
|         } | ||||
|  | ||||
|         $this->markAsExecuted(); | ||||
| @@ -101,7 +101,7 @@ class RenameAccountMeta extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|   | ||||
| @@ -50,15 +50,12 @@ class TransactionIdentifier extends Command | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'firefly-iii:transaction-identifiers {--F|force : Force the execution of this command.}'; | ||||
|  | ||||
|     /** @var JournalRepositoryInterface */ | ||||
|     private $journalRepository; | ||||
|  | ||||
|     /** @var JournalCLIRepositoryInterface */ | ||||
|     private $cliRepository; | ||||
|  | ||||
|     /** @var int */ | ||||
|     private $count; | ||||
|     /** @var JournalRepositoryInterface */ | ||||
|     private $journalRepository; | ||||
|  | ||||
|     /** | ||||
|      * This method gives all transactions which are part of a split journal (so more than 2) a sort of "order" so they are easier | ||||
| @@ -108,17 +105,36 @@ class TransactionIdentifier extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * @param Transaction $transaction | ||||
|      * @param array       $exclude | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      * @return Transaction|null | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     private function findOpposing(Transaction $transaction, array $exclude): ?Transaction | ||||
|     { | ||||
|         $this->journalRepository = app(JournalRepositoryInterface::class); | ||||
|         $this->cliRepository     = app(JournalCLIRepositoryInterface::class); | ||||
|         $this->count             = 0; | ||||
|         // find opposing: | ||||
|         $amount = bcmul((string) $transaction->amount, '-1'); | ||||
|  | ||||
|         try { | ||||
|             /** @var Transaction $opposing */ | ||||
|             $opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id) | ||||
|                                    ->where('amount', $amount)->where('identifier', '=', 0) | ||||
|                                    ->whereNotIn('id', $exclude) | ||||
|                                    ->first(); | ||||
|             // @codeCoverageIgnoreStart | ||||
|         } catch (QueryException $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|             $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); | ||||
|             $this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); | ||||
|             $this->error('Please run "php artisan migrate" to add this field to the table.'); | ||||
|             $this->info('Then, run "php artisan firefly:upgrade-database" to try again.'); | ||||
|  | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // @codeCoverageIgnoreEnd | ||||
|  | ||||
|         return $opposing; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -128,7 +144,7 @@ class TransactionIdentifier extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
| @@ -142,6 +158,20 @@ class TransactionIdentifier extends Command | ||||
|         app('fireflyconfig')->set(self::CONFIG_NAME, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->journalRepository = app(JournalRepositoryInterface::class); | ||||
|         $this->cliRepository     = app(JournalCLIRepositoryInterface::class); | ||||
|         $this->count             = 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Grab all positive transactions from this journal that are not deleted. for each one, grab the negative opposing one | ||||
|      * which has 0 as an identifier and give it the same identifier. | ||||
| @@ -171,36 +201,4 @@ class TransactionIdentifier extends Command | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Transaction $transaction | ||||
|      * @param array $exclude | ||||
|      * @return Transaction|null | ||||
|      */ | ||||
|     private function findOpposing(Transaction $transaction, array $exclude): ?Transaction | ||||
|     { | ||||
|         // find opposing: | ||||
|         $amount = bcmul((string)$transaction->amount, '-1'); | ||||
|  | ||||
|         try { | ||||
|             /** @var Transaction $opposing */ | ||||
|             $opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id) | ||||
|                                    ->where('amount', $amount)->where('identifier', '=', 0) | ||||
|                                    ->whereNotIn('id', $exclude) | ||||
|                                    ->first(); | ||||
|             // @codeCoverageIgnoreStart | ||||
|         } catch (QueryException $e) { | ||||
|             Log::error($e->getMessage()); | ||||
|             $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); | ||||
|             $this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); | ||||
|             $this->error('Please run "php artisan migrate" to add this field to the table.'); | ||||
|             $this->info('Then, run "php artisan firefly:upgrade-database" to try again.'); | ||||
|  | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // @codeCoverageIgnoreEnd | ||||
|  | ||||
|         return $opposing; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,7 +23,6 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Console\Commands\Upgrade; | ||||
|  | ||||
|  | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Transaction; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| @@ -41,7 +40,6 @@ use Log; | ||||
|  */ | ||||
| class TransferCurrenciesCorrections extends Command | ||||
| { | ||||
|  | ||||
|     public const CONFIG_NAME = '480_transfer_currencies'; | ||||
|     /** | ||||
|      * The console command description. | ||||
| @@ -59,27 +57,26 @@ class TransferCurrenciesCorrections extends Command | ||||
|     private $accountCurrencies; | ||||
|     /** @var AccountRepositoryInterface */ | ||||
|     private $accountRepos; | ||||
|     /** @var CurrencyRepositoryInterface */ | ||||
|     private $currencyRepos; | ||||
|     /** @var JournalRepositoryInterface */ | ||||
|     private $journalRepos; | ||||
|     /** @var JournalCLIRepositoryInterface */ | ||||
|     private $cliRepos; | ||||
|     /** @var int */ | ||||
|     private $count; | ||||
|  | ||||
|     /** @var Transaction The source transaction of the current journal. */ | ||||
|     private $sourceTransaction; | ||||
|     /** @var Account The source account of the current journal. */ | ||||
|     private $sourceAccount; | ||||
|     /** @var TransactionCurrency The currency preference of the source account of the current journal. */ | ||||
|     private $sourceCurrency; | ||||
|     /** @var Transaction The destination transaction of the current journal. */ | ||||
|     private $destinationTransaction; | ||||
|     /** @var CurrencyRepositoryInterface */ | ||||
|     private $currencyRepos; | ||||
|     /** @var Account The destination account of the current journal. */ | ||||
|     private $destinationAccount; | ||||
|     /** @var TransactionCurrency The currency preference of the destination account of the current journal. */ | ||||
|     private $destinationCurrency; | ||||
|     /** @var Transaction The destination transaction of the current journal. */ | ||||
|     private $destinationTransaction; | ||||
|     /** @var JournalRepositoryInterface */ | ||||
|     private $journalRepos; | ||||
|     /** @var Account The source account of the current journal. */ | ||||
|     private $sourceAccount; | ||||
|     /** @var TransactionCurrency The currency preference of the source account of the current journal. */ | ||||
|     private $sourceCurrency; | ||||
|     /** @var Transaction The source transaction of the current journal. */ | ||||
|     private $sourceTransaction; | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
| @@ -118,21 +115,216 @@ class TransferCurrenciesCorrections extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      * The destination transaction must have a currency. If not, it will be added by | ||||
|      * taking it from the destination account's preference. | ||||
|      */ | ||||
|     private function stupidLaravel(): void | ||||
|     private function fixDestNoCurrency(): void | ||||
|     { | ||||
|         $this->count             = 0; | ||||
|         $this->accountRepos      = app(AccountRepositoryInterface::class); | ||||
|         $this->currencyRepos     = app(CurrencyRepositoryInterface::class); | ||||
|         $this->journalRepos      = app(JournalRepositoryInterface::class); | ||||
|         $this->cliRepos          = app(JournalCLIRepositoryInterface::class); | ||||
|         $this->accountCurrencies = []; | ||||
|         $this->resetInformation(); | ||||
|         if (null === $this->destinationTransaction->transaction_currency_id && null !== $this->destinationCurrency) { | ||||
|             $this->destinationTransaction | ||||
|                 ->transaction_currency_id | ||||
|                      = (int) $this->destinationCurrency->id; | ||||
|             $message = sprintf( | ||||
|                 'Transaction #%d has no currency setting, now set to %s.', | ||||
|                 $this->destinationTransaction->id, | ||||
|                 $this->destinationCurrency->code | ||||
|             ); | ||||
|             Log::debug($message); | ||||
|             $this->line($message); | ||||
|             $this->count++; | ||||
|             $this->destinationTransaction->save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code | ||||
|      * to restore it. | ||||
|      */ | ||||
|     private function fixDestNullForeignAmount(): void | ||||
|     { | ||||
|         if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) { | ||||
|             $this->destinationTransaction->foreign_amount = bcmul((string) $this->sourceTransaction->foreign_amount, '-1'); | ||||
|             $this->destinationTransaction->save(); | ||||
|             $this->count++; | ||||
|             Log::debug( | ||||
|                 sprintf( | ||||
|                     'Restored foreign amount of destination transaction #%d to %s', | ||||
|                     $this->destinationTransaction->id, | ||||
|                     $this->destinationTransaction->foreign_amount | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The destination transaction must have the correct currency. If not, it will be set by | ||||
|      * taking it from the destination account's preference. | ||||
|      */ | ||||
|     private function fixDestinationUnmatchedCurrency(): void | ||||
|     { | ||||
|         if (null !== $this->destinationCurrency | ||||
|             && null === $this->destinationTransaction->foreign_amount | ||||
|             && (int) $this->destinationTransaction->transaction_currency_id !== (int) $this->destinationCurrency->id | ||||
|         ) { | ||||
|             $message = sprintf( | ||||
|                 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', | ||||
|                 $this->destinationTransaction->id, | ||||
|                 $this->destinationTransaction->transaction_currency_id, | ||||
|                 $this->destinationAccount->id, | ||||
|                 $this->destinationTransaction->amount | ||||
|             ); | ||||
|             Log::debug($message); | ||||
|             $this->line($message); | ||||
|             $this->count++; | ||||
|             $this->destinationTransaction->transaction_currency_id = (int) $this->destinationCurrency->id; | ||||
|             $this->destinationTransaction->save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the destination account currency is the same as the source currency, | ||||
|      * both foreign_amount and foreign_currency_id fields must be NULL | ||||
|      * for both transactions (because foreign currency info would not make sense) | ||||
|      * | ||||
|      */ | ||||
|     private function fixInvalidForeignCurrency(): void | ||||
|     { | ||||
|         if ((int) $this->destinationCurrency->id === (int) $this->sourceCurrency->id) { | ||||
|             // update both transactions to match: | ||||
|             $this->sourceTransaction->foreign_amount      = null; | ||||
|             $this->sourceTransaction->foreign_currency_id = null; | ||||
|  | ||||
|             $this->destinationTransaction->foreign_amount      = null; | ||||
|             $this->destinationTransaction->foreign_currency_id = null; | ||||
|  | ||||
|             $this->sourceTransaction->save(); | ||||
|             $this->destinationTransaction->save(); | ||||
|  | ||||
|             Log::debug( | ||||
|                 sprintf( | ||||
|                     'Currency for account "%s" is %s, and currency for account "%s" is also | ||||
|              %s, so transactions #%d and #%d has been verified to be to %s exclusively.', | ||||
|                     $this->destinationAccount->name, | ||||
|                     $this->destinationCurrency->code, | ||||
|                     $this->sourceAccount->name, | ||||
|                     $this->sourceCurrency->code, | ||||
|                     $this->sourceTransaction->id, | ||||
|                     $this->destinationTransaction->id, | ||||
|                     $this->sourceCurrency->code | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If destination account currency is different from source account currency, | ||||
|      * then both transactions must get the source account's currency as normal currency | ||||
|      * and the opposing account's currency as foreign currency. | ||||
|      */ | ||||
|     private function fixMismatchedForeignCurrency(): void | ||||
|     { | ||||
|         if ((int) $this->sourceCurrency->id !== (int) $this->destinationCurrency->id) { | ||||
|             $this->sourceTransaction->transaction_currency_id      = $this->sourceCurrency->id; | ||||
|             $this->sourceTransaction->foreign_currency_id          = $this->destinationCurrency->id; | ||||
|             $this->destinationTransaction->transaction_currency_id = $this->sourceCurrency->id; | ||||
|             $this->destinationTransaction->foreign_currency_id     = $this->destinationCurrency->id; | ||||
|  | ||||
|             $this->sourceTransaction->save(); | ||||
|             $this->destinationTransaction->save(); | ||||
|             $this->count++; | ||||
|             Log::debug(sprintf('Verified foreign currency ID of transaction #%d and #%d', $this->sourceTransaction->id, $this->destinationTransaction->id)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The source transaction must have a currency. If not, it will be added by | ||||
|      * taking it from the source account's preference. | ||||
|      */ | ||||
|     private function fixSourceNoCurrency(): void | ||||
|     { | ||||
|         if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) { | ||||
|             $this->sourceTransaction | ||||
|                 ->transaction_currency_id | ||||
|                      = (int) $this->sourceCurrency->id; | ||||
|             $message = sprintf( | ||||
|                 'Transaction #%d has no currency setting, now set to %s.', | ||||
|                 $this->sourceTransaction->id, | ||||
|                 $this->sourceCurrency->code | ||||
|             ); | ||||
|             Log::debug($message); | ||||
|             $this->line($message); | ||||
|             $this->count++; | ||||
|             $this->sourceTransaction->save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the foreign amount of the source transaction is null, but that of the other isn't, use this piece of code | ||||
|      * to restore it. | ||||
|      */ | ||||
|     private function fixSourceNullForeignAmount(): void | ||||
|     { | ||||
|         if (null === $this->sourceTransaction->foreign_amount && null !== $this->destinationTransaction->foreign_amount) { | ||||
|             $this->sourceTransaction->foreign_amount = bcmul((string) $this->destinationTransaction->foreign_amount, '-1'); | ||||
|             $this->sourceTransaction->save(); | ||||
|             $this->count++; | ||||
|             Log::debug( | ||||
|                 sprintf( | ||||
|                     'Restored foreign amount of source transaction #%d to %s', | ||||
|                     $this->sourceTransaction->id, | ||||
|                     $this->sourceTransaction->foreign_amount | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The source transaction must have the correct currency. If not, it will be set by | ||||
|      * taking it from the source account's preference. | ||||
|      */ | ||||
|     private function fixSourceUnmatchedCurrency(): void | ||||
|     { | ||||
|         if (null !== $this->sourceCurrency | ||||
|             && null === $this->sourceTransaction->foreign_amount | ||||
|             && (int) $this->sourceTransaction->transaction_currency_id !== (int) $this->sourceCurrency->id | ||||
|         ) { | ||||
|             $message = sprintf( | ||||
|                 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', | ||||
|                 $this->sourceTransaction->id, | ||||
|                 $this->sourceTransaction->transaction_currency_id, | ||||
|                 $this->sourceAccount->id, | ||||
|                 $this->sourceTransaction->amount | ||||
|             ); | ||||
|             Log::debug($message); | ||||
|             $this->line($message); | ||||
|             $this->count++; | ||||
|             $this->sourceTransaction->transaction_currency_id = (int) $this->sourceCurrency->id; | ||||
|             $this->sourceTransaction->save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method makes sure that the transaction journal uses the currency given in the source transaction. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      */ | ||||
|     private function fixTransactionJournalCurrency(TransactionJournal $journal): void | ||||
|     { | ||||
|         if ((int) $journal->transaction_currency_id !== (int) $this->sourceCurrency->id) { | ||||
|             $oldCurrencyCode                  = $journal->transactionCurrency->code ?? '(nothing)'; | ||||
|             $journal->transaction_currency_id = $this->sourceCurrency->id; | ||||
|             $message                          = sprintf( | ||||
|                 'Transfer #%d ("%s") has been updated to use %s instead of %s.', | ||||
|                 $journal->id, | ||||
|                 $journal->description, | ||||
|                 $this->sourceCurrency->code, | ||||
|                 $oldCurrencyCode | ||||
|             ); | ||||
|             $this->count++; | ||||
|             $this->line($message); | ||||
|             Log::debug($message); | ||||
|             $journal->save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -150,7 +342,7 @@ class TransferCurrenciesCorrections extends Command | ||||
|             return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore | ||||
|         } | ||||
|         // TODO we can use getAccountCurrency() instead | ||||
|         $currencyId = (int)$this->accountRepos->getMetaValue($account, 'currency_id'); | ||||
|         $currencyId = (int) $this->accountRepos->getMetaValue($account, 'currency_id'); | ||||
|         $result     = $this->currencyRepos->findNull($currencyId); | ||||
|         if (null === $result) { | ||||
|             // @codeCoverageIgnoreStart | ||||
| @@ -162,8 +354,20 @@ class TransferCurrenciesCorrections extends Command | ||||
|         $this->accountCurrencies[$accountId] = $result; | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Extract destination transaction, destination account + destination account currency from the journal. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function getDestinationInformation(TransactionJournal $journal): void | ||||
|     { | ||||
|         $this->destinationTransaction = $this->getDestinationTransaction($journal); | ||||
|         $this->destinationAccount     = null === $this->destinationTransaction ? null : $this->destinationTransaction->account; | ||||
|         $this->destinationCurrency    = null === $this->destinationAccount ? null : $this->getCurrency($this->destinationAccount); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -177,6 +381,20 @@ class TransferCurrenciesCorrections extends Command | ||||
|         return $transfer->transactions()->where('amount', '>', 0)->first(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extract source transaction, source account + source account currency from the journal. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function getSourceInformation(TransactionJournal $journal): void | ||||
|     { | ||||
|         $this->sourceTransaction = $this->getSourceTransaction($journal); | ||||
|         $this->sourceAccount     = null === $this->sourceTransaction ? null : $this->sourceTransaction->account; | ||||
|         $this->sourceCurrency    = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param TransactionJournal $transfer | ||||
|      * | ||||
| @@ -188,6 +406,19 @@ class TransferCurrenciesCorrections extends Command | ||||
|         return $transfer->transactions()->where('amount', '<', 0)->first(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Is either the source or destination transaction NULL? | ||||
|      * | ||||
|      * @return bool | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function isEmptyTransactions(): bool | ||||
|     { | ||||
|         return null === $this->sourceTransaction || null === $this->destinationTransaction | ||||
|                || null === $this->sourceAccount | ||||
|                || null === $this->destinationAccount; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
| @@ -195,12 +426,56 @@ class TransferCurrenciesCorrections extends Command | ||||
|     { | ||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||
|         if (null !== $configVar) { | ||||
|             return (bool)$configVar->data; | ||||
|             return (bool) $configVar->data; | ||||
|         } | ||||
|  | ||||
|         return false; // @codeCoverageIgnore | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function isNoCurrencyPresent(): bool | ||||
|     { | ||||
|         // source account must have a currency preference. | ||||
|         if (null === $this->sourceCurrency) { | ||||
|             $message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name); | ||||
|             Log::error($message); | ||||
|             $this->error($message); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // destination account must have a currency preference. | ||||
|         if (null === $this->destinationCurrency) { | ||||
|             $message = sprintf( | ||||
|                 'Account #%d ("%s") must have currency preference but has none.', | ||||
|                 $this->destinationAccount->id, | ||||
|                 $this->destinationAccount->name | ||||
|             ); | ||||
|             Log::error($message); | ||||
|             $this->error($message); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Is this a split transaction journal? | ||||
|      * | ||||
|      * @param TransactionJournal $transfer | ||||
|      * | ||||
|      * @return bool | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function isSplitJournal(TransactionJournal $transfer): bool | ||||
|     { | ||||
|         return $transfer->transactions->count() > 2; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
| @@ -210,27 +485,18 @@ class TransferCurrenciesCorrections extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method makes sure that the transaction journal uses the currency given in the source transaction. | ||||
|      * Reset all the class fields for the current transfer. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function fixTransactionJournalCurrency(TransactionJournal $journal): void | ||||
|     private function resetInformation(): void | ||||
|     { | ||||
|         if ((int)$journal->transaction_currency_id !== (int)$this->sourceCurrency->id) { | ||||
|             $oldCurrencyCode                  = $journal->transactionCurrency->code ?? '(nothing)'; | ||||
|             $journal->transaction_currency_id = $this->sourceCurrency->id; | ||||
|             $message                          = sprintf( | ||||
|                 'Transfer #%d ("%s") has been updated to use %s instead of %s.', | ||||
|                 $journal->id, | ||||
|                 $journal->description, | ||||
|                 $this->sourceCurrency->code, | ||||
|                 $oldCurrencyCode | ||||
|             ); | ||||
|             $this->count++; | ||||
|             $this->line($message); | ||||
|             Log::debug($message); | ||||
|             $journal->save(); | ||||
|         } | ||||
|         $this->sourceTransaction      = null; | ||||
|         $this->sourceAccount          = null; | ||||
|         $this->sourceCurrency         = null; | ||||
|         $this->destinationTransaction = null; | ||||
|         $this->destinationAccount     = null; | ||||
|         $this->destinationCurrency    = null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -253,43 +519,21 @@ class TransferCurrenciesCorrections extends Command | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reset all the class fields for the current transfer. | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function resetInformation(): void | ||||
|     { | ||||
|         $this->sourceTransaction      = null; | ||||
|         $this->sourceAccount          = null; | ||||
|         $this->sourceCurrency         = null; | ||||
|         $this->destinationTransaction = null; | ||||
|         $this->destinationAccount     = null; | ||||
|         $this->destinationCurrency    = null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extract source transaction, source account + source account currency from the journal. | ||||
|      * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is | ||||
|      * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should | ||||
|      * be called from the handle method instead of using the constructor to initialize the command. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function getSourceInformation(TransactionJournal $journal): void | ||||
|     private function stupidLaravel(): void | ||||
|     { | ||||
|         $this->sourceTransaction = $this->getSourceTransaction($journal); | ||||
|         $this->sourceAccount     = null === $this->sourceTransaction ? null : $this->sourceTransaction->account; | ||||
|         $this->sourceCurrency    = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extract destination transaction, destination account + destination account currency from the journal. | ||||
|      * | ||||
|      * @param TransactionJournal $journal | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function getDestinationInformation(TransactionJournal $journal): void | ||||
|     { | ||||
|         $this->destinationTransaction = $this->getDestinationTransaction($journal); | ||||
|         $this->destinationAccount     = null === $this->destinationTransaction ? null : $this->destinationTransaction->account; | ||||
|         $this->destinationCurrency    = null === $this->destinationAccount ? null : $this->getCurrency($this->destinationAccount); | ||||
|         $this->count             = 0; | ||||
|         $this->accountRepos      = app(AccountRepositoryInterface::class); | ||||
|         $this->currencyRepos     = app(CurrencyRepositoryInterface::class); | ||||
|         $this->journalRepos      = app(JournalRepositoryInterface::class); | ||||
|         $this->cliRepos          = app(JournalCLIRepositoryInterface::class); | ||||
|         $this->accountCurrencies = []; | ||||
|         $this->resetInformation(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -297,7 +541,6 @@ class TransferCurrenciesCorrections extends Command | ||||
|      */ | ||||
|     private function updateTransferCurrency(TransactionJournal $transfer): void | ||||
|     { | ||||
|  | ||||
|         $this->resetInformation(); | ||||
|  | ||||
|         // @codeCoverageIgnoreStart | ||||
| @@ -325,7 +568,8 @@ class TransferCurrenciesCorrections extends Command | ||||
|         // @codeCoverageIgnoreStart | ||||
|         if ($this->isNoCurrencyPresent()) { | ||||
|             $this->error( | ||||
|                 sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id)); | ||||
|                 sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id) | ||||
|             ); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
| @@ -355,225 +599,5 @@ class TransferCurrenciesCorrections extends Command | ||||
|  | ||||
|         // fix journal itself: | ||||
|         $this->fixTransactionJournalCurrency($transfer); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The source transaction must have a currency. If not, it will be added by | ||||
|      * taking it from the source account's preference. | ||||
|      */ | ||||
|     private function fixSourceNoCurrency(): void | ||||
|     { | ||||
|         if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) { | ||||
|             $this->sourceTransaction | ||||
|                 ->transaction_currency_id = (int)$this->sourceCurrency->id; | ||||
|             $message                      = sprintf('Transaction #%d has no currency setting, now set to %s.', | ||||
|                                                     $this->sourceTransaction->id, $this->sourceCurrency->code); | ||||
|             Log::debug($message); | ||||
|             $this->line($message); | ||||
|             $this->count++; | ||||
|             $this->sourceTransaction->save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The destination transaction must have a currency. If not, it will be added by | ||||
|      * taking it from the destination account's preference. | ||||
|      */ | ||||
|     private function fixDestNoCurrency(): void | ||||
|     { | ||||
|         if (null === $this->destinationTransaction->transaction_currency_id && null !== $this->destinationCurrency) { | ||||
|             $this->destinationTransaction | ||||
|                 ->transaction_currency_id = (int)$this->destinationCurrency->id; | ||||
|             $message                      = sprintf('Transaction #%d has no currency setting, now set to %s.', | ||||
|                                                     $this->destinationTransaction->id, $this->destinationCurrency->code); | ||||
|             Log::debug($message); | ||||
|             $this->line($message); | ||||
|             $this->count++; | ||||
|             $this->destinationTransaction->save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The source transaction must have the correct currency. If not, it will be set by | ||||
|      * taking it from the source account's preference. | ||||
|      */ | ||||
|     private function fixSourceUnmatchedCurrency(): void | ||||
|     { | ||||
|         if (null !== $this->sourceCurrency && | ||||
|             null === $this->sourceTransaction->foreign_amount && | ||||
|             (int)$this->sourceTransaction->transaction_currency_id !== (int)$this->sourceCurrency->id | ||||
|         ) { | ||||
|             $message = sprintf( | ||||
|                 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', | ||||
|                 $this->sourceTransaction->id, | ||||
|                 $this->sourceTransaction->transaction_currency_id, | ||||
|                 $this->sourceAccount->id, | ||||
|                 $this->sourceTransaction->amount | ||||
|             ); | ||||
|             Log::debug($message); | ||||
|             $this->line($message); | ||||
|             $this->count++; | ||||
|             $this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id; | ||||
|             $this->sourceTransaction->save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The destination transaction must have the correct currency. If not, it will be set by | ||||
|      * taking it from the destination account's preference. | ||||
|      */ | ||||
|     private function fixDestinationUnmatchedCurrency(): void | ||||
|     { | ||||
|         if (null !== $this->destinationCurrency && | ||||
|             null === $this->destinationTransaction->foreign_amount && | ||||
|             (int)$this->destinationTransaction->transaction_currency_id !== (int)$this->destinationCurrency->id | ||||
|         ) { | ||||
|             $message = sprintf( | ||||
|                 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', | ||||
|                 $this->destinationTransaction->id, | ||||
|                 $this->destinationTransaction->transaction_currency_id, | ||||
|                 $this->destinationAccount->id, | ||||
|                 $this->destinationTransaction->amount | ||||
|             ); | ||||
|             Log::debug($message); | ||||
|             $this->line($message); | ||||
|             $this->count++; | ||||
|             $this->destinationTransaction->transaction_currency_id = (int)$this->destinationCurrency->id; | ||||
|             $this->destinationTransaction->save(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Is this a split transaction journal? | ||||
|      * | ||||
|      * @param TransactionJournal $transfer | ||||
|      * @return bool | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function isSplitJournal(TransactionJournal $transfer): bool | ||||
|     { | ||||
|         return $transfer->transactions->count() > 2; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Is either the source or destination transaction NULL? | ||||
|      * @return bool | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function isEmptyTransactions(): bool | ||||
|     { | ||||
|         return null === $this->sourceTransaction || null === $this->destinationTransaction || | ||||
|                null === $this->sourceAccount || null === $this->destinationAccount; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the destination account currency is the same as the source currency, | ||||
|      * both foreign_amount and foreign_currency_id fields must be NULL | ||||
|      * for both transactions (because foreign currency info would not make sense) | ||||
|      * | ||||
|      */ | ||||
|     private function fixInvalidForeignCurrency(): void | ||||
|     { | ||||
|         if ((int)$this->destinationCurrency->id === (int)$this->sourceCurrency->id) { | ||||
|             // update both transactions to match: | ||||
|             $this->sourceTransaction->foreign_amount      = null; | ||||
|             $this->sourceTransaction->foreign_currency_id = null; | ||||
|  | ||||
|             $this->destinationTransaction->foreign_amount      = null; | ||||
|             $this->destinationTransaction->foreign_currency_id = null; | ||||
|  | ||||
|             $this->sourceTransaction->save(); | ||||
|             $this->destinationTransaction->save(); | ||||
|  | ||||
|             Log::debug( | ||||
|                 sprintf( | ||||
|                     'Currency for account "%s" is %s, and currency for account "%s" is also | ||||
|              %s, so transactions #%d and #%d has been verified to be to %s exclusively.', | ||||
|                     $this->destinationAccount->name, $this->destinationCurrency->code, | ||||
|                     $this->sourceAccount->name, $this->sourceCurrency->code, | ||||
|                     $this->sourceTransaction->id, $this->destinationTransaction->id, $this->sourceCurrency->code | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If destination account currency is different from source account currency, | ||||
|      * then both transactions must get the source account's currency as normal currency | ||||
|      * and the opposing account's currency as foreign currency. | ||||
|      */ | ||||
|     private function fixMismatchedForeignCurrency(): void | ||||
|     { | ||||
|         if ((int)$this->sourceCurrency->id !== (int)$this->destinationCurrency->id) { | ||||
|             $this->sourceTransaction->transaction_currency_id      = $this->sourceCurrency->id; | ||||
|             $this->sourceTransaction->foreign_currency_id          = $this->destinationCurrency->id; | ||||
|             $this->destinationTransaction->transaction_currency_id = $this->sourceCurrency->id; | ||||
|             $this->destinationTransaction->foreign_currency_id     = $this->destinationCurrency->id; | ||||
|  | ||||
|             $this->sourceTransaction->save(); | ||||
|             $this->destinationTransaction->save(); | ||||
|             $this->count++; | ||||
|             Log::debug(sprintf('Verified foreign currency ID of transaction #%d and #%d', $this->sourceTransaction->id, $this->destinationTransaction->id)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the foreign amount of the source transaction is null, but that of the other isn't, use this piece of code | ||||
|      * to restore it. | ||||
|      */ | ||||
|     private function fixSourceNullForeignAmount(): void | ||||
|     { | ||||
|         if (null === $this->sourceTransaction->foreign_amount && null !== $this->destinationTransaction->foreign_amount) { | ||||
|             $this->sourceTransaction->foreign_amount = bcmul((string)$this->destinationTransaction->foreign_amount, '-1'); | ||||
|             $this->sourceTransaction->save(); | ||||
|             $this->count++; | ||||
|             Log::debug(sprintf('Restored foreign amount of source transaction #%d to %s', | ||||
|                                $this->sourceTransaction->id, $this->sourceTransaction->foreign_amount)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code | ||||
|      * to restore it. | ||||
|      */ | ||||
|     private function fixDestNullForeignAmount(): void | ||||
|     { | ||||
|         if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) { | ||||
|             $this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1'); | ||||
|             $this->destinationTransaction->save(); | ||||
|             $this->count++; | ||||
|             Log::debug(sprintf('Restored foreign amount of destination transaction #%d to %s', | ||||
|                                $this->destinationTransaction->id, $this->destinationTransaction->foreign_amount)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     private function isNoCurrencyPresent(): bool | ||||
|     { | ||||
|         // source account must have a currency preference. | ||||
|         if (null === $this->sourceCurrency) { | ||||
|             $message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name); | ||||
|             Log::error($message); | ||||
|             $this->error($message); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // destination account must have a currency preference. | ||||
|         if (null === $this->destinationCurrency) { | ||||
|             $message = sprintf('Account #%d ("%s") must have currency preference but has none.', | ||||
|                                $this->destinationAccount->id, $this->destinationAccount->name); | ||||
|             Log::error($message); | ||||
|             $this->error($message); | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,7 @@ use Illuminate\Console\Command; | ||||
|  | ||||
| /** | ||||
|  * Class UpgradeDatabase | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class UpgradeDatabase extends Command | ||||
| @@ -112,9 +113,9 @@ class UpgradeDatabase extends Command | ||||
|             echo $result; | ||||
|         } | ||||
|         // set new DB version. | ||||
|         app('fireflyconfig')->set('db_version', (int)config('firefly.db_version')); | ||||
|         app('fireflyconfig')->set('db_version', (int) config('firefly.db_version')); | ||||
|         // index will set FF3 version. | ||||
|         app('fireflyconfig')->set('ff3_version', (string)config('firefly.version')); | ||||
|         app('fireflyconfig')->set('ff3_version', (string) config('firefly.version')); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user