Compare commits

..

186 Commits

Author SHA1 Message Date
github-actions[bot]
7f827fb277 Merge pull request #10995 from firefly-iii/release-1759464256
🤖 Automatically merge the PR into the develop branch.
2025-10-03 06:04:22 +02:00
JC5
8e700944fd 🤖 Auto commit for release 'develop' on 2025-10-03 2025-10-03 06:04:16 +02:00
James Cole
037a128942 Add missing translation. 2025-10-03 05:57:21 +02:00
James Cole
ca364dc877 Remove stats from empty objects. 2025-10-03 05:53:50 +02:00
James Cole
e55af7186c Fix #10994 2025-10-03 05:24:24 +02:00
James Cole
354bbebbee Fix #10965 2025-10-02 19:52:51 +02:00
James Cole
a70fab1e87 Clean up piggy alerts. 2025-10-02 19:49:32 +02:00
James Cole
b9c93091cd Merge pull request #10993 from ctrl-f5/fix/report-category-trans-key
use correct translation key for category report income table
2025-10-02 10:41:56 +02:00
Nicky De Maeyer
4349f8f303 Merge branch 'develop' into fix/report-category-trans-key 2025-10-02 10:31:35 +02:00
Nicky De Maeyer
5088e20f25 use correct translation key for category report income table 2025-10-02 10:25:11 +02:00
Sander Dorigo
3f81aa7403 Fix #10988 2025-10-02 10:15:18 +02:00
github-actions[bot]
8885d1dbeb Merge pull request #10992 from firefly-iii/release-1759383569
🤖 Automatically merge the PR into the develop branch.
2025-10-02 07:39:36 +02:00
JC5
a6ba75d528 🤖 Auto commit for release 'develop' on 2025-10-02 2025-10-02 07:39:29 +02:00
github-actions[bot]
667cbb1332 Merge pull request #10991 from firefly-iii/release-1759381485
🤖 Automatically merge the PR into the develop branch.
2025-10-02 07:04:53 +02:00
JC5
d1bae875f7 🤖 Auto commit for release 'develop' on 2025-10-02 2025-10-02 07:04:45 +02:00
James Cole
c5cf529413 Update changelog. 2025-10-02 06:59:56 +02:00
James Cole
62b9f2785f Ignore db version in scripts. 2025-10-02 06:55:09 +02:00
James Cole
3b85f87502 Stop using "db_version", check versions instead. 2025-10-02 06:53:23 +02:00
James Cole
87d3d14504 Fix #10990 2025-10-01 20:28:24 +02:00
github-actions[bot]
5637573fd0 Merge pull request #10982 from firefly-iii/release-1759116145
🤖 Automatically merge the PR into the develop branch.
2025-09-29 05:22:35 +02:00
JC5
48255ae6ee 🤖 Auto commit for release 'develop' on 2025-09-29 2025-09-29 05:22:25 +02:00
James Cole
ea02986170 Merge pull request #10979 from maksimkurb/patch-1
Add Kazakhstani Tenge (KZT) currency
2025-09-28 15:28:15 +02:00
mergify[bot]
f4fbc15ac6 Merge branch 'develop' into patch-1 2025-09-28 07:17:01 +00:00
Maxim Kurbatov
a02c4b42a4 Add Kazakhstani Tenge (KZT) currency
Signed-off-by: Maxim Kurbatov <maxim@kurb.me>
2025-09-28 11:49:37 +05:00
James Cole
1ff47441ce Also add no budget and no category overview. 2025-09-27 16:07:03 +02:00
James Cole
2cc8568077 Clean up code. 2025-09-27 06:40:56 +02:00
github-actions[bot]
408687ec6b Merge pull request #10975 from firefly-iii/release-1758945886
🤖 Automatically merge the PR into the develop branch.
2025-09-27 06:04:55 +02:00
JC5
308abffb0b 🤖 Auto commit for release 'develop' on 2025-09-27 2025-09-27 06:04:46 +02:00
James Cole
6743b3fe83 Remove stats for other objects as well. 2025-09-27 06:01:14 +02:00
James Cole
ac0113e445 Fix some rector code. 2025-09-27 05:57:58 +02:00
James Cole
0f0a28c3d9 Add cached periods for tags. 2025-09-27 05:49:22 +02:00
James Cole
79f2d70211 Fix #10974 2025-09-27 05:35:20 +02:00
James Cole
8a06c0f7ec Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
# Conflicts:
#	app/Support/Http/Controllers/PeriodOverview.php
2025-09-27 05:32:41 +02:00
James Cole
c54da62005 Enable period statistics for category. 2025-09-27 05:31:26 +02:00
github-actions[bot]
e5923202af Merge pull request #10973 from firefly-iii/release-1758914746
🤖 Automatically merge the PR into the develop branch.
2025-09-26 21:25:55 +02:00
JC5
eb6f78406e 🤖 Auto commit for release 'develop' on 2025-09-26 2025-09-26 21:25:46 +02:00
James Cole
d61f87f649 Fix URL 2025-09-26 20:47:44 +02:00
James Cole
33dcce7525 Optimize query for collecting transactions. 2025-09-26 20:46:23 +02:00
James Cole
b1d86c3a37 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
# Conflicts:
#	app/Models/PeriodStatistic.php
2025-09-26 19:48:46 +02:00
James Cole
822dee6e70 Allow statistics to be removed from /flush 2025-09-26 19:48:20 +02:00
github-actions[bot]
f6dac83777 Merge pull request #10972 from firefly-iii/release-1758908619
🤖 Automatically merge the PR into the develop branch.
2025-09-26 19:43:52 +02:00
JC5
d3c557ca22 🤖 Auto commit for release 'develop' on 2025-09-26 2025-09-26 19:43:39 +02:00
James Cole
853a99852e Also remove them when updating transactions. 2025-09-26 19:39:18 +02:00
James Cole
8f24ac4fcd Remove statistics when creating a new journal. 2025-09-26 19:38:26 +02:00
James Cole
8b09cfb8c9 Optimize query for period statistics. 2025-09-26 19:32:53 +02:00
James Cole
18ae950d2e Optimize queries. 2025-09-26 06:09:44 +02:00
James Cole
69dfbda847 Add empty statistic if necessary. 2025-09-26 06:06:43 +02:00
James Cole
4ec2fcdb8a Optimize queries for statistics. 2025-09-26 06:05:37 +02:00
James Cole
08879d31ba Rearrange some code. 2025-09-26 05:33:35 +02:00
James Cole
66d09450d3 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-25 19:17:20 +02:00
James Cole
74f7c07a76 Add filter for transfer type. 2025-09-25 19:17:10 +02:00
github-actions[bot]
ae3d0a3e49 Merge pull request #10964 from firefly-iii/release-1758820271
🤖 Automatically merge the PR into the develop branch.
2025-09-25 19:11:21 +02:00
JC5
61e8d7d7a2 🤖 Auto commit for release 'develop' on 2025-09-25 2025-09-25 19:11:11 +02:00
James Cole
62c5440605 Add table for period statistics, and see what happens re: performance. 2025-09-25 19:07:02 +02:00
James Cole
0aa90b9453 Fix #10960 2025-09-24 19:51:39 +02:00
Sander Dorigo
855bc2f8e7 attempted fix for #10956 2025-09-24 14:39:54 +02:00
github-actions[bot]
d8f05492c3 Merge pull request #10955 from firefly-iii/release-1758652896
🤖 Automatically merge the PR into the develop branch.
2025-09-23 20:41:46 +02:00
JC5
4a264f34fa 🤖 Auto commit for release 'develop' on 2025-09-23 2025-09-23 20:41:36 +02:00
James Cole
5a1413e758 Fix #10954 2025-09-23 20:22:58 +02:00
github-actions[bot]
84dbeeb0ce Merge pull request #10946 from firefly-iii/release-1758511378
🤖 Automatically merge the PR into the develop branch.
2025-09-22 05:23:04 +02:00
JC5
d868dc0945 🤖 Auto commit for release 'develop' on 2025-09-22 2025-09-22 05:22:58 +02:00
James Cole
beecf9c229 Make sure demo user cannot send notifications. 2025-09-21 18:00:23 +02:00
github-actions[bot]
e39ba46398 Merge pull request #10943 from firefly-iii/release-1758470243
🤖 Automatically merge the PR into the develop branch.
2025-09-21 17:57:31 +02:00
JC5
e6b6a3cee5 🤖 Auto commit for release 'develop' on 2025-09-21 2025-09-21 17:57:23 +02:00
James Cole
b5483f6ad3 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-21 17:52:16 +02:00
James Cole
a751218d53 Fix rule order for #10938 2025-09-21 17:52:07 +02:00
github-actions[bot]
2af5e6eeef Merge pull request #10942 from firefly-iii/release-1758460508
🤖 Automatically merge the PR into the develop branch.
2025-09-21 15:15:17 +02:00
JC5
013c43f9f2 🤖 Auto commit for release 'develop' on 2025-09-21 2025-09-21 15:15:08 +02:00
James Cole
7e08a1f33c Possible fix for #10940, not sure. 2025-09-21 15:11:16 +02:00
James Cole
e592b56d7a Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-21 15:06:32 +02:00
github-actions[bot]
a2479f71fe Merge pull request #10937 from firefly-iii/release-1758437913
🤖 Automatically merge the PR into the develop branch.
2025-09-21 08:58:42 +02:00
JC5
7d3b993b98 🤖 Auto commit for release 'develop' on 2025-09-21 2025-09-21 08:58:33 +02:00
James Cole
90623101a3 Add earned + spent, needs cleaning up still. 2025-09-21 08:54:26 +02:00
github-actions[bot]
e2eca79b25 Merge pull request #10936 from firefly-iii/release-1758436089
🤖 Automatically merge the PR into the develop branch.
2025-09-21 08:28:16 +02:00
JC5
8c0ee8f024 🤖 Auto commit for release 'develop' on 2025-09-21 2025-09-21 08:28:09 +02:00
James Cole
69cae3ae55 Fix autocomplete. 2025-09-21 08:13:13 +02:00
James Cole
8a06298385 Repair charts and balances. 2025-09-21 07:35:46 +02:00
James Cole
acc3c294d8 Fix #10924 2025-09-17 20:46:03 +02:00
James Cole
dbf7dba421 Fix #10916 2025-09-17 20:04:24 +02:00
James Cole
65813f290d Expand changelog. 2025-09-17 13:48:56 +02:00
James Cole
3491fbb99d Force account search to validate it did not just find the source account. #10920 2025-09-17 07:09:40 +02:00
James Cole
cb6b3d5f85 Fix #10891 2025-09-16 21:09:29 +02:00
github-actions[bot]
956d4e09c3 Merge pull request #10917 from firefly-iii/release-1758048927
🤖 Automatically merge the PR into the develop branch.
2025-09-16 20:55:35 +02:00
JC5
6a7c35e7bc 🤖 Auto commit for release 'develop' on 2025-09-16 2025-09-16 20:55:27 +02:00
James Cole
090aecb5f5 Clean up command. 2025-09-16 20:50:25 +02:00
James Cole
b653d63d3d Add new correction command, will probably fix #10833 2025-09-16 20:44:54 +02:00
github-actions[bot]
258dbf4a98 Merge pull request #10913 from firefly-iii/release-1757957159
🤖 Automatically merge the PR into the develop branch.
2025-09-15 19:26:09 +02:00
JC5
53335077ff 🤖 Auto commit for release 'develop' on 2025-09-15 2025-09-15 19:25:59 +02:00
James Cole
ecfb3e2f95 Fix #10854 and another issue (again). 2025-09-15 19:20:51 +02:00
github-actions[bot]
f512e6724e Merge pull request #10911 from firefly-iii/release-1757906627
🤖 Automatically merge the PR into the develop branch.
2025-09-15 05:23:56 +02:00
JC5
de9efb0727 🤖 Auto commit for release 'develop' on 2025-09-15 2025-09-15 05:23:47 +02:00
James Cole
9075fa8ac8 Allow webhooks to be send for budget limit update. 2025-09-14 09:21:32 +02:00
James Cole
768bd892c8 Allow sending of webhooks from budget limit store. 2025-09-14 09:14:41 +02:00
James Cole
9d9483e20f Refactor models. 2025-09-14 09:00:01 +02:00
James Cole
935453796e Allow budget update to have webhooks controlled with "fire_webhooks" 2025-09-14 08:59:00 +02:00
James Cole
c2d3f5da16 Allow budget store to have optional webhook using "fire_webhooks". 2025-09-14 08:55:29 +02:00
James Cole
9e6f9d16e4 Move observers to attributes. 2025-09-14 08:55:08 +02:00
James Cole
fad016f92f Update changelog. 2025-09-14 07:46:46 +02:00
James Cole
30df6684cb Fix another missing filter for #10803 2025-09-14 07:45:54 +02:00
James Cole
4aa911420a Merge branch 'main' into develop 2025-09-13 18:53:00 +02:00
github-actions[bot]
19555a7046 Merge pull request #10907 from firefly-iii/release-1757782324
🤖 Automatically merge the PR into the develop branch.
2025-09-13 18:52:14 +02:00
JC5
e5c409a8fc 🤖 Auto commit for release 'develop' on 2025-09-13 2025-09-13 18:52:04 +02:00
github-actions[bot]
3adf3d2fdb Merge pull request #10906 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-09-13 18:38:12 +02:00
github-actions[bot]
0923d5a23e Merge pull request #10905 from firefly-iii/release-1757781480
🤖 Automatically merge the PR into the develop branch.
2025-09-13 18:38:07 +02:00
JC5
76b8cdc385 🤖 Auto commit for release 'v6.4.0' on 2025-09-13 2025-09-13 18:38:00 +02:00
James Cole
0a27da83eb Merge branch 'main' into develop
# Conflicts:
#	.github/workflows/release.yml
2025-09-13 18:33:47 +02:00
github-actions[bot]
17d6e2be85 Merge pull request #10903 from firefly-iii/release-1757781134
🤖 Automatically merge the PR into the develop branch.
2025-09-13 18:32:20 +02:00
JC5
7381f3eba9 🤖 Auto commit for release 'v6.4.0' on 2025-09-13 2025-09-13 18:32:14 +02:00
James Cole
7f2ef1b8e1 Should not be necessary, but OK/ 2025-09-13 18:28:26 +02:00
github-actions[bot]
9dccae2402 Merge pull request #10901 from firefly-iii/release-1757780827
🤖 Automatically merge the PR into the develop branch.
2025-09-13 18:27:17 +02:00
JC5
073afd5b6e 🤖 Auto commit for release 'v6.4.0' on 2025-09-13 2025-09-13 18:27:07 +02:00
James Cole
4167d85be2 Expand changelog. 2025-09-13 07:30:50 +02:00
James Cole
ee28d1307d Fix #10871 2025-09-13 07:30:30 +02:00
James Cole
7562215666 Add some extra explanation. 2025-09-13 07:24:03 +02:00
github-actions[bot]
0203b918e9 Merge pull request #10899 from firefly-iii/release-1757740712
🤖 Automatically merge the PR into the develop branch.
2025-09-13 07:18:41 +02:00
JC5
ae7c664418 🤖 Auto commit for release 'develop' on 2025-09-13 2025-09-13 07:18:32 +02:00
James Cole
f13e0991fb Fix #10898 2025-09-13 07:13:30 +02:00
James Cole
deae94b658 Set user ID from object, fix #10891 2025-09-11 19:37:34 +02:00
James Cole
c38c752520 Fix #10888 2025-09-10 20:42:15 +02:00
James Cole
28e7df2527 Small php fixes. 2025-09-10 16:16:31 +02:00
James Cole
cb0b42e44b Replace method calls. 2025-09-10 16:07:19 +02:00
James Cole
a3674c4dfe No need to use the exception either. 2025-09-10 07:03:41 +02:00
James Cole
27480561ee Catch random exception because why not. 2025-09-10 07:03:23 +02:00
github-actions[bot]
6ea7152423 Merge pull request #10886 from firefly-iii/release-1757480185
🤖 Automatically merge the PR into the develop branch.
2025-09-10 06:56:34 +02:00
JC5
dab95f7a86 🤖 Auto commit for release 'develop' on 2025-09-10 2025-09-10 06:56:25 +02:00
James Cole
adf34805a8 Shiny release PR. 2025-09-10 06:52:51 +02:00
James Cole
93e926465f Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-10 06:51:11 +02:00
github-actions[bot]
5b0be91f93 Merge pull request #10885 from firefly-iii/release-1757479782
🤖 Automatically merge the PR into the develop branch.
2025-09-10 06:49:50 +02:00
JC5
01e7b604da 🤖 Auto commit for release 'develop' on 2025-09-10 2025-09-10 06:49:42 +02:00
James Cole
974a550d22 Merge branch 'main' into develop 2025-09-10 06:44:16 +02:00
James Cole
58d175444b Fix #10883 2025-09-10 06:43:24 +02:00
James Cole
29d8861e96 Merge pull request #10882 from firefly-iii/dependabot/npm_and_yarn/npm_and_yarn-f5c1666f0c 2025-09-10 06:02:57 +02:00
dependabot[bot]
eb832c750f Bump vite in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `vite` from 7.1.2 to 7.1.5
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.5
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 20:59:51 +00:00
James Cole
134770644a Fix amounts in transaction. 2025-09-09 17:14:53 +02:00
James Cole
a9f21c9371 Extra text in the PR. 2025-09-09 15:53:25 +02:00
github-actions[bot]
781947beeb Merge pull request #10879 from firefly-iii/release-1757425805
🤖 Automatically merge the PR into the develop branch.
2025-09-09 15:50:18 +02:00
JC5
9760cd2f97 🤖 Auto commit for release 'develop' on 2025-09-09 2025-09-09 15:50:06 +02:00
James Cole
d317e9ec32 Update changelog. 2025-09-09 15:41:19 +02:00
James Cole
534f7fcadb Merge branch 'main' into develop 2025-09-09 15:36:55 +02:00
James Cole
fb3f7a1d4b Merge pull request #10874 from firefly-iii/dependabot/github_actions/actions/stale-10
Bump actions/stale from 9 to 10
2025-09-08 11:41:37 +02:00
James Cole
bf2c3e3561 Merge pull request #10873 from firefly-iii/dependabot/github_actions/actions/github-script-8
Bump actions/github-script from 7 to 8
2025-09-08 11:41:12 +02:00
github-actions[bot]
b670f81dcd Merge pull request #10875 from firefly-iii/release-1757313349
🤖 Automatically merge the PR into the develop branch.
2025-09-08 08:36:01 +02:00
JC5
7aac1cdf67 🤖 Auto commit for release 'develop' on 2025-09-08 2025-09-08 08:35:49 +02:00
Sander Dorigo
fa0ac8a16c Fix php files 2025-09-08 08:31:09 +02:00
dependabot[bot]
0990b1f0b4 Bump actions/stale from 9 to 10
Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9...v10)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: '10'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 03:08:14 +00:00
dependabot[bot]
c1922670c8 Bump actions/github-script from 7 to 8
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 03:08:10 +00:00
James Cole
81cd89d66f Final nestor and phpstan fixes. 2025-09-07 17:42:16 +02:00
James Cole
f5c202543c Clean up code. 2025-09-07 17:31:08 +02:00
James Cole
034f437c6b Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-07 17:30:43 +02:00
James Cole
8550ba6138 Remove unused code. 2025-09-07 17:30:34 +02:00
github-actions[bot]
a51501025b Merge pull request #10872 from firefly-iii/release-1757249926
🤖 Automatically merge the PR into the develop branch.
2025-09-07 14:58:53 +02:00
JC5
a9d26e4586 🤖 Auto commit for release 'develop' on 2025-09-07 2025-09-07 14:58:46 +02:00
James Cole
949691935f Catch exceptions. 2025-09-07 14:54:44 +02:00
James Cole
4835b05304 Optimize currency search. 2025-09-07 14:49:49 +02:00
James Cole
cce5a73dd2 Clean up and fix phpstan issues. 2025-09-07 14:28:58 +02:00
github-actions[bot]
3152f635dd Merge pull request #10868 from firefly-iii/release-1757235851
🤖 Automatically merge the PR into the develop branch.
2025-09-07 11:04:21 +02:00
JC5
12e8651017 🤖 Auto commit for release 'develop' on 2025-09-07 2025-09-07 11:04:11 +02:00
James Cole
f88286c848 Merge branches 'develop' and 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-07 10:59:12 +02:00
James Cole
39cf0533d9 Fix various phpstan issues. 2025-09-07 10:59:07 +02:00
github-actions[bot]
6d82887960 Merge pull request #10867 from firefly-iii/release-1757224570
🤖 Automatically merge the PR into the develop branch.
2025-09-07 07:56:20 +02:00
JC5
262f1bae34 🤖 Auto commit for release 'develop' on 2025-09-07 2025-09-07 07:56:10 +02:00
James Cole
75dfdcc220 Fix final phpstan issue. 2025-09-07 07:51:35 +02:00
James Cole
c3a3bdf525 Fix phpstan issues. 2025-09-07 07:51:01 +02:00
James Cole
602df95f3c Fix phpstan issues. 2025-09-07 07:43:04 +02:00
James Cole
296a64e284 Fix phpstan issues 2025-09-07 07:31:00 +02:00
James Cole
b87b99a755 Fix phpstan issues. 2025-09-07 06:25:26 +02:00
James Cole
bf53f5d6b7 Fix various phpstan issues. 2025-09-05 05:09:46 +02:00
James Cole
bcbb868acc Update packages. 2025-09-04 19:46:52 +02:00
James Cole
b3a9e6569e Fix #10854 2025-09-04 06:26:28 +02:00
James Cole
f174f124ef Fix # 2025-09-04 06:26:12 +02:00
James Cole
8745377f31 Fix #10833 2025-09-04 06:22:18 +02:00
James Cole
cf76e93f31 Remove primary currency from views, add some debug for #10854 2025-09-04 06:22:04 +02:00
James Cole
19e36f6f6e Share primary currency to all pages. 2025-09-03 21:02:13 +02:00
James Cole
2a7bb6f048 Fix #10853 2025-09-03 20:34:40 +02:00
James Cole
536eacbc0c Fix sort params 2025-09-03 20:34:28 +02:00
James Cole
af78158d0b Fix minor issues. 2025-09-02 18:38:34 +02:00
github-actions[bot]
fb97910a34 Merge pull request #10848 from firefly-iii/release-1756787147
🤖 Automatically merge the PR into the develop branch.
2025-09-02 06:25:54 +02:00
JC5
66b4d14129 🤖 Auto commit for release 'develop' on 2025-09-02 2025-09-02 06:25:47 +02:00
James Cole
fd53047dbc Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-02 06:03:17 +02:00
James Cole
5f6fc1bab4 Fix #10846 2025-09-02 06:03:11 +02:00
github-actions[bot]
cd0b64bd24 Merge pull request #10845 from firefly-iii/release-1756752249
🤖 Automatically merge the PR into the develop branch.
2025-09-01 20:44:18 +02:00
JC5
91b26bce0e 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 20:44:09 +02:00
James Cole
26a8bd921d Fix columns, improve views. 2025-09-01 20:39:40 +02:00
github-actions[bot]
ad77d55c16 Merge pull request #10844 from firefly-iii/release-1756729274
🤖 Automatically merge the PR into the develop branch.
2025-09-01 14:21:21 +02:00
JC5
445a383dd0 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 14:21:14 +02:00
Sander Dorigo
1666af939e Fix null 2025-09-01 14:03:21 +02:00
488 changed files with 10412 additions and 11672 deletions

