Compare commits

...

136 Commits

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


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

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

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

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 03:08:10 +00:00
James Cole
81cd89d66f Final nestor and phpstan fixes. 2025-09-07 17:42:16 +02:00
James Cole
f5c202543c Clean up code. 2025-09-07 17:31:08 +02:00
James Cole
034f437c6b Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-07 17:30:43 +02:00
James Cole
8550ba6138 Remove unused code. 2025-09-07 17:30:34 +02:00
github-actions[bot]
a51501025b Merge pull request #10872 from firefly-iii/release-1757249926
🤖 Automatically merge the PR into the develop branch.
2025-09-07 14:58:53 +02:00
JC5
a9d26e4586 🤖 Auto commit for release 'develop' on 2025-09-07 2025-09-07 14:58:46 +02:00
James Cole
949691935f Catch exceptions. 2025-09-07 14:54:44 +02:00
James Cole
4835b05304 Optimize currency search. 2025-09-07 14:49:49 +02:00
James Cole
cce5a73dd2 Clean up and fix phpstan issues. 2025-09-07 14:28:58 +02:00
github-actions[bot]
3152f635dd Merge pull request #10868 from firefly-iii/release-1757235851
🤖 Automatically merge the PR into the develop branch.
2025-09-07 11:04:21 +02:00
JC5
12e8651017 🤖 Auto commit for release 'develop' on 2025-09-07 2025-09-07 11:04:11 +02:00
James Cole
f88286c848 Merge branches 'develop' and 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-07 10:59:12 +02:00
James Cole
39cf0533d9 Fix various phpstan issues. 2025-09-07 10:59:07 +02:00
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
354 changed files with 8642 additions and 10132 deletions

View File

@@ -402,16 +402,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.87.1",
"version": "v3.88.2",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "2f5170365e2a422d0c5421f9c8818b2c078105f6"
"reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2f5170365e2a422d0c5421f9c8818b2c078105f6",
"reference": "2f5170365e2a422d0c5421f9c8818b2c078105f6",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99",
"reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99",
"shasum": ""
},
"require": {
@@ -438,12 +438,13 @@
"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.7",
"infection/infection": "^0.29.14",
"infection/infection": "^0.31.0",
"justinrainbow/json-schema": "^6.5",
"keradus/cli-executor": "^2.2",
"mikey179/vfsstream": "^1.6.12",
@@ -451,7 +452,6 @@
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
"symfony/polyfill-php84": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2",
"symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2"
},
@@ -494,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.87.1"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2"
},
"funding": [
{
@@ -502,7 +502,7 @@
"type": "github"
}
],
"time": "2025-09-02T15:27:36+00:00"
"time": "2025-09-27T00:24:15+00:00"
},
{
"name": "psr/container",
@@ -2283,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

@@ -7,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

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

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;
@@ -114,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];
@@ -133,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';
@@ -143,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']);
@@ -208,7 +208,7 @@ class BudgetController extends Controller
// */
// private function noBudgetLimits(Budget $budget, Carbon $start, Carbon $end): array
// {
// $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget]));
// $spent = $this->opsRepository->listExpenses($start, $end, null, new Collection()->push($budget));
//
// return $this->processExpenses($budget->id, $spent, $start, $end);
// }
@@ -231,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'],
@@ -250,7 +250,9 @@ 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);
}
}
@@ -288,7 +290,7 @@ class BudgetController extends Controller
// 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]));
// $spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection()->push($budget));
// $limitCurrencyId = $limit->transaction_currency_id;
//
// /** @var array $entry */

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;
@@ -157,7 +156,6 @@ 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);
}
@@ -234,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

@@ -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

@@ -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

@@ -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

@@ -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

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

View File

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

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

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

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

@@ -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.')) {

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

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Account;
use Illuminate\Validation\Validator;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Rules\IsValidSortInstruction;
@@ -30,7 +31,6 @@ use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\User;
use Illuminate\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
class ShowRequest extends FormRequest

View File

@@ -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

@@ -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,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit;
use FireflyIII\Rules\IsBoolean;
use Illuminate\Validation\Validator;
use Carbon\Carbon;
use FireflyIII\Rules\IsValidPositiveAmount;
@@ -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

@@ -58,8 +58,8 @@ class StoreByCurrenciesRequest extends FormRequest
$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\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

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
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

@@ -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

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

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

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

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

@@ -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

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

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

@@ -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

@@ -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

