Compare commits

...

124 Commits

Author SHA1 Message Date
github-actions[bot]
b670f81dcd Merge pull request #10875 from firefly-iii/release-1757313349
🤖 Automatically merge the PR into the develop branch.
2025-09-08 08:36:01 +02:00
JC5
7aac1cdf67 🤖 Auto commit for release 'develop' on 2025-09-08 2025-09-08 08:35:49 +02:00
Sander Dorigo
fa0ac8a16c Fix php files 2025-09-08 08:31:09 +02:00
James Cole
81cd89d66f Final nestor and phpstan fixes. 2025-09-07 17:42:16 +02:00
James Cole
f5c202543c Clean up code. 2025-09-07 17:31:08 +02:00
James Cole
034f437c6b Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-07 17:30:43 +02:00
James Cole
8550ba6138 Remove unused code. 2025-09-07 17:30:34 +02:00
github-actions[bot]
a51501025b Merge pull request #10872 from firefly-iii/release-1757249926
🤖 Automatically merge the PR into the develop branch.
2025-09-07 14:58:53 +02:00
JC5
a9d26e4586 🤖 Auto commit for release 'develop' on 2025-09-07 2025-09-07 14:58:46 +02:00
James Cole
949691935f Catch exceptions. 2025-09-07 14:54:44 +02:00
James Cole
4835b05304 Optimize currency search. 2025-09-07 14:49:49 +02:00
James Cole
cce5a73dd2 Clean up and fix phpstan issues. 2025-09-07 14:28:58 +02:00
github-actions[bot]
3152f635dd Merge pull request #10868 from firefly-iii/release-1757235851
🤖 Automatically merge the PR into the develop branch.
2025-09-07 11:04:21 +02:00
JC5
12e8651017 🤖 Auto commit for release 'develop' on 2025-09-07 2025-09-07 11:04:11 +02:00
James Cole
f88286c848 Merge branches 'develop' and 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-07 10:59:12 +02:00
James Cole
39cf0533d9 Fix various phpstan issues. 2025-09-07 10:59:07 +02:00
github-actions[bot]
6d82887960 Merge pull request #10867 from firefly-iii/release-1757224570
🤖 Automatically merge the PR into the develop branch.
2025-09-07 07:56:20 +02:00
JC5
262f1bae34 🤖 Auto commit for release 'develop' on 2025-09-07 2025-09-07 07:56:10 +02:00
James Cole
75dfdcc220 Fix final phpstan issue. 2025-09-07 07:51:35 +02:00
James Cole
c3a3bdf525 Fix phpstan issues. 2025-09-07 07:51:01 +02:00
James Cole
602df95f3c Fix phpstan issues. 2025-09-07 07:43:04 +02:00
James Cole
296a64e284 Fix phpstan issues 2025-09-07 07:31:00 +02:00
James Cole
b87b99a755 Fix phpstan issues. 2025-09-07 06:25:26 +02:00
James Cole
bf53f5d6b7 Fix various phpstan issues. 2025-09-05 05:09:46 +02:00
James Cole
bcbb868acc Update packages. 2025-09-04 19:46:52 +02:00
James Cole
b3a9e6569e Fix #10854 2025-09-04 06:26:28 +02:00
James Cole
f174f124ef Fix # 2025-09-04 06:26:12 +02:00
James Cole
8745377f31 Fix #10833 2025-09-04 06:22:18 +02:00
James Cole
cf76e93f31 Remove primary currency from views, add some debug for #10854 2025-09-04 06:22:04 +02:00
James Cole
19e36f6f6e Share primary currency to all pages. 2025-09-03 21:02:13 +02:00
James Cole
2a7bb6f048 Fix #10853 2025-09-03 20:34:40 +02:00
James Cole
536eacbc0c Fix sort params 2025-09-03 20:34:28 +02:00
James Cole
af78158d0b Fix minor issues. 2025-09-02 18:38:34 +02:00
github-actions[bot]
fb97910a34 Merge pull request #10848 from firefly-iii/release-1756787147
🤖 Automatically merge the PR into the develop branch.
2025-09-02 06:25:54 +02:00
JC5
66b4d14129 🤖 Auto commit for release 'develop' on 2025-09-02 2025-09-02 06:25:47 +02:00
James Cole
fd53047dbc Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-09-02 06:03:17 +02:00
James Cole
5f6fc1bab4 Fix #10846 2025-09-02 06:03:11 +02:00
github-actions[bot]
cd0b64bd24 Merge pull request #10845 from firefly-iii/release-1756752249
🤖 Automatically merge the PR into the develop branch.
2025-09-01 20:44:18 +02:00
JC5
91b26bce0e 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 20:44:09 +02:00
James Cole
26a8bd921d Fix columns, improve views. 2025-09-01 20:39:40 +02:00
github-actions[bot]
ad77d55c16 Merge pull request #10844 from firefly-iii/release-1756729274
🤖 Automatically merge the PR into the develop branch.
2025-09-01 14:21:21 +02:00
JC5
445a383dd0 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 14:21:14 +02:00
Sander Dorigo
1666af939e Fix null 2025-09-01 14:03:21 +02:00
github-actions[bot]
35447f5eee Merge pull request #10843 from firefly-iii/release-1756727799
🤖 Automatically merge the PR into the develop branch.
2025-09-01 13:56:46 +02:00
JC5
7c6fcb5731 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 13:56:39 +02:00
Sander Dorigo
fabd9bf765 Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop 2025-09-01 13:38:39 +02:00
Sander Dorigo
6352d26633 fix argument 2025-09-01 13:38:32 +02:00
github-actions[bot]
ebef145bd6 Merge pull request #10842 from firefly-iii/release-1756723300
🤖 Automatically merge the PR into the develop branch.
2025-09-01 12:41:48 +02:00
JC5
acc89eb5f9 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 12:41:40 +02:00
Sander Dorigo
6523596415 fix bad call 2025-09-01 12:36:36 +02:00
github-actions[bot]
b6c2d23116 Merge pull request #10838 from firefly-iii/release-1756697534
🤖 Automatically merge the PR into the develop branch.
2025-09-01 05:32:22 +02:00
JC5
2a123354f9 🤖 Auto commit for release 'develop' on 2025-09-01 2025-09-01 05:32:14 +02:00
James Cole
1e7ea4b76c Improve account list and view. 2025-08-31 19:20:02 +02:00
James Cole
d959526eb3 Fix #10837 2025-08-31 19:07:45 +02:00
James Cole
8846ee9091 Fix #10827 2025-08-30 07:51:28 +02:00
github-actions[bot]
6eb8d0fc8c Merge pull request #10826 from firefly-iii/release-1756445011
🤖 Automatically merge the PR into the develop branch.
2025-08-29 07:23:42 +02:00
JC5
1b0e16b6a5 🤖 Auto commit for release 'develop' on 2025-08-29 2025-08-29 07:23:32 +02:00
James Cole
2e4df28288 Less logging. 2025-08-29 06:51:20 +02:00
James Cole
f3b7a3015d Fix #10824 2025-08-27 19:00:12 +02:00
James Cole
5de5e08b1d Fix #10820 2025-08-26 16:05:36 +02:00
James Cole
0a116cd04c Add API autocomplete tests. 2025-08-25 17:17:51 +02:00
James Cole
fd32a692c1 Add some API tests. 2025-08-25 15:31:51 +02:00
James Cole
1ac762aba8 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-08-25 15:31:15 +02:00
James Cole
315dc532b6 Fix #10819 2025-08-25 15:31:07 +02:00
github-actions[bot]
e19ed1be15 Merge pull request #10817 from firefly-iii/release-1756092462
🤖 Automatically merge the PR into the develop branch.
2025-08-25 05:27:50 +02:00
JC5
cbb0621fd9 🤖 Auto commit for release 'develop' on 2025-08-25 2025-08-25 05:27:42 +02:00
James Cole
049cbab861 Remove superfluous logging. 2025-08-24 20:31:12 +02:00
James Cole
28b620fb5c Remove superfluous logging. 2025-08-24 20:30:40 +02:00
James Cole
c183f91ff6 Add issue to changelog 2025-08-24 20:29:39 +02:00
github-actions[bot]
172efae41c Merge pull request #10816 from firefly-iii/release-1756060004
🤖 Automatically merge the PR into the develop branch.
2025-08-24 20:26:54 +02:00
JC5
756e857ba0 🤖 Auto commit for release 'develop' on 2025-08-24 2025-08-24 20:26:44 +02:00
James Cole
1cde7aab0c Fix #10815 2025-08-24 20:14:02 +02:00
James Cole
2d67eece5d Various code cleanup. 2025-08-24 17:14:07 +02:00
James Cole
b1f79c4c0f Fix #10814 2025-08-24 13:31:00 +02:00
James Cole
33bd2ceae8 Fix amounts in transaction show. 2025-08-24 13:30:41 +02:00
James Cole
e68850f192 Fix changelog. 2025-08-24 06:51:48 +02:00
James Cole
450ac7e6ee Fix #10813 2025-08-24 06:51:28 +02:00
James Cole
91f52b5dbc Add copyright statements. 2025-08-23 11:26:04 +02:00
James Cole
eed2405d76 Update changelog before I forget about it. 2025-08-23 11:17:05 +02:00
github-actions[bot]
c956df7790 Merge pull request #10809 from firefly-iii/release-1755940492
🤖 Automatically merge the PR into the develop branch.
2025-08-23 11:15:02 +02:00
JC5
0fdccec6a8 🤖 Auto commit for release 'develop' on 2025-08-23 2025-08-23 11:14:52 +02:00
James Cole
8ded54d7a8 Fix #10808 2025-08-23 08:56:25 +02:00
James Cole
bb1b4ca5ca Fix #10807 2025-08-23 08:54:50 +02:00
James Cole
e90d60113b Extra validation and a new config variable for #10806 2025-08-23 08:47:34 +02:00
James Cole
d95dada0e0 Update changelog for #10804 2025-08-22 20:20:07 +02:00
James Cole
8722456595 Fix #10804 2025-08-22 20:19:52 +02:00
James Cole
b5ad226451 Remove unused headers. 2025-08-22 15:45:39 +02:00
James Cole
ddb0e66651 Update changelog. 2025-08-22 15:45:05 +02:00
James Cole
e802899608 Pre-filter expenses, fixes #10803 2025-08-22 11:53:08 +02:00
James Cole
0894d3bf42 Fix #10802 2025-08-22 11:09:17 +02:00
James Cole
80bcfd3bcd Small issues to fix #5532 2025-08-22 09:52:27 +02:00
James Cole
8c410f42bd Expand changelog. 2025-08-22 09:15:34 +02:00
James Cole
7a1021dffc Add spent + earned info to category chart. 2025-08-22 09:15:13 +02:00
github-actions[bot]
63883c9a84 Merge pull request #10801 from firefly-iii/release-1755840718
🤖 Automatically merge the PR into the develop branch.
2025-08-22 07:32:06 +02:00
JC5
50d7f9d1ec 🤖 Auto commit for release 'develop' on 2025-08-22 2025-08-22 07:31:58 +02:00
James Cole
ebc7ea0eb6 Fix amount display 2025-08-22 07:28:09 +02:00
github-actions[bot]
b905efd0aa Merge pull request #10800 from firefly-iii/release-1755839053
🤖 Automatically merge the PR into the develop branch.
2025-08-22 07:04:20 +02:00
JC5
93085599b7 🤖 Auto commit for release 'develop' on 2025-08-22 2025-08-22 07:04:13 +02:00
James Cole
8a8bbaf827 Remove test webhook, no longer necessary. 2025-08-22 06:57:25 +02:00
James Cole
96a66b894a Fix #10799 2025-08-22 06:49:16 +02:00
James Cole
1ba641c279 Prevent a loop. 2025-08-21 20:31:32 +02:00
James Cole
6c5ddfcb8a Add newsletter link. 2025-08-21 20:10:53 +02:00
James Cole
65ddc246dc Merge branch 'main' into develop 2025-08-21 20:07:32 +02:00
James Cole
e4aff5ff4c Update webhook code. 2025-08-21 20:07:12 +02:00
James Cole
dfece91541 Update label-actions.yml
Signed-off-by: James Cole <james@firefly-iii.org>
2025-08-21 08:28:54 +02:00
James Cole
6ddda13c3a Fix #10794 2025-08-20 10:13:23 +02:00
James Cole
46219c4678 Fix #10791 2025-08-20 09:09:38 +02:00
James Cole
a1595d0647 Fix #10790 2025-08-20 07:26:18 +02:00
James Cole
4ee9f9bb27 Fix collection and message generation. 2025-08-20 06:38:53 +02:00
James Cole
bcaa0bddea Update message generator. 2025-08-20 06:32:25 +02:00
James Cole
01cce49070 Expand webhook API, edit and create screen. 2025-08-20 06:22:55 +02:00
James Cole
293be04d40 Expand webhooks to support multiple delivery payloads, event triggers and responses. 2025-08-19 19:35:12 +02:00
James Cole
44a00ec8eb Use 6. 2025-08-19 06:41:07 +02:00
James Cole
d84e772945 Use nr. 5 2025-08-19 06:36:19 +02:00
James Cole
b475f6c51d Refer to nr 4 2025-08-19 06:32:26 +02:00
James Cole
2712662510 Refer to exact version. 2025-08-19 06:30:50 +02:00
James Cole
1f2eeba862 Refer to latest 2025-08-19 06:30:15 +02:00
James Cole
46a60134f4 Refer to JC5, 6. 2025-08-19 06:28:09 +02:00
James Cole
f3f7820816 Fix lock threads action. 2025-08-19 06:10:03 +02:00
github-actions[bot]
a57f8076b2 Merge pull request #10788 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-08-19 06:08:27 +02:00
github-actions[bot]
517afa2273 Merge pull request #10787 from firefly-iii/release-1755576495
🤖 Automatically merge the PR into the develop branch.
2025-08-19 06:08:22 +02:00
JC5
2142b23aec 🤖 Auto commit for release 'v6.3.2' on 2025-08-19 2025-08-19 06:08:15 +02:00
James Cole
1bec43b111 Expand changelog. 2025-08-19 06:04:22 +02:00
James Cole
d2978a5ee8 Remove references to deleted code. 2025-08-19 06:03:58 +02:00
397 changed files with 6048 additions and 5830 deletions