View File

@@ -402,16 +402,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.86.0", "version": "v3.88.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36" "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4a952bd19dc97879b0620f495552ef09b55f7d36", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36", "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -422,39 +422,38 @@
"ext-hash": "*", "ext-hash": "*",
"ext-json": "*", "ext-json": "*",
"ext-tokenizer": "*", "ext-tokenizer": "*",
"fidry/cpu-core-counter": "^1.2", "fidry/cpu-core-counter": "^1.3",
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"react/child-process": "^0.6.6", "react/child-process": "^0.6.6",
"react/event-loop": "^1.5", "react/event-loop": "^1.5",
"react/promise": "^3.2", "react/promise": "^3.3",
"react/socket": "^1.16", "react/socket": "^1.16",
"react/stream": "^1.4", "react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
"symfony/console": "^5.4.47 || ^6.4.13 || ^7.0", "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0", "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0", "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0", "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0", "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/polyfill-mbstring": "^1.32", "symfony/polyfill-mbstring": "^1.33",
"symfony/polyfill-php80": "^1.32", "symfony/polyfill-php80": "^1.33",
"symfony/polyfill-php81": "^1.32", "symfony/polyfill-php81": "^1.33",
"symfony/process": "^5.4.47 || ^6.4.20 || ^7.2", "symfony/polyfill-php84": "^1.33",
"symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0" "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0"
}, },
"require-dev": { "require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.6", "facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.29.14", "infection/infection": "^0.31.0",
"justinrainbow/json-schema": "^5.3 || ^6.4", "justinrainbow/json-schema": "^6.5",
"keradus/cli-executor": "^2.2", "keradus/cli-executor": "^2.2",
"mikey179/vfsstream": "^1.6.12", "mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.8", "php-coveralls/php-coveralls": "^2.8",
"php-cs-fixer/accessible-object": "^1.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25", "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
"symfony/polyfill-php84": "^1.32", "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2",
"symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1", "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2"
"symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1"
}, },
"suggest": { "suggest": {
"ext-dom": "For handling output formats in XML", "ext-dom": "For handling output formats in XML",
@@ -495,7 +494,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.86.0" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2"
}, },
"funding": [ "funding": [
{ {
@@ -503,7 +502,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-08-13T22:36:21+00:00" "time": "2025-09-27T00:24:15+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@@ -1253,16 +1252,16 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v7.3.3", "version": "v7.3.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db",
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1327,7 +1326,7 @@
"terminal" "terminal"
], ],
"support": { "support": {
"source": "https://github.com/symfony/console/tree/v7.3.3" "source": "https://github.com/symfony/console/tree/v7.3.4"
}, },
"funding": [ "funding": [
{ {
@@ -1347,7 +1346,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-08-25T06:35:40+00:00" "time": "2025-09-22T15:31:00+00:00"
}, },
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
@@ -2285,17 +2284,97 @@
"time": "2024-09-09T11:45:10+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{ {
"name": "symfony/process", "name": "symfony/polyfill-php84",
"version": "v7.3.3", "version": "v1.33.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/process.git", "url": "https://github.com/symfony/polyfill-php84.git",
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1" "reference": "d8ced4d875142b6a7426000426b8abc631d6b191"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191",
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1", "reference": "d8ced4d875142b6a7426000426b8abc631d6b191",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php84\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-06-24T13:30:11+00:00"
},
{
"name": "symfony/process",
"version": "v7.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "f24f8f316367b30810810d4eb30c543d7003ff3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b",
"reference": "f24f8f316367b30810810d4eb30c543d7003ff3b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2327,7 +2406,7 @@
"description": "Executes commands in sub-processes", "description": "Executes commands in sub-processes",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/process/tree/v7.3.3" "source": "https://github.com/symfony/process/tree/v7.3.4"
}, },
"funding": [ "funding": [
{ {
@@ -2347,7 +2426,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-08-18T09:42:54+00:00" "time": "2025-09-11T10:12:26+00:00"
}, },
{ {
"name": "symfony/service-contracts", "name": "symfony/service-contracts",
@@ -2496,16 +2575,16 @@
}, },
{ {
"name": "symfony/string", "name": "symfony/string",
"version": "v7.3.3", "version": "v7.3.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/string.git", "url": "https://github.com/symfony/string.git",
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" "reference": "f96476035142921000338bad71e5247fbc138872"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872",
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", "reference": "f96476035142921000338bad71e5247fbc138872",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2520,7 +2599,6 @@
}, },
"require-dev": { "require-dev": {
"symfony/emoji": "^7.1", "symfony/emoji": "^7.1",
"symfony/error-handler": "^6.4|^7.0",
"symfony/http-client": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0",
"symfony/intl": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0",
"symfony/translation-contracts": "^2.5|^3.0", "symfony/translation-contracts": "^2.5|^3.0",
@@ -2563,7 +2641,7 @@
"utf8" "utf8"
], ],
"support": { "support": {
"source": "https://github.com/symfony/string/tree/v7.3.3" "source": "https://github.com/symfony/string/tree/v7.3.4"
}, },
"funding": [ "funding": [
{ {
@@ -2583,7 +2661,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-08-25T06:35:40+00:00" "time": "2025-09-11T14:36:48+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],

View File

@@ -1,6 +1,4 @@
parameters: parameters:
scanFiles:
- ../_ide_helper.php
paths: paths:
- ../app - ../app
- ../database - ../database
@@ -9,28 +7,24 @@ parameters:
- ../bootstrap/app.php - ../bootstrap/app.php
universalObjectCratesClasses: universalObjectCratesClasses:
- Illuminate\Database\Eloquent\Model - Illuminate\Database\Eloquent\Model
# TODO: slowly remove these parameters and fix the issues found.
reportUnmatchedIgnoredErrors: true reportUnmatchedIgnoredErrors: true
ignoreErrors: ignoreErrors:
# TODO: slowly remove these exceptions and fix the issues found. # all errors below I will never fix.
- '#Dynamic call to static method#' # all the Laravel ORM things depend on this. - '#expects view-string\|null, string given#'
- identifier: varTag.nativeType - '#expects view-string, string given#'
- identifier: varTag.type - "#Parameter \\#[1-2] \\$num[1-2] of function bc[a-z]+ expects numeric-string, [a-z\\-|&]+ given#"
- identifier: missingType.generics # not interesting enough to fix.
- -
identifier: larastan.noEnvCallsOutsideOfConfig identifier: larastan.noEnvCallsOutsideOfConfig
path: ../app/Console/Commands/System/CreatesDatabase.php path: ../app/Console/Commands/System/CreatesDatabase.php
- identifier: missingType.iterableValue # not interesting enough to fix. - identifier: missingType.iterableValue # not interesting enough to fix.
- identifier: missingType.generics # not interesting enough to fix. - identifier: varTag.type # needs a custom extension for every repository, not gonna happen.
- "#Parameter \\#[1-2] \\$num[1-2] of function bc[a-z]+ expects numeric-string, [a-z\\-|&]+ given#" - '#Dynamic call to static method Illuminate#'
- '#expects view-string, string given#' - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::before#' # is custom scope
- '#expects view-string\|null, string given#' - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::after#' # is custom scope
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::withTrashed#' # is to allow soft delete
# phpstan can't handle this so we ignore them. - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::accountTypeIn#' # is a custom scope
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::before#' - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\BelongsTo::withTrashed#' # is to allow soft delete
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::after#'
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::withTrashed#'
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::accountTypeIn#'
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\BelongsTo::withTrashed#'
# The level 8 is the highest level. original was 5 # The level 8 is the highest level. original was 5
# 7 is more than enough, higher just leaves NULL things. # 7 is more than enough, higher just leaves NULL things.

View File

@@ -314,8 +314,9 @@ DEMO_USERNAME=
DEMO_PASSWORD= DEMO_PASSWORD=
# #
# Disable or enable the running balance column data # Disable or enable the running balance column data.
# Please disable this. It's a very experimental feature. # If you enable this, please also run "php artisan firefly-iii:correct-database"
# This will take some time the first run.
# #
USE_RUNNING_BALANCE=false USE_RUNNING_BALANCE=false

View File

@@ -15,7 +15,7 @@ jobs:
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- name: Prune cancelled/skipped runs - name: Prune cancelled/skipped runs
uses: actions/github-script@v7 uses: actions/github-script@v8
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
@@ -45,7 +45,7 @@ jobs:
} }
- name: Prune runs older than 3 days - name: Prune runs older than 3 days
uses: actions/github-script@v7 uses: actions/github-script@v8
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |

View File

@@ -250,7 +250,7 @@ jobs:
fi fi
echo "Merge all changes from $BRANCH_NAME back into '$MERGE_INTO' using a PR" echo "Merge all changes from $BRANCH_NAME back into '$MERGE_INTO' using a PR"
PR_URL=$(gh pr create -B $MERGE_INTO -H $BRANCH_NAME --title "🤖 Automatic PR to merge all changes into the '$MERGE_INTO' branch." --body '🤖 Created by GitHub action') PR_URL=$(gh pr create -B $MERGE_INTO -H $BRANCH_NAME --title "🤖 Automatic PR to merge all changes into the '$MERGE_INTO' branch." --body '🤖 This PR was created automatically by a GitHub action to merge the changed files into this branch. It will be merged automatically. `Share and enjoy`')
echo "PR URL is '$PR_URL'" echo "PR URL is '$PR_URL'"
IFS='/' read -ra parts <<< "$PR_URL" IFS='/' read -ra parts <<< "$PR_URL"
PR_NR=$(printf %s\\n "${parts[@]:(-1)}") PR_NR=$(printf %s\\n "${parts[@]:(-1)}")
@@ -272,7 +272,7 @@ jobs:
echo "Also merge everything into main since this is a release." echo "Also merge everything into main since this is a release."
echo 'create PR' echo 'create PR'
PR_URL=$(gh pr create -B main -H develop --title "🤖 Automatic PR to merge all changes into the main branch." --body "🤖 Created by GitHub action") PR_URL=$(gh pr create -B main -H develop --title "🤖 Automatic PR to merge all changes into the main branch." --body '🤖 This PR was created automatically by a GitHub action to merge the changed files into this branch. It will be merged automatically. `Share and enjoy`')
echo "PR URL is '$PR_URL'" echo "PR URL is '$PR_URL'"
IFS='/' read -ra parts <<< "$PR_URL" IFS='/' read -ra parts <<< "$PR_URL"

View File

@@ -15,7 +15,7 @@ jobs:
actions: write actions: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v9 - uses: actions/stale@v10
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: | stale-issue-message: |

View File

@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution. Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
## 2025 ## 2025
- Nicky De Maeyer
- Denis Iskandarov - Denis Iskandarov
- = - =
- Lompi - Lompi

View File

@@ -28,8 +28,10 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@@ -96,6 +98,7 @@ class PiggyBankController extends Controller
/** @var PiggyBank $piggy */ /** @var PiggyBank $piggy */
foreach ($piggies as $piggy) { foreach ($piggies as $piggy) {
/** @var TransactionCurrency $currency */
$currency = $piggy->transactionCurrency; $currency = $piggy->transactionCurrency;
$currentAmount = $this->piggyRepository->getCurrentAmount($piggy); $currentAmount = $this->piggyRepository->getCurrentAmount($piggy);
$objectGroup = $piggy->objectGroups()->first(); $objectGroup = $piggy->objectGroups()->first();
@@ -105,8 +108,8 @@ class PiggyBankController extends Controller
'name_with_balance' => sprintf( 'name_with_balance' => sprintf(
'%s (%s / %s)', '%s (%s / %s)',
$piggy->name, $piggy->name,
app('amount')->formatAnything($currency, $currentAmount, false), Amount::formatAnything($currency, $currentAmount, false),
app('amount')->formatAnything($currency, $piggy->target_amount, false), Amount::formatAnything($currency, $piggy->target_amount, false),
), ),
'currency_id' => (string) $currency->id, 'currency_id' => (string) $currency->id,
'currency_name' => $currency->name, 'currency_name' => $currency->name,

View File

@@ -31,9 +31,7 @@ use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\AccountBalanceGrouped; use FireflyIII\Support\Http\Api\AccountBalanceGrouped;
use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter; use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;

View File

@@ -31,10 +31,10 @@ use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
@@ -55,7 +55,6 @@ class BudgetController extends Controller
protected OperationsRepositoryInterface $opsRepository; protected OperationsRepositoryInterface $opsRepository;
private BudgetLimitRepositoryInterface $blRepository; private BudgetLimitRepositoryInterface $blRepository;
private array $currencies = []; private array $currencies = [];
private TransactionCurrency $currency;
private BudgetRepositoryInterface $repository; private BudgetRepositoryInterface $repository;
public function __construct() public function __construct()
@@ -115,7 +114,7 @@ class BudgetController extends Controller
// get all limits: // get all limits:
$limits = $this->blRepository->getBudgetLimits($budget, $start, $end); $limits = $this->blRepository->getBudgetLimits($budget, $start, $end);
$rows = []; $rows = [];
$spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget])); $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection()->push($budget));
$expenses = $this->processExpenses($budget->id, $spent, $start, $end); $expenses = $this->processExpenses($budget->id, $spent, $start, $end);
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
$currencies = [$this->primaryCurrency->id => $this->primaryCurrency]; $currencies = [$this->primaryCurrency->id => $this->primaryCurrency];
@@ -134,9 +133,9 @@ class BudgetController extends Controller
$row['pc_left'] = '0'; $row['pc_left'] = '0';
$row['pc_overspent'] = '0'; $row['pc_overspent'] = '0';
if (null !== $limit) { if ($limit instanceof BudgetLimit) {
$row['budgeted'] = $limit->amount; $row['budgeted'] = $limit->amount;
$row['left'] = bcsub($row['budgeted'], bcmul($row['spent'], '-1')); $row['left'] = bcsub((string) $row['budgeted'], bcmul((string) $row['spent'], '-1'));
$row['overspent'] = bcmul($row['left'], '-1'); $row['overspent'] = bcmul($row['left'], '-1');
$row['left'] = 1 === bccomp($row['left'], '0') ? $row['left'] : '0'; $row['left'] = 1 === bccomp($row['left'], '0') ? $row['left'] : '0';
$row['overspent'] = 1 === bccomp($row['overspent'], '0') ? $row['overspent'] : '0'; $row['overspent'] = 1 === bccomp($row['overspent'], '0') ? $row['overspent'] : '0';
@@ -144,7 +143,7 @@ class BudgetController extends Controller
// convert data if necessary. // convert data if necessary.
if (true === $this->convertToPrimary && $currencyId !== $this->primaryCurrency->id) { if (true === $this->convertToPrimary && $currencyId !== $this->primaryCurrency->id) {
$currencies[$currencyId] ??= TransactionCurrency::find($currencyId); $currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId);
$row['pc_budgeted'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['budgeted']); $row['pc_budgeted'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['budgeted']);
$row['pc_spent'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['spent']); $row['pc_spent'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['spent']);
$row['pc_left'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['left']); $row['pc_left'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['left']);
@@ -201,18 +200,18 @@ class BudgetController extends Controller
return $return; return $return;
} }
/** // /**
* When no budget limits are present, the expenses of the whole period are collected and grouped. // * When no budget limits are present, the expenses of the whole period are collected and grouped.
* This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty. // * This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty.
* // *
* @throws FireflyException // * @throws FireflyException
*/ // */
private function noBudgetLimits(Budget $budget, Carbon $start, Carbon $end): array // private function noBudgetLimits(Budget $budget, Carbon $start, Carbon $end): array
{ // {
$spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget])); // $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection()->push($budget));
//
return $this->processExpenses($budget->id, $spent, $start, $end); // return $this->processExpenses($budget->id, $spent, $start, $end);
} // }
/** /**
* Shared between the "noBudgetLimits" function and "processLimit". Will take a single set of expenses and return * Shared between the "noBudgetLimits" function and "processLimit". Will take a single set of expenses and return
@@ -232,7 +231,7 @@ class BudgetController extends Controller
* @var array $block * @var array $block
*/ */
foreach ($spent as $currencyId => $block) { foreach ($spent as $currencyId => $block) {
$this->currencies[$currencyId] ??= TransactionCurrency::find($currencyId); $this->currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId);
$return[$currencyId] ??= [ $return[$currencyId] ??= [
'currency_id' => (string)$currencyId, 'currency_id' => (string)$currencyId,
'currency_code' => $block['currency_code'], 'currency_code' => $block['currency_code'],
@@ -251,66 +250,68 @@ class BudgetController extends Controller
// var_dump($return); // var_dump($return);
/** @var array $journal */ /** @var array $journal */
foreach ($currentBudgetArray['transaction_journals'] as $journal) { foreach ($currentBudgetArray['transaction_journals'] as $journal) {
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], (string)$journal['amount']); /** @var numeric-string $amount */
$amount = (string)$journal['amount'];
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $amount);
} }
} }
return $return; return $return;
} }
/** // /**
* Function that processes each budget limit (per budget). // * Function that processes each budget limit (per budget).
* // *
* If you have a budget limit in EUR, only transactions in EUR will be considered. // * If you have a budget limit in EUR, only transactions in EUR will be considered.
* If you have a budget limit in GBP, only transactions in GBP will be considered. // * If you have a budget limit in GBP, only transactions in GBP will be considered.
* // *
* If you have a budget limit in EUR, and a transaction in GBP, it will not be considered for the EUR budget limit. // * If you have a budget limit in EUR, and a transaction in GBP, it will not be considered for the EUR budget limit.
* // *
* @throws FireflyException // * @throws FireflyException
*/ // */
private function budgetLimits(Budget $budget, Collection $limits): array // private function budgetLimits(Budget $budget, Collection $limits): array
{ // {
Log::debug(sprintf('Now in budgetLimits(#%d)', $budget->id)); // Log::debug(sprintf('Now in budgetLimits(#%d)', $budget->id));
$data = []; // $data = [];
//
// /** @var BudgetLimit $limit */
// foreach ($limits as $limit) {
// $data = array_merge($data, $this->processLimit($budget, $limit));
// }
//
// return $data;
// }
/** @var BudgetLimit $limit */ // /**
foreach ($limits as $limit) { // * @throws FireflyException
$data = array_merge($data, $this->processLimit($budget, $limit)); // */
} // private function processLimit(Budget $budget, BudgetLimit $limit): array
// {
return $data; // Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
} // $end = clone $limit->end_date;
// $end->endOfDay();
/** // $spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection()->push($budget));
* @throws FireflyException // $limitCurrencyId = $limit->transaction_currency_id;
*/ //
private function processLimit(Budget $budget, BudgetLimit $limit): array // /** @var array $entry */
{ // // only spent the entry where the entry's currency matches the budget limit's currency
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); // // so $filtered will only have 1 or 0 entries
$end = clone $limit->end_date; // $filtered = array_filter($spent, fn ($entry) => $entry['currency_id'] === $limitCurrencyId);
$end->endOfDay(); // $result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end);
$spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection([$budget])); // if (1 === count($result)) {
$limitCurrencyId = $limit->transaction_currency_id; // $compare = bccomp($limit->amount, (string)app('steam')->positive($result[$limitCurrencyId]['spent']));
// $result[$limitCurrencyId]['budgeted'] = $limit->amount;
/** @var array $entry */ // if (1 === $compare) {
// only spent the entry where the entry's currency matches the budget limit's currency // // convert this amount into the primary currency:
// so $filtered will only have 1 or 0 entries // $result[$limitCurrencyId]['left'] = bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent']);
$filtered = array_filter($spent, fn ($entry) => $entry['currency_id'] === $limitCurrencyId); // }
$result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end); // if ($compare <= 0) {
if (1 === count($result)) { // $result[$limitCurrencyId]['overspent'] = app('steam')->positive(bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent']));
$compare = bccomp($limit->amount, (string)app('steam')->positive($result[$limitCurrencyId]['spent'])); // }
$result[$limitCurrencyId]['budgeted'] = $limit->amount; // }
if (1 === $compare) { //
// convert this amount into the primary currency: // return $result;
$result[$limitCurrencyId]['left'] = bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent']); // }
}
if ($compare <= 0) {
$result[$limitCurrencyId]['overspent'] = app('steam')->positive(bcadd($limit->amount, (string)$result[$limitCurrencyId]['spent']));
}
}
return $result;
}
private function filterLimit(int $currencyId, Collection $limits): ?BudgetLimit private function filterLimit(int $currencyId, Collection $limits): ?BudgetLimit
{ {

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException; use Carbon\Exceptions\InvalidFormatException;
use FireflyIII\Exceptions\BadHttpHeaderException; use FireflyIII\Exceptions\BadHttpHeaderException;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Facades\Steam;
@@ -66,8 +65,6 @@ abstract class Controller extends BaseController
protected const string JSON_CONTENT_TYPE = 'application/json'; protected const string JSON_CONTENT_TYPE = 'application/json';
protected array $accepts = ['application/json', 'application/vnd.api+json']; protected array $accepts = ['application/json', 'application/vnd.api+json'];
/** @var array<int, string> */
protected array $allowedSort;
protected bool $convertToPrimary = false; protected bool $convertToPrimary = false;
protected TransactionCurrency $primaryCurrency; protected TransactionCurrency $primaryCurrency;
protected ParameterBag $parameters; protected ParameterBag $parameters;
@@ -78,7 +75,6 @@ abstract class Controller extends BaseController
public function __construct() public function __construct()
{ {
// get global parameters // get global parameters
$this->allowedSort = config('firefly.allowed_sort_parameters');
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
$this->parameters = $this->getParameters(); $this->parameters = $this->getParameters();
@@ -150,13 +146,7 @@ abstract class Controller extends BaseController
} }
if (null !== $value) { if (null !== $value) {
$value = (int)$value; $value = (int)$value;
if ($value < 1) { $value = min(max(1, $value), 2 ** 16);
$value = 1;
}
if ($value > 2 ** 16) {
$value = 2 ** 16;
}
$bag->set($integer, $value); $bag->set($integer, $value);
} }
if (null === $value if (null === $value
@@ -166,46 +156,14 @@ abstract class Controller extends BaseController
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
/** @var Preference $pageSize */
$pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data; $pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data;
$bag->set($integer, $pageSize); $bag->set($integer, $pageSize);
} }
} }
// sort fields: // sort fields:
return $this->getSortParameters($bag);
}
private function getSortParameters(ParameterBag $bag): ParameterBag
{
$sortParameters = [];
try {
$param = (string)request()->query->get('sort');
} catch (BadRequestException $e) {
Log::error('Request field "sort" contains a non-scalar value. Value set to NULL.');
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$param = '';
}
if ('' === $param) {
return $bag;
}
$parts = explode(',', $param);
foreach ($parts as $part) {
$part = trim($part);
$direction = 'asc';
if ('-' === $part[0]) {
$part = substr($part, 1);
$direction = 'desc';
}
if (in_array($part, $this->allowedSort, true)) {
$sortParameters[] = [$part, $direction];
}
}
$bag->set('sort', $sortParameters);
return $bag; return $bag;
// return $this->getSortParameters($bag);
} }
/** /**
@@ -274,8 +232,6 @@ abstract class Controller extends BaseController
$baseUrl = sprintf('%s/api/v1', request()->getSchemeAndHttpHost()); $baseUrl = sprintf('%s/api/v1', request()->getSchemeAndHttpHost());
$manager->setSerializer(new JsonApiSerializer($baseUrl)); $manager->setSerializer(new JsonApiSerializer($baseUrl));
// $transformer->collectMetaData(new Collection([$object]));
$resource = new Item($object, $transformer, $key); $resource = new Item($object, $transformer, $key);
return $manager->createData($resource)->toArray(); return $manager->createData($resource)->toArray();

View File

@@ -76,7 +76,7 @@ class BudgetController extends Controller
/** @var Budget $budget */ /** @var Budget $budget */
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
$expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection([$budget])); $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection()->push($budget));
/** @var array $expense */ /** @var array $expense */
foreach ($expenses as $expense) { foreach ($expenses as $expense) {

View File

@@ -76,7 +76,7 @@ class CategoryController extends Controller
/** @var Category $category */ /** @var Category $category */
foreach ($categories as $category) { foreach ($categories as $category) {
$expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection([$category])); $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection()->push($category));
/** @var array $expense */ /** @var array $expense */
foreach ($expenses as $expense) { foreach ($expenses as $expense) {

View File

@@ -76,7 +76,7 @@ class CategoryController extends Controller
/** @var Category $category */ /** @var Category $category */
foreach ($categories as $category) { foreach ($categories as $category) {
$expenses = $this->opsRepository->sumIncome($start, $end, $assetAccounts, new Collection([$category])); $expenses = $this->opsRepository->sumIncome($start, $end, $assetAccounts, new Collection()->push($category));
/** @var array $expense */ /** @var array $expense */
foreach ($expenses as $expense) { foreach ($expenses as $expense) {

View File

@@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@@ -71,7 +72,7 @@ class PeriodController extends Controller
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $currencyCode, 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], (string) app('steam')->positive($journal[$field])); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], Steam::positive($journal[$field]));
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // float but on purpose. $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // float but on purpose.
} }

View File

@@ -30,6 +30,7 @@ use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@@ -97,7 +98,7 @@ class TagController extends Controller
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $currencyCode, 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], (string) app('steam')->positive($journal[$field])); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], Steam::positive($journal[$field]));
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference'];
} }
@@ -148,7 +149,7 @@ class TagController extends Controller
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $journal['currency_code'],
]; ];
$response[$key]['difference'] = bcadd((string) $response[$key]['difference'], (string) app('steam')->positive($journal['amount'])); $response[$key]['difference'] = bcadd((string) $response[$key]['difference'], Steam::positive($journal['amount']));
$response[$key]['difference_float'] = (float) $response[$key]['difference']; $response[$key]['difference_float'] = (float) $response[$key]['difference'];
} }
@@ -160,10 +161,7 @@ class TagController extends Controller
'currency_id' => (string) $foreignCurrencyId, 'currency_id' => (string) $foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'], 'currency_code' => $journal['foreign_currency_code'],
]; ];
$response[$foreignKey]['difference'] = bcadd( $response[$foreignKey]['difference'] = bcadd((string) $response[$foreignKey]['difference'], Steam::positive($journal['foreign_amount']));
(string) $response[$foreignKey]['difference'],
(string) app('steam')->positive($journal['foreign_amount'])
);
$response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; $response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference'];
} }
} }