@@ -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;

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,26 @@ 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);
foreach ($journal->categories as $category) {
$repository->deleteStatisticsForModel($category, $journal->date);
}
foreach ($journal->tags as $tag) {
$repository->deleteStatisticsForModel($tag, $journal->date);
}
}
}
/**
* This method processes all webhooks that respond to the "stored transaction group" trigger (100)
*/
@@ -116,7 +140,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,35 @@ class UpdatedGroupEventHandler
$this->processRules($event);
$this->recalculateCredit($event);
$this->triggerWebhooks($event);
$this->removePeriodStatistics($event);
if ($event->runRecalculations) {
$this->updateRunningBalance($event);
}
}
/**
* TODO duplicate
*/
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);
foreach ($journal->categories as $category) {
$repository->deleteStatisticsForModel($category, $journal->date);
}
foreach ($journal->tags as $tag) {
$repository->deleteStatisticsForModel($tag, $journal->date);
}
}
}
/**
@@ -163,7 +189,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.

View File

@@ -62,5 +62,8 @@ class WebhookEventHandler
Log::debug(sprintf('Skip message #%d', $message->id));
}
}
// clean up sent messages table:
WebhookMessage::where('webhook_messages.sent', true)->where('webhook_messages.created_at', '<', now()->subDays(30))->delete();
}
}

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

@@ -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();

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Collector\Extensions;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
@@ -597,16 +598,16 @@ trait MetaCollection
$foundTagCount = 0;
foreach ($object['transactions'] as $transaction) {
$transactionTagCount = count($transaction['tags']);
app('log')->debug(sprintf('Transaction #%d has %d tag(s)', $transaction['transaction_journal_id'], $transactionTagCount));
Log::debug(sprintf('Transaction #%d has %d tag(s)', $transaction['transaction_journal_id'], $transactionTagCount));
if ($transactionTagCount < $expectedTagCount) {
app('log')->debug(sprintf('Transaction has %d tag(s), we expect %d tag(s), return false.', $transactionTagCount, $expectedTagCount));
Log::debug(sprintf('Transaction has %d tag(s), we expect %d tag(s), return false.', $transactionTagCount, $expectedTagCount));
return false;
}
foreach ($transaction['tags'] as $tag) {
Log::debug(sprintf('"%s" versus', strtolower((string) $tag['name'])), $list);
if (in_array(strtolower((string) $tag['name']), $list, true)) {
app('log')->debug(sprintf('Transaction has tag "%s" so count++.', $tag['name']));
Log::debug(sprintf('Transaction has tag "%s" so count++.', $tag['name']));
++$foundTagCount;
$journalId = $transaction['transaction_journal_id'];
// #8377 prevent adding a transaction twice when multiple tag searches find this transaction
@@ -761,7 +762,7 @@ trait MetaCollection
public function setTag(Tag $tag): GroupCollectorInterface
{
$this->withTagInformation();
$this->setTags(new Collection([$tag]));
$this->setTags(new Collection()->push($tag));
return $this;
}
@@ -775,6 +776,9 @@ trait MetaCollection
$this->withTagInformation();
$this->query->whereNotNull('tag_transaction_journal.tag_id');
// Added this while fixing #10898, not sure why a post filter was ever necessary.
$this->query->whereIn('tag_transaction_journal.tag_id', $tags->pluck('id')->toArray());
// this method adds a "postFilter" to the collector.
$list = $tags->pluck('tag')->toArray();
$list = array_map('strtolower', $list);
@@ -784,13 +788,13 @@ trait MetaCollection
foreach ($transaction['tags'] as $tag) {
Log::debug(sprintf('"%s" versus', strtolower((string) $tag['name'])), $list);
if (in_array(strtolower((string) $tag['name']), $list, true)) {
app('log')->debug(sprintf('Transaction has tag "%s" so return true.', $tag['name']));
Log::debug(sprintf('Transaction has tag "%s" so return true.', $tag['name']));
return true;
}
}
}
app('log')->debug('Transaction has no tags from the list, so return false.');
Log::debug('Transaction has no tags from the list, so return false.');
return false;
};
@@ -812,11 +816,11 @@ trait MetaCollection
$filter = static function (array $object) use ($list): bool {
Log::debug(sprintf('Now in setWithoutSpecificTags(%s) filter', implode(', ', $list)));
foreach ($object['transactions'] as $transaction) {
app('log')->debug(sprintf('Transaction has %d tag(s)', count($transaction['tags'])));
Log::debug(sprintf('Transaction has %d tag(s)', count($transaction['tags'])));
foreach ($transaction['tags'] as $tag) {
Log::debug(sprintf('"%s" versus', strtolower((string) $tag['name'])), $list);
if (in_array(strtolower((string) $tag['name']), $list, true)) {
app('log')->debug(sprintf('Transaction has tag "%s", but should not have it, return false.', $tag['name']));
Log::debug(sprintf('Transaction has tag "%s", but should not have it, return false.', $tag['name']));
return false;
}
@@ -919,6 +923,8 @@ trait MetaCollection
{
$this->withCategoryInformation();
$this->query->whereNull('category_transaction_journal.category_id');
// better fix for #10507
$this->query->whereNotIn('transaction_types.type', [TransactionTypeEnum::OPENING_BALANCE->value]);
return $this;
}

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Helpers\Collector;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use Closure;
use Exception;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
@@ -45,7 +46,6 @@ use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Closure;
use Override;
use function Safe\json_decode;
@@ -303,7 +303,7 @@ class GroupCollector implements GroupCollectorInterface
foreach ($params as $param) {
$replace = sprintf('"%s"', $param);
if (is_int($param)) {
$replace = (string) $param;
$replace = (string)$param;
}
$pos = strpos($query, '?');
if (false !== $pos) {
@@ -518,13 +518,13 @@ class GroupCollector implements GroupCollectorInterface
/** @var TransactionJournal $augumentedJournal */
foreach ($collection as $augumentedJournal) {
$groupId = (int) $augumentedJournal->transaction_group_id;
$groupId = (int)$augumentedJournal->transaction_group_id;
if (!array_key_exists($groupId, $groups)) {
// make new array
$parsedGroup = $this->parseAugmentedJournal($augumentedJournal);
$groupArray = [
'id' => (int) $augumentedJournal->transaction_group_id,
'id' => (int)$augumentedJournal->transaction_group_id,
'user_id' => $augumentedJournal->user_id,
'user_group_id' => $augumentedJournal->user_group_id,
// Field transaction_group_title was added by the query.
@@ -537,7 +537,7 @@ class GroupCollector implements GroupCollectorInterface
'transactions' => [],
];
// Field transaction_journal_id was added by the query.
$journalId = (int) $augumentedJournal->transaction_journal_id;
$journalId = (int)$augumentedJournal->transaction_journal_id;
$groupArray['transactions'][$journalId] = $parsedGroup;
$groups[$groupId] = $groupArray;
@@ -545,7 +545,7 @@ class GroupCollector implements GroupCollectorInterface
}
// or parse the rest.
// Field transaction_journal_id was added by the query.
$journalId = (int) $augumentedJournal->transaction_journal_id;
$journalId = (int)$augumentedJournal->transaction_journal_id;
if (array_key_exists($journalId, $groups[$groupId]['transactions'])) {
// append data to existing group + journal (for multiple tags or multiple attachments)
$groups[$groupId]['transactions'][$journalId] = $this->mergeTags($groups[$groupId]['transactions'][$journalId], $augumentedJournal);
@@ -560,7 +560,7 @@ class GroupCollector implements GroupCollectorInterface
}
$groups = $this->parseSums($groups);
return new Collection($groups);
return new Collection()->push(...$groups);
}
/**
@@ -597,8 +597,8 @@ class GroupCollector implements GroupCollectorInterface
$dates = ['interest_date', 'payment_date', 'invoice_date', 'book_date', 'due_date', 'process_date'];
if (array_key_exists('meta_name', $result) && in_array($result['meta_name'], $dates, true)) {
$name = $result['meta_name'];
if (array_key_exists('meta_data', $result) && '' !== (string) $result['meta_data']) {
$result[$name] = Carbon::createFromFormat('!Y-m-d', substr((string) json_decode((string) $result['meta_data']), 0, 10));
if (array_key_exists('meta_data', $result) && '' !== (string)$result['meta_data']) {
$result[$name] = Carbon::createFromFormat('!Y-m-d', substr((string)json_decode((string)$result['meta_data']), 0, 10));
}
}
@@ -611,9 +611,9 @@ class GroupCollector implements GroupCollectorInterface
// convert back to strings because SQLite is dumb like that.
$result = $this->convertToStrings($result);
$result['reconciled'] = 1 === (int) $result['reconciled'];
$result['reconciled'] = 1 === (int)$result['reconciled'];
if (array_key_exists('tag_id', $result) && null !== $result['tag_id']) { // assume the other fields are present as well.
$tagId = (int) $augumentedJournal['tag_id'];
$tagId = (int)$augumentedJournal['tag_id'];
$tagDate = null;
try {
@@ -623,7 +623,7 @@ class GroupCollector implements GroupCollectorInterface
}
$result['tags'][$tagId] = [
'id' => (int) $result['tag_id'],
'id' => (int)$result['tag_id'],
'name' => $result['tag_name'],
'date' => $tagDate,
'description' => $result['tag_description'],
@@ -631,10 +631,11 @@ class GroupCollector implements GroupCollectorInterface
}
// also merge attachments:
if (array_key_exists('attachment_id', $result)) {
$uploaded = 1 === (int) $result['attachment_uploaded'];
$attachmentId = (int) $augumentedJournal['attachment_id'];
if (0 !== $attachmentId && $uploaded) {
if (array_key_exists('attachment_id', $result) && null !== $result['attachment_id']) {
$uploaded = 1 === (int)$result['attachment_uploaded'];
$attachmentId = (int)$augumentedJournal['attachment_id'];
$deleted = null !== $result['attachment_deleted_at'];
if (0 !== $attachmentId && $uploaded && !$deleted) {
$result['attachments'][$attachmentId] = [
'id' => $attachmentId,
'filename' => $augumentedJournal['attachment_filename'],
@@ -659,7 +660,7 @@ class GroupCollector implements GroupCollectorInterface
private function convertToInteger(array $array): array
{
foreach ($this->integerFields as $field) {
$array[$field] = array_key_exists($field, $array) ? (int) $array[$field] : null;
$array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (int)$array[$field] : null;
}
return $array;
@@ -668,7 +669,7 @@ class GroupCollector implements GroupCollectorInterface
private function convertToBoolean(array $array): array
{
foreach ($this->booleanFields as $field) {
$array[$field] = array_key_exists($field, $array) ? (bool) $array[$field] : null;
$array[$field] = array_key_exists($field, $array) ? (bool)$array[$field] : null;
}
return $array;
@@ -677,7 +678,7 @@ class GroupCollector implements GroupCollectorInterface
private function convertToStrings(array $array): array
{
foreach ($this->stringFields as $field) {
$array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string) $array[$field] : null;
$array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string)$array[$field] : null;
}
return $array;
@@ -687,7 +688,7 @@ class GroupCollector implements GroupCollectorInterface
{
$newArray = $newJournal->toArray();
if (array_key_exists('tag_id', $newArray)) { // assume the other fields are present as well.
$tagId = (int) $newJournal['tag_id'];
$tagId = (int)$newJournal['tag_id'];
$tagDate = null;
@@ -698,7 +699,7 @@ class GroupCollector implements GroupCollectorInterface
}
$existingJournal['tags'][$tagId] = [
'id' => (int) $newArray['tag_id'],
'id' => (int)$newArray['tag_id'],
'name' => $newArray['tag_name'],
'date' => $tagDate,
'description' => $newArray['tag_description'],
@@ -712,7 +713,7 @@ class GroupCollector implements GroupCollectorInterface
{
$newArray = $newJournal->toArray();
if (array_key_exists('attachment_id', $newArray)) {
$attachmentId = (int) $newJournal['attachment_id'];
$attachmentId = (int)$newJournal['attachment_id'];
$existingJournal['attachments'][$attachmentId] = [
'id' => $attachmentId,
@@ -731,13 +732,13 @@ class GroupCollector implements GroupCollectorInterface
foreach ($groups as $groudId => $group) {
/** @var array $transaction */
foreach ($group['transactions'] as $transaction) {
$currencyId = (int) $transaction['currency_id'];
$currencyId = (int)$transaction['currency_id'];
if (null === $transaction['amount']) {
throw new FireflyException(sprintf('Amount is NULL for a transaction in group #%d, please investigate.', $groudId));
}
$pcAmount = (string) ('' === $transaction['pc_amount'] ? '0' : $transaction['pc_amount']);
$pcForeignAmount = (string) ('' === $transaction['pc_foreign_amount'] ? '0' : $transaction['pc_foreign_amount']);
$foreignAmount = (string) ('' === $transaction['foreign_amount'] ? '0' : $transaction['foreign_amount']);
$pcAmount = (string)('' === $transaction['pc_amount'] ? '0' : $transaction['pc_amount']);
$pcForeignAmount = (string)('' === $transaction['pc_foreign_amount'] ? '0' : $transaction['pc_foreign_amount']);
$foreignAmount = (string)('' === $transaction['foreign_amount'] ? '0' : $transaction['foreign_amount']);
// set default:
if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) {
@@ -748,11 +749,11 @@ class GroupCollector implements GroupCollectorInterface
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = '0';
}
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string) $groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']);
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd((string) $groups[$groudId]['sums'][$currencyId]['pc_amount'], $pcAmount);
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']);
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['pc_amount'], $pcAmount);
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
$currencyId = (int) $transaction['foreign_currency_id'];
$currencyId = (int)$transaction['foreign_currency_id'];
// set default:
if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) {
@@ -763,7 +764,7 @@ class GroupCollector implements GroupCollectorInterface
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = '0';
}
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string) $groups[$groudId]['sums'][$currencyId]['amount'], $foreignAmount);
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['amount'], $foreignAmount);
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $pcForeignAmount);
}
}
@@ -1095,10 +1096,6 @@ class GroupCollector implements GroupCollectorInterface
->whereNull('transaction_groups.deleted_at')
->whereNull('transaction_journals.deleted_at')
->whereNull('source.deleted_at')
// #10507 ignore opening balance.
->where('transaction_types.type', '!=', TransactionTypeEnum::OPENING_BALANCE->value)
->whereNotNull('transaction_groups.id')
->whereNull('destination.deleted_at')
->orderBy('transaction_journals.date', 'DESC')