View File

@@ -151,16 +151,16 @@
},
{
"name": "composer/semver",
"version": "3.4.3",
"version": "3.4.4",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12"
"reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
"reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
"url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95",
"reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95",
"shasum": ""
},
"require": {
@@ -212,7 +212,7 @@
"support": {
"irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.4.3"
"source": "https://github.com/composer/semver/tree/3.4.4"
},
"funding": [
{
@@ -222,13 +222,9 @@
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2024-09-19T14:15:21+00:00"
"time": "2025-08-20T19:15:30+00:00"
},
{
"name": "composer/xdebug-handler",
@@ -406,16 +402,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.86.0",
"version": "v3.87.1",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36"
"reference": "2f5170365e2a422d0c5421f9c8818b2c078105f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4a952bd19dc97879b0620f495552ef09b55f7d36",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2f5170365e2a422d0c5421f9c8818b2c078105f6",
"reference": "2f5170365e2a422d0c5421f9c8818b2c078105f6",
"shasum": ""
},
"require": {
@@ -426,39 +422,38 @@
"ext-hash": "*",
"ext-json": "*",
"ext-tokenizer": "*",
"fidry/cpu-core-counter": "^1.2",
"fidry/cpu-core-counter": "^1.3",
"php": "^7.4 || ^8.0",
"react/child-process": "^0.6.6",
"react/event-loop": "^1.5",
"react/promise": "^3.2",
"react/promise": "^3.3",
"react/socket": "^1.16",
"react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
"symfony/console": "^5.4.47 || ^6.4.13 || ^7.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0",
"symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0",
"symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0",
"symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0",
"symfony/polyfill-mbstring": "^1.32",
"symfony/polyfill-php80": "^1.32",
"symfony/polyfill-php81": "^1.32",
"symfony/process": "^5.4.47 || ^6.4.20 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0"
"symfony/console": "^5.4.47 || ^6.4.24 || ^7.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/polyfill-mbstring": "^1.33",
"symfony/polyfill-php80": "^1.33",
"symfony/polyfill-php81": "^1.33",
"symfony/process": "^5.4.47 || ^6.4.24 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0"
},
"require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.6",
"facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.29.14",
"justinrainbow/json-schema": "^5.3 || ^6.4",
"justinrainbow/json-schema": "^6.5",
"keradus/cli-executor": "^2.2",
"mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.8",
"php-cs-fixer/accessible-object": "^1.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25",
"symfony/polyfill-php84": "^1.32",
"symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1",
"symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1"
"phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
"symfony/polyfill-php84": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2",
"symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
@@ -499,7 +494,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.86.0"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.87.1"
},
"funding": [
{
@@ -507,7 +502,7 @@
"type": "github"
}
],
"time": "2025-08-13T22:36:21+00:00"
"time": "2025-09-02T15:27:36+00:00"
},
{
"name": "psr/container",
@@ -959,23 +954,23 @@
},
{
"name": "react/promise",
"version": "v3.2.0",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/promise.git",
"reference": "8a164643313c71354582dc850b42b33fa12a4b63"
"reference": "23444f53a813a3296c1368bb104793ce8d88f04a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63",
"reference": "8a164643313c71354582dc850b42b33fa12a4b63",
"url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a",
"reference": "23444f53a813a3296c1368bb104793ce8d88f04a",
"shasum": ""
},
"require": {
"php": ">=7.1.0"
},
"require-dev": {
"phpstan/phpstan": "1.10.39 || 1.4.10",
"phpstan/phpstan": "1.12.28 || 1.4.10",
"phpunit/phpunit": "^9.6 || ^7.5"
},
"type": "library",
@@ -1020,7 +1015,7 @@
],
"support": {
"issues": "https://github.com/reactphp/promise/issues",
"source": "https://github.com/reactphp/promise/tree/v3.2.0"
"source": "https://github.com/reactphp/promise/tree/v3.3.0"
},
"funding": [
{
@@ -1028,7 +1023,7 @@
"type": "open_collective"
}
],
"time": "2024-05-24T10:39:05+00:00"
"time": "2025-08-19T18:57:03+00:00"
},
{
"name": "react/socket",
@@ -1257,16 +1252,16 @@
},
{
"name": "symfony/console",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1"
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1",
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1",
"url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
"shasum": ""
},
"require": {
@@ -1331,7 +1326,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.3.2"
"source": "https://github.com/symfony/console/tree/v7.3.3"
},
"funding": [
{
@@ -1351,7 +1346,7 @@
"type": "tidelift"
}
],
"time": "2025-07-30T17:13:41+00:00"
"time": "2025-08-25T06:35:40+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -1422,16 +1417,16 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v7.3.0",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d"
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d",
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191",
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191",
"shasum": ""
},
"require": {
@@ -1482,7 +1477,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0"
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3"
},
"funding": [
{
@@ -1493,12 +1488,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-22T09:11:45+00:00"
"time": "2025-08-13T11:49:31+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -1716,16 +1715,16 @@
},
{
"name": "symfony/options-resolver",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37"
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37",
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
"shasum": ""
},
"require": {
@@ -1763,7 +1762,7 @@
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v7.3.2"
"source": "https://github.com/symfony/options-resolver/tree/v7.3.3"
},
"funding": [
{
@@ -1783,11 +1782,11 @@
"type": "tidelift"
}
],
"time": "2025-07-15T11:36:08+00:00"
"time": "2025-08-05T10:16:07+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.32.0",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
@@ -1846,7 +1845,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
},
"funding": [
{
@@ -1857,6 +1856,10 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
@@ -1866,16 +1869,16 @@
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.32.0",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe"
"reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
"reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70",
"reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70",
"shasum": ""
},
"require": {
@@ -1924,7 +1927,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0"
},
"funding": [
{
@@ -1935,16 +1938,20 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
"time": "2025-06-27T09:58:17+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.32.0",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
@@ -2005,7 +2012,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0"
},
"funding": [
{
@@ -2016,6 +2023,10 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
@@ -2025,7 +2036,7 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.32.0",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
@@ -2086,7 +2097,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
},
"funding": [
{
@@ -2097,6 +2108,10 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
@@ -2106,7 +2121,7 @@
},
{
"name": "symfony/polyfill-php80",
"version": "v1.32.0",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
@@ -2166,7 +2181,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
},
"funding": [
{
@@ -2177,6 +2192,10 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
@@ -2186,7 +2205,7 @@
},
{
"name": "symfony/polyfill-php81",
"version": "v1.32.0",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
@@ -2242,7 +2261,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0"
"source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0"
},
"funding": [
{
@@ -2253,6 +2272,10 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
@@ -2262,16 +2285,16 @@
},
{
"name": "symfony/process",
"version": "v7.3.0",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af"
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
"url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
"shasum": ""
},
"require": {
@@ -2303,7 +2326,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.3.0"
"source": "https://github.com/symfony/process/tree/v7.3.3"
},
"funding": [
{
@@ -2314,12 +2337,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-17T09:11:12+00:00"
"time": "2025-08-18T09:42:54+00:00"
},
{
"name": "symfony/service-contracts",
@@ -2468,16 +2495,16 @@
},
{
"name": "symfony/string",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca"
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca",
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca",
"url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
"shasum": ""
},
"require": {
@@ -2535,7 +2562,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.3.2"
"source": "https://github.com/symfony/string/tree/v7.3.3"
},
"funding": [
{
@@ -2555,7 +2582,7 @@
"type": "tidelift"
}
],
"time": "2025-07-10T08:47:49+00:00"
"time": "2025-08-25T06:35:40+00:00"
}
],
"packages-dev": [],

View File

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

View File

@@ -25,7 +25,7 @@ feature:
This issue has been marked as a feature request.
If you come across this issue, please be aware there is NO need to reply with "+1" or "me too" or "I need this too" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted. You can subscribe to this issue to get updates.
If you come across this issue, please be aware there is NO need to reply with "+1" or "I need this too" or "any updates?" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted. You can subscribe to this issue to get updates.
Thank you for your contributions.
@@ -39,7 +39,7 @@ epic:
This issue has been marked as an epic. In epics, large amounts of works are collected that will be part of a major new feature. If you have more ideas that could be a part of this epic, feel free to reply.
*However*, please be aware there is NO need to reply with "+1" or "me too" or "I need this too" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted.
*However*, please be aware there is NO need to reply with "+1" or "I need this too" or "any updates?" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted.
If you are merely interested in this epic's progress, you can subscribe to this issue to get updates.
@@ -56,7 +56,7 @@ enhancement:
This issue has been marked as an enhancement.
If you come across this issue, please be aware there is NO need to reply with "+1" or "me too" or "I need this too" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted. You can subscribe to this issue to get updates.
If you come across this issue, please be aware there is NO need to reply with "+1" or "I need this too" or "any updates?" or whatever. Such comments are not helpful, and do not influence [the roadmap](https://roadmap.firefly-iii.org/). Your comment may be :skull: deleted. You can subscribe to this issue to get updates.
Thank you for your contributions.

View File

@@ -16,6 +16,10 @@ Alpha releases are created to test new features and fixes before they are includ
The release files are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Develop with Firefly III
Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III.
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

View File

@@ -16,6 +16,10 @@ Alpha releases are created to test new features and fixes before they are includ
The release files are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Develop with Firefly III
Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III.
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

View File

@@ -16,6 +16,10 @@ There is no changelog for this release, as it is not final. However, [changelog.
The release files are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Develop with Firefly III
Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III.
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

View File

@@ -16,6 +16,10 @@ The changelog for this release may not be up-to-date, so it is not included. How
The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Develop with Firefly III
Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III.
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

View File

@@ -11,6 +11,10 @@ Welcome to release %version of Firefly III. It contains the latest fixes, transl
The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Develop with Firefly III
Are you interested in (future) API changes to Firefly III, or other interesting dev-related updates? Sign up to the [Firefly III developer newsletter](https://firefly-iii.kit.com/dev) to receive low-frequency updates about the development of Firefly III.
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

View File

@@ -21,7 +21,7 @@ jobs:
discussions: write
runs-on: ubuntu-latest
steps:
- uses: JC5/lock-threads@v6.0.2
- uses: JC5/lock-threads@v6.0.6
with:
issue-inactive-days: 21
pr-inactive-days: 21

View File

@@ -114,6 +114,7 @@ class AccountController extends Controller
'id' => (string) $account->id,
'name' => $account->name,
'name_with_balance' => $nameWithBalance,
'active' => $account->active,
'type' => $account->accountType->type,
'currency_id' => (string) $useCurrency->id,
'currency_name' => $useCurrency->name,

View File

@@ -69,6 +69,7 @@ class BudgetController extends Controller
static fn (Budget $item) => [
'id' => (string) $item->id,
'name' => $item->name,
'active' => $item->active,
]
);

View File

@@ -69,6 +69,7 @@ class RecurrenceController extends Controller
'id' => (string) $recurrence->id,
'name' => $recurrence->title,
'description' => $recurrence->description,
'active' => $recurrence->active,
];
}

View File

@@ -69,6 +69,7 @@ class RuleController extends Controller
'id' => (string)$rule->id,
'name' => $rule->title,
'description' => $rule->description,
'active' => $rule->active,
];
}

View File

@@ -69,6 +69,7 @@ class RuleGroupController extends Controller
'id' => (string) $group->id,
'name' => $group->title,
'description' => $group->description,
'active' => $group->active,
];
}

View File

@@ -1,5 +1,26 @@
<?php
/*
* BalanceController.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart;

View File

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

View File

@@ -97,13 +97,14 @@ class CategoryController extends Controller
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->withAccountInformation();
$collector->setXorAccounts($accounts)->withCategoryInformation();
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::RECONCILIATION->value]);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value]);
$journals = $collector->getExtractedJournals();
/** @var array $journal */
foreach ($journals as $journal) {
// find journal:
$journalCurrencyId = (int)$journal['currency_id'];
$type = $journal['transaction_type_type'];
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
$currencies[$journalCurrencyId] = $currency;
$currencyId = (int)$currency->id;
@@ -111,7 +112,7 @@ class CategoryController extends Controller
$currencyCode = (string)$currency->code;
$currencySymbol = (string)$currency->symbol;
$currencyDecimalPlaces = (int)$currency->decimal_places;
$amount = Steam::positive($journal['amount']);
$amount = Steam::positive((string)$journal['amount']);
$pcAmount = null;
// overrule if necessary:
@@ -151,22 +152,36 @@ class CategoryController extends Controller
'type' => 'bar',
'entries' => [
'spent' => '0',
'earned' => '0',
],
'pc_entries' => [
'spent' => '0',
'earned' => '0',
],
];
// add monies
$return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], (string)$amount);
// expenses to spent
if (TransactionTypeEnum::WITHDRAWAL->value === $type) {
$return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], $amount);
if (null !== $pcAmount) {
$return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], (string)$pcAmount);
$return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], $pcAmount);
}
continue;
}
// positive amount = earned
if (TransactionTypeEnum::DEPOSIT->value === $type) {
$return[$key]['entries']['earned'] = bcadd($return[$key]['entries']['earned'], $amount);
if (null !== $pcAmount) {
$return[$key]['pc_entries']['earned'] = bcadd($return[$key]['pc_entries']['earned'], $pcAmount);
}
}
}
$return = array_values($return);
// order by amount
usort($return, static fn (array $a, array $b) => (float)$a['entries']['spent'] < (float)$b['entries']['spent'] ? 1 : -1);
usort($return, static fn (array $a, array $b) => ((float)$a['entries']['spent'] + (float)$a['entries']['earned']) < ((float)$b['entries']['spent'] + (float)$b['entries']['earned']) ? 1 : -1);
return response()->json($this->clean($return));
}

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use FireflyIII\Exceptions\BadHttpHeaderException;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
@@ -66,8 +65,6 @@ abstract class Controller extends BaseController
protected const string JSON_CONTENT_TYPE = 'application/json';
protected array $accepts = ['application/json', 'application/vnd.api+json'];
/** @var array<int, string> */
protected array $allowedSort;
protected bool $convertToPrimary = false;
protected TransactionCurrency $primaryCurrency;
protected ParameterBag $parameters;
@@ -78,7 +75,6 @@ abstract class Controller extends BaseController
public function __construct()
{
// get global parameters
$this->allowedSort = config('firefly.allowed_sort_parameters');
$this->middleware(
function ($request, $next) {
$this->parameters = $this->getParameters();
@@ -108,12 +104,7 @@ abstract class Controller extends BaseController
{
$bag = new ParameterBag();
$page = (int)request()->get('page');
if ($page < 1) {
$page = 1;
}
if ($page > 2 ** 16) {
$page = 2 ** 16;
}
$page = min(max(1, $page), 2 ** 16);
$bag->set('page', $page);
// some date fields:
@@ -131,20 +122,16 @@ abstract class Controller extends BaseController
$obj = null;
if (null !== $date) {
try {
$obj = Carbon::parse((string)$date);
$obj = Carbon::parse((string)$date, config('app.timezone'));
} catch (InvalidFormatException $e) {
// don't care
Log::warning(
sprintf(
'Ignored invalid date "%s" in API controller parameter check: %s',
substr((string)$date, 0, 20),
$e->getMessage()
)
);
Log::warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', substr((string)$date, 0, 20), $e->getMessage()));
}
}
if ($obj instanceof Carbon) {
$bag->set($field, $obj);
}
}
// integer fields:
$integers = ['limit'];
@@ -159,13 +146,7 @@ abstract class Controller extends BaseController
}
if (null !== $value) {
$value = (int)$value;
if ($value < 1) {
$value = 1;
}
if ($value > 2 ** 16) {
$value = 2 ** 16;
}
$value = min(max(1, $value), 2 ** 16);
$bag->set($integer, $value);
}
if (null === $value
@@ -175,46 +156,14 @@ abstract class Controller extends BaseController
/** @var User $user */
$user = auth()->user();
/** @var Preference $pageSize */
$pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data;
$bag->set($integer, $pageSize);
}
}
// sort fields:
return $this->getSortParameters($bag);
}
private function getSortParameters(ParameterBag $bag): ParameterBag
{
$sortParameters = [];
try {
$param = (string)request()->query->get('sort');
} catch (BadRequestException $e) {
Log::error('Request field "sort" contains a non-scalar value. Value set to NULL.');
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$param = '';
}
if ('' === $param) {
return $bag;
}
$parts = explode(',', $param);
foreach ($parts as $part) {
$part = trim($part);
$direction = 'asc';
if ('-' === $part[0]) {
$part = substr($part, 1);
$direction = 'desc';
}
if (in_array($part, $this->allowedSort, true)) {
$sortParameters[] = [$part, $direction];
}
}
$bag->set('sort', $sortParameters);
return $bag;
// return $this->getSortParameters($bag);
}
/**

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Models\Account;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\Account\ShowRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -33,7 +34,6 @@ use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
@@ -71,34 +71,38 @@ class ShowController extends Controller
*
* @throws FireflyException
*/
public function index(Request $request): JsonResponse
public function index(ShowRequest $request): JsonResponse
{
$manager = $this->getManager();
$type = $request->get('type') ?? 'all';
$this->parameters->set('type', $type);
$params = $request->getParameters();
$this->parameters->set('type', $params['type']);
// types to get, page size:
$types = $this->mapAccountTypes($this->parameters->get('type'));
$pageSize = $this->parameters->get('limit');
$types = $this->mapAccountTypes($params['type']);
// get list of accounts. Count it and split it.
$this->repository->resetAccountOrder();
$collection = $this->repository->getAccountsByType($types, $this->parameters->get('sort') ?? []);
$collection = $this->repository->getAccountsByType($types, $params['sort']);
$count = $collection->count();
// continue sort:
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// TODO if the user sorts on DB dependent field there must be no slice before enrichment, only after.
// TODO still need to figure out how to do this easily.
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $params['limit'], $params['limit']);
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setSort($params['sort']);
$enrichment->setDate($this->parameters->get('date'));
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$enrichment->setUser($admin);
$accounts = $enrichment->enrich($accounts);
// make paginator:
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
$paginator = new LengthAwarePaginator($accounts, $count, $params['limit'], $this->parameters->get('page'));
$paginator->setPath(route('api.v1.accounts.index').$this->buildParams());
/** @var AccountTransformer $transformer */
@@ -117,7 +121,7 @@ class ShowController extends Controller
*
* Show single instance.
*/
public function show(Account $account): JsonResponse
public function show(ShowRequest $request, Account $account): JsonResponse
{
// get list of accounts. Count it and split it.
$this->repository->resetAccountOrder();
@@ -129,6 +133,8 @@ class ShowController extends Controller
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setDate($this->parameters->get('date'));
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$enrichment->setUser($admin);
$account = $enrichment->enrichSingle($account);

View File

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

View File

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

View File

@@ -86,7 +86,7 @@ class UpdateController extends Controller
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimit = $enrichment->enrich($budgetLimit);
$budgetLimit = $enrichment->enrichSingle($budgetLimit);
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);