View File

@@ -76,7 +76,7 @@ class CategoryController extends Controller
/** @var Category $category */ /** @var Category $category */
foreach ($categories as $category) { foreach ($categories as $category) {
$expenses = $this->opsRepository->sumTransfers($start, $end, $assetAccounts, new Collection([$category])); $expenses = $this->opsRepository->sumTransfers($start, $end, $assetAccounts, new Collection()->push($category));
/** @var array $expense */ /** @var array $expense */
foreach ($expenses as $expense) { foreach ($expenses as $expense) {

View File

@@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@@ -71,7 +72,7 @@ class PeriodController extends Controller
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $currencyCode, 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], (string) app('steam')->positive($journal[$field])); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], Steam::positive($journal[$field]));
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference'];
} }

View File

@@ -30,6 +30,7 @@ use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@@ -95,7 +96,7 @@ class TagController extends Controller
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $currencyCode, 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], (string) app('steam')->positive($journal[$field])); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], Steam::positive($journal[$field]));
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference'];
} }
@@ -146,7 +147,7 @@ class TagController extends Controller
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $journal['currency_code'],
]; ];
$response[$key]['difference'] = bcadd((string) $response[$key]['difference'], (string) app('steam')->positive($journal['amount'])); $response[$key]['difference'] = bcadd((string) $response[$key]['difference'], Steam::positive($journal['amount']));
$response[$key]['difference_float'] = (float) $response[$key]['difference']; $response[$key]['difference_float'] = (float) $response[$key]['difference'];
} }
@@ -160,7 +161,7 @@ class TagController extends Controller
]; ];
$response[$foreignKey]['difference'] = bcadd( $response[$foreignKey]['difference'] = bcadd(
(string) $response[$foreignKey]['difference'], (string) $response[$foreignKey]['difference'],
(string) app('steam')->positive($journal['foreign_amount']) Steam::positive($journal['foreign_amount'])
); );
$response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; // intentional float $response[$foreignKey]['difference_float'] = (float) $response[$foreignKey]['difference']; // intentional float
} }