View File

@@ -31,7 +31,6 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
@@ -49,7 +48,6 @@ use Illuminate\Support\Facades\Log;
class NetWorth implements NetWorthInterface
{
private AccountRepositoryInterface $accountRepository;
private CurrencyRepositoryInterface $currencyRepos;
private User $user; // @phpstan-ignore-line
private ?UserGroup $userGroup = null;
@@ -131,8 +129,6 @@ class NetWorth implements NetWorthInterface
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->accountRepository->setUserGroup($userGroup);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->currencyRepos->setUserGroup($this->userGroup);
}
#[Deprecated]

View File

@@ -45,7 +45,7 @@ class PopupReport implements PopupReportInterface
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))
$collector->setAccounts(new Collection()->push($account))
->withAccountInformation()
->withBudgetInformation()
->withCategoryInformation()
@@ -72,7 +72,7 @@ class PopupReport implements PopupReportInterface
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setAccounts(new Collection([$account]))
->setAccounts(new Collection()->push($account))
->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
->withAccountInformation()
->withCategoryInformation()
@@ -191,7 +191,7 @@ class PopupReport implements PopupReportInterface
// $set = $attributes['accounts'] ?? new Collection;
// $set->push($account);
$collector->setDestinationAccounts(new Collection([$account]))
$collector->setDestinationAccounts(new Collection()->push($account))
->setRange($attributes['startDate'], $attributes['endDate'])
->withAccountInformation()
->withBudgetInformation()
@@ -218,7 +218,7 @@ class PopupReport implements PopupReportInterface
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setSourceAccounts(new Collection([$account]))
->setSourceAccounts(new Collection()->push($account))
->setDestinationAccounts($attributes['accounts'])
->setRange($attributes['startDate'], $attributes['endDate'])
->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value])