View File

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

View File

@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
/*
* TriggerController.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V1\Controllers\Models\Recurrence;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Generic\SingleDateRequest;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Jobs\CreateRecurringTransactions;
use FireflyIII\Models\Recurrence;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
class TriggerController extends Controller
{
private RecurringRepositoryInterface $repository;
/**
* RecurrenceController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(RecurringRepositoryInterface::class);
$this->repository->setUser(auth()->user());
return $next($request);
}
);
}
public function trigger(SingleDateRequest $request, Recurrence $recurrence): JsonResponse
{
// find recurrence occurrence for this date and trigger it.
// grab the date from the last time the recurrence fired:
$backupDate = $recurrence->latest_date;
$date = $request->getDate();
// fire the recurring cron job on the given date, then post-date the created transaction.
Log::info(sprintf('Trigger: will now fire recurring cron job task for date "%s".', $date->format('Y-m-d H:i:s')));
/** @var CreateRecurringTransactions $job */
$job = app(CreateRecurringTransactions::class);
$job->setRecurrences(new Collection()->push($recurrence));
$job->setDate($date);
$job->setForce(false);
$job->handle();
Log::debug('Done with recurrence.');
$groups = $job->getGroups();
$this->repository->markGroupsAsNow($groups);
$recurrence->latest_date = $backupDate;
$recurrence->latest_date_tz = $backupDate?->format('e');
$recurrence->save();
Preferences::mark();
// enrich groups and return them:
if (0 === $groups->count()) {
$paginator = new LengthAwarePaginator(new Collection(), 0, 1);
}
if ($groups->count() > 0) {
/** @var User $admin */
$admin = auth()->user();
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
->setIds($groups->pluck('id')->toArray())
->withAPIInformation()
;
$paginator = $collector->getPaginatedGroups();
}
$manager = $this->getManager();
$paginator->setPath(route('api.v1.recurrences.trigger', [$recurrence->id]).$this->buildParams());
// enrich
$admin = auth()->user();
$enrichment = new TransactionGroupEnrichment();
$enrichment->setUser($admin);
$transactions = $enrichment->enrich($paginator->getCollection());
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
}
}

