Compare commits

..

115 Commits

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

View File

@@ -402,16 +402,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.87.2",
"version": "v3.88.2",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992"
"reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
"reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
"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.2"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2"
},
"funding": [
{
@@ -502,7 +502,7 @@
"type": "github"
}
],
"time": "2025-09-10T09:51:40+00:00"
"time": "2025-09-27T00:24:15+00:00"
},
{
"name": "psr/container",
@@ -1252,16 +1252,16 @@
},
{
"name": "symfony/console",
"version": "v7.3.3",
"version": "v7.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
"reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db",
"reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db",
"shasum": ""
},
"require": {
@@ -1326,7 +1326,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.3.3"
"source": "https://github.com/symfony/console/tree/v7.3.4"
},
"funding": [
{
@@ -1346,7 +1346,7 @@
"type": "tidelift"
}
],
"time": "2025-08-25T06:35:40+00:00"
"time": "2025-09-22T15:31:00+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -2284,17 +2284,97 @@
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/process",
"version": "v7.3.3",
"name": "symfony/polyfill-php84",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
"url": "https://github.com/symfony/polyfill-php84.git",
"reference": "d8ced4d875142b6a7426000426b8abc631d6b191"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
"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.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "f24f8f316367b30810810d4eb30c543d7003ff3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b",
"reference": "f24f8f316367b30810810d4eb30c543d7003ff3b",
"shasum": ""
},
"require": {
@@ -2326,7 +2406,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.3.3"
"source": "https://github.com/symfony/process/tree/v7.3.4"
},
"funding": [
{
@@ -2346,7 +2426,7 @@
"type": "tidelift"
}
],
"time": "2025-08-18T09:42:54+00:00"
"time": "2025-09-11T10:12:26+00:00"
},
{
"name": "symfony/service-contracts",
@@ -2495,16 +2575,16 @@
},
{
"name": "symfony/string",
"version": "v7.3.3",
"version": "v7.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
"reference": "f96476035142921000338bad71e5247fbc138872"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872",
"reference": "f96476035142921000338bad71e5247fbc138872",
"shasum": ""
},
"require": {
@@ -2519,7 +2599,6 @@
},
"require-dev": {
"symfony/emoji": "^7.1",
"symfony/error-handler": "^6.4|^7.0",
"symfony/http-client": "^6.4|^7.0",
"symfony/intl": "^6.4|^7.0",
"symfony/translation-contracts": "^2.5|^3.0",
@@ -2562,7 +2641,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.3.3"
"source": "https://github.com/symfony/string/tree/v7.3.4"
},
"funding": [
{
@@ -2582,7 +2661,7 @@
"type": "tidelift"
}
],
"time": "2025-08-25T06:35:40+00:00"
"time": "2025-09-11T14:36:48+00:00"
}
],
"packages-dev": [],

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

