Compare commits

...

152 Commits

Author SHA1 Message Date
github-actions[bot]
b0e21dd553 Merge pull request #11773 from firefly-iii/release-1771657248
🤖 Automatically merge the PR into the develop branch.
2026-02-21 08:00:55 +01:00
JC5
88291c5f63 🤖 Auto commit for release 'develop' on 2026-02-21 2026-02-21 08:00:48 +01:00
James Cole
a87e10f734 Add a week's delay. 2026-02-21 07:54:23 +01:00
github-actions[bot]
75f42d57f1 Merge pull request #11772 from firefly-iii/release-1771653268
🤖 Automatically merge the PR into the develop branch.
2026-02-21 06:54:36 +01:00
JC5
3f60442281 🤖 Auto commit for release 'develop' on 2026-02-21 2026-02-21 06:54:28 +01:00
James Cole
dd14bb1664 Add 10m slack 2026-02-21 06:48:34 +01:00
James Cole
403a2ce2cd Remove weird repeated line from Mago. 2026-02-21 06:47:26 +01:00
James Cole
e5366dbf6c Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
# Conflicts:
#	app/Services/FireflyIIIOrg/Update/GitHubUpdateRequest.php
2026-02-21 06:44:11 +01:00
James Cole
091f264f3e Remove mago analyse, fix update check. 2026-02-21 06:40:51 +01:00
github-actions[bot]
3ad1420262 Merge pull request #11771 from firefly-iii/release-1771652323
🤖 Automatically merge the PR into the develop branch.
2026-02-21 06:38:50 +01:00
JC5
018f68b789 🤖 Auto commit for release 'develop' on 2026-02-21 2026-02-21 06:38:43 +01:00
James Cole
e23d0de8f9 Fix local update check. 2026-02-21 06:31:42 +01:00
James Cole
04c3bf966d Rebuild update request to use GitHub API. 2026-02-21 06:25:18 +01:00
James Cole
681619f732 Remove unused classes 2026-02-20 20:33:36 +01:00
James Cole
cab298708c Remove unused enums 2026-02-20 20:33:13 +01:00
James Cole
c4392f89d1 Improve code quality, add PHP 8.5 things. 2026-02-20 20:22:13 +01:00
James Cole
36789a310a Tiny changes. 2026-02-20 20:17:10 +01:00
James Cole
e92a1b6dda Add new support guide. 2026-02-20 20:03:11 +01:00
James Cole
1d687a632f Add changelog. 2026-02-20 20:01:25 +01:00
github-actions[bot]
1fa4a1bdc8 Merge pull request #11770 from firefly-iii/release-1771612722
🤖 Automatically merge the PR into the develop branch.
2026-02-20 19:38:50 +01:00
JC5
aee53cffb9 🤖 Auto commit for release 'develop' on 2026-02-20 2026-02-20 19:38:42 +01:00
github-actions[bot]
828b965c98 Merge pull request #11769 from firefly-iii/release-1771605618
🤖 Automatically merge the PR into the develop branch.
2026-02-20 17:40:27 +01:00
JC5
c008cd41db 🤖 Auto commit for release 'develop' on 2026-02-20 2026-02-20 17:40:18 +01:00
James Cole
accac89ffb Upgrade job to PHP 8.5 2026-02-20 17:35:59 +01:00
James Cole
fb79e3b08c Push Firefly III to PHP 8.5 2026-02-20 17:33:53 +01:00
github-actions[bot]
db2f804b6a Merge pull request #11767 from firefly-iii/release-1771595862
🤖 Automatically merge the PR into the develop branch.
2026-02-20 14:57:50 +01:00
JC5
986e86ed51 🤖 Auto commit for release 'develop' on 2026-02-20 2026-02-20 14:57:43 +01:00
James Cole
9cbd0380d6 Fix periods and labels, add some debug info. 2026-02-20 14:43:05 +01:00
James Cole
f6cd45a44c Make sure that overlapping available budgets are also recalculated, this fixes #11685 2026-02-20 11:23:36 +01:00
github-actions[bot]
0e321ad82a Merge pull request #11765 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-20 08:01:53 +01:00
github-actions[bot]
c353a4de95 Merge pull request #11764 from firefly-iii/release-1771570899
🤖 Automatically merge the PR into the develop branch.
2026-02-20 08:01:47 +01:00
JC5
57f828a73c 🤖 Auto commit for release 'v6.4.23' on 2026-02-20 2026-02-20 08:01:39 +01:00
github-actions[bot]
11385b208b Merge pull request #11763 from firefly-iii/release-1771570551
🤖 Automatically merge the PR into the develop branch.
2026-02-20 07:55:59 +01:00
JC5
5584866b96 🤖 Auto commit for release 'develop' on 2026-02-20 2026-02-20 07:55:51 +01:00
James Cole
0b124d875a Update changelog. 2026-02-20 07:51:19 +01:00
James Cole
177cfad862 Fix https://github.com/orgs/firefly-iii/discussions/11736 2026-02-20 07:41:32 +01:00
github-actions[bot]
e929cf7cb0 Merge pull request #11762 from firefly-iii/release-1771564191
🤖 Automatically merge the PR into the develop branch.
2026-02-20 06:10:03 +01:00
JC5
528fda08e9 🤖 Auto commit for release 'develop' on 2026-02-20 2026-02-20 06:09:51 +01:00
James Cole
6f605e5fd2 Remove phpstan thing. 2026-02-20 06:08:43 +01:00
James Cole
7c76fc7721 Fix various issues and code quality things. 2026-02-20 06:04:21 +01:00
James Cole
474680dbbf Fix issue with invitee mail. 2026-02-20 05:44:51 +01:00
James Cole
8babc144fe Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-19 20:03:30 +01:00
James Cole
6eb14f8a96 Fix #11752 2026-02-19 19:55:52 +01:00
github-actions[bot]
c4786586fb Merge pull request #11760 from firefly-iii/release-1771527037
🤖 Automatically merge the PR into the develop branch.
2026-02-19 19:50:46 +01:00
JC5
a22cc889b9 🤖 Auto commit for release 'develop' on 2026-02-19 2026-02-19 19:50:37 +01:00
James Cole
9ea7bd3b01 Update audit config. 2026-02-19 19:45:49 +01:00
James Cole
8d68cef8a5 Fix #11744 2026-02-19 19:34:22 +01:00
James Cole
b383ac1a95 Fix https://github.com/firefly-iii/firefly-iii/issues/11757 2026-02-19 19:27:18 +01:00
James Cole
d2bfb2e9df Fix https://github.com/firefly-iii/firefly-iii/issues/11734 2026-02-16 20:40:32 +01:00
github-actions[bot]
0afec28e5f Merge pull request #11743 from firefly-iii/release-1771253383
🤖 Automatically merge the PR into the develop branch.
2026-02-16 15:49:51 +01:00
JC5
e60e0bb99e 🤖 Auto commit for release 'develop' on 2026-02-16 2026-02-16 15:49:43 +01:00
Sander Dorigo
02064445bc Fix missing objects thing 2026-02-16 15:29:13 +01:00
github-actions[bot]
386bb811a8 Merge pull request #11742 from firefly-iii/release-1771230440
🤖 Automatically merge the PR into the develop branch.
2026-02-16 09:27:29 +01:00
JC5
653fa53da2 🤖 Auto commit for release 'develop' on 2026-02-16 2026-02-16 09:27:20 +01:00
Sander Dorigo
63d4572863 Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop 2026-02-16 09:22:36 +01:00
Sander Dorigo
9eb31fc777 Remove check for #11735 2026-02-16 09:22:30 +01:00
github-actions[bot]
7fd33035a6 Merge pull request #11741 from firefly-iii/release-1771230058
🤖 Automatically merge the PR into the develop branch.
2026-02-16 09:21:05 +01:00
JC5
5a87f6b2c0 🤖 Auto commit for release 'develop' on 2026-02-16 2026-02-16 09:20:58 +01:00
Sander Dorigo
cbebd7928f Fix #11738 2026-02-16 09:16:42 +01:00
github-actions[bot]
73089c2084 Merge pull request #11737 from firefly-iii/release-1771214139
🤖 Automatically merge the PR into the develop branch.
2026-02-16 04:55:46 +01:00
JC5
34260f2a4f 🤖 Auto commit for release 'develop' on 2026-02-16 2026-02-16 04:55:39 +01:00
James Cole
c3872aa738 Fix header in changelog. 2026-02-15 19:44:38 +01:00
github-actions[bot]
4bcd163b47 Merge pull request #11732 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-15 19:42:55 +01:00
github-actions[bot]
31d444292f Merge pull request #11731 from firefly-iii/release-1771180963
🤖 Automatically merge the PR into the develop branch.
2026-02-15 19:42:50 +01:00
JC5
a27e21f002 🤖 Auto commit for release 'v6.4.22' on 2026-02-15 2026-02-15 19:42:43 +01:00
github-actions[bot]
0f7118d5b3 Merge pull request #11730 from firefly-iii/release-1771176353
🤖 Automatically merge the PR into the develop branch.
2026-02-15 18:26:02 +01:00
JC5
4f9a1fde5d 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 18:25:54 +01:00
James Cole
bc596cb1c2 Update changelog and fix calls during startup. 2026-02-15 18:18:47 +01:00
github-actions[bot]
7110556ef0 Merge pull request #11729 from firefly-iii/release-1771170628
🤖 Automatically merge the PR into the develop branch.
2026-02-15 16:50:35 +01:00
JC5
3e1b703dfc 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 16:50:28 +01:00
James Cole
fce0750509 Remove unnecessary package. 2026-02-15 16:43:35 +01:00
github-actions[bot]
f5385d0229 Merge pull request #11727 from firefly-iii/release-1771154935
🤖 Automatically merge the PR into the develop branch.
2026-02-15 12:29:02 +01:00
JC5
8599c4a3c1 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 12:28:55 +01:00
James Cole
d210e7dcdd Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-15 12:24:46 +01:00
James Cole
8de9e0ba29 Remove checks for null 2026-02-15 12:24:33 +01:00
github-actions[bot]
7fa9e79f2a Merge pull request #11726 from firefly-iii/release-1771154592
🤖 Automatically merge the PR into the develop branch.
2026-02-15 12:23:20 +01:00
JC5
4a5281fd80 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 12:23:13 +01:00
James Cole
ad60974430 Update command so it is really the first attempt to connect. 2026-02-15 12:19:10 +01:00
James Cole
195794881b Catch nulls in dates. 2026-02-15 12:11:25 +01:00
James Cole
3e6a997dd5 Catch preferences with no date info. 2026-02-15 12:09:57 +01:00
James Cole
2eedfd9f26 Merge branch 'main' into develop 2026-02-15 12:06:20 +01:00
James Cole
9ec0515bb6 Fix string pointer 2026-02-15 12:06:05 +01:00
James Cole
6b197eecb9 Remove PR update rule from mergify configuration
Removed the rule for automatically updating PRs before merging.