View File

@@ -86,7 +86,7 @@ class CreateController extends Controller
'latitude' => $hasOldInput ? old('location_latitude') : config('firefly.default_location.latitude'),
'longitude' => $hasOldInput ? old('location_longitude') : config('firefly.default_location.longitude'),
'zoom_level' => $hasOldInput ? old('location_zoom_level') : config('firefly.default_location.zoom_level'),
'has_location' => $hasOldInput ? 'true' === old('location_has_location') : false,
'has_location' => $hasOldInput && 'true' === old('location_has_location'),
],
];
$liabilityDirections = [
@@ -106,7 +106,7 @@ class CreateController extends Controller
'preFilled',
[
'currency_id' => $this->primaryCurrency->id,
'include_net_worth' => $hasOldInput ? (bool) $request->old('include_net_worth') : true,
'include_net_worth' => !$hasOldInput || (bool)$request->old('include_net_worth'),
]
);
// issue #8321

View File

@@ -102,7 +102,7 @@ class ShowController extends Controller
// make sure dates are end of day and start of day:
$start->startOfDay();
$end->endOfDay();
$end->endOfDay()->milli(0);
$location = $this->repository->getLocation($account);
$attachments = $this->repository->getAttachments($account);
@@ -143,7 +143,7 @@ class ShowController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setAccounts(new Collection([$account]))
->setAccounts(new Collection()->push($account))
->setLimit($pageSize)
->setPage($page)
->withAttachmentInformation()
@@ -221,7 +221,7 @@ class ShowController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page)->withAccountInformation()->withCategoryInformation();
$collector->setAccounts(new Collection()->push($account))->setLimit($pageSize)->setPage($page)->withAccountInformation()->withCategoryInformation();
// this search will not include transaction groups where this asset account (or liability)
// is just part of ONE of the journals. To force this:

View File

@@ -122,6 +122,11 @@ class NotificationController extends Controller
public function testNotification(Request $request): RedirectResponse
{
if (true === auth()->user()->hasRole('demo')) {
session()->flash('error', (string) trans('firefly.not_available_demo_user'));
return redirect(route('settings.notification.index'));
}
$all = $request->all();
$channel = $all['test_submit'] ?? '';

View File

@@ -27,6 +27,7 @@ use Carbon\Carbon;
use FireflyIII\Helpers\Update\UpdateTrait;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -66,8 +67,8 @@ class UpdateController extends Controller
{
$subTitle = (string) trans('firefly.update_check_title');
$subTitleIcon = 'fa-star';
$permission = app('fireflyconfig')->get('permission_update_check', -1);
$channel = app('fireflyconfig')->get('update_channel', 'stable');
$permission = FireflyConfig::get('permission_update_check', -1);
$channel = FireflyConfig::get('update_channel', 'stable');
$selected = $permission->data;
$channelSelected = $channel->data;
$options = [
@@ -96,9 +97,9 @@ class UpdateController extends Controller
$channel = $request->get('update_channel');
$channel = in_array($channel, ['stable', 'beta', 'alpha'], true) ? $channel : 'stable';
app('fireflyconfig')->set('permission_update_check', $checkForUpdates);
app('fireflyconfig')->set('last_update_check', Carbon::now()->getTimestamp());
app('fireflyconfig')->set('update_channel', $channel);
FireflyConfig::set('permission_update_check', $checkForUpdates);
FireflyConfig::set('last_update_check', Carbon::now()->getTimestamp());
FireflyConfig::set('update_channel', $channel);
session()->flash('success', (string) trans('firefly.configuration_updated'));
return redirect(route('settings.update-check'));

View File

@@ -203,7 +203,7 @@ class BudgetLimitController extends Controller
if ($request->expectsJson()) {
$array = $limit->toArray();
// add some extra metadata:
$spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $currency);
$spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency);
$array['spent'] = $spentArr[$currency->id]['sum'] ?? '0';
$array['left_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, bcadd($array['spent'], (string) $array['amount']));
$array['amount_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $limit['amount']);
@@ -264,7 +264,7 @@ class BudgetLimitController extends Controller
$limit->start_date,
$limit->end_date,
null,
new Collection([$budgetLimit->budget]),
new Collection()->push($budgetLimit->budget),
$budgetLimit->transactionCurrency
);
$daysLeft = $this->activeDaysLeft($limit->start_date, $limit->end_date);

View File

@@ -235,7 +235,7 @@ class IndexController extends Controller
/** @var TransactionCurrency $currency */
foreach ($currencies as $currency) {
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$current]), $currency, false);
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($current), $currency, false);
if (array_key_exists($currency->id, $spentArr) && array_key_exists('sum', $spentArr[$currency->id])) {
$array['spent'][$currency->id]['spent'] = $spentArr[$currency->id]['sum'];
$array['spent'][$currency->id]['currency_id'] = $currency->id;

View File

@@ -235,7 +235,7 @@ class AccountController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))
$collector->setAccounts(new Collection()->push($account))
->setRange($start, $end)
->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
;
@@ -322,7 +322,7 @@ class AccountController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$collector->setAccounts(new Collection()->push($account))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$journals = $collector->getExtractedJournals();
$result = [];
$chartData = [];
@@ -429,7 +429,7 @@ class AccountController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$collector->setAccounts(new Collection()->push($account))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$journals = $collector->getExtractedJournals();
$result = [];
$chartData = [];

View File

@@ -100,7 +100,7 @@ class BudgetController extends Controller
return response()->json($cache->get());
}
$step = $this->calculateStep($start, $end); // depending on diff, do something with range of chart.
$collection = new Collection([$budget]);
$collection = new Collection()->push($budget);
$chartData = [];
$loopStart = clone $start;
$loopStart = Navigation::startOfPeriod($loopStart, $step);
@@ -169,7 +169,7 @@ class BudgetController extends Controller
$locale = app('steam')->getLocale();
$entries = [];
$amount = $budgetLimit->amount ?? '0';
$budgetCollection = new Collection([$budget]);
$budgetCollection = new Collection()->push($budget);
$currency = $budgetLimit->transactionCurrency;
if ($this->convertToPrimary) {
$amount = $budgetLimit->native_amount ?? $amount;
@@ -535,7 +535,7 @@ class BudgetController extends Controller
}
// get spent amount in this period for this currency.
$sum = $this->opsRepository->sumExpenses($currentStart, $currentEnd, $accounts, new Collection([$budget]), $currency);
$sum = $this->opsRepository->sumExpenses($currentStart, $currentEnd, $accounts, new Collection()->push($budget), $currency);
$amount = app('steam')->positive($sum[$currency->id]['sum'] ?? '0');
$chartData[0]['entries'][$title] = app('steam')->bcround($amount, $currency->decimal_places);