View File

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

View File

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

View File

@@ -156,13 +156,11 @@ class ConfigurationController extends Controller
}
// fallback
if (!str_starts_with($configKey, 'configuration.')) {
$data = [
'title' => $configKey,
'value' => config($configKey),
'value' => config($shortKey),
'editable' => false,
];
}
return response()->api(['data' => $data])->header('Content-Type', self::JSON_CONTENT_TYPE);
}

View File

@@ -32,7 +32,9 @@ use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\Webhook;
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment;
use FireflyIII\Transformers\WebhookTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
@@ -90,6 +92,13 @@ class ShowController extends Controller
$paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.webhooks.index').$this->buildParams());
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new WebhookEnrichment();
$enrichment->setUser($admin);
$webhooks = $enrichment->enrich($webhooks);
/** @var WebhookTransformer $transformer */
$transformer = app(WebhookTransformer::class);
$transformer->setParameters($this->parameters);
@@ -117,6 +126,13 @@ class ShowController extends Controller
Log::channel('audit')->info(sprintf('User views webhook #%d.', $webhook->id));
$manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new WebhookEnrichment();
$enrichment->setUser($admin);
$webhook = $enrichment->enrichSingle($webhook);
/** @var WebhookTransformer $transformer */
$transformer = app(WebhookTransformer::class);
$transformer->setParameters($this->parameters);
@@ -156,7 +172,7 @@ class ShowController extends Controller
$engine->generateMessages();
// trigger event to send them:
Log::debug('send event RequestedSendWebhookMessages');
Log::debug('send event RequestedSendWebhookMessages from ShowController::triggerTransaction()');
event(new RequestedSendWebhookMessages());
return response()->json([], 204);