Signed-off-by: James Cole <james@firefly-iii.org>
2026-02-15 11:58:07 +01:00
github-actions[bot]
591c970882 Merge pull request #11725 from firefly-iii/release-1771152972
🤖 Automatically merge the PR into the develop branch.
2026-02-15 11:56:22 +01:00
JC5
15d91dbe1b 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 11:56:12 +01:00
James Cole
6ff87bf447 Add command that first verifies the database connection. Saves some time booting up. 2026-02-15 11:51:04 +01:00
James Cole
147ce154d8 Remove unused file. 2026-02-15 11:25:26 +01:00
James Cole
2c8be33000 Clean up routes and API calls. 2026-02-15 11:25:12 +01:00
github-actions[bot]
b472890c84 Merge pull request #11724 from firefly-iii/release-1771138148
🤖 Automatically merge the PR into the develop branch.
2026-02-15 07:49:18 +01:00
JC5
2fabcf5193 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 07:49:08 +01:00
James Cole
5ed4e7aa79 Clean up kernel files and middleware. 2026-02-15 07:41:31 +01:00
James Cole
973caad7e4 Clean up providers 2026-02-15 07:27:28 +01:00
github-actions[bot]
d273503a15 Merge pull request #11723 from firefly-iii/release-1771134641
🤖 Automatically merge the PR into the develop branch.
2026-02-15 06:50:49 +01:00
JC5
cc149adb5d 🤖 Auto commit for release 'develop' on 2026-02-15 2026-02-15 06:50:41 +01:00
James Cole
ac8bcb786b Fix https://github.com/firefly-iii/firefly-iii/issues/11720 2026-02-15 06:18:52 +01:00
github-actions[bot]
642deefba5 Merge pull request #11718 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-14 20:40:32 +01:00
github-actions[bot]
5b13b64fd7 Merge pull request #11717 from firefly-iii/release-1771098019
🤖 Automatically merge the PR into the develop branch.
2026-02-14 20:40:27 +01:00
JC5
653a64d0a8 🤖 Auto commit for release 'v6.4.21' on 2026-02-14 2026-02-14 20:40:19 +01:00
github-actions[bot]
c9dcdc90ec Merge pull request #11716 from firefly-iii/release-1771097648
🤖 Automatically merge the PR into the develop branch.
2026-02-14 20:34:19 +01:00
JC5
1f04888331 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 20:34:08 +01:00
James Cole
28c21ecb7e Update changelog 2026-02-14 20:29:19 +01:00
github-actions[bot]
7382030e61 Merge pull request #11711 from firefly-iii/release-1771075473
🤖 Automatically merge the PR into the develop branch.
2026-02-14 14:24:39 +01:00
JC5
a573e19dbc 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 14:24:33 +01:00
James Cole
b152a2c8d9 Fix #11710 2026-02-14 14:20:24 +01:00
github-actions[bot]
7e14c36fc0 Merge pull request #11709 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-14 13:38:49 +01:00
github-actions[bot]
58bfc7f8ce Merge pull request #11708 from firefly-iii/release-1771072714
🤖 Automatically merge the PR into the develop branch.
2026-02-14 13:38:43 +01:00
JC5
6b8c005108 🤖 Auto commit for release 'v6.4.20' on 2026-02-14 2026-02-14 13:38:34 +01:00
James Cole
ceb660fd02 Fix missing setting. Restore 8.4 for the moment. Very professional. 2026-02-14 13:33:29 +01:00
James Cole
7fac1e8614 Remove statistics. 2026-02-14 13:25:39 +01:00
github-actions[bot]
809e34f5fe Merge pull request #11707 from firefly-iii/release-1771071314
🤖 Automatically merge the PR into the develop branch.
2026-02-14 13:15:20 +01:00
JC5
73ca57c8c4 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 13:15:14 +01:00
James Cole
63088ffeb1 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-14 13:10:54 +01:00
James Cole
4e88ebd52d Make Firefly III PHP 8.5 only. 2026-02-14 13:10:41 +01:00
github-actions[bot]
85fd38d146 Merge pull request #11706 from firefly-iii/release-1771070819
🤖 Automatically merge the PR into the develop branch.
2026-02-14 13:07:16 +01:00
JC5
e83d7051eb 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 13:06:59 +01:00
James Cole
567f2dae17 Remove problematic comment repetition by Mago. 2026-02-14 13:02:48 +01:00
github-actions[bot]
c231ae4016 Merge pull request #11705 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-14 12:55:26 +01:00
github-actions[bot]
fb13a4cdcb Merge pull request #11704 from firefly-iii/release-1771070112
🤖 Automatically merge the PR into the develop branch.
2026-02-14 12:55:21 +01:00
JC5
724042e1d8 🤖 Auto commit for release 'v6.4.19' on 2026-02-14 2026-02-14 12:55:12 +01:00
James Cole
84a30c3c8f Missing ltter. 2026-02-14 12:49:16 +01:00
github-actions[bot]
6c9dac831a Merge pull request #11703 from firefly-iii/release-1771069573
🤖 Automatically merge the PR into the develop branch.
2026-02-14 12:46:20 +01:00
JC5
e605ddb779 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 12:46:13 +01:00
James Cole
4b19ed8f07 Add changelog. 2026-02-14 12:41:28 +01:00
James Cole
57bd8e09d4 Fix #11702 2026-02-14 10:31:01 +01:00
github-actions[bot]
32f1a7c9c2 Merge pull request #11701 from firefly-iii/release-1771057524
🤖 Automatically merge the PR into the develop branch.
2026-02-14 09:25:32 +01:00
JC5
fc5b0db43f 🤖 Auto commit for release 'develop' on 2026-02-14 2026-02-14 09:25:24 +01:00
James Cole
c2721f3f48 Fix https://github.com/firefly-iii/firefly-iii/issues/11700 2026-02-14 08:18:53 +01:00
James Cole
96291c9bce Fix https://github.com/firefly-iii/firefly-iii/issues/11684 2026-02-14 08:17:19 +01:00
James Cole
ab9400aaee Merge branches 'develop' and 'develop' of github.com:firefly-iii/firefly-iii into develop 2026-02-13 08:05:02 +01:00
James Cole
31d1ee11cb Fix https://github.com/firefly-iii/firefly-iii/issues/11694 2026-02-13 08:04:39 +01:00
github-actions[bot]
694dc3816c Merge pull request #11699 from firefly-iii/release-1770965983
🤖 Automatically merge the PR into the develop branch.
2026-02-13 07:59:49 +01:00
JC5
c647fa7b94 🤖 Auto commit for release 'develop' on 2026-02-13 2026-02-13 07:59:43 +01:00
James Cole
022d59bfdd Fix https://github.com/firefly-iii/firefly-iii/issues/11688 2026-02-13 07:33:30 +01:00
James Cole
057b82b471 fix rename of rules when bill name changes. 2026-02-11 07:00:59 +01:00
github-actions[bot]
2872c6592f Merge pull request #11692 from firefly-iii/release-1770751884
🤖 Automatically merge the PR into the develop branch.
2026-02-10 20:31:31 +01:00
JC5
fa02c15f55 🤖 Auto commit for release 'develop' on 2026-02-10 2026-02-10 20:31:24 +01:00
James Cole
c74c8a96b8 Validate directory permissions with a lot of debug logs. 2026-02-10 20:26:41 +01:00
github-actions[bot]
2c2ebae8fa Merge pull request #11691 from firefly-iii/release-1770727195
🤖 Automatically merge the PR into the develop branch.
2026-02-10 13:40:04 +01:00
JC5
aff9afb767 🤖 Auto commit for release 'develop' on 2026-02-10 2026-02-10 13:39:55 +01:00
James Cole
c8e95fe131 Fix audit log entry position. 2026-02-09 20:05:38 +01:00
James Cole
cef514c22b Merge pull request #11680 from firefly-iii/dependabot/composer/develop/mailersend/laravel-driver-3.0.0 2026-02-09 05:57:03 +01:00
github-actions[bot]
46d2ba3d3b Merge pull request #11681 from firefly-iii/release-1770609440
🤖 Automatically merge the PR into the develop branch.
2026-02-09 04:57:28 +01:00
JC5
45a0504eba 🤖 Auto commit for release 'develop' on 2026-02-09 2026-02-09 04:57:20 +01:00
dependabot[bot]
9711bc1d04 Bump mailersend/laravel-driver from 2.12.0 to 3.0.0
Bumps [mailersend/laravel-driver](https://github.com/mailersend/mailersend-laravel-driver) from 2.12.0 to 3.0.0.
- [Release notes](https://github.com/mailersend/mailersend-laravel-driver/releases)
- [Commits](https://github.com/mailersend/mailersend-laravel-driver/compare/v2.12.0...v3.0.0)

---
updated-dependencies:
- dependency-name: mailersend/laravel-driver
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-09 03:52:32 +00:00
James Cole
cf2343c0dd Catch errors in DB thing. 2026-02-08 21:07:50 +01:00
James Cole
34097cecf0 Fix viewrange. 2026-02-08 08:29:59 +01:00
github-actions[bot]
8735be2f6b Merge pull request #11676 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2026-02-08 08:27:47 +01:00
github-actions[bot]
0e8cc91308 Merge pull request #11675 from firefly-iii/release-1770535655
🤖 Automatically merge the PR into the develop branch.
2026-02-08 08:27:42 +01:00
JC5
5cbb311e4d 🤖 Auto commit for release 'v6.4.18' on 2026-02-08 2026-02-08 08:27:35 +01:00
James Cole
c334641b90 Add class reference. 2026-02-08 08:21:24 +01:00
James Cole
4c2356881d Remove unused debug logging. 2026-02-08 08:20:50 +01:00
James Cole
d9a0d06712 Clean up some debug logging. 2026-02-08 08:16:33 +01:00
158 changed files with 2140 additions and 1734 deletions

View File

@@ -43,6 +43,7 @@ return $config->setRules(
// rule sets
'@PHP8x3Migration' => true,
'@PHP8x4Migration' => true,
'@PHP8x5Migration' => true,
'@PhpCsFixer' => true,
'@PhpCsFixer:risky' => true,
'@PSR12' => true,

View File

@@ -1,5 +1,6 @@
{
"require": {
"php": ">=8.5.0",
"friendsofphp/php-cs-fixer": "^3.12"
}
}

View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f1e0b38af4ded66da271a99d2bff5be8",
"content-hash": "35b5ad9b3c4e1ffe78ef9bec73987499",
"packages": [
{
"name": "clue/ndjson-react",
@@ -402,16 +402,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.93.1",
"version": "v3.94.2",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a"
"reference": "7787ceff91365ba7d623ec410b8f429cdebb4f63"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b3546ab487c0762c39f308dc1ec0ea2c461fc21a",
"reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7787ceff91365ba7d623ec410b8f429cdebb4f63",
"reference": "7787ceff91365ba7d623ec410b8f429cdebb4f63",
"shasum": ""
},
"require": {
@@ -428,7 +428,7 @@
"react/event-loop": "^1.5",
"react/socket": "^1.16",
"react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0",
"symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
"symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0",
@@ -442,18 +442,18 @@
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0"
},
"require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.32",
"justinrainbow/json-schema": "^6.6",
"facile-it/paraunit": "^1.3.1 || ^2.7.1",
"infection/infection": "^0.32.3",
"justinrainbow/json-schema": "^6.6.4",
"keradus/cli-executor": "^2.3",
"mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.9",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.48",
"php-coveralls/php-coveralls": "^2.9.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.7",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.7",
"phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.51",
"symfony/polyfill-php85": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0",
"symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0"
"symfony/var-dumper": "^5.4.48 || ^6.4.32 || ^7.4.4 || ^8.0.4",
"symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0.1"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
@@ -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.93.1"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.94.2"
},
"funding": [
{
@@ -502,7 +502,7 @@
"type": "github"
}
],
"time": "2026-01-28T23:50:50+00:00"
"time": "2026-02-20T16:13:53+00:00"
},
{
"name": "psr/container",
@@ -1185,29 +1185,29 @@
},
{
"name": "sebastian/diff",
"version": "7.0.0",
"version": "8.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "7ab1ea946c012266ca32390913653d844ecd085f"
"reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f",
"reference": "7ab1ea946c012266ca32390913653d844ecd085f",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/a2b6d09d7729ee87d605a439469f9dcc39be5ea3",
"reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3",
"shasum": ""
},
"require": {
"php": ">=8.3"
"php": ">=8.4"
},
"require-dev": {
"phpunit/phpunit": "^12.0",
"phpunit/phpunit": "^13.0",
"symfony/process": "^7.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "7.0-dev"
"dev-main": "8.0-dev"
}
},
"autoload": {
@@ -1240,15 +1240,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/7.0.0"
"source": "https://github.com/sebastianbergmann/diff/tree/8.0.0"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/diff",
"type": "tidelift"
}
],
"time": "2025-02-07T04:55:46+00:00"
"time": "2026-02-06T04:42:27+00:00"
},
{
"name": "symfony/console",
@@ -2671,7 +2683,9 @@
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform": {
"php": ">=8.5.0"
},
"platform-dev": {},
"plugin-api-version": "2.9.0"
}