View File

@@ -163,7 +163,7 @@ class BudgetReportController extends Controller
public function mainChart(Collection $accounts, Budget $budget, Carbon $start, Carbon $end): JsonResponse
{
$chartData = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$budget]));
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection()->push($budget));
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
// loop expenses.

View File

@@ -174,7 +174,7 @@ class CategoryController extends Controller
$opsRepository = app(OperationsRepositoryInterface::class);
$categoryId = $category->id;
// this gives us all currencies
$collection = new Collection([$category]);
$collection = new Collection()->push($category);
$expenses = $opsRepository->listExpenses($start, $end, $accounts, $collection);
$income = $opsRepository->listIncome($start, $end, $accounts, $collection);
}

View File

@@ -208,8 +208,8 @@ class CategoryReportController extends Controller
public function mainChart(Collection $accounts, Category $category, Carbon $start, Carbon $end): JsonResponse
{
$chartData = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$category]));
$earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$category]));
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection()->push($category));
$earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection()->push($category));
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
// loop expenses.
foreach ($spent as $currency) {

View File

@@ -150,7 +150,7 @@ class DoubleReportController extends Controller
{
$chartData = [];
$opposing = $this->repository->expandWithDoubles(new Collection([$account]));
$opposing = $this->repository->expandWithDoubles(new Collection()->push($account));
$accounts = $accounts->merge($opposing);
$spent = $this->opsRepository->listExpenses($start, $end, $accounts);
$earned = $this->opsRepository->listIncome($start, $end, $accounts);

View File

@@ -212,8 +212,8 @@ class TagReportController extends Controller
public function mainChart(Collection $accounts, Tag $tag, Carbon $start, Carbon $end): JsonResponse
{
$chartData = [];
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$tag]));
$earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$tag]));
$spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection()->push($tag));
$earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection()->push($tag));
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
// loop expenses.

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Http\Controllers;
use FireflyIII\Events\RequestedSendWebhookMessages;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Controllers\RequestInformation;
use FireflyIII\Support\Http\Controllers\UserNavigation;
@@ -133,7 +134,7 @@ abstract class Controller extends BaseController
$this->primaryCurrency = Amount::getPrimaryCurrency();
$language = Steam::getLanguage();
$locale = Steam::getLocale();
$darkMode = app('preferences')->get('darkMode', 'browser')->data;
$darkMode = Preferences::get('darkMode', 'browser')->data;
$this->convertToPrimary = Amount::convertToPrimary();
$page = $this->getPageName();
$shownDemo = $this->hasSeenDemo();

View File

@@ -30,6 +30,7 @@ use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\PeriodStatistic;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
@@ -108,6 +109,8 @@ class DebugController extends Controller
Artisan::call('route:clear');
Artisan::call('view:clear');
PeriodStatistic::where('id', '>', 0)->delete();
// also do some recalculations.
Artisan::call('correction:recalculates-liabilities');
AccountBalanceCalculator::recalculateAll(false);

View File

@@ -170,7 +170,7 @@ class HomeController extends Controller
foreach ($accounts as $account) {
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->withAccountInformation()->setRange($start, $end)->setLimit(10)->setPage(1);
$collector->setAccounts(new Collection()->push($account))->withAccountInformation()->setRange($start, $end)->setLimit(10)->setPage(1);
$set = $collector->getExtractedJournals();
$transactions[] = ['transactions' => $set, 'account' => $account];
}

View File

@@ -171,7 +171,7 @@ class BoxController extends Controller
$filtered = $allAccounts->filter(
static function (Account $account) use ($accountRepository) {
$includeNetWorth = $accountRepository->getMetaValue($account, 'include_net_worth');
$result = null === $includeNetWorth ? true : '1' === $includeNetWorth;
$result = null === $includeNetWorth || '1' === $includeNetWorth;
if (false === $result) {
app('log')->debug(sprintf('Will not include "%s" in net worth charts.', $account->name));
}

View File

@@ -217,7 +217,7 @@ class ReconcileController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))
$collector->setAccounts(new Collection()->push($account))
->setRange($selectionStart, $selectionEnd)
->withBudgetInformation()->withCategoryInformation()->withAccountInformation()
;

View File