View File

@@ -27,7 +27,9 @@ namespace FireflyIII\Api\V1\Controllers\Webhook;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\Webhook\CreateRequest;
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment;
use FireflyIII\Transformers\WebhookTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use League\Fractal\Resource\Item;
@@ -68,6 +70,15 @@ class StoreController extends Controller
}
$webhook = $this->repository->store($data);
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new WebhookEnrichment();
$enrichment->setUser($admin);
$webhook = $enrichment->enrichSingle($webhook);
$manager = $this->getManager();
Log::channel('audit')->info('User stores new webhook', $data);

View File

@@ -28,7 +28,9 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\Webhook\UpdateRequest;
use FireflyIII\Models\Webhook;
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment;
use FireflyIII\Transformers\WebhookTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use League\Fractal\Resource\Item;
@@ -70,6 +72,15 @@ class UpdateController extends Controller
$webhook = $this->repository->update($webhook, $data);
$manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new WebhookEnrichment();
$enrichment->setUser($admin);
/** @var Webhook $webhook */
$webhook = $enrichment->enrichSingle($webhook);
Log::channel('audit')->info(sprintf('User updates webhook #%d', $webhook->id), $data);
/** @var WebhookTransformer $transformer */

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Chart;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\Validator;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Support\Request\ChecksLogin;
@@ -64,6 +64,7 @@ class ChartRequest extends FormRequest
'end' => 'required|date|after:1970-01-02|before:2038-01-17|after_or_equal:start',
'preselected' => sprintf('nullable|in:%s', implode(',', config('firefly.preselected_accounts'))),
'period' => sprintf('nullable|in:%s', implode(',', config('firefly.valid_view_ranges'))),
'accounts' => 'nullable|array',
'accounts.*' => 'exists:accounts,id',
];

View File

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

View File

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

View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/*
* ShowRequest.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V1\Requests\Models\Account;
use Illuminate\Validation\Validator;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Rules\IsValidSortInstruction;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\User;
use Illuminate\Foundation\Http\FormRequest;
class ShowRequest extends FormRequest
{
use AccountFilter;
use ConvertsDataTypes;
public function getParameters(): array
{
$limit = $this->convertInteger('limit');
if (0 === $limit) {
// get default for user:
/** @var User $user */
$user = auth()->user();
$limit = (int)Preferences::getForUser($user, 'listPageSize', 50)->data;
}
$page = $this->convertInteger('page');
$page = min(max(1, $page), 2 ** 16);
return [
'type' => $this->convertString('type', 'all'),
'limit' => $limit,
'sort' => $this->convertSortParameters('sort', Account::class),
'page' => $page,
];
}
public function rules(): array
{
$keys = implode(',', array_keys($this->types));
return [
'date' => 'date',
'start' => 'date|present_with:end|before_or_equal:end|before:2038-01-17|after:1970-01-02',
'end' => 'date|present_with:start|after_or_equal:start|before:2038-01-17|after:1970-01-02',
'sort' => ['nullable', new IsValidSortInstruction(Account::class)],
'type' => sprintf('in:%s', $keys),
'limit' => 'numeric|min:1|max:131337',
'page' => 'numeric|min:1|max:131337',
];
}
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator): void {
if (count($validator->failed()) > 0) {
return;
}
$data = $validator->getData();
if (array_key_exists('date', $data) && array_key_exists('start', $data) && array_key_exists('end', $data)) {
// assume valid dates, before we got here.
$start = Carbon::parse($data['start'], config('app.timezone'))->startOfDay();
$end = Carbon::parse($data['end'], config('app.timezone'))->endOfDay();
$date = Carbon::parse($data['date'], config('app.timezone'));
if (!$date->between($start, $end)) {
$validator->errors()->add('date', (string)trans('validation.between_date'));
}
}
}
);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,7 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\Validator;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsValidZeroOrMoreAmount;
@@ -78,7 +79,7 @@ class StoreRequest extends FormRequest
'object_group_id' => 'numeric|belongsToUser:object_groups,id',
'object_group_title' => ['min:1', 'max:255'],
'target_amount' => ['required', new IsValidZeroOrMoreAmount()],
'start_date' => 'date|nullable',
'start_date' => 'required|date|after:1970-01-01|before:2038-01-17',
'transaction_currency_id' => 'exists:transaction_currencies,id|required_without:transaction_currency_code',
'transaction_currency_code' => 'exists:transaction_currencies,code|required_without:transaction_currency_id',
'target_date' => 'date|nullable|after:start_date',
@@ -96,7 +97,7 @@ class StoreRequest extends FormRequest
// validate start before end only if both are there.
$data = $validator->getData();
$currency = $this->getCurrencyFromData($validator, $data);
if (null === $currency) {
if (!$currency instanceof TransactionCurrency) {
return;
}
$targetAmount = (string) ($data['target_amount'] ?? '0');
@@ -135,16 +136,10 @@ class StoreRequest extends FormRequest
private function getCurrencyFromData(Validator $validator, array $data): ?TransactionCurrency
{
if (array_key_exists('transaction_currency_code', $data) && '' !== (string) $data['transaction_currency_code']) {
$currency = TransactionCurrency::whereCode($data['transaction_currency_code'])->first();
if (null !== $currency) {
return $currency;
}
return Amount::getTransactionCurrencyByCode((string) $data['transaction_currency_code']);
}
if (array_key_exists('transaction_currency_id', $data) && '' !== (string) $data['transaction_currency_id']) {
$currency = TransactionCurrency::find((int) $data['transaction_currency_id']);
if (null !== $currency) {
return $currency;
}
return Amount::getTransactionCurrencyById((int) $data['transaction_currency_id']);
}
$validator->errors()->add('transaction_currency_id', trans('validation.require_currency_id_code'));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,15 +24,13 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
use FireflyIII\Enums\WebhookResponse;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Webhook;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Contracts\Validation\Validator;
use FireflyIII\Support\Request\ValidatesWebhooks;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
/**
* Class CreateRequest
@@ -41,27 +39,28 @@ class CreateRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
use ValidatesWebhooks;
public function getData(): array
{
$triggers = Webhook::getTriggersForValidation();
$responses = Webhook::getResponsesForValidation();
$deliveries = Webhook::getDeliveriesForValidation();
$fields = [
'title' => ['title', 'convertString'],
'active' => ['active', 'boolean'],
'trigger' => ['trigger', 'convertString'],
'response' => ['response', 'convertString'],
'delivery' => ['delivery', 'convertString'],
'url' => ['url', 'convertString'],
];
$triggers = $this->get('triggers', []);
$responses = $this->get('responses', []);
$deliveries = $this->get('deliveries', []);
if (0 === count($triggers) || 0 === count($responses) || 0 === count($deliveries)) {
throw new FireflyException('Unexpectedly got no responses, triggers or deliveries.');
}
// this is the way.
$return = $this->getAllData($fields);
$return['trigger'] = $triggers[$return['trigger']] ?? (int)$return['trigger'];
$return['response'] = $responses[$return['response']] ?? (int)$return['response'];
$return['delivery'] = $deliveries[$return['delivery']] ?? (int)$return['delivery'];
$return['triggers'] = $triggers;
$return['responses'] = $responses;
$return['deliveries'] = $deliveries;
return $return;
}
@@ -71,59 +70,24 @@ class CreateRequest extends FormRequest
*/
public function rules(): array
{
$triggers = implode(',', array_keys(Webhook::getTriggersForValidation()));
$responses = implode(',', array_keys(Webhook::getResponsesForValidation()));
$deliveries = implode(',', array_keys(Webhook::getDeliveriesForValidation()));
$triggers = implode(',', array_values(Webhook::getTriggers()));
$responses = implode(',', array_values(Webhook::getResponses()));
$deliveries = implode(',', array_values(Webhook::getDeliveries()));
$validProtocols = config('firefly.valid_url_protocols');
return [
'title' => 'required|min:1|max:255|uniqueObjectForUser:webhooks,title',
'active' => [new IsBoolean()],
'trigger' => sprintf('required|in:%s', $triggers),
'response' => sprintf('required|in:%s', $responses),
'delivery' => sprintf('required|in:%s', $deliveries),
'url' => ['required', sprintf('url:%s', $validProtocols), 'uniqueWebhook'],
'trigger' => 'prohibited',
'triggers' => 'required|array|min:1|max:10',
'triggers.*' => sprintf('required|in:%s', $triggers),
'response' => 'prohibited',
'responses' => 'required|array|min:1|max:1',
'responses.*' => sprintf('required|in:%s', $responses),
'delivery' => 'prohibited',
'deliveries' => 'required|array|min:1|max:1',
'deliveries.*' => sprintf('required|in:%s', $deliveries),
'url' => ['required', sprintf('url:%s', $validProtocols)],
];
}
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator): void {
Log::debug('Validating webhook');
$data = $validator->getData();
$trigger = $data['trigger'] ?? null;
$response = $data['response'] ?? null;
if (null === $trigger || null === $response) {
Log::debug('No trigger or response, return.');
return;
}
$triggers = array_keys(Webhook::getTriggersForValidation());
$responses = array_keys(Webhook::getResponsesForValidation());
if (!in_array($trigger, $triggers, true) || !in_array($response, $responses, true)) {
return;
}
// cannot deliver budget info.
if (is_int($trigger)) {
Log::debug(sprintf('Trigger was integer (%d).', $trigger));
$trigger = WebhookTrigger::from($trigger)->name;
}
if (is_int($response)) {
Log::debug(sprintf('Response was integer (%d).', $response));
$response = WebhookResponse::from($response)->name;
}
Log::debug(sprintf('Trigger is %s, response is %s', $trigger, $response));
if (str_contains($trigger, 'TRANSACTION') && str_contains($response, 'BUDGET')) {
$validator->errors()->add('response', trans('validation.webhook_budget_info'));
}
if (str_contains($trigger, 'BUDGET') && str_contains($response, 'ACCOUNT')) {
$validator->errors()->add('response', trans('validation.webhook_account_info'));
}
if (str_contains($trigger, 'BUDGET') && str_contains($response, 'TRANSACTION')) {
$validator->errors()->add('response', trans('validation.webhook_transaction_info'));
}
}
);
}
}