7
.github/mergify.yml vendored
View File

@@ -1,11 +1,4 @@
pull_request_rules:
- name: Make sure PR are up to date before merging
description: This automatically updates PRs when they are out-of-date with the
base branch to avoid semantic conflicts (next step is using a merge
queue).
conditions: []
actions:
update:
- name: Close all on main
conditions:
- base=main

View File

@@ -10,7 +10,7 @@ on:
phpversion:
description: 'PHP version'
required: true
default: '8.4'
default: '8.5'
schedule:
- cron: '0 3 * * MON'
@@ -42,7 +42,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ github.event.inputs.phpversion || '8.4' }}
php-version: ${{ github.event.inputs.phpversion || '8.5' }}
extensions: mbstring, intl, zip, bcmath
- name: Switch and pull
run: |
@@ -177,7 +177,7 @@ jobs:
rm -rf vendor composer.lock
composer update --no-scripts --no-plugins -q
mago format || true
mago analyze --reporting-format=github || true
# mago analyze --reporting-format=github || true
sudo chown -R runner:docker resources/lang
.ci/phpcs.sh || true
- name: Calculate variables

View File

@@ -167,7 +167,7 @@ class BasicController extends Controller
],
];
$newIncomes = [
$primary->id => [
$primary->id => [
'currency_id' => $primary->id,
'currency_code' => $primary->code,
'currency_symbol' => $primary->symbol,
@@ -176,7 +176,7 @@ class BasicController extends Controller
],
];
$sums = [
$primary->id => [
$primary->id => [
'currency_id' => $primary->id,
'currency_code' => $primary->code,
'currency_symbol' => $primary->symbol,
@@ -244,7 +244,7 @@ class BasicController extends Controller
// create objects for big array.
$return[] = [
'key' => sprintf('balance-in-%s', $currency->code),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $sums[$currencyId]['sum'] ?? '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -258,7 +258,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $expenses[$currencyId]['sum'] ?? '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -270,7 +270,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('earned-in-%s', $currency->code),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $incomes[$currencyId]['sum'] ?? '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -286,7 +286,7 @@ class BasicController extends Controller
// create objects for big array.
$return[] = [
'key' => sprintf('balance-in-%s', $currency->code),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -298,7 +298,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -310,7 +310,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('earned-in-%s', $currency->code),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -345,7 +345,7 @@ class BasicController extends Controller
$currencies[$currencyId] ??= $this->currencyRepos->find($currencyId);
$return[$currencyId] = [
'key' => sprintf('left-to-spend-in-%s', $currencies[$currencyId]->code),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currencies[$currencyId]->symbol]),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currencies[$currencyId]->symbol]),
'no_available_budgets' => false,
'monetary_value' => $availableBudget,
'currency_id' => (string) $currencies[$currencyId]->id,
@@ -386,7 +386,7 @@ class BasicController extends Controller
$return[$currencyId] = [
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
'no_available_budgets' => false,
'monetary_value' => $leftToSpend,
'currency_id' => (string) $row['currency_id'],
@@ -493,7 +493,7 @@ class BasicController extends Controller
// return stuff
$return[] = [
'key' => sprintf('net-worth-in-%s', $data['currency_code']),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]),
'monetary_value' => $amount,
'currency_id' => (string) $data['currency_id'],
'currency_code' => $data['currency_code'],
@@ -507,7 +507,7 @@ class BasicController extends Controller
if (0 === count($return)) {
$return[] = [
'key' => sprintf('net-worth-in-%s', $this->primaryCurrency->code),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $this->primaryCurrency->symbol]),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $this->primaryCurrency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $this->primaryCurrency->id,
'currency_code' => $this->primaryCurrency->code,
@@ -533,7 +533,7 @@ class BasicController extends Controller
*/
$paidAmount = $this->billRepository->sumPaidInRange($start, $end);
$unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end);
$currencies = [$this->primaryCurrency->id => $this->primaryCurrency];
$currencies = [$this->primaryCurrency->id => $this->primaryCurrency];
if ($this->convertToPrimary) {
$converter = new ExchangeRateConverter();
@@ -598,7 +598,7 @@ class BasicController extends Controller
$amount = bcmul((string) $info['sum'], '-1');
$return[] = [
'key' => sprintf('bills-paid-in-%s', $info['code']),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $info['symbol']]),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $info['symbol']]),
'monetary_value' => $amount,
'currency_id' => (string) $info['id'],
'currency_code' => $info['code'],
@@ -617,7 +617,7 @@ class BasicController extends Controller
$amount = bcmul((string) $info['sum'], '-1');
$return[] = [
'key' => sprintf('bills-unpaid-in-%s', $info['code']),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $info['symbol']]),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $info['symbol']]),
'monetary_value' => $amount,
'currency_id' => (string) $info['id'],
'currency_code' => $info['code'],
@@ -636,7 +636,7 @@ class BasicController extends Controller
$return[] = [
'key' => sprintf('bills-paid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@@ -648,7 +648,7 @@ class BasicController extends Controller
];
$return[] = [
'key' => sprintf('bills-unpaid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,

View File

@@ -61,7 +61,7 @@ class TransactionRequest extends FormRequest
public function rules(): array
{
return ['query' => ['required', 'min:1', 'max:255', 'json', new IsValidBulkClause(ClauseType::TRANSACTION)]];
return ['query' => ['required', 'min:1', 'max:255', 'json', new IsValidBulkClause(ClauseType::TRANSACTION->value)]];
}
public function withValidator(Validator $validator): void

View File

@@ -113,7 +113,7 @@ class UpdateRequest extends FormRequest
];
$this->booleanFields = ['reconciled'];
$this->arrayFields = ['tags'];
$data = ['batch_submission' => false];
$data = ['batch_submission' => false];
if ($this->has('transactions')) {
$data['transactions'] = $this->getTransactionData();
}

View File

@@ -63,6 +63,9 @@ class UpdateRequest extends FormRequest
{
/** @var TransactionCurrency $currency */
$currency = $this->route()->parameter('currency_code');
if (is_string($currency)) {
$currency = TransactionCurrency::whereCode($currency)->first();
}
return [
'name' => sprintf('min:1|max:255|unique:transaction_currencies,name,%d', $currency->id),

View File

@@ -56,8 +56,8 @@ class CorrectsGroupAccounts extends Command
}
}
$flags = new TransactionGroupEventFlags();
$flags->applyRules = true;
$flags->fireWebhooks = true;
$flags->applyRules = false;
$flags->fireWebhooks = false;
$flags->recalculateCredit = true;
$objects = new TransactionGroupEventObjects();
foreach ($groups as $groupId) {

View File

@@ -68,7 +68,7 @@ class CorrectsMetaDataFields extends Command
private function rename(string $original, string $update): void
{
$total = DB::table('journal_meta')->where('name', '=', $original)->update(['name' => $update]);
$total = DB::table('journal_meta')->where('name', '=', $original)->update(['name' => $update]);
$this->count += $total;
}
}

View File

@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
/*
* RollbackSingleMigration.php
* Copyright (c) 2026 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 Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class RollbacksSingleMigration extends Command
{
use ShowsFriendlyMessages;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'correction:rollback-single-migration {--force}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes the last entry from the migration table. ';
/**
* Execute the console command.
*/
public function handle(): int
{
$entry = DB::table('migrations')->orderBy('id', 'DESC')->first();
if (null === $entry) {
$this->friendlyError('There are no more database migrations to rollback.');
return Command::FAILURE;
}
$this->friendlyLine(sprintf('This command will remove the database migration entry called "%s" from your database.', $entry->migration));
$this->friendlyLine('This does not change the database in anyway. It makes Firefly III forget it made the changes in this particular migration.');
$this->friendlyLine('');
$this->friendlyLine('If you run "php artisan migrate" after doing this, Firefly III will try to run the database migration again.');
$this->friendlyLine('Missing tables or indices will be created.');
$this->friendlyLine('This may not work, or give you warnings, but if you have a botched database it may restore it again.');
$this->friendlyLine('');
$this->friendlyLine('If this doesn\'t work, run the command a few times to remove more rows and try "php artisan migrate" again.');
$this->friendlyLine('');
$res = true;
if (!$this->option('force')) {
$this->friendlyWarning('Use this command at your own risk.');
$res = $this->confirm('Are you sure you want to continue?');
}
if ($res) {
DB::table('migrations')->where('id', (int) $entry->id)->delete();
$this->friendlyInfo(sprintf('Database migration #%d ("%s") is deleted.', $entry->id, $entry->migration));
$this->friendlyLine('');
$this->friendlyLine('Try running "php artisan migrate" now.');
$this->friendlyLine('');
}
if (!$res) {
$this->friendlyError('User cancelled, will not delete anything.');
}
return Command::SUCCESS;
}
}

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class ValidatesFilePermissions extends Command
{
@@ -50,26 +51,39 @@ class ValidatesFilePermissions extends Command
*/
public function handle(): int
{
Log::debug(sprintf('Start of %s', $this->signature));
$directories = [storage_path('upload')];
$errors = false;
/** @var string $directory */
foreach ($directories as $directory) {
Log::debug(sprintf('Processing directory: %s', $directory));
if (!is_dir($directory)) {
$this->friendlyError(sprintf('Directory "%s" cannot found. It is necessary to allow files to be uploaded.', $directory));
$errors = true;
$message = sprintf('Directory "%s" cannot found. It is necessary to allow files to be uploaded.', $directory);
Log::error($message);
$this->friendlyError($message);
$errors = true;
continue;
}
Log::debug('It is a directory!');
if (!is_writable($directory)) {
$this->friendlyError(sprintf('Directory "%s" is not writeable. Uploading attachments may fail silently.', $directory));
$errors = true;
$message = sprintf('Directory "%s" is not writeable. Uploading attachments may fail silently.', $directory);
$this->friendlyError($message);
Log::error($message);
$errors = true;
}
Log::debug('It is writeable!');
Log::debug(sprintf('Done processing %s', $directory));
}
Log::debug('Done with loop.');
if (false === $errors) {
Log::debug('No errors.');
$this->friendlyInfo('All necessary file paths seem to exist, and are writeable.');
}
Log::debug(sprintf('End of %s', $this->signature));
return self::SUCCESS;
}
}

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Console\Commands\Tools\VerifiesDatabaseConnectionTrait;
use Illuminate\Console\Command;
use PDO;
use PDOException;
@@ -32,6 +33,7 @@ use PDOException;
class CreatesDatabase extends Command
{
use ShowsFriendlyMessages;
use VerifiesDatabaseConnectionTrait;
protected $description = 'Tries to create the database if it doesn\'t exist yet.';
@@ -39,21 +41,27 @@ class CreatesDatabase extends Command
public function handle(): int
{
if ('mysql' !== env('DB_CONNECTION')) { // @phpstan-ignore larastan.noEnvCallsOutsideOfConfig */
$this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));
$connected = $this->verifyDatabaseConnection();
if (!$connected) {
$this->friendlyError('Failed to connect to the database. Is it up?');
return Command::FAILURE;
}
if ('mysql' !== config('database.default')) {
$this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', config('database.default')));
return 0;
}
// try to set up a raw connection:
$exists = false;
$dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST'), env('DB_PORT'));
$exists = false;
$dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST'), env('DB_PORT'));
if ('' !== (string) env('DB_SOCKET')) {
$dsn = sprintf('mysql:unix_socket=%s;charset=utf8mb4', env('DB_SOCKET'));
}
$this->friendlyLine(sprintf('DSN is %s', $dsn));
$options = [
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
@@ -71,7 +79,7 @@ class CreatesDatabase extends Command
// only continue when no error.
// with PDO, try to list DB's (
/** @var array $stmt */
$stmt = $pdo->query('SHOW DATABASES;');
$stmt = $pdo->query('SHOW DATABASES;');
// slightly more complex but less error-prone.
foreach ($stmt as $row) {
$name = $row['Database'] ?? false;

View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/*
* ChecksForUpdates.php
* Copyright (c) 2026 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\Tools;
use Carbon\Carbon;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Console\Command;
class ChecksForUpdates extends Command
{
use ShowsFriendlyMessages;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:check-for-updates {--force}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Checks for Firefly III updates';
/**
* Execute the console command.
*/
public function handle(): int
{
$build = Carbon::createFromTimestamp(config('firefly.build_time'), config('app.timezone'));
$version = config('firefly.version');
$this->friendlyLine(sprintf('You are running version "%s", built on %s', $version, $build->format('Y-m-d H:i')));
$permission = FireflyConfig::get('permission_update_check', -1)->data;
if (1 !== $permission && false === $this->option('force')) {
$this->friendlyWarning('Checking for updates is disabled. To overrule, use --force.');
return Command::SUCCESS;
}
if (str_contains(config('firefly.version'), 'develop')) {
$this->friendlyWarning('You are running a development version.');
}
/** @var UpdateRequestInterface $request */
$request = app(UpdateRequestInterface::class);
// stable, alpha or beta
$info = $request->getUpdateInformation($version, $build, 'stable');
if ('' !== $info->getError()) {
$this->friendlyError($info->getError());
return Command::FAILURE;
}
if (!$info->isNewVersionAvailable()) {
$this->friendlyInfo(trans('firefly.no_new_release_available'));
return Command::SUCCESS;
}
// if running develop, slightly different message.
if (str_contains($version, 'develop')) {
$this->friendlyInfo(trans('firefly.update_current_dev_older', ['version' => $version, 'new_version' => $info->getNewVersion()]));
return Command::SUCCESS;
}
$this->friendlyInfo(trans('firefly.update_new_version_alert', [
'your_version' => $version,
'new_version' => $info->getNewVersion(),
'date' => $info->getPublishedAt()->format('Y-m-d H:i:s'),
]));
return Command::SUCCESS;
}
}

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* VerifiesDatabaseConnection.php
* Copyright (c) 2026 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\Tools;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command;
class VerifiesDatabaseConnection extends Command
{
use ShowsFriendlyMessages;
use VerifiesDatabaseConnectionTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:verify-database-connection';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command tries to connect to the database.';
/**
* Execute the console command.
*/
public function handle(): int
{
$connected = $this->verifyDatabaseConnection();
if ($connected) {
$this->friendlyPositive('Connected to the database.');
return Command::SUCCESS;
}
$this->friendlyError('Failed to connect to the database. Is it up?');
return Command::FAILURE;
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/*
* VerifiesDatabaseConnectionTrait.php
* Copyright (c) 2026 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\Tools;
use Exception;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
trait VerifiesDatabaseConnectionTrait
{
protected function verifyDatabaseConnection(): bool
{
$loops = 30;
$loop = 0;
$queries = ['pgsql' => 'SELECT * FROM pg_catalog.pg_tables;', 'sqlite' => 'SELECT name FROM sqlite_schema;', 'mysql' => 'SHOW TABLES;'];
$default = config('database.default');
if (!array_key_exists($default, $queries)) {
$this->friendlyWarning(sprintf('Cannot validate database connection for "%s"', $default));
return true;
}
$query = $queries[$default];
$connected = false;
Log::debug(sprintf('Connecting to database "%s"...', config('database.default')));
while (!$connected && $loop < $loops) {
try {
DB::select($query);
$connected = true;
} catch (QueryException $e) {
Log::error(sprintf('Loop #%d: connection failed: %s', $loop, $e->getMessage()));
$this->friendlyWarning(sprintf('Database connection attempt #%d failed. Sleep for 10 seconds...', $loop + 1));
sleep(10);
} catch (Exception $e) {
Log::error(sprintf('Loop #%d: not connected yet because of a %s: %s', $loop, get_class($e), $e->getMessage()));
$this->friendlyWarning(sprintf('Database connection attempt #%d failed. Sleep for 10 seconds...', $loop + 1));
sleep(10);
}
++$loop;
}
return $connected;
}
}

View File

@@ -110,7 +110,7 @@ class UpgradesBillsToRules extends Command
'active' => true,
'strict' => false,
'stop_processing' => false, // field is no longer used.
'title' => (string) trans('firefly.rule_for_bill_title', ['name' => $bill->name], $languageString),
'title' => (string) trans('firefly.rule_for_bill_title', ['name' => $bill->name], $languageString),
'description' => (string) trans('firefly.rule_for_bill_description', ['name' => $bill->name], $languageString),
'trigger' => 'store-journal',
'triggers' => [['type' => 'description_contains', 'value' => $match]],

View File

@@ -94,6 +94,7 @@ class UpgradesDatabase extends Command
private function callInitialCommands(): void
{
$this->call('firefly-iii:verify-database-connection');
$this->call('migrate', ['--seed' => true, '--force' => true, '--no-interaction' => true]);
$this->call('upgrade:600-pgsql-sequences');
$this->call('upgrade:480-decrypt-all');

View File

@@ -1,69 +0,0 @@
<?php
/**
* Kernel.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Log;
use Override;
/**
* File to make sure commands work.
*/
class Kernel extends ConsoleKernel
{
/**
* Register the commands for the application.
*/
#[Override]
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
/**
* Define the application's command schedule.
*/
#[Override]
protected function schedule(Schedule $schedule): void
{
$schedule->call(static function (): void {
Log::error('Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions at https://docs.firefly-iii.org/');
echo "\n";
echo '------------';
echo "\n";
echo wordwrap('Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions here:');
echo "\n";
echo 'https://docs.firefly-iii.org/';
echo "\n\n";
echo 'Disable this cron job!';
echo "\n";
echo '------------';
echo "\n";
})->daily();
}
}

View File

@@ -1,50 +0,0 @@
<?php
/*
* AccountBalance.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Entities;
use FireflyIII\Models\Account;
class AccountBalance
{
public string $amount;
public string $currencyId;
public string $id;
public static function fromArray(): self
{
$balance = new self();
$balance->id = (string) random_int(1, 1000);
// $balance->name = (string) random_int(1, 1000);
$balance->amount = (string) random_int(1, 1000);
$balance->currencyId = '1';
return $balance;
}
public function getAccount(): Account
{
return Account::inRandomOrder()->first();
}
}

View File

@@ -27,9 +27,9 @@ namespace FireflyIII\Enums;
/**
* Class ClauseType
*/
class ClauseType
enum ClauseType: string
{
public const string TRANSACTION = 'transaction';
public const string UPDATE = 'update';
public const string WHERE = 'where';
case TRANSACTION = 'transaction';
case UPDATE = 'update';
case WHERE = 'where';
}

View File

@@ -1,8 +1,10 @@
<?php
declare(strict_types=1);
/*
* api-noauth.php
* Copyright (c) 2021 james@firefly-iii.org
* UpdatedExistingBill.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,20 +22,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;
use Illuminate\Support\Facades\Route;
// Cron job API routes:
use FireflyIII\Http\Middleware\AcceptHeaders;
use FireflyIII\Events\Event;
use FireflyIII\Models\Bill;
use Illuminate\Queue\SerializesModels;
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\System',
'prefix' => '',
'as' => 'api.v1.cron.',
'middleware' => [AcceptHeaders::class],
],
static function (): void {
Route::get('{cliToken}', ['uses' => 'CronController@cron', 'as' => 'index']);
}
);
class UpdatedExistingBill extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(
public Bill $bill,
public array $oldData
) {}
}

View File

@@ -25,13 +25,14 @@ declare(strict_types=1);
namespace FireflyIII\Events\Model\TransactionGroup;
use FireflyIII\Events\Event;
use Illuminate\Support\Facades\Log;
class UserRequestedBatchProcessing extends Event
{
public TransactionGroupEventObjects $objects;
public function __construct(
public TransactionGroupEventFlags $flags
) {
Log::debug(__METHOD__);
$this->objects = new TransactionGroupEventObjects();
}
}

View File

@@ -157,7 +157,7 @@ class Handler extends ExceptionHandler
return response()->json([
'message' => sprintf('Validation exception: %s', $e->getMessage()),
'errors' => ['field' => 'Field is invalid'],
'errors' => ['field' => 'Field is invalid'],
], $errorCode);
}
@@ -208,6 +208,7 @@ class Handler extends ExceptionHandler
}
Log::debug(sprintf('Error "%s" has no Firefly III treatment, parent will handle.', $e::class));
Log::error($e->getMessage());
return parent::render($request, $e);
}

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Generator\Chart\Basic;
use FireflyIII\Support\ChartColour;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Facades\Log;
/**
* Class ChartJsGenerator.
@@ -92,14 +93,21 @@ class ChartJsGenerator implements GeneratorInterface
*
* // it's five.
*/
public function multiSet(array $data): array
public function multiSet(array $data, array $labels = []): array
{
reset($data);
$first = current($data);
if (!is_array($first)) {
return [];
}
$labels = is_array($first['entries']) ? array_keys($first['entries']) : [];
Log::debug('Now in multiSet()');
if (0 !== count($labels)) {
Log::debug('Labels are given: ', $labels);
}
if (0 === count($labels)) {
$labels = is_array($first['entries']) ? array_keys($first['entries']) : [];
Log::debug('Labels are generated: ', $labels);
}
$chartData = [
'count' => count($data),

View File

@@ -58,7 +58,7 @@ interface GeneratorInterface
*
* // it's five.
*/
public function multiSet(array $data): array;
public function multiSet(array $data, array $labels = []): array;
/**
* Expects data as:.

View File

@@ -192,6 +192,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
case WebhookResponse::BUDGET->name:
$basicMessage['content'] = [];
if ($model instanceof Budget) {
$model->refresh();
$enrichment = new BudgetEnrichment();
$enrichment->setUser($model->user);
@@ -201,6 +202,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
$basicMessage['content'] = $transformer->transform($model);
}
if ($model instanceof BudgetLimit) {
$model->refresh();
$user = $model->budget->user;
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($user);
@@ -224,6 +226,8 @@ class StandardMessageGenerator implements MessageGeneratorInterface
break;
case WebhookResponse::TRANSACTIONS->name:
$model->refresh();
/** @var TransactionGroup $model */
$transformer = new TransactionGroupTransformer();
@@ -243,6 +247,8 @@ class StandardMessageGenerator implements MessageGeneratorInterface
break;
case WebhookResponse::ACCOUNTS->name:
$model->refresh();
/** @var TransactionGroup $model */
$accounts = $this->collectAccounts($model);
$enrichment = new AccountEnrichment();

View File

@@ -36,13 +36,13 @@ class ConvertsAmountToPrimaryAmount
$primaryAmountField = $params->primaryAmountField;
if (!Amount::convertToPrimary($params->user)) {
Log::debug(sprintf(
'User does not want to do conversion, no need to convert "%s" and store it in field "%s" for %s #%d.',
$params->amountField,
$params->primaryAmountField,
get_class($params->model),
$params->model->id
));
// Log::debug(sprintf(
// 'User does not want to do conversion, no need to convert "%s" and store it in field "%s" for %s #%d.',
// $params->amountField,
// $params->primaryAmountField,
// get_class($params->model),
// $params->model->id
// ));
$params->model->{$primaryAmountField} = null;
$params->model->saveQuietly();

View File

@@ -24,7 +24,9 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Update;
use Carbon\Carbon;
use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface;
use FireflyIII\Services\FireflyIIIOrg\Update\UpdateResponse;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Support\Facades\Log;
@@ -38,7 +40,7 @@ trait UpdateTrait
* 'message' => 'A new version is available.
* 'level' => 'info' / 'success' / 'error'
*/
public function getLatestRelease(): array
public function getLatestRelease(): UpdateResponse
{
Log::debug('Now in getLatestRelease()');
@@ -46,7 +48,9 @@ trait UpdateTrait
$checker = app(UpdateRequestInterface::class);
$channelConfig = FireflyConfig::get('update_channel', 'stable');
$channel = (string) $channelConfig->data;
$build = Carbon::createFromTimestamp(config('firefly.build_time'), config('app.timezone'));
$version = config('firefly.version');
return $checker->getUpdateInformation($channel);
return $checker->getUpdateInformation($version, $build, $channel);
}
}

View File

@@ -72,7 +72,7 @@ class DeleteController extends Controller
}
$typeName = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
$subTitle = (string) trans(sprintf('firefly.delete_%s_account', $typeName), ['name' => $account->name]);
$subTitle = (string) trans(sprintf('firefly.delete_%s_account', $typeName), ['name' => $account->name]);
$accountList = app('expandedform')->makeSelectListWithEmpty($this->repository->getAccountsByType([$account->accountType->type]));
$objectType = $typeName;
unset($accountList[$account->id]);

View File

@@ -86,7 +86,7 @@ class EditController extends Controller
}
$objectType = config('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = (string) trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]);
$subTitle = (string) trans(sprintf('firefly.edit_%s_account', $objectType), ['name' => $account->name]);
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
$roles = $this->getRoles();
$liabilityTypes = $this->getLiabilityTypes();

View File

@@ -137,7 +137,7 @@ class ReconcileController extends Controller
);
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
$subTitle = (string) trans('firefly.reconcile_account', ['account' => $account->name]);
$subTitle = (string) trans('firefly.reconcile_account', ['account' => $account->name]);
// various links
$transactionsUrl = route('accounts.reconcile.transactions', [$account->id, '%start%', '%end%']);

View File

@@ -219,7 +219,7 @@ class ShowController extends Controller
$page = (int) $request->get('page');
$pageSize = (int) Preferences::get('listPageSize', 50)->data;
$currency = $this->repository->getAccountCurrency($account) ?? $this->primaryCurrency;
$subTitle = (string) trans('firefly.all_journals_for_account', ['name' => $account->name]);
$subTitle = (string) trans('firefly.all_journals_for_account', ['name' => $account->name]);
$periods = new Collection();
$end->endOfDay();

View File

@@ -115,6 +115,7 @@ class ConfigurationController extends Controller
FireflyConfig::set('enable_external_map', $data['enable_external_map']);
FireflyConfig::set('enable_external_rates', $data['enable_external_rates']);
FireflyConfig::set('allow_webhooks', $data['allow_webhooks']);
FireflyConfig::set('enable_batch_processing', $data['enable_batch_processing']);
FireflyConfig::set('valid_url_protocols', $data['valid_url_protocols']);
FireflyConfig::set('is_demo_site', $data['is_demo_site']);

View File

@@ -94,7 +94,7 @@ class LinkController extends Controller
}
Log::channel('audit')->info(sprintf('User wants to delete link type #%d', $linkType->id));
$subTitle = (string) trans('firefly.delete_link_type', ['name' => $linkType->name]);
$subTitle = (string) trans('firefly.delete_link_type', ['name' => $linkType->name]);
$otherTypes = $this->repository->get();
$count = $this->repository->countJournals($linkType);
$moveTo = [];
@@ -141,7 +141,7 @@ class LinkController extends Controller
return redirect(route('settings.links.index'));
}
$subTitle = (string) trans('firefly.edit_link_type', ['name' => $linkType->name]);
$subTitle = (string) trans('firefly.edit_link_type', ['name' => $linkType->name]);
$subTitleIcon = 'fa-link';
Log::channel('audit')->info(sprintf('User wants to edit link type #%d', $linkType->id));
@@ -181,7 +181,7 @@ class LinkController extends Controller
*/
public function show(LinkType $linkType): Factory|\Illuminate\Contracts\View\View
{
$subTitle = (string) trans('firefly.overview_for_link', ['name' => $linkType->name]);
$subTitle = (string) trans('firefly.overview_for_link', ['name' => $linkType->name]);
$subTitleIcon = 'fa-link';
$links = $this->repository->getJournalLinks($linkType);

View File

@@ -114,8 +114,27 @@ class UpdateController extends Controller
public function updateCheck(): RedirectResponse
{
$release = $this->getLatestRelease();
$level = 'info';
$message = trans('firefly.no_new_release_available');
if ('' !== $release->getError()) {
$level = 'error';
$message = $release->getError();
}
if ($release->isNewVersionAvailable()) {
// if running develop, slightly different message.
if (str_contains(config('firefly.version'), 'develop')) {
$message = trans('firefly.update_current_dev_older', ['version' => config('firefly.version'), 'new_version' => $release->getNewVersion()]);
}
if (!str_contains(config('firefly.version'), 'develop')) {
$message = trans('firefly.update_new_version_alert', [
'your_version' => config('firefly.version'),
'new_version' => $release->getNewVersion(),
'date' => $release->getPublishedAt()->format('Y-m-d H:i:s'),
]);
}
}
session()->flash($release['level'], $release['message']);
session()->flash($level, $message);
return redirect(route('settings.update-check'));
}

View File

@@ -135,7 +135,7 @@ class UserController extends Controller
}
session()->forget('users.edit.fromUpdate');
$subTitle = (string) trans('firefly.edit_user', ['email' => $user->email]);
$subTitle = (string) trans('firefly.edit_user', ['email' => $user->email]);
$subTitleIcon = 'fa-user-o';
$currentUser = auth()->user();
$isAdmin = $this->repository->hasRole($user, 'owner');
@@ -216,7 +216,7 @@ class UserController extends Controller
{
$title = (string) trans('firefly.system_settings');
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = (string) trans('firefly.single_user_administration', ['email' => $user->email]);
$subTitle = (string) trans('firefly.single_user_administration', ['email' => $user->email]);
$subTitleIcon = 'fa-user';
$information = $this->repository->getUserData($user);

View File

@@ -131,7 +131,7 @@ class AttachmentController extends Controller
public function edit(Request $request, Attachment $attachment): Factory|\Illuminate\Contracts\View\View
{
$subTitleIcon = 'fa-pencil';
$subTitle = (string) trans('firefly.edit_attachment', ['name' => $attachment->filename]);
$subTitle = (string) trans('firefly.edit_attachment', ['name' => $attachment->filename]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('attachments.edit.fromUpdate')) {

View File

@@ -190,6 +190,14 @@ class LoginController extends Controller
*/
public function showLoginForm(Request $request): Factory|Redirector|RedirectResponse|View
{
if ('remote_user_guard' === config('auth.defaults.guard')) {
$message = sprintf(
'Firefly III is configured to use the "remote user guard", but was unable to link you to a user. Are you sure the "%s" header is in place?',
config('auth.guard_header')
);
return view('errors.error', ['message' => $message]);
}
Log::channel('audit')->info('Show login form (1.1).');
$count = DB::table('users')->count();
@@ -251,7 +259,7 @@ class LoginController extends Controller
*/
protected function sendFailedLoginResponse(Request $request): void
{
$exception = ValidationException::withMessages([$this->username() => [trans('auth.failed')]]);
$exception = ValidationException::withMessages([$this->username() => [trans('auth.failed')]]);
$exception->redirectTo = route('login');
throw $exception;

View File

@@ -82,7 +82,7 @@ class IndexController extends Controller
if ('last7' === $viewRange || 'last30' === $viewRange) {
$end->addDays(30);
}
if ('last90') {
if ('last90' === $viewRange) {
$end->addDays(90);
}

View File

@@ -71,7 +71,7 @@ class EditController extends Controller
*/
public function edit(Request $request, Budget $budget): Factory|\Illuminate\Contracts\View\View
{
$subTitle = (string) trans('firefly.edit_budget', ['name' => $budget->name]);
$subTitle = (string) trans('firefly.edit_budget', ['name' => $budget->name]);
$autoBudget = $this->repository->getAutoBudget($budget);
// auto budget types

View File

@@ -73,7 +73,7 @@ class BillController extends Controller
*/
foreach ($paid as $info) {
$amount = $info['sum'];
$label = (string) trans('firefly.paid_in_currency', ['currency' => $info['name']]);
$label = (string) trans('firefly.paid_in_currency', ['currency' => $info['name']]);
$chartData[$label] = ['amount' => $amount, 'currency_symbol' => $info['symbol'], 'currency_code' => $info['code']];
}
@@ -82,7 +82,7 @@ class BillController extends Controller
*/
foreach ($unpaid as $info) {
$amount = $info['sum'];
$label = (string) trans('firefly.unpaid_in_currency', ['currency' => $info['name']]);
$label = (string) trans('firefly.unpaid_in_currency', ['currency' => $info['name']]);
$chartData[$label] = ['amount' => $amount, 'currency_symbol' => $info['symbol'], 'currency_code' => $info['code']];
}

View File

@@ -505,14 +505,14 @@ class BudgetController extends Controller
$preferredRange = Navigation::preferredRangeFormat($start, $end);
$chartData = [
[
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency->name]),
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency->name]),
'type' => 'bar',
'entries' => [],
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
],
[
'label' => (string) trans('firefly.box_budgeted_in_currency', ['currency' => $currency->name]),
'label' => (string) trans('firefly.box_budgeted_in_currency', ['currency' => $currency->name]),
'type' => 'bar',
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,

View File

@@ -171,7 +171,7 @@ class BudgetReportController extends Controller
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.spent_in_specific_budget', ['budget' => $budget->name]),
(string) trans('firefly.spent_in_specific_budget', ['budget' => $budget->name]),
$currency['currency_name']
),
'type' => 'bar',

View File

@@ -281,6 +281,6 @@ class CategoryController extends Controller
}
}
return $this->generator->multiSet($chartData);
return $this->generator->multiSet($chartData, array_values($periods));
}
}

View File

@@ -45,11 +45,9 @@ class CategoryReportController extends Controller
use AugumentData;
use TransactionCalculation;
/** @var GeneratorInterface Chart generation methods. */
private $generator;
private GeneratorInterface $generator;
/** @var OperationsRepositoryInterface */
private $opsRepository;
private OperationsRepositoryInterface $opsRepository;
/**
* CategoryReportController constructor.
@@ -216,7 +214,7 @@ class CategoryReportController extends Controller
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.spent_in_specific_category', ['category' => $category->name]),
(string) trans('firefly.spent_in_specific_category', ['category' => $category->name]),
$currency['currency_name']
),
'type' => 'bar',
@@ -243,7 +241,7 @@ class CategoryReportController extends Controller
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.earned_in_specific_category', ['category' => $category->name]),
(string) trans('firefly.earned_in_specific_category', ['category' => $category->name]),
$currency['currency_name']
),
'type' => 'bar',

View File

@@ -162,11 +162,7 @@ class DoubleReportController extends Controller
$name = $this->getCounterpartName($accounts, $account->id, $account->name, $account->iban);
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.spent_in_specific_double', ['account' => $name]),
$currency['currency_name']
),
'label' => sprintf('%s (%s)', (string) trans('firefly.spent_in_specific_double', ['account' => $name]), $currency['currency_name']),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
@@ -188,11 +184,7 @@ class DoubleReportController extends Controller
$name = $this->getCounterpartName($accounts, $account->id, $account->name, $account->iban);
$chartData[$earnedKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.earned_in_specific_double', ['account' => $name]),
$currency['currency_name']
),
'label' => sprintf('%s (%s)', (string) trans('firefly.earned_in_specific_double', ['account' => $name]), $currency['currency_name']),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],

View File

@@ -212,7 +212,7 @@ class ReportController extends Controller
foreach ($data as $currency) {
Log::debug(sprintf('Now processing currency "%s"', $currency['currency_name']));
$income = [
'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
'type' => 'bar',
'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green
'currency_id' => $currency['currency_id'],
@@ -221,7 +221,7 @@ class ReportController extends Controller
'entries' => [],
];
$expense = [
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
'type' => 'bar',
'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red
'currency_id' => $currency['currency_id'],

View File

@@ -220,11 +220,7 @@ class TagReportController extends Controller
// add things to chart Data for each currency:
$spentKey = sprintf('%d-spent', $currency['currency_id']);
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.spent_in_specific_tag', ['tag' => $tag->tag]),
$currency['currency_name']
),
'label' => sprintf('%s (%s)', (string) trans('firefly.spent_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name']),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
@@ -247,11 +243,7 @@ class TagReportController extends Controller
// add things to chart Data for each currency:
$spentKey = sprintf('%d-earned', $currency['currency_id']);
$chartData[$spentKey] ??= [
'label' => sprintf(
'%s (%s)',
(string) trans('firefly.earned_in_specific_tag', ['tag' => $tag->tag]),
$currency['currency_name']
),
'label' => sprintf('%s (%s)', (string) trans('firefly.earned_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name']),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],

View File

@@ -80,6 +80,14 @@ abstract class Controller extends BaseController
View::share('FF_VERSION', config('firefly.version'));
View::share('FF_BUILD_TIME', config('firefly.build_time'));
// this breaks when running < PHP 8.5 and is totally intentional.
$input = ' James is cool';
$output = $input
|> trim(...)
|> (fn (string $string) => str_replace(' ', '-', $string))
|> (fn (string $string) => str_replace(['.', '/', '…'], '', $string))
|> strtolower(...);
// is webhooks enabled?
View::share(
'featuringWebhooks',

View File

@@ -183,11 +183,11 @@ class RecurrenceController extends Controller
$result = [
'daily' => ['label' => (string) trans('firefly.recurring_daily'), 'selected' => str_starts_with($preSelected, 'daily')],
$weekly => [
'label' => (string) trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]),
'label' => (string) trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]),
'selected' => str_starts_with($preSelected, 'weekly'),
],
$monthly => [
'label' => (string) trans('firefly.recurring_monthly', ['dayOfMonth' => $date->day]),
'label' => (string) trans('firefly.recurring_monthly', ['dayOfMonth' => $date->day]),
'selected' => str_starts_with($preSelected, 'monthly'),
],
$ndom => [
@@ -195,7 +195,7 @@ class RecurrenceController extends Controller
'selected' => str_starts_with($preSelected, 'ndom'),
],
$yearly => [
'label' => (string) trans('firefly.recurring_yearly', ['date' => $yearlyDate]),
'label' => (string) trans('firefly.recurring_yearly', ['date' => $yearlyDate]),
'selected' => str_starts_with($preSelected, 'yearly'),
],
];

View File

@@ -61,7 +61,7 @@ class DeleteController extends Controller
*/
public function delete(ObjectGroup $objectGroup): Factory|View
{
$subTitle = (string) trans('firefly.delete_object_group', ['title' => $objectGroup->title]);
$subTitle = (string) trans('firefly.delete_object_group', ['title' => $objectGroup->title]);
$piggyBanks = $objectGroup->piggyBanks()->count();
// put previous url in session

View File

@@ -64,7 +64,7 @@ class EditController extends Controller
*/
public function edit(ObjectGroup $objectGroup): Factory|View
{
$subTitle = (string) trans('firefly.edit_object_group', ['title' => $objectGroup->title]);
$subTitle = (string) trans('firefly.edit_object_group', ['title' => $objectGroup->title]);
$subTitleIcon = 'fa-pencil';
if (true !== session('object-groups.edit.fromUpdate')) {

View File

@@ -70,7 +70,7 @@ class EditController extends Controller
*/
public function edit(PiggyBank $piggyBank): Factory|\Illuminate\Contracts\View\View
{
$subTitle = (string) trans('firefly.update_piggy_title', ['name' => $piggyBank->name]);
$subTitle = (string) trans('firefly.update_piggy_title', ['name' => $piggyBank->name]);
$subTitleIcon = 'fa-pencil';
$note = $piggyBank->notes()->first();
// Flash some data to fill the form.

View File

@@ -80,7 +80,7 @@ class CreateController extends Controller
public function create(Request $request, ?RuleGroup $ruleGroup = null): Factory|\Illuminate\Contracts\View\View
{
$this->createDefaultRuleGroup();
$preFilled = ['strict' => true];
$preFilled = ['strict' => true];
$oldTriggers = [];
$oldActions = [];
@@ -159,7 +159,7 @@ class CreateController extends Controller
$this->createDefaultRuleGroup();
$preFilled = [
'strict' => true,
'title' => (string) trans('firefly.new_rule_for_bill_title', ['name' => $bill->name]),
'title' => (string) trans('firefly.new_rule_for_bill_title', ['name' => $bill->name]),
'description' => (string) trans('firefly.new_rule_for_bill_description', ['name' => $bill->name]),
];
@@ -221,7 +221,7 @@ class CreateController extends Controller
// collect pre-filled information:
$preFilled = [
'strict' => true,
'title' => (string) trans('firefly.new_rule_for_journal_title', ['description' => $journal->description]),
'title' => (string) trans('firefly.new_rule_for_journal_title', ['description' => $journal->description]),
'description' => (string) trans('firefly.new_rule_for_journal_description', ['description' => $journal->description]),
];

View File

@@ -70,7 +70,7 @@ class EditController extends Controller
$subTitle = (string) trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]);
$hasOldInput = null !== $request->old('_token');
$preFilled = ['active' => $hasOldInput ? (bool) $request->old('active') : $ruleGroup->active];
$preFilled = ['active' => $hasOldInput ? (bool) $request->old('active') : $ruleGroup->active];
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('rule-groups.edit.fromUpdate')) {
$this->rememberPreviousUrl('rule-groups.edit.url');

View File

@@ -87,7 +87,7 @@ class SearchController extends Controller
$excludedWords = $searcher->getExcludedWords();
$operators = $searcher->getOperators();
$invalidOperators = $searcher->getInvalidOperators();
$subTitle = (string) trans('breadcrumbs.search_result', ['query' => $fullQuery]);
$subTitle = (string) trans('breadcrumbs.search_result', ['query' => $fullQuery]);
return view('search.index', [
'words' => $words,

View File

@@ -58,10 +58,11 @@ class InstallController extends Controller
private array $upgradeCommands = [
// there are 5 initial commands
// Check 4 places: InstallController, Docker image, UpgradeDatabase, composer.json
'firefly-iii:create-database' => [],
'migrate' => ['--seed' => true, '--force' => true],
'generate-keys' => [], // an exception :(
'firefly-iii:upgrade-database' => [],
'firefly-iii:set-latest-version' => ['--james-is-cool' => true],
'firefly-iii:set-latest-version' => ['--james-is-cool' => true],
'firefly-iii:verify-security-alerts' => [],
];

View File

@@ -134,7 +134,7 @@ class TagController extends Controller
*/
public function edit(Tag $tag): Factory|\Illuminate\Contracts\View\View
{
$subTitle = (string) trans('firefly.edit_tag', ['tag' => $tag->tag]);
$subTitle = (string) trans('firefly.edit_tag', ['tag' => $tag->tag]);
$subTitleIcon = 'fa-tag';
$location = $this->repository->getLocation($tag);
@@ -289,7 +289,7 @@ class TagController extends Controller
$page = (int) $request->get('page');
$pageSize = (int) Preferences::get('listPageSize', 50)->data;
$periods = [];
$subTitle = (string) trans('firefly.all_journals_for_tag', ['tag' => $tag->tag]);
$subTitle = (string) trans('firefly.all_journals_for_tag', ['tag' => $tag->tag]);
$start = $this->repository->firstUseDate($tag) ?? today(config('app.timezone'));
$end = $this->repository->lastUseDate($tag) ?? today(config('app.timezone'));
$attachments = $this->repository->getAttachments($tag);

View File

@@ -103,7 +103,7 @@ class ConvertController extends Controller
$groupTitle = $group->title ?? $first->description;
$groupArray = $transformer->transformObject($group);
$subTitle = (string) trans('firefly.convert_to_'.$destinationType->type, ['description' => $groupTitle]);
$subTitle = (string) trans('firefly.convert_to_'.$destinationType->type, ['description' => $groupTitle]);
$subTitleIcon = 'fa-exchange';
// get a list of asset accounts and liabilities and stuff, in various combinations:

View File

@@ -77,7 +77,7 @@ class DeleteController extends Controller
throw new NotFoundHttpException();
}
$objectType = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$subTitle = (string) trans('firefly.delete_'.$objectType, ['description' => $group->title ?? $journal->description]);
$subTitle = (string) trans('firefly.delete_'.$objectType, ['description' => $group->title ?? $journal->description]);
$previous = Steam::getSafePreviousUrl();
// put previous url in session
Log::debug('Will try to remember previous URL');

View File

@@ -91,7 +91,7 @@ class EditController extends Controller
$title = $transactionGroup->transactionJournals()->count() > 1
? $transactionGroup->title
: $transactionGroup->transactionJournals()->first()->description;
$subTitle = (string) trans('firefly.edit_transaction_title', ['description' => $title]);
$subTitle = (string) trans('firefly.edit_transaction_title', ['description' => $title]);
$subTitleIcon = 'fa-plus';
$cash = $repository->getCashAccount();
$previousUrl = $this->rememberPreviousUrl('transactions.edit.url');

View File

@@ -103,10 +103,11 @@ class IndexController extends Controller
$firstJournal = $this->repository->firstNull();
$startPeriod = $firstJournal instanceof TransactionJournal ? $firstJournal->date : new Carbon();
$endPeriod = clone $end;
// limit to 3 years for the time being.
if (now()->diffInYears($startPeriod, true) > 3) {
$startPeriod = now()->subYears(3);
$endPeriod->endOfDay();
// limit to 6 years for the time being.
$max = 6;
if (now()->diffInYears($startPeriod, true) > $max) {
$startPeriod = now()->subYears($max);
}
$periods = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod);

View File

@@ -78,7 +78,7 @@ class EditController extends Controller
}
$subTitleIcon = 'fa-pencil';
$subTitle = (string) trans('breadcrumbs.edit_currency', ['name' => $currency->name]);
$subTitle = (string) trans('breadcrumbs.edit_currency', ['name' => $currency->name]);
$currency->symbol = htmlentities($currency->symbol);
// is currently enabled (for this user?)
@@ -91,7 +91,7 @@ class EditController extends Controller
// code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token');
$preFilled = ['enabled' => $hasOldInput ? (bool) $request->old('enabled') : $enabled];
$preFilled = ['enabled' => $hasOldInput ? (bool) $request->old('enabled') : $enabled];
$request->session()->flash('preFilled', $preFilled);
Log::channel('audit')->info('Edit currency.', $currency->toArray());

View File

@@ -39,7 +39,7 @@ class EditController extends Controller
public function edit(UserGroup $userGroup)
{
$title = (string) trans('firefly.administrations_page_title');
$subTitle = (string) trans('firefly.administrations_page_edit_sub_title', ['title' => $userGroup->title]);
$subTitle = (string) trans('firefly.administrations_page_edit_sub_title', ['title' => $userGroup->title]);
$mainTitleIcon = 'fa-book';
Log::debug(sprintf('Now at %s', __METHOD__));

View File

@@ -1,65 +0,0 @@
<?php
/**
* Kernel.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http;
use FireflyIII\Http\Middleware\Authenticate;
use FireflyIII\Http\Middleware\Binder;
use FireflyIII\Http\Middleware\InstallationId;
use FireflyIII\Http\Middleware\RedirectIfAuthenticated;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\TrimStrings;
use FireflyIII\Http\Middleware\TrustProxies;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\View\Middleware\ShareErrorsFromSession;
/**
* Class Kernel
*/
class Kernel extends HttpKernel
{
protected $middleware = [
// SecureHeaders::class,
CheckForMaintenanceMode::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
TrustProxies::class,
InstallationId::class,
];
protected $middlewareAliases = [
'auth' => Authenticate::class,
'auth.basic' => AuthenticateWithBasicAuth::class,
'bindings' => Binder::class,
'can' => Authorize::class,
'guest' => RedirectIfAuthenticated::class,
'throttle' => ThrottleRequests::class,
];
protected $middlewarePriority = [StartFireflySession::class, ShareErrorsFromSession::class, Authenticate::class, Binder::class, Authorize::class];
}

View File

@@ -82,22 +82,25 @@ class Authenticate
protected function authenticate($request, array $guards)
{
if (0 === count($guards)) {
// go for default guard:
// @noinspection PhpUndefinedMethodInspection
if ($this->auth->check()) {
// do an extra check on user object.
/** @noinspection PhpUndefinedMethodInspection */
/** @var User $user */
$user = $this->auth->authenticate();
Log::debug('in Authenticate::authenticate() with zero guards.');
// There are no guards defined, go for the default guard:
if (auth()->check()) {
Log::debug('User is authenticated.');
$user = auth()->user();
$this->validateBlockedUser($user, $guards);
}
return;
}
// @noinspection PhpUndefinedMethodInspection
return $this->auth->authenticate();
$this->auth->authenticate();
if (!$this->auth->check()) {
throw new AuthenticationException('The user is not logged in but must be.', $guards);
}
}
exit('five');
foreach ($guards as $guard) {
exit('six');
if ('api' !== $guard) {
$this->auth->guard($guard)->authenticate();
}
@@ -111,6 +114,7 @@ class Authenticate
}
}
exit('seven');
// this is a massive hack, but if the handler has the oauth exception
// at this point we can report its error instead of a generic one.
$message = 'Unauthenticated.';
@@ -143,5 +147,6 @@ class Authenticate
// @phpstan-ignore-line (thinks function is undefined)
throw new AuthenticationException('Blocked account.', $guards);
}
Log::debug(sprintf('User #%d is not blocked.', $user->id));
}
}

View File

@@ -32,7 +32,7 @@ use Override;
/**
* Class StartFireflySession.
*/
class StartFireflySession extends StartSession
class StartFireflyIIISession extends StartSession
{
/**
* Store the current URL for the request if necessary.

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/*
* UpdatesRulesForChangedBill.php
* Copyright (c) 2026 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\Listeners\Model\Bill;
use FireflyIII\Events\Model\Bill\UpdatedExistingBill;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
class UpdatesRulesForChangedBill implements ShouldQueue
{
public function handle(UpdatedExistingBill $event): void
{
// update rule actions.
if ($event->bill->name !== $event->oldData['name']) {
$this->updateBillTriggersAndActions($event->bill, $event->oldData);
}
}
private function updateBillTriggersAndActions(Bill $bill, array $oldData): void
{
Log::debug(sprintf('Now in updateBillTriggersAndActions(#%d)', $bill->id));
$repository = app(RuleRepositoryInterface::class);
$repository->setUser($bill->user);
$rules = $repository->getAll();
/** @var Rule $rule */
foreach ($rules as $rule) {
$this->updateRule($bill, $rule, $oldData);
}
}
private function updateRule(Bill $bill, Rule $rule, array $oldData): void
{
$triggers = ['bill_is', 'bill_ends', 'bill_starts', 'bill_contains'];
/** @var RuleTrigger $trigger */
foreach ($rule->ruleTriggers as $trigger) {
if (in_array($trigger->trigger_type, $triggers, true) && $trigger->trigger_value === $oldData['name']) {
Log::debug(sprintf('Updated trigger #%d in rule #%d to new subscription name', $trigger->id, $rule->id));
$trigger->trigger_value = $bill->name;
$trigger->save();
}
}
/** @var RuleAction $action */
foreach ($rule->ruleActions as $action) {
if ('link_to_bill' === $action->action_type && $action->action_value === $oldData['name']) {
Log::debug(sprintf('Updated action #%d in rule #%d to new subscription name', $action->id, $rule->id));
$action->action_value = $bill->name;
$action->save();
}
}
}
}

View File

@@ -40,7 +40,7 @@ trait SupportsGroupProcessingTrait
$first = $set->first();
$journalIds = implode(',', $array);
$user = $first->user;
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
// Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
// collect rules:
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
@@ -104,21 +104,13 @@ trait SupportsGroupProcessingTrait
$repository->deleteStatisticsForType(Category::class, $objects->categories, $dates);
$repository->deleteStatisticsForType(Tag::class, $objects->tags, $dates);
// remove if no stuff present:
// remove for no tag, no cat, etc.
if (0 === $objects->budgets->count()) {
Log::debug('No budgets, delete "no_category" stats.');
$repository->deleteStatisticsForPrefix('no_budget', $dates);
}
if (0 === $objects->categories->count()) {
Log::debug('No categories, delete "no_category" stats.');
$repository->deleteStatisticsForPrefix('no_category', $dates);
}
if (0 === $objects->tags->count()) {
Log::debug('No tags, delete "no_category" stats.');
$repository->deleteStatisticsForPrefix('no_tag', $dates);
}
Log::debug('Done with remove period statistics for all objects.');
// remove generic statistics:
$repository->deleteStatisticsForPrefix('all_', $dates);
// ALWAYS remove for no tag, no cat, etc.
$repository->deleteStatisticsForPrefix('no_budget', $dates);
$repository->deleteStatisticsForPrefix('no_category', $dates);
$repository->deleteStatisticsForPrefix('no_tag', $dates);
}
private function collectDatesFromJournals(Collection $journals): Collection

View File

@@ -76,8 +76,27 @@ class ChecksForNewVersion implements ShouldQueue
// last check time was more than a week ago.
Log::debug('Have not checked for a new version in a week!');
$release = $this->getLatestRelease();
$level = 'info';
$message = trans('firefly.no_new_release_available');
if ('' !== $release->getError()) {
$level = 'error';
$message = $release->getError();
}
if ($release->isNewVersionAvailable()) {
// if running develop, slightly different message.
if (str_contains(config('firefly.version'), 'develop')) {
$message = trans('firefly.update_current_dev_older', ['version' => config('firefly.version'), 'new_version' => $release->getNewVersion()]);
}
if (!str_contains(config('firefly.version'), 'develop')) {
$message = trans('firefly.update_new_version_alert', [
'your_version' => config('firefly.version'),
'new_version' => $release->getNewVersion(),
'date' => $release->getPublishedAt()->format('Y-m-d H:i:s'),
]);
}
}
session()->flash($release['level'], $release['message']);
session()->flash($level, $message);
FireflyConfig::set('last_update_check', Carbon::now()->getTimestamp());
}

View File

@@ -62,7 +62,7 @@ class NotifiesAboutNewInvitation implements ShouldQueue
$url = route('invite', [$invitee->invite_code]);
try {
Mail::to($email)->send(new InvitationMail($invitee, $admin, $url));
Mail::to($email)->send(new InvitationMail($email, $admin, $url));
} catch (Exception $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());

View File

@@ -149,12 +149,10 @@ class RecalculatesPrimaryCurrencyAmounts
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_group_id', $userGroup->id)
->where(static function (Builder $q): void {
$q
->whereNotNull('native_amount')
->orWhereNotNull('native_foreign_amount')
->orWhere('native_amount', '!=', '')
->orWhere('native_foreign_amount', '!=', '')
;
$q->whereNotNull('native_amount')->orWhereNotNull('native_foreign_amount');
if ('pgsql' !== config('database.default')) {
$q->orWhere('native_amount', '!=', '')->orWhere('native_foreign_amount', '!=', '');
}
})
->update(['native_amount' => null, 'native_foreign_amount' => null])
;

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Deprecated;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -32,65 +31,9 @@ class AccountType extends Model
{
use ReturnsIntegerIdTrait;
/** @deprecated */
#[Deprecated]
public const string ASSET = 'Asset account';
protected $casts = ['created_at' => 'datetime', 'updated_at' => 'datetime'];
/** @deprecated */
#[Deprecated]
public const string BENEFICIARY = 'Beneficiary account';
/** @deprecated */
#[Deprecated]
public const string CASH = 'Cash account';
/** @deprecated */
#[Deprecated]
public const string CREDITCARD = 'Credit card';
/** @deprecated */
#[Deprecated]
public const string DEBT = 'Debt';
/** @deprecated */
#[Deprecated]
public const string DEFAULT = 'Default account';
/** @deprecated */
#[Deprecated]
public const string EXPENSE = 'Expense account';
/** @deprecated */
#[Deprecated]
public const string IMPORT = 'Import account';
/** @deprecated */
#[Deprecated]
public const string INITIAL_BALANCE = 'Initial balance account';
/** @deprecated */
#[Deprecated]
public const string LIABILITY_CREDIT = 'Liability credit account';
/** @deprecated */
#[Deprecated]
public const string LOAN = 'Loan';
/** @deprecated */
#[Deprecated]
public const string MORTGAGE = 'Mortgage';
/** @deprecated */
#[Deprecated]
public const string RECONCILIATION = 'Reconciliation account';
/** @deprecated */
#[Deprecated]
public const string REVENUE = 'Revenue account';
protected $casts = ['created_at' => 'datetime', 'updated_at' => 'datetime'];
protected $fillable = ['type'];
protected $fillable = ['type'];
public function accounts(): HasMany
{

View File

@@ -24,7 +24,6 @@ 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;
@@ -39,20 +38,8 @@ class AutoBudget extends Model
use ReturnsIntegerIdTrait;
use SoftDeletes;
/** @deprecated */
#[Deprecated]
public const int AUTO_BUDGET_ADJUSTED = 3;
/** @deprecated */
#[Deprecated]
public const int AUTO_BUDGET_RESET = 1;
/** @deprecated */
#[Deprecated]
public const int AUTO_BUDGET_ROLLOVER = 2;
protected $casts = ['amount' => 'string', 'native_amount' => 'string'];
protected $fillable = ['budget_id', 'amount', 'period', 'native_amount'];
protected $casts = ['amount' => 'string', 'native_amount' => 'string'];
protected $fillable = ['budget_id', 'amount', 'period', 'native_amount'];
public function budget(): BelongsTo
{

View File

@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Deprecated;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
@@ -36,23 +35,7 @@ class RecurrenceRepetition extends Model
use ReturnsIntegerIdTrait;
use SoftDeletes;
/** @deprecated */
#[Deprecated]
public const int WEEKEND_DO_NOTHING = 1;
/** @deprecated */
#[Deprecated]
public const int WEEKEND_SKIP_CREATION = 2;
/** @deprecated */
#[Deprecated]
public const int WEEKEND_TO_FRIDAY = 3;
/** @deprecated */
#[Deprecated]
public const int WEEKEND_TO_MONDAY = 4;
protected $casts = [
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
@@ -62,9 +45,9 @@ class RecurrenceRepetition extends Model
'weekend' => 'int',
];
protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip'];
protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip'];
protected $table = 'recurrences_repetitions';
protected $table = 'recurrences_repetitions';
public function recurrence(): BelongsTo
{

View File

@@ -30,7 +30,6 @@ 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;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@@ -39,7 +38,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
#[ObservedBy([TransactionObserver::class])]
class Transaction extends Model
{
use HasFactory;
use ReturnsIntegerIdTrait;
use SoftDeletes;

View File

@@ -34,7 +34,6 @@ 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;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@@ -53,7 +52,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
#[ObservedBy([DeletedTransactionJournalObserver::class])]
class TransactionJournal extends Model
{
use HasFactory;
use ReturnsIntegerIdTrait;
use ReturnsIntegerUserIdTrait;
use SoftDeletes;

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Deprecated;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use Illuminate\Database\Eloquent\Model;
@@ -36,36 +35,8 @@ class TransactionType extends Model
use ReturnsIntegerIdTrait;
use SoftDeletes;
/** @deprecated */
#[Deprecated]
public const string DEPOSIT = 'Deposit';
/** @deprecated */
#[Deprecated]
public const string INVALID = 'Invalid';
/** @deprecated */
#[Deprecated]
public const string LIABILITY_CREDIT = 'Liability credit';
/** @deprecated */
#[Deprecated]
public const string OPENING_BALANCE = 'Opening balance';
/** @deprecated */
#[Deprecated]
public const string RECONCILIATION = 'Reconciliation';
/** @deprecated */
#[Deprecated]
public const string TRANSFER = 'Transfer';
/** @deprecated */
#[Deprecated]
public const string WITHDRAWAL = 'Withdrawal';
protected $casts = ['created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime'];
protected $fillable = ['type'];
protected $casts = ['created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime'];
protected $fillable = ['type'];
/**
* Route binder. Converts the key in the URL to the specified object (or throw 404).

View File

@@ -52,6 +52,7 @@ class NotificationSender
return;
}
Log::error('Could not send notification :(.');
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}

View File

@@ -56,7 +56,7 @@ class MFABackupFewLeftNotification extends Notification
*/
public function toMail(User $notifiable): MailMessage
{
$subject = (string) trans('email.mfa_few_backups_left_subject', ['count' => $this->count]);
$subject = (string) trans('email.mfa_few_backups_left_subject', ['count' => $this->count]);
$ip = Request::ip();
$host = Steam::getHostName($ip);
$userAgent = Request::userAgent();

View File

@@ -53,7 +53,7 @@ class MFAManyFailedAttemptsNotification extends Notification
*/
public function toMail(User $notifiable): MailMessage
{
$subject = (string) trans('email.mfa_many_failed_subject', ['count' => $this->count]);
$subject = (string) trans('email.mfa_many_failed_subject', ['count' => $this->count]);
$ip = Request::ip();
$host = Steam::getHostName($ip);
$userAgent = Request::userAgent();

View File

@@ -58,7 +58,7 @@ class SubscriptionsOverdueReminder extends Notification
$info = [];
$count = 0;
foreach ($this->overdue as $item) {
$current = ['bill' => $item['bill']];
$current = ['bill' => $item['bill']];
$current['pay_dates'] = array_map(static fn (string $date): string => new Carbon($date)->isoFormat((string) trans(
'config.month_and_day_moment_js'
)), $item['dates']['pay_dates']);

View File

View File

@@ -1,68 +0,0 @@
<?php
/*
* AccountPolicy.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Policies;
use FireflyIII\Models\Account;
use FireflyIII\User;
use Illuminate\Support\Facades\Log;
class AccountPolicy
{
public function create(): bool
{
return auth()->check();
}
/**
* TODO needs better authentication, also for group.
*/
public function view(User $user, Account $account): bool
{
return auth()->check() && $user->id === $account->user_id;
}
public function viewAccountBalances(User $user, Account $account): bool
{
return $this->view($user, $account);
}
/**
* Everybody can do this, but selection should limit to user.
*/
public function viewAny(): bool
{
Log::debug(__METHOD__);
return auth()->check();
}
/**
* Everybody can do this, but selection should limit to user.
*/
public function viewUser(User $user, Account $account): bool
{
return $this->view($user, $account);
}
}

View File

@@ -1,47 +0,0 @@
<?php
/*
* BalancePolicy.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Policies;
use FireflyIII\Models\Account;
use FireflyIII\User;
class BalancePolicy
{
/**
* TODO needs better authentication.
*/
public function view(User $user, Account $account): bool
{
return auth()->check() && $user->id === $account->user_id;
}
/**
* Everybody can do this, but selection should limit to user.
*/
public function viewAny(): bool
{
return auth()->check();
}
}

View File

@@ -1,59 +0,0 @@
<?php
/*
* UserPolicy.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Policies;
use FireflyIII\User;
class UserPolicy
{
/**
* TODO needs better authentication.
*/
public function view(User $user, User $user1): bool
{
return true;
// return auth()->check() && $user->id === $account->user_id;
}
public function viewAccounts(User $user): bool
{
return true;
// return auth()->check();
}
/**
* Everybody can do this, but selection should limit to user.
*
* @return true
*/
public function viewAny(): bool
{
return true;
// return auth()->check();
}
}

View File

@@ -34,10 +34,6 @@ use Laravel\Passport\Passport;
*/
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
// 'FireflyIII\Model' => 'FireflyIII\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*

View File

@@ -53,6 +53,7 @@ use FireflyIII\Repositories\UserGroup\UserGroupRepository;
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
use FireflyIII\Repositories\Webhook\WebhookRepository;
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use FireflyIII\Services\FireflyIIIOrg\Update\GitHubUpdateRequest;
use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequest;
use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface;
use FireflyIII\Services\Password\PwndVerifierV2;
@@ -191,7 +192,8 @@ class FireflyServiceProvider extends ServiceProvider
$this->app->bind(PopupReportInterface::class, PopupReport::class);
$this->app->bind(ReportHelperInterface::class, ReportHelper::class);
$this->app->bind(FiscalHelperInterface::class, FiscalHelper::class);
$this->app->bind(UpdateRequestInterface::class, UpdateRequest::class);
// $this->app->bind(UpdateRequestInterface::class, UpdateRequest::class);
$this->app->bind(UpdateRequestInterface::class, GitHubUpdateRequest::class);
// webhooks:
$this->app->bind(MessageGeneratorInterface::class, StandardMessageGenerator::class);

View File

@@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Providers;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\StartFireflyIIISession;
use Illuminate\Session\SessionManager;
use Illuminate\Support\ServiceProvider;
use Override;
@@ -43,7 +43,7 @@ class FireflySessionProvider extends ServiceProvider
$this->registerSessionDriver();
$this->app->singleton(StartFireflySession::class);
$this->app->singleton(StartFireflyIIISession::class);
}
/**

View File

@@ -42,20 +42,18 @@ class RouteServiceProvider extends ServiceProvider
#[Override]
public function boot(): void
{
$this->routes(function (): void {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'))
;
Route::prefix('api/v1/cron')
->middleware('api_basic')
->namespace($this->namespace)
->group(base_path('routes/api-noauth.php'))
;
Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php'));
});
// $this->routes(function (): void {
// Route::prefix('api')
// ->middleware('api')
// ->namespace($this->namespace)
// ->group(base_path('routes/api.php'))
// ;
// Route::prefix('api/v1/cron')
// ->middleware('api_basic')
// ->namespace($this->namespace)
// ->group(base_path('routes/api-noauth.php'))
// ;
// Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php'));
// });
}
}

View File

@@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Providers;
use FireflyIII\Http\Middleware\StartFireflySession;
use FireflyIII\Http\Middleware\StartFireflyIIISession;
use Illuminate\Session\SessionServiceProvider as BaseSessionServiceProvider;
use Override;
@@ -42,6 +42,6 @@ class SessionServiceProvider extends BaseSessionServiceProvider
$this->registerSessionDriver();
$this->app->singleton(StartFireflySession::class);
$this->app->singleton(StartFireflyIIISession::class);
}
}

View File

@@ -178,7 +178,7 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$currencies = [$primaryCurrency->id => $primaryCurrency];
$currencies = [$primaryCurrency->id => $primaryCurrency];
$report = ['accounts' => [], 'sums' => []];
/** @var array $journal */
@@ -232,7 +232,7 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$currencies = [$primaryCurrency->id => $primaryCurrency];
$currencies = [$primaryCurrency->id => $primaryCurrency];
$report = ['accounts' => [], 'sums' => []];
/** @var array $journal */

View File

@@ -96,7 +96,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
foreach ($set as $bill) {
if ($bill->order !== $current) {
$bill->order = $current;
$bill->save();
$bill->saveQuietly();
}
++$current;
}
@@ -613,7 +613,7 @@ class BillRepository implements BillRepositoryInterface, UserGroupInterface
public function setOrder(Bill $bill, int $order): void
{
$bill->order = $order;
$bill->save();
$bill->saveQuietly();
}
/**

View File

@@ -105,6 +105,22 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface, U
return $this->user->availableBudgets->find($id);
}
#[Override]
public function findInRange(TransactionCurrency $currency, Carbon $start, Carbon $end): Collection
{
/** @var null|AvailableBudget */
return $this->user
->availableBudgets()
->where('transaction_currency_id', $currency->id)
// start date OR end date must be the same or equal to start or END respectively.
->where(function ($q1) use ($start, $end): void {
$q1->whereBetween('start_date', [$start, $end]);
$q1->orWhereBetween('end_date', [$start, $end]);
})
->get(['available_budgets.*'])
;
}
/**
* Return a list of all available budgets (in all currencies) (for the selected period).
*/

View File

@@ -62,6 +62,11 @@ interface AvailableBudgetRepositoryInterface
public function findById(int $id): ?AvailableBudget;
/**
* Find existing ABs in this exact time range.
*/
public function findInRange(TransactionCurrency $currency, Carbon $start, Carbon $end): Collection;
/**
* Return a list of all available budgets (in all currencies) (for the selected period).
*/

View File

@@ -204,7 +204,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$currencySymbol = $primaryCurrency->symbol;
$currencyDecimalPlaces = $primaryCurrency->decimal_places;
$converter = new ExchangeRateConverter();
$currencies = [$currencyId => $primaryCurrency];
$currencies = [$currencyId => $primaryCurrency];
foreach ($journals as $journal) {
$amount = Steam::negative($journal['amount']);

View File

@@ -52,6 +52,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface, UserGroupIn
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->withoutCategory();
if ($accounts instanceof Collection && $accounts->count() > 0) {
$collector->setAccounts($accounts);
$collector->excludeDestinationAccounts($accounts); // to exclude withdrawals to liabilities.
}
$journals = $collector->getExtractedJournals();
$array = [];
@@ -97,6 +98,7 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface, UserGroupIn
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value])->withoutCategory();
if ($accounts instanceof Collection && $accounts->count() > 0) {
$collector->setAccounts($accounts);
$collector->excludeSourceAccounts($accounts); // to prevent income from liabilities.
}
$journals = $collector->getExtractedJournals();
$array = [];

View File

@@ -49,6 +49,9 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U
#[Override]
public function allInRangeForPrefix(string $prefix, Carbon $start, Carbon $end): Collection
{
Log::debug(sprintf('Collect all statistics where type starts with "%s"', $prefix));
Log::debug(sprintf('Between %s and %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
return $this->userGroup
->periodStatistics()
->where('type', 'LIKE', sprintf('%s%%', $prefix))
@@ -156,6 +159,7 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U
int $count,
string $amount
): PeriodStatistic {
Log::debug(sprintf('Store as type "%s"', sprintf('%s_%s', $prefix, $type)));
$stat = new PeriodStatistic();
$stat->transaction_currency_id = $currencyId;
$stat->user_group_id = $this->getUserGroup()->id;

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