@@ -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 "🤖 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`")
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

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

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

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

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

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

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()->push($group));
// set the webhook to trigger
$engine->setWebhooks(new Collection()->push($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

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

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

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

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

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

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

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

View File

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

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

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

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'));

View File

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

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)');

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,38 @@ 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);
$categories = $journal->categories;
$tags = $journal->tags;
$budgets = $journal->budgets;
foreach ($categories as $category) {
$repository->deleteStatisticsForModel($category, $journal->date);
}
foreach ($tags as $tag) {
$repository->deleteStatisticsForModel($tag, $journal->date);
}
foreach ($budgets as $budget) {
$repository->deleteStatisticsForModel($budget, $journal->date);
}
if (0 === $categories->count()) {
$repository->deleteStatisticsForPrefix($journal->userGroup, 'no_category', $journal->date);
}
if (0 === $budgets->count()) {
$repository->deleteStatisticsForPrefix($journal->userGroup, 'no_budget', $journal->date);
}
}
}
/**
* This method processes all webhooks that respond to the "stored transaction group" trigger (100)
*/

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);
}
}
}
/**

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

@@ -634,7 +634,8 @@ class GroupCollector implements GroupCollectorInterface
if (array_key_exists('attachment_id', $result) && null !== $result['attachment_id']) {
$uploaded = 1 === (int)$result['attachment_uploaded'];
$attachmentId = (int)$augumentedJournal['attachment_id'];
if (0 !== $attachmentId && $uploaded) {
$deleted = null !== $result['attachment_deleted_at'];
if (0 !== $attachmentId && $uploaded && !$deleted) {
$result['attachments'][$attachmentId] = [
'id' => $attachmentId,
'filename' => $augumentedJournal['attachment_filename'],

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

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

@@ -92,7 +92,7 @@ class ShowController extends Controller
// get first journal ever to set off the budget period overview.
$first = $this->journalRepos->firstNull();
$firstDate = $first instanceof TransactionJournal ? $first->date : $start;
$periods = $this->getNoBudgetPeriodOverview($firstDate, $end);
$periods = $this->getNoModelPeriodOverview('budget', $firstDate, $end);
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;

View File

@@ -35,6 +35,7 @@ use FireflyIII\Support\Http\Controllers\PeriodOverview;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@@ -74,7 +75,7 @@ class NoCategoryController extends Controller
*/
public function show(Request $request, ?Carbon $start = null, ?Carbon $end = null)
{
app('log')->debug('Start of noCategory()');
Log::debug('Start of noCategory()');
$start ??= session('start');
$end ??= session('end');
@@ -82,14 +83,12 @@ class NoCategoryController extends Controller
/** @var Carbon $end */
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$subTitle = trans(
'firefly.without_category_between',
['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]
);
$periods = $this->getNoCategoryPeriodOverview($start);
$subTitle = trans('firefly.without_category_between', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]);
$first = $this->journalRepos->firstNull()->date ?? clone $start;
$periods = $this->getNoModelPeriodOverview('category', $first, $end);
app('log')->debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d')));
app('log')->debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d')));
Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d')));
Log::debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d')));
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
@@ -117,13 +116,13 @@ class NoCategoryController extends Controller
$periods = new Collection();
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
app('log')->debug('Start of noCategory()');
Log::debug('Start of noCategory()');
$subTitle = (string) trans('firefly.all_journals_without_category');
$first = $this->journalRepos->firstNull();
$start = $first instanceof TransactionJournal ? $first->date : new Carbon();
$end = today(config('app.timezone'));
app('log')->debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d')));
app('log')->debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d')));
Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d')));
Log::debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d')));
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);

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);
@@ -181,7 +184,6 @@ class DebugController extends Controller
$currentDriver = DB::getDriverName();
return [
'db_version' => app('fireflyconfig')->get('db_version', 1)->data,
'php_version' => PHP_VERSION,
'php_os' => PHP_OS,
'uname' => php_uname('m'),

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\System;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Exception;
@@ -81,10 +82,7 @@ class InstallController extends Controller
{
app('view')->share('FF_VERSION', config('firefly.version'));
// index will set FF3 version.
app('fireflyconfig')->set('ff3_version', (string) config('firefly.version'));
// set new DB version.
app('fireflyconfig')->set('db_version', (int) config('firefly.db_version'));
FireflyConfig::set('ff3_version', (string) config('firefly.version'));
return view('install.index');
}

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

@@ -26,6 +26,7 @@ namespace FireflyIII\Http\Middleware;
use Closure;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\System\IsOldVersion;
use FireflyIII\Support\System\OAuthKeys;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
@@ -37,6 +38,8 @@ use Illuminate\Support\Facades\Log;
*/
class Installer
{
use IsOldVersion;
/**
* Handle an incoming request.
*
@@ -65,7 +68,7 @@ class Installer
// run installer when no tables are present,
// or when old scheme version
// or when old firefly version
if ($this->hasNoTables() || $this->oldDBVersion() || $this->oldVersion()) {
if ($this->hasNoTables() || $this->isOldVersionInstalled()) {
return response()->redirectTo(route('installer.index'));
}
OAuthKeys::verifyKeysRoutine();
@@ -126,59 +129,4 @@ class Installer
{
return false !== stripos($message, 'Base table or view not found');
}
/**
* Check if the "db_version" variable is correct.
*/
private function oldDBVersion(): bool
{
// older version in config than database?
$configVersion = (int) config('firefly.db_version');
$dbVersion = (int) app('fireflyconfig')->getFresh('db_version', 1)->data;
if ($configVersion > $dbVersion) {
Log::warning(
sprintf(
'The current configured version (%d) is older than the required version (%d). Redirect to migrate routine.',
$dbVersion,
$configVersion
)
);
return true;
}
// Log::info(sprintf('Configured DB version (%d) equals expected DB version (%d)', $dbVersion, $configVersion));
return false;
}
/**
* Check if the "firefly_version" variable is correct.
*/
private function oldVersion(): bool
{
// version compare thing.
$configVersion = (string) config('firefly.version');
$dbVersion = (string) app('fireflyconfig')->getFresh('ff3_version', '1.0')->data;
if (str_starts_with($configVersion, 'develop')) {
Log::debug('Skipping version check for develop version.');
return false;
}
if (1 === version_compare($configVersion, $dbVersion)) {
Log::warning(
sprintf(
'The current configured Firefly III version (%s) is older than the required version (%s). Redirect to migrate routine.',
$dbVersion,
$configVersion
)
);
return true;
}
// Log::info(sprintf('Installed Firefly III version (%s) equals expected Firefly III version (%s)', $dbVersion, $configVersion));
return false;
}
}

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)]);
}
}

View File