View File

@@ -24,15 +24,13 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
use FireflyIII\Enums\WebhookResponse;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Webhook;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Contracts\Validation\Validator;
use FireflyIII\Support\Request\ValidatesWebhooks;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
/**
* Class UpdateRequest
@@ -41,37 +39,28 @@ class UpdateRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
use ValidatesWebhooks;
public function getData(): array
{
$triggers = Webhook::getTriggersForValidation();
$responses = Webhook::getResponsesForValidation();
$deliveries = Webhook::getDeliveriesForValidation();
$fields = [
'title' => ['title', 'convertString'],
'active' => ['active', 'boolean'],
'trigger' => ['trigger', 'convertString'],
'response' => ['response', 'convertString'],
'delivery' => ['delivery', 'convertString'],
'url' => ['url', 'convertString'],
];
// this is the way.
$triggers = $this->get('triggers', []);
$responses = $this->get('responses', []);
$deliveries = $this->get('deliveries', []);
if (0 === count($triggers) || 0 === count($responses) || 0 === count($deliveries)) {
throw new FireflyException('Unexpectedly got no responses, triggers or deliveries.');
}
$return = $this->getAllData($fields);
if (array_key_exists('trigger', $return)) {
$return['trigger'] = $triggers[$return['trigger']] ?? 0;
}
if (array_key_exists('response', $return)) {
$return['response'] = $responses[$return['response']] ?? 0;
}
if (array_key_exists('delivery', $return)) {
$return['delivery'] = $deliveries[$return['delivery']] ?? 0;
}
$return['secret'] = null !== $this->get('secret');
if (null !== $this->get('title')) {
$return['title'] = $this->convertString('title');
}
$return['triggers'] = $triggers;
$return['responses'] = $responses;
$return['deliveries'] = $deliveries;
return $return;
}
@@ -81,9 +70,9 @@ class UpdateRequest extends FormRequest
*/
public function rules(): array
{
$triggers = implode(',', array_keys(Webhook::getTriggersForValidation()));
$responses = implode(',', array_keys(Webhook::getResponsesForValidation()));
$deliveries = implode(',', array_keys(Webhook::getDeliveriesForValidation()));
$triggers = implode(',', array_values(Webhook::getTriggers()));
$responses = implode(',', array_values(Webhook::getResponses()));
$deliveries = implode(',', array_values(Webhook::getDeliveries()));
$validProtocols = config('firefly.valid_url_protocols');
/** @var Webhook $webhook */
@@ -92,51 +81,18 @@ class UpdateRequest extends FormRequest
return [
'title' => sprintf('min:1|max:255|uniqueObjectForUser:webhooks,title,%d', $webhook->id),
'active' => [new IsBoolean()],
'trigger' => sprintf('in:%s', $triggers),
'response' => sprintf('in:%s', $responses),
'delivery' => sprintf('in:%s', $deliveries),
'trigger' => 'prohibited',
'triggers' => 'required|array|min:1|max:10',
'triggers.*' => sprintf('required|in:%s', $triggers),
'response' => 'prohibited',
'responses' => 'required|array|min:1|max:1',
'responses.*' => sprintf('required|in:%s', $responses),
'delivery' => 'prohibited',
'deliveries' => 'required|array|min:1|max:1',
'deliveries.*' => sprintf('required|in:%s', $deliveries),
'url' => [sprintf('url:%s', $validProtocols), sprintf('uniqueExistingWebhook:%d', $webhook->id)],
];
}
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator): void {
Log::debug('Validating webhook');
$data = $validator->getData();
$trigger = $data['trigger'] ?? null;
$response = $data['response'] ?? null;
if (null === $trigger || null === $response) {
Log::debug('No trigger or response, return.');
return;
}
$triggers = array_keys(Webhook::getTriggersForValidation());
$responses = array_keys(Webhook::getResponsesForValidation());
if (!in_array($trigger, $triggers, true) || !in_array($response, $responses, true)) {
return;
}
// cannot deliver budget info.
if (is_int($trigger)) {
Log::debug(sprintf('Trigger was integer (%d).', $trigger));
$trigger = WebhookTrigger::from($trigger)->name;
}
if (is_int($response)) {
Log::debug(sprintf('Response was integer (%d).', $response));
$response = WebhookResponse::from($response)->name;
}
Log::debug(sprintf('Trigger is %s, response is %s', $trigger, $response));
if (str_contains($trigger, 'TRANSACTION') && str_contains($response, 'BUDGET')) {
$validator->errors()->add('response', trans('validation.webhook_budget_info'));
}
if (str_contains($trigger, 'BUDGET') && str_contains($response, 'ACCOUNT')) {
$validator->errors()->add('response', trans('validation.webhook_account_info'));
}
if (str_contains($trigger, 'BUDGET') && str_contains($response, 'TRANSACTION')) {
$validator->errors()->add('response', trans('validation.webhook_transaction_info'));
}
}
);
}
}

View File

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

View File

