mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-05 20:22:07 +00:00
Compare commits
129 Commits
develop-20
...
develop-20
Author | SHA1 | Date | |
---|---|---|---|
|
ebef145bd6 | ||
|
acc89eb5f9 | ||
|
6523596415 | ||
|
b6c2d23116 | ||
|
2a123354f9 | ||
|
1e7ea4b76c | ||
|
d959526eb3 | ||
|
8846ee9091 | ||
|
6eb8d0fc8c | ||
|
1b0e16b6a5 | ||
|
2e4df28288 | ||
|
f3b7a3015d | ||
|
5de5e08b1d | ||
|
0a116cd04c | ||
|
fd32a692c1 | ||
|
1ac762aba8 | ||
|
315dc532b6 | ||
|
e19ed1be15 | ||
|
cbb0621fd9 | ||
|
049cbab861 | ||
|
28b620fb5c | ||
|
c183f91ff6 | ||
|
172efae41c | ||
|
756e857ba0 | ||
|
1cde7aab0c | ||
|
2d67eece5d | ||
|
b1f79c4c0f | ||
|
33bd2ceae8 | ||
|
e68850f192 | ||
|
450ac7e6ee | ||
|
91f52b5dbc | ||
|
eed2405d76 | ||
|
c956df7790 | ||
|
0fdccec6a8 | ||
|
8ded54d7a8 | ||
|
bb1b4ca5ca | ||
|
e90d60113b | ||
|
d95dada0e0 | ||
|
8722456595 | ||
|
b5ad226451 | ||
|
ddb0e66651 | ||
|
e802899608 | ||
|
0894d3bf42 | ||
|
80bcfd3bcd | ||
|
8c410f42bd | ||
|
7a1021dffc | ||
|
63883c9a84 | ||
|
50d7f9d1ec | ||
|
ebc7ea0eb6 | ||
|
b905efd0aa | ||
|
93085599b7 | ||
|
8a8bbaf827 | ||
|
96a66b894a | ||
|
1ba641c279 | ||
|
6c5ddfcb8a | ||
|
65ddc246dc | ||
|
e4aff5ff4c | ||
|
dfece91541 | ||
|
6ddda13c3a | ||
|
46219c4678 | ||
|
a1595d0647 | ||
|
4ee9f9bb27 | ||
|
bcaa0bddea | ||
|
01cce49070 | ||
|
293be04d40 | ||
|
44a00ec8eb | ||
|
d84e772945 | ||
|
b475f6c51d | ||
|
2712662510 | ||
|
1f2eeba862 | ||
|
46a60134f4 | ||
|
f3f7820816 | ||
|
a57f8076b2 | ||
|
517afa2273 | ||
|
2142b23aec | ||
|
1bec43b111 | ||
|
d2978a5ee8 | ||
|
39b61c71e8 | ||
|
fa2c964790 | ||
|
134aeb3a46 | ||
|
6f6e1a4ff4 | ||
|
b743bf3d9e | ||
|
84ee6f16c9 | ||
|
9fe39e42b3 | ||
|
4013c7e316 | ||
|
0b76747531 | ||
|
3129756b37 | ||
|
b0df383004 | ||
|
9c5b1df86c | ||
|
5854e24775 | ||
|
3f873422f2 | ||
|
f898990773 | ||
|
a5759ab1c6 | ||
|
9bd6417e02 | ||
|
cd12a10214 | ||
|
ee8cb62e04 | ||
|
87ee95a852 | ||
|
10f8436885 | ||
|
6955846a1c | ||
|
11d2f8d471 | ||
|
99347ffff1 | ||
|
3ddc11a905 | ||
|
0a48c0c20f | ||
|
8bc764d6ef | ||
|
4b4f568558 | ||
|
d42117281a | ||
|
d68ed5a713 | ||
|
2f6f36c3f0 | ||
|
984aa02e35 | ||
|
cdadc7d533 | ||
|
4d59955cc5 | ||
|
fc547ba59a | ||
|
1b2ded3167 | ||
|
dcf20a472a | ||
|
7bd528defe | ||
|
9e6d123165 | ||
|
c592f51c83 | ||
|
fbb6f30366 | ||
|
4546c721fb | ||
|
3a659c9a81 | ||
|
e28a988eb3 | ||
|
a29742fe1f | ||
|
dd06838eec | ||
|
5eb828bff8 | ||
|
c7f3701053 | ||
|
ab6799442c | ||
|
1fe9bf7d76 | ||
|
bd14797da6 | ||
|
93b4e6a8d0 |
154
.ci/php-cs-fixer/composer.lock
generated
154
.ci/php-cs-fixer/composer.lock
generated
@@ -151,16 +151,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/semver",
|
"name": "composer/semver",
|
||||||
"version": "3.4.3",
|
"version": "3.4.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/composer/semver.git",
|
"url": "https://github.com/composer/semver.git",
|
||||||
"reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12"
|
"reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
|
"url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95",
|
||||||
"reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
|
"reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -212,7 +212,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"irc": "ircs://irc.libera.chat:6697/composer",
|
"irc": "ircs://irc.libera.chat:6697/composer",
|
||||||
"issues": "https://github.com/composer/semver/issues",
|
"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": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -222,13 +222,9 @@
|
|||||||
{
|
{
|
||||||
"url": "https://github.com/composer",
|
"url": "https://github.com/composer",
|
||||||
"type": "github"
|
"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",
|
"name": "composer/xdebug-handler",
|
||||||
@@ -959,23 +955,23 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "react/promise",
|
"name": "react/promise",
|
||||||
"version": "v3.2.0",
|
"version": "v3.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/reactphp/promise.git",
|
"url": "https://github.com/reactphp/promise.git",
|
||||||
"reference": "8a164643313c71354582dc850b42b33fa12a4b63"
|
"reference": "23444f53a813a3296c1368bb104793ce8d88f04a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63",
|
"url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a",
|
||||||
"reference": "8a164643313c71354582dc850b42b33fa12a4b63",
|
"reference": "23444f53a813a3296c1368bb104793ce8d88f04a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.1.0"
|
"php": ">=7.1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "1.10.39 || 1.4.10",
|
"phpstan/phpstan": "1.12.28 || 1.4.10",
|
||||||
"phpunit/phpunit": "^9.6 || ^7.5"
|
"phpunit/phpunit": "^9.6 || ^7.5"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@@ -1020,7 +1016,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/reactphp/promise/issues",
|
"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": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1028,7 +1024,7 @@
|
|||||||
"type": "open_collective"
|
"type": "open_collective"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-05-24T10:39:05+00:00"
|
"time": "2025-08-19T18:57:03+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "react/socket",
|
"name": "react/socket",
|
||||||
@@ -1257,16 +1253,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
"version": "v7.3.2",
|
"version": "v7.3.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/console.git",
|
"url": "https://github.com/symfony/console.git",
|
||||||
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1"
|
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1",
|
"url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
|
||||||
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1",
|
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1331,7 +1327,7 @@
|
|||||||
"terminal"
|
"terminal"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/console/tree/v7.3.2"
|
"source": "https://github.com/symfony/console/tree/v7.3.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1351,7 +1347,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-07-30T17:13:41+00:00"
|
"time": "2025-08-25T06:35:40+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/deprecation-contracts",
|
"name": "symfony/deprecation-contracts",
|
||||||
@@ -1422,16 +1418,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/event-dispatcher",
|
"name": "symfony/event-dispatcher",
|
||||||
"version": "v7.3.0",
|
"version": "v7.3.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||||
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d"
|
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d",
|
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191",
|
||||||
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d",
|
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1482,7 +1478,7 @@
|
|||||||
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
|
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0"
|
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1493,12 +1489,16 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-04-22T09:11:45+00:00"
|
"time": "2025-08-13T11:49:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/event-dispatcher-contracts",
|
"name": "symfony/event-dispatcher-contracts",
|
||||||
@@ -1716,16 +1716,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/options-resolver",
|
"name": "symfony/options-resolver",
|
||||||
"version": "v7.3.2",
|
"version": "v7.3.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/options-resolver.git",
|
"url": "https://github.com/symfony/options-resolver.git",
|
||||||
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37"
|
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37",
|
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
|
||||||
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37",
|
"reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1763,7 +1763,7 @@
|
|||||||
"options"
|
"options"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/options-resolver/tree/v7.3.2"
|
"source": "https://github.com/symfony/options-resolver/tree/v7.3.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1783,11 +1783,11 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-07-15T11:36:08+00:00"
|
"time": "2025-08-05T10:16:07+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.32.0",
|
"version": "v1.33.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
@@ -1846,7 +1846,7 @@
|
|||||||
"portable"
|
"portable"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1857,6 +1857,10 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
@@ -1866,16 +1870,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-grapheme",
|
"name": "symfony/polyfill-intl-grapheme",
|
||||||
"version": "v1.32.0",
|
"version": "v1.33.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||||
"reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe"
|
"reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70",
|
||||||
"reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
|
"reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1924,7 +1928,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"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": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1935,16 +1939,20 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-09-09T11:45:10+00:00"
|
"time": "2025-06-27T09:58:17+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-normalizer",
|
"name": "symfony/polyfill-intl-normalizer",
|
||||||
"version": "v1.32.0",
|
"version": "v1.33.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||||
@@ -2005,7 +2013,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"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": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2016,6 +2024,10 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
@@ -2025,7 +2037,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.32.0",
|
"version": "v1.33.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
@@ -2086,7 +2098,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2097,6 +2109,10 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
@@ -2106,7 +2122,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php80",
|
"name": "symfony/polyfill-php80",
|
||||||
"version": "v1.32.0",
|
"version": "v1.33.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
@@ -2166,7 +2182,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2177,6 +2193,10 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
@@ -2186,7 +2206,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php81",
|
"name": "symfony/polyfill-php81",
|
||||||
"version": "v1.32.0",
|
"version": "v1.33.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||||
@@ -2242,7 +2262,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0"
|
"source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2253,6 +2273,10 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
@@ -2262,16 +2286,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/process",
|
"name": "symfony/process",
|
||||||
"version": "v7.3.0",
|
"version": "v7.3.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/process.git",
|
"url": "https://github.com/symfony/process.git",
|
||||||
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af"
|
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
|
"url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
|
||||||
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
|
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -2303,7 +2327,7 @@
|
|||||||
"description": "Executes commands in sub-processes",
|
"description": "Executes commands in sub-processes",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/process/tree/v7.3.0"
|
"source": "https://github.com/symfony/process/tree/v7.3.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2314,12 +2338,16 @@
|
|||||||
"url": "https://github.com/fabpot",
|
"url": "https://github.com/fabpot",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-04-17T09:11:12+00:00"
|
"time": "2025-08-18T09:42:54+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/service-contracts",
|
"name": "symfony/service-contracts",
|
||||||
@@ -2468,16 +2496,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/string",
|
"name": "symfony/string",
|
||||||
"version": "v7.3.2",
|
"version": "v7.3.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/string.git",
|
"url": "https://github.com/symfony/string.git",
|
||||||
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca"
|
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca",
|
"url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
|
||||||
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca",
|
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -2535,7 +2563,7 @@
|
|||||||
"utf8"
|
"utf8"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/string/tree/v7.3.2"
|
"source": "https://github.com/symfony/string/tree/v7.3.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2555,7 +2583,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-07-10T08:47:49+00:00"
|
"time": "2025-08-25T06:35:40+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [],
|
"packages-dev": [],
|
||||||
|
6
.github/label-actions.yml
vendored
6
.github/label-actions.yml
vendored
@@ -25,7 +25,7 @@ feature:
|
|||||||
|
|
||||||
This issue has been marked as a feature request.
|
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.
|
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.
|
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.
|
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.
|
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.
|
Thank you for your contributions.
|
||||||
|
|
||||||
|
4
.github/release-notes/alpha.md
vendored
4
.github/release-notes/alpha.md
vendored
@@ -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/).
|
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
|
## 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.
|
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.
|
||||||
|
4
.github/release-notes/beta.md
vendored
4
.github/release-notes/beta.md
vendored
@@ -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/).
|
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
|
## 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.
|
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.
|
||||||
|
4
.github/release-notes/branch.md
vendored
4
.github/release-notes/branch.md
vendored
@@ -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/).
|
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
|
## 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.
|
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.
|
||||||
|
4
.github/release-notes/develop.md
vendored
4
.github/release-notes/develop.md
vendored
@@ -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/).
|
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
|
## 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.
|
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.
|
||||||
|
4
.github/release-notes/release.md
vendored
4
.github/release-notes/release.md
vendored
@@ -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/).
|
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
|
## 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.
|
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.
|
||||||
|
2
.github/workflows/depsreview.yml
vendored
2
.github/workflows/depsreview.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: 'Checkout repository'
|
- name: 'Checkout repository'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: 'Dependency review'
|
- name: 'Dependency review'
|
||||||
|
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
discussions: write
|
discussions: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: JC5/lock-threads@v6.0.2
|
- uses: JC5/lock-threads@v6.0.6
|
||||||
with:
|
with:
|
||||||
issue-inactive-days: 21
|
issue-inactive-days: 21
|
||||||
pr-inactive-days: 21
|
pr-inactive-days: 21
|
||||||
|
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
|||||||
git pull
|
git pull
|
||||||
echo "Current branch is $(git branch --show-current)"
|
echo "Current branch is $(git branch --show-current)"
|
||||||
env:
|
env:
|
||||||
version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
|
version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
|
||||||
- name: Configure Git
|
- name: Configure Git
|
||||||
run: |
|
run: |
|
||||||
# do some configuration
|
# do some configuration
|
||||||
@@ -118,7 +118,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
FIREFLY_III_ROOT: /github/workspace
|
FIREFLY_III_ROOT: /github/workspace
|
||||||
GH_TOKEN: ""
|
GH_TOKEN: ""
|
||||||
FF_III_VERSION: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
|
FF_III_VERSION: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
|
||||||
- name: Generate JSON v1
|
- name: Generate JSON v1
|
||||||
id: json-v1
|
id: json-v1
|
||||||
uses: JC5/firefly-iii-dev@main
|
uses: JC5/firefly-iii-dev@main
|
||||||
@@ -221,7 +221,7 @@ jobs:
|
|||||||
echo "tarName=$tarName" >> "$GITHUB_ENV"
|
echo "tarName=$tarName" >> "$GITHUB_ENV"
|
||||||
echo "BRANCH_NAME=$BRANCH_NAME" >> "$GITHUB_ENV"
|
echo "BRANCH_NAME=$BRANCH_NAME" >> "$GITHUB_ENV"
|
||||||
env:
|
env:
|
||||||
version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
|
version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
|
||||||
- name: Commit all changes
|
- name: Commit all changes
|
||||||
run: |
|
run: |
|
||||||
# add all content, except output.txt (this contains the changelog and/or the download instructions)
|
# add all content, except output.txt (this contains the changelog and/or the download instructions)
|
||||||
@@ -232,12 +232,12 @@ jobs:
|
|||||||
git commit -m "🤖 Auto commit for release '$version' on $(date +'%Y-%m-%d')" || true
|
git commit -m "🤖 Auto commit for release '$version' on $(date +'%Y-%m-%d')" || true
|
||||||
git push
|
git push
|
||||||
env:
|
env:
|
||||||
version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
|
version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
|
||||||
- name: Generate release description
|
- name: Generate release description
|
||||||
id: release-description
|
id: release-description
|
||||||
uses: JC5/firefly-iii-dev@main
|
uses: JC5/firefly-iii-dev@main
|
||||||
with:
|
with:
|
||||||
action: "ff3:generate-release-notes firefly-iii ${{ github.event.inputs.version }}"
|
action: "ff3:generate-release-notes firefly-iii ${{ inputs.version || 'develop' }}"
|
||||||
output: 'output'
|
output: 'output'
|
||||||
env:
|
env:
|
||||||
FIREFLY_III_ROOT: /github/workspace
|
FIREFLY_III_ROOT: /github/workspace
|
||||||
@@ -291,7 +291,7 @@ jobs:
|
|||||||
echo "DONE!"
|
echo "DONE!"
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
|
version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
|
||||||
- name: Create archives
|
- name: Create archives
|
||||||
run: |
|
run: |
|
||||||
echo "Create zip file $zipName"
|
echo "Create zip file $zipName"
|
||||||
@@ -375,7 +375,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
|
version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
run: |
|
run: |
|
||||||
# add zip file to release.
|
# add zip file to release.
|
||||||
@@ -411,4 +411,4 @@ jobs:
|
|||||||
rm -f $tarName.sha256
|
rm -f $tarName.sha256
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
|
version: ${{ github.event_name == 'schedule' && 'develop' || inputs.version }}
|
||||||
|
@@ -114,6 +114,7 @@ class AccountController extends Controller
|
|||||||
'id' => (string) $account->id,
|
'id' => (string) $account->id,
|
||||||
'name' => $account->name,
|
'name' => $account->name,
|
||||||
'name_with_balance' => $nameWithBalance,
|
'name_with_balance' => $nameWithBalance,
|
||||||
|
'active' => $account->active,
|
||||||
'type' => $account->accountType->type,
|
'type' => $account->accountType->type,
|
||||||
'currency_id' => (string) $useCurrency->id,
|
'currency_id' => (string) $useCurrency->id,
|
||||||
'currency_name' => $useCurrency->name,
|
'currency_name' => $useCurrency->name,
|
||||||
|
@@ -67,8 +67,9 @@ class BudgetController extends Controller
|
|||||||
$result = $this->repository->searchBudget($data['query'], $this->parameters->get('limit'));
|
$result = $this->repository->searchBudget($data['query'], $this->parameters->get('limit'));
|
||||||
$filtered = $result->map(
|
$filtered = $result->map(
|
||||||
static fn (Budget $item) => [
|
static fn (Budget $item) => [
|
||||||
'id' => (string) $item->id,
|
'id' => (string) $item->id,
|
||||||
'name' => $item->name,
|
'name' => $item->name,
|
||||||
|
'active' => $item->active,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -69,6 +69,7 @@ class RecurrenceController extends Controller
|
|||||||
'id' => (string) $recurrence->id,
|
'id' => (string) $recurrence->id,
|
||||||
'name' => $recurrence->title,
|
'name' => $recurrence->title,
|
||||||
'description' => $recurrence->description,
|
'description' => $recurrence->description,
|
||||||
|
'active' => $recurrence->active,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,7 +37,7 @@ use Illuminate\Http\JsonResponse;
|
|||||||
class RuleController extends Controller
|
class RuleController extends Controller
|
||||||
{
|
{
|
||||||
private RuleRepositoryInterface $repository;
|
private RuleRepositoryInterface $repository;
|
||||||
protected array $acceptedRoles = [UserRoleEnum::READ_RULES];
|
protected array $acceptedRoles = [UserRoleEnum::READ_RULES];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RuleController constructor.
|
* RuleController constructor.
|
||||||
@@ -66,9 +66,10 @@ class RuleController extends Controller
|
|||||||
/** @var Rule $rule */
|
/** @var Rule $rule */
|
||||||
foreach ($rules as $rule) {
|
foreach ($rules as $rule) {
|
||||||
$response[] = [
|
$response[] = [
|
||||||
'id' => (string) $rule->id,
|
'id' => (string)$rule->id,
|
||||||
'name' => $rule->title,
|
'name' => $rule->title,
|
||||||
'description' => $rule->description,
|
'description' => $rule->description,
|
||||||
|
'active' => $rule->active,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -69,6 +69,7 @@ class RuleGroupController extends Controller
|
|||||||
'id' => (string) $group->id,
|
'id' => (string) $group->id,
|
||||||
'name' => $group->title,
|
'name' => $group->title,
|
||||||
'description' => $group->description,
|
'description' => $group->description,
|
||||||
|
'active' => $group->active,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -49,8 +49,6 @@ class TransactionTypeController extends Controller
|
|||||||
function ($request, $next) {
|
function ($request, $next) {
|
||||||
$this->validateUserGroup($request);
|
$this->validateUserGroup($request);
|
||||||
$this->repository = app(TransactionTypeRepositoryInterface::class);
|
$this->repository = app(TransactionTypeRepositoryInterface::class);
|
||||||
$this->repository->setUser($this->user);
|
|
||||||
$this->repository->setUserGroup($this->userGroup);
|
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
@@ -130,6 +130,7 @@ class AccountController extends Controller
|
|||||||
'yAxisID' => 0,
|
'yAxisID' => 0,
|
||||||
'period' => '1D',
|
'period' => '1D',
|
||||||
'entries' => [],
|
'entries' => [],
|
||||||
|
'pc_entries' => [],
|
||||||
];
|
];
|
||||||
if ($this->convertToPrimary) {
|
if ($this->convertToPrimary) {
|
||||||
$currentSet['pc_entries'] = [];
|
$currentSet['pc_entries'] = [];
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Api\V1\Controllers\Chart;
|
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||||
|
@@ -97,22 +97,23 @@ class CategoryController extends Controller
|
|||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setRange($start, $end)->withAccountInformation();
|
$collector->setRange($start, $end)->withAccountInformation();
|
||||||
$collector->setXorAccounts($accounts)->withCategoryInformation();
|
$collector->setXorAccounts($accounts)->withCategoryInformation();
|
||||||
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::RECONCILIATION->value]);
|
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value]);
|
||||||
$journals = $collector->getExtractedJournals();
|
$journals = $collector->getExtractedJournals();
|
||||||
|
|
||||||
/** @var array $journal */
|
/** @var array $journal */
|
||||||
foreach ($journals as $journal) {
|
foreach ($journals as $journal) {
|
||||||
// find journal:
|
// find journal:
|
||||||
$journalCurrencyId = (int)$journal['currency_id'];
|
$journalCurrencyId = (int)$journal['currency_id'];
|
||||||
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
|
$type = $journal['transaction_type_type'];
|
||||||
$currencies[$journalCurrencyId] = $currency;
|
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
|
||||||
$currencyId = (int)$currency->id;
|
$currencies[$journalCurrencyId] = $currency;
|
||||||
$currencyName = (string)$currency->name;
|
$currencyId = (int)$currency->id;
|
||||||
$currencyCode = (string)$currency->code;
|
$currencyName = (string)$currency->name;
|
||||||
$currencySymbol = (string)$currency->symbol;
|
$currencyCode = (string)$currency->code;
|
||||||
$currencyDecimalPlaces = (int)$currency->decimal_places;
|
$currencySymbol = (string)$currency->symbol;
|
||||||
$amount = Steam::positive($journal['amount']);
|
$currencyDecimalPlaces = (int)$currency->decimal_places;
|
||||||
$pcAmount = null;
|
$amount = Steam::positive((string)$journal['amount']);
|
||||||
|
$pcAmount = null;
|
||||||
|
|
||||||
// overrule if necessary:
|
// overrule if necessary:
|
||||||
if ($this->convertToPrimary && $journalCurrencyId === $this->primaryCurrency->id) {
|
if ($this->convertToPrimary && $journalCurrencyId === $this->primaryCurrency->id) {
|
||||||
@@ -129,8 +130,8 @@ class CategoryController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category');
|
$categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category');
|
||||||
$key = sprintf('%s-%s', $categoryName, $currencyCode);
|
$key = sprintf('%s-%s', $categoryName, $currencyCode);
|
||||||
// create arrays
|
// create arrays
|
||||||
$return[$key] ??= [
|
$return[$key] ??= [
|
||||||
'label' => $categoryName,
|
'label' => $categoryName,
|
||||||
@@ -150,23 +151,37 @@ class CategoryController extends Controller
|
|||||||
'yAxisID' => 0,
|
'yAxisID' => 0,
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
'entries' => [
|
'entries' => [
|
||||||
'spent' => '0',
|
'spent' => '0',
|
||||||
|
'earned' => '0',
|
||||||
],
|
],
|
||||||
'pc_entries' => [
|
'pc_entries' => [
|
||||||
'spent' => '0',
|
'spent' => '0',
|
||||||
|
'earned' => '0',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
// add monies
|
// add monies
|
||||||
$return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], (string)$amount);
|
// expenses to spent
|
||||||
if (null !== $pcAmount) {
|
if (TransactionTypeEnum::WITHDRAWAL->value === $type) {
|
||||||
$return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], (string)$pcAmount);
|
$return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], $amount);
|
||||||
|
if (null !== $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);
|
$return = array_values($return);
|
||||||
|
|
||||||
// order by amount
|
// 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));
|
return response()->json($this->clean($return));
|
||||||
}
|
}
|
||||||
|
@@ -108,12 +108,7 @@ abstract class Controller extends BaseController
|
|||||||
{
|
{
|
||||||
$bag = new ParameterBag();
|
$bag = new ParameterBag();
|
||||||
$page = (int)request()->get('page');
|
$page = (int)request()->get('page');
|
||||||
if ($page < 1) {
|
$page = min(max(1, $page), 2 ** 16);
|
||||||
$page = 1;
|
|
||||||
}
|
|
||||||
if ($page > 2 ** 16) {
|
|
||||||
$page = 2 ** 16;
|
|
||||||
}
|
|
||||||
$bag->set('page', $page);
|
$bag->set('page', $page);
|
||||||
|
|
||||||
// some date fields:
|
// some date fields:
|
||||||
@@ -131,19 +126,15 @@ abstract class Controller extends BaseController
|
|||||||
$obj = null;
|
$obj = null;
|
||||||
if (null !== $date) {
|
if (null !== $date) {
|
||||||
try {
|
try {
|
||||||
$obj = Carbon::parse((string)$date);
|
$obj = Carbon::parse((string)$date, config('app.timezone'));
|
||||||
} catch (InvalidFormatException $e) {
|
} catch (InvalidFormatException $e) {
|
||||||
// don't care
|
// don't care
|
||||||
Log::warning(
|
Log::warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', substr((string)$date, 0, 20), $e->getMessage()));
|
||||||
sprintf(
|
|
||||||
'Ignored invalid date "%s" in API controller parameter check: %s',
|
|
||||||
substr((string)$date, 0, 20),
|
|
||||||
$e->getMessage()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$bag->set($field, $obj);
|
if ($obj instanceof Carbon) {
|
||||||
|
$bag->set($field, $obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// integer fields:
|
// integer fields:
|
||||||
|
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Api\V1\Controllers\Models\Account;
|
namespace FireflyIII\Api\V1\Controllers\Models\Account;
|
||||||
|
|
||||||
use FireflyIII\Api\V1\Controllers\Controller;
|
use FireflyIII\Api\V1\Controllers\Controller;
|
||||||
|
use FireflyIII\Api\V1\Requests\Models\Account\ShowRequest;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
@@ -33,7 +34,6 @@ use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
|
|||||||
use FireflyIII\Transformers\AccountTransformer;
|
use FireflyIII\Transformers\AccountTransformer;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||||
use League\Fractal\Resource\Collection as FractalCollection;
|
use League\Fractal\Resource\Collection as FractalCollection;
|
||||||
@@ -71,23 +71,22 @@ class ShowController extends Controller
|
|||||||
*
|
*
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
public function index(Request $request): JsonResponse
|
public function index(ShowRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
$manager = $this->getManager();
|
$manager = $this->getManager();
|
||||||
$type = $request->get('type') ?? 'all';
|
$params = $request->getParameters();
|
||||||
$this->parameters->set('type', $type);
|
$this->parameters->set('type', $params['type']);
|
||||||
|
|
||||||
// types to get, page size:
|
// types to get, page size:
|
||||||
$types = $this->mapAccountTypes($this->parameters->get('type'));
|
$types = $this->mapAccountTypes($params['type']);
|
||||||
$pageSize = $this->parameters->get('limit');
|
|
||||||
|
|
||||||
// get list of accounts. Count it and split it.
|
// get list of accounts. Count it and split it.
|
||||||
$this->repository->resetAccountOrder();
|
$this->repository->resetAccountOrder();
|
||||||
$collection = $this->repository->getAccountsByType($types, $this->parameters->get('sort') ?? []);
|
$collection = $this->repository->getAccountsByType($types, $params['sort']);
|
||||||
$count = $collection->count();
|
$count = $collection->count();
|
||||||
|
|
||||||
// continue sort:
|
// continue sort:
|
||||||
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $params['limit'], $params['limit']);
|
||||||
|
|
||||||
// enrich
|
// enrich
|
||||||
/** @var User $admin */
|
/** @var User $admin */
|
||||||
@@ -98,7 +97,7 @@ class ShowController extends Controller
|
|||||||
$accounts = $enrichment->enrich($accounts);
|
$accounts = $enrichment->enrich($accounts);
|
||||||
|
|
||||||
// make paginator:
|
// 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());
|
$paginator->setPath(route('api.v1.accounts.index').$this->buildParams());
|
||||||
|
|
||||||
/** @var AccountTransformer $transformer */
|
/** @var AccountTransformer $transformer */
|
||||||
|
@@ -96,7 +96,6 @@ class ShowController extends Controller
|
|||||||
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
|
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
|
||||||
$paginator->setPath(route('api.v1.budgets.limits.index', [$budget->id]).$this->buildParams());
|
$paginator->setPath(route('api.v1.budgets.limits.index', [$budget->id]).$this->buildParams());
|
||||||
|
|
||||||
|
|
||||||
// enrich
|
// enrich
|
||||||
$enrichment = new BudgetLimitEnrichment();
|
$enrichment = new BudgetLimitEnrichment();
|
||||||
$enrichment->setUser($admin);
|
$enrichment->setUser($admin);
|
||||||
|
@@ -86,7 +86,7 @@ class UpdateController extends Controller
|
|||||||
$admin = auth()->user();
|
$admin = auth()->user();
|
||||||
$enrichment = new BudgetLimitEnrichment();
|
$enrichment = new BudgetLimitEnrichment();
|
||||||
$enrichment->setUser($admin);
|
$enrichment->setUser($admin);
|
||||||
$budgetLimit = $enrichment->enrich($budgetLimit);
|
$budgetLimit = $enrichment->enrichSingle($budgetLimit);
|
||||||
|
|
||||||
/** @var BudgetLimitTransformer $transformer */
|
/** @var BudgetLimitTransformer $transformer */
|
||||||
$transformer = app(BudgetLimitTransformer::class);
|
$transformer = app(BudgetLimitTransformer::class);
|
||||||
|
126
app/Api/V1/Controllers/Models/Recurrence/TriggerController.php
Normal file
126
app/Api/V1/Controllers/Models/Recurrence/TriggerController.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@@ -32,7 +32,9 @@ use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
|||||||
use FireflyIII\Models\TransactionGroup;
|
use FireflyIII\Models\TransactionGroup;
|
||||||
use FireflyIII\Models\Webhook;
|
use FireflyIII\Models\Webhook;
|
||||||
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
|
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
|
||||||
|
use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment;
|
||||||
use FireflyIII\Transformers\WebhookTransformer;
|
use FireflyIII\Transformers\WebhookTransformer;
|
||||||
|
use FireflyIII\User;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -90,6 +92,13 @@ class ShowController extends Controller
|
|||||||
$paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page'));
|
$paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page'));
|
||||||
$paginator->setPath(route('api.v1.webhooks.index').$this->buildParams());
|
$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 */
|
/** @var WebhookTransformer $transformer */
|
||||||
$transformer = app(WebhookTransformer::class);
|
$transformer = app(WebhookTransformer::class);
|
||||||
$transformer->setParameters($this->parameters);
|
$transformer->setParameters($this->parameters);
|
||||||
@@ -117,6 +126,13 @@ class ShowController extends Controller
|
|||||||
Log::channel('audit')->info(sprintf('User views webhook #%d.', $webhook->id));
|
Log::channel('audit')->info(sprintf('User views webhook #%d.', $webhook->id));
|
||||||
$manager = $this->getManager();
|
$manager = $this->getManager();
|
||||||
|
|
||||||
|
// enrich
|
||||||
|
/** @var User $admin */
|
||||||
|
$admin = auth()->user();
|
||||||
|
$enrichment = new WebhookEnrichment();
|
||||||
|
$enrichment->setUser($admin);
|
||||||
|
$webhook = $enrichment->enrichSingle($webhook);
|
||||||
|
|
||||||
/** @var WebhookTransformer $transformer */
|
/** @var WebhookTransformer $transformer */
|
||||||
$transformer = app(WebhookTransformer::class);
|
$transformer = app(WebhookTransformer::class);
|
||||||
$transformer->setParameters($this->parameters);
|
$transformer->setParameters($this->parameters);
|
||||||
@@ -156,7 +172,7 @@ class ShowController extends Controller
|
|||||||
$engine->generateMessages();
|
$engine->generateMessages();
|
||||||
|
|
||||||
// trigger event to send them:
|
// trigger event to send them:
|
||||||
Log::debug('send event RequestedSendWebhookMessages');
|
Log::debug('send event RequestedSendWebhookMessages from ShowController::triggerTransaction()');
|
||||||
event(new RequestedSendWebhookMessages());
|
event(new RequestedSendWebhookMessages());
|
||||||
|
|
||||||
return response()->json([], 204);
|
return response()->json([], 204);
|
||||||
|
@@ -27,7 +27,9 @@ namespace FireflyIII\Api\V1\Controllers\Webhook;
|
|||||||
use FireflyIII\Api\V1\Controllers\Controller;
|
use FireflyIII\Api\V1\Controllers\Controller;
|
||||||
use FireflyIII\Api\V1\Requests\Models\Webhook\CreateRequest;
|
use FireflyIII\Api\V1\Requests\Models\Webhook\CreateRequest;
|
||||||
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
|
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
|
||||||
|
use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment;
|
||||||
use FireflyIII\Transformers\WebhookTransformer;
|
use FireflyIII\Transformers\WebhookTransformer;
|
||||||
|
use FireflyIII\User;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use League\Fractal\Resource\Item;
|
use League\Fractal\Resource\Item;
|
||||||
@@ -68,6 +70,15 @@ class StoreController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$webhook = $this->repository->store($data);
|
$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();
|
$manager = $this->getManager();
|
||||||
|
|
||||||
Log::channel('audit')->info('User stores new webhook', $data);
|
Log::channel('audit')->info('User stores new webhook', $data);
|
||||||
|
@@ -28,7 +28,9 @@ use FireflyIII\Api\V1\Controllers\Controller;
|
|||||||
use FireflyIII\Api\V1\Requests\Models\Webhook\UpdateRequest;
|
use FireflyIII\Api\V1\Requests\Models\Webhook\UpdateRequest;
|
||||||
use FireflyIII\Models\Webhook;
|
use FireflyIII\Models\Webhook;
|
||||||
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
|
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
|
||||||
|
use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment;
|
||||||
use FireflyIII\Transformers\WebhookTransformer;
|
use FireflyIII\Transformers\WebhookTransformer;
|
||||||
|
use FireflyIII\User;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use League\Fractal\Resource\Item;
|
use League\Fractal\Resource\Item;
|
||||||
@@ -70,6 +72,13 @@ class UpdateController extends Controller
|
|||||||
$webhook = $this->repository->update($webhook, $data);
|
$webhook = $this->repository->update($webhook, $data);
|
||||||
$manager = $this->getManager();
|
$manager = $this->getManager();
|
||||||
|
|
||||||
|
// enrich
|
||||||
|
/** @var User $admin */
|
||||||
|
$admin = auth()->user();
|
||||||
|
$enrichment = new WebhookEnrichment();
|
||||||
|
$enrichment->setUser($admin);
|
||||||
|
$webhook = $enrichment->enrichSingle($webhook);
|
||||||
|
|
||||||
Log::channel('audit')->info(sprintf('User updates webhook #%d', $webhook->id), $data);
|
Log::channel('audit')->info(sprintf('User updates webhook #%d', $webhook->id), $data);
|
||||||
|
|
||||||
/** @var WebhookTransformer $transformer */
|
/** @var WebhookTransformer $transformer */
|
||||||
|
@@ -64,6 +64,7 @@ class ChartRequest extends FormRequest
|
|||||||
'end' => 'required|date|after:1970-01-02|before:2038-01-17|after_or_equal:start',
|
'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'))),
|
'preselected' => sprintf('nullable|in:%s', implode(',', config('firefly.preselected_accounts'))),
|
||||||
'period' => sprintf('nullable|in:%s', implode(',', config('firefly.valid_view_ranges'))),
|
'period' => sprintf('nullable|in:%s', implode(',', config('firefly.valid_view_ranges'))),
|
||||||
|
'accounts' => 'nullable|array',
|
||||||
'accounts.*' => 'exists:accounts,id',
|
'accounts.*' => 'exists:accounts,id',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
98
app/Api/V1/Requests/Models/Account/ShowRequest.php
Normal file
98
app/Api/V1/Requests/Models/Account/ShowRequest.php
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?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 Carbon\Carbon;
|
||||||
|
use FireflyIII\Models\Preference;
|
||||||
|
use FireflyIII\Support\Facades\Preferences;
|
||||||
|
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||||
|
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Contracts\Validation\Validator;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class ShowRequest extends FormRequest
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
|
||||||
|
/** @var Preference $pageSize */
|
||||||
|
$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->convertString('sort', 'order'),
|
||||||
|
'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' => 'in:active,iban,name,order,-active,-iban,-name,-order', // TODO improve me.
|
||||||
|
'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 ($validator->failed()) {
|
||||||
|
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'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -78,7 +78,7 @@ class StoreRequest extends FormRequest
|
|||||||
'object_group_id' => 'numeric|belongsToUser:object_groups,id',
|
'object_group_id' => 'numeric|belongsToUser:object_groups,id',
|
||||||
'object_group_title' => ['min:1', 'max:255'],
|
'object_group_title' => ['min:1', 'max:255'],
|
||||||
'target_amount' => ['required', new IsValidZeroOrMoreAmount()],
|
'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_id' => 'exists:transaction_currencies,id|required_without:transaction_currency_code',
|
||||||
'transaction_currency_code' => 'exists:transaction_currencies,code|required_without:transaction_currency_id',
|
'transaction_currency_code' => 'exists:transaction_currencies,code|required_without:transaction_currency_id',
|
||||||
'target_date' => 'date|nullable|after:start_date',
|
'target_date' => 'date|nullable|after:start_date',
|
||||||
|
@@ -24,15 +24,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
|
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
|
||||||
|
|
||||||
use FireflyIII\Enums\WebhookResponse;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Enums\WebhookTrigger;
|
|
||||||
use FireflyIII\Models\Webhook;
|
use FireflyIII\Models\Webhook;
|
||||||
use FireflyIII\Rules\IsBoolean;
|
use FireflyIII\Rules\IsBoolean;
|
||||||
use FireflyIII\Support\Request\ChecksLogin;
|
use FireflyIII\Support\Request\ChecksLogin;
|
||||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||||
use Illuminate\Contracts\Validation\Validator;
|
use FireflyIII\Support\Request\ValidatesWebhooks;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class CreateRequest
|
* Class CreateRequest
|
||||||
@@ -41,27 +39,28 @@ class CreateRequest extends FormRequest
|
|||||||
{
|
{
|
||||||
use ChecksLogin;
|
use ChecksLogin;
|
||||||
use ConvertsDataTypes;
|
use ConvertsDataTypes;
|
||||||
|
use ValidatesWebhooks;
|
||||||
|
|
||||||
public function getData(): array
|
public function getData(): array
|
||||||
{
|
{
|
||||||
$triggers = Webhook::getTriggersForValidation();
|
$fields = [
|
||||||
$responses = Webhook::getResponsesForValidation();
|
'title' => ['title', 'convertString'],
|
||||||
$deliveries = Webhook::getDeliveriesForValidation();
|
'active' => ['active', 'boolean'],
|
||||||
|
'url' => ['url', 'convertString'],
|
||||||
$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', []);
|
||||||
|
|
||||||
// this is the way.
|
if (0 === count($triggers) || 0 === count($responses) || 0 === count($deliveries)) {
|
||||||
$return = $this->getAllData($fields);
|
throw new FireflyException('Unexpectedly got no responses, triggers or deliveries.');
|
||||||
$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 = $this->getAllData($fields);
|
||||||
|
$return['triggers'] = $triggers;
|
||||||
|
$return['responses'] = $responses;
|
||||||
|
$return['deliveries'] = $deliveries;
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
@@ -71,59 +70,24 @@ class CreateRequest extends FormRequest
|
|||||||
*/
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
$triggers = implode(',', array_keys(Webhook::getTriggersForValidation()));
|
$triggers = implode(',', array_values(Webhook::getTriggers()));
|
||||||
$responses = implode(',', array_keys(Webhook::getResponsesForValidation()));
|
$responses = implode(',', array_values(Webhook::getResponses()));
|
||||||
$deliveries = implode(',', array_keys(Webhook::getDeliveriesForValidation()));
|
$deliveries = implode(',', array_values(Webhook::getDeliveries()));
|
||||||
$validProtocols = config('firefly.valid_url_protocols');
|
$validProtocols = config('firefly.valid_url_protocols');
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'title' => 'required|min:1|max:255|uniqueObjectForUser:webhooks,title',
|
'title' => 'required|min:1|max:255|uniqueObjectForUser:webhooks,title',
|
||||||
'active' => [new IsBoolean()],
|
'active' => [new IsBoolean()],
|
||||||
'trigger' => sprintf('required|in:%s', $triggers),
|
'trigger' => 'prohibited',
|
||||||
'response' => sprintf('required|in:%s', $responses),
|
'triggers' => 'required|array|min:1|max:10',
|
||||||
'delivery' => sprintf('required|in:%s', $deliveries),
|
'triggers.*' => sprintf('required|in:%s', $triggers),
|
||||||
'url' => ['required', sprintf('url:%s', $validProtocols), 'uniqueWebhook'],
|
'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'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -24,15 +24,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
|
namespace FireflyIII\Api\V1\Requests\Models\Webhook;
|
||||||
|
|
||||||
use FireflyIII\Enums\WebhookResponse;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Enums\WebhookTrigger;
|
|
||||||
use FireflyIII\Models\Webhook;
|
use FireflyIII\Models\Webhook;
|
||||||
use FireflyIII\Rules\IsBoolean;
|
use FireflyIII\Rules\IsBoolean;
|
||||||
use FireflyIII\Support\Request\ChecksLogin;
|
use FireflyIII\Support\Request\ChecksLogin;
|
||||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||||
use Illuminate\Contracts\Validation\Validator;
|
use FireflyIII\Support\Request\ValidatesWebhooks;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class UpdateRequest
|
* Class UpdateRequest
|
||||||
@@ -41,38 +39,29 @@ class UpdateRequest extends FormRequest
|
|||||||
{
|
{
|
||||||
use ChecksLogin;
|
use ChecksLogin;
|
||||||
use ConvertsDataTypes;
|
use ConvertsDataTypes;
|
||||||
|
use ValidatesWebhooks;
|
||||||
|
|
||||||
public function getData(): array
|
public function getData(): array
|
||||||
{
|
{
|
||||||
$triggers = Webhook::getTriggersForValidation();
|
$fields = [
|
||||||
$responses = Webhook::getResponsesForValidation();
|
|
||||||
$deliveries = Webhook::getDeliveriesForValidation();
|
|
||||||
|
|
||||||
$fields = [
|
|
||||||
'title' => ['title', 'convertString'],
|
'title' => ['title', 'convertString'],
|
||||||
'active' => ['active', 'boolean'],
|
'active' => ['active', 'boolean'],
|
||||||
'trigger' => ['trigger', 'convertString'],
|
|
||||||
'response' => ['response', 'convertString'],
|
|
||||||
'delivery' => ['delivery', 'convertString'],
|
|
||||||
'url' => ['url', 'convertString'],
|
'url' => ['url', 'convertString'],
|
||||||
];
|
];
|
||||||
|
|
||||||
// this is the way.
|
$triggers = $this->get('triggers', []);
|
||||||
$return = $this->getAllData($fields);
|
$responses = $this->get('responses', []);
|
||||||
if (array_key_exists('trigger', $return)) {
|
$deliveries = $this->get('deliveries', []);
|
||||||
$return['trigger'] = $triggers[$return['trigger']] ?? 0;
|
|
||||||
}
|
if (0 === count($triggers) || 0 === count($responses) || 0 === count($deliveries)) {
|
||||||
if (array_key_exists('response', $return)) {
|
throw new FireflyException('Unexpectedly got no responses, triggers or deliveries.');
|
||||||
$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 = $this->getAllData($fields);
|
||||||
|
$return['triggers'] = $triggers;
|
||||||
|
$return['responses'] = $responses;
|
||||||
|
$return['deliveries'] = $deliveries;
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,62 +70,29 @@ class UpdateRequest extends FormRequest
|
|||||||
*/
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
$triggers = implode(',', array_keys(Webhook::getTriggersForValidation()));
|
$triggers = implode(',', array_values(Webhook::getTriggers()));
|
||||||
$responses = implode(',', array_keys(Webhook::getResponsesForValidation()));
|
$responses = implode(',', array_values(Webhook::getResponses()));
|
||||||
$deliveries = implode(',', array_keys(Webhook::getDeliveriesForValidation()));
|
$deliveries = implode(',', array_values(Webhook::getDeliveries()));
|
||||||
$validProtocols = config('firefly.valid_url_protocols');
|
$validProtocols = config('firefly.valid_url_protocols');
|
||||||
|
|
||||||
/** @var Webhook $webhook */
|
/** @var Webhook $webhook */
|
||||||
$webhook = $this->route()->parameter('webhook');
|
$webhook = $this->route()->parameter('webhook');
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'title' => sprintf('min:1|max:255|uniqueObjectForUser:webhooks,title,%d', $webhook->id),
|
'title' => sprintf('min:1|max:255|uniqueObjectForUser:webhooks,title,%d', $webhook->id),
|
||||||
'active' => [new IsBoolean()],
|
'active' => [new IsBoolean()],
|
||||||
'trigger' => sprintf('in:%s', $triggers),
|
|
||||||
'response' => sprintf('in:%s', $responses),
|
'trigger' => 'prohibited',
|
||||||
'delivery' => sprintf('in:%s', $deliveries),
|
'triggers' => 'required|array|min:1|max:10',
|
||||||
'url' => [sprintf('url:%s', $validProtocols), sprintf('uniqueExistingWebhook:%d', $webhook->id)],
|
'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'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,7 @@ namespace FireflyIII\Casts;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class SeparateTimezoneCaster
|
* Class SeparateTimezoneCaster
|
||||||
@@ -51,6 +52,7 @@ class SeparateTimezoneCaster implements CastsAttributes
|
|||||||
$timeZone = $attributes[sprintf('%s_tz', $key)] ?? config('app.timezone');
|
$timeZone = $attributes[sprintf('%s_tz', $key)] ?? config('app.timezone');
|
||||||
|
|
||||||
return Carbon::parse($value, $timeZone)->setTimezone(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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
/*
|
/*
|
||||||
* ValidatesEnvironmentVariables.php
|
* 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).
|
* 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.
|
* GNU Affero General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* 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;
|
namespace FireflyIII\Console\Commands\Integrity;
|
||||||
|
|
||||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||||
|
@@ -133,6 +133,9 @@ class OutputsInstructions extends Command
|
|||||||
if ('03-31' === $today) {
|
if ('03-31' === $today) {
|
||||||
$colors = ['bright-blue', 'bright-red', 'white', 'white', 'bright-red', 'bright-blue', 'default', 'default'];
|
$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[0]));
|
||||||
$this->line(sprintf('<fg=%s> | ____(_) / _| | |_ _|_ _|_ _| </>', $colors[1]));
|
$this->line(sprintf('<fg=%s> | ____(_) / _| | |_ _|_ _|_ _| </>', $colors[1]));
|
||||||
@@ -238,14 +241,38 @@ class OutputsInstructions extends Command
|
|||||||
|
|
||||||
private function someQuote(): void
|
private function someQuote(): void
|
||||||
{
|
{
|
||||||
$lines = [
|
$lines = [
|
||||||
'Forgive yourself for not being at peace.',
|
'Forgive yourself for not being at peace.',
|
||||||
'Doesn\'t look like anything to me.',
|
'Doesn\'t look like anything to me.',
|
||||||
'Be proud of what you make.',
|
'Be proud of what you make.',
|
||||||
'Be there or forever wonder.',
|
'Be there or forever wonder.',
|
||||||
'A year from now you will wish you had started today.',
|
'A year from now you will wish you had started today.',
|
||||||
];
|
];
|
||||||
$random = random_int(0, count($lines) - 1);
|
$addQuotes = true;
|
||||||
$this->line(sprintf(' "%s"', $lines[$random]));
|
|
||||||
|
// fuck the Russian aggression in Ukraine.
|
||||||
|
|
||||||
|
// There is no point even trying to be neutral, because you can’t. When I say you can’t 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]));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
/*
|
/*
|
||||||
* RecalculatesRunningBalance.php
|
* 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).
|
* 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.
|
* GNU Affero General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* 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;
|
namespace FireflyIII\Console\Commands\System;
|
||||||
|
|
||||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Console\Commands\System;
|
namespace FireflyIII\Console\Commands\System;
|
||||||
|
@@ -75,6 +75,7 @@ class UpgradesDatabase extends Command
|
|||||||
'upgrade:610-currency-preferences',
|
'upgrade:610-currency-preferences',
|
||||||
'upgrade:620-piggy-banks',
|
'upgrade:620-piggy-banks',
|
||||||
'upgrade:620-pc-amounts',
|
'upgrade:620-pc-amounts',
|
||||||
|
'upgrade:640-upgrade-webhooks',
|
||||||
'firefly-iii:correct-database',
|
'firefly-iii:correct-database',
|
||||||
];
|
];
|
||||||
$args = [];
|
$args = [];
|
||||||
|
116
app/Console/Commands/Upgrade/UpgradesWebhooks.php
Normal file
116
app/Console/Commands/Upgrade/UpgradesWebhooks.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@@ -32,5 +32,6 @@ enum WebhookResponse: int
|
|||||||
case TRANSACTIONS = 200;
|
case TRANSACTIONS = 200;
|
||||||
case ACCOUNTS = 210;
|
case ACCOUNTS = 210;
|
||||||
case BUDGET = 230;
|
case BUDGET = 230;
|
||||||
|
case RELEVANT = 240;
|
||||||
case NONE = 220;
|
case NONE = 220;
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,7 @@ namespace FireflyIII\Enums;
|
|||||||
*/
|
*/
|
||||||
enum WebhookTrigger: int
|
enum WebhookTrigger: int
|
||||||
{
|
{
|
||||||
|
case ANY = 50;
|
||||||
case STORE_TRANSACTION = 100;
|
case STORE_TRANSACTION = 100;
|
||||||
case UPDATE_TRANSACTION = 110;
|
case UPDATE_TRANSACTION = 110;
|
||||||
case DESTROY_TRANSACTION = 120;
|
case DESTROY_TRANSACTION = 120;
|
||||||
|
@@ -1,6 +1,27 @@
|
|||||||
<?php
|
<?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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Events\Model\Bill;
|
namespace FireflyIII\Events\Model\Bill;
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Events\Model\Bill;
|
namespace FireflyIII\Events\Model\Bill;
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Events\Model\PiggyBank;
|
namespace FireflyIII\Events\Model\PiggyBank;
|
||||||
|
@@ -30,6 +30,7 @@ use FireflyIII\Models\Recurrence;
|
|||||||
use FireflyIII\Services\Internal\Support\RecurringTransactionTrait;
|
use FireflyIII\Services\Internal\Support\RecurringTransactionTrait;
|
||||||
use FireflyIII\Services\Internal\Support\TransactionTypeTrait;
|
use FireflyIII\Services\Internal\Support\TransactionTypeTrait;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\MessageBag;
|
use Illuminate\Support\MessageBag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,8 +63,8 @@ class RecurrenceFactory
|
|||||||
$type = $this->findTransactionType(ucfirst((string) $data['recurrence']['type']));
|
$type = $this->findTransactionType(ucfirst((string) $data['recurrence']['type']));
|
||||||
} catch (FireflyException $e) {
|
} catch (FireflyException $e) {
|
||||||
$message = sprintf('Cannot make a recurring transaction of type "%s"', $data['recurrence']['type']);
|
$message = sprintf('Cannot make a recurring transaction of type "%s"', $data['recurrence']['type']);
|
||||||
app('log')->error($message);
|
Log::error($message);
|
||||||
app('log')->error($e->getTraceAsString());
|
Log::error($e->getTraceAsString());
|
||||||
|
|
||||||
throw new FireflyException($message, 0, $e);
|
throw new FireflyException($message, 0, $e);
|
||||||
}
|
}
|
||||||
@@ -101,17 +102,18 @@ class RecurrenceFactory
|
|||||||
|
|
||||||
$recurrence = new Recurrence(
|
$recurrence = new Recurrence(
|
||||||
[
|
[
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
'user_group_id' => $this->user->user_group_id,
|
'user_group_id' => $this->user->user_group_id,
|
||||||
'transaction_type_id' => $type->id,
|
'transaction_type_id' => $type->id,
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'description' => $description,
|
'description' => $description,
|
||||||
'first_date' => $firstDate?->format('Y-m-d'),
|
'first_date' => $firstDate?->format('Y-m-d'),
|
||||||
'repeat_until' => $repetitions > 0 ? null : $repeatUntilString,
|
'first_date_tz' => $firstDate?->format('e'),
|
||||||
'latest_date' => null,
|
'repeat_until' => $repetitions > 0 ? null : $repeatUntilString,
|
||||||
'repetitions' => $repetitions,
|
'latest_date' => null,
|
||||||
'apply_rules' => $applyRules,
|
'repetitions' => $repetitions,
|
||||||
'active' => $active,
|
'apply_rules' => $applyRules,
|
||||||
|
'active' => $active,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$recurrence->save();
|
$recurrence->save();
|
||||||
@@ -125,8 +127,8 @@ class RecurrenceFactory
|
|||||||
try {
|
try {
|
||||||
$this->createTransactions($recurrence, $data['transactions'] ?? []);
|
$this->createTransactions($recurrence, $data['transactions'] ?? []);
|
||||||
} catch (FireflyException $e) {
|
} catch (FireflyException $e) {
|
||||||
app('log')->error($e->getMessage());
|
Log::error($e->getMessage());
|
||||||
app('log')->error($e->getTraceAsString());
|
Log::error($e->getTraceAsString());
|
||||||
$recurrence->forceDelete();
|
$recurrence->forceDelete();
|
||||||
$message = sprintf('Could not create recurring transaction: %s', $e->getMessage());
|
$message = sprintf('Could not create recurring transaction: %s', $e->getMessage());
|
||||||
$this->errors->add('store', $message);
|
$this->errors->add('store', $message);
|
||||||
|
@@ -27,13 +27,21 @@ namespace FireflyIII\Generator\Webhook;
|
|||||||
use FireflyIII\Enums\WebhookResponse;
|
use FireflyIII\Enums\WebhookResponse;
|
||||||
use FireflyIII\Enums\WebhookTrigger;
|
use FireflyIII\Enums\WebhookTrigger;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Models\Budget;
|
||||||
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionGroup;
|
use FireflyIII\Models\TransactionGroup;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\Webhook;
|
use FireflyIII\Models\Webhook;
|
||||||
use FireflyIII\Models\WebhookMessage;
|
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\AccountEnrichment;
|
||||||
|
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
|
||||||
|
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
|
||||||
use FireflyIII\Transformers\AccountTransformer;
|
use FireflyIII\Transformers\AccountTransformer;
|
||||||
|
use FireflyIII\Transformers\BudgetLimitTransformer;
|
||||||
|
use FireflyIII\Transformers\BudgetTransformer;
|
||||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@@ -74,7 +82,13 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
|||||||
|
|
||||||
private function getWebhooks(): Collection
|
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.*'])
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,23 +123,25 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
|||||||
*/
|
*/
|
||||||
private function generateMessage(Webhook $webhook, Model $model): void
|
private function generateMessage(Webhook $webhook, Model $model): void
|
||||||
{
|
{
|
||||||
$class = $model::class;
|
$class = $model::class;
|
||||||
// Line is ignored because all of Firefly III's Models have an id property.
|
// 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));
|
Log::debug(sprintf('Now in generateMessage(#%d, %s#%d)', $webhook->id, $class, $model->id));
|
||||||
|
$uuid = Uuid::uuid4();
|
||||||
|
|
||||||
$uuid = Uuid::uuid4();
|
/** @var WebhookResponseModel $response */
|
||||||
$basicMessage = [
|
$response = $webhook->webhookResponses()->first();
|
||||||
|
$triggers = $this->getTriggerTitles($webhook->webhookTriggers()->get());
|
||||||
|
$basicMessage = [
|
||||||
'uuid' => $uuid->toString(),
|
'uuid' => $uuid->toString(),
|
||||||
'user_id' => 0,
|
'user_id' => 0,
|
||||||
'user_group_id' => 0,
|
'user_group_id' => 0,
|
||||||
'trigger' => $webhook->trigger->name,
|
'trigger' => $this->trigger->name,
|
||||||
'response' => WebhookResponse::from($webhook->response)->name,
|
'response' => $response->title, // guess that the database is correct.
|
||||||
'url' => $webhook->url,
|
'url' => $webhook->url,
|
||||||
'version' => sprintf('v%d', $this->getVersion()),
|
'version' => sprintf('v%d', $this->getVersion()),
|
||||||
'content' => [],
|
'content' => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
// depends on the model how user_id is set:
|
|
||||||
switch ($class) {
|
switch ($class) {
|
||||||
default:
|
default:
|
||||||
// Line is ignored because all of Firefly III's Models have an id property.
|
// Line is ignored because all of Firefly III's Models have an id property.
|
||||||
@@ -133,6 +149,21 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case Budget::class:
|
||||||
|
/** @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;
|
||||||
|
|
||||||
case TransactionGroup::class:
|
case TransactionGroup::class:
|
||||||
/** @var TransactionGroup $model */
|
/** @var TransactionGroup $model */
|
||||||
$basicMessage['user_id'] = $model->user_id;
|
$basicMessage['user_id'] = $model->user_id;
|
||||||
@@ -140,20 +171,46 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
$responseTitle = $this->getRelevantResponse($triggers, $response, $class);
|
||||||
|
|
||||||
// then depends on the response what to put in the message:
|
switch ($responseTitle) {
|
||||||
switch ($webhook->response) {
|
|
||||||
default:
|
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;
|
return;
|
||||||
|
|
||||||
case WebhookResponse::NONE->value:
|
case WebhookResponse::BUDGET->name:
|
||||||
|
$basicMessage['content'] = [];
|
||||||
|
if ($model instanceof Budget) {
|
||||||
|
$enrichment = new BudgetEnrichment();
|
||||||
|
$enrichment->setUser($model->user);
|
||||||
|
$model = $enrichment->enrichSingle($model);
|
||||||
|
$transformer = new BudgetTransformer();
|
||||||
|
$basicMessage['content'] = $transformer->transform($model);
|
||||||
|
}
|
||||||
|
if ($model instanceof BudgetLimit) {
|
||||||
|
$user = $model->budget->user;
|
||||||
|
$enrichment = new BudgetLimitEnrichment();
|
||||||
|
$enrichment->setUser($user);
|
||||||
|
|
||||||
|
$parameters = new ParameterBag();
|
||||||
|
$parameters->set('start', $model->start_date);
|
||||||
|
$parameters->set('end', $model->end_date);
|
||||||
|
|
||||||
|
$model = $enrichment->enrichSingle($model);
|
||||||
|
$transformer = new BudgetLimitTransformer();
|
||||||
|
$transformer->setParameters($parameters);
|
||||||
|
$basicMessage['content'] = $transformer->transform($model);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WebhookResponse::NONE->name:
|
||||||
$basicMessage['content'] = [];
|
$basicMessage['content'] = [];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WebhookResponse::TRANSACTIONS->value:
|
case WebhookResponse::TRANSACTIONS->name:
|
||||||
/** @var TransactionGroup $model */
|
/** @var TransactionGroup $model */
|
||||||
$transformer = new TransactionGroupTransformer();
|
$transformer = new TransactionGroupTransformer();
|
||||||
|
|
||||||
@@ -170,7 +227,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WebhookResponse::ACCOUNTS->value:
|
case WebhookResponse::ACCOUNTS->name:
|
||||||
/** @var TransactionGroup $model */
|
/** @var TransactionGroup $model */
|
||||||
$accounts = $this->collectAccounts($model);
|
$accounts = $this->collectAccounts($model);
|
||||||
$enrichment = new AccountEnrichment();
|
$enrichment = new AccountEnrichment();
|
||||||
@@ -237,4 +294,50 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
|||||||
{
|
{
|
||||||
$this->webhooks = $webhooks;
|
$this->webhooks = $webhooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getRelevantResponse(array $triggers, WebhookResponseModel $response, $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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -108,10 +108,10 @@ class BillEventHandler
|
|||||||
{
|
{
|
||||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||||
|
|
||||||
$bill = $event->bill;
|
$bill = $event->bill;
|
||||||
|
|
||||||
/** @var bool $preference */
|
/** @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) {
|
if (true === $preference) {
|
||||||
Log::debug('Bill reminder is true!');
|
Log::debug('Bill reminder is true!');
|
||||||
|
@@ -55,7 +55,7 @@ class DestroyedGroupEventHandler
|
|||||||
$engine->setObjects(new Collection([$group]));
|
$engine->setObjects(new Collection([$group]));
|
||||||
$engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION);
|
$engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION);
|
||||||
$engine->generateMessages();
|
$engine->generateMessages();
|
||||||
|
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
|
||||||
event(new RequestedSendWebhookMessages());
|
event(new RequestedSendWebhookMessages());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -121,6 +121,7 @@ class StoredGroupEventHandler
|
|||||||
$engine->generateMessages();
|
$engine->generateMessages();
|
||||||
|
|
||||||
// trigger event to send them:
|
// trigger event to send them:
|
||||||
|
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
|
||||||
event(new RequestedSendWebhookMessages());
|
event(new RequestedSendWebhookMessages());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -167,6 +167,7 @@ class UpdatedGroupEventHandler
|
|||||||
$engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION);
|
$engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION);
|
||||||
$engine->generateMessages();
|
$engine->generateMessages();
|
||||||
|
|
||||||
|
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
|
||||||
event(new RequestedSendWebhookMessages());
|
event(new RequestedSendWebhookMessages());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -40,7 +40,7 @@ class WebhookEventHandler
|
|||||||
{
|
{
|
||||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||||
if (false === config('firefly.feature_flags.webhooks') || false === config('firefly.allow_webhooks')) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -24,17 +24,37 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Handlers\Observer;
|
namespace FireflyIII\Handlers\Observer;
|
||||||
|
|
||||||
|
use FireflyIII\Enums\WebhookTrigger;
|
||||||
|
use FireflyIII\Events\RequestedSendWebhookMessages;
|
||||||
|
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Support\Facades\Amount;
|
use FireflyIII\Support\Facades\Amount;
|
||||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||||
|
use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class BudgetLimitObserver
|
class BudgetLimitObserver
|
||||||
{
|
{
|
||||||
|
use RecalculatesAvailableBudgetsTrait;
|
||||||
|
|
||||||
public function created(BudgetLimit $budgetLimit): void
|
public function created(BudgetLimit $budgetLimit): void
|
||||||
{
|
{
|
||||||
Log::debug('Observe "created" of a budget limit.');
|
Log::debug('Observe "created" of a budget limit.');
|
||||||
$this->updatePrimaryCurrencyAmount($budgetLimit);
|
$this->updatePrimaryCurrencyAmount($budgetLimit);
|
||||||
|
$this->updateAvailableBudget($budgetLimit);
|
||||||
|
|
||||||
|
$user = $budgetLimit->budget->user;
|
||||||
|
|
||||||
|
/** @var MessageGeneratorInterface $engine */
|
||||||
|
$engine = app(MessageGeneratorInterface::class);
|
||||||
|
$engine->setUser($user);
|
||||||
|
$engine->setObjects(new Collection()->push($budgetLimit));
|
||||||
|
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
|
||||||
|
$engine->generateMessages();
|
||||||
|
|
||||||
|
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
|
||||||
|
event(new RequestedSendWebhookMessages());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function updatePrimaryCurrencyAmount(BudgetLimit $budgetLimit): void
|
private function updatePrimaryCurrencyAmount(BudgetLimit $budgetLimit): void
|
||||||
@@ -60,5 +80,18 @@ class BudgetLimitObserver
|
|||||||
{
|
{
|
||||||
Log::debug('Observe "updated" of a budget limit.');
|
Log::debug('Observe "updated" of a budget limit.');
|
||||||
$this->updatePrimaryCurrencyAmount($budgetLimit);
|
$this->updatePrimaryCurrencyAmount($budgetLimit);
|
||||||
|
$this->updateAvailableBudget($budgetLimit);
|
||||||
|
|
||||||
|
$user = $budgetLimit->budget->user;
|
||||||
|
|
||||||
|
/** @var MessageGeneratorInterface $engine */
|
||||||
|
$engine = app(MessageGeneratorInterface::class);
|
||||||
|
$engine->setUser($user);
|
||||||
|
$engine->setObjects(new Collection()->push($budgetLimit));
|
||||||
|
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
|
||||||
|
$engine->generateMessages();
|
||||||
|
|
||||||
|
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
|
||||||
|
event(new RequestedSendWebhookMessages());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,19 +23,70 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Handlers\Observer;
|
namespace FireflyIII\Handlers\Observer;
|
||||||
|
|
||||||
|
use FireflyIII\Enums\WebhookTrigger;
|
||||||
|
use FireflyIII\Events\RequestedSendWebhookMessages;
|
||||||
|
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||||
use FireflyIII\Models\Attachment;
|
use FireflyIII\Models\Attachment;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||||
|
use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class BudgetObserver
|
* Class BudgetObserver
|
||||||
*/
|
*/
|
||||||
class BudgetObserver
|
class BudgetObserver
|
||||||
{
|
{
|
||||||
|
use RecalculatesAvailableBudgetsTrait;
|
||||||
|
|
||||||
|
public function created(Budget $budget): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('Observe "created" of budget #%d ("%s").', $budget->id, $budget->name));
|
||||||
|
|
||||||
|
// fire event.
|
||||||
|
$user = $budget->user;
|
||||||
|
|
||||||
|
/** @var MessageGeneratorInterface $engine */
|
||||||
|
$engine = app(MessageGeneratorInterface::class);
|
||||||
|
$engine->setUser($user);
|
||||||
|
$engine->setObjects(new Collection()->push($budget));
|
||||||
|
$engine->setTrigger(WebhookTrigger::STORE_BUDGET);
|
||||||
|
$engine->generateMessages();
|
||||||
|
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
|
||||||
|
event(new RequestedSendWebhookMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updated(Budget $budget): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name));
|
||||||
|
$user = $budget->user;
|
||||||
|
|
||||||
|
/** @var MessageGeneratorInterface $engine */
|
||||||
|
$engine = app(MessageGeneratorInterface::class);
|
||||||
|
$engine->setUser($user);
|
||||||
|
$engine->setObjects(new Collection()->push($budget));
|
||||||
|
$engine->setTrigger(WebhookTrigger::UPDATE_BUDGET);
|
||||||
|
$engine->generateMessages();
|
||||||
|
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
|
||||||
|
event(new RequestedSendWebhookMessages());
|
||||||
|
}
|
||||||
|
|
||||||
public function deleting(Budget $budget): void
|
public function deleting(Budget $budget): void
|
||||||
{
|
{
|
||||||
app('log')->debug('Observe "deleting" of a budget.');
|
Log::debug('Observe "deleting" of a budget.');
|
||||||
|
|
||||||
|
$user = $budget->user;
|
||||||
|
|
||||||
|
/** @var MessageGeneratorInterface $engine */
|
||||||
|
$engine = app(MessageGeneratorInterface::class);
|
||||||
|
$engine->setUser($user);
|
||||||
|
$engine->setObjects(new Collection()->push($budget));
|
||||||
|
$engine->setTrigger(WebhookTrigger::DESTROY_BUDGET);
|
||||||
|
$engine->generateMessages();
|
||||||
|
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
|
||||||
|
event(new RequestedSendWebhookMessages());
|
||||||
|
|
||||||
$repository = app(AttachmentRepositoryInterface::class);
|
$repository = app(AttachmentRepositoryInterface::class);
|
||||||
$repository->setUser($budget->user);
|
$repository->setUser($budget->user);
|
||||||
@@ -49,7 +100,10 @@ class BudgetObserver
|
|||||||
/** @var BudgetLimit $budgetLimit */
|
/** @var BudgetLimit $budgetLimit */
|
||||||
foreach ($budgetLimits as $budgetLimit) {
|
foreach ($budgetLimits as $budgetLimit) {
|
||||||
// this loop exists so several events are fired.
|
// this loop exists so several events are fired.
|
||||||
$budgetLimit->delete();
|
$copy = clone $budgetLimit;
|
||||||
|
$copy->id = 0;
|
||||||
|
$this->updateAvailableBudget($copy);
|
||||||
|
$budgetLimit->deleteQuietly(); // delete is quietly when in a loop.
|
||||||
}
|
}
|
||||||
|
|
||||||
$budget->notes()->delete();
|
$budget->notes()->delete();
|
||||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Handlers\Observer;
|
namespace FireflyIII\Handlers\Observer;
|
||||||
|
|
||||||
use FireflyIII\Models\TransactionGroup;
|
use FireflyIII\Models\TransactionGroup;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class TransactionGroup
|
* Class TransactionGroup
|
||||||
@@ -32,7 +33,7 @@ class TransactionGroupObserver
|
|||||||
{
|
{
|
||||||
public function deleting(TransactionGroup $transactionGroup): void
|
public function deleting(TransactionGroup $transactionGroup): void
|
||||||
{
|
{
|
||||||
app('log')->debug('Observe "deleting" of a transaction group.');
|
Log::debug('Observe "deleting" of a transaction group.');
|
||||||
foreach ($transactionGroup->transactionJournals()->get() as $journal) {
|
foreach ($transactionGroup->transactionJournals()->get() as $journal) {
|
||||||
$journal->delete();
|
$journal->delete();
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,7 @@ namespace FireflyIII\Handlers\Observer;
|
|||||||
use FireflyIII\Models\Attachment;
|
use FireflyIII\Models\Attachment;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class TransactionJournalObserver
|
* Class TransactionJournalObserver
|
||||||
@@ -34,7 +35,7 @@ class TransactionJournalObserver
|
|||||||
{
|
{
|
||||||
public function deleting(TransactionJournal $transactionJournal): void
|
public function deleting(TransactionJournal $transactionJournal): void
|
||||||
{
|
{
|
||||||
app('log')->debug('Observe "deleting" of a transaction journal.');
|
Log::debug('Observe "deleting" of a transaction journal.');
|
||||||
|
|
||||||
$repository = app(AttachmentRepositoryInterface::class);
|
$repository = app(AttachmentRepositoryInterface::class);
|
||||||
$repository->setUser($transactionJournal->user);
|
$repository->setUser($transactionJournal->user);
|
||||||
|
@@ -72,7 +72,7 @@ class TransactionObserver
|
|||||||
}
|
}
|
||||||
|
|
||||||
$transaction->saveQuietly();
|
$transaction->saveQuietly();
|
||||||
Log::debug('Transaction primary currency amounts are updated.');
|
Log::debug(sprintf('Transaction #%d primary currency amounts are updated.', $transaction->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleting(?Transaction $transaction): void
|
public function deleting(?Transaction $transaction): void
|
||||||
|
@@ -851,7 +851,7 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
*/
|
*/
|
||||||
public function getPaginatedGroups(): LengthAwarePaginator
|
public function getPaginatedGroups(): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
$set = $this->getGroups();
|
$set = $this->getGroups();
|
||||||
if (0 === $this->limit) {
|
if (0 === $this->limit) {
|
||||||
$this->setLimit(50);
|
$this->setLimit(50);
|
||||||
}
|
}
|
||||||
@@ -861,8 +861,9 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
|
|
||||||
return new LengthAwarePaginator($set, $this->total, $total, 1);
|
return new LengthAwarePaginator($set, $this->total, $total, 1);
|
||||||
}
|
}
|
||||||
|
$limit = $this->limit ?? 1;
|
||||||
|
|
||||||
return new LengthAwarePaginator($set, $this->total, $this->limit, $this->page);
|
return new LengthAwarePaginator($set, $this->total, $limit, $this->page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -158,18 +158,8 @@ class ShowController extends Controller
|
|||||||
|
|
||||||
Log::debug('End collect transactions');
|
Log::debug('End collect transactions');
|
||||||
$timer->stop('collection');
|
$timer->stop('collection');
|
||||||
|
|
||||||
// enrich data in arrays.
|
|
||||||
|
|
||||||
// enrich
|
|
||||||
// $enrichment = new TransactionGroupEnrichment();
|
|
||||||
// $enrichment->setUser(auth()->user());
|
|
||||||
// $groups->setCollection($enrichment->enrich($groups->getCollection()));
|
|
||||||
|
|
||||||
|
|
||||||
$groups->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]));
|
$groups->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]));
|
||||||
$showAll = false;
|
$showAll = false;
|
||||||
// correct
|
|
||||||
$now = today()->endOfDay();
|
$now = today()->endOfDay();
|
||||||
if ($now->gt($end) || $now->lt($start)) {
|
if ($now->gt($end) || $now->lt($start)) {
|
||||||
$now = $end;
|
$now = $end;
|
||||||
|
@@ -129,7 +129,6 @@ class IndexController extends Controller
|
|||||||
$spent = $spentArr[$this->primaryCurrency->id]['sum'] ?? '0';
|
$spent = $spentArr[$this->primaryCurrency->id]['sum'] ?? '0';
|
||||||
unset($spentArr);
|
unset($spentArr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// number of days for consistent budgeting.
|
// number of days for consistent budgeting.
|
||||||
$activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description.
|
$activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description.
|
||||||
$activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description.
|
$activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description.
|
||||||
|
@@ -37,6 +37,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
|||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use FireflyIII\Support\Facades\Preferences;
|
use FireflyIII\Support\Facades\Preferences;
|
||||||
use FireflyIII\Support\Facades\Steam;
|
use FireflyIII\Support\Facades\Steam;
|
||||||
|
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||||
use FireflyIII\Support\Http\Controllers\AugumentData;
|
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||||
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
||||||
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
||||||
@@ -504,6 +505,7 @@ class AccountController extends Controller
|
|||||||
Log::debug(sprintf('Step is %s', $step));
|
Log::debug(sprintf('Step is %s', $step));
|
||||||
$locale = Steam::getLocale();
|
$locale = Steam::getLocale();
|
||||||
$return = [];
|
$return = [];
|
||||||
|
$converter = new ExchangeRateConverter();
|
||||||
|
|
||||||
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
|
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
|
||||||
// have to make sure this chart is always based on the balance at the END of the period.
|
// have to make sure this chart is always based on the balance at the END of the period.
|
||||||
@@ -512,10 +514,10 @@ class AccountController extends Controller
|
|||||||
$current = app('navigation')->endOfX($current, $step, null);
|
$current = app('navigation')->endOfX($current, $step, null);
|
||||||
$format = (string)trans('config.month_and_day_js', [], $locale);
|
$format = (string)trans('config.month_and_day_js', [], $locale);
|
||||||
$accountCurrency = $this->accountRepository->getAccountCurrency($account);
|
$accountCurrency = $this->accountRepository->getAccountCurrency($account);
|
||||||
|
Log::debug('Get and filter balance for entire range start');
|
||||||
$range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary);
|
$range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary);
|
||||||
$range = Steam::filterAccountBalances($range, $account, $this->convertToPrimary, $accountCurrency);
|
$range = Steam::filterAccountBalances($range, $account, $this->convertToPrimary, $accountCurrency);
|
||||||
|
Log::debug('Get and filter balance for entire range end');
|
||||||
// temp, get end balance.
|
// temp, get end balance.
|
||||||
Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
|
Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
|
||||||
Steam::finalAccountBalance($account, $end);
|
Steam::finalAccountBalance($account, $end);
|
||||||
@@ -552,7 +554,15 @@ class AccountController extends Controller
|
|||||||
$carbon = Carbon::createFromFormat('Y-m-d', $newRange[$expectedIndex]['date'])->endOfDay();
|
$carbon = Carbon::createFromFormat('Y-m-d', $newRange[$expectedIndex]['date'])->endOfDay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log::debug(sprintf('momentBalance is now %s', json_encode($momentBalance)));
|
Log::debug(sprintf('momentBalance[%s] is now %s', $current->format('Y-m-d H:i:s'), json_encode($momentBalance)));
|
||||||
|
|
||||||
|
// check, perhaps recalculate the amount in currency X if the
|
||||||
|
if ($accountCurrency->id !== $this->primaryCurrency->id && $this->convertToPrimary && array_key_exists($accountCurrency->code, $momentBalance)) {
|
||||||
|
$converted = $converter->convert($accountCurrency, $this->primaryCurrency, $current, $momentBalance[$accountCurrency->code]);
|
||||||
|
$momentBalance['pc_balance'] = $converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$return = $this->updateChartKeys($return, $momentBalance);
|
$return = $this->updateChartKeys($return, $momentBalance);
|
||||||
$previous = $momentBalance;
|
$previous = $momentBalance;
|
||||||
|
|
||||||
|
@@ -105,7 +105,6 @@ class EditController extends Controller
|
|||||||
/** @var RecurrenceTransformer $transformer */
|
/** @var RecurrenceTransformer $transformer */
|
||||||
$transformer = app(RecurrenceTransformer::class);
|
$transformer = app(RecurrenceTransformer::class);
|
||||||
$transformer->setParameters(new ParameterBag());
|
$transformer->setParameters(new ParameterBag());
|
||||||
|
|
||||||
$array = $transformer->transform($recurrence);
|
$array = $transformer->transform($recurrence);
|
||||||
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets());
|
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets());
|
||||||
$bills = ExpandedForm::makeSelectListWithEmpty($this->billRepository->getActiveBills());
|
$bills = ExpandedForm::makeSelectListWithEmpty($this->billRepository->getActiveBills());
|
||||||
|
@@ -28,8 +28,7 @@ use FireflyIII\Http\Controllers\Controller;
|
|||||||
use FireflyIII\Http\Requests\TriggerRecurrenceRequest;
|
use FireflyIII\Http\Requests\TriggerRecurrenceRequest;
|
||||||
use FireflyIII\Jobs\CreateRecurringTransactions;
|
use FireflyIII\Jobs\CreateRecurringTransactions;
|
||||||
use FireflyIII\Models\Recurrence;
|
use FireflyIII\Models\Recurrence;
|
||||||
use FireflyIII\Models\TransactionGroup;
|
use FireflyIII\Support\Facades\Preferences;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
@@ -58,20 +57,11 @@ class TriggerController extends Controller
|
|||||||
app('log')->debug('Done with recurrence.');
|
app('log')->debug('Done with recurrence.');
|
||||||
|
|
||||||
$groups = $job->getGroups();
|
$groups = $job->getGroups();
|
||||||
|
$this->repository->markGroupsAsNow($groups);
|
||||||
/** @var TransactionGroup $group */
|
|
||||||
foreach ($groups as $group) {
|
|
||||||
/** @var TransactionJournal $journal */
|
|
||||||
foreach ($group->transactionJournals as $journal) {
|
|
||||||
app('log')->debug(sprintf('Set date of journal #%d to today!', $journal->id));
|
|
||||||
$journal->date = today(config('app.timezone'));
|
|
||||||
$journal->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$recurrence->latest_date = $backupDate;
|
$recurrence->latest_date = $backupDate;
|
||||||
$recurrence->latest_date_tz = $backupDate?->format('e');
|
$recurrence->latest_date_tz = $backupDate?->format('e');
|
||||||
$recurrence->save();
|
$recurrence->save();
|
||||||
app('preferences')->mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (0 === $groups->count()) {
|
if (0 === $groups->count()) {
|
||||||
$request->session()->flash('info', (string) trans('firefly.no_new_transaction_in_recurrence'));
|
$request->session()->flash('info', (string) trans('firefly.no_new_transaction_in_recurrence'));
|
||||||
|
@@ -25,7 +25,6 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Http\Controllers\Rule;
|
namespace FireflyIII\Http\Controllers\Rule;
|
||||||
|
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Carbon\Carbon;
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Http\Requests\SelectTransactionsRequest;
|
use FireflyIII\Http\Requests\SelectTransactionsRequest;
|
||||||
@@ -74,20 +73,16 @@ class SelectController extends Controller
|
|||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
$accounts = implode(',', $request->get('accounts'));
|
$accounts = implode(',', $request->get('accounts'));
|
||||||
$startDate = new Carbon($request->get('start'));
|
|
||||||
$endDate = new Carbon($request->get('end'));
|
|
||||||
|
|
||||||
// create new rule engine:
|
// create new rule engine:
|
||||||
$newRuleEngine = app(RuleEngineInterface::class);
|
$newRuleEngine = app(RuleEngineInterface::class);
|
||||||
$newRuleEngine->setUser($user);
|
$newRuleEngine->setUser($user);
|
||||||
|
|
||||||
// add extra operators:
|
// add extra operators:
|
||||||
$newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]);
|
|
||||||
$newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]);
|
|
||||||
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
|
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
|
||||||
|
|
||||||
// set rules:
|
// set rules:
|
||||||
$newRuleEngine->setRules(new Collection([$rule]));
|
$newRuleEngine->setRules(new Collection()->push($rule));
|
||||||
$newRuleEngine->fire();
|
$newRuleEngine->fire();
|
||||||
$resultCount = $newRuleEngine->getResults();
|
$resultCount = $newRuleEngine->getResults();
|
||||||
|
|
||||||
@@ -107,11 +102,9 @@ class SelectController extends Controller
|
|||||||
return redirect(route('rules.index'));
|
return redirect(route('rules.index'));
|
||||||
}
|
}
|
||||||
// does the user have shared accounts?
|
// does the user have shared accounts?
|
||||||
$first = session('first', today(config('app.timezone'))->subYear())->format('Y-m-d');
|
|
||||||
$today = today(config('app.timezone'))->format('Y-m-d');
|
|
||||||
$subTitle = (string) trans('firefly.apply_rule_selection', ['title' => $rule->title]);
|
$subTitle = (string) trans('firefly.apply_rule_selection', ['title' => $rule->title]);
|
||||||
|
|
||||||
return view('rules.rule.select-transactions', compact('first', 'today', 'rule', 'subTitle'));
|
return view('rules.rule.select-transactions', compact('rule', 'subTitle'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -25,11 +25,9 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Http\Controllers\RuleGroup;
|
namespace FireflyIII\Http\Controllers\RuleGroup;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Carbon\Carbon;
|
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Http\Requests\SelectTransactionsRequest;
|
use FireflyIII\Http\Requests\SelectTransactionsRequest;
|
||||||
use FireflyIII\Models\RuleGroup;
|
use FireflyIII\Models\RuleGroup;
|
||||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
|
||||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
@@ -42,8 +40,6 @@ use Illuminate\View\View;
|
|||||||
*/
|
*/
|
||||||
class ExecutionController extends Controller
|
class ExecutionController extends Controller
|
||||||
{
|
{
|
||||||
private RuleGroupRepositoryInterface $ruleGroupRepository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ExecutionController constructor.
|
* ExecutionController constructor.
|
||||||
*/
|
*/
|
||||||
@@ -56,7 +52,6 @@ class ExecutionController extends Controller
|
|||||||
app('view')->share('title', (string) trans('firefly.rules'));
|
app('view')->share('title', (string) trans('firefly.rules'));
|
||||||
app('view')->share('mainTitleIcon', 'fa-random');
|
app('view')->share('mainTitleIcon', 'fa-random');
|
||||||
|
|
||||||
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
@@ -74,15 +69,11 @@ class ExecutionController extends Controller
|
|||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
$accounts = implode(',', $request->get('accounts'));
|
$accounts = implode(',', $request->get('accounts'));
|
||||||
$startDate = new Carbon($request->get('start'));
|
|
||||||
$endDate = new Carbon($request->get('end'));
|
|
||||||
// create new rule engine:
|
// create new rule engine:
|
||||||
$newRuleEngine = app(RuleEngineInterface::class);
|
$newRuleEngine = app(RuleEngineInterface::class);
|
||||||
$newRuleEngine->setUser($user);
|
$newRuleEngine->setUser($user);
|
||||||
|
|
||||||
// add extra operators:
|
// add extra operators:
|
||||||
$newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]);
|
|
||||||
$newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]);
|
|
||||||
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
|
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
|
||||||
|
|
||||||
// set rules:
|
// set rules:
|
||||||
@@ -104,10 +95,8 @@ class ExecutionController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function selectTransactions(RuleGroup $ruleGroup)
|
public function selectTransactions(RuleGroup $ruleGroup)
|
||||||
{
|
{
|
||||||
$first = session('first')->format('Y-m-d');
|
|
||||||
$today = today(config('app.timezone'))->format('Y-m-d');
|
|
||||||
$subTitle = (string) trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]);
|
$subTitle = (string) trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]);
|
||||||
|
|
||||||
return view('rules.rule-group.select-transactions', compact('first', 'today', 'ruleGroup', 'subTitle'));
|
return view('rules.rule-group.select-transactions', compact('ruleGroup', 'subTitle'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -303,22 +303,22 @@ class ConvertController extends Controller
|
|||||||
private function convertJournal(TransactionJournal $journal, TransactionType $transactionType, array $data): TransactionJournal
|
private function convertJournal(TransactionJournal $journal, TransactionType $transactionType, array $data): TransactionJournal
|
||||||
{
|
{
|
||||||
/** @var AccountValidator $validator */
|
/** @var AccountValidator $validator */
|
||||||
$validator = app(AccountValidator::class);
|
$validator = app(AccountValidator::class);
|
||||||
$validator->setUser(auth()->user());
|
$validator->setUser(auth()->user());
|
||||||
$validator->setTransactionType($transactionType->type);
|
$validator->setTransactionType($transactionType->type);
|
||||||
|
|
||||||
$sourceId = $data['source_id'][$journal->id] ?? null;
|
$sourceId = $data['source_id'][$journal->id] ?? null;
|
||||||
$sourceName = $data['source_name'][$journal->id] ?? null;
|
$sourceName = $data['source_name'][$journal->id] ?? null;
|
||||||
$destinationId = $data['destination_id'][$journal->id] ?? null;
|
$destinationId = $data['destination_id'][$journal->id] ?? null;
|
||||||
$destinationName = $data['destination_name'][$journal->id] ?? null;
|
$destinationName = $data['destination_name'][$journal->id] ?? null;
|
||||||
|
|
||||||
// double check it's not an empty string.
|
// double check it's not an empty string.
|
||||||
$sourceId = '' === $sourceId || null === $sourceId ? null : (int) $sourceId;
|
$sourceId = '' === $sourceId || null === $sourceId ? null : (int) $sourceId;
|
||||||
$sourceName = '' === $sourceName ? null : (string) $sourceName;
|
$sourceName = '' === $sourceName ? null : (string) $sourceName;
|
||||||
$destinationId = '' === $destinationId || null === $destinationId ? null : (int) $destinationId;
|
$destinationId = '' === $destinationId || null === $destinationId ? null : (int) $destinationId;
|
||||||
$destinationName = '' === $destinationName ? null : (string) $destinationName;
|
$destinationName = '' === $destinationName ? null : (string) $destinationName;
|
||||||
$validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]);
|
$validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]);
|
||||||
$validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]);
|
$validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]);
|
||||||
|
|
||||||
if (false === $validSource) {
|
if (false === $validSource) {
|
||||||
throw new FireflyException(sprintf(trans('firefly.convert_invalid_source'), $journal->id));
|
throw new FireflyException(sprintf(trans('firefly.convert_invalid_source'), $journal->id));
|
||||||
@@ -329,7 +329,7 @@ class ConvertController extends Controller
|
|||||||
|
|
||||||
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
|
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
|
||||||
|
|
||||||
$update = [
|
$update = [
|
||||||
'source_id' => $sourceId,
|
'source_id' => $sourceId,
|
||||||
'source_name' => $sourceName,
|
'source_name' => $sourceName,
|
||||||
'destination_id' => $destinationId,
|
'destination_id' => $destinationId,
|
||||||
@@ -337,6 +337,9 @@ class ConvertController extends Controller
|
|||||||
'type' => $transactionType->type,
|
'type' => $transactionType->type,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
|
||||||
|
$amount = $sourceTransaction?->amount ?? '0';
|
||||||
|
|
||||||
// also set the currency to the currency of the source account, in case you're converting a deposit into a transfer.
|
// also set the currency to the currency of the source account, in case you're converting a deposit into a transfer.
|
||||||
if (TransactionTypeEnum::TRANSFER->value === $transactionType->type && TransactionTypeEnum::DEPOSIT->value === $journal->transactionType->type) {
|
if (TransactionTypeEnum::TRANSFER->value === $transactionType->type && TransactionTypeEnum::DEPOSIT->value === $journal->transactionType->type) {
|
||||||
$source = $this->accountRepository->find((int) $sourceId);
|
$source = $this->accountRepository->find((int) $sourceId);
|
||||||
@@ -346,12 +349,25 @@ class ConvertController extends Controller
|
|||||||
if ($sourceCurrency instanceof TransactionCurrency && $destCurrency instanceof TransactionCurrency && $sourceCurrency->code !== $destCurrency->code) {
|
if ($sourceCurrency instanceof TransactionCurrency && $destCurrency instanceof TransactionCurrency && $sourceCurrency->code !== $destCurrency->code) {
|
||||||
$update['currency_id'] = $sourceCurrency->id;
|
$update['currency_id'] = $sourceCurrency->id;
|
||||||
$update['foreign_currency_id'] = $destCurrency->id;
|
$update['foreign_currency_id'] = $destCurrency->id;
|
||||||
$update['foreign_amount'] = '1'; // not the best solution but at this point the amount is hard to get.
|
$update['foreign_amount'] = Steam::positive($amount); // not the best solution but at this point the amount is hard to get.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// same thing for converting a withdrawal into a transfer, but with the currency of the destination account.
|
||||||
|
if (TransactionTypeEnum::TRANSFER->value === $transactionType->type && TransactionTypeEnum::WITHDRAWAL->value === $journal->transactionType->type) {
|
||||||
|
$source = $this->accountRepository->find((int) $sourceId);
|
||||||
|
$sourceCurrency = $this->accountRepository->getAccountCurrency($source);
|
||||||
|
$dest = $this->accountRepository->find((int) $destinationId);
|
||||||
|
$destCurrency = $this->accountRepository->getAccountCurrency($dest);
|
||||||
|
if ($sourceCurrency instanceof TransactionCurrency && $destCurrency instanceof TransactionCurrency && $sourceCurrency->code !== $destCurrency->code) {
|
||||||
|
$update['currency_id'] = $sourceCurrency->id;
|
||||||
|
$update['foreign_currency_id'] = $destCurrency->id;
|
||||||
|
$update['foreign_amount'] = Steam::positive($amount); // not the best solution but at this point the amount is hard to get.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var JournalUpdateService $service */
|
/** @var JournalUpdateService $service */
|
||||||
$service = app(JournalUpdateService::class);
|
$service = app(JournalUpdateService::class);
|
||||||
$service->setTransactionJournal($journal);
|
$service->setTransactionJournal($journal);
|
||||||
$service->setData($update);
|
$service->setData($update);
|
||||||
$service->update();
|
$service->update();
|
||||||
|
@@ -41,8 +41,6 @@ class SelectTransactionsRequest extends FormRequest
|
|||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'start' => 'required|date|after:1970-01-02|before:2038-01-17|before:end|required_with:end',
|
|
||||||
'end' => 'required|date|after:1970-01-02|before:2038-01-17|after:start|required_with:start',
|
|
||||||
'accounts' => 'required',
|
'accounts' => 'required',
|
||||||
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
|
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
|
||||||
];
|
];
|
||||||
|
@@ -37,6 +37,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
|||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class CreateAutoBudgetLimits
|
* Class CreateAutoBudgetLimits
|
||||||
@@ -59,7 +60,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
$newDate = clone $date;
|
$newDate = clone $date;
|
||||||
$newDate->startOfDay();
|
$newDate->startOfDay();
|
||||||
$this->date = $newDate;
|
$this->date = $newDate;
|
||||||
app('log')->debug(sprintf('Created new CreateAutoBudgetLimits("%s")', $this->date->format('Y-m-d')));
|
Log::debug(sprintf('Created new CreateAutoBudgetLimits("%s")', $this->date->format('Y-m-d')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,9 +71,9 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
app('log')->debug(sprintf('Now at start of CreateAutoBudgetLimits() job for %s.', $this->date->format('D d M Y')));
|
Log::debug(sprintf('Now at start of CreateAutoBudgetLimits() job for %s.', $this->date->format('D d M Y')));
|
||||||
$autoBudgets = AutoBudget::get();
|
$autoBudgets = AutoBudget::get();
|
||||||
app('log')->debug(sprintf('Found %d auto budgets.', $autoBudgets->count()));
|
Log::debug(sprintf('Found %d auto budgets.', $autoBudgets->count()));
|
||||||
foreach ($autoBudgets as $autoBudget) {
|
foreach ($autoBudgets as $autoBudget) {
|
||||||
$this->handleAutoBudget($autoBudget);
|
$this->handleAutoBudget($autoBudget);
|
||||||
}
|
}
|
||||||
@@ -84,18 +85,18 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
private function handleAutoBudget(AutoBudget $autoBudget): void
|
private function handleAutoBudget(AutoBudget $autoBudget): void
|
||||||
{
|
{
|
||||||
if (null === $autoBudget->budget) {
|
if (null === $autoBudget->budget) {
|
||||||
app('log')->info(sprintf('Auto budget #%d is associated with a deleted budget.', $autoBudget->id));
|
Log::info(sprintf('Auto budget #%d is associated with a deleted budget.', $autoBudget->id));
|
||||||
$autoBudget->delete();
|
$autoBudget->delete();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (false === $autoBudget->budget->active) {
|
if (false === $autoBudget->budget->active) {
|
||||||
app('log')->info(sprintf('Auto budget #%d is associated with an inactive budget.', $autoBudget->id));
|
Log::info(sprintf('Auto budget #%d is associated with an inactive budget.', $autoBudget->id));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!$this->isMagicDay($autoBudget)) {
|
if (!$this->isMagicDay($autoBudget)) {
|
||||||
app('log')->info(
|
Log::info(
|
||||||
sprintf(
|
sprintf(
|
||||||
'Today (%s) is not a magic day for %s auto-budget #%d (part of budget #%d "%s")',
|
'Today (%s) is not a magic day for %s auto-budget #%d (part of budget #%d "%s")',
|
||||||
$this->date->format('Y-m-d'),
|
$this->date->format('Y-m-d'),
|
||||||
@@ -105,11 +106,11 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
$autoBudget->budget->name
|
$autoBudget->budget->name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app('log')->info(
|
Log::info(
|
||||||
sprintf(
|
sprintf(
|
||||||
'Today (%s) is a magic day for %s auto-budget #%d (part of budget #%d "%s")',
|
'Today (%s) is a magic day for %s auto-budget #%d (part of budget #%d "%s")',
|
||||||
$this->date->format('Y-m-d'),
|
$this->date->format('Y-m-d'),
|
||||||
@@ -131,7 +132,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
// that's easy: create one.
|
// that's easy: create one.
|
||||||
// do nothing else.
|
// do nothing else.
|
||||||
$this->createBudgetLimit($autoBudget, $start, $end);
|
$this->createBudgetLimit($autoBudget, $start, $end);
|
||||||
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -139,18 +140,18 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
if (!$budgetLimit instanceof BudgetLimit && AutoBudgetType::AUTO_BUDGET_ROLLOVER->value === (int) $autoBudget->auto_budget_type) {
|
if (!$budgetLimit instanceof BudgetLimit && AutoBudgetType::AUTO_BUDGET_ROLLOVER->value === (int) $autoBudget->auto_budget_type) {
|
||||||
// budget limit exists already,
|
// budget limit exists already,
|
||||||
$this->createRollover($autoBudget);
|
$this->createRollover($autoBudget);
|
||||||
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!$budgetLimit instanceof BudgetLimit && AutoBudgetType::AUTO_BUDGET_ADJUSTED->value === (int) $autoBudget->auto_budget_type) {
|
if (!$budgetLimit instanceof BudgetLimit && AutoBudgetType::AUTO_BUDGET_ADJUSTED->value === (int) $autoBudget->auto_budget_type) {
|
||||||
// budget limit exists already,
|
// budget limit exists already,
|
||||||
$this->createAdjustedLimit($autoBudget);
|
$this->createAdjustedLimit($autoBudget);
|
||||||
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,7 +194,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
|
|
||||||
private function findBudgetLimit(Budget $budget, Carbon $start, Carbon $end): ?BudgetLimit
|
private function findBudgetLimit(Budget $budget, Carbon $start, Carbon $end): ?BudgetLimit
|
||||||
{
|
{
|
||||||
app('log')->debug(
|
Log::debug(
|
||||||
sprintf(
|
sprintf(
|
||||||
'Going to find a budget limit for budget #%d ("%s") between %s and %s',
|
'Going to find a budget limit for budget #%d ("%s") between %s and %s',
|
||||||
$budget->id,
|
$budget->id,
|
||||||
@@ -212,21 +213,21 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
|
|
||||||
private function createBudgetLimit(AutoBudget $autoBudget, Carbon $start, Carbon $end, ?string $amount = null): void
|
private function createBudgetLimit(AutoBudget $autoBudget, Carbon $start, Carbon $end, ?string $amount = null): void
|
||||||
{
|
{
|
||||||
app('log')->debug(sprintf('No budget limit exist. Must create one for auto-budget #%d', $autoBudget->id));
|
Log::debug(sprintf('No budget limit exist. Must create one for auto-budget #%d', $autoBudget->id));
|
||||||
if (null !== $amount) {
|
if (null !== $amount) {
|
||||||
app('log')->debug(sprintf('Amount is overruled and will be set to %s', $amount));
|
Log::debug(sprintf('Amount is overruled and will be set to %s', $amount));
|
||||||
}
|
}
|
||||||
$budgetLimit = new BudgetLimit();
|
$budgetLimit = new BudgetLimit();
|
||||||
$budgetLimit->budget()->associate($autoBudget->budget);
|
$budgetLimit->budget()->associate($autoBudget->budget);
|
||||||
$budgetLimit->transactionCurrency()->associate($autoBudget->transactionCurrency);
|
$budgetLimit->transactionCurrency()->associate($autoBudget->transactionCurrency);
|
||||||
$budgetLimit->start_date = $start;
|
$budgetLimit->start_date = clone $start;
|
||||||
$budgetLimit->end_date = $end;
|
$budgetLimit->end_date = clone $end;
|
||||||
$budgetLimit->amount = $amount ?? $autoBudget->amount;
|
$budgetLimit->amount = $amount ?? $autoBudget->amount;
|
||||||
$budgetLimit->period = $autoBudget->period;
|
$budgetLimit->period = $autoBudget->period;
|
||||||
$budgetLimit->generated = 1;
|
$budgetLimit->generated = 1;
|
||||||
$budgetLimit->save();
|
$budgetLimit->save();
|
||||||
|
|
||||||
app('log')->debug(sprintf('Created budget limit #%d.', $budgetLimit->id));
|
Log::debug(sprintf('Created budget limit #%d.', $budgetLimit->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -234,7 +235,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
private function createRollover(AutoBudget $autoBudget): void
|
private function createRollover(AutoBudget $autoBudget): void
|
||||||
{
|
{
|
||||||
app('log')->debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id));
|
||||||
// current period:
|
// current period:
|
||||||
$start = app('navigation')->startOfPeriod($this->date, $autoBudget->period);
|
$start = app('navigation')->startOfPeriod($this->date, $autoBudget->period);
|
||||||
$end = app('navigation')->endOfPeriod($start, $autoBudget->period);
|
$end = app('navigation')->endOfPeriod($start, $autoBudget->period);
|
||||||
@@ -243,7 +244,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
$previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period);
|
$previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period);
|
||||||
$previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period);
|
$previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period);
|
||||||
|
|
||||||
app('log')->debug(
|
Log::debug(
|
||||||
sprintf(
|
sprintf(
|
||||||
'Current period is %s-%s, so previous period is %s-%s',
|
'Current period is %s-%s, so previous period is %s-%s',
|
||||||
$start->format('Y-m-d'),
|
$start->format('Y-m-d'),
|
||||||
@@ -257,44 +258,44 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
$budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd);
|
$budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd);
|
||||||
|
|
||||||
if (!$budgetLimit instanceof BudgetLimit) {
|
if (!$budgetLimit instanceof BudgetLimit) {
|
||||||
app('log')->debug('No budget limit exists in previous period, so create one.');
|
Log::debug('No budget limit exists in previous period, so create one.');
|
||||||
// if not, create it and we're done.
|
// if not, create it and we're done.
|
||||||
$this->createBudgetLimit($autoBudget, $start, $end);
|
$this->createBudgetLimit($autoBudget, $start, $end);
|
||||||
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app('log')->debug('Budget limit exists for previous period.');
|
Log::debug('Budget limit exists for previous period.');
|
||||||
// if has one, calculate expenses and use that as a base.
|
// if has one, calculate expenses and use that as a base.
|
||||||
$repository = app(OperationsRepositoryInterface::class);
|
$repository = app(OperationsRepositoryInterface::class);
|
||||||
$repository->setUser($autoBudget->budget->user);
|
$repository->setUser($autoBudget->budget->user);
|
||||||
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
|
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
|
||||||
$currencyId = $autoBudget->transaction_currency_id;
|
$currencyId = $autoBudget->transaction_currency_id;
|
||||||
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
|
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
|
||||||
app('log')->debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
|
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
|
||||||
|
|
||||||
// if you spent more in previous budget period, than whatever you had previous budget period, the amount resets
|
// if you spent more in previous budget period, than whatever you had previous budget period, the amount resets
|
||||||
// previous budget limit + spent
|
// previous budget limit + spent
|
||||||
$budgetLeft = bcadd($budgetLimit->amount, $spentAmount);
|
$budgetLeft = bcadd($budgetLimit->amount, $spentAmount);
|
||||||
$totalAmount = $autoBudget->amount;
|
$totalAmount = $autoBudget->amount;
|
||||||
app('log')->debug(sprintf('Total amount left for previous budget period is %s', $budgetLeft));
|
Log::debug(sprintf('Total amount left for previous budget period is %s', $budgetLeft));
|
||||||
|
|
||||||
if (-1 !== bccomp('0', $budgetLeft)) {
|
if (-1 !== bccomp('0', $budgetLeft)) {
|
||||||
app('log')->info(sprintf('The amount left is negative, so it will be reset to %s.', $totalAmount));
|
Log::info(sprintf('The amount left is negative, so it will be reset to %s.', $totalAmount));
|
||||||
}
|
}
|
||||||
if (1 !== bccomp('0', $budgetLeft)) {
|
if (1 !== bccomp('0', $budgetLeft)) {
|
||||||
$totalAmount = bcadd($budgetLeft, $totalAmount);
|
$totalAmount = bcadd($budgetLeft, $totalAmount);
|
||||||
app('log')->info(sprintf('The amount left is positive, so the new amount will be %s.', $totalAmount));
|
Log::info(sprintf('The amount left is positive, so the new amount will be %s.', $totalAmount));
|
||||||
}
|
}
|
||||||
|
|
||||||
// create budget limit:
|
// create budget limit:
|
||||||
$this->createBudgetLimit($autoBudget, $start, $end, $totalAmount);
|
$this->createBudgetLimit($autoBudget, $start, $end, $totalAmount);
|
||||||
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createAdjustedLimit(AutoBudget $autoBudget): void
|
private function createAdjustedLimit(AutoBudget $autoBudget): void
|
||||||
{
|
{
|
||||||
app('log')->debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id));
|
||||||
// current period:
|
// current period:
|
||||||
$start = app('navigation')->startOfPeriod($this->date, $autoBudget->period);
|
$start = app('navigation')->startOfPeriod($this->date, $autoBudget->period);
|
||||||
$end = app('navigation')->endOfPeriod($start, $autoBudget->period);
|
$end = app('navigation')->endOfPeriod($start, $autoBudget->period);
|
||||||
@@ -303,7 +304,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
$previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period);
|
$previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period);
|
||||||
$previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period);
|
$previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period);
|
||||||
|
|
||||||
app('log')->debug(
|
Log::debug(
|
||||||
sprintf(
|
sprintf(
|
||||||
'Current period is %s-%s, so previous period is %s-%s',
|
'Current period is %s-%s, so previous period is %s-%s',
|
||||||
$start->format('Y-m-d'),
|
$start->format('Y-m-d'),
|
||||||
@@ -317,13 +318,13 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
$budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd);
|
$budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd);
|
||||||
|
|
||||||
if (!$budgetLimit instanceof BudgetLimit) {
|
if (!$budgetLimit instanceof BudgetLimit) {
|
||||||
app('log')->debug('No budget limit exists in previous period, so create one.');
|
Log::debug('No budget limit exists in previous period, so create one.');
|
||||||
// if not, create standard amount, and we're done.
|
// if not, create standard amount, and we're done.
|
||||||
$this->createBudgetLimit($autoBudget, $start, $end);
|
$this->createBudgetLimit($autoBudget, $start, $end);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app('log')->debug('Budget limit exists for previous period.');
|
Log::debug('Budget limit exists for previous period.');
|
||||||
|
|
||||||
// if has one, calculate expenses and use that as a base.
|
// if has one, calculate expenses and use that as a base.
|
||||||
$repository = app(OperationsRepositoryInterface::class);
|
$repository = app(OperationsRepositoryInterface::class);
|
||||||
@@ -331,31 +332,31 @@ class CreateAutoBudgetLimits implements ShouldQueue
|
|||||||
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
|
$spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
|
||||||
$currencyId = $autoBudget->transaction_currency_id;
|
$currencyId = $autoBudget->transaction_currency_id;
|
||||||
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
|
$spentAmount = $spent[$currencyId]['sum'] ?? '0';
|
||||||
app('log')->debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
|
Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
|
||||||
|
|
||||||
// what you spent in previous period PLUS the amount for the current period,
|
// what you spent in previous period PLUS the amount for the current period,
|
||||||
// if that is more than zero, that's the amount that will be set.
|
// if that is more than zero, that's the amount that will be set.
|
||||||
|
|
||||||
$budgetAvailable = bcadd(bcadd($budgetLimit->amount, $autoBudget->amount), $spentAmount);
|
$budgetAvailable = bcadd(bcadd($budgetLimit->amount, $autoBudget->amount), $spentAmount);
|
||||||
$totalAmount = $autoBudget->amount;
|
$totalAmount = $autoBudget->amount;
|
||||||
app('log')->debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable));
|
Log::debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable));
|
||||||
|
|
||||||
if (-1 !== bccomp($budgetAvailable, $totalAmount)) {
|
if (-1 !== bccomp($budgetAvailable, $totalAmount)) {
|
||||||
app('log')->info(sprintf('There is no overspending, no need to adjust. Budget limit amount will be %s.', $budgetAvailable));
|
Log::info(sprintf('There is no overspending, no need to adjust. Budget limit amount will be %s.', $budgetAvailable));
|
||||||
// create budget limit:
|
// create budget limit:
|
||||||
$this->createBudgetLimit($autoBudget, $start, $end, $budgetAvailable);
|
$this->createBudgetLimit($autoBudget, $start, $end, $budgetAvailable);
|
||||||
}
|
}
|
||||||
if (1 !== bccomp($budgetAvailable, $totalAmount) && 1 === bccomp($budgetAvailable, '0')) {
|
if (1 !== bccomp($budgetAvailable, $totalAmount) && 1 === bccomp($budgetAvailable, '0')) {
|
||||||
app('log')->info(sprintf('There was overspending, so the new amount will be %s.', $budgetAvailable));
|
Log::info(sprintf('There was overspending, so the new amount will be %s.', $budgetAvailable));
|
||||||
// create budget limit:
|
// create budget limit:
|
||||||
$this->createBudgetLimit($autoBudget, $start, $end, $budgetAvailable);
|
$this->createBudgetLimit($autoBudget, $start, $end, $budgetAvailable);
|
||||||
}
|
}
|
||||||
if (1 !== bccomp($budgetAvailable, $totalAmount) && -1 === bccomp($budgetAvailable, '0')) {
|
if (1 !== bccomp($budgetAvailable, $totalAmount) && -1 === bccomp($budgetAvailable, '0')) {
|
||||||
app('log')->info('There was overspending, but so much even this period cant fix that. Reset it to 1.');
|
Log::info('There was overspending, but so much even this period cant fix that. Reset it to 1.');
|
||||||
// create budget limit:
|
// create budget limit:
|
||||||
$this->createBudgetLimit($autoBudget, $start, $end, '1');
|
$this->createBudgetLimit($autoBudget, $start, $end, '1');
|
||||||
}
|
}
|
||||||
app('log')->debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDate(Carbon $date): void
|
public function setDate(Carbon $date): void
|
||||||
|
@@ -24,9 +24,6 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Created;
|
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Deleted;
|
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Updated;
|
|
||||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@@ -37,12 +34,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|||||||
class BudgetLimit extends Model
|
class BudgetLimit extends Model
|
||||||
{
|
{
|
||||||
use ReturnsIntegerIdTrait;
|
use ReturnsIntegerIdTrait;
|
||||||
protected $dispatchesEvents
|
|
||||||
= [
|
|
||||||
'created' => Created::class,
|
|
||||||
'updated' => Updated::class,
|
|
||||||
'deleted' => Deleted::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $fillable = ['budget_id', 'start_date', 'end_date', 'start_date_tz', 'end_date_tz', 'amount', 'transaction_currency_id', 'native_amount'];
|
protected $fillable = ['budget_id', 'start_date', 'end_date', 'start_date_tz', 'end_date_tz', 'amount', 'transaction_currency_id', 'native_amount'];
|
||||||
|
|
||||||
|
@@ -24,14 +24,15 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use FireflyIII\Enums\WebhookDelivery;
|
use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum;
|
||||||
use FireflyIII\Enums\WebhookResponse;
|
use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum;
|
||||||
use FireflyIII\Enums\WebhookTrigger;
|
use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum;
|
||||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
@@ -56,7 +57,7 @@ class Webhook extends Model
|
|||||||
public static function getDeliveries(): array
|
public static function getDeliveries(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookDelivery::cases();
|
$set = WebhookDeliveryEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->value] = $item->name;
|
$array[$item->value] = $item->name;
|
||||||
}
|
}
|
||||||
@@ -67,7 +68,7 @@ class Webhook extends Model
|
|||||||
public static function getDeliveriesForValidation(): array
|
public static function getDeliveriesForValidation(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookDelivery::cases();
|
$set = WebhookDeliveryEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->name] = $item->value;
|
$array[$item->name] = $item->value;
|
||||||
$array[$item->value] = $item->value;
|
$array[$item->value] = $item->value;
|
||||||
@@ -79,7 +80,7 @@ class Webhook extends Model
|
|||||||
public static function getResponses(): array
|
public static function getResponses(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookResponse::cases();
|
$set = WebhookResponseEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->value] = $item->name;
|
$array[$item->value] = $item->name;
|
||||||
}
|
}
|
||||||
@@ -90,7 +91,7 @@ class Webhook extends Model
|
|||||||
public static function getResponsesForValidation(): array
|
public static function getResponsesForValidation(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookResponse::cases();
|
$set = WebhookResponseEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->name] = $item->value;
|
$array[$item->name] = $item->value;
|
||||||
$array[$item->value] = $item->value;
|
$array[$item->value] = $item->value;
|
||||||
@@ -102,7 +103,7 @@ class Webhook extends Model
|
|||||||
public static function getTriggers(): array
|
public static function getTriggers(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookTrigger::cases();
|
$set = WebhookTriggerEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->value] = $item->name;
|
$array[$item->value] = $item->name;
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,7 @@ class Webhook extends Model
|
|||||||
public static function getTriggersForValidation(): array
|
public static function getTriggersForValidation(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookTrigger::cases();
|
$set = WebhookTriggerEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->name] = $item->value;
|
$array[$item->name] = $item->value;
|
||||||
$array[$item->value] = $item->value;
|
$array[$item->value] = $item->value;
|
||||||
@@ -130,7 +131,7 @@ class Webhook extends Model
|
|||||||
public static function routeBinder(string $value): self
|
public static function routeBinder(string $value): self
|
||||||
{
|
{
|
||||||
if (auth()->check()) {
|
if (auth()->check()) {
|
||||||
$webhookId = (int) $value;
|
$webhookId = (int)$value;
|
||||||
|
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
@@ -155,6 +156,21 @@ class Webhook extends Model
|
|||||||
return $this->hasMany(WebhookMessage::class);
|
return $this->hasMany(WebhookMessage::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function webhookDeliveries(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(WebhookDelivery::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function webhookResponses(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(WebhookResponse::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function webhookTriggers(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(WebhookTrigger::class);
|
||||||
|
}
|
||||||
|
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deleted.php
|
* WebhookDelivery.php
|
||||||
* Copyright (c) 2023 james@firefly-iii.org
|
* Copyright (c) 2025 james@firefly-iii.org
|
||||||
*
|
*
|
||||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
*
|
*
|
||||||
@@ -22,18 +23,25 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Events\Model\BudgetLimit;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use FireflyIII\Events\Event;
|
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
/**
|
class WebhookDelivery extends Model
|
||||||
* Class Deleted
|
|
||||||
*/
|
|
||||||
class Deleted extends Event
|
|
||||||
{
|
{
|
||||||
use SerializesModels;
|
use ReturnsIntegerIdTrait;
|
||||||
|
|
||||||
public function __construct(public BudgetLimit $budgetLimit) {}
|
/**
|
||||||
|
* Get the ID
|
||||||
|
*
|
||||||
|
* @SuppressWarnings("PHPMD.ShortMethodName")
|
||||||
|
*/
|
||||||
|
protected function key(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: static fn ($value) => (int) $value,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,8 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Updated.php
|
* WebhookResponse.php
|
||||||
* Copyright (c) 2023 james@firefly-iii.org
|
* Copyright (c) 2025 james@firefly-iii.org
|
||||||
*
|
*
|
||||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
*
|
*
|
||||||
@@ -22,18 +23,25 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Events\Model\BudgetLimit;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use FireflyIII\Events\Event;
|
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
/**
|
class WebhookResponse extends Model
|
||||||
* Class Updated
|
|
||||||
*/
|
|
||||||
class Updated extends Event
|
|
||||||
{
|
{
|
||||||
use SerializesModels;
|
use ReturnsIntegerIdTrait;
|
||||||
|
|
||||||
public function __construct(public BudgetLimit $budgetLimit) {}
|
/**
|
||||||
|
* Get the ID
|
||||||
|
*
|
||||||
|
* @SuppressWarnings("PHPMD.ShortMethodName")
|
||||||
|
*/
|
||||||
|
protected function key(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: static fn ($value) => (int) $value,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,8 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created.php
|
* WebhookTrigger.php
|
||||||
* Copyright (c) 2023 james@firefly-iii.org
|
* Copyright (c) 2025 james@firefly-iii.org
|
||||||
*
|
*
|
||||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
*
|
*
|
||||||
@@ -22,18 +23,25 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Events\Model\BudgetLimit;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use FireflyIII\Events\Event;
|
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
/**
|
class WebhookTrigger extends Model
|
||||||
* Class Created
|
|
||||||
*/
|
|
||||||
class Created extends Event
|
|
||||||
{
|
{
|
||||||
use SerializesModels;
|
use ReturnsIntegerIdTrait;
|
||||||
|
|
||||||
public function __construct(public BudgetLimit $budgetLimit) {}
|
/**
|
||||||
|
* Get the ID
|
||||||
|
*
|
||||||
|
* @SuppressWarnings("PHPMD.ShortMethodName")
|
||||||
|
*/
|
||||||
|
protected function key(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: static fn ($value) => (int) $value,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SubscriptionsOverdueReminder.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Notifications\User;
|
namespace FireflyIII\Notifications\User;
|
||||||
|
@@ -29,9 +29,6 @@ use FireflyIII\Events\DestroyedTransactionGroup;
|
|||||||
use FireflyIII\Events\DetectedNewIPAddress;
|
use FireflyIII\Events\DetectedNewIPAddress;
|
||||||
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
|
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
|
||||||
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
|
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Created;
|
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Deleted;
|
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Updated;
|
|
||||||
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
|
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
|
||||||
use FireflyIII\Events\Model\PiggyBank\ChangedName;
|
use FireflyIII\Events\Model\PiggyBank\ChangedName;
|
||||||
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
|
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
|
||||||
@@ -219,17 +216,6 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
|
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
|
||||||
],
|
],
|
||||||
|
|
||||||
// budget related events: CRUD budget limit
|
|
||||||
Created::class => [
|
|
||||||
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@created',
|
|
||||||
],
|
|
||||||
Updated::class => [
|
|
||||||
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@updated',
|
|
||||||
],
|
|
||||||
Deleted::class => [
|
|
||||||
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
|
|
||||||
],
|
|
||||||
|
|
||||||
// rule actions
|
// rule actions
|
||||||
RuleActionFailedOnArray::class => [
|
RuleActionFailedOnArray::class => [
|
||||||
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
|
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
|
||||||
|
@@ -268,7 +268,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
|
|||||||
*/
|
*/
|
||||||
foreach ($budgets as $index => $budget) {
|
foreach ($budgets as $index => $budget) {
|
||||||
$budget->order = $index + 1;
|
$budget->order = $index + 1;
|
||||||
$budget->save();
|
$budget->saveQuietly();
|
||||||
}
|
}
|
||||||
// other budgets, set to 0.
|
// other budgets, set to 0.
|
||||||
$this->user->budgets()->where('active', 0)->update(['order' => 0]);
|
$this->user->budgets()->where('active', 0)->update(['order' => 0]);
|
||||||
|
@@ -35,6 +35,7 @@ use FireflyIII\Models\RecurrenceMeta;
|
|||||||
use FireflyIII\Models\RecurrenceRepetition;
|
use FireflyIII\Models\RecurrenceRepetition;
|
||||||
use FireflyIII\Models\RecurrenceTransaction;
|
use FireflyIII\Models\RecurrenceTransaction;
|
||||||
use FireflyIII\Models\RecurrenceTransactionMeta;
|
use FireflyIII\Models\RecurrenceTransactionMeta;
|
||||||
|
use FireflyIII\Models\TransactionGroup;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\TransactionJournalMeta;
|
use FireflyIII\Models\TransactionJournalMeta;
|
||||||
use FireflyIII\Services\Internal\Destroy\RecurrenceDestroyService;
|
use FireflyIII\Services\Internal\Destroy\RecurrenceDestroyService;
|
||||||
@@ -582,4 +583,17 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte
|
|||||||
|
|
||||||
return $service->update($recurrence, $data);
|
return $service->update($recurrence, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function markGroupsAsNow(Collection $groups): void
|
||||||
|
{
|
||||||
|
/** @var TransactionGroup $group */
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
/** @var TransactionJournal $journal */
|
||||||
|
foreach ($group->transactionJournals as $journal) {
|
||||||
|
Log::debug(sprintf('Set date of journal #%d to today!', $journal->id));
|
||||||
|
$journal->date = now(config('app.timezone'));
|
||||||
|
$journal->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -139,6 +139,8 @@ interface RecurringRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array;
|
public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array;
|
||||||
|
|
||||||
|
public function markGroupsAsNow(Collection $groups): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the repetition in a string that is user readable.
|
* Parse the repetition in a string that is user readable.
|
||||||
*/
|
*/
|
||||||
|
@@ -24,9 +24,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Repositories\Webhook;
|
namespace FireflyIII\Repositories\Webhook;
|
||||||
|
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Models\Webhook;
|
use FireflyIII\Models\Webhook;
|
||||||
use FireflyIII\Models\WebhookAttempt;
|
use FireflyIII\Models\WebhookAttempt;
|
||||||
|
use FireflyIII\Models\WebhookDelivery;
|
||||||
use FireflyIII\Models\WebhookMessage;
|
use FireflyIII\Models\WebhookMessage;
|
||||||
|
use FireflyIII\Models\WebhookResponse;
|
||||||
|
use FireflyIII\Models\WebhookTrigger;
|
||||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
|
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
|
||||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
|
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -41,11 +45,20 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac
|
|||||||
|
|
||||||
public function all(): Collection
|
public function all(): Collection
|
||||||
{
|
{
|
||||||
return $this->user->webhooks()->get();
|
return $this->user->webhooks()
|
||||||
|
// only get upgraded webhooks
|
||||||
|
->where('delivery', 1)
|
||||||
|
->where('response', 1)
|
||||||
|
->where('trigger', 1)
|
||||||
|
->get()
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Webhook $webhook): void
|
public function destroy(Webhook $webhook): void
|
||||||
{
|
{
|
||||||
|
// force delete all messages and attempts:
|
||||||
|
$webhook->webhookMessages()->delete();
|
||||||
|
|
||||||
$webhook->delete();
|
$webhook->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,38 +100,108 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac
|
|||||||
|
|
||||||
public function store(array $data): Webhook
|
public function store(array $data): Webhook
|
||||||
{
|
{
|
||||||
$secret = Str::random(24);
|
$secret = Str::random(24);
|
||||||
$fullData = [
|
$fullData = [
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
'user_group_id' => $this->user->user_group_id,
|
'user_group_id' => $this->user->user_group_id,
|
||||||
'active' => $data['active'] ?? false,
|
'active' => $data['active'] ?? false,
|
||||||
'title' => $data['title'] ?? null,
|
'title' => $data['title'] ?? null,
|
||||||
'trigger' => $data['trigger'],
|
// 'trigger' => $data['trigger'],
|
||||||
'response' => $data['response'],
|
// 'response' => $data['response'],
|
||||||
'delivery' => $data['delivery'],
|
// 'delivery' => $data['delivery'],
|
||||||
|
'trigger' => 1,
|
||||||
|
'response' => 1,
|
||||||
|
'delivery' => 1,
|
||||||
'secret' => $secret,
|
'secret' => $secret,
|
||||||
'url' => $data['url'],
|
'url' => $data['url'],
|
||||||
];
|
];
|
||||||
|
|
||||||
return Webhook::create($fullData);
|
/** @var Webhook $webhook */
|
||||||
|
$webhook = Webhook::create($fullData);
|
||||||
|
$triggers = new Collection();
|
||||||
|
$responses = new Collection();
|
||||||
|
$deliveries = new Collection();
|
||||||
|
|
||||||
|
foreach ($data['triggers'] as $trigger) {
|
||||||
|
// get the relevant ID:
|
||||||
|
$object = WebhookTrigger::where('title', $trigger)->first();
|
||||||
|
if (null === $object) {
|
||||||
|
throw new FireflyException(sprintf('Could not find webhook trigger with title "%s".', $trigger));
|
||||||
|
}
|
||||||
|
$triggers->push($object);
|
||||||
|
}
|
||||||
|
$webhook->webhookTriggers()->saveMany($triggers);
|
||||||
|
|
||||||
|
foreach ($data['responses'] as $response) {
|
||||||
|
// get the relevant ID:
|
||||||
|
$object = WebhookResponse::where('title', $response)->first();
|
||||||
|
if (null === $object) {
|
||||||
|
throw new FireflyException(sprintf('Could not find webhook response with title "%s".', $response));
|
||||||
|
}
|
||||||
|
$responses->push($object);
|
||||||
|
}
|
||||||
|
$webhook->webhookResponses()->saveMany($responses);
|
||||||
|
|
||||||
|
foreach ($data['deliveries'] as $delivery) {
|
||||||
|
// get the relevant ID:
|
||||||
|
$object = WebhookDelivery::where('title', $delivery)->first();
|
||||||
|
if (null === $object) {
|
||||||
|
throw new FireflyException(sprintf('Could not find webhook delivery with title "%s".', $delivery));
|
||||||
|
}
|
||||||
|
$deliveries->push($object);
|
||||||
|
}
|
||||||
|
$webhook->webhookDeliveries()->saveMany($deliveries);
|
||||||
|
|
||||||
|
return $webhook;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Webhook $webhook, array $data): Webhook
|
public function update(Webhook $webhook, array $data): Webhook
|
||||||
{
|
{
|
||||||
$webhook->active = $data['active'] ?? $webhook->active;
|
$webhook->active = $data['active'] ?? $webhook->active;
|
||||||
$webhook->trigger = $data['trigger'] ?? $webhook->trigger;
|
$webhook->title = $data['title'] ?? $webhook->title;
|
||||||
$webhook->response = $data['response'] ?? $webhook->response;
|
$webhook->url = $data['url'] ?? $webhook->url;
|
||||||
$webhook->delivery = $data['delivery'] ?? $webhook->delivery;
|
|
||||||
$webhook->title = $data['title'] ?? $webhook->title;
|
|
||||||
$webhook->url = $data['url'] ?? $webhook->url;
|
|
||||||
|
|
||||||
if (true === $data['secret']) {
|
if (array_key_exists('secret', $data) && true === $data['secret']) {
|
||||||
$secret = Str::random(24);
|
$secret = Str::random(24);
|
||||||
$webhook->secret = $secret;
|
$webhook->secret = $secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
$webhook->save();
|
$webhook->save();
|
||||||
|
|
||||||
|
$triggers = new Collection();
|
||||||
|
$responses = new Collection();
|
||||||
|
$deliveries = new Collection();
|
||||||
|
|
||||||
|
foreach ($data['triggers'] as $trigger) {
|
||||||
|
// get the relevant ID:
|
||||||
|
$object = WebhookTrigger::where('title', $trigger)->first();
|
||||||
|
if (null === $object) {
|
||||||
|
throw new FireflyException(sprintf('Could not find webhook trigger with title "%s".', $trigger));
|
||||||
|
}
|
||||||
|
$triggers->push($object);
|
||||||
|
}
|
||||||
|
$webhook->webhookTriggers()->sync($triggers);
|
||||||
|
|
||||||
|
foreach ($data['responses'] as $response) {
|
||||||
|
// get the relevant ID:
|
||||||
|
$object = WebhookResponse::where('title', $response)->first();
|
||||||
|
if (null === $object) {
|
||||||
|
throw new FireflyException(sprintf('Could not find webhook response with title "%s".', $response));
|
||||||
|
}
|
||||||
|
$responses->push($object);
|
||||||
|
}
|
||||||
|
$webhook->webhookResponses()->sync($responses);
|
||||||
|
|
||||||
|
foreach ($data['deliveries'] as $delivery) {
|
||||||
|
// get the relevant ID:
|
||||||
|
$object = WebhookDelivery::where('title', $delivery)->first();
|
||||||
|
if (null === $object) {
|
||||||
|
throw new FireflyException(sprintf('Could not find webhook delivery with title "%s".', $delivery));
|
||||||
|
}
|
||||||
|
$deliveries->push($object);
|
||||||
|
}
|
||||||
|
$webhook->webhookDeliveries()->sync($deliveries);
|
||||||
|
|
||||||
return $webhook;
|
return $webhook;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,6 +52,7 @@ class EitherConfigKey
|
|||||||
'firefly.languages',
|
'firefly.languages',
|
||||||
'app.timezone',
|
'app.timezone',
|
||||||
'firefly.valid_view_ranges',
|
'firefly.valid_view_ranges',
|
||||||
|
'firefly.preselected_accounts',
|
||||||
|
|
||||||
// triggers and actions:
|
// triggers and actions:
|
||||||
'firefly.rule-actions',
|
'firefly.rule-actions',
|
||||||
|
@@ -103,7 +103,7 @@ class ExchangeRateConverter
|
|||||||
|
|
||||||
// find in cache
|
// find in cache
|
||||||
if (null !== $res) {
|
if (null !== $res) {
|
||||||
Log::debug(sprintf('ExchangeRateConverter: Return cached rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d')));
|
Log::debug(sprintf('ExchangeRateConverter: Return cached rate (%s) from %s to %s on %s.', $res, $from->code, $to->code, $date->format('Y-m-d')));
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
@@ -234,9 +234,9 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
private function appendCollectedData(): void
|
private function appendCollectedData(): void
|
||||||
{
|
{
|
||||||
$this->collection = $this->collection->map(function (Account $item) {
|
$this->collection = $this->collection->map(function (Account $item) {
|
||||||
$id = (int)$item->id;
|
$id = (int)$item->id;
|
||||||
$item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null;
|
$item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null;
|
||||||
$meta = [
|
$meta = [
|
||||||
'currency' => null,
|
'currency' => null,
|
||||||
'location' => [
|
'location' => [
|
||||||
'latitude' => null,
|
'latitude' => null,
|
||||||
@@ -249,14 +249,14 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
'opening_balance_date' => null,
|
'opening_balance_date' => null,
|
||||||
'opening_balance_amount' => null,
|
'opening_balance_amount' => null,
|
||||||
'account_number' => null,
|
'account_number' => null,
|
||||||
'notes' => $notes[$id] ?? null,
|
'notes' => $this->notes[$id] ?? null,
|
||||||
'last_activity' => $this->lastActivities[$id] ?? null,
|
'last_activity' => $this->lastActivities[$id] ?? null,
|
||||||
];
|
];
|
||||||
|
|
||||||
// add object group if available
|
// add object group if available
|
||||||
if (array_key_exists($id, $this->mappedObjects)) {
|
if (array_key_exists($id, $this->mappedObjects)) {
|
||||||
$key = $this->mappedObjects[$id];
|
$key = $this->mappedObjects[$id];
|
||||||
$meta['object_group_id'] = $this->objectGroups[$key]['id'];
|
$meta['object_group_id'] = (string) $this->objectGroups[$key]['id'];
|
||||||
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
|
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
|
||||||
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
|
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
|
||||||
}
|
}
|
||||||
@@ -283,28 +283,28 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
|
|
||||||
// add balances
|
// add balances
|
||||||
// get currencies:
|
// get currencies:
|
||||||
$currency = $this->primaryCurrency; // assume primary currency
|
$currency = $this->primaryCurrency; // assume primary currency
|
||||||
if (null !== $meta['currency']) {
|
if (null !== $meta['currency']) {
|
||||||
$currency = $meta['currency'];
|
$currency = $meta['currency'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the current balance:
|
// get the current balance:
|
||||||
$date = $this->getDate();
|
$date = $this->getDate();
|
||||||
// $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary);
|
// $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
$finalBalance = $this->balances[$id];
|
$finalBalance = $this->balances[$id];
|
||||||
Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance);
|
Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance);
|
||||||
|
|
||||||
// collect current balances:
|
// collect current balances:
|
||||||
$currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places);
|
$currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places);
|
||||||
$openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places);
|
$openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places);
|
||||||
$virtualBalance = Steam::bcround($account->virtual_balance ?? '0', $currency->decimal_places);
|
$virtualBalance = Steam::bcround($account->virtual_balance ?? '0', $currency->decimal_places);
|
||||||
$debtAmount = $meta['current_debt'] ?? null;
|
$debtAmount = $meta['current_debt'] ?? null;
|
||||||
|
|
||||||
// set some pc_ default values to NULL:
|
// set some pc_ default values to NULL:
|
||||||
$pcCurrentBalance = null;
|
$pcCurrentBalance = null;
|
||||||
$pcOpeningBalance = null;
|
$pcOpeningBalance = null;
|
||||||
$pcVirtualBalance = null;
|
$pcVirtualBalance = null;
|
||||||
$pcDebtAmount = null;
|
$pcDebtAmount = null;
|
||||||
|
|
||||||
// convert to primary currency if needed:
|
// convert to primary currency if needed:
|
||||||
if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) {
|
if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) {
|
||||||
@@ -327,7 +327,8 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
$openingBalance = null;
|
$openingBalance = null;
|
||||||
$pcOpeningBalance = null;
|
$pcOpeningBalance = null;
|
||||||
}
|
}
|
||||||
$meta['balances'] = [
|
$meta['current_balance_date'] = $this->getDate();
|
||||||
|
$meta['balances'] = [
|
||||||
'current_balance' => $currentBalance,
|
'current_balance' => $currentBalance,
|
||||||
'pc_current_balance' => $pcCurrentBalance,
|
'pc_current_balance' => $pcCurrentBalance,
|
||||||
'opening_balance' => $openingBalance,
|
'opening_balance' => $openingBalance,
|
||||||
@@ -338,7 +339,7 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
'pc_debt_amount' => $pcDebtAmount,
|
'pc_debt_amount' => $pcDebtAmount,
|
||||||
];
|
];
|
||||||
// end add balances
|
// end add balances
|
||||||
$item->meta = $meta;
|
$item->meta = $meta;
|
||||||
|
|
||||||
return $item;
|
return $item;
|
||||||
});
|
});
|
||||||
@@ -378,13 +379,17 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
|
|
||||||
public function setDate(?Carbon $date): void
|
public function setDate(?Carbon $date): void
|
||||||
{
|
{
|
||||||
|
if (null !== $date) {
|
||||||
|
$date->endOfDay();
|
||||||
|
Log::debug(sprintf('Date is now %s', $date->toW3cString()));
|
||||||
|
}
|
||||||
$this->date = $date;
|
$this->date = $date;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDate(): Carbon
|
public function getDate(): Carbon
|
||||||
{
|
{
|
||||||
if (null === $this->date) {
|
if (null === $this->date) {
|
||||||
return today();
|
return now();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->date;
|
return $this->date;
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BudgetEnrichment.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Support\JsonApi\Enrichments;
|
namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||||
@@ -117,7 +138,7 @@ class BudgetEnrichment implements EnrichmentInterface
|
|||||||
// add object group if available
|
// add object group if available
|
||||||
if (array_key_exists($id, $this->mappedObjects)) {
|
if (array_key_exists($id, $this->mappedObjects)) {
|
||||||
$key = $this->mappedObjects[$id];
|
$key = $this->mappedObjects[$id];
|
||||||
$meta['object_group_id'] = $this->objectGroups[$key]['id'];
|
$meta['object_group_id'] = (string) $this->objectGroups[$key]['id'];
|
||||||
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
|
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
|
||||||
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
|
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BudgetLimitEnrichment.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Support\JsonApi\Enrichments;
|
namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||||
@@ -47,6 +68,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
|||||||
$this->collectCurrencies();
|
$this->collectCurrencies();
|
||||||
$this->collectNotes();
|
$this->collectNotes();
|
||||||
$this->collectBudgets();
|
$this->collectBudgets();
|
||||||
|
$this->stringifyIds();
|
||||||
$this->appendCollectedData();
|
$this->appendCollectedData();
|
||||||
|
|
||||||
return $this->collection;
|
return $this->collection;
|
||||||
@@ -134,6 +156,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
|||||||
/** @var BudgetLimit $budgetLimit */
|
/** @var BudgetLimit $budgetLimit */
|
||||||
foreach ($this->collection as $budgetLimit) {
|
foreach ($this->collection as $budgetLimit) {
|
||||||
$id = (int)$budgetLimit->id;
|
$id = (int)$budgetLimit->id;
|
||||||
|
$filteredExpenses = $this->filterToBudget($expenses, $budgetLimit->budget_id);
|
||||||
$filteredExpenses = $repository->sumCollectedExpenses($expenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, false);
|
$filteredExpenses = $repository->sumCollectedExpenses($expenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, false);
|
||||||
$this->expenses[$id] = array_values($filteredExpenses);
|
$this->expenses[$id] = array_values($filteredExpenses);
|
||||||
|
|
||||||
@@ -155,4 +178,30 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
|||||||
$this->currencies[(int)$currency->id] = $currency;
|
$this->currencies[(int)$currency->id] = $currency;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function stringifyIds(): void
|
||||||
|
{
|
||||||
|
$this->expenses = array_map(function ($first) {
|
||||||
|
return array_map(function ($second) {
|
||||||
|
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
|
||||||
|
|
||||||
|
return $second;
|
||||||
|
}, $first);
|
||||||
|
}, $this->expenses);
|
||||||
|
|
||||||
|
$this->pcExpenses = array_map(function ($first) {
|
||||||
|
return array_map(function ($second) {
|
||||||
|
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
|
||||||
|
|
||||||
|
return $second;
|
||||||
|
}, $first);
|
||||||
|
}, $this->expenses);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function filterToBudget(array $expenses, int $budget): array
|
||||||
|
{
|
||||||
|
return array_filter($expenses, function (array $item) use ($budget) {
|
||||||
|
return (int)$item['budget_id'] === $budget;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CategoryEnrichment.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Support\JsonApi\Enrichments;
|
namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PiggyBankEnrichment.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Support\JsonApi\Enrichments;
|
namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||||
@@ -105,7 +126,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
|
|||||||
'pc_current_amount' => '0',
|
'pc_current_amount' => '0',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], $item->current_amount);
|
$this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], (string) $item->current_amount);
|
||||||
if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) {
|
if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) {
|
||||||
$this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], $item->native_current_amount);
|
$this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], $item->native_current_amount);
|
||||||
}
|
}
|
||||||
@@ -170,7 +191,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
|
|||||||
// add object group if available
|
// add object group if available
|
||||||
if (array_key_exists($id, $this->mappedObjects)) {
|
if (array_key_exists($id, $this->mappedObjects)) {
|
||||||
$key = $this->mappedObjects[$id];
|
$key = $this->mappedObjects[$id];
|
||||||
$meta['object_group_id'] = $this->objectGroups[$key]['id'];
|
$meta['object_group_id'] = (string) $this->objectGroups[$key]['id'];
|
||||||
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
|
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
|
||||||
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
|
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PiggyBankEventEnrichment.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Support\JsonApi\Enrichments;
|
namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RecurringEnrichment.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Support\JsonApi\Enrichments;
|
namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||||
@@ -38,7 +59,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
|||||||
private Collection $collection;
|
private Collection $collection;
|
||||||
private array $ids = [];
|
private array $ids = [];
|
||||||
private array $transactionTypeIds = [];
|
private array $transactionTypeIds = [];
|
||||||
private array $transactionTypes = [];
|
// private array $transactionTypes = [];
|
||||||
private array $notes = [];
|
private array $notes = [];
|
||||||
private array $repetitions = [];
|
private array $repetitions = [];
|
||||||
private array $transactions = [];
|
private array $transactions = [];
|
||||||
@@ -107,14 +128,14 @@ class RecurringEnrichment implements EnrichmentInterface
|
|||||||
$this->ids[] = $id;
|
$this->ids[] = $id;
|
||||||
$this->transactionTypeIds[$id] = $typeId;
|
$this->transactionTypeIds[$id] = $typeId;
|
||||||
}
|
}
|
||||||
$this->ids = array_unique($this->ids);
|
$this->ids = array_unique($this->ids);
|
||||||
|
|
||||||
// collect transaction types.
|
// collect transaction types.
|
||||||
$transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get();
|
// $transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get();
|
||||||
foreach ($transactionTypes as $transactionType) {
|
// foreach ($transactionTypes as $transactionType) {
|
||||||
$id = (int)$transactionType->id;
|
// $id = (int)$transactionType->id;
|
||||||
$this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type);
|
// $this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private function collectRepetitions(): void
|
private function collectRepetitions(): void
|
||||||
@@ -129,7 +150,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
|||||||
$recurrence = $this->collection->filter(function (Recurrence $item) use ($repetition) {
|
$recurrence = $this->collection->filter(function (Recurrence $item) use ($repetition) {
|
||||||
return (int)$item->id === (int)$repetition->recurrence_id;
|
return (int)$item->id === (int)$repetition->recurrence_id;
|
||||||
})->first();
|
})->first();
|
||||||
$fromDate = $recurrence->latest_date ?? $recurrence->first_date;
|
$fromDate = clone ($recurrence->latest_date ?? $recurrence->first_date);
|
||||||
$id = (int)$repetition->recurrence_id;
|
$id = (int)$repetition->recurrence_id;
|
||||||
$repId = (int)$repetition->id;
|
$repId = (int)$repetition->id;
|
||||||
$this->repetitions[$id] ??= [];
|
$this->repetitions[$id] ??= [];
|
||||||
@@ -137,6 +158,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
|||||||
// get the (future) occurrences for this specific type of repetition:
|
// get the (future) occurrences for this specific type of repetition:
|
||||||
$amount = 'daily' === $repetition->repetition_type ? 9 : 5;
|
$amount = 'daily' === $repetition->repetition_type ? 9 : 5;
|
||||||
$set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount);
|
$set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount);
|
||||||
|
$occurrences = [];
|
||||||
|
|
||||||
/** @var Carbon $carbon */
|
/** @var Carbon $carbon */
|
||||||
foreach ($set as $carbon) {
|
foreach ($set as $carbon) {
|
||||||
@@ -378,7 +400,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
|||||||
private function collectTransactionMetaData(): void
|
private function collectTransactionMetaData(): void
|
||||||
{
|
{
|
||||||
$ids = array_keys($this->transactions);
|
$ids = array_keys($this->transactions);
|
||||||
$meta = RecurrenceTransactionMeta::whereIn('rt_id', $ids)->get();
|
$meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get();
|
||||||
// other meta-data to be collected:
|
// other meta-data to be collected:
|
||||||
$billIds = [];
|
$billIds = [];
|
||||||
$piggyBankIds = [];
|
$piggyBankIds = [];
|
||||||
@@ -388,8 +410,15 @@ class RecurringEnrichment implements EnrichmentInterface
|
|||||||
foreach ($meta as $entry) {
|
foreach ($meta as $entry) {
|
||||||
$id = (int)$entry->id;
|
$id = (int)$entry->id;
|
||||||
$transactionId = (int)$entry->rt_id;
|
$transactionId = (int)$entry->rt_id;
|
||||||
$recurrenceId = $this->recurrenceIds[$transactionId];
|
|
||||||
$name = (string)$entry->name;
|
// this should refer to another array, were rtIds can be used to find the recurrence.
|
||||||
|
$recurrenceId = $this->recurrenceIds[$transactionId] ?? 0;
|
||||||
|
$name = (string)$entry->name ?? '';
|
||||||
|
if (0 === $recurrenceId) {
|
||||||
|
Log::error(sprintf('Could not find recurrence ID for recurrence transaction ID %d', $transactionId));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
default:
|
default:
|
||||||
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SubscriptionEnrichment.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Support\JsonApi\Enrichments;
|
namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||||
@@ -101,7 +122,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
|||||||
// add object group if available
|
// add object group if available
|
||||||
if (array_key_exists($id, $this->mappedObjects)) {
|
if (array_key_exists($id, $this->mappedObjects)) {
|
||||||
$key = $this->mappedObjects[$id];
|
$key = $this->mappedObjects[$id];
|
||||||
$meta['object_group_id'] = $objectGroups[$key]['id'];
|
$meta['object_group_id'] = (string) $objectGroups[$key]['id'];
|
||||||
$meta['object_group_title'] = $objectGroups[$key]['title'];
|
$meta['object_group_title'] = $objectGroups[$key]['title'];
|
||||||
$meta['object_group_order'] = $objectGroups[$key]['order'];
|
$meta['object_group_order'] = $objectGroups[$key]['order'];
|
||||||
}
|
}
|
||||||
@@ -362,7 +383,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
|||||||
Log::debug(sprintf('[b] Last paid date is: %s', $return->format('Y-m-d')));
|
Log::debug(sprintf('[b] Last paid date is: %s', $return->format('Y-m-d')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log::debug(sprintf('[c] Last paid date is: "%s"', $return?->format('Y-m-d')));
|
// Log::debug(sprintf('[c] Last paid date is: "%s"', $return?->format('Y-m-d')));
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
@@ -143,9 +143,9 @@ class TransactionGroupEnrichment implements EnrichmentInterface
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (in_array($name, $this->dateFields, true)) {
|
if (in_array($name, $this->dateFields, true)) {
|
||||||
Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data));
|
// Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data));
|
||||||
$this->metaData[$entry['transaction_journal_id']][$name] = Carbon::parse($data, config('app.timezone'));
|
$this->metaData[$entry['transaction_journal_id']][$name] = Carbon::parse($data, config('app.timezone'));
|
||||||
Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString()));
|
// Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString()));
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
164
app/Support/JsonApi/Enrichments/WebhookEnrichment.php
Normal file
164
app/Support/JsonApi/Enrichments/WebhookEnrichment.php
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WebhookEnrichment.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\Support\JsonApi\Enrichments;
|
||||||
|
|
||||||
|
use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum;
|
||||||
|
use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum;
|
||||||
|
use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum;
|
||||||
|
use FireflyIII\Models\UserGroup;
|
||||||
|
use FireflyIII\Models\Webhook;
|
||||||
|
use FireflyIII\Models\WebhookDelivery;
|
||||||
|
use FireflyIII\Models\WebhookResponse;
|
||||||
|
use FireflyIII\Models\WebhookTrigger;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class WebhookEnrichment implements EnrichmentInterface
|
||||||
|
{
|
||||||
|
private Collection $collection;
|
||||||
|
private User $user;
|
||||||
|
private UserGroup $userGroup;
|
||||||
|
private array $ids = [];
|
||||||
|
private array $deliveries = [];
|
||||||
|
private array $responses = [];
|
||||||
|
private array $triggers = [];
|
||||||
|
|
||||||
|
private array $webhookDeliveries = [];
|
||||||
|
private array $webhookResponses = [];
|
||||||
|
private array $webhookTriggers = [];
|
||||||
|
|
||||||
|
public function enrich(Collection $collection): Collection
|
||||||
|
{
|
||||||
|
$this->collection = $collection;
|
||||||
|
if ($this->collection->count() > 0) {
|
||||||
|
$this->collectIds();
|
||||||
|
$this->collectInfo();
|
||||||
|
$this->collectWebhookInfo();
|
||||||
|
$this->appendCollectedInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enrichSingle(array|Model $model): array|Model
|
||||||
|
{
|
||||||
|
Log::debug(__METHOD__);
|
||||||
|
$collection = new Collection([$model]);
|
||||||
|
$collection = $this->enrich($collection);
|
||||||
|
|
||||||
|
return $collection->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(User $user): void
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUserGroup(UserGroup $userGroup): void
|
||||||
|
{
|
||||||
|
$this->userGroup = $userGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function collectIds(): void
|
||||||
|
{
|
||||||
|
/** @var Webhook $webhook */
|
||||||
|
foreach ($this->collection as $webhook) {
|
||||||
|
$this->ids[] = $webhook->id;
|
||||||
|
}
|
||||||
|
$this->ids = array_unique($this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function collectInfo(): void
|
||||||
|
{
|
||||||
|
$all = WebhookDelivery::get();
|
||||||
|
|
||||||
|
/** @var WebhookDelivery $item */
|
||||||
|
foreach ($all as $item) {
|
||||||
|
$this->deliveries[$item->id] = $item->key;
|
||||||
|
}
|
||||||
|
$all = WebhookResponse::get();
|
||||||
|
|
||||||
|
/** @var WebhookResponse $item */
|
||||||
|
foreach ($all as $item) {
|
||||||
|
$this->responses[$item->id] = $item->key;
|
||||||
|
}
|
||||||
|
$all = WebhookTrigger::get();
|
||||||
|
|
||||||
|
/** @var WebhookTrigger $item */
|
||||||
|
foreach ($all as $item) {
|
||||||
|
$this->triggers[$item->id] = $item->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function collectWebhookInfo(): void
|
||||||
|
{
|
||||||
|
$set = DB::table('webhook_webhook_delivery')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_delivery_id']);
|
||||||
|
|
||||||
|
/** @var stdClass $item */
|
||||||
|
foreach ($set as $item) {
|
||||||
|
$id = $item->webhook_id;
|
||||||
|
$deliveryId = $item->webhook_delivery_id;
|
||||||
|
$this->webhookDeliveries[$id][] = WebhookDeliveryEnum::from($this->deliveries[$deliveryId])->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$set = DB::table('webhook_webhook_response')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_response_id']);
|
||||||
|
|
||||||
|
/** @var stdClass $item */
|
||||||
|
foreach ($set as $item) {
|
||||||
|
$id = $item->webhook_id;
|
||||||
|
$responseId = $item->webhook_response_id;
|
||||||
|
$this->webhookResponses[$id][] = WebhookResponseEnum::from($this->responses[$responseId])->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$set = DB::table('webhook_webhook_trigger')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_trigger_id']);
|
||||||
|
|
||||||
|
/** @var stdClass $item */
|
||||||
|
foreach ($set as $item) {
|
||||||
|
$id = $item->webhook_id;
|
||||||
|
$triggerId = $item->webhook_trigger_id;
|
||||||
|
$this->webhookTriggers[$id][] = WebhookTriggerEnum::from($this->triggers[$triggerId])->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function appendCollectedInfo(): void
|
||||||
|
{
|
||||||
|
$this->collection = $this->collection->map(function (Webhook $item) {
|
||||||
|
$meta = [
|
||||||
|
'deliveries' => $this->webhookDeliveries[$item->id] ?? [],
|
||||||
|
'responses' => $this->webhookResponses[$item->id] ?? [],
|
||||||
|
'triggers' => $this->webhookTriggers[$item->id] ?? [],
|
||||||
|
];
|
||||||
|
$item->meta = $meta;
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BudgetLimitHandler.php
|
* RecalculatesAvailableBudgetsTrait.php
|
||||||
* Copyright (c) 2023 james@firefly-iii.org
|
* Copyright (c) 2025 james@firefly-iii.org
|
||||||
*
|
*
|
||||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
*
|
*
|
||||||
@@ -22,11 +23,8 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Handlers\Events\Model;
|
namespace FireflyIII\Support\Observers;
|
||||||
|
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Created;
|
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Deleted;
|
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Updated;
|
|
||||||
use FireflyIII\Models\AvailableBudget;
|
use FireflyIII\Models\AvailableBudget;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
@@ -39,17 +37,8 @@ use Spatie\Period\Boundaries;
|
|||||||
use Spatie\Period\Period;
|
use Spatie\Period\Period;
|
||||||
use Spatie\Period\Precision;
|
use Spatie\Period\Precision;
|
||||||
|
|
||||||
/**
|
trait RecalculatesAvailableBudgetsTrait
|
||||||
* Class BudgetLimitHandler
|
|
||||||
*/
|
|
||||||
class BudgetLimitHandler
|
|
||||||
{
|
{
|
||||||
public function created(Created $event): void
|
|
||||||
{
|
|
||||||
Log::debug(sprintf('BudgetLimitHandler::created(#%s)', $event->budgetLimit->id));
|
|
||||||
$this->updateAvailableBudget($event->budgetLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function updateAvailableBudget(BudgetLimit $budgetLimit): void
|
private function updateAvailableBudget(BudgetLimit $budgetLimit): void
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('Now in updateAvailableBudget(limit #%d)', $budgetLimit->id));
|
Log::debug(sprintf('Now in updateAvailableBudget(limit #%d)', $budgetLimit->id));
|
||||||
@@ -241,18 +230,4 @@ class BudgetLimitHandler
|
|||||||
|
|
||||||
return $amount;
|
return $amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleted(Deleted $event): void
|
|
||||||
{
|
|
||||||
Log::debug(sprintf('BudgetLimitHandler::deleted(#%s)', $event->budgetLimit->id));
|
|
||||||
$budgetLimit = $event->budgetLimit;
|
|
||||||
$budgetLimit->id = 0;
|
|
||||||
$this->updateAvailableBudget($event->budgetLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updated(Updated $event): void
|
|
||||||
{
|
|
||||||
Log::debug(sprintf('BudgetLimitHandler::updated(#%s)', $event->budgetLimit->id));
|
|
||||||
$this->updateAvailableBudget($event->budgetLimit);
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -255,7 +255,7 @@ trait ConvertsDataTypes
|
|||||||
if (10 === strlen((string) $value)) {
|
if (10 === strlen((string) $value)) {
|
||||||
// probably a date format.
|
// probably a date format.
|
||||||
try {
|
try {
|
||||||
$carbon = Carbon::createFromFormat('Y-m-d', $value);
|
$carbon = Carbon::createFromFormat('Y-m-d', $value, config('app.timezone'));
|
||||||
} catch (InvalidDateException $e) { // @phpstan-ignore-line
|
} catch (InvalidDateException $e) { // @phpstan-ignore-line
|
||||||
Log::error(sprintf('[1] "%s" is not a valid date: %s', $value, $e->getMessage()));
|
Log::error(sprintf('[1] "%s" is not a valid date: %s', $value, $e->getMessage()));
|
||||||
|
|
||||||
@@ -276,7 +276,7 @@ trait ConvertsDataTypes
|
|||||||
|
|
||||||
// is an atom string, I hope?
|
// is an atom string, I hope?
|
||||||
try {
|
try {
|
||||||
$carbon = Carbon::parse($value);
|
$carbon = Carbon::parse($value, $value, config('app.timezone'));
|
||||||
} catch (InvalidDateException $e) { // @phpstan-ignore-line
|
} catch (InvalidDateException $e) { // @phpstan-ignore-line
|
||||||
Log::error(sprintf('[3] "%s" is not a valid date or time: %s', $value, $e->getMessage()));
|
Log::error(sprintf('[3] "%s" is not a valid date or time: %s', $value, $e->getMessage()));
|
||||||
|
|
||||||
|
95
app/Support/Request/ValidatesWebhooks.php
Normal file
95
app/Support/Request/ValidatesWebhooks.php
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ValidatesWebhooks.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\Support\Request;
|
||||||
|
|
||||||
|
use FireflyIII\Enums\WebhookTrigger;
|
||||||
|
use FireflyIII\Models\Webhook;
|
||||||
|
use Illuminate\Contracts\Validation\Validator;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
trait ValidatesWebhooks
|
||||||
|
{
|
||||||
|
public function withValidator(Validator $validator): void
|
||||||
|
{
|
||||||
|
$validator->after(
|
||||||
|
function (Validator $validator): void {
|
||||||
|
Log::debug('Validating webhook');
|
||||||
|
if ($validator->failed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$data = $validator->getData();
|
||||||
|
$triggers = $data['triggers'] ?? [];
|
||||||
|
$responses = $data['responses'] ?? [];
|
||||||
|
|
||||||
|
if (0 === count($triggers) || 0 === count($responses)) {
|
||||||
|
Log::debug('No trigger or response, return.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$validTriggers = array_values(Webhook::getTriggers());
|
||||||
|
$validResponses = array_values(Webhook::getResponses());
|
||||||
|
$containsAny = false;
|
||||||
|
$count = 0;
|
||||||
|
foreach ($triggers as $trigger) {
|
||||||
|
if (!in_array($trigger, $validTriggers, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
++$count;
|
||||||
|
if ($trigger === WebhookTrigger::ANY->name) {
|
||||||
|
$containsAny = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($containsAny && $count > 1) {
|
||||||
|
$validator->errors()->add('triggers.0', trans('validation.only_any_trigger'));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($responses as $response) {
|
||||||
|
if (!in_array($response, $validResponses, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// some combinations are illegal.
|
||||||
|
foreach ($triggers as $i => $trigger) {
|
||||||
|
$forbidden = config(sprintf('webhooks.forbidden_responses.%s', $trigger));
|
||||||
|
if (null === $forbidden) {
|
||||||
|
$validator->errors()->add(sprintf('triggers.%d', $i), trans('validation.unknown_webhook_trigger', ['trigger' => $trigger]));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($responses as $ii => $response) {
|
||||||
|
if (in_array($response, $forbidden, true)) {
|
||||||
|
Log::debug(sprintf('Trigger %s and response %s are forbidden.', $trigger, $response));
|
||||||
|
$validator->errors()->add(sprintf('responses.%d', $ii), trans('validation.bad_webhook_combination', ['trigger' => $trigger, 'response' => $response]));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PreferencesSingleton.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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace FireflyIII\Support\Singleton;
|
namespace FireflyIII\Support\Singleton;
|
||||||
|
@@ -224,6 +224,7 @@ class Steam
|
|||||||
*/
|
*/
|
||||||
$request = clone $start;
|
$request = clone $start;
|
||||||
$request->subDay()->endOfDay();
|
$request->subDay()->endOfDay();
|
||||||
|
Log::debug('Get first balance to start.');
|
||||||
Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String()));
|
Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String()));
|
||||||
$startBalance = $this->finalAccountBalance($account, $request);
|
$startBalance = $this->finalAccountBalance($account, $request);
|
||||||
$primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
|
$primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
|
||||||
@@ -315,7 +316,7 @@ class Steam
|
|||||||
Log::debug(sprintf('Updated entry [%s]', $carbonKey), $currentBalance);
|
Log::debug(sprintf('Updated entry [%s]', $carbonKey), $currentBalance);
|
||||||
}
|
}
|
||||||
$cache->store($balances);
|
$cache->store($balances);
|
||||||
Log::debug('End of method');
|
Log::debug('End of method finalAccountBalanceInRange');
|
||||||
|
|
||||||
return $balances;
|
return $balances;
|
||||||
}
|
}
|
||||||
@@ -356,8 +357,10 @@ class Steam
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$accountSum = array_values($accountSum)[0];
|
$accountSum = array_values($accountSum)[0];
|
||||||
|
$sumOfAmount = (string)$accountSum['sum_of_amount'];
|
||||||
|
$sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount);
|
||||||
$sumsByCode = [
|
$sumsByCode = [
|
||||||
$accountSum['code'] => $accountSum['sum_of_amount'],
|
$accountSum['code'] => $sumOfAmount,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Log::debug('All balances are (joined)', $others);
|
// Log::debug('All balances are (joined)', $others);
|
||||||
|
@@ -78,10 +78,6 @@ class AccountTransformer extends AbstractTransformer
|
|||||||
$zoomLevel = $account->meta['location']['zoom_level'] ?? null;
|
$zoomLevel = $account->meta['location']['zoom_level'] ?? null;
|
||||||
$order = $account->order;
|
$order = $account->order;
|
||||||
|
|
||||||
// date (for balance etc.)
|
|
||||||
$date = $this->getDate();
|
|
||||||
$date->endOfDay();
|
|
||||||
|
|
||||||
// get primary currency as fallback:
|
// get primary currency as fallback:
|
||||||
$currency = $this->primary; // assume primary currency
|
$currency = $this->primary; // assume primary currency
|
||||||
if ($hasCurrencySettings) {
|
if ($hasCurrencySettings) {
|
||||||
@@ -141,7 +137,7 @@ class AccountTransformer extends AbstractTransformer
|
|||||||
'debt_amount' => $account->meta['balances']['debt_amount'],
|
'debt_amount' => $account->meta['balances']['debt_amount'],
|
||||||
'pc_debt_amount' => $account->meta['balances']['pc_debt_amount'],
|
'pc_debt_amount' => $account->meta['balances']['pc_debt_amount'],
|
||||||
|
|
||||||
'current_balance_date' => $date->toAtomString(),
|
'current_balance_date' => $account->meta['current_balance_date']->toAtomString(),
|
||||||
'notes' => $account->meta['notes'] ?? null,
|
'notes' => $account->meta['notes'] ?? null,
|
||||||
'monthly_payment_date' => $monthlyPaymentDate,
|
'monthly_payment_date' => $monthlyPaymentDate,
|
||||||
'credit_card_type' => $creditCardType,
|
'credit_card_type' => $creditCardType,
|
||||||
|
@@ -28,6 +28,7 @@ use FireflyIII\Models\BudgetLimit;
|
|||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Support\Facades\Amount;
|
use FireflyIII\Support\Facades\Amount;
|
||||||
use FireflyIII\Support\Facades\Steam;
|
use FireflyIII\Support\Facades\Steam;
|
||||||
|
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
|
||||||
use League\Fractal\Resource\Item;
|
use League\Fractal\Resource\Item;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +56,15 @@ class BudgetLimitTransformer extends AbstractTransformer
|
|||||||
*/
|
*/
|
||||||
public function includeBudget(BudgetLimit $limit)
|
public function includeBudget(BudgetLimit $limit)
|
||||||
{
|
{
|
||||||
return $this->item($limit->budget, new BudgetTransformer(), 'budgets');
|
// enrich budget
|
||||||
|
$budget = $limit->budget;
|
||||||
|
$enrichment = new BudgetEnrichment();
|
||||||
|
$enrichment->setStart($this->parameters->get('start'));
|
||||||
|
$enrichment->setEnd($this->parameters->get('end'));
|
||||||
|
$enrichment->setUser($budget->user);
|
||||||
|
$budget = $enrichment->enrichSingle($budget);
|
||||||
|
|
||||||
|
return $this->item($budget, new BudgetTransformer(), 'budgets');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,7 +100,7 @@ class BudgetLimitTransformer extends AbstractTransformer
|
|||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
|
||||||
'primary_currency_id' => (int)$this->primaryCurrency->id,
|
'primary_currency_id' => (string) $this->primaryCurrency->id,
|
||||||
'primary_currency_name' => $this->primaryCurrency->name,
|
'primary_currency_name' => $this->primaryCurrency->name,
|
||||||
'primary_currency_code' => $this->primaryCurrency->code,
|
'primary_currency_code' => $this->primaryCurrency->code,
|
||||||
'primary_currency_symbol' => $this->primaryCurrency->symbol,
|
'primary_currency_symbol' => $this->primaryCurrency->symbol,
|
||||||
|
@@ -51,9 +51,12 @@ class WebhookTransformer extends AbstractTransformer
|
|||||||
'active' => $webhook->active,
|
'active' => $webhook->active,
|
||||||
'title' => $webhook->title,
|
'title' => $webhook->title,
|
||||||
'secret' => $webhook->secret,
|
'secret' => $webhook->secret,
|
||||||
'trigger' => $this->getEnum('trigger', $webhook->trigger),
|
'triggers' => $webhook->meta['triggers'],
|
||||||
'response' => $this->getEnum('response', $webhook->response),
|
'deliveries' => $webhook->meta['deliveries'],
|
||||||
'delivery' => $this->getEnum('delivery', $webhook->delivery),
|
'responses' => $webhook->meta['responses'],
|
||||||
|
// 'trigger' => $this->getEnum('trigger', $webhook->trigger),
|
||||||
|
// 'response' => $this->getEnum('response', $webhook->response),
|
||||||
|
// 'delivery' => $this->getEnum('delivery', $webhook->delivery),
|
||||||
'url' => $webhook->url,
|
'url' => $webhook->url,
|
||||||
'links' => [
|
'links' => [
|
||||||
[
|
[
|
||||||
|
57
changelog.md
57
changelog.md
@@ -3,7 +3,62 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
## 6.3.0 - 2025-08-xx
|
## 6.4.0 - 2025-09-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- [Issue 5532](https://github.com/firefly-iii/firefly-iii/issues/5532) (Asset prices and exchange rates) reported by @svozniuk
|
||||||
|
- [Discussion 10725](https://github.com/orgs/firefly-iii/discussions/10725) (New webhook triggers) started by @Billos. See the [documentation](https://docs.firefly-iii.org/how-to/firefly-iii/features/webhooks/).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Initial release.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- [Issue 10790](https://github.com/firefly-iii/firefly-iii/issues/10790) (Undefined variable $occurrences) reported by @senna1992
|
||||||
|
- [Issue 10791](https://github.com/firefly-iii/firefly-iii/issues/10791) (Clone and edit a transaction with a different currency doesn't clear the foreign transaction amount) reported by @jxtxzzw
|
||||||
|
- [Issue 10794](https://github.com/firefly-iii/firefly-iii/issues/10794) (Error with recurring transaction) reported by @MaximSN
|
||||||
|
- [Issue 10799](https://github.com/firefly-iii/firefly-iii/issues/10799) (Budget - "Left (per day)" not showing the correct value) reported by @GensHaze
|
||||||
|
- [Issue 10802](https://github.com/firefly-iii/firefly-iii/issues/10802) (Crash when trying to update a budget limit) reported by @Billos
|
||||||
|
- [Issue 10803](https://github.com/firefly-iii/firefly-iii/issues/10803) (Issue in /v1/budget-limits spent attribute) reported by @Billos
|
||||||
|
- [Issue 10804](https://github.com/firefly-iii/firefly-iii/issues/10804) (No notes information included in the "List all accounts" API call) reported by @gpampuro
|
||||||
|
- [Issue 10808](https://github.com/firefly-iii/firefly-iii/issues/10808) (cron job Error: Undefined variable $preference) reported by @MexerSam
|
||||||
|
- [Issue 10813](https://github.com/firefly-iii/firefly-iii/issues/10813) (Error "Argument #2 ($symbol) must be of type string" while try open subscriptions section) reported by @mrResident
|
||||||
|
- [Issue 10819](https://github.com/firefly-iii/firefly-iii/issues/10819) (Internal Server Error when trying to open piggy banks) reported by @noantiq
|
||||||
|
- [Issue 10820](https://github.com/firefly-iii/firefly-iii/issues/10820) (Unable to search date 1970-01-01 to apply rule.) reported by @Kage1
|
||||||
|
- [Issue 10824](https://github.com/firefly-iii/firefly-iii/issues/10824) (Converting withdrawal to transfer to account in different currency doesn't allow setting correct currencies) reported by @avee87
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
- [Issue 8345](https://github.com/firefly-iii/firefly-iii/issues/8345) (API: Distinguish spent & earned at `/v2/chart/category/dashboard` (or future `v2/categories`)) reported by @dreautall
|
||||||
|
- [Issue 10804](https://github.com/firefly-iii/firefly-iii/issues/10804) (No notes information included in the "List all accounts" API call) reported by @gpampuro
|
||||||
|
- [Issue 10806](https://github.com/firefly-iii/firefly-iii/issues/10806) (API: `/v1/chart/balance/balance` has undocumented `period` parameter) reported by @dreautall
|
||||||
|
- [Issue 10807](https://github.com/firefly-iii/firefly-iii/issues/10807) (API: `/v1/bills` field `object_group_id` returns int, should be string) reported by @dreautall
|
||||||
|
- [Issue 10815](https://github.com/firefly-iii/firefly-iii/issues/10815) (API: `/v1/accounts` balance is off by a day) reported by @dreautall
|
||||||
|
- [Issue 10827](https://github.com/firefly-iii/firefly-iii/issues/10827) (Trigger Recurrence by API) reported by @MexerSam
|
||||||
|
|
||||||
|
## 6.3.2 - 2025-08-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- [Discussion 10768](https://github.com/orgs/firefly-iii/discussions/10768) (Argument #1 ($start) must be of type Carbon\Carbon, null given) started by @tangodance
|
||||||
|
- [Issue 10771](https://github.com/firefly-iii/firefly-iii/issues/10771) (/v1/budgets/{id}/limits seems broken) reported by @Sceptorrh
|
||||||
|
- [Issue 10773](https://github.com/firefly-iii/firefly-iii/issues/10773) (API: Wrong Return types) reported by @dreautall
|
||||||
|
- [Issue 10775](https://github.com/firefly-iii/firefly-iii/issues/10775) (API: /v1/chart/account/overview broken) reported by @dreautall
|
||||||
|
- [Issue 10782](https://github.com/firefly-iii/firefly-iii/issues/10782) ([error'] /accounts/[asset,expense,revenue]) reported by @vkanev
|
||||||
|
|
||||||
|
## 6.3.1 - 2025-08-19
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- [Discussion 10768](https://github.com/orgs/firefly-iii/discussions/10768) (Argument #1 ($start) must be of type Carbon\Carbon, null given) started by @tangodance
|
||||||
|
- [Issue 10771](https://github.com/firefly-iii/firefly-iii/issues/10771) (/v1/budgets/{id}/limits seems broken) reported by @Sceptorrh
|
||||||
|
- [Issue 10773](https://github.com/firefly-iii/firefly-iii/issues/10773) (API: Wrong Return types) reported by @dreautall
|
||||||
|
- [Issue 10775](https://github.com/firefly-iii/firefly-iii/issues/10775) (API: /v1/chart/account/overview broken) reported by @dreautall
|
||||||
|
- [Issue 10782](https://github.com/firefly-iii/firefly-iii/issues/10782) ([error'] /accounts/[asset,expense,revenue]) reported by @vkanev
|
||||||
|
|
||||||
|
## 6.3.0 - 2025-08-17
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Firefly III v6.3.0 introduces a lot of API changes that deal with multi-currency support. Make sure your beloved apps are updated to support this.
|
> Firefly III v6.3.0 introduces a lot of API changes that deal with multi-currency support. Make sure your beloved apps are updated to support this.
|
||||||
|
492
composer.lock
generated
492
composer.lock
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user