@@ -32,46 +32,60 @@ class AccountType extends Model
{
use ReturnsIntegerIdTrait;
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string ASSET = 'Asset account';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string BENEFICIARY = 'Beneficiary account';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string CASH = 'Cash account';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string CREDITCARD = 'Credit card';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string DEBT = 'Debt';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string DEFAULT = 'Default account';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string EXPENSE = 'Expense account';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string IMPORT = 'Import account';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string INITIAL_BALANCE = 'Initial balance account';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string LIABILITY_CREDIT = 'Liability credit account';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string LOAN = 'Loan';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string MORTGAGE = 'Mortgage';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string RECONCILIATION = 'Reconciliation account';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string REVENUE = 'Revenue account';
protected $casts

View File

@@ -23,9 +23,11 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Handlers\Observer\AttachmentObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -34,6 +36,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([AttachmentObserver::class])]
class Attachment extends Model
{
use ReturnsIntegerIdTrait;
@@ -50,7 +53,7 @@ class Attachment extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$attachmentId = (int) $value;
$attachmentId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -83,7 +86,7 @@ class Attachment extends Model
*/
public function fileName(): string
{
return sprintf('at-%s.data', (string) $this->id);
return sprintf('at-%s.data', (string)$this->id);
}
/**
@@ -97,7 +100,7 @@ class Attachment extends Model
protected function attachableId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}

View File

@@ -48,14 +48,7 @@ class AuditLogEntry extends Model
protected function auditableId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function changerId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
@@ -69,4 +62,11 @@ class AuditLogEntry extends Model
'deleted_at' => 'datetime',
];
}
protected function changerId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -25,24 +25,30 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Deprecated;
use FireflyIII\Handlers\Observer\AutoBudgetObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
#[ObservedBy([AutoBudgetObserver::class])]
class AutoBudget extends Model
{
use ReturnsIntegerIdTrait;
use SoftDeletes;
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const int AUTO_BUDGET_ADJUSTED = 3;
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const int AUTO_BUDGET_RESET = 1;
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const int AUTO_BUDGET_ROLLOVER = 2;
protected $casts
= [
@@ -64,14 +70,14 @@ class AutoBudget extends Model
protected function amount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
get: static fn ($value) => (string)$value,
);
}
protected function budgetId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
@@ -85,7 +91,7 @@ class AutoBudget extends Model
protected function transactionCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -24,15 +24,18 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use FireflyIII\Handlers\Observer\AvailableBudgetObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([AvailableBudgetObserver::class])]
class AvailableBudget extends Model
{
use ReturnsIntegerIdTrait;
@@ -49,7 +52,7 @@ class AvailableBudget extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$availableBudgetId = (int) $value;
$availableBudgetId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -77,10 +80,26 @@ class AvailableBudget extends Model
protected function amount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
get: static fn ($value) => (string)$value,
);
}
protected function casts(): array
{
return [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
'start_date' => 'date',
'end_date' => 'date',
'transaction_currency_id' => 'int',
'amount' => 'string',
'native_amount' => 'string',
'user_id' => 'integer',
'user_group_id' => 'integer',
];
}
protected function endDate(): Attribute
{
return Attribute::make(
@@ -100,23 +119,7 @@ class AvailableBudget extends Model
protected function transactionCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
protected function casts(): array
{
return [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
'start_date' => 'date',
'end_date' => 'date',
'transaction_currency_id' => 'int',
'amount' => 'string',
'native_amount' => 'string',
'user_id' => 'integer',
'user_group_id' => 'integer',
];
}
}

View File

@@ -24,9 +24,11 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Handlers\Observer\BillObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -36,6 +38,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([BillObserver::class])]
class Bill extends Model
{
use ReturnsIntegerIdTrait;
@@ -75,7 +78,7 @@ class Bill extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$billId = (int) $value;
$billId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -121,7 +124,7 @@ class Bill extends Model
*/
public function setAmountMaxAttribute($value): void
{
$this->attributes['amount_max'] = (string) $value;
$this->attributes['amount_max'] = (string)$value;
}
/**
@@ -129,7 +132,7 @@ class Bill extends Model
*/
public function setAmountMinAttribute($value): void
{
$this->attributes['amount_min'] = (string) $value;
$this->attributes['amount_min'] = (string)$value;
}
public function transactionCurrency(): BelongsTo
@@ -148,7 +151,7 @@ class Bill extends Model
protected function amountMax(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
get: static fn ($value) => (string)$value,
);
}
@@ -158,31 +161,7 @@ class Bill extends Model
protected function amountMin(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
/**
* Get the skip
*/
protected function skip(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function transactionCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (string)$value,
);
}
@@ -206,4 +185,28 @@ class Bill extends Model
'native_amount_max' => 'string',
];
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
/**
* Get the skip
*/
protected function skip(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function transactionCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -23,9 +23,11 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Handlers\Observer\BudgetObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -35,6 +37,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([BudgetObserver::class])]
class Budget extends Model
{
use ReturnsIntegerIdTrait;
@@ -53,7 +56,7 @@ class Budget extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$budgetId = (int) $value;
$budgetId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -106,13 +109,6 @@ class Budget extends Model
return $this->belongsToMany(Transaction::class, 'budget_transaction', 'budget_id');
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -125,4 +121,11 @@ class Budget extends Model
'user_group_id' => 'integer',
];
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -24,13 +24,16 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Handlers\Observer\BudgetLimitObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([BudgetLimitObserver::class])]
class BudgetLimit extends Model
{
use ReturnsIntegerIdTrait;
@@ -45,7 +48,7 @@ class BudgetLimit extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$budgetLimitId = (int) $value;
$budgetLimitId = (int)$value;
$budgetLimit = self::where('budget_limits.id', $budgetLimitId)
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->where('budgets.user_id', auth()->user()->id)
@@ -83,21 +86,14 @@ class BudgetLimit extends Model
protected function amount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
get: static fn ($value) => (string)$value,
);
}
protected function budgetId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function transactionCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
@@ -113,4 +109,11 @@ class BudgetLimit extends Model
'native_amount' => 'string',
];
}
protected function transactionCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -24,9 +24,11 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Handlers\Observer\CategoryObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@@ -34,6 +36,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([CategoryObserver::class])]
class Category extends Model
{
use ReturnsIntegerIdTrait;
@@ -52,7 +55,7 @@ class Category extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$categoryId = (int) $value;
$categoryId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -106,4 +109,9 @@ class Category extends Model
'user_group_id' => 'integer',
];
}
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\SoftDeletes;
@@ -38,14 +38,6 @@ class Configuration extends Model
protected $table = 'configuration';
/**
* TODO can be replaced with native laravel code.
*/
protected function data(): Attribute
{
return Attribute::make(get: fn ($value) => json_decode((string) $value), set: fn ($value) => ['data' => json_encode($value)]);
}
protected function casts(): array
{
return [
@@ -54,4 +46,12 @@ class Configuration extends Model
'deleted_at' => 'datetime',
];
}
/**
* TODO can be replaced with native laravel code.
*/
protected function data(): Attribute
{
return Attribute::make(get: fn ($value) => json_decode((string)$value), set: fn ($value) => ['data' => json_encode($value)]);
}
}

View File

@@ -37,7 +37,8 @@ class CurrencyExchangeRate extends Model
use ReturnsIntegerIdTrait;
use ReturnsIntegerUserIdTrait;
use SoftDeletes;
protected $fillable = ['user_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate'];
protected $fillable = ['user_id', 'user_group_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate'];
public function fromCurrency(): BelongsTo
{
@@ -54,34 +55,6 @@ class CurrencyExchangeRate extends Model
return $this->belongsTo(User::class);
}
protected function fromCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function rate(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
}
protected function toCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function userRate(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
}
protected function casts(): array
{
return [
@@ -96,4 +69,32 @@ class CurrencyExchangeRate extends Model
'user_rate' => 'string',
];
}
protected function fromCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function rate(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string)$value,
);
}
protected function toCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function userRate(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string)$value,
);
}
}