@@ -28,6 +28,7 @@ namespace FireflyIII\Casts;
use Carbon\Carbon;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
/**
* Class SeparateTimezoneCaster
@@ -51,6 +52,7 @@ class SeparateTimezoneCaster implements CastsAttributes
$timeZone = $attributes[sprintf('%s_tz', $key)] ?? config('app.timezone');
return Carbon::parse($value, $timeZone)->setTimezone(config('app.timezone'));
// Log::debug(sprintf('SeparateTimezoneCaster: %s.%s = %s', str_replace('FireflyIII\\Models\\','',get_class($model)), $key, $result->toAtomString()));
}
/**

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
<?php
declare(strict_types=1);
/*
* ValidatesEnvironmentVariables.php
* Copyright (c) 2025 james@firefly-iii.org.
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -18,9 +18,11 @@ declare(strict_types=1);
* 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/.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
@@ -30,18 +32,7 @@ class ValidatesEnvironmentVariables extends Command
{
use ShowsFriendlyMessages;
/**
* The console command description.
*
* @var null|string
*/
protected $description = 'Makes sure you use the correct variables.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'integrity:validates-environment-variables';
/**

View File

@@ -133,6 +133,9 @@ class OutputsInstructions extends Command
if ('03-31' === $today) {
$colors = ['bright-blue', 'bright-red', 'white', 'white', 'bright-red', 'bright-blue', 'default', 'default'];
}
if ('ru_RU' === config('firefly.default_language')) {
$colors = ['blue', 'blue', 'blue', 'yellow', 'yellow', 'yellow', 'default', 'default'];
}
$this->line(sprintf('<fg=%s> ______ _ __ _ _____ _____ _____ </>', $colors[0]));
$this->line(sprintf('<fg=%s> | ____(_) / _| | |_ _|_ _|_ _| </>', $colors[1]));
@@ -245,7 +248,31 @@ class OutputsInstructions extends Command
'Be there or forever wonder.',
'A year from now you will wish you had started today.',
];
$addQuotes = true;
// fuck the Russian aggression in Ukraine.
// There is no point even trying to be neutral, because you cant. When I say you cant be neutral on
// a moving train, it means the world is already moving in certain directions. Children are going
// hungry, wars are taking place. In a situation like that, to be neutral or to try to be neutral,
// to stand aside, not to take a stand, not to participate, is to collaborate with whatever is
// going on, to allow that to happen.
if ('ru_RU' === config('firefly.default_language')) {
$addQuotes = false;
$lines = [
'🇺🇦 Слава Україні!',
'🇺🇦 Slava Ukraini!',
];
}
$random = random_int(0, count($lines) - 1);
if ($addQuotes) {
$this->line(sprintf(' "%s"', $lines[$random]));
return;
}
$this->line(sprintf(' %s', $lines[$random]));
}
}

View File

@@ -1,9 +1,9 @@
<?php
declare(strict_types=1);
/*
* RecalculatesRunningBalance.php
* Copyright (c) 2025 james@firefly-iii.org.
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -18,9 +18,11 @@ declare(strict_types=1);
* 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/.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
@@ -48,7 +50,7 @@ class RecalculatesRunningBalance extends Command
/**
* Execute the console command.
*/
public function handle()
public function handle(): int
{
if (true === config('firefly.feature_flags.running_balance_column')) {
$this->friendlyInfo('Will recalculate account balances. This may take a LONG time. Please be patient.');
@@ -58,6 +60,8 @@ class RecalculatesRunningBalance extends Command
return 0;
}
$this->friendlyWarning('This command has been disabled.');
return 0;
}
private function correctBalanceAmounts(bool $forced): void

View File

@@ -1,5 +1,26 @@
<?php
/*
* ResetsErrorMailLimit.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\System;
@@ -8,6 +29,9 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command;
use Symfony\Component\Console\Command\Command as CommandAlias;
use function Safe\file_put_contents;
use function Safe\json_encode;
class ResetsErrorMailLimit extends Command
{
use ShowsFriendlyMessages;

View File

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

View File

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

View File

@@ -25,9 +25,11 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Upgrade;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
@@ -117,9 +119,10 @@ class UpgradesCurrencyPreferences extends Command
// set the default currency for the user and for the group:
$preference = $this->getPreference($user);
$primaryCurrency = TransactionCurrency::where('code', $preference)->first();
if (null === $primaryCurrency) {
// get EUR
try {
$primaryCurrency = Amount::getTransactionCurrencyByCode($preference);
} catch (FireflyException) {
$primaryCurrency = TransactionCurrency::where('code', 'EUR')->first();
}
$user->currencies()->updateExistingPivot($primaryCurrency->id, ['user_default' => true]);

View File

@@ -75,6 +75,7 @@ class UpgradesDatabase extends Command
'upgrade:610-currency-preferences',
'upgrade:620-piggy-banks',
'upgrade:620-pc-amounts',
'upgrade:640-upgrade-webhooks',
'firefly-iii:correct-database',
];
$args = [];

View File

@@ -0,0 +1,116 @@
<?php
/*
* UpgradesWebhooks.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Upgrade;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Enums\WebhookDelivery;
use FireflyIII\Enums\WebhookResponse;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Models\Webhook;
use FireflyIII\Models\WebhookDelivery as WebhookDeliveryModel;
use FireflyIII\Models\WebhookResponse as WebhookResponseModel;
use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel;
use Illuminate\Console\Command;
class UpgradesWebhooks extends Command
{
use ShowsFriendlyMessages;
public const string CONFIG_NAME = '640_upgrade_webhooks';
protected $description = 'Upgrade webhooks so they can handle multiple triggers.';
protected $signature = 'upgrade:640-upgrade-webhooks {--F|force : Force the execution of this command.}';
/**
* Execute the console command.
*/
public function handle(): int
{
if ($this->isExecuted() && true !== $this->option('force')) {
$this->friendlyInfo('This command has already been executed.');
return 0;
}
$this->upgradeWebhooks();
$this->markAsExecuted();
$this->friendlyPositive('Upgraded webhooks.');
return 0;
}
private function isExecuted(): bool
{
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) {
return (bool)$configVar->data;
}
return false;
}
private function upgradeWebhooks(): void
{
$set = Webhook::where('delivery', '>', 1)->orWhere('trigger', '>', 1)->orWhere('response', '>', 1)->get();
/** @var Webhook $webhook */
foreach ($set as $webhook) {
$this->upgradeWebhook($webhook);
}
}
private function upgradeWebhook(Webhook $webhook): void
{
$delivery = WebhookDelivery::tryFrom((int)$webhook->delivery);
$response = WebhookResponse::tryFrom((int)$webhook->response);
$trigger = WebhookTrigger::tryFrom((int)$webhook->trigger);
if (null === $delivery || null === $response || null === $trigger) {
$this->friendlyError(sprintf('[a] Webhook #%d has an invalid delivery, response or trigger value. Will not upgrade.', $webhook->id));
return;
}
$deliveryModel = WebhookDeliveryModel::where('key', $delivery->value)->first();
$responseModel = WebhookResponseModel::where('key', $response->value)->first();
$triggerModel = WebhookTriggerModel::where('key', $trigger->value)->first();
if (null === $deliveryModel || null === $responseModel || null === $triggerModel) {
$this->friendlyError(sprintf('[b] Webhook #%d has an invalid delivery, response or trigger model. Will not upgrade.', $webhook->id));
return;
}
$webhook->webhookDeliveries()->attach([$deliveryModel->id]);
$webhook->webhookResponses()->attach([$responseModel->id]);
$webhook->webhookTriggers()->attach([$triggerModel->id]);
$webhook->delivery = 1;
$webhook->response = 1;
$webhook->trigger = 1;
$webhook->save();
$this->friendlyPositive(sprintf('Webhook #%d upgraded.', $webhook->id));
}
private function markAsExecuted(): void
{
app('fireflyconfig')->set(self::CONFIG_NAME, true);
}
}

View File

@@ -32,5 +32,6 @@ enum WebhookResponse: int
case TRANSACTIONS = 200;
case ACCOUNTS = 210;
case BUDGET = 230;
case RELEVANT = 240;
case NONE = 220;
}

View File