@@ -412,7 +412,7 @@ class ProfileController extends Controller
// found user.which email address to return to?
$set = app('preferences')->beginsWith($user, 'previous_email_');
/** @var string $match */
/** @var null|string $match */
$match = null;
foreach ($set as $entry) {
$hashed = hash('sha256', sprintf('%s%s', (string) config('app.key'), $entry->data));

View File

@@ -74,7 +74,7 @@ class TriggerController extends Controller
/** @var CreateRecurringTransactions $job */
$job = app(CreateRecurringTransactions::class);
$job->setRecurrences(new Collection([$recurrence]));
$job->setRecurrences(new Collection()->push($recurrence));
$job->setDate($date);
$job->setForce(false);
$job->handle();

View File

@@ -291,7 +291,7 @@ class BudgetController extends Controller
$cache->addProperty('budget-period-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
// return $cache->get();
return $cache->get();
}
$periods = Navigation::listOfPeriods($start, $end);

View File

@@ -152,7 +152,7 @@ class SelectController extends Controller
$newRuleEngine = app(RuleEngineInterface::class);
// set rules:
$newRuleEngine->setRules(new Collection([$rule]));
$newRuleEngine->setRules(new Collection()->push($rule));
$newRuleEngine->setRefreshTriggers(false);
$collection = $newRuleEngine->find();
$collection = $collection->slice(0, 20);
@@ -196,7 +196,7 @@ class SelectController extends Controller
$newRuleEngine = app(RuleEngineInterface::class);
// set rules:
$newRuleEngine->setRules(new Collection([$rule]));
$newRuleEngine->setRules(new Collection()->push($rule));
$collection = $newRuleEngine->find();
$collection = $collection->slice(0, 20);

View File

@@ -78,7 +78,7 @@ class ExecutionController extends Controller
// set rules:
// #10427, file rule group and not the set of rules.
$collection = new Collection([$ruleGroup]);
$collection = new Collection()->push($ruleGroup);
$newRuleEngine->setRuleGroups($collection);
$newRuleEngine->fire();

View File

@@ -250,10 +250,7 @@ class TagController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation()
->setTag($tag)->withBudgetInformation()->withCategoryInformation()
->withAttachmentInformation()
;
$collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation()->setTag($tag)->withBudgetInformation()->withCategoryInformation()->withAttachmentInformation();
$groups = $collector->getPaginatedGroups();
$groups->setPath($path);
$sums = $this->repository->sumsOfTag($tag, $start, $end);

View File

@@ -117,7 +117,7 @@ class BulkController extends Controller
// run rules on changed journals:
/** @var TransactionJournal $journal */
foreach ($collection as $journal) {
foreach ($collection as $journal) { // @phpstan-ignore-line
event(new UpdatedTransactionGroup($journal->transactionGroup, true, true, false));
}

View File

@@ -31,6 +31,7 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use FireflyIII\Services\Internal\Update\GroupCloneService;
use FireflyIII\Support\Facades\Preferences;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
@@ -76,7 +77,7 @@ class CreateController extends Controller
// event!
event(new StoredTransactionGroup($newGroup, true, true));
app('preferences')->mark();
Preferences::mark();
$title = $newGroup->title ?? $newGroup->transactionJournals->first()->description;
$link = route('transactions.show', [$newGroup->id]);
@@ -103,7 +104,7 @@ class CreateController extends Controller
* */
public function create(?string $objectType)
{
app('preferences')->mark();
Preferences::mark();
$sourceId = (int) request()->get('source');
$destinationId = (int) request()->get('destination');
@@ -114,7 +115,9 @@ class CreateController extends Controller
$preFilled = session()->has('preFilled') ? session('preFilled') : [];
$subTitle = (string) trans(sprintf('breadcrumbs.create_%s', strtolower((string) $objectType)));
$subTitleIcon = 'fa-plus';
$optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
/** @var null|array $optionalFields */
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
$allowedOpposingTypes = config('firefly.allowed_opposing_types');
$accountToTypes = config('firefly.account_to_transaction');
$previousUrl = $this->rememberPreviousUrl('transactions.create.url');

View File

@@ -146,25 +146,7 @@ class ShowController extends Controller
$attachments = $this->repository->getAttachments($transactionGroup);
$links = $this->repository->getLinks($transactionGroup);
return view(
'transactions.show',
compact(
'transactionGroup',
'amounts',
'first',
'type',
'logEntries',
'groupLogEntries',
'subTitle',
'splits',
'selectedGroup',
'groupArray',
'events',
'attachments',
'links',
'accounts',
)
);
return view('transactions.show', compact('transactionGroup', 'amounts', 'first', 'type', 'logEntries', 'groupLogEntries', 'subTitle', 'splits', 'selectedGroup', 'groupArray', 'events', 'attachments', 'links', 'accounts'));
}
private function getAmounts(array $group): array

View File

@@ -25,14 +25,15 @@ declare(strict_types=1);
namespace FireflyIII\Http\Middleware;
use Closure;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\GroupMembership;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\UserGroup;
use FireflyIII\Models\Webhook;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User;
use Illuminate\Http\Request;
@@ -103,7 +104,7 @@ class InterestingMessage
// send message about newly created transaction group.
/** @var null|TransactionGroup $group */
$group = auth()->user()->transactionGroups()->with(['transactionJournals', 'transactionJournals.transactionType'])->find((int) $transactionGroupId);
$group = auth()->user()->transactionGroups()->with(['transactionJournals', 'transactionJournals.transactionType'])->find((int)$transactionGroupId);
if (null === $group) {
return;
@@ -119,17 +120,17 @@ class InterestingMessage
$title = $count > 1 ? $group->title : $journal->description;
if ('created' === $message) {
session()->flash('success_url', route('transactions.show', [$transactionGroupId]));
session()->flash('success', (string) trans('firefly.stored_journal', ['description' => $title]));
session()->flash('success', (string)trans('firefly.stored_journal', ['description' => $title]));
}
if ('updated' === $message) {
$type = strtolower((string) $journal->transactionType->type);
$type = strtolower((string)$journal->transactionType->type);
session()->flash('success_url', route('transactions.show', [$transactionGroupId]));
session()->flash('success', (string) trans(sprintf('firefly.updated_%s', $type), ['description' => $title]));
session()->flash('success', (string)trans(sprintf('firefly.updated_%s', $type), ['description' => $title]));
}
if ('no_change' === $message) {
$type = strtolower((string) $journal->transactionType->type);
$type = strtolower((string)$journal->transactionType->type);
session()->flash('warning_url', route('transactions.show', [$transactionGroupId]));
session()->flash('warning', (string) trans(sprintf('firefly.no_changes_%s', $type), ['description' => $title]));
session()->flash('warning', (string)trans(sprintf('firefly.no_changes_%s', $type), ['description' => $title]));
}
}
@@ -170,13 +171,13 @@ class InterestingMessage
if ('deleted' === $message) {
session()->flash('success', (string) trans('firefly.flash_administration_deleted', ['title' => $userGroup->title]));
session()->flash('success', (string)trans('firefly.flash_administration_deleted', ['title' => $userGroup->title]));
}
if ('created' === $message) {
session()->flash('success', (string) trans('firefly.flash_administration_created', ['title' => $userGroup->title]));
session()->flash('success', (string)trans('firefly.flash_administration_created', ['title' => $userGroup->title]));
}
if ('updated' === $message) {
session()->flash('success', (string) trans('firefly.flash_administration_updated', ['title' => $userGroup->title]));
session()->flash('success', (string)trans('firefly.flash_administration_updated', ['title' => $userGroup->title]));
}
}
@@ -205,13 +206,13 @@ class InterestingMessage
return;
}
if ('deleted' === $message) {
session()->flash('success', (string) trans('firefly.account_deleted', ['name' => $account->name]));
session()->flash('success', (string)trans('firefly.account_deleted', ['name' => $account->name]));
}
if ('created' === $message) {
session()->flash('success', (string) trans('firefly.stored_new_account', ['name' => $account->name]));
session()->flash('success', (string)trans('firefly.stored_new_account', ['name' => $account->name]));
}
if ('updated' === $message) {
session()->flash('success', (string) trans('firefly.updated_account', ['name' => $account->name]));
session()->flash('success', (string)trans('firefly.updated_account', ['name' => $account->name]));
}
}
@@ -237,10 +238,10 @@ class InterestingMessage
return;
}
if ('deleted' === $message) {
session()->flash('success', (string) trans('firefly.deleted_bill', ['name' => $bill->name]));
session()->flash('success', (string)trans('firefly.deleted_bill', ['name' => $bill->name]));
}
if ('created' === $message) {
session()->flash('success', (string) trans('firefly.stored_new_bill', ['name' => $bill->name]));
session()->flash('success', (string)trans('firefly.stored_new_bill', ['name' => $bill->name]));
}
}
@@ -266,13 +267,13 @@ class InterestingMessage
return;
}
if ('deleted' === $message) {
session()->flash('success', (string) trans('firefly.deleted_webhook', ['title' => $webhook->title]));
session()->flash('success', (string)trans('firefly.deleted_webhook', ['title' => $webhook->title]));
}
if ('updated' === $message) {
session()->flash('success', (string) trans('firefly.updated_webhook', ['title' => $webhook->title]));
session()->flash('success', (string)trans('firefly.updated_webhook', ['title' => $webhook->title]));
}
if ('created' === $message) {
session()->flash('success', (string) trans('firefly.stored_new_webhook', ['title' => $webhook->title]));
session()->flash('success', (string)trans('firefly.stored_new_webhook', ['title' => $webhook->title]));
}
}
@@ -289,32 +290,32 @@ class InterestingMessage
{
// params:
// get parameters from request.
$code = $request->get('code');
$message = $request->get('message');
$code = (string) $request->get('code');
$message = (string) $request->get('message');
/** @var null|TransactionCurrency $currency */
$currency = TransactionCurrency::whereCode($code)->first();
if (null === $currency) {
try {
$currency = Amount::getTransactionCurrencyByCode($code);
} catch (FireflyException) {
return;
}
if ('enabled' === $message) {
session()->flash('success', (string) trans('firefly.currency_is_now_enabled', ['name' => $currency->name]));
session()->flash('success', (string)trans('firefly.currency_is_now_enabled', ['name' => $currency->name]));
}
if ('enable_failed' === $message) {
session()->flash('error', (string) trans('firefly.could_not_enable_currency', ['name' => $currency->name]));
session()->flash('error', (string)trans('firefly.could_not_enable_currency', ['name' => $currency->name]));
}
if ('disabled' === $message) {
session()->flash('success', (string) trans('firefly.currency_is_now_disabled', ['name' => $currency->name]));
session()->flash('success', (string)trans('firefly.currency_is_now_disabled', ['name' => $currency->name]));
}
if ('disable_failed' === $message) {
session()->flash('error', (string) trans('firefly.could_not_disable_currency', ['name' => $currency->name]));
session()->flash('error', (string)trans('firefly.could_not_disable_currency', ['name' => $currency->name]));
}
if ('default' === $message) {
session()->flash('success', (string) trans('firefly.new_default_currency', ['name' => $currency->name]));
session()->flash('success', (string)trans('firefly.new_default_currency', ['name' => $currency->name]));
}
if ('default_failed' === $message) {
session()->flash('error', (string) trans('firefly.default_currency_failed', ['name' => $currency->name]));
session()->flash('error', (string)trans('firefly.default_currency_failed', ['name' => $currency->name]));
}
}
}