View File

@@ -53,13 +53,6 @@ class GroupMembership extends Model
return $this->belongsTo(UserRole::class);
}
protected function userRoleId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -69,4 +62,11 @@ class GroupMembership extends Model
'user_group_id' => 'integer',
];
}
protected function userRoleId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -36,6 +36,7 @@ class InvitedUser extends Model
{
use ReturnsIntegerIdTrait;
use ReturnsIntegerUserIdTrait;
protected $fillable = ['user_group_id', 'user_id', 'email', 'invite_code', 'expires', 'expires_tz', 'redeemed'];
/**
@@ -44,7 +45,7 @@ class InvitedUser extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$attemptId = (int) $value;
$attemptId = (int)$value;
/** @var null|InvitedUser $attempt */
$attempt = self::find($attemptId);

View File

@@ -44,7 +44,7 @@ class LinkType extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$linkTypeId = (int) $value;
$linkTypeId = (int)$value;
$linkType = self::find($linkTypeId);
if (null !== $linkType) {
return $linkType;

View File

@@ -66,13 +66,6 @@ class Location extends Model
return $this->morphMany(TransactionJournal::class, 'locatable');
}
protected function locatableId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -84,4 +77,11 @@ class Location extends Model
'longitude' => 'float',
];
}
protected function locatableId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -44,13 +44,6 @@ class Note extends Model
return $this->morphTo();
}
protected function noteableId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -59,4 +52,11 @@ class Note extends Model
'deleted_at' => 'datetime',
];
}
protected function noteableId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -37,6 +37,7 @@ class ObjectGroup extends Model
{
use ReturnsIntegerIdTrait;
use ReturnsIntegerUserIdTrait;
protected $fillable = ['title', 'order', 'user_id', 'user_group_id'];
/**
@@ -47,7 +48,7 @@ class ObjectGroup extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$objectGroupId = (int) $value;
$objectGroupId = (int)$value;
/** @var null|ObjectGroup $objectGroup */
$objectGroup = self::where('object_groups.id', $objectGroupId)
@@ -90,13 +91,6 @@ class ObjectGroup extends Model
return $this->morphedByMany(PiggyBank::class, 'object_groupable');
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -107,4 +101,11 @@ class ObjectGroup extends Model
'deleted_at' => 'datetime',
];
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class PeriodStatistic extends Model
{
use ReturnsIntegerUserIdTrait;
protected function casts(): array
{
return [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'start' => SeparateTimezoneCaster::class,
'end' => SeparateTimezoneCaster::class,
];
}
public function userGroup(): BelongsTo
{
return $this->belongsTo(UserGroup::class);
}
protected function count(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
public function primaryStatable(): MorphTo
{
return $this->morphTo();
}
public function secondaryStatable(): MorphTo
{
return $this->morphTo();
}
public function tertiaryStatable(): MorphTo
{
return $this->morphTo();
}
}

View File

@@ -23,7 +23,9 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Handlers\Observer\PiggyBankObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -34,6 +36,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([PiggyBankObserver::class])]
class PiggyBank extends Model
{
use ReturnsIntegerIdTrait;
@@ -49,7 +52,7 @@ class PiggyBank extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$piggyBankId = (int) $value;
$piggyBankId = (int)$value;
$piggyBank = self::where('piggy_banks.id', $piggyBankId)
->leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
@@ -109,7 +112,7 @@ class PiggyBank extends Model
*/
public function setTargetAmountAttribute($value): void
{
$this->attributes['target_amount'] = (string) $value;
$this->attributes['target_amount'] = (string)$value;
}
public function transactionCurrency(): BelongsTo
@@ -120,24 +123,7 @@ class PiggyBank extends Model
protected function accountId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
/**
* Get the max amount
*/
protected function targetAmount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
get: static fn ($value) => (int)$value,
);
}
@@ -156,4 +142,21 @@ class PiggyBank extends Model
'native_target_amount' => 'string',
];
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
/**
* Get the max amount
*/
protected function targetAmount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string)$value,
);
}
}

View File

@@ -24,11 +24,14 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Handlers\Observer\PiggyBankEventObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
#[ObservedBy([PiggyBankEventObserver::class])]
class PiggyBankEvent extends Model
{
use ReturnsIntegerIdTrait;
@@ -47,7 +50,7 @@ class PiggyBankEvent extends Model
*/
public function setAmountAttribute($value): void
{
$this->attributes['amount'] = (string) $value;
$this->attributes['amount'] = (string)$value;
}
public function transactionJournal(): BelongsTo
@@ -61,14 +64,7 @@ class PiggyBankEvent extends Model
protected function amount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
}
protected function piggyBankId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (string)$value,
);
}
@@ -82,4 +78,11 @@ class PiggyBankEvent extends Model
'native_amount' => 'string',
];
}
protected function piggyBankId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -23,10 +23,10 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Carbon\Carbon;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
@@ -43,12 +43,48 @@ class PiggyBankRepetition extends Model
return $this->belongsTo(PiggyBank::class);
}
/**
* @param mixed $value
*/
public function setCurrentAmountAttribute($value): void
{
$this->attributes['current_amount'] = (string)$value;
}
protected function casts(): array
{
return [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'start_date' => SeparateTimezoneCaster::class,
'target_date' => SeparateTimezoneCaster::class,
'virtual_balance' => 'string',
];
}
/**
* Get the amount
*/
protected function currentAmount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string)$value,
);
}
#[Scope]
protected function onDates(EloquentBuilder $query, Carbon $start, Carbon $target): EloquentBuilder
{
return $query->where('start_date', $start->format('Y-m-d'))->where('target_date', $target->format('Y-m-d'));
}
protected function piggyBankId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
/**
* @return EloquentBuilder
*/
@@ -69,40 +105,4 @@ class PiggyBankRepetition extends Model
)
;
}
/**
* @param mixed $value
*/
public function setCurrentAmountAttribute($value): void
{
$this->attributes['current_amount'] = (string) $value;
}
/**
* Get the amount
*/
protected function currentAmount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
}
protected function piggyBankId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'start_date' => SeparateTimezoneCaster::class,
'target_date' => SeparateTimezoneCaster::class,
'virtual_balance' => 'string',
];
}
}