@@ -29,6 +29,7 @@ namespace FireflyIII\Enums;
*/
enum WebhookTrigger: int
{
case ANY = 50;
case STORE_TRANSACTION = 100;
case UPDATE_TRANSACTION = 110;
case DESTROY_TRANSACTION = 120;

View File

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

View File

@@ -1,6 +1,27 @@
<?php
/*
* WarnUserAboutBill.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;

View File

@@ -1,5 +1,26 @@
<?php
/*
* WarnUserAboutOverdueSubscriptions.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;

View File

@@ -1,5 +1,26 @@
<?php
/*
* ChangedName.php
* Copyright (c) 2025 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events\Model\PiggyBank;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -170,7 +170,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
}
/** @var null|Account $account */
$account = $user->accounts()->with(['accountType'])->withTrashed()->find($accountId);
$account = $user->accounts()->withTrashed()->with(['accountType'])->find($accountId);
if (null === $account) {
app('log')->error(sprintf('Could not find account %d, so give big fat error.', $accountId));

View File

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

View File

@@ -30,6 +30,7 @@ use FireflyIII\Models\Recurrence;
use FireflyIII\Services\Internal\Support\RecurringTransactionTrait;
use FireflyIII\Services\Internal\Support\TransactionTypeTrait;
use FireflyIII\User;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\MessageBag;
/**
@@ -62,8 +63,8 @@ class RecurrenceFactory
$type = $this->findTransactionType(ucfirst((string) $data['recurrence']['type']));
} catch (FireflyException $e) {
$message = sprintf('Cannot make a recurring transaction of type "%s"', $data['recurrence']['type']);
app('log')->error($message);
app('log')->error($e->getTraceAsString());
Log::error($message);
Log::error($e->getTraceAsString());
throw new FireflyException($message, 0, $e);
}
@@ -107,6 +108,7 @@ class RecurrenceFactory
'title' => $title,
'description' => $description,
'first_date' => $firstDate?->format('Y-m-d'),
'first_date_tz' => $firstDate?->format('e'),
'repeat_until' => $repetitions > 0 ? null : $repeatUntilString,
'latest_date' => null,
'repetitions' => $repetitions,
@@ -125,8 +127,8 @@ class RecurrenceFactory
try {
$this->createTransactions($recurrence, $data['transactions'] ?? []);
} catch (FireflyException $e) {
app('log')->error($e->getMessage());
app('log')->error($e->getTraceAsString());
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$recurrence->forceDelete();
$message = sprintf('Could not create recurring transaction: %s', $e->getMessage());
$this->errors->add('store', $message);

View File

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

View File

@@ -34,6 +34,8 @@ use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Webhook;
use FireflyIII\Models\WebhookMessage;
use FireflyIII\Models\WebhookResponse as WebhookResponseModel;
use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
@@ -80,7 +82,13 @@ class StandardMessageGenerator implements MessageGeneratorInterface
private function getWebhooks(): Collection
{
return $this->user->webhooks()->where('active', true)->where('trigger', $this->trigger)->get(['webhooks.*']);
return $this->user->webhooks()
->leftJoin('webhook_webhook_trigger', 'webhook_webhook_trigger.webhook_id', 'webhooks.id')
->leftJoin('webhook_triggers', 'webhook_webhook_trigger.webhook_trigger_id', 'webhook_triggers.id')
->where('active', true)
->whereIn('webhook_triggers.title', [$this->trigger->name, WebhookTrigger::ANY->name])
->get(['webhooks.*'])
;
}
/**
@@ -119,18 +127,21 @@ class StandardMessageGenerator implements MessageGeneratorInterface
// Line is ignored because all of Firefly III's Models have an id property.
Log::debug(sprintf('Now in generateMessage(#%d, %s#%d)', $webhook->id, $class, $model->id));
$uuid = Uuid::uuid4();
/** @var WebhookResponseModel $response */
$response = $webhook->webhookResponses()->first();
$triggers = $this->getTriggerTitles($webhook->webhookTriggers()->get());
$basicMessage = [
'uuid' => $uuid->toString(),
'user_id' => 0,
'user_group_id' => 0,
'trigger' => WebhookTrigger::from((int)$webhook->trigger)->name,
'response' => WebhookResponse::from((int)$webhook->response)->name,
'trigger' => $this->trigger->name,
'response' => $response->title, // guess that the database is correct.
'url' => $webhook->url,
'version' => sprintf('v%d', $this->getVersion()),
'content' => [],
];
// depends on the model how user_id is set:
switch ($class) {
default:
// Line is ignored because all of Firefly III's Models have an id property.
@@ -142,12 +153,14 @@ class StandardMessageGenerator implements MessageGeneratorInterface
/** @var Budget $model */
$basicMessage['user_id'] = $model->user_id;
$basicMessage['user_group_id'] = $model->user_group_id;
$relevantResponse = WebhookResponse::BUDGET->name;
break;
case BudgetLimit::class:
$basicMessage['user_id'] = $model->budget->user_id;
$basicMessage['user_group_id'] = $model->budget->user_group_id;
$relevantResponse = WebhookResponse::BUDGET->name;
break;
@@ -158,31 +171,27 @@ class StandardMessageGenerator implements MessageGeneratorInterface
break;
}
$responseTitle = $this->getRelevantResponse($triggers, $response, $class);
// then depends on the response what to put in the message:
switch ($webhook->response) {
switch ($responseTitle) {
default:
Log::error(sprintf('The response code for webhook #%d is "%d" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response));
Log::error(sprintf('The response code for webhook #%d is "%s" and the message generator cant handle it. Soft fail.', $webhook->id, $webhook->response));
return;
case WebhookResponse::BUDGET->value:
case WebhookResponse::BUDGET->name:
$basicMessage['content'] = [];
if ($model instanceof Budget) {
$enrichment = new BudgetEnrichment();
$enrichment->setUser($model->user);
/** @var Budget $model */
$model = $enrichment->enrichSingle($model);
$transformer = new BudgetTransformer();
$basicMessage['content'] = $transformer->transform($model);
}
if ($model instanceof BudgetLimit) {
$user = $model->budget->user;
$enrichment = new BudgetEnrichment();
$enrichment->setUser($user);
$enrichment->setStart($model->start_date);
$enrichment->setEnd($model->end_date);
$budget = $enrichment->enrichSingle($model->budget);
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($user);
@@ -190,6 +199,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
$parameters->set('start', $model->start_date);
$parameters->set('end', $model->end_date);
/** @var BudgetLimit $model */
$model = $enrichment->enrichSingle($model);
$transformer = new BudgetLimitTransformer();
$transformer->setParameters($parameters);
@@ -198,12 +208,12 @@ class StandardMessageGenerator implements MessageGeneratorInterface
break;
case WebhookResponse::NONE->value:
case WebhookResponse::NONE->name:
$basicMessage['content'] = [];
break;
case WebhookResponse::TRANSACTIONS->value:
case WebhookResponse::TRANSACTIONS->name:
/** @var TransactionGroup $model */
$transformer = new TransactionGroupTransformer();
@@ -220,7 +230,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
break;
case WebhookResponse::ACCOUNTS->value:
case WebhookResponse::ACCOUNTS->name:
/** @var TransactionGroup $model */
$accounts = $this->collectAccounts($model);
$enrichment = new AccountEnrichment();
@@ -287,4 +297,50 @@ class StandardMessageGenerator implements MessageGeneratorInterface
{
$this->webhooks = $webhooks;
}
private function getRelevantResponse(array $triggers, WebhookResponseModel $response, string $class): string
{
// return none if none.
if (WebhookResponse::NONE->name === $response->title) {
Log::debug(sprintf('Return "%s" because requested nothing.', WebhookResponse::NONE->name));
return WebhookResponse::NONE->name;
}
if (WebhookResponse::RELEVANT->name === $response->title) {
Log::debug('Expected response is any relevant data.');
// depends on the $class
switch ($class) {
case TransactionGroup::class:
Log::debug(sprintf('Return "%s" because class is %s', WebhookResponse::TRANSACTIONS->name, $class));
return WebhookResponse::TRANSACTIONS->name;
case Budget::class:
case BudgetLimit::class:
Log::debug(sprintf('Return "%s" because class is %s', WebhookResponse::BUDGET->name, $class));
return WebhookResponse::BUDGET->name;
default:
throw new FireflyException(sprintf('Cannot deal with "relevant" if the given object is a "%s"', $class));
}
}
Log::debug(sprintf('Return response again: %s', $response->title));
return $response->title;
}
private function getTriggerTitles(Collection $collection): array
{
$return = [];
/** @var WebhookTriggerModel $item */
foreach ($collection as $item) {
$return[] = $item->title;
}
return array_unique($return);
}
}

View File

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

View File

@@ -34,6 +34,8 @@ use FireflyIII\Support\Facades\Preferences;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use function Safe\json_encode;
/**
* Class BillEventHandler
*/
@@ -111,7 +113,7 @@ class BillEventHandler
$bill = $event->bill;
/** @var bool $preference */
Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
$preference = Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
if (true === $preference) {
Log::debug('Bill reminder is true!');

View File

@@ -55,7 +55,7 @@ class DestroyedGroupEventHandler
$engine->setObjects(new Collection([$group]));
$engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
}

View File

@@ -1,45 +0,0 @@
<?php
/*
* BudgetLimitHandler.php
* Copyright (c) 2023 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\Handlers\Events\Model;
use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated;
use Illuminate\Support\Facades\Log;
/**
* Class BudgetLimitHandler
*/
class BudgetLimitHandler
{
public function created(Created $event): void
{
Log::debug(sprintf('BudgetLimitHandler::created(#%s)', $event->budgetLimit->id));
}
public function deleted(Deleted $event): void {}
public function updated(Updated $event): void {}
}

View File

@@ -121,6 +121,7 @@ class StoredGroupEventHandler
$engine->generateMessages();
// trigger event to send them:
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
}
}

View File

@@ -167,6 +167,7 @@ class UpdatedGroupEventHandler
$engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
}

View File

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

View File

@@ -40,7 +40,7 @@ class WebhookEventHandler
{
Log::debug(sprintf('Now in %s', __METHOD__));
if (false === config('firefly.feature_flags.webhooks') || false === config('firefly.allow_webhooks')) {
Log::info('Webhook event handler is disabled, do not run sendWebhookMessages().');
Log::debug('Webhook event handler is disabled, do not run sendWebhookMessages().');
return;
}

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