Compare commits

..

146 Commits

Author SHA1 Message Date
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
456 changed files with 9731 additions and 11072 deletions

View File

@@ -402,16 +402,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.86.0",
"version": "v3.88.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36"
"reference": "f23469674ae50d40e398bfff8018911a2a2b0dbe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4a952bd19dc97879b0620f495552ef09b55f7d36",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f23469674ae50d40e398bfff8018911a2a2b0dbe",
"reference": "f23469674ae50d40e398bfff8018911a2a2b0dbe",
"shasum": ""
},
"require": {
@@ -422,39 +422,38 @@
"ext-hash": "*",
"ext-json": "*",
"ext-tokenizer": "*",
"fidry/cpu-core-counter": "^1.2",
"fidry/cpu-core-counter": "^1.3",
"php": "^7.4 || ^8.0",
"react/child-process": "^0.6.6",
"react/event-loop": "^1.5",
"react/promise": "^3.2",
"react/promise": "^3.3",
"react/socket": "^1.16",
"react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
"symfony/console": "^5.4.47 || ^6.4.13 || ^7.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0",
"symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0",
"symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0",
"symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0",
"symfony/polyfill-mbstring": "^1.32",
"symfony/polyfill-php80": "^1.32",
"symfony/polyfill-php81": "^1.32",
"symfony/process": "^5.4.47 || ^6.4.20 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0"
"symfony/console": "^5.4.47 || ^6.4.24 || ^7.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/polyfill-mbstring": "^1.33",
"symfony/polyfill-php80": "^1.33",
"symfony/polyfill-php81": "^1.33",
"symfony/polyfill-php84": "^1.33",
"symfony/process": "^5.4.47 || ^6.4.24 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0"
},
"require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.6",
"infection/infection": "^0.29.14",
"justinrainbow/json-schema": "^5.3 || ^6.4",
"facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.31.0",
"justinrainbow/json-schema": "^6.5",
"keradus/cli-executor": "^2.2",
"mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.8",
"php-cs-fixer/accessible-object": "^1.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25",
"symfony/polyfill-php84": "^1.32",
"symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1",
"symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1"
"phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
"symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2",
"symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
@@ -495,7 +494,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.86.0"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.0"
},
"funding": [
{
@@ -503,7 +502,7 @@
"type": "github"
}
],
"time": "2025-08-13T22:36:21+00:00"
"time": "2025-09-24T21:31:42+00:00"
},
{
"name": "psr/container",
@@ -2284,6 +2283,86 @@
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php84",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php84.git",
"reference": "d8ced4d875142b6a7426000426b8abc631d6b191"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191",
"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.3",

View File

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

View File

@@ -314,8 +314,9 @@ DEMO_USERNAME=
DEMO_PASSWORD=
#
# Disable or enable the running balance column data
# Please disable this. It's a very experimental feature.
# Disable or enable the running balance column data.
# 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

View File

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

View File

@@ -250,7 +250,7 @@ jobs:
fi
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'"
IFS='/' read -ra parts <<< "$PR_URL"
PR_NR=$(printf %s\\n "${parts[@]:(-1)}")
@@ -272,7 +272,7 @@ jobs:
echo "Also merge everything into main since this is a release."
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'"
IFS='/' read -ra parts <<< "$PR_URL"

View File

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

View File

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

View File

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

View File

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

View File

@@ -76,7 +76,7 @@ class BudgetController extends Controller
/** @var Budget $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 */
foreach ($expenses as $expense) {

View File

@@ -76,7 +76,7 @@ class CategoryController extends Controller
/** @var Category $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 */
foreach ($expenses as $expense) {

View File

@@ -76,7 +76,7 @@ class CategoryController extends Controller
/** @var Category $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 */
foreach ($expenses as $expense) {

View File

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

View File

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

View File

@@ -76,7 +76,7 @@ class CategoryController extends Controller
/** @var Category $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 */
foreach ($expenses as $expense) {

View File

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

View File

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

View File

@@ -152,7 +152,7 @@ class ListController extends Controller
// use new group collector:
/** @var GroupCollectorInterface $collector */
$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)
;

View File

@@ -82,18 +82,22 @@ class ShowController extends Controller
// get list of accounts. Count it and split it.
$this->repository->resetAccountOrder();
// TODO fix sort.
$collection = $this->repository->getAccountsByType($types, null);
$collection = $this->repository->getAccountsByType($types, $params['sort']);
$count = $collection->count();
// 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']);
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setSort($params['sort']);
$enrichment->setDate($this->parameters->get('date'));
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$enrichment->setUser($admin);
$accounts = $enrichment->enrich($accounts);
@@ -117,7 +121,7 @@ class ShowController extends Controller
*
* Show single instance.
*/
public function show(Account $account): JsonResponse
public function show(ShowRequest $request, Account $account): JsonResponse
{
// get list of accounts. Count it and split it.
$this->repository->resetAccountOrder();
@@ -129,6 +133,8 @@ class ShowController extends Controller
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setDate($this->parameters->get('date'));
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$enrichment->setUser($admin);
$account = $enrichment->enrichSingle($account);

View File

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

View File

@@ -67,7 +67,9 @@ class StoreController extends Controller
*/
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();
$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
{
$data = $request->getAll();
$data['fire_webhooks'] ??= true;
$budget = $this->repository->update($budget, $data);
$manager = $this->getManager();

View File

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

View File

@@ -70,6 +70,7 @@ class StoreController extends Controller
$data = $request->getAll();
$data['start_date'] = $data['start'];
$data['end_date'] = $data['end'];
$data['fire_webhooks'] ??= true;
$data['budget_id'] = $budget->id;
$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.');
}
$data = $request->getAll();
$data['fire_webhooks'] ??= true;
$data['budget_id'] = $budget->id;
$budgetLimit = $this->blRepository->update($budgetLimit, $data);
$manager = $this->getManager();

View File

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

View File

@@ -75,7 +75,7 @@ class TriggerController extends Controller
/** @var RuleEngineInterface $ruleEngine */
$ruleEngine = app(RuleEngineInterface::class);
$ruleEngine->setRules(new Collection([$rule]));
$ruleEngine->setRules(new Collection()->push($rule));
// overrule the rule(s) if necessary.
if (array_key_exists('start', $parameters) && null !== $parameters['start']) {
@@ -129,7 +129,7 @@ class TriggerController extends Controller
/** @var RuleEngineInterface $ruleEngine */
$ruleEngine = app(RuleEngineInterface::class);
$ruleEngine->setRules(new Collection([$rule]));
$ruleEngine->setRules(new Collection()->push($rule));
// overrule the rule(s) if necessary.
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\Bill;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
@@ -192,7 +190,7 @@ class ListController extends Controller
$enrichment->setUser($admin);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$bills = $enrichment->enrichSingle($bills);
$bills = $enrichment->enrich($bills);
// make paginator:
$paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page'));
@@ -268,7 +266,6 @@ class ListController extends Controller
// filter selection
$collection = $unfiltered->filter(
static function (Recurrence $recurrence) use ($currency) { // @phpstan-ignore-line
/** @var RecurrenceTransaction $transaction */
if (array_any($recurrence->recurrenceTransactions, fn ($transaction) => $transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id)) {
return $recurrence;
}
@@ -320,7 +317,6 @@ class ListController extends Controller
$collection = $unfiltered->filter(
static function (Rule $rule) use ($currency) { // @phpstan-ignore-line
/** @var RuleTrigger $trigger */
if (array_any($rule->ruleTriggers, fn ($trigger) => 'currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value)) {
return $rule;
}

View File

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

View File

@@ -133,7 +133,6 @@ class ConfigurationController extends Controller
*/
public function show(string $configKey): JsonResponse
{
$data = [];
$dynamic = $this->getDynamicConfiguration();
$shortKey = str_replace('configuration.', '', $configKey);
if (str_starts_with($configKey, 'configuration.')) {
@@ -156,13 +155,11 @@ class ConfigurationController extends Controller
}
// fallback
if (!str_starts_with($configKey, 'configuration.')) {
$data = [
'title' => $configKey,
'value' => config($configKey),
'editable' => false,
];
}
$data = [
'title' => $configKey,
'value' => config($shortKey),
'editable' => false,
];
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::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
$engine->setTrigger(WebhookTrigger::tryFrom($webhook->trigger));
// tell the generator which objects to process
$engine->setObjects(new Collection([$group]));
// set the webhook to trigger
$engine->setWebhooks(new Collection([$webhook]));
// tell the generator to generate the messages
$engine->generateMessages();
/** @var \FireflyIII\Models\WebhookTrigger $trigger */
foreach ($webhook->webhookTriggers as $trigger) {
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser(auth()->user());
// tell the generator which trigger it should look for
$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:
Log::debug('send event RequestedSendWebhookMessages from ShowController::triggerTransaction()');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Budget;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\Validator;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
@@ -48,17 +48,20 @@ class StoreRequest extends FormRequest
public function getAll(): array
{
$fields = [
'name' => ['name', 'convertString'],
'active' => ['active', 'boolean'],
'order' => ['active', 'convertInteger'],
'notes' => ['notes', 'convertString'],
'name' => ['name', 'convertString'],
'active' => ['active', 'boolean'],
'order' => ['active', 'convertInteger'],
'notes' => ['notes', 'convertString'],
// auto budget currency:
'currency_id' => ['auto_budget_currency_id', 'convertInteger'],
'currency_code' => ['auto_budget_currency_code', 'convertString'],
'auto_budget_type' => ['auto_budget_type', 'convertString'],
'auto_budget_amount' => ['auto_budget_amount', 'convertString'],
'auto_budget_period' => ['auto_budget_period', 'convertString'],
'currency_id' => ['auto_budget_currency_id', 'convertInteger'],
'currency_code' => ['auto_budget_currency_code', 'convertString'],
'auto_budget_type' => ['auto_budget_type', 'convertString'],
'auto_budget_amount' => ['auto_budget_amount', 'convertString'],
'auto_budget_period' => ['auto_budget_period', 'convertString'],
// webhooks
'fire_webhooks' => ['fire_webhooks', 'boolean'],
];
return $this->getAllData($fields);
@@ -70,15 +73,18 @@ class StoreRequest extends FormRequest
public function rules(): array
{
return [
'name' => 'required|min:1|max:255|uniqueObjectForUser:budgets,name',
'active' => [new IsBoolean()],
'currency_id' => 'exists:transaction_currencies,id',
'currency_code' => 'exists:transaction_currencies,code',
'notes' => 'nullable|min:1|max:32768',
'name' => 'required|min:1|max:255|uniqueObjectForUser:budgets,name',
'active' => [new IsBoolean()],
'currency_id' => 'exists:transaction_currencies,id',
'currency_code' => 'exists:transaction_currencies,code',
'notes' => 'nullable|min:1|max:32768',
// auto budget info
'auto_budget_type' => 'in:reset,rollover,adjusted,none',
'auto_budget_amount' => ['required_if:auto_budget_type,reset', 'required_if:auto_budget_type,rollover', 'required_if:auto_budget_type,adjusted', new IsValidPositiveAmount()],
'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_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_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;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\Validator;
use FireflyIII\Models\Budget;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
@@ -50,15 +50,18 @@ class UpdateRequest extends FormRequest
{
// this is the way:
$fields = [
'name' => ['name', 'convertString'],
'active' => ['active', 'boolean'],
'order' => ['order', 'convertInteger'],
'notes' => ['notes', 'convertString'],
'currency_id' => ['auto_budget_currency_id', 'convertInteger'],
'currency_code' => ['auto_budget_currency_code', 'convertString'],
'auto_budget_type' => ['auto_budget_type', 'convertString'],
'auto_budget_amount' => ['auto_budget_amount', 'convertString'],
'auto_budget_period' => ['auto_budget_period', 'convertString'],
'name' => ['name', 'convertString'],
'active' => ['active', 'boolean'],
'order' => ['order', 'convertInteger'],
'notes' => ['notes', 'convertString'],
'currency_id' => ['auto_budget_currency_id', 'convertInteger'],
'currency_code' => ['auto_budget_currency_code', 'convertString'],
'auto_budget_type' => ['auto_budget_type', 'convertString'],
'auto_budget_amount' => ['auto_budget_amount', 'convertString'],
'auto_budget_period' => ['auto_budget_period', 'convertString'],
// webhooks
'fire_webhooks' => ['fire_webhooks', 'boolean'],
];
$allData = $this->getAllData($fields);
if (array_key_exists('auto_budget_type', $allData)) {
@@ -83,14 +86,17 @@ class UpdateRequest extends FormRequest
$budget = $this->route()->parameter('budget');
return [
'name' => sprintf('min:1|max:100|uniqueObjectForUser:budgets,name,%d', $budget->id),
'active' => [new IsBoolean()],
'notes' => 'nullable|min:1|max:32768',
'auto_budget_type' => 'in:reset,rollover,adjusted,none',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_currency_code' => 'exists:transaction_currencies,code',
'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()],
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
'name' => sprintf('min:1|max:100|uniqueObjectForUser:budgets,name,%d', $budget->id),
'active' => [new IsBoolean()],
'notes' => 'nullable|min:1|max:32768',
'auto_budget_type' => 'in:reset,rollover,adjusted,none',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_currency_code' => 'exists:transaction_currencies,code',
'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()],
'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;
use Carbon\Carbon;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
/**
* Class StoreRequest
@@ -49,6 +55,9 @@ class StoreRequest extends FormRequest
'currency_id' => $this->convertInteger('currency_id'),
'currency_code' => $this->convertString('currency_code'),
'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
{
return [
'start' => 'required|before:end|date',
'end' => 'required|after:start|date',
'amount' => ['required', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'notes' => 'nullable|min:0|max:32768',
'start' => 'required|before:end|date',
'end' => 'required|after:start|date',
'amount' => ['required', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'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;
use Illuminate\Contracts\Validation\Validator;
use FireflyIII\Rules\IsBoolean;
use Illuminate\Validation\Validator;
use Carbon\Carbon;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
@@ -46,12 +47,15 @@ class UpdateRequest extends FormRequest
public function getAll(): array
{
$fields = [
'start' => ['start', 'date'],
'end' => ['end', 'date'],
'amount' => ['amount', 'convertString'],
'currency_id' => ['currency_id', 'convertInteger'],
'currency_code' => ['currency_code', 'convertString'],
'notes' => ['notes', 'stringWithNewlines'],
'start' => ['start', 'date'],
'end' => ['end', 'date'],
'amount' => ['amount', 'convertString'],
'currency_id' => ['currency_id', 'convertInteger'],
'currency_code' => ['currency_code', 'convertString'],
'notes' => ['notes', 'stringWithNewlines'],
// webhooks
'fire_webhooks' => ['fire_webhooks', 'boolean'],
];
if (false === $this->has('notes')) {
// ignore notes, not submitted.
@@ -67,12 +71,15 @@ class UpdateRequest extends FormRequest
public function rules(): array
{
return [
'start' => 'date|after:1970-01-02|before:2038-01-17',
'end' => 'date|after:1970-01-02|before:2038-01-17',
'amount' => ['nullable', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'notes' => 'nullable|min:0|max:32768',
'start' => 'date|after:1970-01-02|before:2038-01-17',
'end' => 'date|after:1970-01-02|before:2038-01-17',
'amount' => ['nullable', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'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 FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
class StoreByCurrenciesRequest extends FormRequest
@@ -55,11 +55,11 @@ class StoreByCurrenciesRequest extends FormRequest
{
$validator->after(
static function (Validator $validator): void {
$data = $validator->getData() ?? [];
$data = $validator->getData();
foreach ($data as $date => $rate) {
try {
$date = Carbon::createFromFormat('Y-m-d', $date);
} catch (InvalidFormatException $e) {
Carbon::createFromFormat('Y-m-d', $date);
} catch (InvalidFormatException) {
$validator->errors()->add('date', trans('validation.date', ['attribute' => 'date']));
return;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,12 +29,15 @@ use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Facades\Log;
class CorrectsAccountTypes extends Command
{
@@ -45,6 +48,7 @@ class CorrectsAccountTypes extends Command
private int $count;
private array $expected;
private AccountFactory $factory;
private AccountRepositoryInterface $repository;
/**
* Execute the console command.
@@ -110,7 +114,7 @@ class CorrectsAccountTypes extends Command
if ($resultSet->count() > 0) {
$this->friendlyLine(sprintf('Found %d journals that need to be fixed.', $resultSet->count()));
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 */
$journal = TransactionJournal::find($entry->id);
@@ -120,7 +124,7 @@ class CorrectsAccountTypes extends Command
}
}
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));
}
@@ -134,10 +138,10 @@ class CorrectsAccountTypes extends Command
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();
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));
return;
@@ -151,20 +155,20 @@ class CorrectsAccountTypes extends Command
$destAccountType = $destAccount->accountType->type;
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));
return;
}
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);
return;
}
$expectedTypes = $this->expected[$type][$sourceAccountType];
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);
}
}
@@ -181,13 +185,15 @@ class CorrectsAccountTypes extends Command
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;
// variables:
$sourceType = $source->account->accountType->type;
$destinationType = $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));
$sourceType = $source->account->accountType->type;
$destinationType = $dest->account->accountType->type;
$combination = sprintf('%s%s%s', $transactionType, $source->account->accountType->type, $dest->account->accountType->type);
Log::debug(sprintf('Combination is "%s"', $combination));
if ($this->shouldBeTransfer($transactionType, $sourceType, $destinationType)) {
$this->makeTransfer($journal);
@@ -211,37 +217,45 @@ class CorrectsAccountTypes extends Command
}
// transaction has no valid source.
$validSources = array_keys($this->expected[$transactionType]);
$canCreateSource = $this->canCreateSource($validSources);
$hasValidSource = $this->hasValidAccountType($validSources, $sourceType);
$validSources = array_keys($this->expected[$transactionType]);
$canCreateSource = $this->canCreateSource($validSources);
$hasValidSource = $this->hasValidAccountType($validSources, $sourceType);
if (!$hasValidSource && $canCreateSource) {
$this->giveNewRevenue($journal, $source);
return;
}
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);
$this->friendlyError($message);
app('log')->debug($message);
Log::debug($message);
return;
}
/** @var array $validDestinations */
$validDestinations = $this->expected[$transactionType][$sourceType] ?? [];
$canCreateDestination = $this->canCreateDestination($validDestinations);
$hasValidDestination = $this->hasValidAccountType($validDestinations, $destinationType);
$validDestinations = $this->expected[$transactionType][$sourceType] ?? [];
$canCreateDestination = $this->canCreateDestination($validDestinations);
$hasValidDestination = $this->hasValidAccountType($validDestinations, $destinationType);
$alternativeDestination = $this->repository->findByName($dest->account->name, $validDestinations);
if (!$hasValidDestination && $canCreateDestination) {
$this->giveNewExpense($journal, $dest);
return;
}
if (!$canCreateDestination && !$hasValidDestination) {
app('log')->debug('This transaction type has no destination we can create. Just give error.');
if (!$canCreateDestination && !$hasValidDestination && null === $alternativeDestination) {
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);
$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();
$message = sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id);
$this->friendlyInfo($message);
app('log')->debug($message);
Log::debug($message);
// check it again:
$this->inspectJournal($journal);
}
@@ -281,7 +295,7 @@ class CorrectsAccountTypes extends Command
$journal->save();
$message = sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id);
$this->friendlyInfo($message);
app('log')->debug($message);
Log::debug($message);
// check it again:
$this->inspectJournal($journal);
}
@@ -308,7 +322,7 @@ class CorrectsAccountTypes extends Command
$result->name
);
$this->friendlyWarning($message);
app('log')->debug($message);
Log::debug($message);
$this->inspectJournal($journal);
}
@@ -335,7 +349,7 @@ class CorrectsAccountTypes extends Command
$result->name
);
$this->friendlyWarning($message);
app('log')->debug($message);
Log::debug($message);
$this->inspectJournal($journal);
}
@@ -354,14 +368,14 @@ class CorrectsAccountTypes extends Command
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);
$name = $source->account->name;
$newSource = $this->factory->findOrCreate($name, AccountTypeEnum::REVENUE->value);
$source->account()->associate($newSource);
$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));
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);
}
@@ -372,14 +386,33 @@ class CorrectsAccountTypes extends Command
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);
$name = $destination->account->name;
$newDestination = $this->factory->findOrCreate($name, AccountTypeEnum::EXPENSE->value);
$destination->account()->associate($newDestination);
$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));
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);
}
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:preferences',
// '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',
];
foreach ($commands as $command) {

View File

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

View File

@@ -57,8 +57,6 @@ class CorrectsPiggyBanks extends Command
$event->transaction_journal_id = null;
$event->save();
++$count;
continue;
}
}
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('[d] %s', var_export((int) $destination->transaction_currency_id ===(int) $source->foreign_currency_id, true)));
if (0 === bccomp((string) app('steam')->positive($source->amount), (string) app('steam')->positive($destination->foreign_amount))
if (0 === bccomp(Steam::positive($source->amount), Steam::positive($destination->foreign_amount))
&& $source->transaction_currency_id === $destination->foreign_currency_id
&& 0 === bccomp((string) app('steam')->positive($destination->amount), (string) app('steam')->positive($source->foreign_amount))
&& 0 === bccomp(Steam::positive($destination->amount), Steam::positive($source->foreign_amount))
&& (int) $destination->transaction_currency_id === (int) $source->foreign_currency_id
) {
return true;
@@ -302,10 +302,10 @@ class CorrectsUnevenAmount extends Command
private function isBetweenAssetAndLiability(TransactionJournal $journal): bool
{
/** @var Transaction $sourceTransaction */
/** @var null|Transaction $sourceTransaction */
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
/** @var Transaction $destinationTransaction */
/** @var null|Transaction $destinationTransaction */
$destinationTransaction = $journal->transactions()->where('amount', '>', 0)->first();
if (null === $sourceTransaction || null === $destinationTransaction) {
Log::warning('Either transaction is false, stop.');

View File

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

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->friendlyInfo('Stored OAuth keys in database.');
return;
}
}

View File

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

View File

@@ -515,7 +515,7 @@ class ForcesDecimalSize extends Command
continue;
}
// 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);
$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;
}
// 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);
$this->friendlyWarning(
sprintf('Transaction #%d has foreign amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct)

View File

@@ -27,6 +27,7 @@ namespace FireflyIII\Console\Commands\System;
use Carbon\Carbon;
use FireflyIII\Support\System\GeneratesInstallationId;
use Illuminate\Console\Command;
use Random\RandomException;
class OutputsInstructions extends Command
{
@@ -266,7 +267,11 @@ class OutputsInstructions extends Command
];
}
$random = random_int(0, count($lines) - 1);
try {
$random = random_int(0, count($lines) - 1);
} catch (RandomException) {
$random = 0;
}
if ($addQuotes) {
$this->line(sprintf(' "%s"', $lines[$random]));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 function __construct(null|Authenticatable|User $user)
public function __construct(Authenticatable|User|null $user)
{
if ($user instanceof User) {
$this->user = $user;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -282,7 +282,7 @@ class PiggyBankFactory
// create event:
Log::debug('linkToAccountIds: Trigger change for positive amount [b].');
event(new ChangedAmount($piggyBank, $toBeLinked[$account->id]['current_amount'], null, null));
event(new ChangedAmount($piggyBank, $toBeLinked[$account->id]['current_amount'] ?? '0', null, null));
}
if (!array_key_exists('current_amount', $info)) {
$toBeLinked[$account->id] ??= [];

View File

@@ -26,7 +26,9 @@ namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Log;
/**
* Class TransactionCurrencyFactory
@@ -41,14 +43,14 @@ class TransactionCurrencyFactory
$data['code'] = e($data['code']);
$data['symbol'] = e($data['symbol']);
$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)
// force delete it and then create the transaction:
$count = TransactionCurrency::withTrashed()->whereCode($data['code'])->count();
if (1 === $count) {
$old = TransactionCurrency::withTrashed()->whereCode($data['code'])->first();
$old->forceDelete();
app('log')->warning(sprintf('Force deleted old currency with ID #%d and code "%s".', $old->id, $data['code']));
Log::warning(sprintf('Force deleted old currency with ID #%d and code "%s".', $old->id, $data['code']));
}
try {
@@ -64,8 +66,8 @@ class TransactionCurrencyFactory
);
} catch (QueryException $e) {
$result = null;
app('log')->error(sprintf('Could not create new currency: %s', $e->getMessage()));
app('log')->error($e->getTraceAsString());
Log::error(sprintf('Could not create new currency: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
throw new FireflyException('400004: Could not store new currency.', 0, $e);
}
@@ -76,32 +78,33 @@ class TransactionCurrencyFactory
public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency
{
$currencyCode = e($currencyCode);
$currencyId = (int) $currencyId;
$currencyId = (int)$currencyId;
$currency = null;
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;
}
// first by ID:
if ($currencyId > 0) {
$currency = TransactionCurrency::find($currencyId);
if (null !== $currency) {
return $currency;
try {
$currency = Amount::getTransactionCurrencyById($currencyId);
} 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:
if ('' !== $currencyCode) {
$currency = TransactionCurrency::whereCode($currencyCode)->first();
if (null !== $currency) {
return $currency;
if ('' !== $currencyCode && null === $currency) {
try {
$currency = Amount::getTransactionCurrencyByCode($currencyCode);
} 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('Destination info:', $destInfo);
$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)');
@@ -270,7 +270,7 @@ class TransactionJournalFactory
$negative = $transactionFactory->createNegative((string) $row['amount'], (string) $row['foreign_amount']);
} catch (FireflyException $e) {
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);
}
@@ -305,7 +305,7 @@ class TransactionJournalFactory
} catch (FireflyException $e) {
Log::error(sprintf('Exception creating positive transaction: %s', $e->getMessage()));
$this->forceTrDelete($negative);
$this->forceDeleteOnError(new Collection([$journal]));
$this->forceDeleteOnError(new Collection()->push($journal));
throw new FireflyException($e->getMessage(), 0, $e);
}

View File

@@ -132,7 +132,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
/** @var GroupCollectorInterface $collector */
$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()
;
$journals = $collector->getExtractedJournals();

View File

@@ -184,6 +184,8 @@ class StandardMessageGenerator implements MessageGeneratorInterface
if ($model instanceof Budget) {
$enrichment = new BudgetEnrichment();
$enrichment->setUser($model->user);
/** @var Budget $model */
$model = $enrichment->enrichSingle($model);
$transformer = new BudgetTransformer();
$basicMessage['content'] = $transformer->transform($model);
@@ -197,6 +199,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
$parameters->set('start', $model->start_date);
$parameters->set('end', $model->end_date);
/** @var BudgetLimit $model */
$model = $enrichment->enrichSingle($model);
$transformer = new BudgetLimitTransformer();
$transformer->setParameters($parameters);
@@ -295,7 +298,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
$this->webhooks = $webhooks;
}
private function getRelevantResponse(array $triggers, WebhookResponseModel $response, $class): string
private function getRelevantResponse(array $triggers, WebhookResponseModel $response, string $class): string
{
// return none if none.
if (WebhookResponse::NONE->name === $response->title) {

View File

@@ -32,7 +32,6 @@ use FireflyIII\Notifications\Admin\UserInvitation;
use FireflyIII\Notifications\Admin\VersionCheckResult;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Notifications\Test\OwnerTestNotificationEmail;
use FireflyIII\Notifications\Test\OwnerTestNotificationNtfy;
use FireflyIII\Notifications\Test\OwnerTestNotificationPushover;
use FireflyIII\Notifications\Test\OwnerTestNotificationSlack;
use Illuminate\Support\Facades\Log;
@@ -140,10 +139,10 @@ class AdminEventHandler
break;
case 'ntfy':
$class = OwnerTestNotificationNtfy::class;
break;
// case 'ntfy':
// $class = OwnerTestNotificationNtfy::class;
//
// break;
case 'pushover':
$class = OwnerTestNotificationPushover::class;

View File

@@ -34,6 +34,8 @@ use FireflyIII\Support\Facades\Preferences;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use function Safe\json_encode;
/**
* Class BillEventHandler
*/

View File

@@ -52,7 +52,7 @@ class DestroyedGroupEventHandler
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection([$group]));
$engine->setObjects(new Collection()->push($group));
$engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));

View File

@@ -28,6 +28,7 @@ use FireflyIII\Events\RequestedSendWebhookMessages;
use FireflyIII\Events\StoredTransactionGroup;
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
@@ -36,6 +37,8 @@ use Illuminate\Support\Facades\Log;
/**
* Class StoredGroupEventHandler
*
* TODO migrate to observer?
*/
class StoredGroupEventHandler
{
@@ -44,6 +47,7 @@ class StoredGroupEventHandler
$this->processRules($event);
$this->recalculateCredit($event);
$this->triggerWebhooks($event);
$this->removePeriodStatistics($event);
}
/**
@@ -94,6 +98,20 @@ class StoredGroupEventHandler
$object->recalculate();
}
private function removePeriodStatistics(StoredTransactionGroup $event): void
{
/** @var PeriodStatisticRepositoryInterface $repository */
$repository = app(PeriodStatisticRepositoryInterface::class);
/** @var TransactionJournal $journal */
foreach ($event->transactionGroup->transactionJournals as $journal) {
$source = $journal->transactions()->where('amount', '<', '0')->first();
$dest = $journal->transactions()->where('amount', '>', '0')->first();
$repository->deleteStatisticsForModel($source->account, $journal->date);
$repository->deleteStatisticsForModel($dest->account, $journal->date);
}
}
/**
* This method processes all webhooks that respond to the "stored transaction group" trigger (100)
*/
@@ -116,7 +134,7 @@ class StoredGroupEventHandler
// tell the generator which trigger it should look for
$engine->setTrigger(WebhookTrigger::STORE_TRANSACTION);
// tell the generator which objects to process
$engine->setObjects(new Collection([$group]));
$engine->setObjects(new Collection()->push($group));
// tell the generator to generate the messages
$engine->generateMessages();

View File

@@ -31,6 +31,7 @@ use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
use FireflyIII\Support\Models\AccountBalanceCalculator;
@@ -49,10 +50,26 @@ class UpdatedGroupEventHandler
$this->processRules($event);
$this->recalculateCredit($event);
$this->triggerWebhooks($event);
$this->removePeriodStatistics($event);
if ($event->runRecalculations) {
$this->updateRunningBalance($event);
}
}
private function removePeriodStatistics(UpdatedTransactionGroup $event): void
{
/** @var PeriodStatisticRepositoryInterface $repository */
$repository = app(PeriodStatisticRepositoryInterface::class);
/** @var TransactionJournal $journal */
foreach ($event->transactionGroup->transactionJournals as $journal) {
$source = $journal->transactions()->where('amount', '<', '0')->first();
$dest = $journal->transactions()->where('amount', '>', '0')->first();
$repository->deleteStatisticsForModel($source->account, $journal->date);
$repository->deleteStatisticsForModel($dest->account, $journal->date);
}
}
/**
@@ -163,7 +180,7 @@ class UpdatedGroupEventHandler
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection([$group]));
$engine->setObjects(new Collection()->push($group));
$engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION);
$engine->generateMessages();

View File

@@ -44,7 +44,6 @@ use FireflyIII\Models\UserRole;
use FireflyIII\Notifications\Admin\UserRegistration as AdminRegistrationNotification;
use FireflyIII\Notifications\Security\UserFailedLoginAttempt;
use FireflyIII\Notifications\Test\UserTestNotificationEmail;
use FireflyIII\Notifications\Test\UserTestNotificationNtfy;
use FireflyIII\Notifications\Test\UserTestNotificationPushover;
use FireflyIII\Notifications\Test\UserTestNotificationSlack;
use FireflyIII\Notifications\User\UserLogin;
@@ -129,7 +128,7 @@ class UserEventHandler
$groupTitle = $user->email;
$index = 1;
/** @var UserGroup $group */
/** @var null|UserGroup $group */
$group = null;
// create a new group.
@@ -411,10 +410,10 @@ class UserEventHandler
break;
case 'ntfy':
$class = UserTestNotificationNtfy::class;
break;
// case 'ntfy':
// $class = UserTestNotificationNtfy::class;
//
// break;
case 'pushover':
$class = UserTestNotificationPushover::class;

View File

@@ -31,6 +31,7 @@ use FireflyIII\Models\BudgetLimit;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait;
use FireflyIII\Support\Singleton\PreferencesSingleton;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
@@ -44,17 +45,24 @@ class BudgetLimitObserver
$this->updatePrimaryCurrencyAmount($budgetLimit);
$this->updateAvailableBudget($budgetLimit);
$user = $budgetLimit->budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budgetLimit));
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
$engine->generateMessages();
// this is a lame trick to communicate with the observer.
$singleton = PreferencesSingleton::getInstance();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
if (true === $singleton->getPreference('fire_webhooks_bl_store')) {
$user = $budgetLimit->budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budgetLimit));
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
}
}
private function updatePrimaryCurrencyAmount(BudgetLimit $budgetLimit): void
@@ -82,16 +90,21 @@ class BudgetLimitObserver
$this->updatePrimaryCurrencyAmount($budgetLimit);
$this->updateAvailableBudget($budgetLimit);
$user = $budgetLimit->budget->user;
// this is a lame trick to communicate with the observer.
$singleton = PreferencesSingleton::getInstance();
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budgetLimit));
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
$engine->generateMessages();
if (true === $singleton->getPreference('fire_webhooks_bl_update')) {
$user = $budgetLimit->budget->user;
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budgetLimit));
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
}
}
}