View File

@@ -152,7 +152,7 @@ class ListController extends Controller
// use new group collector: // use new group collector:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($admin)->setAccounts(new Collection([$account])) $collector->setUser($admin)->setAccounts(new Collection()->push($account))
->withAPIInformation()->setLimit($pageSize)->setPage($this->parameters->get('page'))->setTypes($types) ->withAPIInformation()->setLimit($pageSize)->setPage($this->parameters->get('page'))->setTypes($types)
; ;

View File

@@ -82,18 +82,22 @@ class ShowController extends Controller
// get list of accounts. Count it and split it. // get list of accounts. Count it and split it.
$this->repository->resetAccountOrder(); $this->repository->resetAccountOrder();
// TODO fix sort. $collection = $this->repository->getAccountsByType($types, $params['sort']);
$collection = $this->repository->getAccountsByType($types, null);
$count = $collection->count(); $count = $collection->count();
// continue sort: // continue sort:
// TODO if the user sorts on DB dependent field there must be no slice before enrichment, only after.
// TODO still need to figure out how to do this easily.
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $params['limit'], $params['limit']); $accounts = $collection->slice(($this->parameters->get('page') - 1) * $params['limit'], $params['limit']);
// enrich // enrich
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
$enrichment = new AccountEnrichment(); $enrichment = new AccountEnrichment();
$enrichment->setSort($params['sort']);
$enrichment->setDate($this->parameters->get('date')); $enrichment->setDate($this->parameters->get('date'));
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$enrichment->setUser($admin); $enrichment->setUser($admin);
$accounts = $enrichment->enrich($accounts); $accounts = $enrichment->enrich($accounts);
@@ -117,7 +121,7 @@ class ShowController extends Controller
* *
* Show single instance. * Show single instance.
*/ */
public function show(Account $account): JsonResponse public function show(ShowRequest $request, Account $account): JsonResponse
{ {
// get list of accounts. Count it and split it. // get list of accounts. Count it and split it.
$this->repository->resetAccountOrder(); $this->repository->resetAccountOrder();
@@ -129,6 +133,8 @@ class ShowController extends Controller
$admin = auth()->user(); $admin = auth()->user();
$enrichment = new AccountEnrichment(); $enrichment = new AccountEnrichment();
$enrichment->setDate($this->parameters->get('date')); $enrichment->setDate($this->parameters->get('date'));
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$enrichment->setUser($admin); $enrichment->setUser($admin);
$account = $enrichment->enrichSingle($account); $account = $enrichment->enrichSingle($account);

View File

@@ -114,8 +114,8 @@ class ShowController extends Controller
public function show(AvailableBudget $availableBudget): JsonResponse public function show(AvailableBudget $availableBudget): JsonResponse
{ {
$manager = $this->getManager(); $manager = $this->getManager();
$start = $this->parameters->get('start'); // $start = $this->parameters->get('start');
$end = $this->parameters->get('end'); // $end = $this->parameters->get('end');
/** @var AvailableBudgetTransformer $transformer */ /** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class); $transformer = app(AvailableBudgetTransformer::class);
@@ -126,8 +126,8 @@ class ShowController extends Controller
$admin = auth()->user(); $admin = auth()->user();
$enrichment = new AvailableBudgetEnrichment(); $enrichment = new AvailableBudgetEnrichment();
$enrichment->setUser($admin); $enrichment->setUser($admin);
$enrichment->setStart($start); // $enrichment->setStart($start);
$enrichment->setEnd($end); // $enrichment->setEnd($end);
$availableBudget = $enrichment->enrichSingle($availableBudget); $availableBudget = $enrichment->enrichSingle($availableBudget);

View File

@@ -67,7 +67,9 @@ class StoreController extends Controller
*/ */
public function store(StoreRequest $request): JsonResponse public function store(StoreRequest $request): JsonResponse
{ {
$budget = $this->repository->store($request->getAll()); $data = $request->getAll();
$data['fire_webhooks'] ??= true;
$budget = $this->repository->store($data);
$budget->refresh(); $budget->refresh();
$manager = $this->getManager(); $manager = $this->getManager();

View File

@@ -57,15 +57,10 @@ class UpdateController extends Controller
); );
} }
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/updateBudget
*
* Update a budget.
*/
public function update(UpdateRequest $request, Budget $budget): JsonResponse public function update(UpdateRequest $request, Budget $budget): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$data['fire_webhooks'] ??= true;
$budget = $this->repository->update($budget, $data); $budget = $this->repository->update($budget, $data);
$manager = $this->getManager(); $manager = $this->getManager();

View File

@@ -84,6 +84,8 @@ class ShowController extends Controller
$enrichment->setUser($admin); $enrichment->setUser($admin);
$enrichment->setStart($this->parameters->get('start')); $enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end')); $enrichment->setEnd($this->parameters->get('end'));
/** @var Budget $budget */
$budget = $enrichment->enrichSingle($budget); $budget = $enrichment->enrichSingle($budget);

View File

@@ -70,6 +70,7 @@ class StoreController extends Controller
$data = $request->getAll(); $data = $request->getAll();
$data['start_date'] = $data['start']; $data['start_date'] = $data['start'];
$data['end_date'] = $data['end']; $data['end_date'] = $data['end'];
$data['fire_webhooks'] ??= true;
$data['budget_id'] = $budget->id; $data['budget_id'] = $budget->id;
$budgetLimit = $this->blRepository->store($data); $budgetLimit = $this->blRepository->store($data);

View File

@@ -77,6 +77,7 @@ class UpdateController extends Controller
throw new FireflyException('20028: The budget limit does not belong to the budget.'); throw new FireflyException('20028: The budget limit does not belong to the budget.');
} }
$data = $request->getAll(); $data = $request->getAll();
$data['fire_webhooks'] ??= true;
$data['budget_id'] = $budget->id; $data['budget_id'] = $budget->id;
$budgetLimit = $this->blRepository->update($budgetLimit, $data); $budgetLimit = $this->blRepository->update($budgetLimit, $data);
$manager = $this->getManager(); $manager = $this->getManager();

View File

@@ -72,7 +72,7 @@ class DestroyController extends Controller
public function destroySingleByDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse public function destroySingleByDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse
{ {
$exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date); $exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null !== $exchangeRate) { if ($exchangeRate instanceof CurrencyExchangeRate) {
$this->repository->deleteRate($exchangeRate); $this->repository->deleteRate($exchangeRate);
} }

View File

@@ -95,7 +95,7 @@ class ShowController extends Controller
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date); $exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null === $exchangeRate) { if (!$exchangeRate instanceof CurrencyExchangeRate) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }

View File