View File

@@ -50,7 +50,7 @@ class Preference extends Model
// some preferences do not have an administration ID.
// some need it, to make sure the correct one is selected.
$userGroupId = (int) $user->user_group_id;
$userGroupId = (int)$user->user_group_id;
$userGroupId = 0 === $userGroupId ? null : $userGroupId;
/** @var null|Preference $preference */
@@ -67,7 +67,7 @@ class Preference extends Model
// try again with ID, but this time don't care about the preferred user_group_id
if (null === $preference) {
$preference = $user->preferences()->where('id', (int) $value)->first();
$preference = $user->preferences()->where('id', (int)$value)->first();
}
if (null !== $preference) {
/** @var Preference $preference */
@@ -78,7 +78,7 @@ class Preference extends Model
$preference = new self();
$preference->name = $value;
$preference->data = $default[$value];
$preference->user_id = (int) $user->id;
$preference->user_id = (int)$user->id;
$preference->user_group_id = in_array($value, $items, true) ? $userGroupId : null;
$preference->save();

View File

@@ -25,9 +25,11 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Handlers\Observer\RecurrenceObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -36,6 +38,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([RecurrenceObserver::class])]
class Recurrence extends Model
{
use ReturnsIntegerIdTrait;
@@ -55,7 +58,7 @@ class Recurrence extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$recurrenceId = (int) $value;
$recurrenceId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -113,13 +116,6 @@ class Recurrence extends Model
return $this->belongsTo(TransactionType::class);
}
protected function transactionTypeId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -139,4 +135,11 @@ class Recurrence extends Model
'user_group_id' => 'integer',
];
}
protected function transactionTypeId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -44,13 +44,6 @@ class RecurrenceMeta extends Model
return $this->belongsTo(Recurrence::class);
}
protected function recurrenceId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -61,4 +54,11 @@ class RecurrenceMeta extends Model
'value' => 'string',
];
}
protected function recurrenceId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -36,16 +36,20 @@ class RecurrenceRepetition extends Model
use ReturnsIntegerIdTrait;
use SoftDeletes;
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const int WEEKEND_DO_NOTHING = 1;
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const int WEEKEND_SKIP_CREATION = 2;
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const int WEEKEND_TO_FRIDAY = 3;
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const int WEEKEND_TO_MONDAY = 4;
protected $casts
@@ -78,21 +82,21 @@ class RecurrenceRepetition extends Model
protected function recurrenceId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
protected function repetitionSkip(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
protected function weekend(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -24,13 +24,16 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Handlers\Observer\RecurrenceTransactionObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
#[ObservedBy([RecurrenceTransactionObserver::class])]
class RecurrenceTransaction extends Model
{
use ReturnsIntegerIdTrait;
@@ -88,49 +91,7 @@ class RecurrenceTransaction extends Model
protected function amount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
}
protected function destinationId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function foreignAmount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
}
protected function recurrenceId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function sourceId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function transactionCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function userId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (string)$value,
);
}
@@ -145,4 +106,46 @@ class RecurrenceTransaction extends Model
'description' => 'string',
];
}
protected function destinationId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function foreignAmount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string)$value,
);
}
protected function recurrenceId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function sourceId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function transactionCurrencyId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function userId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -44,13 +44,6 @@ class RecurrenceTransactionMeta extends Model
return $this->belongsTo(RecurrenceTransaction::class, 'rt_id');
}
protected function rtId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -61,4 +54,11 @@ class RecurrenceTransactionMeta extends Model
'value' => 'string',
];
}
protected function rtId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -23,9 +23,11 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Handlers\Observer\RuleObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -33,6 +35,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([RuleObserver::class])]
class Rule extends Model
{
use ReturnsIntegerIdTrait;
@@ -49,7 +52,7 @@ class Rule extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$ruleId = (int) $value;
$ruleId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -84,30 +87,11 @@ class Rule extends Model
return $this->hasMany(RuleTrigger::class);
}
protected function description(): Attribute
{
return Attribute::make(set: fn ($value) => ['description' => e($value)]);
}
public function userGroup(): BelongsTo
{
return $this->belongsTo(UserGroup::class);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function ruleGroupId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -123,4 +107,23 @@ class Rule extends Model
'user_group_id' => 'integer',
];
}
protected function description(): Attribute
{
return Attribute::make(set: fn ($value) => ['description' => e($value)]);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function ruleGroupId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -42,7 +42,7 @@ class RuleAction extends Model
if (false === config('firefly.feature_flags.expression_engine')) {
Log::debug('Expression engine is disabled, returning action value as string.');
return (string) $this->action_value;
return (string)$this->action_value;
}
if (true === config('firefly.feature_flags.expression_engine') && str_starts_with($this->action_value, '\=')) {
// return literal string.
@@ -54,7 +54,7 @@ class RuleAction extends Model
$result = $expr->evaluate($journal);
} catch (SyntaxError $e) {
Log::error(sprintf('Expression engine failed to evaluate expression "%s" with error "%s".', $this->action_value, $e->getMessage()));
$result = (string) $this->action_value;
$result = (string)$this->action_value;
}
Log::debug(sprintf('Expression engine is enabled, result of expression "%s" is "%s".', $this->action_value, $result));
@@ -66,20 +66,6 @@ class RuleAction extends Model
return $this->belongsTo(Rule::class);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function ruleId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -90,4 +76,18 @@ class RuleAction extends Model
'stop_processing' => 'boolean',
];
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function ruleId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -23,9 +23,11 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Handlers\Observer\RuleGroupObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -33,6 +35,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([RuleGroupObserver::class])]
class RuleGroup extends Model
{
use ReturnsIntegerIdTrait;
@@ -49,7 +52,7 @@ class RuleGroup extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$ruleGroupId = (int) $value;
$ruleGroupId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -74,13 +77,6 @@ class RuleGroup extends Model
return $this->hasMany(Rule::class);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -94,4 +90,11 @@ class RuleGroup extends Model
'user_group_id' => 'integer',
];
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -39,20 +39,6 @@ class RuleTrigger extends Model
return $this->belongsTo(Rule::class);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function ruleId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -63,4 +49,18 @@ class RuleTrigger extends Model
'stop_processing' => 'boolean',
];
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function ruleId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -24,9 +24,11 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Handlers\Observer\TagObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@@ -34,6 +36,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([TagObserver::class])]
class Tag extends Model
{
use ReturnsIntegerIdTrait;
@@ -42,7 +45,7 @@ class Tag extends Model
protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'date_tz', 'description', 'tag_mode'];
protected $hidden = ['zoomLevel', 'latitude', 'longitude'];
protected $hidden = ['zoomLevel', 'zoom_level', 'latitude', 'longitude'];
/**
* Route binder. Converts the key in the URL to the specified object (or throw 404).
@@ -52,7 +55,7 @@ class Tag extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$tagId = (int) $value;
$tagId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -101,4 +104,9 @@ class Tag extends Model
'user_group_id' => 'integer',
];
}
public function primaryPeriodStatistics(): MorphMany
{
return $this->morphMany(PeriodStatistic::class, 'primary_statable');
}
}

View File

@@ -23,9 +23,11 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Carbon\Carbon;
use FireflyIII\Handlers\Observer\TransactionObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -34,6 +36,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
#[ObservedBy([TransactionObserver::class])]
class Transaction extends Model
{
use HasFactory;
@@ -89,6 +92,31 @@ class Transaction extends Model
return $this->belongsTo(TransactionCurrency::class, 'foreign_currency_id');
}
/**
* @param mixed $value
*/
public function setAmountAttribute($value): void
{
$this->attributes['amount'] = (string)$value;
}
public function transactionCurrency(): BelongsTo
{
return $this->belongsTo(TransactionCurrency::class);
}
public function transactionJournal(): BelongsTo
{
return $this->belongsTo(TransactionJournal::class);
}
protected function accountId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
/**
* Check for transactions AFTER a specified date.
*/
@@ -117,6 +145,23 @@ class Transaction extends Model
return false;
}
/**
* Get the amount
*/
protected function amount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string)$value,
);
}
protected function balanceDirty(): Attribute
{
return Attribute::make(
get: static fn ($value) => 1 === (int)$value,
);
}
/**
* Check for transactions BEFORE the specified date.
*/
@@ -129,78 +174,6 @@ class Transaction extends Model
$query->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'));
}
#[Scope]
protected function transactionTypes(Builder $query, array $types): void
{
if (!self::isJoined($query, 'transaction_journals')) {
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
}
if (!self::isJoined($query, 'transaction_types')) {
$query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
}
$query->whereIn('transaction_types.type', $types);
}
/**
* @param mixed $value
*/
public function setAmountAttribute($value): void
{
$this->attributes['amount'] = (string) $value;
}
public function transactionCurrency(): BelongsTo
{
return $this->belongsTo(TransactionCurrency::class);
}
public function transactionJournal(): BelongsTo
{
return $this->belongsTo(TransactionJournal::class);
}
protected function accountId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
/**
* Get the amount
*/
protected function amount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
}
protected function balanceDirty(): Attribute
{
return Attribute::make(
get: static fn ($value) => 1 === (int) $value,
);
}
/**
* Get the foreign amount
*/
protected function foreignAmount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string) $value,
);
}
protected function transactionJournalId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -221,4 +194,34 @@ class Transaction extends Model
'native_foreign_amount' => 'string',
];
}
/**
* Get the foreign amount
*/
protected function foreignAmount(): Attribute
{
return Attribute::make(
get: static fn ($value) => (string)$value,
);
}
protected function transactionJournalId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
#[Scope]
protected function transactionTypes(Builder $query, array $types): void
{
if (!self::isJoined($query, 'transaction_journals')) {
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
}
if (!self::isJoined($query, 'transaction_types')) {
$query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
}
$query->whereIn('transaction_types.type', $types);
}
}