View File

@@ -31,6 +31,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait;
use FireflyIII\Support\Singleton\PreferencesSingleton;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
@@ -45,32 +46,43 @@ class BudgetObserver
{
Log::debug(sprintf('Observe "created" of budget #%d ("%s").', $budget->id, $budget->name));
// fire event.
$user = $budget->user;
// this is a lame trick to communicate with the observer.
$singleton = PreferencesSingleton::getInstance();
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budget));
$engine->setTrigger(WebhookTrigger::STORE_BUDGET);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
if (true === $singleton->getPreference('fire_webhooks_budget_create')) {
// fire event.
$user = $budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budget));
$engine->setTrigger(WebhookTrigger::STORE_BUDGET);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
}
}
public function updated(Budget $budget): void
{
Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name));
$user = $budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budget));
$engine->setTrigger(WebhookTrigger::UPDATE_BUDGET);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
// this is a lame trick to communicate with the observer.
$singleton = PreferencesSingleton::getInstance();
if (true === $singleton->getPreference('fire_webhooks_budget_update')) {
$user = $budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budget));
$engine->setTrigger(WebhookTrigger::UPDATE_BUDGET);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
}
}
public function deleting(Budget $budget): void

View File

@@ -35,6 +35,8 @@ use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\MessageBag;
use Safe\Exceptions\FileinfoException;
use Safe\Exceptions\FilesystemException;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use function Safe\tmpfile;
@@ -126,9 +128,11 @@ class AttachmentHelper implements AttachmentHelperInterface
public function saveAttachmentFromApi(Attachment $attachment, string $content): bool
{
Log::debug(sprintf('Now in %s', __METHOD__));
$resource = tmpfile();
if (false === $resource) {
Log::error('Cannot create temp-file for file upload.');
try {
$resource = tmpfile();
} catch (FilesystemException $e) {
Log::error(sprintf('Cannot create temp-file for file upload: %s', $e->getMessage()));
return false;
}
@@ -141,17 +145,20 @@ class AttachmentHelper implements AttachmentHelperInterface
$path = stream_get_meta_data($resource)['uri'];
Log::debug(sprintf('Path is %s', $path));
$result = fwrite($resource, $content);
if (false === $result) {
Log::error('Could not write temp file.');
try {
$result = fwrite($resource, $content);
} catch (FilesystemException $e) {
Log::error(sprintf('Could not write to temp file: %s', $e->getMessage()));
return false;
}
Log::debug(sprintf('Wrote %d bytes to temp file.', $result));
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if (false === $finfo) {
Log::error('Could not open finfo.');
fclose($resource);
try {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
} catch (FileinfoException $e) {
Log::error(sprintf('Could not open finfo handler: %s', $e->getMessage()));
return false;
}
@@ -171,7 +178,7 @@ class AttachmentHelper implements AttachmentHelperInterface
$this->uploadDisk->put($file, $content);
// update attachment.
$attachment->md5 = (string) md5_file($path);
$attachment->md5 = md5_file($path);
$attachment->mime = $mime;
$attachment->size = strlen($content);
$attachment->uploaded = true;
@@ -233,7 +240,7 @@ class AttachmentHelper implements AttachmentHelperInterface
$attachment = new Attachment(); // create Attachment object.
$attachment->user()->associate($user);
$attachment->attachable()->associate($model);
$attachment->md5 = (string) md5_file($file->getRealPath());
$attachment->md5 = md5_file($file->getRealPath());
$attachment->filename = $file->getClientOriginalName();
$attachment->mime = $file->getMimeType();
$attachment->size = $file->getSize();

View File

@@ -94,7 +94,7 @@ trait AttachmentCollection
static function (EloquentBuilder $q1): void { // @phpstan-ignore-line
$q1->where('attachments.attachable_type', TransactionJournal::class);
// $q1->where('attachments.uploaded', true);
$q1->whereNull('attachments.deleted_at');
// $q1->whereNull('attachments.deleted_at');
$q1->orWhereNull('attachments.attachable_type');
}
)
@@ -107,6 +107,7 @@ trait AttachmentCollection
$this->fields[] = 'attachments.id as attachment_id';
$this->fields[] = 'attachments.filename as attachment_filename';
$this->fields[] = 'attachments.title as attachment_title';
$this->fields[] = 'attachments.deleted_at as attachment_deleted_at';
$this->fields[] = 'attachments.uploaded as attachment_uploaded';
$this->joinAttachmentTables();

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