View File

@@ -23,6 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use Illuminate\Validation\Validator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsValidPositiveAmount;
@@ -31,7 +33,6 @@ 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 PiggyBankStoreRequest.
@@ -60,7 +61,7 @@ class PiggyBankStoreRequest extends FormRequest
$accounts = [];
}
foreach ($accounts as $item) {
$data['accounts'][] = ['account_id' => (int) $item];
$data['accounts'][] = ['account_id' => (int)$item];
}
return $data;
@@ -97,7 +98,7 @@ class PiggyBankStoreRequest extends FormRequest
$repository = app(AccountRepositoryInterface::class);
$types = config('firefly.piggy_bank_account_types');
foreach ($data['accounts'] as $value) {
$accountId = (int) $value;
$accountId = (int)$value;
$account = $repository->find($accountId);
if (null !== $account) {
// check currency here.
@@ -123,9 +124,11 @@ class PiggyBankStoreRequest extends FormRequest
private function getCurrencyFromData(array $data): TransactionCurrency
{
$currencyId = (int) ($data['transaction_currency_id'] ?? 0);
$currency = TransactionCurrency::find($currencyId);
if (null === $currency) {
$currencyId = (int)($data['transaction_currency_id'] ?? 0);
try {
$currency = Amount::getTransactionCurrencyById($currencyId);
} catch (FireflyException) {
return Amount::getPrimaryCurrency();
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use Illuminate\Validation\Validator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -129,8 +130,10 @@ class PiggyBankUpdateRequest extends FormRequest
private function getCurrencyFromData(array $data): TransactionCurrency
{
$currencyId = (int) ($data['transaction_currency_id'] ?? 0);
$currency = TransactionCurrency::find($currencyId);
if (null === $currency) {
try {
$currency = Amount::getTransactionCurrencyById($currencyId);
} catch (FireflyException) {
return Amount::getPrimaryCurrency();
}

View File

@@ -138,7 +138,7 @@ class RecurrenceFormRequest extends FormRequest
* @var int $index
* @var array $transaction
*/
foreach ($return['transactions'] as $index => $transaction) {
foreach ($return['transactions'] as $index => $transaction) { // @phpstan-ignore-line
$categoryName = $transaction['category_name'] ?? null;
if (null !== $categoryName) {
$category = $factory->findOrCreate(null, $categoryName);

View File

@@ -269,7 +269,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
// if has one, calculate expenses and use that as a base.
$repository = app(OperationsRepositoryInterface::class);
$repository->setUser($autoBudget->budget->user);
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency);
$currencyId = $autoBudget->transaction_currency_id;
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
@@ -329,7 +329,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
// if has one, calculate expenses and use that as a base.
$repository = app(OperationsRepositoryInterface::class);
$repository->setUser($autoBudget->budget->user);
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection()->push($autoBudget->budget), $autoBudget->transactionCurrency);
$currencyId = $autoBudget->transaction_currency_id;
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));

View File

@@ -39,6 +39,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use function Safe\json_decode;
@@ -74,7 +75,7 @@ class DownloadExchangeRates implements ShouldQueue
$newDate = clone $date;
$newDate->startOfDay();
$this->date = $newDate;
app('log')->debug(sprintf('Created new DownloadExchangeRates("%s")', $this->date->format('Y-m-d')));
Log::debug(sprintf('Created new DownloadExchangeRates("%s")', $this->date->format('Y-m-d')));
}
}
@@ -83,7 +84,7 @@ class DownloadExchangeRates implements ShouldQueue
*/
public function handle(): void
{
app('log')->debug('Now in handle()');
Log::debug('Now in handle()');
$currencies = $this->repository->getCompleteSet();
/** @var TransactionCurrency $currency */
@@ -97,7 +98,7 @@ class DownloadExchangeRates implements ShouldQueue
*/
private function downloadRates(TransactionCurrency $currency): void
{
app('log')->debug(sprintf('Now downloading new exchange rates for currency %s.', $currency->code));
Log::debug(sprintf('Now downloading new exchange rates for currency %s.', $currency->code));
$base = sprintf('%s/%s/%s', (string) config('cer.url'), $this->date->year, $this->date->isoWeek);
$client = new Client();
$url = sprintf('%s/%s.json', $base, $currency->code);
@@ -105,20 +106,20 @@ class DownloadExchangeRates implements ShouldQueue
try {
$res = $client->get($url);
} catch (ConnectException|RequestException $e) {
app('log')->warning(sprintf('Trying to grab "%s" resulted in error "%s".', $url, $e->getMessage()));
Log::warning(sprintf('Trying to grab "%s" resulted in error "%s".', $url, $e->getMessage()));
return;
}
$statusCode = $res->getStatusCode();
if (200 !== $statusCode) {
app('log')->warning(sprintf('Trying to grab "%s" resulted in status code %d.', $url, $statusCode));
Log::warning(sprintf('Trying to grab "%s" resulted in status code %d.', $url, $statusCode));
return;
}
$body = (string) $res->getBody();
$json = json_decode($body, true);
if (false === $json || null === $json) {
app('log')->warning(sprintf('Trying to grab "%s" resulted in bad JSON.', $url));
Log::warning(sprintf('Trying to grab "%s" resulted in bad JSON.', $url));
return;
}
@@ -134,11 +135,11 @@ class DownloadExchangeRates implements ShouldQueue
foreach ($rates as $code => $rate) {
$to = $this->getCurrency($code);
if (!$to instanceof TransactionCurrency) {
app('log')->debug(sprintf('Currency %s is not in use, do not save rate.', $code));
Log::debug(sprintf('Currency %s is not in use, do not save rate.', $code));
continue;
}
app('log')->debug(sprintf('Currency %s is in use.', $code));
Log::debug(sprintf('Currency %s is in use.', $code));
$this->saveRate($currency, $to, $date, $rate);
}
}
@@ -147,25 +148,25 @@ class DownloadExchangeRates implements ShouldQueue
{
// if we have it already, don't bother searching for it again.
if (array_key_exists($code, $this->active)) {
app('log')->debug(sprintf('Already know what the result is of searching for %s', $code));
Log::debug(sprintf('Already know what the result is of searching for %s', $code));
return $this->active[$code];
}
// find it in the database.
$currency = $this->repository->findByCode($code);
if (!$currency instanceof TransactionCurrency) {
app('log')->debug(sprintf('Did not find currency %s.', $code));
Log::debug(sprintf('Did not find currency %s.', $code));
$this->active[$code] = null;
return null;
}
if (false === $currency->enabled) {
app('log')->debug(sprintf('Currency %s is not enabled.', $code));
Log::debug(sprintf('Currency %s is not enabled.', $code));
$this->active[$code] = null;
return null;
}
app('log')->debug(sprintf('Currency %s is enabled.', $code));
Log::debug(sprintf('Currency %s is enabled.', $code));
$this->active[$code] = $currency;
return $currency;
@@ -177,7 +178,7 @@ class DownloadExchangeRates implements ShouldQueue
$this->repository->setUser($user);
$existing = $this->repository->getExchangeRate($from, $to, $date);
if (!$existing instanceof CurrencyExchangeRate) {
app('log')->debug(sprintf('Saved rate from %s to %s for user #%d.', $from->code, $to->code, $user->id));
Log::debug(sprintf('Saved rate from %s to %s for user #%d.', $from->code, $to->code, $user->id));
$this->repository->setExchangeRate($from, $to, $date, $rate);
}
}

View File

@@ -23,11 +23,13 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Attributes\Scope;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Handlers\Observer\AccountObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -40,6 +42,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([AccountObserver::class])]
class Account extends Model
{
use HasFactory;
@@ -60,7 +63,7 @@ class Account extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$accountId = (int) $value;
$accountId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -95,39 +98,6 @@ class Account extends Model
return $this->morphMany(Attachment::class, 'attachable');
}
/**
* Get the account number.
*/
protected function accountNumber(): Attribute
{
return Attribute::make(get: function () {
/** @var null|AccountMeta $metaValue */
$metaValue = $this->accountMeta()
->where('name', 'account_number')
->first()
;
return null !== $metaValue ? $metaValue->data : '';
});
}
public function accountMeta(): HasMany
{
return $this->hasMany(AccountMeta::class);
}
protected function editName(): Attribute
{
return Attribute::make(get: function () {
$name = $this->name;
if (AccountTypeEnum::CASH->value === $this->accountType->type) {
return '';
}
return $name;
});
}
public function locations(): MorphMany
{
return $this->morphMany(Location::class, 'locatable');
@@ -154,19 +124,9 @@ class Account extends Model
return $this->belongsToMany(PiggyBank::class);
}
#[Scope]
protected function accountTypeIn(EloquentBuilder $query, array $types): void
{
if (false === $this->joinedAccountTypes) {
$query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id');
$this->joinedAccountTypes = true;
}
$query->whereIn('account_types.type', $types);
}
public function setVirtualBalanceAttribute(mixed $value): void
{
$value = (string) $value;
$value = (string)$value;
if ('' === $value) {
$value = null;
}
@@ -186,42 +146,49 @@ class Account extends Model
protected function accountId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
/**
* Get the account number.
*/
protected function accountNumber(): Attribute
{
return Attribute::make(get: function () {
/** @var null|AccountMeta $metaValue */
$metaValue = $this->accountMeta()
->where('name', 'account_number')
->first()
;
return null !== $metaValue ? $metaValue->data : '';
});
}
public function accountMeta(): HasMany
{
return $this->hasMany(AccountMeta::class);
}
/**
* Get the user ID
*/
protected function accountTypeId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
protected function iban(): Attribute
#[Scope]
protected function accountTypeIn(EloquentBuilder $query, array $types): void
{
return Attribute::make(
get: static fn ($value) => null === $value ? null : trim(str_replace(' ', '', (string) $value)),
);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
/**
* Get the virtual balance
*/
protected function virtualBalance(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
if (false === $this->joinedAccountTypes) {
$query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id');
$this->joinedAccountTypes = true;
}
$query->whereIn('account_types.type', $types);
}
protected function casts(): array
@@ -238,4 +205,47 @@ class Account extends Model
'native_virtual_balance' => 'string',
];
}
protected function editName(): Attribute
{
return Attribute::make(get: function () {
$name = $this->name;
if (AccountTypeEnum::CASH->value === $this->accountType->type) {
return '';
}
return $name;
});
}
protected function iban(): Attribute
{
return Attribute::make(
get: static fn ($value) => null === $value ? null : trim(str_replace(' ', '', (string)$value)),
);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
/**
* Get the virtual balance
*/
protected function virtualBalance(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string)$value,
);
}
public function primaryPeriodStatistics(): MorphMany
{
return $this->morphMany(PeriodStatistic::class, 'primary_statable');
}
}

View File

@@ -23,8 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -43,11 +43,6 @@ class AccountMeta extends Model
return $this->belongsTo(Account::class);
}
protected function data(): Attribute
{
return Attribute::make(get: fn (mixed $value) => (string) json_decode((string) $value, true), set: fn (mixed $value) => ['data' => json_encode($value)]);
}
protected function casts(): array
{
return [
@@ -55,4 +50,9 @@ class AccountMeta extends Model
'updated_at' => 'datetime',
];
}
protected function data(): Attribute
{
return Attribute::make(get: fn (mixed $value) => (string)json_decode((string)$value, true), set: fn (mixed $value) => ['data' => json_encode($value)]);
}
}

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