View File

@@ -50,7 +50,7 @@ class TransactionCurrency extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$currencyId = (int) $value;
$currencyId = (int)$value;
$currency = self::find($currencyId);
if (null !== $currency) {
$currency->refreshForUser(auth()->user());
@@ -101,13 +101,6 @@ class TransactionCurrency extends Model
return $this->belongsToMany(User::class)->withTimestamps()->withPivot('user_default');
}
protected function decimalPlaces(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -118,4 +111,11 @@ class TransactionCurrency extends Model
'enabled' => 'bool',
];
}
protected function decimalPlaces(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -23,15 +23,18 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Handlers\Observer\TransactionGroupObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([TransactionGroupObserver::class])]
class TransactionGroup extends Model
{
use ReturnsIntegerIdTrait;
@@ -49,7 +52,7 @@ class TransactionGroup extends Model
{
app('log')->debug(sprintf('Now in %s("%s")', __METHOD__, $value));
if (auth()->check()) {
$groupId = (int) $value;
$groupId = (int)$value;
/** @var User $user */
$user = auth()->user();

View File

@@ -23,13 +23,15 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Carbon\Carbon;
use FireflyIII\Casts\SeparateTimezoneCaster;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Handlers\Observer\TransactionJournalObserver;
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;
@@ -46,6 +48,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method EloquentBuilder|static after()
* @method static EloquentBuilder|static query()
*/
#[ObservedBy([TransactionJournalObserver::class])]
class TransactionJournal extends Model
{
use HasFactory;
@@ -78,7 +81,7 @@ class TransactionJournal extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$journalId = (int) $value;
$journalId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -165,32 +168,6 @@ class TransactionJournal extends Model
return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'));
}
#[Scope]
protected function transactionTypes(EloquentBuilder $query, array $types): void
{
if (!self::isJoined($query, 'transaction_types')) {
$query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
}
if (0 !== count($types)) {
$query->whereIn('transaction_types.type', $types);
}
}
/**
* Checks if tables are joined.
*/
public static function isJoined(EloquentBuilder $query, string $table): bool
{
$joins = $query->getQuery()->joins;
foreach ($joins as $join) {
if ($join->table === $table) {
return true;
}
}
return false;
}
public function sourceJournalLinks(): HasMany
{
return $this->hasMany(TransactionJournalLink::class, 'source_id');
@@ -231,20 +208,6 @@ class TransactionJournal extends Model
return $this->belongsTo(UserGroup::class);
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function transactionTypeId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -263,4 +226,44 @@ class TransactionJournal extends Model
'user_group_id' => 'integer',
];
}
protected function order(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function transactionTypeId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
#[Scope]
protected function transactionTypes(EloquentBuilder $query, array $types): void
{
if (!self::isJoined($query, 'transaction_types')) {
$query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
}
if (0 !== count($types)) {
$query->whereIn('transaction_types.type', $types);
}
}
/**
* Checks if tables are joined.
*/
public static function isJoined(EloquentBuilder $query, string $table): bool
{
$joins = $query->getQuery()->joins;
foreach ($joins as $join) {
if ($join->table === $table) {
return true;
}
}
return false;
}
}

View File

@@ -44,7 +44,7 @@ class TransactionJournalLink extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$linkId = (int) $value;
$linkId = (int)$value;
$link = self::where('journal_links.id', $linkId)
->leftJoin('transaction_journals as t_a', 't_a.id', '=', 'source_id')
->leftJoin('transaction_journals as t_b', 't_b.id', '=', 'destination_id')
@@ -83,27 +83,6 @@ class TransactionJournalLink extends Model
return $this->belongsTo(TransactionJournal::class, 'source_id');
}
protected function destinationId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function linkTypeId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function sourceId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -111,4 +90,25 @@ class TransactionJournalLink extends Model
'updated_at' => 'datetime',
];
}
protected function destinationId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function linkTypeId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
protected function sourceId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -41,27 +41,11 @@ class TransactionJournalMeta extends Model
protected $table = 'journal_meta';
protected function data(): Attribute
{
return Attribute::make(get: fn ($value) => json_decode((string) $value, false), set: function ($value) {
$data = json_encode($value);
return ['data' => $data, 'hash' => hash('sha256', $data)];
});
}
public function transactionJournal(): BelongsTo
{
return $this->belongsTo(TransactionJournal::class);
}
protected function transactionJournalId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -70,4 +54,20 @@ class TransactionJournalMeta extends Model
'deleted_at' => 'datetime',
];
}
protected function data(): Attribute
{
return Attribute::make(get: fn ($value) => json_decode((string)$value, false), set: function ($value) {
$data = json_encode($value);
return ['data' => $data, 'hash' => hash('sha256', $data)];
});
}
protected function transactionJournalId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -36,25 +36,32 @@ class TransactionType extends Model
use ReturnsIntegerIdTrait;
use SoftDeletes;
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string DEPOSIT = 'Deposit';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string INVALID = 'Invalid';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string LIABILITY_CREDIT = 'Liability credit';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string OPENING_BALANCE = 'Opening balance';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string RECONCILIATION = 'Reconciliation';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string TRANSFER = 'Transfer';
#[Deprecated] /** @deprecated */
#[Deprecated]
/** @deprecated */
public const string WITHDRAWAL = 'Withdrawal';
protected $casts