@@ -33,6 +33,7 @@ use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface; use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Transformers\ExchangeRateTransformer; use FireflyIII\Transformers\ExchangeRateTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
@@ -69,7 +70,7 @@ class StoreController extends Controller
foreach ($data as $date => $rate) { foreach ($data as $date => $rate) {
$date = Carbon::createFromFormat('Y-m-d', $date); $date = Carbon::createFromFormat('Y-m-d', $date);
$existing = $this->repository->getSpecificRateOnDate($from, $to, $date); $existing = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null !== $existing) { if ($existing instanceof CurrencyExchangeRate) {
// update existing rate. // update existing rate.
$existing = $this->repository->updateExchangeRate($existing, $rate); $existing = $this->repository->updateExchangeRate($existing, $rate);
$collection->push($existing); $collection->push($existing);
@@ -98,12 +99,9 @@ class StoreController extends Controller
$from = $request->getFromCurrency(); $from = $request->getFromCurrency();
$collection = new Collection(); $collection = new Collection();
foreach ($data['rates'] as $key => $rate) { foreach ($data['rates'] as $key => $rate) {
$to = TransactionCurrency::where('code', $key)->first(); $to = Amount::getTransactionCurrencyByCode($key);
if (null === $to) {
continue; // should not happen.
}
$existing = $this->repository->getSpecificRateOnDate($from, $to, $date); $existing = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null !== $existing) { if ($existing instanceof CurrencyExchangeRate) {
// update existing rate. // update existing rate.
$existing = $this->repository->updateExchangeRate($existing, $rate); $existing = $this->repository->updateExchangeRate($existing, $rate);
$collection->push($existing); $collection->push($existing);

View File

@@ -74,7 +74,7 @@ class UpdateController extends Controller
public function updateByDate(UpdateRequest $request, TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse public function updateByDate(UpdateRequest $request, TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse
{ {
$exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date); $exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null === $exchangeRate) { if (!$exchangeRate instanceof CurrencyExchangeRate) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
$date = $request->getDate(); $date = $request->getDate();

View File

@@ -68,10 +68,6 @@ class UpdateController extends Controller
$data = $request->getAll(); $data = $request->getAll();
$piggyBank = $this->repository->update($piggyBank, $data); $piggyBank = $this->repository->update($piggyBank, $data);
if (array_key_exists('current_amount', $data) && '' !== $data['current_amount']) {
$this->repository->setCurrentAmount($piggyBank, $data['current_amount']);
}
// enrich // enrich
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();

View File

@@ -75,7 +75,7 @@ class TriggerController extends Controller
/** @var RuleEngineInterface $ruleEngine */ /** @var RuleEngineInterface $ruleEngine */
$ruleEngine = app(RuleEngineInterface::class); $ruleEngine = app(RuleEngineInterface::class);
$ruleEngine->setRules(new Collection([$rule])); $ruleEngine->setRules(new Collection()->push($rule));
// overrule the rule(s) if necessary. // overrule the rule(s) if necessary.
if (array_key_exists('start', $parameters) && null !== $parameters['start']) { if (array_key_exists('start', $parameters) && null !== $parameters['start']) {
@@ -129,7 +129,7 @@ class TriggerController extends Controller
/** @var RuleEngineInterface $ruleEngine */ /** @var RuleEngineInterface $ruleEngine */
$ruleEngine = app(RuleEngineInterface::class); $ruleEngine = app(RuleEngineInterface::class);
$ruleEngine->setRules(new Collection([$rule])); $ruleEngine->setRules(new Collection()->push($rule));
// overrule the rule(s) if necessary. // overrule the rule(s) if necessary.
if (array_key_exists('start', $parameters) && null !== $parameters['start']) { if (array_key_exists('start', $parameters) && null !== $parameters['start']) {

View File

@@ -30,9 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Recurrence; use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\Rule; use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
@@ -192,7 +190,7 @@ class ListController extends Controller
$enrichment->setUser($admin); $enrichment->setUser($admin);
$enrichment->setStart($this->parameters->get('start')); $enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end')); $enrichment->setEnd($this->parameters->get('end'));
$bills = $enrichment->enrichSingle($bills); $bills = $enrichment->enrich($bills);
// make paginator: // make paginator:
$paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page')); $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page'));
@@ -268,7 +266,6 @@ class ListController extends Controller
// filter selection // filter selection
$collection = $unfiltered->filter( $collection = $unfiltered->filter(
static function (Recurrence $recurrence) use ($currency) { // @phpstan-ignore-line static function (Recurrence $recurrence) use ($currency) { // @phpstan-ignore-line
/** @var RecurrenceTransaction $transaction */
if (array_any($recurrence->recurrenceTransactions, fn ($transaction) => $transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id)) { if (array_any($recurrence->recurrenceTransactions, fn ($transaction) => $transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id)) {
return $recurrence; return $recurrence;
} }
@@ -320,7 +317,6 @@ class ListController extends Controller
$collection = $unfiltered->filter( $collection = $unfiltered->filter(
static function (Rule $rule) use ($currency) { // @phpstan-ignore-line static function (Rule $rule) use ($currency) { // @phpstan-ignore-line
/** @var RuleTrigger $trigger */
if (array_any($rule->ruleTriggers, fn ($trigger) => 'currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value)) { if (array_any($rule->ruleTriggers, fn ($trigger) => 'currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value)) {
return $rule; return $rule;
} }

View File

@@ -481,7 +481,7 @@ class BasicController extends Controller
$currencies = []; $currencies = [];
// first, create an entry for each entry in the "available" array. // first, create an entry for each entry in the "available" array.
/** @var array $availableBudget */ /** @var string $availableBudget */
foreach ($available as $currencyId => $availableBudget) { foreach ($available as $currencyId => $availableBudget) {
$currencies[$currencyId] ??= $this->currencyRepos->find($currencyId); $currencies[$currencyId] ??= $this->currencyRepos->find($currencyId);
$return[$currencyId] = [ $return[$currencyId] = [

View File

@@ -133,7 +133,6 @@ class ConfigurationController extends Controller
*/ */
public function show(string $configKey): JsonResponse public function show(string $configKey): JsonResponse
{ {
$data = [];
$dynamic = $this->getDynamicConfiguration(); $dynamic = $this->getDynamicConfiguration();
$shortKey = str_replace('configuration.', '', $configKey); $shortKey = str_replace('configuration.', '', $configKey);
if (str_starts_with($configKey, 'configuration.')) { if (str_starts_with($configKey, 'configuration.')) {
@@ -156,13 +155,11 @@ class ConfigurationController extends Controller
} }
// fallback // fallback
if (!str_starts_with($configKey, 'configuration.')) { $data = [
$data = [ 'title' => $configKey,
'title' => $configKey, 'value' => config($shortKey),
'value' => config($configKey), 'editable' => false,
'editable' => false, ];
];
}
return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE); return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
} }

View File

@@ -158,18 +158,23 @@ class ShowController extends Controller
Log::debug(sprintf('Now in triggerTransaction(%d, %d)', $webhook->id, $group->id)); Log::debug(sprintf('Now in triggerTransaction(%d, %d)', $webhook->id, $group->id));
Log::channel('audit')->info(sprintf('User triggers webhook #%d on transaction group #%d.', $webhook->id, $group->id)); Log::channel('audit')->info(sprintf('User triggers webhook #%d on transaction group #%d.', $webhook->id, $group->id));
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser(auth()->user());
// tell the generator which trigger it should look for /** @var \FireflyIII\Models\WebhookTrigger $trigger */
$engine->setTrigger(WebhookTrigger::tryFrom($webhook->trigger)); foreach ($webhook->webhookTriggers as $trigger) {
// tell the generator which objects to process /** @var MessageGeneratorInterface $engine */
$engine->setObjects(new Collection([$group])); $engine = app(MessageGeneratorInterface::class);
// set the webhook to trigger $engine->setUser(auth()->user());
$engine->setWebhooks(new Collection([$webhook]));
// tell the generator to generate the messages // tell the generator which trigger it should look for
$engine->generateMessages(); $engine->setTrigger(WebhookTrigger::tryFrom((int)$trigger->key));
// tell the generator which objects to process
$engine->setObjects(new Collection()->push($group));
// set the webhook to trigger
$engine->setWebhooks(new Collection()->push($webhook));
// tell the generator to generate the messages
$engine->generateMessages();
}
// trigger event to send them: // trigger event to send them:
Log::debug('send event RequestedSendWebhookMessages from ShowController::triggerTransaction()'); Log::debug('send event RequestedSendWebhookMessages from ShowController::triggerTransaction()');

View File

@@ -77,6 +77,8 @@ class UpdateController extends Controller
$admin = auth()->user(); $admin = auth()->user();
$enrichment = new WebhookEnrichment(); $enrichment = new WebhookEnrichment();
$enrichment->setUser($admin); $enrichment->setUser($admin);
/** @var Webhook $webhook */
$webhook = $enrichment->enrichSingle($webhook); $webhook = $enrichment->enrichSingle($webhook);
Log::channel('audit')->info(sprintf('User updates webhook #%d', $webhook->id), $data); Log::channel('audit')->info(sprintf('User updates webhook #%d', $webhook->id), $data);

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Chart; namespace FireflyIII\Api\V1\Requests\Chart;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Data\Bulk; namespace FireflyIII\Api\V1\Requests\Data\Bulk;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Data\Bulk; namespace FireflyIII\Api\V1\Requests\Data\Bulk;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use JsonException; use JsonException;
use FireflyIII\Enums\ClauseType; use FireflyIII\Enums\ClauseType;
use FireflyIII\Rules\IsValidBulkClause; use FireflyIII\Rules\IsValidBulkClause;

View File

@@ -44,8 +44,18 @@ class DateRequest extends FormRequest
*/ */
public function getAll(): array public function getAll(): array
{ {
$start = $this->getCarbonDate('start'); $start = $this->getCarbonDate('start');
$end = $this->getCarbonDate('end'); $end = $this->getCarbonDate('end');
if (null === $start) {
$start = now()->startOfMonth();
}
if (null === $end) {
$end = now()->endOfMonth();
}
// sanity check on dates:
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
if ($start->diffInYears($end, true) > 5) { if ($start->diffInYears($end, true) > 5) {

View File

@@ -23,13 +23,14 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Account; namespace FireflyIII\Api\V1\Requests\Models\Account;
use Illuminate\Validation\Validator;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Preference; use FireflyIII\Models\Account;
use FireflyIII\Rules\IsValidSortInstruction;
use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
class ShowRequest extends FormRequest class ShowRequest extends FormRequest
@@ -44,8 +45,6 @@ class ShowRequest extends FormRequest
// get default for user: // get default for user:
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
/** @var Preference $pageSize */
$limit = (int)Preferences::getForUser($user, 'listPageSize', 50)->data; $limit = (int)Preferences::getForUser($user, 'listPageSize', 50)->data;
} }
@@ -55,7 +54,7 @@ class ShowRequest extends FormRequest
return [ return [
'type' => $this->convertString('type', 'all'), 'type' => $this->convertString('type', 'all'),
'limit' => $limit, 'limit' => $limit,
'sort' => $this->convertString('sort', 'order'), 'sort' => $this->convertSortParameters('sort', Account::class),
'page' => $page, 'page' => $page,
]; ];
} }
@@ -68,7 +67,7 @@ class ShowRequest extends FormRequest
'date' => 'date', 'date' => 'date',
'start' => 'date|present_with:end|before_or_equal:end|before:2038-01-17|after:1970-01-02', 'start' => 'date|present_with:end|before_or_equal:end|before:2038-01-17|after:1970-01-02',
'end' => 'date|present_with:start|after_or_equal:start|before:2038-01-17|after:1970-01-02', 'end' => 'date|present_with:start|after_or_equal:start|before:2038-01-17|after:1970-01-02',
'sort' => 'in:active,iban,name,order,-active,-iban,-name,-order', // TODO improve me. 'sort' => ['nullable', new IsValidSortInstruction(Account::class)],
'type' => sprintf('in:%s', $keys), 'type' => sprintf('in:%s', $keys),
'limit' => 'numeric|min:1|max:131337', 'limit' => 'numeric|min:1|max:131337',
'page' => 'numeric|min:1|max:131337', 'page' => 'numeric|min:1|max:131337',
@@ -79,10 +78,12 @@ class ShowRequest extends FormRequest
{ {
$validator->after( $validator->after(
function (Validator $validator): void { function (Validator $validator): void {
if ($validator->failed()) { if (count($validator->failed()) > 0) {
return; return;
} }
$data = $validator->getData(); $data = $validator->getData();
if (array_key_exists('date', $data) && array_key_exists('start', $data) && array_key_exists('end', $data)) { if (array_key_exists('date', $data) && array_key_exists('start', $data) && array_key_exists('end', $data)) {
// assume valid dates, before we got here. // assume valid dates, before we got here.
$start = Carbon::parse($data['start'], config('app.timezone'))->startOfDay(); $start = Carbon::parse($data['start'], config('app.timezone'))->startOfDay();

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Account; namespace FireflyIII\Api\V1\Requests\Models\Account;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Location; use FireflyIII\Models\Location;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\AvailableBudget; namespace FireflyIII\Api\V1\Requests\Models\AvailableBudget;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Bill; namespace FireflyIII\Api\V1\Requests\Models\Bill;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use ValueError; use ValueError;
use TypeError; use TypeError;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Bill; namespace FireflyIII\Api\V1\Requests\Models\Bill;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Rules\IsValidPositiveAmount;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Budget; namespace FireflyIII\Api\V1\Requests\Models\Budget;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
@@ -48,17 +48,20 @@ class StoreRequest extends FormRequest
public function getAll(): array public function getAll(): array
{ {
$fields = [ $fields = [
'name' => ['name', 'convertString'], 'name' => ['name', 'convertString'],
'active' => ['active', 'boolean'], 'active' => ['active', 'boolean'],
'order' => ['active', 'convertInteger'], 'order' => ['active', 'convertInteger'],
'notes' => ['notes', 'convertString'], 'notes' => ['notes', 'convertString'],
// auto budget currency: // auto budget currency:
'currency_id' => ['auto_budget_currency_id', 'convertInteger'], 'currency_id' => ['auto_budget_currency_id', 'convertInteger'],
'currency_code' => ['auto_budget_currency_code', 'convertString'], 'currency_code' => ['auto_budget_currency_code', 'convertString'],
'auto_budget_type' => ['auto_budget_type', 'convertString'], 'auto_budget_type' => ['auto_budget_type', 'convertString'],
'auto_budget_amount' => ['auto_budget_amount', 'convertString'], 'auto_budget_amount' => ['auto_budget_amount', 'convertString'],
'auto_budget_period' => ['auto_budget_period', 'convertString'], 'auto_budget_period' => ['auto_budget_period', 'convertString'],
// webhooks
'fire_webhooks' => ['fire_webhooks', 'boolean'],
]; ];
return $this->getAllData($fields); return $this->getAllData($fields);
@@ -70,15 +73,18 @@ class StoreRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'name' => 'required|min:1|max:255|uniqueObjectForUser:budgets,name', 'name' => 'required|min:1|max:255|uniqueObjectForUser:budgets,name',
'active' => [new IsBoolean()], 'active' => [new IsBoolean()],
'currency_id' => 'exists:transaction_currencies,id', 'currency_id' => 'exists:transaction_currencies,id',
'currency_code' => 'exists:transaction_currencies,code', 'currency_code' => 'exists:transaction_currencies,code',
'notes' => 'nullable|min:1|max:32768', 'notes' => 'nullable|min:1|max:32768',
// auto budget info // auto budget info
'auto_budget_type' => 'in:reset,rollover,adjusted,none', 'auto_budget_type' => 'in:reset,rollover,adjusted,none',
'auto_budget_amount' => ['required_if:auto_budget_type,reset', 'required_if:auto_budget_type,rollover', 'required_if:auto_budget_type,adjusted', new IsValidPositiveAmount()], 'auto_budget_amount' => ['required_if:auto_budget_type,reset', 'required_if:auto_budget_type,rollover', 'required_if:auto_budget_type,adjusted', new IsValidPositiveAmount()],
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover|required_if:auto_budget_type,adjusted', 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover|required_if:auto_budget_type,adjusted',
// webhooks
'fire_webhooks' => [new IsBoolean()],
]; ];
} }

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Budget; namespace FireflyIII\Api\V1\Requests\Models\Budget;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Rules\IsValidPositiveAmount;
@@ -50,15 +50,18 @@ class UpdateRequest extends FormRequest
{ {
// this is the way: // this is the way:
$fields = [ $fields = [
'name' => ['name', 'convertString'], 'name' => ['name', 'convertString'],
'active' => ['active', 'boolean'], 'active' => ['active', 'boolean'],
'order' => ['order', 'convertInteger'], 'order' => ['order', 'convertInteger'],
'notes' => ['notes', 'convertString'], 'notes' => ['notes', 'convertString'],
'currency_id' => ['auto_budget_currency_id', 'convertInteger'], 'currency_id' => ['auto_budget_currency_id', 'convertInteger'],
'currency_code' => ['auto_budget_currency_code', 'convertString'], 'currency_code' => ['auto_budget_currency_code', 'convertString'],
'auto_budget_type' => ['auto_budget_type', 'convertString'], 'auto_budget_type' => ['auto_budget_type', 'convertString'],
'auto_budget_amount' => ['auto_budget_amount', 'convertString'], 'auto_budget_amount' => ['auto_budget_amount', 'convertString'],
'auto_budget_period' => ['auto_budget_period', 'convertString'], 'auto_budget_period' => ['auto_budget_period', 'convertString'],
// webhooks
'fire_webhooks' => ['fire_webhooks', 'boolean'],
]; ];
$allData = $this->getAllData($fields); $allData = $this->getAllData($fields);
if (array_key_exists('auto_budget_type', $allData)) { if (array_key_exists('auto_budget_type', $allData)) {
@@ -83,14 +86,17 @@ class UpdateRequest extends FormRequest
$budget = $this->route()->parameter('budget'); $budget = $this->route()->parameter('budget');
return [ return [
'name' => sprintf('min:1|max:100|uniqueObjectForUser:budgets,name,%d', $budget->id), 'name' => sprintf('min:1|max:100|uniqueObjectForUser:budgets,name,%d', $budget->id),
'active' => [new IsBoolean()], 'active' => [new IsBoolean()],
'notes' => 'nullable|min:1|max:32768', 'notes' => 'nullable|min:1|max:32768',
'auto_budget_type' => 'in:reset,rollover,adjusted,none', 'auto_budget_type' => 'in:reset,rollover,adjusted,none',
'auto_budget_currency_id' => 'exists:transaction_currencies,id', 'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_currency_code' => 'exists:transaction_currencies,code', 'auto_budget_currency_code' => 'exists:transaction_currencies,code',
'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()], 'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()],
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly', 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
// webhooks
'fire_webhooks' => [new IsBoolean()],
]; ];
} }

View File

@@ -24,10 +24,16 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit;
use Carbon\Carbon;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
/** /**
* Class StoreRequest * Class StoreRequest
@@ -49,6 +55,9 @@ class StoreRequest extends FormRequest
'currency_id' => $this->convertInteger('currency_id'), 'currency_id' => $this->convertInteger('currency_id'),
'currency_code' => $this->convertString('currency_code'), 'currency_code' => $this->convertString('currency_code'),
'notes' => $this->stringWithNewlines('notes'), 'notes' => $this->stringWithNewlines('notes'),
// for webhooks:
'fire_webhooks' => $this->boolean('fire_webhooks', true),
]; ];
} }
@@ -58,12 +67,59 @@ class StoreRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'start' => 'required|before:end|date', 'start' => 'required|before:end|date',
'end' => 'required|after:start|date', 'end' => 'required|after:start|date',
'amount' => ['required', new IsValidPositiveAmount()], 'amount' => ['required', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'notes' => 'nullable|min:0|max:32768', 'notes' => 'nullable|min:0|max:32768',
// webhooks
'fire_webhooks' => [new IsBoolean()],
]; ];
} }
/**
* Configure the validator instance.
*/
public function withValidator(Validator $validator): void
{
$budget = $this->route()->parameter('budget');
$validator->after(
static function (Validator $validator) use ($budget): void {
if (0 !== count($validator->failed())) {
return;
}
$data = $validator->getData();
// if no currency has been provided, use the user's default currency:
/** @var TransactionCurrencyFactory $factory */
$factory = app(TransactionCurrencyFactory::class);
$currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null);
if (null === $currency) {
$currency = Amount::getPrimaryCurrency();
}
$currency->enabled = true;
$currency->save();
// validator already concluded start and end are valid dates:
$start = Carbon::parse($data['start'], config('app.timezone'));
$end = Carbon::parse($data['end'], config('app.timezone'));
// find limit with same date range and currency.
$limit = $budget->budgetlimits()
->where('budget_limits.start_date', $start->format('Y-m-d'))
->where('budget_limits.end_date', $end->format('Y-m-d'))
->where('budget_limits.transaction_currency_id', $currency->id)
->first(['budget_limits.*'])
;
if (null !== $limit) {
$validator->errors()->add('start', trans('validation.limit_exists'));
}
}
);
if ($validator->fails()) {
Log::channel('audit')->error(sprintf('Validation errors in %s', self::class), $validator->errors()->toArray());
}
}
} }

View File

@@ -24,7 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit;
use Illuminate\Contracts\Validation\Validator; use FireflyIII\Rules\IsBoolean;
use Illuminate\Validation\Validator;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
@@ -46,12 +47,15 @@ class UpdateRequest extends FormRequest
public function getAll(): array public function getAll(): array
{ {
$fields = [ $fields = [
'start' => ['start', 'date'], 'start' => ['start', 'date'],
'end' => ['end', 'date'], 'end' => ['end', 'date'],
'amount' => ['amount', 'convertString'], 'amount' => ['amount', 'convertString'],
'currency_id' => ['currency_id', 'convertInteger'], 'currency_id' => ['currency_id', 'convertInteger'],
'currency_code' => ['currency_code', 'convertString'], 'currency_code' => ['currency_code', 'convertString'],
'notes' => ['notes', 'stringWithNewlines'], 'notes' => ['notes', 'stringWithNewlines'],
// webhooks
'fire_webhooks' => ['fire_webhooks', 'boolean'],
]; ];
if (false === $this->has('notes')) { if (false === $this->has('notes')) {
// ignore notes, not submitted. // ignore notes, not submitted.
@@ -67,12 +71,15 @@ class UpdateRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'start' => 'date|after:1970-01-02|before:2038-01-17', 'start' => 'date|after:1970-01-02|before:2038-01-17',
'end' => 'date|after:1970-01-02|before:2038-01-17', 'end' => 'date|after:1970-01-02|before:2038-01-17',
'amount' => ['nullable', new IsValidPositiveAmount()], 'amount' => ['nullable', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'notes' => 'nullable|min:0|max:32768', 'notes' => 'nullable|min:0|max:32768',
// webhooks
'fire_webhooks' => [new IsBoolean()],
]; ];
} }

View File

@@ -28,7 +28,7 @@ use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException; use Carbon\Exceptions\InvalidFormatException;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
class StoreByCurrenciesRequest extends FormRequest class StoreByCurrenciesRequest extends FormRequest
@@ -55,11 +55,11 @@ class StoreByCurrenciesRequest extends FormRequest
{ {
$validator->after( $validator->after(
static function (Validator $validator): void { static function (Validator $validator): void {
$data = $validator->getData() ?? []; $data = $validator->getData();
foreach ($data as $date => $rate) { foreach ($data as $date => $rate) {
try { try {
$date = Carbon::createFromFormat('Y-m-d', $date); Carbon::createFromFormat('Y-m-d', $date);
} catch (InvalidFormatException $e) { } catch (InvalidFormatException) {
$validator->errors()->add('date', trans('validation.date', ['attribute' => 'date'])); $validator->errors()->add('date', trans('validation.date', ['attribute' => 'date']));
return; return;

View File

@@ -24,10 +24,12 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate; namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate;
use Illuminate\Validation\Validator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
class StoreByDateRequest extends FormRequest class StoreByDateRequest extends FormRequest
@@ -35,6 +37,9 @@ class StoreByDateRequest extends FormRequest
use ChecksLogin; use ChecksLogin;
use ConvertsDataTypes; use ConvertsDataTypes;
/**
* @return array<string, mixed>
*/
public function getAll(): array public function getAll(): array
{ {
return [ return [
@@ -45,11 +50,13 @@ class StoreByDateRequest extends FormRequest
public function getFromCurrency(): TransactionCurrency public function getFromCurrency(): TransactionCurrency
{ {
return TransactionCurrency::where('code', $this->get('from'))->first(); return Amount::getTransactionCurrencyByCode((string)$this->get('from'));
} }
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
*
* @return array<string, string>
*/ */
public function rules(): array public function rules(): array
{ {
@@ -79,8 +86,10 @@ class StoreByDateRequest extends FormRequest
continue; continue;
} }
$to = TransactionCurrency::where('code', $key)->first();
if (null === $to) { try {
Amount::getTransactionCurrencyByCode((string)$key);
} catch (FireflyException) {
$validator->errors()->add(sprintf('rates.%s', $key), trans('validation.invalid_currency_code', ['code' => $key])); $validator->errors()->add(sprintf('rates.%s', $key), trans('validation.invalid_currency_code', ['code' => $key]));
} }
} }

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
@@ -42,7 +43,7 @@ class StoreRequest extends FormRequest
public function getFromCurrency(): TransactionCurrency public function getFromCurrency(): TransactionCurrency
{ {
return TransactionCurrency::where('code', $this->get('from'))->first(); return Amount::getTransactionCurrencyByCode((string) $this->get('from'));
} }
public function getRate(): string public function getRate(): string
@@ -52,7 +53,7 @@ class StoreRequest extends FormRequest
public function getToCurrency(): TransactionCurrency public function getToCurrency(): TransactionCurrency
{ {
return TransactionCurrency::where('code', $this->get('to'))->first(); return Amount::getTransactionCurrencyByCode((string) $this->get('to'));
} }
/** /**

View File

@@ -24,7 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\PiggyBank; namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsValidZeroOrMoreAmount; use FireflyIII\Rules\IsValidZeroOrMoreAmount;
@@ -96,7 +97,7 @@ class StoreRequest extends FormRequest
// validate start before end only if both are there. // validate start before end only if both are there.
$data = $validator->getData(); $data = $validator->getData();
$currency = $this->getCurrencyFromData($validator, $data); $currency = $this->getCurrencyFromData($validator, $data);
if (null === $currency) { if (!$currency instanceof TransactionCurrency) {
return; return;
} }
$targetAmount = (string) ($data['target_amount'] ?? '0'); $targetAmount = (string) ($data['target_amount'] ?? '0');
@@ -135,16 +136,10 @@ class StoreRequest extends FormRequest
private function getCurrencyFromData(Validator $validator, array $data): ?TransactionCurrency private function getCurrencyFromData(Validator $validator, array $data): ?TransactionCurrency
{ {
if (array_key_exists('transaction_currency_code', $data) && '' !== (string) $data['transaction_currency_code']) { if (array_key_exists('transaction_currency_code', $data) && '' !== (string) $data['transaction_currency_code']) {
$currency = TransactionCurrency::whereCode($data['transaction_currency_code'])->first(); return Amount::getTransactionCurrencyByCode((string) $data['transaction_currency_code']);
if (null !== $currency) {
return $currency;
}
} }
if (array_key_exists('transaction_currency_id', $data) && '' !== (string) $data['transaction_currency_id']) { if (array_key_exists('transaction_currency_id', $data) && '' !== (string) $data['transaction_currency_id']) {
$currency = TransactionCurrency::find((int) $data['transaction_currency_id']); return Amount::getTransactionCurrencyById((int) $data['transaction_currency_id']);
if (null !== $currency) {
return $currency;
}
} }
$validator->errors()->add('transaction_currency_id', trans('validation.require_currency_id_code')); $validator->errors()->add('transaction_currency_id', trans('validation.require_currency_id_code'));

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Recurrence; namespace FireflyIII\Api\V1\Requests\Models\Recurrence;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Rules\IsValidPositiveAmount;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Recurrence; namespace FireflyIII\Api\V1\Requests\Models\Recurrence;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Models\Recurrence; use FireflyIII\Models\Recurrence;
use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Rule; namespace FireflyIII\Api\V1\Requests\Models\Rule;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Rules\IsValidActionExpression;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Rule; namespace FireflyIII\Api\V1\Requests\Models\Rule;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Models\Rule; use FireflyIII\Models\Rule;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Rules\IsValidActionExpression;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Transaction; namespace FireflyIII\Api\V1\Requests\Models\Transaction;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Models\Location; use FireflyIII\Models\Location;
use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
@@ -183,6 +183,7 @@ class StoreRequest extends FormRequest
// basic fields for group: // basic fields for group:
'group_title' => 'min:1|max:1000|nullable', 'group_title' => 'min:1|max:1000|nullable',
'error_if_duplicate_hash' => [new IsBoolean()], 'error_if_duplicate_hash' => [new IsBoolean()],
'fire_webhooks' => [new IsBoolean()],
'apply_rules' => [new IsBoolean()], 'apply_rules' => [new IsBoolean()],
// location rules // location rules

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Transaction; namespace FireflyIII\Api\V1\Requests\Models\Transaction;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\BelongsUser;
@@ -64,7 +64,7 @@ class UpdateRequest extends FormRequest
*/ */
public function getAll(): array public function getAll(): array
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$this->integerFields = ['order', 'currency_id', 'foreign_currency_id', 'transaction_journal_id', 'source_id', 'destination_id', 'budget_id', 'category_id', 'bill_id', 'recurrence_id']; $this->integerFields = ['order', 'currency_id', 'foreign_currency_id', 'transaction_journal_id', 'source_id', 'destination_id', 'budget_id', 'category_id', 'bill_id', 'recurrence_id'];
$this->dateFields = ['date', 'interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date']; $this->dateFields = ['date', 'interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];
$this->textareaFields = ['notes']; $this->textareaFields = ['notes'];
@@ -97,7 +97,7 @@ class UpdateRequest extends FormRequest
*/ */
private function getTransactionData(): array private function getTransactionData(): array
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$return = []; $return = [];
/** @var null|array $transactions */ /** @var null|array $transactions */
@@ -181,7 +181,7 @@ class UpdateRequest extends FormRequest
private function getDateData(array $current, array $transaction): array private function getDateData(array $current, array $transaction): array
{ {
foreach ($this->dateFields as $fieldName) { foreach ($this->dateFields as $fieldName) {
app('log')->debug(sprintf('Now at date field %s', $fieldName)); Log::debug(sprintf('Now at date field %s', $fieldName));
if (array_key_exists($fieldName, $transaction)) { if (array_key_exists($fieldName, $transaction)) {
Log::debug(sprintf('New value: "%s"', $transaction[$fieldName])); Log::debug(sprintf('New value: "%s"', $transaction[$fieldName]));
$current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]); $current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]);
@@ -247,7 +247,7 @@ class UpdateRequest extends FormRequest
*/ */
public function rules(): array public function rules(): array
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$validProtocols = config('firefly.valid_url_protocols'); $validProtocols = config('firefly.valid_url_protocols');
return [ return [
@@ -330,7 +330,7 @@ class UpdateRequest extends FormRequest
*/ */
public function withValidator(Validator $validator): void public function withValidator(Validator $validator): void
{ {
app('log')->debug('Now in withValidator'); Log::debug('Now in withValidator');
/** @var TransactionGroup $transactionGroup */ /** @var TransactionGroup $transactionGroup */
$transactionGroup = $this->route()->parameter('transactionGroup'); $transactionGroup = $this->route()->parameter('transactionGroup');

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\TransactionLink; namespace FireflyIII\Api\V1\Requests\Models\TransactionLink;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\TransactionLink; namespace FireflyIII\Api\V1\Requests\Models\TransactionLink;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Models\TransactionJournalLink;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\System; namespace FireflyIII\Api\V1\Requests\System;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Validation\Validator;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;

View File

@@ -28,7 +28,6 @@ namespace FireflyIII\Casts;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
/** /**
* Class SeparateTimezoneCaster * Class SeparateTimezoneCaster

View File

@@ -29,12 +29,15 @@ use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory; use FireflyIII\Factory\AccountFactory;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Facades\Log;
class CorrectsAccountTypes extends Command class CorrectsAccountTypes extends Command
{ {
@@ -45,6 +48,7 @@ class CorrectsAccountTypes extends Command
private int $count; private int $count;
private array $expected; private array $expected;
private AccountFactory $factory; private AccountFactory $factory;
private AccountRepositoryInterface $repository;
/** /**
* Execute the console command. * Execute the console command.
@@ -110,7 +114,7 @@ class CorrectsAccountTypes extends Command
if ($resultSet->count() > 0) { if ($resultSet->count() > 0) {
$this->friendlyLine(sprintf('Found %d journals that need to be fixed.', $resultSet->count())); $this->friendlyLine(sprintf('Found %d journals that need to be fixed.', $resultSet->count()));
foreach ($resultSet as $entry) { foreach ($resultSet as $entry) {
app('log')->debug(sprintf('Now fixing journal #%d', $entry->id)); Log::debug(sprintf('Now fixing journal #%d', $entry->id));
/** @var null|TransactionJournal $journal */ /** @var null|TransactionJournal $journal */
$journal = TransactionJournal::find($entry->id); $journal = TransactionJournal::find($entry->id);
@@ -120,7 +124,7 @@ class CorrectsAccountTypes extends Command
} }
} }
if (0 !== $this->count) { if (0 !== $this->count) {
app('log')->debug(sprintf('%d journals had to be fixed.', $this->count)); Log::debug(sprintf('%d journals had to be fixed.', $this->count));
$this->friendlyInfo(sprintf('Acted on %d transaction(s)', $this->count)); $this->friendlyInfo(sprintf('Acted on %d transaction(s)', $this->count));
} }
@@ -134,10 +138,10 @@ class CorrectsAccountTypes extends Command
private function inspectJournal(TransactionJournal $journal): void private function inspectJournal(TransactionJournal $journal): void
{ {
app('log')->debug(sprintf('Now inspecting journal #%d', $journal->id)); Log::debug(sprintf('Now inspecting journal #%d', $journal->id));
$transactions = $journal->transactions()->count(); $transactions = $journal->transactions()->count();
if (2 !== $transactions) { if (2 !== $transactions) {
app('log')->debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions)); Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions));
$this->friendlyError(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions)); $this->friendlyError(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions));
return; return;
@@ -151,20 +155,20 @@ class CorrectsAccountTypes extends Command
$destAccountType = $destAccount->accountType->type; $destAccountType = $destAccount->accountType->type;
if (!array_key_exists($type, $this->expected)) { if (!array_key_exists($type, $this->expected)) {
app('log')->info(sprintf('No source/destination info for transaction type %s.', $type)); Log::info(sprintf('No source/destination info for transaction type %s.', $type));
$this->friendlyError(sprintf('No source/destination info for transaction type %s.', $type)); $this->friendlyError(sprintf('No source/destination info for transaction type %s.', $type));
return; return;
} }
if (!array_key_exists($sourceAccountType, $this->expected[$type])) { if (!array_key_exists($sourceAccountType, $this->expected[$type])) {
app('log')->debug(sprintf('[a] Going to fix journal #%d', $journal->id)); Log::debug(sprintf('[a] Going to fix journal #%d', $journal->id));
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
return; return;
} }
$expectedTypes = $this->expected[$type][$sourceAccountType]; $expectedTypes = $this->expected[$type][$sourceAccountType];
if (!in_array($destAccountType, $expectedTypes, true)) { if (!in_array($destAccountType, $expectedTypes, true)) {
app('log')->debug(sprintf('[b] Going to fix journal #%d', $journal->id)); Log::debug(sprintf('[b] Going to fix journal #%d', $journal->id));
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
} }
} }
@@ -181,13 +185,15 @@ class CorrectsAccountTypes extends Command
private function fixJournal(TransactionJournal $journal, string $transactionType, Transaction $source, Transaction $dest): void private function fixJournal(TransactionJournal $journal, string $transactionType, Transaction $source, Transaction $dest): void
{ {
app('log')->debug(sprintf('Going to fix journal #%d', $journal->id)); Log::debug(sprintf('Going to fix journal #%d', $journal->id));
$this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUser($journal->user);
++$this->count; ++$this->count;
// variables: // variables:
$sourceType = $source->account->accountType->type; $sourceType = $source->account->accountType->type;
$destinationType = $dest->account->accountType->type; $destinationType = $dest->account->accountType->type;
$combination = sprintf('%s%s%s', $transactionType, $source->account->accountType->type, $dest->account->accountType->type); $combination = sprintf('%s%s%s', $transactionType, $source->account->accountType->type, $dest->account->accountType->type);
app('log')->debug(sprintf('Combination is "%s"', $combination)); Log::debug(sprintf('Combination is "%s"', $combination));
if ($this->shouldBeTransfer($transactionType, $sourceType, $destinationType)) { if ($this->shouldBeTransfer($transactionType, $sourceType, $destinationType)) {
$this->makeTransfer($journal); $this->makeTransfer($journal);
@@ -211,37 +217,45 @@ class CorrectsAccountTypes extends Command
} }
// transaction has no valid source. // transaction has no valid source.
$validSources = array_keys($this->expected[$transactionType]); $validSources = array_keys($this->expected[$transactionType]);
$canCreateSource = $this->canCreateSource($validSources); $canCreateSource = $this->canCreateSource($validSources);
$hasValidSource = $this->hasValidAccountType($validSources, $sourceType); $hasValidSource = $this->hasValidAccountType($validSources, $sourceType);
if (!$hasValidSource && $canCreateSource) { if (!$hasValidSource && $canCreateSource) {
$this->giveNewRevenue($journal, $source); $this->giveNewRevenue($journal, $source);
return; return;
} }
if (!$canCreateSource && !$hasValidSource) { if (!$canCreateSource && !$hasValidSource) {
app('log')->debug('This transaction type has no source we can create. Just give error.'); Log::debug('This transaction type has no source we can create. Just give error.');
$message = sprintf('The source account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $transactionType, $journal->id, $source->account->accountType->type); $message = sprintf('The source account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $transactionType, $journal->id, $source->account->accountType->type);
$this->friendlyError($message); $this->friendlyError($message);
app('log')->debug($message); Log::debug($message);
return; return;
} }
/** @var array $validDestinations */ /** @var array $validDestinations */
$validDestinations = $this->expected[$transactionType][$sourceType] ?? []; $validDestinations = $this->expected[$transactionType][$sourceType] ?? [];
$canCreateDestination = $this->canCreateDestination($validDestinations); $canCreateDestination = $this->canCreateDestination($validDestinations);
$hasValidDestination = $this->hasValidAccountType($validDestinations, $destinationType); $hasValidDestination = $this->hasValidAccountType($validDestinations, $destinationType);
$alternativeDestination = $this->repository->findByName($dest->account->name, $validDestinations);
if (!$hasValidDestination && $canCreateDestination) { if (!$hasValidDestination && $canCreateDestination) {
$this->giveNewExpense($journal, $dest); $this->giveNewExpense($journal, $dest);
return; return;
} }
if (!$canCreateDestination && !$hasValidDestination) { if (!$canCreateDestination && !$hasValidDestination && null === $alternativeDestination) {
app('log')->debug('This transaction type has no destination we can create. Just give error.'); Log::debug('This transaction type has no destination we can create. Just give error.');
$message = sprintf('The destination account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $transactionType, $journal->id, $dest->account->accountType->type); $message = sprintf('The destination account of %s #%d cannot be of type "%s". Firefly III cannot fix this. You may have to remove the transaction yourself.', $transactionType, $journal->id, $dest->account->accountType->type);
$this->friendlyError($message); $this->friendlyError($message);
app('log')->debug($message); Log::debug($message);
}
if (!$canCreateDestination && !$hasValidDestination && null !== $alternativeDestination) {
Log::debug('This transaction type has no destination we can create, but found alternative with the same name.');
$message = sprintf('The destination account of %s #%d cannot be of type "%s". Firefly III found an alternative account. Please make sure this transaction is correct.', $transactionType, $journal->transaction_group_id, $dest->account->accountType->type);
$this->friendlyInfo($message);
Log::debug($message);
$this->giveNewDestinationAccount($journal, $alternativeDestination);
} }
} }
@@ -263,7 +277,7 @@ class CorrectsAccountTypes extends Command
$journal->save(); $journal->save();
$message = sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id); $message = sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id);
$this->friendlyInfo($message); $this->friendlyInfo($message);
app('log')->debug($message); Log::debug($message);
// check it again: // check it again:
$this->inspectJournal($journal); $this->inspectJournal($journal);
} }
@@ -281,7 +295,7 @@ class CorrectsAccountTypes extends Command
$journal->save(); $journal->save();
$message = sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id); $message = sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id);
$this->friendlyInfo($message); $this->friendlyInfo($message);
app('log')->debug($message); Log::debug($message);
// check it again: // check it again:
$this->inspectJournal($journal); $this->inspectJournal($journal);
} }
@@ -308,7 +322,7 @@ class CorrectsAccountTypes extends Command
$result->name $result->name
); );
$this->friendlyWarning($message); $this->friendlyWarning($message);
app('log')->debug($message); Log::debug($message);
$this->inspectJournal($journal); $this->inspectJournal($journal);
} }
@@ -335,7 +349,7 @@ class CorrectsAccountTypes extends Command
$result->name $result->name
); );
$this->friendlyWarning($message); $this->friendlyWarning($message);
app('log')->debug($message); Log::debug($message);
$this->inspectJournal($journal); $this->inspectJournal($journal);
} }
@@ -354,14 +368,14 @@ class CorrectsAccountTypes extends Command
private function giveNewRevenue(TransactionJournal $journal, Transaction $source): void private function giveNewRevenue(TransactionJournal $journal, Transaction $source): void
{ {
app('log')->debug(sprintf('An account of type "%s" could be a valid source.', AccountTypeEnum::REVENUE->value)); Log::debug(sprintf('An account of type "%s" could be a valid source.', AccountTypeEnum::REVENUE->value));
$this->factory->setUser($journal->user); $this->factory->setUser($journal->user);
$name = $source->account->name; $name = $source->account->name;
$newSource = $this->factory->findOrCreate($name, AccountTypeEnum::REVENUE->value); $newSource = $this->factory->findOrCreate($name, AccountTypeEnum::REVENUE->value);
$source->account()->associate($newSource); $source->account()->associate($newSource);
$source->save(); $source->save();
$this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new source %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::REVENUE->value, $newSource->id, $newSource->name)); $this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new source %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::REVENUE->value, $newSource->id, $newSource->name));
app('log')->debug(sprintf('Associated account #%d with transaction #%d', $newSource->id, $source->id)); Log::debug(sprintf('Associated account #%d with transaction #%d', $newSource->id, $source->id));
$this->inspectJournal($journal); $this->inspectJournal($journal);
} }
@@ -372,14 +386,33 @@ class CorrectsAccountTypes extends Command
private function giveNewExpense(TransactionJournal $journal, Transaction $destination): void private function giveNewExpense(TransactionJournal $journal, Transaction $destination): void
{ {
app('log')->debug(sprintf('An account of type "%s" could be a valid destination.', AccountTypeEnum::EXPENSE->value)); Log::debug(sprintf('An account of type "%s" could be a valid destination.', AccountTypeEnum::EXPENSE->value));
$this->factory->setUser($journal->user); $this->factory->setUser($journal->user);
$name = $destination->account->name; $name = $destination->account->name;
$newDestination = $this->factory->findOrCreate($name, AccountTypeEnum::EXPENSE->value); $newDestination = $this->factory->findOrCreate($name, AccountTypeEnum::EXPENSE->value);
$destination->account()->associate($newDestination); $destination->account()->associate($newDestination);
$destination->save(); $destination->save();
$this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new destination %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::EXPENSE->value, $newDestination->id, $newDestination->name)); $this->friendlyPositive(sprintf('Firefly III gave transaction #%d a new destination %s: #%d ("%s").', $journal->transaction_group_id, AccountTypeEnum::EXPENSE->value, $newDestination->id, $newDestination->name));
app('log')->debug(sprintf('Associated account #%d with transaction #%d', $newDestination->id, $destination->id)); Log::debug(sprintf('Associated account #%d with transaction #%d', $newDestination->id, $destination->id));
$this->inspectJournal($journal); $this->inspectJournal($journal);
} }
private function giveNewDestinationAccount(TransactionJournal $journal, Account $newDestination): void
{
$destTransaction = $this->getDestinationTransaction($journal);
$oldDest = $destTransaction->account;
$destTransaction->account_id = $newDestination->id;
$destTransaction->save();
$message = sprintf(
'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").',
$journal->id,
$oldDest->id,
$oldDest->name,
$newDestination->id,
$newDestination->name
);
$this->friendlyInfo($message);
$journal->refresh();
Log::debug($message);
}
} }

View File

@@ -75,7 +75,8 @@ class CorrectsDatabase extends Command
'correction:recalculates-liabilities', 'correction:recalculates-liabilities',
'correction:preferences', 'correction:preferences',
// 'correction:transaction-types', // resource heavy, disabled. // 'correction:transaction-types', // resource heavy, disabled.
'correction:recalculate-pc-amounts', // not necessary, disabled. 'correction:recalculate-pc-amounts',
'correction:remove-links-to-deleted-objects',
'firefly-iii:report-integrity', 'firefly-iii:report-integrity',
]; ];
foreach ($commands as $command) { foreach ($commands as $command) {

View File

@@ -45,9 +45,7 @@ class CorrectsGroupAccounts extends Command
public function handle(): int public function handle(): int
{ {
$groups = []; $groups = [];
$res = TransactionJournal::groupBy('transaction_group_id') $res = TransactionJournal::groupBy('transaction_group_id')->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')])// @phpstan-ignore-line
;
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($res as $journal) { foreach ($res as $journal) {

View File

@@ -57,8 +57,6 @@ class CorrectsPiggyBanks extends Command
$event->transaction_journal_id = null; $event->transaction_journal_id = null;
$event->save(); $event->save();
++$count; ++$count;
continue;
} }
} }
if (0 !== $count) { if (0 !== $count) {

View File

@@ -263,9 +263,9 @@ class CorrectsUnevenAmount extends Command
// Log::debug(sprintf('[c] %s', var_export($source->transaction_currency_id === $destination->foreign_currency_id,true))); // Log::debug(sprintf('[c] %s', var_export($source->transaction_currency_id === $destination->foreign_currency_id,true)));
// Log::debug(sprintf('[d] %s', var_export((int) $destination->transaction_currency_id ===(int) $source->foreign_currency_id, true))); // Log::debug(sprintf('[d] %s', var_export((int) $destination->transaction_currency_id ===(int) $source->foreign_currency_id, true)));
if (0 === bccomp((string) app('steam')->positive($source->amount), (string) app('steam')->positive($destination->foreign_amount)) if (0 === bccomp(Steam::positive($source->amount), Steam::positive($destination->foreign_amount))
&& $source->transaction_currency_id === $destination->foreign_currency_id && $source->transaction_currency_id === $destination->foreign_currency_id
&& 0 === bccomp((string) app('steam')->positive($destination->amount), (string) app('steam')->positive($source->foreign_amount)) && 0 === bccomp(Steam::positive($destination->amount), Steam::positive($source->foreign_amount))
&& (int) $destination->transaction_currency_id === (int) $source->foreign_currency_id && (int) $destination->transaction_currency_id === (int) $source->foreign_currency_id
) { ) {
return true; return true;
@@ -302,10 +302,10 @@ class CorrectsUnevenAmount extends Command
private function isBetweenAssetAndLiability(TransactionJournal $journal): bool private function isBetweenAssetAndLiability(TransactionJournal $journal): bool
{ {
/** @var Transaction $sourceTransaction */ /** @var null|Transaction $sourceTransaction */
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
/** @var Transaction $destinationTransaction */ /** @var null|Transaction $destinationTransaction */
$destinationTransaction = $journal->transactions()->where('amount', '>', 0)->first(); $destinationTransaction = $journal->transactions()->where('amount', '>', 0)->first();
if (null === $sourceTransaction || null === $destinationTransaction) { if (null === $sourceTransaction || null === $destinationTransaction) {
Log::warning('Either transaction is false, stop.'); Log::warning('Either transaction is false, stop.');

View File

@@ -55,10 +55,7 @@ class RemovesEmptyJournals extends Command
*/ */
private function deleteUnevenJournals(): void private function deleteUnevenJournals(): void
{ {
$set = Transaction::whereNull('deleted_at') $set = Transaction::whereNull('deleted_at')->groupBy('transactions.transaction_journal_id')->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']);
->groupBy('transactions.transaction_journal_id')
->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']) // @phpstan-ignore-line
;
$total = 0; $total = 0;
/** @var Transaction $row */ /** @var Transaction $row */

View File

@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
/*
* RemovesLinksToDeletedObjects.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class RemovesLinksToDeletedObjects extends Command
{
use ShowsFriendlyMessages;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'correction:remove-links-to-deleted-objects';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes deleted entries from intermediate tables.';
/**
* Execute the console command.
*/
public function handle(): void
{
$deletedTags = Tag::withTrashed()->whereNotNull('deleted_at')->get('tags.id')->pluck('id')->toArray();
$deletedJournals = TransactionJournal::withTrashed()->whereNotNull('deleted_at')->get('transaction_journals.id')->pluck('id')->toArray();
$deletedBudgets = Budget::withTrashed()->whereNotNull('deleted_at')->get('budgets.id')->pluck('id')->toArray();
$deletedCategories = Category::withTrashed()->whereNotNull('deleted_at')->get('categories.id')->pluck('id')->toArray();
if (count($deletedTags) > 0) {
$this->cleanupTags($deletedTags);
}
if (count($deletedJournals) > 0) {
$this->cleanupJournals($deletedJournals);
}
if (count($deletedBudgets) > 0) {
$this->cleanupBudgets($deletedBudgets);
}
if (count($deletedCategories) > 0) {
$this->cleanupCategories($deletedCategories);
}
$this->friendlyNeutral('Validated links to deleted objects.');
}
private function cleanupTags(array $tags): void
{
$count = DB::table('tag_transaction_journal')->whereIn('tag_id', $tags)->delete();
if ($count > 0) {
$this->friendlyInfo(sprintf('Removed %d old relationship(s) categories transactions and tags.', $count));
}
}
private function cleanupJournals(array $journals): void
{
$count = DB::table('tag_transaction_journal')->whereIn('transaction_journal_id', $journals)->delete();
if ($count > 0) {
$this->friendlyInfo(sprintf('Removed %d old relationship(s) between tags and transactions.', $count));
}
$count = DB::table('budget_transaction_journal')->whereIn('transaction_journal_id', $journals)->delete();
if ($count > 0) {
$this->friendlyInfo(sprintf('Removed %d old relationship(s) between budgets and transactions.', $count));
}
$count = DB::table('category_transaction_journal')->whereIn('transaction_journal_id', $journals)->delete();
if ($count > 0) {
$this->friendlyInfo(sprintf('Removed %d old relationship(s) categories and transactions.', $count));
}
}
private function cleanupBudgets(array $budgets): void
{
$count = DB::table('budget_transaction_journal')->whereIn('budget_id', $budgets)->delete();
if ($count > 0) {
$this->friendlyInfo(sprintf('Removed %d old relationship(s) between budgets and transactions.', $count));
}
}
private function cleanupCategories(array $categories): void
{
$count = DB::table('category_transaction_journal')->whereIn('category_id', $categories)->delete();
if ($count > 0) {
$this->friendlyInfo(sprintf('Removed %d old relationship(s) categories categories and transactions.', $count));
}
}
}

View File

@@ -71,7 +71,6 @@ class RestoresOAuthKeys extends Command
$this->storeKeysInDB(); $this->storeKeysInDB();
$this->friendlyInfo('Stored OAuth keys in database.'); $this->friendlyInfo('Stored OAuth keys in database.');
return;
} }
} }

View File

@@ -32,18 +32,7 @@ class ValidatesEnvironmentVariables extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
/**
* The console command description.
*
* @var null|string
*/
protected $description = 'Makes sure you use the correct variables.'; protected $description = 'Makes sure you use the correct variables.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'integrity:validates-environment-variables'; protected $signature = 'integrity:validates-environment-variables';
/** /**

View File

@@ -515,7 +515,7 @@ class ForcesDecimalSize extends Command
continue; continue;
} }
// fix $field by rounding it down correctly. // fix $field by rounding it down correctly.
$pow = (float) 10 ** $currency->decimal_places; $pow = 10.0 ** $currency->decimal_places;
$correct = bcdiv((string) round((float) $value * $pow), (string) $pow, 12); $correct = bcdiv((string) round((float) $value * $pow), (string) $pow, 12);
$this->friendlyWarning(sprintf('Transaction #%d has amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct)); $this->friendlyWarning(sprintf('Transaction #%d has amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct));
@@ -546,7 +546,7 @@ class ForcesDecimalSize extends Command
continue; continue;
} }
// fix $field by rounding it down correctly. // fix $field by rounding it down correctly.
$pow = (float) 10 ** $currency->decimal_places; $pow = 10.0 ** $currency->decimal_places;
$correct = bcdiv((string) round((float) $value * $pow), (string) $pow, 12); $correct = bcdiv((string) round((float) $value * $pow), (string) $pow, 12);
$this->friendlyWarning( $this->friendlyWarning(
sprintf('Transaction #%d has foreign amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct) sprintf('Transaction #%d has foreign amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct)

View File

@@ -26,11 +26,14 @@ namespace FireflyIII\Console\Commands\System;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Support\System\GeneratesInstallationId; use FireflyIII\Support\System\GeneratesInstallationId;
use FireflyIII\Support\System\IsOldVersion;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Random\RandomException;
class OutputsInstructions extends Command class OutputsInstructions extends Command
{ {
use GeneratesInstallationId; use GeneratesInstallationId;
use IsOldVersion;
protected $description = 'Instructions in case of upgrade trouble.'; protected $description = 'Instructions in case of upgrade trouble.';
@@ -57,7 +60,7 @@ class OutputsInstructions extends Command
*/ */
private function updateInstructions(): void private function updateInstructions(): void
{ {
$version = (string) config('firefly.version'); $version = (string)config('firefly.version');
/** @var array $config */ /** @var array $config */
$config = config('upgrade.text.upgrade'); $config = config('upgrade.text.upgrade');
@@ -67,12 +70,12 @@ class OutputsInstructions extends Command
foreach (array_keys($config) as $compare) { foreach (array_keys($config) as $compare) {
// if string starts with: // if string starts with:
if (str_starts_with($version, $compare)) { if (str_starts_with($version, $compare)) {
$text = (string) $config[$compare]; $text = (string)$config[$compare];
} }
} }
// validate some settings. // validate some settings.
if ('' === $text && 'local' === (string) config('app.env')) { if ('' === $text && 'local' === (string)config('app.env')) {
$text = 'Please set APP_ENV=production for a safer environment.'; $text = 'Please set APP_ENV=production for a safer environment.';
} }
@@ -190,7 +193,7 @@ class OutputsInstructions extends Command
*/ */
private function installInstructions(): void private function installInstructions(): void
{ {
$version = (string) config('firefly.version'); $version = (string)config('firefly.version');
/** @var array $config */ /** @var array $config */
$config = config('upgrade.text.install'); $config = config('upgrade.text.install');
@@ -200,12 +203,12 @@ class OutputsInstructions extends Command
foreach (array_keys($config) as $compare) { foreach (array_keys($config) as $compare) {
// if string starts with: // if string starts with:
if (str_starts_with($version, $compare)) { if (str_starts_with($version, $compare)) {
$text = (string) $config[$compare]; $text = (string)$config[$compare];
} }
} }
// validate some settings. // validate some settings.
if ('' === $text && 'local' === (string) config('app.env')) { if ('' === $text && 'local' === (string)config('app.env')) {
$text = 'Please set APP_ENV=production for a safer environment.'; $text = 'Please set APP_ENV=production for a safer environment.';
} }
@@ -241,14 +244,15 @@ class OutputsInstructions extends Command
private function someQuote(): void private function someQuote(): void
{ {
$lines = [ $lines = [
'Forgive yourself for not being at peace.', '"Forgive yourself for not being at peace."',
'Doesn\'t look like anything to me.', '"Doesn\'t look like anything to me."',
'Be proud of what you make.', '"Be proud of what you make."',
'Be there or forever wonder.', '"Be there or forever wonder."',
'A year from now you will wish you had started today.', '"A year from now you will wish you had started today."',
'🇺🇦 Слава Україні!',
'🇺🇦 Slava Ukraini!',
]; ];
$addQuotes = true;
// fuck the Russian aggression in Ukraine. // fuck the Russian aggression in Ukraine.
@@ -259,18 +263,16 @@ class OutputsInstructions extends Command
// going on, to allow that to happen. // going on, to allow that to happen.
if ('ru_RU' === config('firefly.default_language')) { if ('ru_RU' === config('firefly.default_language')) {
$addQuotes = false; $lines = [
$lines = [
'🇺🇦 Слава Україні!', '🇺🇦 Слава Україні!',
'🇺🇦 Slava Ukraini!', '🇺🇦 Slava Ukraini!',
]; ];
} }
$random = random_int(0, count($lines) - 1); try {
if ($addQuotes) { $random = random_int(0, count($lines) - 1);
$this->line(sprintf(' "%s"', $lines[$random])); } catch (RandomException) {
$random = 0;
return;
} }
$this->line(sprintf(' %s', $lines[$random])); $this->line(sprintf(' %s', $lines[$random]));

View File

@@ -50,7 +50,7 @@ class RecalculatesRunningBalance extends Command
/** /**
* Execute the console command. * Execute the console command.
*/ */
public function handle() public function handle(): int
{ {
if (true === config('firefly.feature_flags.running_balance_column')) { if (true === config('firefly.feature_flags.running_balance_column')) {
$this->friendlyInfo('Will recalculate account balances. This may take a LONG time. Please be patient.'); $this->friendlyInfo('Will recalculate account balances. This may take a LONG time. Please be patient.');
@@ -60,6 +60,8 @@ class RecalculatesRunningBalance extends Command
return 0; return 0;
} }
$this->friendlyWarning('This command has been disabled.'); $this->friendlyWarning('This command has been disabled.');
return 0;
} }
private function correctBalanceAmounts(bool $forced): void private function correctBalanceAmounts(bool $forced): void

View File

@@ -29,6 +29,9 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Symfony\Component\Console\Command\Command as CommandAlias; use Symfony\Component\Console\Command\Command as CommandAlias;
use function Safe\file_put_contents;
use function Safe\json_encode;
class ResetsErrorMailLimit extends Command class ResetsErrorMailLimit extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;

View File

@@ -78,8 +78,8 @@ class ScansAttachments extends Command
} }
$tempFileName = tempnam(sys_get_temp_dir(), 'FireflyIII'); $tempFileName = tempnam(sys_get_temp_dir(), 'FireflyIII');
file_put_contents($tempFileName, $decryptedContent); file_put_contents($tempFileName, $decryptedContent);
$attachment->md5 = (string)md5_file($tempFileName); $attachment->md5 = md5_file($tempFileName);
$attachment->mime = (string)mime_content_type($tempFileName); $attachment->mime = mime_content_type($tempFileName);
$attachment->save(); $attachment->save();
$this->friendlyInfo(sprintf('Fixed attachment #%d', $attachment->id)); $this->friendlyInfo(sprintf('Fixed attachment #%d', $attachment->id));
} }

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System; namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class SetsLatestVersion extends Command class SetsLatestVersion extends Command
@@ -45,8 +46,7 @@ class SetsLatestVersion extends Command
return 0; return 0;
} }
app('fireflyconfig')->set('db_version', config('firefly.db_version')); FireflyConfig::set('ff3_version', config('firefly.version'));
app('fireflyconfig')->set('ff3_version', config('firefly.version'));
$this->friendlyInfo('Updated version.'); $this->friendlyInfo('Updated version.');
return 0; return 0;

View File

@@ -283,7 +283,7 @@ class ApplyRules extends Command
if (null !== $endString && '' !== $endString) { if (null !== $endString && '' !== $endString) {
$inputEnd = Carbon::createFromFormat('Y-m-d', $endString); $inputEnd = Carbon::createFromFormat('Y-m-d', $endString);
} }
if (null === $inputEnd || null === $inputStart) { if (!$inputEnd instanceof Carbon || null === $inputStart) {
Log::error('Could not parse start or end date in verifyInputDate().'); Log::error('Could not parse start or end date in verifyInputDate().');
return; return;

View File

@@ -66,7 +66,7 @@ class RemovesDatabaseDecryption extends Command
* @var string $table * @var string $table
* @var array $fields * @var array $fields
*/ */
foreach ($tables as $table => $fields) { foreach ($tables as $table => $fields) { // @phpstan-ignore-line
$this->decryptTable($table, $fields); $this->decryptTable($table, $fields);
} }

View File

@@ -25,9 +25,11 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Upgrade; namespace FireflyIII\Console\Commands\Upgrade;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserGroup;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -65,7 +67,7 @@ class UpgradesCurrencyPreferences extends Command
{ {
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) { if (null !== $configVar) {
return (bool) $configVar->data; return (bool)$configVar->data;
} }
return false; return false;
@@ -104,8 +106,8 @@ class UpgradesCurrencyPreferences extends Command
private function upgradeUserPreferences(User $user): void private function upgradeUserPreferences(User $user): void
{ {
$currencies = TransactionCurrency::get(); $currencies = TransactionCurrency::get();
$enabled = new Collection(); $enabled = new Collection();
/** @var TransactionCurrency $currency */ /** @var TransactionCurrency $currency */
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
@@ -116,10 +118,11 @@ class UpgradesCurrencyPreferences extends Command
$user->currencies()->sync($enabled->pluck('id')->toArray()); $user->currencies()->sync($enabled->pluck('id')->toArray());
// set the default currency for the user and for the group: // set the default currency for the user and for the group:
$preference = $this->getPreference($user); $preference = $this->getPreference($user);
$primaryCurrency = TransactionCurrency::where('code', $preference)->first();
if (null === $primaryCurrency) { try {
// get EUR $primaryCurrency = Amount::getTransactionCurrencyByCode($preference);
} catch (FireflyException) {
$primaryCurrency = TransactionCurrency::where('code', 'EUR')->first(); $primaryCurrency = TransactionCurrency::where('code', 'EUR')->first();
} }
$user->currencies()->updateExistingPivot($primaryCurrency->id, ['user_default' => true]); $user->currencies()->updateExistingPivot($primaryCurrency->id, ['user_default' => true]);
@@ -135,7 +138,7 @@ class UpgradesCurrencyPreferences extends Command
} }
if (null !== $preference->data && !is_array($preference->data)) { if (null !== $preference->data && !is_array($preference->data)) {
return (string) $preference->data; return (string)$preference->data;
} }
return 'EUR'; return 'EUR';

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Upgrade; namespace FireflyIII\Console\Commands\Upgrade;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Safe\Exceptions\InfoException; use Safe\Exceptions\InfoException;
@@ -86,10 +87,8 @@ class UpgradesDatabase extends Command
$this->friendlyLine(sprintf('Now executing %s', $command)); $this->friendlyLine(sprintf('Now executing %s', $command));
$this->call($command, $args); $this->call($command, $args);
} }
// set new DB version.
app('fireflyconfig')->set('db_version', (int) config('firefly.db_version'));
// index will set FF3 version. // index will set FF3 version.
app('fireflyconfig')->set('ff3_version', (string) config('firefly.version')); FireflyConfig::set('ff3_version', (string) config('firefly.version'));
return 0; return 0;
} }

View File

@@ -157,7 +157,6 @@ class UpgradesLiabilitiesEight extends Command
$service = new TransactionGroupDestroyService(); $service = new TransactionGroupDestroyService();
$service->destroy($group); $service->destroy($group);
return;
} }
} }

View File

@@ -37,7 +37,7 @@ class ActuallyLoggedIn extends Event
public User $user; public User $user;
public function __construct(null|Authenticatable|User $user) public function __construct(Authenticatable|User|null $user)
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -1,43 +0,0 @@
<?php
/*
* DestroyedTransactionLink.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
use FireflyIII\Models\TransactionJournalLink;
use Illuminate\Queue\SerializesModels;
/**
* Class DestroyedTransactionLink
*/
class DestroyedTransactionLink extends Event
{
use SerializesModels;
// @phpstan-ignore-line
/**
* DestroyedTransactionLink constructor.
*/
public function __construct(private TransactionJournalLink $link) {}
}

View File

@@ -35,7 +35,7 @@ class DisabledMFA extends Event
public User $user; public User $user;
public function __construct(null|Authenticatable|User $user) public function __construct(Authenticatable|User|null $user)
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -35,7 +35,7 @@ class EnabledMFA extends Event
public User $user; public User $user;
public function __construct(null|Authenticatable|User $user) public function __construct(Authenticatable|User|null $user)
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -35,7 +35,7 @@ class MFABackupFewLeft extends Event
public User $user; public User $user;
public function __construct(null|Authenticatable|User $user, public int $count) public function __construct(Authenticatable|User|null $user, public int $count)
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -35,7 +35,7 @@ class MFABackupNoLeft extends Event
public User $user; public User $user;
public function __construct(null|Authenticatable|User $user) public function __construct(Authenticatable|User|null $user)
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -35,7 +35,7 @@ class MFAManyFailedAttempts extends Event
public User $user; public User $user;
public function __construct(null|Authenticatable|User $user, public int $count) public function __construct(Authenticatable|User|null $user, public int $count)
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -35,7 +35,7 @@ class MFANewBackupCodes extends Event
public User $user; public User $user;
public function __construct(null|Authenticatable|User $user) public function __construct(Authenticatable|User|null $user)
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -35,7 +35,7 @@ class MFAUsedBackupCode extends Event
public User $user; public User $user;
public function __construct(null|Authenticatable|User $user) public function __construct(Authenticatable|User|null $user)
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -35,7 +35,7 @@ class UserAttemptedLogin extends Event
public User $user; public User $user;
public function __construct(null|Authenticatable|User $user) public function __construct(Authenticatable|User|null $user)
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -86,6 +86,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
return $this->handleAttachment($request, $e); return $this->handleAttachment($request, $e);
case 'bills.show': case 'bills.show':
case 'subscriptions.show':
$request->session()->reflash(); $request->session()->reflash();
return redirect(route('bills.index')); return redirect(route('bills.index'));
@@ -128,10 +129,6 @@ class GracefulNotFoundHandler extends ExceptionHandler
return redirect(route('categories.index')); return redirect(route('categories.index'));
case 'rules.edit': case 'rules.edit':
$request->session()->reflash();
return redirect(route('rules.index'));
case 'rule-groups.edit': case 'rule-groups.edit':
$request->session()->reflash(); $request->session()->reflash();
@@ -170,7 +167,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
} }
/** @var null|Account $account */ /** @var null|Account $account */
$account = $user->accounts()->with(['accountType'])->withTrashed()->find($accountId); $account = $user->accounts()->withTrashed()->with(['accountType'])->find($accountId);
if (null === $account) { if (null === $account) {
app('log')->error(sprintf('Could not find account %d, so give big fat error.', $accountId)); app('log')->error(sprintf('Could not find account %d, so give big fat error.', $accountId));

View File

@@ -242,6 +242,7 @@ class PiggyBankFactory
} }
} }
} }
Log::debug('Looping all accounts.');
/** @var array $info */ /** @var array $info */
foreach ($accounts as $info) { foreach ($accounts as $info) {
@@ -251,6 +252,7 @@ class PiggyBankFactory
continue; continue;
} }
Log::debug(sprintf('Working on account #%d', $account->id));
if (array_key_exists('current_amount', $info) && null !== $info['current_amount']) { if (array_key_exists('current_amount', $info) && null !== $info['current_amount']) {
// an amount is set, first check out if there is a difference with the previous amount. // an amount is set, first check out if there is a difference with the previous amount.
$previous = $toBeLinked[$account->id]['current_amount'] ?? '0'; $previous = $toBeLinked[$account->id]['current_amount'] ?? '0';
@@ -258,22 +260,24 @@ class PiggyBankFactory
// create event for difference. // create event for difference.
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// 2025-10-01 for issue #10990 disable this event.
Log::debug(sprintf('[a] Will save event for difference %s (previous value was %s)', $diff, $previous)); Log::debug(sprintf('[a] Will save event for difference %s (previous value was %s)', $diff, $previous));
event(new ChangedAmount($piggyBank, $diff, null, null)); // event(new ChangedAmount($piggyBank, $diff, null, null));
} }
$toBeLinked[$account->id] = ['current_amount' => $info['current_amount']]; $toBeLinked[$account->id] = ['current_amount' => $info['current_amount']];
Log::debug(sprintf('[a] Will link account #%d with amount %s', $account->id, $info['current_amount'])); Log::debug(sprintf('[a] Will link account #%d with amount %s', $account->id, $info['current_amount']));
} }
if (array_key_exists('current_amount', $info) && null === $info['current_amount']) { if (array_key_exists('current_amount', $info) && null === $info['current_amount']) {
// an amount is set, first check out if there is a difference with the previous amount. // no amount is set, first check out if there is a difference with the previous amount.
$previous = $toBeLinked[$account->id]['current_amount'] ?? '0'; $previous = $toBeLinked[$account->id]['current_amount'] ?? '0';
$diff = bcsub('0', $previous); $diff = bcsub('0', $previous);
// create event for difference. // create event for difference.
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// 2025-10-01 for issue #10990 disable this event.
Log::debug(sprintf('[b] Will save event for difference %s (previous value was %s)', $diff, $previous)); Log::debug(sprintf('[b] Will save event for difference %s (previous value was %s)', $diff, $previous));
event(new ChangedAmount($piggyBank, $diff, null, null)); // event(new ChangedAmount($piggyBank, $diff, null, null));
} }
// no amount set, use previous amount or go to ZERO. // no amount set, use previous amount or go to ZERO.
@@ -282,7 +286,8 @@ class PiggyBankFactory
// create event: // create event:
Log::debug('linkToAccountIds: Trigger change for positive amount [b].'); Log::debug('linkToAccountIds: Trigger change for positive amount [b].');
event(new ChangedAmount($piggyBank, $toBeLinked[$account->id]['current_amount'], null, null)); // 2025-10-01 for issue #10990 disable this event.
// event(new ChangedAmount($piggyBank, $toBeLinked[$account->id]['current_amount'] ?? '0', null, null));
} }
if (!array_key_exists('current_amount', $info)) { if (!array_key_exists('current_amount', $info)) {
$toBeLinked[$account->id] ??= []; $toBeLinked[$account->id] ??= [];

View File

@@ -26,7 +26,9 @@ namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Log;
/** /**
* Class TransactionCurrencyFactory * Class TransactionCurrencyFactory
@@ -41,14 +43,14 @@ class TransactionCurrencyFactory
$data['code'] = e($data['code']); $data['code'] = e($data['code']);
$data['symbol'] = e($data['symbol']); $data['symbol'] = e($data['symbol']);
$data['name'] = e($data['name']); $data['name'] = e($data['name']);
$data['decimal_places'] = (int) $data['decimal_places']; $data['decimal_places'] = (int)$data['decimal_places'];
// if the code already exists (deleted) // if the code already exists (deleted)
// force delete it and then create the transaction: // force delete it and then create the transaction:
$count = TransactionCurrency::withTrashed()->whereCode($data['code'])->count(); $count = TransactionCurrency::withTrashed()->whereCode($data['code'])->count();
if (1 === $count) { if (1 === $count) {
$old = TransactionCurrency::withTrashed()->whereCode($data['code'])->first(); $old = TransactionCurrency::withTrashed()->whereCode($data['code'])->first();
$old->forceDelete(); $old->forceDelete();
app('log')->warning(sprintf('Force deleted old currency with ID #%d and code "%s".', $old->id, $data['code'])); Log::warning(sprintf('Force deleted old currency with ID #%d and code "%s".', $old->id, $data['code']));
} }
try { try {
@@ -64,8 +66,8 @@ class TransactionCurrencyFactory
); );
} catch (QueryException $e) { } catch (QueryException $e) {
$result = null; $result = null;
app('log')->error(sprintf('Could not create new currency: %s', $e->getMessage())); Log::error(sprintf('Could not create new currency: %s', $e->getMessage()));
app('log')->error($e->getTraceAsString()); Log::error($e->getTraceAsString());
throw new FireflyException('400004: Could not store new currency.', 0, $e); throw new FireflyException('400004: Could not store new currency.', 0, $e);
} }
@@ -76,32 +78,33 @@ class TransactionCurrencyFactory
public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency
{ {
$currencyCode = e($currencyCode); $currencyCode = e($currencyCode);
$currencyId = (int) $currencyId; $currencyId = (int)$currencyId;
$currency = null;
if ('' === $currencyCode && 0 === $currencyId) { if ('' === $currencyCode && 0 === $currencyId) {
app('log')->debug('Cannot find anything on empty currency code and empty currency ID!'); Log::debug('Cannot find anything on empty currency code and empty currency ID!');
return null; return null;
} }
// first by ID: // first by ID:
if ($currencyId > 0) { if ($currencyId > 0) {
$currency = TransactionCurrency::find($currencyId); try {
if (null !== $currency) { $currency = Amount::getTransactionCurrencyById($currencyId);
return $currency; } catch (FireflyException) {
Log::warning(sprintf('Currency ID is #%d but found nothing!', $currencyId));
} }
app('log')->warning(sprintf('Currency ID is %d but found nothing!', $currencyId));
} }
// then by code: // then by code:
if ('' !== $currencyCode) { if ('' !== $currencyCode && null === $currency) {
$currency = TransactionCurrency::whereCode($currencyCode)->first(); try {
if (null !== $currency) { $currency = Amount::getTransactionCurrencyByCode($currencyCode);
return $currency; } catch (FireflyException) {
Log::warning(sprintf('Currency code is "%s" but found nothing!', $currencyCode));
} }
app('log')->warning(sprintf('Currency code is %d but found nothing!', $currencyCode));
} }
app('log')->warning('Found nothing for currency.'); Log::info(sprintf('Found currency #%d based on ID %d and code "%s".', $currency->id, $currencyId, $currencyCode));
return null; return $currency;
} }
} }

View File

@@ -222,7 +222,7 @@ class TransactionJournalFactory
Log::debug('Source info:', $sourceInfo); Log::debug('Source info:', $sourceInfo);
Log::debug('Destination info:', $destInfo); Log::debug('Destination info:', $destInfo);
$sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo); $sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo);
$destinationAccount = $this->getAccount($type->type, 'destination', $destInfo); $destinationAccount = $this->getAccount($type->type, 'destination', $destInfo, $sourceAccount);
Log::debug('Done with getAccount(2x)'); Log::debug('Done with getAccount(2x)');
@@ -270,7 +270,7 @@ class TransactionJournalFactory
$negative = $transactionFactory->createNegative((string) $row['amount'], (string) $row['foreign_amount']); $negative = $transactionFactory->createNegative((string) $row['amount'], (string) $row['foreign_amount']);
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error(sprintf('Exception creating negative transaction: %s', $e->getMessage())); Log::error(sprintf('Exception creating negative transaction: %s', $e->getMessage()));
$this->forceDeleteOnError(new Collection([$journal])); $this->forceDeleteOnError(new Collection()->push($journal));
throw new FireflyException($e->getMessage(), 0, $e); throw new FireflyException($e->getMessage(), 0, $e);
} }
@@ -305,7 +305,7 @@ class TransactionJournalFactory
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error(sprintf('Exception creating positive transaction: %s', $e->getMessage())); Log::error(sprintf('Exception creating positive transaction: %s', $e->getMessage()));
$this->forceTrDelete($negative); $this->forceTrDelete($negative);
$this->forceDeleteOnError(new Collection([$journal])); $this->forceDeleteOnError(new Collection()->push($journal));
throw new FireflyException($e->getMessage(), 0, $e); throw new FireflyException($e->getMessage(), 0, $e);
} }

View File

@@ -132,7 +132,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end)->withAccountInformation() $collector->setAccounts(new Collection()->push($account))->setRange($this->start, $this->end)->withAccountInformation()
->withBudgetInformation()->withCategoryInformation()->withBillInformation()->withNotes() ->withBudgetInformation()->withCategoryInformation()->withBillInformation()->withNotes()
; ;
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();

Some files were not shown because too many files have changed in this diff Show More