View File

@@ -47,7 +47,7 @@ class UserGroup extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$userGroupId = (int) $value;
$userGroupId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -76,6 +76,14 @@ class UserGroup extends Model
return $this->hasMany(Account::class);
}
/**
* Link to accounts.
*/
public function periodStatistics(): HasMany
{
return $this->hasMany(PeriodStatistic::class);
}
/**
* Link to attachments.
*/

View File

@@ -27,9 +27,11 @@ namespace FireflyIII\Models;
use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum;
use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum;
use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum;
use FireflyIII\Handlers\Observer\WebhookObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@@ -37,6 +39,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([WebhookObserver::class])]
class Webhook extends Model
{
use ReturnsIntegerIdTrait;
@@ -151,16 +154,16 @@ class Webhook extends Model
return $this->belongsTo(User::class);
}
public function webhookMessages(): HasMany
{
return $this->hasMany(WebhookMessage::class);
}
public function webhookDeliveries(): BelongsToMany
{
return $this->belongsToMany(WebhookDelivery::class);
}
public function webhookMessages(): HasMany
{
return $this->hasMany(WebhookMessage::class);
}
public function webhookResponses(): BelongsToMany
{
return $this->belongsToMany(WebhookResponse::class);

View File

@@ -45,7 +45,7 @@ class WebhookAttempt extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$attemptId = (int) $value;
$attemptId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -68,7 +68,7 @@ class WebhookAttempt extends Model
protected function webhookMessageId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -41,7 +41,7 @@ class WebhookDelivery extends Model
protected function key(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -24,14 +24,17 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\Handlers\Observer\WebhookMessageObserver;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([WebhookMessageObserver::class])]
class WebhookMessage extends Model
{
use ReturnsIntegerIdTrait;
@@ -44,7 +47,7 @@ class WebhookMessage extends Model
public static function routeBinder(string $value): self
{
if (auth()->check()) {
$messageId = (int) $value;
$messageId = (int)$value;
/** @var User $user */
$user = auth()->user();
@@ -69,23 +72,6 @@ class WebhookMessage extends Model
return $this->hasMany(WebhookAttempt::class);
}
/**
* Get the amount
*/
protected function sent(): Attribute
{
return Attribute::make(
get: static fn ($value) => (bool) $value,
);
}
protected function webhookId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
);
}
protected function casts(): array
{
return [
@@ -96,4 +82,21 @@ class WebhookMessage extends Model
'logs' => 'json',
];
}
/**
* Get the amount
*/
protected function sent(): Attribute
{
return Attribute::make(
get: static fn ($value) => (bool)$value,
);
}
protected function webhookId(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -41,7 +41,7 @@ class WebhookResponse extends Model
protected function key(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -41,7 +41,7 @@ class WebhookTrigger extends Model
protected function key(): Attribute
{
return Attribute::make(
get: static fn ($value) => (int) $value,
get: static fn ($value) => (int)$value,
);
}
}

View File

@@ -57,46 +57,6 @@ use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Events\UpdatedAccount;
use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Events\UserChangedEmail;
use FireflyIII\Handlers\Observer\AccountObserver;
use FireflyIII\Handlers\Observer\AttachmentObserver;
use FireflyIII\Handlers\Observer\AutoBudgetObserver;
use FireflyIII\Handlers\Observer\AvailableBudgetObserver;
use FireflyIII\Handlers\Observer\BillObserver;
use FireflyIII\Handlers\Observer\BudgetLimitObserver;
use FireflyIII\Handlers\Observer\BudgetObserver;
use FireflyIII\Handlers\Observer\CategoryObserver;
use FireflyIII\Handlers\Observer\PiggyBankEventObserver;
use FireflyIII\Handlers\Observer\PiggyBankObserver;
use FireflyIII\Handlers\Observer\RecurrenceObserver;
use FireflyIII\Handlers\Observer\RecurrenceTransactionObserver;
use FireflyIII\Handlers\Observer\RuleGroupObserver;
use FireflyIII\Handlers\Observer\RuleObserver;
use FireflyIII\Handlers\Observer\TagObserver;
use FireflyIII\Handlers\Observer\TransactionGroupObserver;
use FireflyIII\Handlers\Observer\TransactionJournalObserver;
use FireflyIII\Handlers\Observer\TransactionObserver;
use FireflyIII\Handlers\Observer\WebhookMessageObserver;
use FireflyIII\Handlers\Observer\WebhookObserver;
use FireflyIII\Models\Account;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Category;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Webhook;
use FireflyIII\Models\WebhookMessage;
use Illuminate\Auth\Events\Login;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Laravel\Passport\Events\AccessTokenCreated;
@@ -256,32 +216,5 @@ class EventServiceProvider extends ServiceProvider
* Register any events for your application.
*/
#[Override]
public function boot(): void
{
$this->registerObservers();
}
private function registerObservers(): void
{
Attachment::observe(new AttachmentObserver());
Account::observe(new AccountObserver());
AutoBudget::observe(new AutoBudgetObserver());
AvailableBudget::observe(new AvailableBudgetObserver());
Bill::observe(new BillObserver());
Budget::observe(new BudgetObserver());
BudgetLimit::observe(new BudgetLimitObserver());
Category::observe(new CategoryObserver());
PiggyBank::observe(new PiggyBankObserver());
PiggyBankEvent::observe(new PiggyBankEventObserver());
Recurrence::observe(new RecurrenceObserver());
RecurrenceTransaction::observe(new RecurrenceTransactionObserver());
Rule::observe(new RuleObserver());
RuleGroup::observe(new RuleGroupObserver());
Tag::observe(new TagObserver());
Transaction::observe(new TransactionObserver());
TransactionJournal::observe(new TransactionJournalObserver());
TransactionGroup::observe(new TransactionGroupObserver());
Webhook::observe(new WebhookObserver());
WebhookMessage::observe(new WebhookMessageObserver());
}
public function boot(): void {}
}

View File

@@ -43,6 +43,8 @@ use FireflyIII\Repositories\AuditLogEntry\ALERepository;
use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface;
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepository;
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepository;
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
use FireflyIII\Repositories\TransactionType\TransactionTypeRepository;
use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepository;
@@ -174,6 +176,18 @@ class FireflyServiceProvider extends ServiceProvider
}
);
$this->app->bind(
static function (Application $app): PeriodStatisticRepositoryInterface {
/** @var PeriodStatisticRepository $repository */
$repository = app(PeriodStatisticRepository::class);
if ($app->auth->check()) { // @phpstan-ignore-line (phpstan does not understand the reference to auth)
$repository->setUser(auth()->user());
}
return $repository;
}
);
$this->app->bind(
static function (Application $app): WebhookRepositoryInterface {
/** @var WebhookRepository $repository */

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