mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-06-09 20:05:36 +00:00
Compare commits
117 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5ce9fd1e2 | ||
|
|
3d64f7fe08 | ||
|
|
f2efb69b76 | ||
|
|
7499a414f4 | ||
|
|
8b0f790a56 | ||
|
|
b70ed32952 | ||
|
|
9e511c822e | ||
|
|
fa6c123595 | ||
|
|
ec1dfca2b5 | ||
|
|
bebd3b189e | ||
|
|
e3319dca5d | ||
|
|
a38cb85f55 | ||
|
|
0226673a01 | ||
|
|
7816f1be9b | ||
|
|
5878173e80 | ||
|
|
45c30f11bc | ||
|
|
fea97efdbf | ||
|
|
fe0e8796ca | ||
|
|
e83c5b9f86 | ||
|
|
9558f05947 | ||
|
|
f3d6bb0fb5 | ||
|
|
57010cd2e0 | ||
|
|
9436eeacaf | ||
|
|
7ddf395ea9 | ||
|
|
492c55bd76 | ||
|
|
894dea5c9c | ||
|
|
fecf12790d | ||
|
|
883c083860 | ||
|
|
e059753c43 | ||
|
|
0bac0aaaee | ||
|
|
2a68c48e2a | ||
|
|
c394034876 | ||
|
|
7bd91048ea | ||
|
|
d64bca7700 | ||
|
|
7d768cfa23 | ||
|
|
fd50fbf193 | ||
|
|
134c232f45 | ||
|
|
ce603f50d8 | ||
|
|
3b7bff4c57 | ||
|
|
1485f99579 | ||
|
|
7d24783c49 | ||
|
|
c4ee3598e1 | ||
|
|
8cf8e91448 | ||
|
|
28f2de0df7 | ||
|
|
86adf11263 | ||
|
|
045f875041 | ||
|
|
f781e9f2b6 | ||
|
|
5eb52812f0 | ||
|
|
916abd8464 | ||
|
|
6baca9510f | ||
|
|
5c22a40849 | ||
|
|
51e994056a | ||
|
|
984e735bc1 | ||
|
|
c1a271d9c4 | ||
|
|
0f9a2f010c | ||
|
|
ed9557aaa0 | ||
|
|
1cc471fcc4 | ||
|
|
6c27424bfe | ||
|
|
38576f7fe0 | ||
|
|
5b80a5bdbe | ||
|
|
0202f4abd9 | ||
|
|
615d568479 | ||
|
|
2ace0d3f23 | ||
|
|
42204f8dc1 | ||
|
|
cfac8fa569 | ||
|
|
04704392f3 | ||
|
|
4b9bbc9d6a | ||
|
|
f24f535d39 | ||
|
|
3a9ac03358 | ||
|
|
4ac7fec5f6 | ||
|
|
b6759c3fa0 | ||
|
|
4a83b1e3e5 | ||
|
|
4b701dfc4c | ||
|
|
b2997d0a5a | ||
|
|
fd8722e401 | ||
|
|
e46153330a | ||
|
|
525f0c752a | ||
|
|
13e4160e85 | ||
|
|
7806d63f91 | ||
|
|
8c620b6536 | ||
|
|
e47ce30579 | ||
|
|
7e6eadc047 | ||
|
|
dae4f6f351 | ||
|
|
8c8af51bc4 | ||
|
|
a5e1cba39c | ||
|
|
96d56ad723 | ||
|
|
e4b1c3045e | ||
|
|
e974594fe3 | ||
|
|
c93a2dc23a | ||
|
|
639efee78a | ||
|
|
eb4971fec6 | ||
|
|
0c63a3380d | ||
|
|
edd30b2d42 | ||
|
|
a4f6c2b748 | ||
|
|
5fc90e0f76 | ||
|
|
e8ab7d8a93 | ||
|
|
e73d04bc0f | ||
|
|
c0eca4298a | ||
|
|
dafb43393a | ||
|
|
ba8155a391 | ||
|
|
9e4329ebfc | ||
|
|
60e2645e54 | ||
|
|
33a9e5b3f0 | ||
|
|
cd0290475b | ||
|
|
45528cf7d3 | ||
|
|
7cfd21362a | ||
|
|
f91063c88b | ||
|
|
30c1f4c13d | ||
|
|
39be5075eb | ||
|
|
714133dad5 | ||
|
|
1caf639b85 | ||
|
|
1ebff22785 | ||
|
|
8b14a11969 | ||
|
|
7b0e40543b | ||
|
|
ea57a0a8c8 | ||
|
|
62ee37f631 | ||
|
|
c96cc8d941 |
196
.ci/php-cs-fixer/composer.lock
generated
196
.ci/php-cs-fixer/composer.lock
generated
@@ -294,42 +294,42 @@
|
||||
},
|
||||
{
|
||||
"name": "ergebnis/agent-detector",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ergebnis/agent-detector.git",
|
||||
"reference": "5b654a9f1ff8a5d2ce6a57568df5ae8801c87f64"
|
||||
"reference": "e211f17928c8b95a51e06040792d57f5462fb271"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ergebnis/agent-detector/zipball/5b654a9f1ff8a5d2ce6a57568df5ae8801c87f64",
|
||||
"reference": "5b654a9f1ff8a5d2ce6a57568df5ae8801c87f64",
|
||||
"url": "https://api.github.com/repos/ergebnis/agent-detector/zipball/e211f17928c8b95a51e06040792d57f5462fb271",
|
||||
"reference": "e211f17928c8b95a51e06040792d57f5462fb271",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ergebnis/composer-normalize": "^2.50.0",
|
||||
"ergebnis/composer-normalize": "^2.51.0",
|
||||
"ergebnis/license": "^2.7.0",
|
||||
"ergebnis/php-cs-fixer-config": "^6.60.2",
|
||||
"ergebnis/phpstan-rules": "^2.13.1",
|
||||
"ergebnis/phpunit-slow-test-detector": "^2.24.0",
|
||||
"ergebnis/rector-rules": "^1.16.0",
|
||||
"ergebnis/rector-rules": "^1.18.1",
|
||||
"fakerphp/faker": "^1.24.1",
|
||||
"infection/infection": "^0.26.6",
|
||||
"phpstan/extension-installer": "^1.4.3",
|
||||
"phpstan/phpstan": "^2.1.46",
|
||||
"phpstan/phpstan": "^2.1.54",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.4",
|
||||
"phpstan/phpstan-phpunit": "^2.0.16",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.10",
|
||||
"phpunit/phpunit": "^9.6.34",
|
||||
"rector/rector": "^2.4.1"
|
||||
"rector/rector": "^2.4.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.0-dev"
|
||||
"dev-main": "1.2-dev"
|
||||
},
|
||||
"composer-normalize": {
|
||||
"indent-size": 2,
|
||||
@@ -359,7 +359,7 @@
|
||||
"security": "https://github.com/ergebnis/agent-detector/blob/main/.github/SECURITY.md",
|
||||
"source": "https://github.com/ergebnis/agent-detector"
|
||||
},
|
||||
"time": "2026-04-10T13:45:13+00:00"
|
||||
"time": "2026-05-07T08:19:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "evenement/evenement",
|
||||
@@ -471,16 +471,16 @@
|
||||
},
|
||||
{
|
||||
"name": "friendsofphp/php-cs-fixer",
|
||||
"version": "v3.95.1",
|
||||
"version": "v3.95.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||
"reference": "a9727678fbd12997f1d9de8f4a37824ed9df1065"
|
||||
"reference": "a28d88a5e172b27e78d0816992b15a9df3da20f1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a9727678fbd12997f1d9de8f4a37824ed9df1065",
|
||||
"reference": "a9727678fbd12997f1d9de8f4a37824ed9df1065",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a28d88a5e172b27e78d0816992b15a9df3da20f1",
|
||||
"reference": "a28d88a5e172b27e78d0816992b15a9df3da20f1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -512,8 +512,8 @@
|
||||
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"facile-it/paraunit": "^1.3.1 || ^2.8.0",
|
||||
"infection/infection": "^0.32.6",
|
||||
"facile-it/paraunit": "^1.3.1 || ^2.11.0",
|
||||
"infection/infection": "^0.32.7",
|
||||
"justinrainbow/json-schema": "^6.8.0",
|
||||
"keradus/cli-executor": "^2.3",
|
||||
"mikey179/vfsstream": "^1.6.12",
|
||||
@@ -564,7 +564,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.1"
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -572,7 +572,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-12T17:00:09+00:00"
|
||||
"time": "2026-05-15T09:20:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
@@ -1255,16 +1255,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||
"reference": "9c957d730257f49c873f3761674559bd90098a7d"
|
||||
"reference": "b36d33b6e796513de7cb7df053afb3f55eefcd47"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/9c957d730257f49c873f3761674559bd90098a7d",
|
||||
"reference": "9c957d730257f49c873f3761674559bd90098a7d",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b36d33b6e796513de7cb7df053afb3f55eefcd47",
|
||||
"reference": "b36d33b6e796513de7cb7df053afb3f55eefcd47",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1277,7 +1277,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "8.1-dev"
|
||||
"dev-main": "8.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1310,7 +1310,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/diff/issues",
|
||||
"security": "https://github.com/sebastianbergmann/diff/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/8.1.0"
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/8.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1330,20 +1330,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-05T12:02:33+00:00"
|
||||
"time": "2026-05-15T04:58:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v8.0.8",
|
||||
"version": "v8.0.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7"
|
||||
"reference": "3156577f46a38aa1b9323aad223de7a9cd426782"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7",
|
||||
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/3156577f46a38aa1b9323aad223de7a9cd426782",
|
||||
"reference": "3156577f46a38aa1b9323aad223de7a9cd426782",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1400,7 +1400,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v8.0.8"
|
||||
"source": "https://github.com/symfony/console/tree/v8.0.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1420,20 +1420,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-30T15:14:47+00:00"
|
||||
"time": "2026-05-13T12:07:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.6.0",
|
||||
"version": "v3.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
|
||||
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
|
||||
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b",
|
||||
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1446,7 +1446,7 @@
|
||||
"name": "symfony/contracts"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "3.6-dev"
|
||||
"dev-main": "3.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1471,7 +1471,7 @@
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1482,25 +1482,29 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-25T14:21:43+00:00"
|
||||
"time": "2026-04-13T15:52:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v8.0.8",
|
||||
"version": "v8.0.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6"
|
||||
"reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f662acc6ab22a3d6d716dcb44c381c6002940df6",
|
||||
"reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0c3c1a17604c4dbbec4b93fe162c538482096e1f",
|
||||
"reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1552,7 +1556,7 @@
|
||||
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/event-dispatcher/tree/v8.0.8"
|
||||
"source": "https://github.com/symfony/event-dispatcher/tree/v8.0.9"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1572,20 +1576,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-30T15:14:47+00:00"
|
||||
"time": "2026-04-18T13:51:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher-contracts",
|
||||
"version": "v3.6.0",
|
||||
"version": "v3.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
|
||||
"reference": "59eb412e93815df44f05f342958efa9f46b1e586"
|
||||
"reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586",
|
||||
"reference": "59eb412e93815df44f05f342958efa9f46b1e586",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32",
|
||||
"reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1599,7 +1603,7 @@
|
||||
"name": "symfony/contracts"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "3.6-dev"
|
||||
"dev-main": "3.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1632,7 +1636,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0"
|
||||
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1643,25 +1647,29 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-25T14:21:43+00:00"
|
||||
"time": "2026-01-05T13:30:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v8.0.8",
|
||||
"version": "v8.0.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "66b769ae743ce2d13e435528fbef4af03d623e5a"
|
||||
"reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/66b769ae743ce2d13e435528fbef4af03d623e5a",
|
||||
"reference": "66b769ae743ce2d13e435528fbef4af03d623e5a",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/224db910898ce1317b892a9a1338f1f8f17eb7c7",
|
||||
"reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1698,7 +1706,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v8.0.8"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v8.0.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1718,7 +1726,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-30T15:14:47+00:00"
|
||||
"time": "2026-05-11T16:39:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
@@ -1861,7 +1869,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
@@ -1920,7 +1928,7 @@
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1944,16 +1952,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-grapheme",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||
"reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df"
|
||||
"reference": "4864388bfbd3001ce88e234fab652acd91fdc57e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/ad1b7b9092976d6c948b8a187cec9faaea9ec1df",
|
||||
"reference": "ad1b7b9092976d6c948b8a187cec9faaea9ec1df",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e",
|
||||
"reference": "4864388bfbd3001ce88e234fab652acd91fdc57e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2002,7 +2010,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2022,11 +2030,11 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-10T16:19:22+00:00"
|
||||
"time": "2026-04-26T13:13:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
@@ -2087,7 +2095,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2111,7 +2119,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
@@ -2172,7 +2180,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2196,7 +2204,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
@@ -2256,7 +2264,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2280,7 +2288,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||
@@ -2336,7 +2344,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2360,7 +2368,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php84",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.37.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php84.git",
|
||||
@@ -2416,7 +2424,7 @@
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php84/tree/v1.36.0"
|
||||
"source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2440,16 +2448,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v8.0.8",
|
||||
"version": "v8.0.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc"
|
||||
"reference": "26d89e459f037d2873300605d0a07e7a8ef84db0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc",
|
||||
"reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/26d89e459f037d2873300605d0a07e7a8ef84db0",
|
||||
"reference": "26d89e459f037d2873300605d0a07e7a8ef84db0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2481,7 +2489,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v8.0.8"
|
||||
"source": "https://github.com/symfony/process/tree/v8.0.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2501,20 +2509,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-30T15:14:47+00:00"
|
||||
"time": "2026-05-11T16:56:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v3.6.1",
|
||||
"version": "v3.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
|
||||
"reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
|
||||
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a",
|
||||
"reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2532,7 +2540,7 @@
|
||||
"name": "symfony/contracts"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "3.6-dev"
|
||||
"dev-main": "3.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -2568,7 +2576,7 @@
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v3.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2588,7 +2596,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-15T11:30:57+00:00"
|
||||
"time": "2026-03-28T09:44:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/stopwatch",
|
||||
@@ -2658,16 +2666,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v8.0.8",
|
||||
"version": "v8.0.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "ae9488f874d7603f9d2dfbf120203882b645d963"
|
||||
"reference": "39be2ad058a3c0bd558edca23e65f009865d75ff"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963",
|
||||
"reference": "ae9488f874d7603f9d2dfbf120203882b645d963",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/39be2ad058a3c0bd558edca23e65f009865d75ff",
|
||||
"reference": "39be2ad058a3c0bd558edca23e65f009865d75ff",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2724,7 +2732,7 @@
|
||||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v8.0.8"
|
||||
"source": "https://github.com/symfony/string/tree/v8.0.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2744,7 +2752,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-30T15:14:47+00:00"
|
||||
"time": "2026-05-13T12:07:53+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/fr.yml
vendored
8
.github/ISSUE_TEMPLATE/fr.yml
vendored
@@ -12,6 +12,14 @@ body:
|
||||
required: true
|
||||
- label: I've used [the search](https://github.com/firefly-iii/firefly-iii/issues?q=is%3Aissue) and this has not been requested before.
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Use of AI
|
||||
description: AI agents like Claude and CoPilot are not reliable tools. Do not use them.
|
||||
options:
|
||||
- label: I'm a real person and wrote this bug without assistance from AI.
|
||||
required: true
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
|
||||
6
.github/contributing.md
vendored
6
.github/contributing.md
vendored
@@ -1,3 +1,5 @@
|
||||
# [Contributing guidelines](https://docs.firefly-iii.org/explanation/support/#contributing-code)
|
||||
# Contributing guidelines
|
||||
|
||||
[Contributing guidelines](https://docs.firefly-iii.org/explanation/support/#contributing-code)
|
||||
This repository is part of "Firefly III", by being either a main repository for code, a supporting repository with guidelines and documentation, or a repository with tools and secondary code.
|
||||
|
||||
The [contribution guidelines](https://docs.firefly-iii.org/explanation/contributing/) for this repository are the same as they are for ALL Firefly III related repositories, and they can be found in [the Firefly III documentation](https://docs.firefly-iii.org/explanation/contributing/).
|
||||
|
||||
4
.github/pull_request_template.md
vendored
4
.github/pull_request_template.md
vendored
@@ -38,9 +38,11 @@ Example: Fixes #1234. See also #3456.
|
||||
|
||||
#### AI usage disclosure
|
||||
<!--
|
||||
If AI tools were involved in creating this PR, please check all boxes that apply
|
||||
If AI tools were involved in creating this PR, please check all boxes that apply
|
||||
below and make sure that you adhere to our Automated Contributions Policy:
|
||||
https://docs.firefly-iii.org/explanation/support/#automated-contributions-policy
|
||||
|
||||
If you remove or skip this disclosure, your PR may be ignored.
|
||||
-->
|
||||
I used AI assistance for:
|
||||
- [ ] Code generation (e.g., when writing an implementation or fixing a bug)
|
||||
|
||||
11
.github/security.md
vendored
11
.github/security.md
vendored
@@ -3,9 +3,18 @@
|
||||
Firefly III is an application to manage your personal finances. As such, the developer has adopted this security
|
||||
disclosure and response policy to ensure that critical issues are responsibly handled.
|
||||
|
||||
## AI-generated security advisories
|
||||
|
||||
> [!WARNING]
|
||||
> Due to a large number of irrelevant, noisy and uninformed AI-generated security advisories coming my way, reporting any the following security issues may result in a permanent ban from the Firefly III organization on GitHub.
|
||||
|
||||
1. Any SSRF in any user provided URL field (webhooks, ntfy, SimpleFIN, Slack). It's by design that users may set-up any URL they want, be it internal, private or non-existing.
|
||||
2. Any XSS issue without a viable attack tree. If you can find a spot where Firefly III or the associated tools render unescaped data, it's not a security issue unless you can show me an actual attack that gets that data into the system.
|
||||
3. Any issue that is not true. AI models have already *hallucinated* security issues in Firefly III. They've referred to **non-existing** functions, templates and files. Including line numbers and code excerpts. Validate your findings before you report them to me.
|
||||
|
||||
## Supported versions
|
||||
|
||||
Only the latest Firefly III release is maintained. Applicable fixes, including security fixes, will not backported to
|
||||
Only the latest Firefly III release is maintained. Applicable fixes, including security fixes, will not be backported to
|
||||
older release branches. Please refer to [releases.md](https://github.com/firefly-iii/firefly-iii/blob/main/releases.md) for details.
|
||||
|
||||
## Reporting a vulnerability - private disclosure process
|
||||
|
||||
2
.github/workflows/depsreview.yml
vendored
2
.github/workflows/depsreview.yml
vendored
@@ -13,4 +13,4 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Dependency review'
|
||||
uses: actions/dependency-review-action@v4
|
||||
uses: actions/dependency-review-action@v5
|
||||
|
||||
66
.github/workflows/pr-reply-no-disclosure.yml
vendored
Normal file
66
.github/workflows/pr-reply-no-disclosure.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: 'PRs - Check for AI disclosure'
|
||||
|
||||
# the workflow to execute on is comments that are newly created
|
||||
on:
|
||||
pull_request:
|
||||
types: [ opened ]
|
||||
|
||||
# permissions needed for reacting to IssueOps commands on issues and PRs
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
checks: read
|
||||
|
||||
jobs:
|
||||
respond:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
BODY=$(gh pr view $NUMBER --json body)
|
||||
AUTHOR=$(gh pr view $NUMBER --json author)
|
||||
|
||||
if [[ $AUTHOR == *"app/dependabot"* ]]; then
|
||||
echo "Is dependabot, stop"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# I used AI assistance for:
|
||||
# - [ ] Code generation (e.g., when writing an implementation or fixing a bug)
|
||||
# - [ ] Test/benchmark generation
|
||||
# - [ ] Documentation (including examples)
|
||||
# - [ ] Research and understanding
|
||||
|
||||
|
||||
|
||||
# $BODY must contain one of these four uses.
|
||||
if [[ $BODY != *"Code generation"* &&
|
||||
$BODY != *"Test/benchmark generation"* &&
|
||||
$BODY != *"Documentation"* &&
|
||||
$BODY != *"Research and understanding"* &&
|
||||
$BODY != *"I used AI assistance for"* ]]; then
|
||||
MESSAGE="Hi there!
|
||||
|
||||
This is an automated reply. \`Share and enjoy\`
|
||||
|
||||
You triggered an automated reply, because it seems you removed or changed the AI assistance disclosure from the PR template. Without a valid disclosure, your PR cannot be processed.
|
||||
|
||||
Even if you did not use AI, this disclosure must be present. Please reply to your PR and explain your use of AI in any or all of the following areas:
|
||||
|
||||
1. Code generation (e.g., when writing an implementation or fixing a bug)
|
||||
2. Test/benchmark generation
|
||||
3. Documentation (including examples)
|
||||
4. Research and understanding
|
||||
|
||||
There cannot be interaction with your PR without this disclosure.
|
||||
|
||||
If the disclosure is present but the bot did not pick up on it, please accept my apologies for the intrusion. Contrary to other bots, this one is just a simple \`bash\` script and it may be wrong."
|
||||
|
||||
gh pr comment "$NUMBER" --body "$MESSAGE"
|
||||
echo "Triggered on AI disclosure missing."
|
||||
exit 0
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
NUMBER: ${{ github.event.pull_request.number }}
|
||||
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -99,11 +99,6 @@ jobs:
|
||||
env:
|
||||
FIREFLY_III_ROOT: /github/workspace
|
||||
GH_TOKEN: ''
|
||||
- name: Setup Mago
|
||||
uses: nhedger/setup-mago@v1
|
||||
with:
|
||||
version: "latest"
|
||||
working-directory: "."
|
||||
- name: Run CI
|
||||
run: |
|
||||
cp .env.example .env
|
||||
@@ -113,17 +108,19 @@ jobs:
|
||||
|
||||
# format code.
|
||||
echo "Will now run Mago Format"
|
||||
mago format
|
||||
./vendor/bin/mago format
|
||||
sudo chown -R runner:docker resources/lang
|
||||
echo "Will now run PHPCS"
|
||||
.ci/phpcs.sh
|
||||
|
||||
# lint and check
|
||||
echo "Will now run Mago Lint"
|
||||
mago lint
|
||||
./vendor/bin/mago lint
|
||||
echo "Will now run PHPstan"
|
||||
.ci/phpstan.sh
|
||||
rm .env
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Crowdin action
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
|
||||
@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
|
||||
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
|
||||
|
||||
## 2026
|
||||
- iaohkut
|
||||
- tasnim0tantawi
|
||||
- Joe Longendyke
|
||||
- Daniel Holøien
|
||||
|
||||
@@ -179,9 +179,9 @@ final class CategoryController extends Controller
|
||||
|
||||
// order by amount
|
||||
usort($return, static fn (array $a, array $b): int => ((float) $a['entries']['spent'] + (float) $a['entries']['earned'])
|
||||
< ((float) $b['entries']['spent'] + (float) $b['entries']['earned'])
|
||||
? 1
|
||||
: -1);
|
||||
< ((float) $b['entries']['spent'] + (float) $b['entries']['earned'])
|
||||
? 1
|
||||
: -1);
|
||||
|
||||
return response()->json($this->clean($return));
|
||||
}
|
||||
|
||||
@@ -59,8 +59,9 @@ final class DestroyController extends Controller
|
||||
|
||||
public function destroy(DestroyRequest $request, TransactionCurrency $from, TransactionCurrency $to): JsonResponse
|
||||
{
|
||||
$first = Carbon::create(1970, 1, 1);
|
||||
$this->repository->deleteRates($from, $to);
|
||||
event(new DestroyedCurrencyExchangeRate($from, $to, $this->validateUserGroup($request)));
|
||||
event(new DestroyedCurrencyExchangeRate($from, $to, $this->validateUserGroup($request), $first));
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
@@ -74,7 +75,7 @@ final class DestroyController extends Controller
|
||||
if (!$exchangeRate instanceof CurrencyExchangeRate) {
|
||||
throw new FireflyException('Bla');
|
||||
}
|
||||
event(new DestroyedCurrencyExchangeRate($from, $to, $this->validateUserGroup($request)));
|
||||
event(new DestroyedCurrencyExchangeRate($from, $to, $this->validateUserGroup($request), $date));
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
@@ -85,7 +86,7 @@ final class DestroyController extends Controller
|
||||
$to = $exchangeRate->toCurrency;
|
||||
$this->repository->deleteRate($exchangeRate);
|
||||
|
||||
event(new DestroyedCurrencyExchangeRate($from, $to, $this->validateUserGroup($request)));
|
||||
event(new DestroyedCurrencyExchangeRate($from, $to, $this->validateUserGroup($request), $exchangeRate->date));
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,9 @@ final class StoreController extends Controller
|
||||
$transactionGroup = $this->groupRepository->store($data);
|
||||
} catch (DuplicateTransactionException $e) {
|
||||
Log::warning('Caught a duplicate transaction. Return error message.');
|
||||
$validator = Validator::make(['transactions' => [['description' => $e->getMessage()]]], ['transactions.0.description' => new IsDuplicateTransaction()]);
|
||||
$validator = Validator::make(['transactions' => [['description' => $e->getMessage()]]], [
|
||||
'transactions.0.description' => new IsDuplicateTransaction(),
|
||||
]);
|
||||
|
||||
throw new ValidationException($validator);
|
||||
} catch (FireflyException $e) {
|
||||
|
||||
@@ -158,7 +158,7 @@ final class ConfigurationController extends Controller
|
||||
$enableExternalRates = FireflyConfig::get('enable_external_rates', false);
|
||||
$allowWebhooks = FireflyConfig::get('allow_webhooks', false);
|
||||
$enableBatchProcessing = FireflyConfig::get('enable_batch_processing', false);
|
||||
$validUrlProtocols = FireflyConfig::get('valid_url_protocols', 'http,https');
|
||||
$validUrlProtocols = FireflyConfig::get('valid_url_protocols', config('firefly.valid_url_protocols'));
|
||||
|
||||
return [
|
||||
'is_demo_site' => $isDemoSite?->data,
|
||||
@@ -171,7 +171,7 @@ final class ConfigurationController extends Controller
|
||||
'enable_external_rates' => $enableExternalRates?->data,
|
||||
'allow_webhooks' => $allowWebhooks?->data,
|
||||
'enable_batch_processing' => $enableBatchProcessing?->data,
|
||||
'valid_url_protocols' => $validUrlProtocols->data ?? 'http,https',
|
||||
'valid_url_protocols' => $validUrlProtocols->data ?? config('firefly.valid_url_protocols'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -162,12 +162,12 @@ class StoreRequest extends FormRequest
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
'transactions.*.interest_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.book_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.process_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.due_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.payment_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.invoice_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -209,12 +209,12 @@ class UpdateRequest extends FormRequest
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
'transactions.*.interest_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.book_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.process_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.due_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.payment_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
'transactions.*.invoice_date' => 'date|nullable|after:1970-01-02|before:2038-01-17',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Model\CurrencyExchangeRate;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
@@ -37,7 +38,8 @@ class DestroyedCurrencyExchangeRate extends Event
|
||||
public function __construct(
|
||||
public TransactionCurrency $from,
|
||||
public TransactionCurrency $to,
|
||||
public UserGroup $userGroup
|
||||
public UserGroup $userGroup,
|
||||
public Carbon $date
|
||||
) {
|
||||
Log::debug(sprintf('DestroyedCurrencyExchangeRate(%s, %s) Event', $from->code, $to->code));
|
||||
}
|
||||
|
||||
@@ -245,7 +245,10 @@ class PiggyBankFactory
|
||||
);
|
||||
|
||||
// validate amount:
|
||||
if (array_key_exists('target_amount', $piggyBankData) && '' === (string) $piggyBankData['target_amount']) {
|
||||
if (array_key_exists('target_amount', $piggyBankData) && '' === trim((string) $piggyBankData['target_amount'])) {
|
||||
$piggyBankData['target_amount'] = '0';
|
||||
}
|
||||
if (!array_key_exists('target_amount', $piggyBankData)) {
|
||||
$piggyBankData['target_amount'] = '0';
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class PiggyBankObserver
|
||||
}
|
||||
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $piggyBank->accounts()->first()?->user;
|
||||
$params->user = $piggyBank->accounts()->first()->user;
|
||||
$params->model = $piggyBank;
|
||||
$params->originalCurrency = $piggyBank->transactionCurrency;
|
||||
$params->amountField = 'target_amount';
|
||||
|
||||
@@ -40,8 +40,7 @@ class ReportHelper implements ReportHelperInterface
|
||||
/**
|
||||
* ReportHelper constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
/** @var BudgetRepositoryInterface The budget repository */
|
||||
public function __construct(/** @var BudgetRepositoryInterface The budget repository */
|
||||
protected BudgetRepositoryInterface $budgetRepository
|
||||
) {}
|
||||
|
||||
|
||||
@@ -245,13 +245,8 @@ final class IndexController extends Controller
|
||||
$inPast = $limitPeriod->startsBefore(now()) && $limitPeriod->endsBefore(now());
|
||||
$currency = $limit->transactionCurrency ?? $primaryCurrency;
|
||||
$amount = Steam::bcround($limit->amount, $currency->decimal_places);
|
||||
$spent = $this->opsRepository->sumExpenses(
|
||||
$limit->start_date,
|
||||
$limit->end_date,
|
||||
null,
|
||||
new Collection()->push($budget),
|
||||
$currency
|
||||
);
|
||||
$spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency);
|
||||
|
||||
$spentAmount = $spent[$currency->id]['sum'] ?? '0';
|
||||
$array['budgeted'][] = [
|
||||
'id' => $limit->id,
|
||||
@@ -289,7 +284,7 @@ final class IndexController extends Controller
|
||||
|
||||
if (array_key_exists($currency->id, $spentArr) && array_key_exists('sum', $spentArr[$currency->id])) {
|
||||
$array['spent'][$currency->id]['spent'] = $spentArr[$currency->id]['sum'];
|
||||
$array['spent'][$currency->id]['spent_outside'] = bcmul(bcsub($spentInLimits[$currency->id], $spentArr[$currency->id]['sum']), '-1');
|
||||
$array['spent'][$currency->id]['spent_outside'] = Steam::negative(bcsub($spentInLimits[$currency->id], $spentArr[$currency->id]['sum']));
|
||||
$array['spent'][$currency->id]['currency_id'] = $currency->id;
|
||||
$array['spent'][$currency->id]['currency_symbol'] = $currency->symbol;
|
||||
$array['spent'][$currency->id]['currency_decimal_places'] = $currency->decimal_places;
|
||||
|
||||
@@ -589,7 +589,6 @@ final class AccountController extends Controller
|
||||
Log::debug('End of chart loop.');
|
||||
// second loop (yes) to create nice array with info! Yay!
|
||||
$chartData = [];
|
||||
|
||||
foreach ($return as $key => $info) {
|
||||
if ('balance' !== $key && 'pc_balance' !== $key) {
|
||||
// assume it's a currency:
|
||||
@@ -608,6 +607,11 @@ final class AccountController extends Controller
|
||||
$info['currency_code'] = $this->primaryCurrency->code;
|
||||
$info['label'] = sprintf('%s (%s) (%s)', $account->name, (string) trans('firefly.sum'), $this->primaryCurrency->symbol);
|
||||
}
|
||||
// do not add pc_balance to the array if the account is in the primary currency anyway,
|
||||
// and it has no currency balances.
|
||||
if (2 === count(array_keys($return)) && 'pc_balance' === $key && $accountCurrency->id === $this->primaryCurrency->id) {
|
||||
continue;
|
||||
}
|
||||
$chartData[] = $info;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Controllers\Profile;
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -144,6 +145,7 @@ final class OAuthController extends Controller
|
||||
->where('expires_at', '>', Date::now())
|
||||
->get()
|
||||
->filter(fn (#[SensitiveParameter] Token $token) => $token->client->hasGrantType('personal_access'))
|
||||
->values()
|
||||
;
|
||||
|
||||
return response()->json($tokens);
|
||||
@@ -165,9 +167,10 @@ final class OAuthController extends Controller
|
||||
|
||||
public function storeClient(Request $request): JsonResponse
|
||||
{
|
||||
$validProtocols = FireflyConfig::get('valid_url_protocols', config('firefly.valid_url_protocols'))->data;
|
||||
$this->validation->make($request->only(['name', 'redirect_uris', 'confidential']), [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'redirect_uris' => ['required', 'url'],
|
||||
'redirect_uris' => ['required', sprintf('url:%s', $validProtocols)],
|
||||
'confidential' => 'boolean',
|
||||
])->validate();
|
||||
|
||||
@@ -195,15 +198,15 @@ final class OAuthController extends Controller
|
||||
|
||||
public function updateClient(Request $request, string $clientId): Client|Response
|
||||
{
|
||||
$client = auth()->user()->oauthApps()->where('revoked', false)->find($clientId);
|
||||
|
||||
$client = auth()->user()->oauthApps()->where('revoked', false)->find($clientId);
|
||||
$validProtocols = FireflyConfig::get('valid_url_protocols', config('firefly.valid_url_protocols'))->data;
|
||||
if (null === $client) {
|
||||
return new Response('', 404);
|
||||
}
|
||||
|
||||
$this->validation->make($request->only(['name', 'redirect_uris']), [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'redirect_uris' => ['required', 'url'],
|
||||
'redirect_uris' => ['required', sprintf('url:%s', $validProtocols)],
|
||||
])->validate();
|
||||
|
||||
$this->clients->update($client, $request->input('name'), explode(',', $request->input('redirect_uris'))); // FIXME replace
|
||||
|
||||
@@ -284,7 +284,7 @@ final class BudgetController extends Controller
|
||||
$cache->addProperty('budget-period-report');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
// return $cache->get();
|
||||
}
|
||||
|
||||
$periods = Navigation::listOfPeriods($start, $end);
|
||||
@@ -292,7 +292,6 @@ final class BudgetController extends Controller
|
||||
|
||||
// list expenses for budgets in account(s)
|
||||
$expenses = $this->opsRepository->listExpenses($start, $end, $accounts);
|
||||
|
||||
$report = [];
|
||||
foreach ($expenses as $currency) {
|
||||
foreach ($currency['budgets'] as $budget) {
|
||||
@@ -300,9 +299,12 @@ final class BudgetController extends Controller
|
||||
foreach ($budget['transaction_journals'] as $journal) {
|
||||
// #10678
|
||||
// skip transactions between two asset / liability accounts.
|
||||
// #12223
|
||||
// must also be of the same type to be skipped
|
||||
if (
|
||||
in_array($journal['source_account_type'], config('firefly.valid_currency_account_types'), true)
|
||||
&& in_array($journal['destination_account_type'], config('firefly.valid_currency_account_types'), true)
|
||||
&& $journal['source_account_type'] === $journal['destination_account_type']
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -40,10 +40,9 @@ class Authenticate
|
||||
/**
|
||||
* Create a new middleware instance.
|
||||
*/
|
||||
public function __construct(
|
||||
/**
|
||||
* The authentication factory instance.
|
||||
*/
|
||||
public function __construct(/**
|
||||
* The authentication factory instance.
|
||||
*/
|
||||
protected Auth $auth
|
||||
) {}
|
||||
|
||||
|
||||
@@ -42,10 +42,9 @@ class Binder
|
||||
/**
|
||||
* Binder constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
/**
|
||||
* The authentication factory instance.
|
||||
*/
|
||||
public function __construct(/**
|
||||
* The authentication factory instance.
|
||||
*/
|
||||
protected Auth $auth
|
||||
) {
|
||||
$this->binders = Domain::getBindables();
|
||||
|
||||
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use FireflyIII\Rules\Admin\IsValidSlackOrDiscordUrl;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
@@ -65,9 +66,10 @@ class NotificationRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'slack_webhook_url' => ['nullable', 'url', 'min:1', new IsValidSlackOrDiscordUrl()],
|
||||
'ntfy_server' => ['nullable', 'url', 'min:1'],
|
||||
$validProtocols = FireflyConfig::get('valid_url_protocols', config('firefly.valid_url_protocols'))->data;
|
||||
$rules = [
|
||||
'slack_webhook_url' => ['nullable', sprintf('url:%s', $validProtocols), 'min:1', new IsValidSlackOrDiscordUrl()],
|
||||
'ntfy_server' => ['nullable', sprintf('url:%s', $validProtocols), 'min:1'],
|
||||
'ntfy_user' => ['required_with:ntfy_pass,ntfy_auth', 'nullable', 'string', 'min:1'],
|
||||
'ntfy_pass' => ['required_with:ntfy_user,ntfy_auth', 'nullable', 'string', 'min:1'],
|
||||
];
|
||||
|
||||
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use FireflyIII\Rules\Admin\IsValidSlackOrDiscordUrl;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
@@ -39,9 +40,10 @@ class PreferencesRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'slack_webhook_url' => ['nullable', 'url', 'min:1', new IsValidSlackOrDiscordUrl()],
|
||||
'ntfy_server' => ['nullable', 'url', 'min:1'],
|
||||
$validProtocols = FireflyConfig::get('valid_url_protocols', config('firefly.valid_url_protocols'))->data;
|
||||
$rules = [
|
||||
'slack_webhook_url' => ['nullable', sprintf('url:%s', $validProtocols), 'min:1', new IsValidSlackOrDiscordUrl()],
|
||||
'ntfy_server' => ['nullable', sprintf('url:%s', $validProtocols), 'min:1'],
|
||||
'ntfy_user' => ['required_with:ntfy_pass,ntfy_auth', 'nullable', 'string', 'min:1'],
|
||||
'ntfy_pass' => ['required_with:ntfy_user,ntfy_auth', 'nullable', 'string', 'min:1'],
|
||||
];
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Model\CurrencyExchangeRate;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\Model\CurrencyExchangeRate\CreatedCurrencyExchangeRate;
|
||||
use FireflyIII\Events\Model\CurrencyExchangeRate\DestroyedCurrencyExchangeRate;
|
||||
use FireflyIII\Events\Model\CurrencyExchangeRate\UpdatedCurrencyExchangeRate;
|
||||
@@ -42,20 +43,22 @@ class ProcessesExchangeRates
|
||||
Preferences::mark();
|
||||
Cache::clear();
|
||||
if ($event instanceof DestroyedCurrencyExchangeRate) {
|
||||
$this->handleCurrency($event->userGroup, $event->from);
|
||||
$this->handleCurrency($event->userGroup, $event->to);
|
||||
$this->handleCurrency($event->userGroup, $event->from, $event->date);
|
||||
$this->handleCurrency($event->userGroup, $event->to, $event->date);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->handleCurrency($event->rate->userGroup, $event->rate->fromCurrency);
|
||||
$this->handleCurrency($event->rate->userGroup, $event->rate->toCurrency);
|
||||
$this->handleCurrency($event->rate->userGroup, $event->rate->fromCurrency, $event->rate->date);
|
||||
$this->handleCurrency($event->rate->userGroup, $event->rate->toCurrency, $event->rate->date);
|
||||
}
|
||||
|
||||
private function handleCurrency(UserGroup $userGroup, TransactionCurrency $currency): void
|
||||
private function handleCurrency(UserGroup $userGroup, TransactionCurrency $currency, Carbon $date): void
|
||||
{
|
||||
$calculator = new PrimaryAmountRecalculationService();
|
||||
$calculator->setDate($date);
|
||||
if (Amount::convertToPrimary()) {
|
||||
Log::debug(sprintf('Will now convert amounts to primary currency for currency %s.', $currency->code));
|
||||
$date->startOfDay();
|
||||
Log::debug(sprintf('Will now convert amounts to primary currency for currency %s after %s.', $currency->code, $date->format('Y-m-d')));
|
||||
|
||||
$calculator->recalculateForGroupAndCurrency($userGroup, $currency);
|
||||
// $calculator->recalculateForGroup($userGroup);
|
||||
|
||||
@@ -335,7 +335,9 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface, UserGroup
|
||||
$limit = new BudgetLimit();
|
||||
$limit->budget()->associate($budget);
|
||||
$limit->start_date = $data['start_date']->format('Y-m-d');
|
||||
$limit->start_date_tz = $data['start_date']->format('e');
|
||||
$limit->end_date = $data['end_date']->format('Y-m-d');
|
||||
$limit->end_date_tz = $data['end_date']->format('e');
|
||||
$limit->amount = $data['amount'];
|
||||
$limit->generated = $data['generated'] ?? false;
|
||||
$limit->period = $data['period'] ?? '';
|
||||
|
||||
@@ -385,9 +385,9 @@ trait ModifiesPiggyBanks
|
||||
$piggyBank->target_date = $data['target_date'];
|
||||
$piggyBank->target_date_tz = $data['target_date']?->format('e');
|
||||
}
|
||||
if (array_key_exists('start_date', $data)) {
|
||||
if (array_key_exists('start_date', $data) && '' !== $data['start_date']) {
|
||||
$piggyBank->start_date = $data['start_date'];
|
||||
$piggyBank->start_date_tz = $data['target_date']?->format('e');
|
||||
$piggyBank->start_date_tz = $data['start_date']?->format('e');
|
||||
}
|
||||
$piggyBank->save();
|
||||
|
||||
|
||||
@@ -342,33 +342,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte
|
||||
return $piggyBank->piggyBankRepetitions()->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the suggested amount the user should save per month, or "".
|
||||
*/
|
||||
public function getSuggestedMonthlyAmount(PiggyBank $piggyBank): string
|
||||
{
|
||||
$savePerMonth = '0';
|
||||
$currentAmount = $this->getCurrentAmount($piggyBank);
|
||||
if (null !== $piggyBank->target_date && $currentAmount < $piggyBank->target_amount) {
|
||||
$now = today(config('app.timezone'));
|
||||
$startDate = null !== $piggyBank->start_date && $piggyBank->start_date->gte($now) ? $piggyBank->start_date : $now;
|
||||
$diffInMonths = (int) $startDate->diffInMonths($piggyBank->target_date);
|
||||
$remainingAmount = bcsub((string) $piggyBank->target_amount, $currentAmount);
|
||||
|
||||
// more than 1 month to go and still need money to save:
|
||||
if ($diffInMonths > 0 && 1 === bccomp($remainingAmount, '0')) {
|
||||
$savePerMonth = bcdiv($remainingAmount, (string) $diffInMonths);
|
||||
}
|
||||
|
||||
// less than 1 month to go but still need money to save:
|
||||
if (0 === $diffInMonths && 1 === bccomp($remainingAmount, '0')) {
|
||||
$savePerMonth = $remainingAmount;
|
||||
}
|
||||
}
|
||||
|
||||
return $savePerMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get for piggy account what is left to put in piggies.
|
||||
*/
|
||||
|
||||
@@ -111,11 +111,6 @@ interface PiggyBankRepositoryInterface
|
||||
|
||||
public function getRepetition(PiggyBank $piggyBank, bool $overrule = false): ?PiggyBankRepetition;
|
||||
|
||||
/**
|
||||
* Returns the suggested amount the user should save per month, or "".
|
||||
*/
|
||||
public function getSuggestedMonthlyAmount(PiggyBank $piggyBank): string;
|
||||
|
||||
/**
|
||||
* Get for piggy account what is left to put in piggies.
|
||||
*/
|
||||
|
||||
@@ -166,7 +166,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
|
||||
$set = TransactionJournalLink::where(static function (Builder $q) use ($journals): void {
|
||||
$q->whereIn('source_id', $journals);
|
||||
$q->orWhereIn('destination_id', $journals);
|
||||
})->with(['source', 'destination', 'source.transactions'])->leftJoin('link_types', 'link_types.id', '=', 'journal_links.link_type_id')->get([
|
||||
})->with(['source', 'notes', 'destination', 'source.transactions'])->leftJoin('link_types', 'link_types.id', '=', 'journal_links.link_type_id')->get([
|
||||
'journal_links.*',
|
||||
'link_types.inward',
|
||||
'link_types.outward',
|
||||
@@ -191,6 +191,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
|
||||
'editable' => 1 === (int) $entry->editable,
|
||||
'amount' => $amount,
|
||||
'foreign_amount' => $foreignAmount,
|
||||
'notes' => null === $entry->notes->first() ? '' : $entry->notes->first()->text,
|
||||
];
|
||||
}
|
||||
if ($journalId === $entry->destination_id) {
|
||||
@@ -204,6 +205,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
|
||||
'editable' => 1 === (int) $entry->editable,
|
||||
'amount' => $amount,
|
||||
'foreign_amount' => $foreignAmount,
|
||||
'notes' => null === $entry->notes->first() ? '' : $entry->notes->first()->text,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class IsDateOrTime implements ValidationRule
|
||||
if (10 === strlen($value)) {
|
||||
// probably a date format.
|
||||
try {
|
||||
Carbon::createFromFormat('Y-m-d', $value);
|
||||
$object = Carbon::createFromFormat('Y-m-d', $value);
|
||||
} catch (InvalidDateException $e) {
|
||||
Log::error(sprintf('"%s" is not a valid date: %s', $value, $e->getMessage()));
|
||||
|
||||
@@ -64,13 +64,18 @@ class IsDateOrTime implements ValidationRule
|
||||
|
||||
return;
|
||||
}
|
||||
if ($object->year < 1970) {
|
||||
$fail('validation.date_or_time')->translate();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// is an atom string, I hope?
|
||||
try {
|
||||
Carbon::parse($value);
|
||||
$object = Carbon::parse($value);
|
||||
} catch (InvalidDateException $e) {
|
||||
Log::error(sprintf('"%s" is not a valid date or time: %s', $value, $e->getMessage()));
|
||||
|
||||
@@ -82,6 +87,11 @@ class IsDateOrTime implements ValidationRule
|
||||
|
||||
$fail('validation.date_or_time')->translate();
|
||||
|
||||
return;
|
||||
}
|
||||
if ($object->year < 1970) {
|
||||
$fail('validation.date_or_time')->translate();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Internal\Recalculate;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\Model\Account\UpdatedExistingAccount;
|
||||
use FireflyIII\Handlers\Observer\TransactionObserver;
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -52,6 +53,13 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PrimaryAmountRecalculationService
|
||||
{
|
||||
private Carbon $date;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->date = Carbon::createFromDate(1970, 1, 1);
|
||||
}
|
||||
|
||||
public function recalculate(): void
|
||||
{
|
||||
if (false === FireflyConfig::get('enable_exchange_rates', config('cer.enabled'))->data) {
|
||||
@@ -106,12 +114,18 @@ class PrimaryAmountRecalculationService
|
||||
$this->calculateTransactionsForCurrency($userGroup, $currency, $limitCurrency);
|
||||
}
|
||||
|
||||
public function setDate(?Carbon $date): void
|
||||
{
|
||||
$this->date = $date;
|
||||
}
|
||||
|
||||
private function calculateTransactions(UserGroup $userGroup, TransactionCurrency $currency): void
|
||||
{
|
||||
// custom query because of the potential size of this update.
|
||||
$set = DB::table('transactions')
|
||||
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.user_group_id', $userGroup->id)
|
||||
->where('transaction_journals.date', '>=', $this->date)
|
||||
->where(static function (DatabaseBuilder $q1) use ($currency): void {
|
||||
$q1->where(static function (DatabaseBuilder $q2) use ($currency): void {
|
||||
$q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id');
|
||||
@@ -147,6 +161,7 @@ class PrimaryAmountRecalculationService
|
||||
$set = DB::table('transactions')
|
||||
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.user_group_id', $userGroup->id)
|
||||
->where('transaction_journals.date', '>=', $this->date)
|
||||
->where(static function (DatabaseBuilder $q1) use ($currency): void {
|
||||
$q1->where(static function (DatabaseBuilder $q2) use ($currency): void {
|
||||
$q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id');
|
||||
@@ -279,7 +294,15 @@ class PrimaryAmountRecalculationService
|
||||
|
||||
private function recalculateBudgetLimits(Budget $budget, TransactionCurrency $currency): void
|
||||
{
|
||||
$set = $budget->budgetlimits()->where('transaction_currency_id', '!=', $currency->id)->get();
|
||||
$set = $budget
|
||||
->budgetlimits()
|
||||
->where(function (EloquentBuilder $q): void {
|
||||
$q->where('budget_limits.start_date', '>=', $this->date);
|
||||
$q->orWhere('budget_limits.end_date', '<=', $this->date);
|
||||
})
|
||||
->where('transaction_currency_id', '!=', $currency->id)
|
||||
->get()
|
||||
;
|
||||
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($set as $limit) {
|
||||
@@ -436,6 +459,7 @@ class PrimaryAmountRecalculationService
|
||||
$success = DB::table('transactions')
|
||||
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.user_group_id', $userGroup->id)
|
||||
->where('transaction_journals.date', '>=', $this->date)
|
||||
->where(static function (Builder $q): void {
|
||||
$q->whereNotNull('native_amount')->orWhereNotNull('native_foreign_amount');
|
||||
})
|
||||
|
||||
@@ -741,12 +741,12 @@ class JournalUpdateService
|
||||
if (null === $group || null === $this->transactionJournal) {
|
||||
return;
|
||||
}
|
||||
if (0 === bccomp($source->foreign_amount, $foreignAmount)) {
|
||||
if (0 === bccomp(Steam::positive($originalSourceAmount), Steam::positive($foreignAmount))) {
|
||||
Log::debug('Amount was not actually changed, return.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug('Amount was changed, needs audit log entry.');
|
||||
Log::debug(sprintf('Amount was changed (%s -> %s), needs audit log entry.', $originalSourceAmount, $foreignAmount));
|
||||
$transfer = TransactionTypeEnum::TRANSFER->value === $this->transactionJournal->transactionType->type;
|
||||
// $withdrawal = TransactionTypeEnum::WITHDRAWAL->value === $this->transactionJournal->transactionType->type;
|
||||
$deposit = TransactionTypeEnum::DEPOSIT->value === $this->transactionJournal->transactionType->type;
|
||||
|
||||
@@ -163,6 +163,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
|
||||
}
|
||||
|
||||
// get suggested per month.
|
||||
|
||||
$meta['save_per_month'] = Steam::bcround(
|
||||
$this->getSuggestedMonthlyAmount($this->date, $item->target_date, $meta['target_amount'], $meta['current_amount']),
|
||||
$currency->decimal_places
|
||||
@@ -301,23 +302,21 @@ class PiggyBankEnrichment implements EnrichmentInterface
|
||||
if (null === $targetAmount || !$targetDate instanceof Carbon || !$startDate instanceof Carbon) {
|
||||
return '0';
|
||||
}
|
||||
$savePerMonth = '0';
|
||||
if (1 === bccomp($targetAmount, $currentAmount)) {
|
||||
$now = today(config('app.timezone'));
|
||||
$diffInMonths = (int) $startDate->diffInMonths($targetDate);
|
||||
$diffInMonths = ceil($startDate->diffInMonths($targetDate));
|
||||
$remainingAmount = bcsub($targetAmount, $currentAmount);
|
||||
|
||||
// more than 1 month to go and still need money to save:
|
||||
if ($diffInMonths > 0 && 1 === bccomp($remainingAmount, '0')) {
|
||||
$savePerMonth = bcdiv($remainingAmount, (string) $diffInMonths);
|
||||
return bcdiv($remainingAmount, (string) $diffInMonths);
|
||||
}
|
||||
|
||||
// less than 1 month to go but still need money to save:
|
||||
if (0 === $diffInMonths && 1 === bccomp($remainingAmount, '0')) {
|
||||
$savePerMonth = $remainingAmount;
|
||||
if (1 === bccomp($remainingAmount, '0')) {
|
||||
return $remainingAmount;
|
||||
}
|
||||
}
|
||||
|
||||
return $savePerMonth;
|
||||
return '0';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,9 @@ class AccountBalanceCalculator
|
||||
foreach ($set as $entry) {
|
||||
// Log::debug(sprintf('Processing transaction #%d with currency #%d and amount %s', $entry->id, $entry->transaction_currency_id, Steam::bcround($entry->amount, 2)));
|
||||
// start with empty array:
|
||||
$entry->account_id = (int) $entry->account_id;
|
||||
$entry->transaction_currency_id = (int) $entry->transaction_currency_id;
|
||||
|
||||
$balances[$entry->account_id] ??= [];
|
||||
$balances[$entry->account_id][$entry->transaction_currency_id] ??= [
|
||||
self::getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore),
|
||||
|
||||
@@ -543,12 +543,13 @@ class SearchRuleEngine implements RuleEngineInterface
|
||||
}
|
||||
|
||||
// pick up from the action if it actually acted or not:
|
||||
if (true === $ruleAction->stop_processing && $result) {
|
||||
if (true === $ruleAction->stop_processing && true === $result) {
|
||||
Log::debug(sprintf('Rule action "%s" reports changes AND asks to break, so break!', $ruleAction->action_type));
|
||||
|
||||
return true;
|
||||
}
|
||||
if (true === $ruleAction->stop_processing && false === $result) {
|
||||
// reset is false at this point.
|
||||
if (true === $ruleAction->stop_processing) {
|
||||
Log::debug(sprintf('Rule action "%s" reports NO changes AND asks to break, but we wont break!', $ruleAction->action_type));
|
||||
}
|
||||
|
||||
|
||||
44
changelog.md
44
changelog.md
@@ -3,6 +3,50 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## v6.6.3 - 2026-05-21
|
||||
|
||||
<!-- summary: This release fixes an XSS issue and some minor other bugs -->
|
||||
|
||||
### Changed
|
||||
|
||||
- Better explanation text for the password validation code.
|
||||
|
||||
### Fixed
|
||||
|
||||
- [Discussion 11408](https://github.com/orgs/firefly-iii/discussions/11408) (How do I see the content of link notes?) started by @Coderdude112
|
||||
- [Discussion 11455](https://github.com/orgs/firefly-iii/discussions/11455) ("Sum" Line In Account Charts) started by @PVTejas
|
||||
- [Discussion 12097](https://github.com/orgs/firefly-iii/discussions/12097) (Error updating Exchange Rates) started by @gpampuro
|
||||
- [Issue 12204](https://github.com/firefly-iii/firefly-iii/issues/12204) (A change is shown in "Audit log entries" when there was actually no movement (currency values fields)) reported by @jgmm81
|
||||
- [Issue 12207](https://github.com/firefly-iii/firefly-iii/issues/12207) (Rule triggers hidden on rules page for rules with multiple triggers) reported by @frankakn7
|
||||
- [Discussion 12210](https://github.com/orgs/firefly-iii/discussions/12210) (PiggyBanks and suggested amount per month) started by @Thieume
|
||||
- [Issue 12223](https://github.com/firefly-iii/firefly-iii/issues/12223) (Budget figures on the default financial report does not include transactions in liability accounts) reported by @likinon1981
|
||||
- [Issue 12243](https://github.com/firefly-iii/firefly-iii/issues/12243) (Abacus App is not working with the new OAuth) reported by @darkmatter18
|
||||
- [Issue 12254](https://github.com/firefly-iii/firefly-iii/issues/12254) (Personal Access Tokens not listed on web UI) reported by @imjuzcy
|
||||
- [Issue 12257](https://github.com/firefly-iii/firefly-iii/issues/12257) (getLatestBalance(): Argument #2 ($currencyId) must be of type int, string given) reported by @LaCarotteSauvage
|
||||
- [Issue 12258](https://github.com/firefly-iii/firefly-iii/issues/12258) (500 Internal Server Error when creating piggy bank) reported by @davbrito
|
||||
|
||||
### Security
|
||||
|
||||
- [PR 12271](https://github.com/firefly-iii/firefly-iii/pull/12271) (Fix stored XSS in audit log view via piggy bank name (ale.twig)) reported by @alanturing881
|
||||
|
||||
## v6.6.2 - 2026-04-28
|
||||
|
||||
<!-- summary: This releases fixes a security issue and some small UI issues. Please upgrade at your earliest convenience. -->
|
||||
|
||||
### Added
|
||||
|
||||
- [PR 12179](https://github.com/firefly-iii/firefly-iii/pull/12179) (implement password validation JS script) reported by @tasnim0tantawi
|
||||
- [PR 12182](https://github.com/firefly-iii/firefly-iii/pull/12182) (fix shrinked sidebar expanding when navigating by clicking on icons) reported by @tasnim0tantawi
|
||||
|
||||
### Fixed
|
||||
|
||||
- [Issue 12169](https://github.com/firefly-iii/firefly-iii/issues/12169) (The 'Running balance' column is not showing the respective calculation instantly for new records that use 'Rules') reported by @jgmm81
|
||||
- [Issue 12186](https://github.com/firefly-iii/firefly-iii/issues/12186) (Set a year validator accepted by the system when saving or editing a transaction) reported by @jgmm81
|
||||
|
||||
### Security
|
||||
|
||||
- Fixed an issue where oAuth tokens could be generated before you confirmed your 2FA state. This would allow access to your data when your password was stolen, despite you having MFA enabled.
|
||||
|
||||
## v6.6.1 - 2026-04-19
|
||||
|
||||
<!-- summary: This releases upgrades many dependencies and will invalidate all of your OAuth-tokens and clients. -->
|
||||
|
||||
@@ -111,7 +111,9 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-ide-helper": "^3",
|
||||
"carthage-software/mago": "^1.24.0",
|
||||
"driftingly/rector-laravel": "^2.0",
|
||||
"ergebnis/phpstan-rules": "^2",
|
||||
"fakerphp/faker": "1.*",
|
||||
"filp/whoops": "2.*",
|
||||
"fruitcake/laravel-debugbar": "^4.0",
|
||||
@@ -124,8 +126,7 @@
|
||||
"phpstan/phpstan-strict-rules": "^2",
|
||||
"phpunit/phpunit": "^13",
|
||||
"rector/rector": "^2.3",
|
||||
"thecodingmachine/phpstan-safe-rule": "^1.4",
|
||||
"ergebnis/phpstan-rules": "^2"
|
||||
"thecodingmachine/phpstan-safe-rule": "^1.4"
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-php54": "*",
|
||||
|
||||
817
composer.lock
generated
817
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
use Pdo\Mysql;
|
||||
|
||||
use function Safe\parse_url;
|
||||
|
||||
@@ -52,22 +53,22 @@ $mySqlSSLOptions = [];
|
||||
$useSSL = env_default_when_empty(env('MYSQL_USE_SSL'), false);
|
||||
if (false !== $useSSL && null !== $useSSL && '' !== $useSSL) {
|
||||
if (null !== $mysql_ssl_ca_dir) {
|
||||
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CAPATH] = $mysql_ssl_ca_dir;
|
||||
$mySqlSSLOptions[Mysql::ATTR_SSL_CAPATH] = $mysql_ssl_ca_dir;
|
||||
}
|
||||
if (null !== $mysql_ssl_ca_file) {
|
||||
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ssl_ca_file;
|
||||
$mySqlSSLOptions[Mysql::ATTR_SSL_CA] = $mysql_ssl_ca_file;
|
||||
}
|
||||
if (null !== $mysql_ssl_cert) {
|
||||
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CERT] = $mysql_ssl_cert;
|
||||
$mySqlSSLOptions[Mysql::ATTR_SSL_CERT] = $mysql_ssl_cert;
|
||||
}
|
||||
if (null !== $mysql_ssl_key) {
|
||||
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_KEY] = $mysql_ssl_key;
|
||||
$mySqlSSLOptions[Mysql::ATTR_SSL_KEY] = $mysql_ssl_key;
|
||||
}
|
||||
if (null !== $mysql_ssl_ciphers) {
|
||||
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_CIPHER] = $mysql_ssl_ciphers;
|
||||
$mySqlSSLOptions[Mysql::ATTR_SSL_CIPHER] = $mysql_ssl_ciphers;
|
||||
}
|
||||
if (null !== $mysql_ssl_verify) {
|
||||
$mySqlSSLOptions[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $mysql_ssl_verify;
|
||||
$mySqlSSLOptions[Mysql::ATTR_SSL_VERIFY_SERVER_CERT] = $mysql_ssl_verify;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => (bool)env_default_when_empty(env('USE_RUNNING_BALANCE'), true), // this is only the default value, is not used.
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2026-04-25',
|
||||
'build_time' => 1777088715,
|
||||
'version' => 'develop/2026-05-20',
|
||||
'build_time' => 1779302299,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
@@ -198,6 +198,7 @@ return [
|
||||
'default_locale' => env_default_when_empty(env('DEFAULT_LOCALE'), 'equal'),
|
||||
|
||||
// account types that may have or set a currency
|
||||
|
||||
'valid_currency_account_types' => [
|
||||
AccountTypeEnum::ASSET->value,
|
||||
AccountTypeEnum::LOAN->value,
|
||||
@@ -224,7 +225,7 @@ return [
|
||||
'available_dark_modes' => ['light', 'dark', 'browser'],
|
||||
'bill_reminder_periods' => [90, 30, 14, 7, 0],
|
||||
'valid_view_ranges' => ['1D', '1W', '1M', '3M', '6M', '1Y'],
|
||||
'valid_url_protocols' => env_default_when_empty(env('VALID_URL_PROTOCOLS'), 'http,https,ftp,ftps,mailto'), // no longer used, only for default.
|
||||
'valid_url_protocols' => env_default_when_empty(env('VALID_URL_PROTOCOLS'), 'http,https,ftp,ftps,mailto,abacusfiiiapp'), // no longer used, only for default.
|
||||
'allowedMimes' => [
|
||||
// plain files
|
||||
'text/plain',
|
||||
|
||||
566
package-lock.json
generated
566
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
"resources/assets/v2"
|
||||
],
|
||||
"devDependencies": {
|
||||
"postcss": "^8.4.47"
|
||||
"postcss": "^8.5.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"patch-package": "^8.0.1"
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function () {
|
||||
$(function () {
|
||||
"use strict";
|
||||
const form = document.querySelector('form[action="'+route+'"]');
|
||||
const errorBox = document.getElementById('client-errors');
|
||||
const errorList = document.getElementById('client-errors-list');
|
||||
|
||||
@@ -38,9 +38,9 @@ function parseToLocalDates() {
|
||||
var obj = moment.utc(date).local();
|
||||
|
||||
console.log('auto convert to timezone is: "' + obj.format() + '"');
|
||||
console.log('moment.js format is: "'+date_time_js+'"');
|
||||
console.log('moment.js format is: "' + date_time_js + '"');
|
||||
|
||||
$(this).text(obj.format(date_time_js) + ' ('+ timeZone +')');
|
||||
$(this).text(obj.format(date_time_js) + ' (' + timeZone + ')');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -50,19 +50,25 @@ $(function () {
|
||||
configAccounting(currencySymbol);
|
||||
|
||||
// on submit of logout button:
|
||||
$('.logout-link').click(function(e) {
|
||||
$('.logout-link').click(function (e) {
|
||||
e.preventDefault();
|
||||
document.getElementById('logout-form').submit();
|
||||
return false;
|
||||
});
|
||||
|
||||
// save sidebar collapsed state when page loads.
|
||||
$('[data-toggle="push-menu"]').click(function () {
|
||||
localStorage.setItem('ff3_sidebar_collapsed', (!$('body').hasClass('sidebar-collapse')).toString());
|
||||
});
|
||||
|
||||
|
||||
|
||||
// on submit of form, disable any button in form:
|
||||
$('form.form-horizontal:not(.nodisablebutton)').on('submit', function () {
|
||||
$('button[type="submit"]').prop('disabled', true);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// when you click on a currency, this happens:
|
||||
$('.currency-option').on('click', currencySelect);
|
||||
|
||||
|
||||
@@ -100,29 +100,29 @@ $(function () {
|
||||
// show rule triggers
|
||||
$('.rule-triggers-show').click(function (e) {
|
||||
var obj = $(e.currentTarget);
|
||||
$('.rule-trigger-list[data-id="' + obj.data('id') + '"]').show();
|
||||
$('.rule-triggers-show[data-id="' + obj.data('id') + '"]').hide();
|
||||
$('.rule-trigger-list[data-id="' + obj.data('id') + '"]').removeClass('hidden');
|
||||
$('.rule-triggers-show[data-id="' + obj.data('id') + '"]').addClass('hidden');
|
||||
});
|
||||
|
||||
$('.rule-trigger-list').each(function(i,v) {
|
||||
var obj = $(v);
|
||||
if(obj.data('count') > 2) {
|
||||
obj.hide();
|
||||
$('.rule-triggers-show[data-id="' + obj.data('id') + '"]').show();
|
||||
obj.addClass('hidden');
|
||||
$('.rule-triggers-show[data-id="' + obj.data('id') + '"]').removeClass('hidden');
|
||||
}
|
||||
});
|
||||
// show rule actions
|
||||
$('.rule-actions-show').click(function (e) {
|
||||
var obj = $(e.currentTarget);
|
||||
$('.rule-action-list[data-id="' + obj.data('id') + '"]').show();
|
||||
$('.rule-actions-show[data-id="' + obj.data('id') + '"]').hide();
|
||||
$('.rule-action-list[data-id="' + obj.data('id') + '"]').removeClass('hidden');
|
||||
$('.rule-actions-show[data-id="' + obj.data('id') + '"]').addClass('hidden');
|
||||
});
|
||||
|
||||
$('.rule-action-list').each(function(i,v) {
|
||||
var obj = $(v);
|
||||
if(obj.data('count') > 1) {
|
||||
obj.hide();
|
||||
$('.rule-actions-show[data-id="' + obj.data('id') + '"]').show();
|
||||
obj.addClass('hidden');
|
||||
$('.rule-actions-show[data-id="' + obj.data('id') + '"]').removeClass('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -194,9 +194,9 @@ function testRuleTriggers(e) {
|
||||
// Show warning if appropriate
|
||||
if (data.warning) {
|
||||
modal.find(".transaction-warning .warning-contents").text(data.warning);
|
||||
modal.find(".transaction-warning").show();
|
||||
modal.find(".transaction-warning").removeClass('hidden');
|
||||
} else {
|
||||
modal.find(".transaction-warning").hide();
|
||||
modal.find(".transaction-warning").addClass('hidden');
|
||||
}
|
||||
|
||||
// Show the modal dialog
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"font-awesome": "^4.7.0",
|
||||
"jquery": "^3",
|
||||
"laravel-mix": "^6.0",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss": "^8.5.14",
|
||||
"uiv": "^1.4",
|
||||
"vue": "^2.7",
|
||||
"vue-i18n": "^8",
|
||||
|
||||
@@ -442,11 +442,16 @@ export default {
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
if (typeof error.response.data === 'object') {
|
||||
form.errors = _.flatten(_.toArray(error.response.data.errors));
|
||||
for (const [key, value] of Object.entries(error.response.data.errors)) {
|
||||
console.log(`${key}: ${value}`);
|
||||
form.errors.push(value);
|
||||
}
|
||||
} else {
|
||||
form.errors = ['Something went wrong. Please try again.'];
|
||||
}
|
||||
console.log(form.errors);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ export default {
|
||||
* Prepare the component (Vue 1.x).
|
||||
*/
|
||||
ready() {
|
||||
console.log('ready()');
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
@@ -220,6 +221,7 @@ export default {
|
||||
* Prepare the component (Vue 2.x).
|
||||
*/
|
||||
mounted() {
|
||||
console.log('mounted()');
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
@@ -228,6 +230,7 @@ export default {
|
||||
* Prepare the component.
|
||||
*/
|
||||
prepareComponent() {
|
||||
console.log('prepareComponent()');
|
||||
this.getTokens();
|
||||
// this.getScopes();
|
||||
|
||||
@@ -251,8 +254,10 @@ export default {
|
||||
* Get all of the personal access tokens for the user.
|
||||
*/
|
||||
getTokens() {
|
||||
console.log('getTokens()');
|
||||
axios.get('./oauth/personal-access-tokens')
|
||||
.then(response => {
|
||||
console.log(response.data);
|
||||
this.tokens = response.data;
|
||||
});
|
||||
},
|
||||
@@ -261,6 +266,7 @@ export default {
|
||||
* Get all the available scopes.
|
||||
*/
|
||||
getScopes() {
|
||||
console.log('getScopes()');
|
||||
axios.get('./oauth/scopes')
|
||||
.then(response => {
|
||||
this.scopes = response.data;
|
||||
@@ -271,6 +277,7 @@ export default {
|
||||
* Show the form for creating new tokens.
|
||||
*/
|
||||
showCreateTokenForm() {
|
||||
console.log('showCreateTokenForm()');
|
||||
$('#modal-create-token').modal('show');
|
||||
},
|
||||
|
||||
@@ -278,12 +285,14 @@ export default {
|
||||
* Create a new personal access token.
|
||||
*/
|
||||
store() {
|
||||
console.log('store()');
|
||||
this.accessToken = null;
|
||||
|
||||
this.form.errors = [];
|
||||
|
||||
axios.post('./oauth/personal-access-tokens', this.form)
|
||||
.then(response => {
|
||||
console.log('Successful POST new token, reset form content.');
|
||||
this.form.name = '';
|
||||
this.form.scopes = [];
|
||||
this.form.errors = [];
|
||||
@@ -293,6 +302,7 @@ export default {
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
console.warn('Bad POST new token, show error.');
|
||||
if (typeof error.response.data === 'object') {
|
||||
this.form.errors = _.flatten(_.toArray(error.response.data.errors));
|
||||
} else {
|
||||
@@ -323,6 +333,7 @@ export default {
|
||||
* Show the given access token to the user.
|
||||
*/
|
||||
showAccessToken(accessToken) {
|
||||
console.log('showAccessToken');
|
||||
$('#modal-create-token').modal('hide');
|
||||
|
||||
this.accessToken = accessToken;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"firefly": {
|
||||
"explain_pats": "I token di accesso personale sono chiavi lunghe (con un massimo di 1 anno) che consentono l'accesso diretto e illimitato ai tuoi dati di Firefly III. Strumenti come il Firefly III Data Importer e l'integrazione di Firefly III in Home Assistant utilizzano tali token per connettersi a Firefly III e usarlo. Quando si crea un token, \u00e8 visibile solo una volta. Il token \u00e8 molto lungo.",
|
||||
"profile_oauth_clients_explain": "An OAuth client can be used to connect \"smart\" applications to Firefly III: applications that are capable of redirecting you to your Firefly III, get your permission, and return you back. The Firefly III Data Importer is such an application. OAuth clients can be generated with or without a \"secret\". This secret is used to authenticate the client. Since not all clients are capable of storing the secret, so you have the option to generate a client without one.",
|
||||
"profile_oauth_clients_explain": "Un client OAuth pu\u00f2 essere utilizzato per connettere applicazioni \"intelligenti\" a Firefly III: applicazioni in grado di reindirizzarti al tuo Firefly III, ottenere la tua autorizzazione e riportarti indietro. Firefly III Data Importer \u00e8 un esempio di tale applicazione. I client OAuth possono essere generati con o senza una \"chiave segreta\". Questa chiave segreta viene utilizzata per autenticare il client. Poich\u00e9 non tutti i client sono in grado di memorizzare la chiave segreta, hai la possibilit\u00e0 di generare un client senza di essa.",
|
||||
"regenerate_secret": "Rigenera chiave segreta",
|
||||
"administrations_page_title": "Amministrazioni finanziarie",
|
||||
"administrations_index_menu": "Amministrazioni finanziarie",
|
||||
@@ -104,8 +104,8 @@
|
||||
"piggy_bank": "Salvadanaio",
|
||||
"profile_oauth_client_secret_title": "Segreto del client",
|
||||
"profile_oauth_client_secret_expl": "Ecco il segreto del nuovo client. Questa \u00e8 l'unica occasione in cui viene mostrato pertanto non perderlo! Ora puoi usare questo segreto per effettuare delle richieste alle API.",
|
||||
"profile_oauth_confidential": "Keep a secret?",
|
||||
"profile_oauth_confidential_help": "Can the application you're using this for keep a secret? The Firefly III Data Importer CANNOT keep a secret, so UNCHECK the box. In other cases, it's up to you.",
|
||||
"profile_oauth_confidential": "Mantenere un segreto?",
|
||||
"profile_oauth_confidential_help": "L'applicazione che stai utilizzando pu\u00f2 mantenere un segreto? Firefly III Data Importer NON pu\u00f2 mantenere un segreto, quindi DESELEZIONARE la casella. Negli altri casi, la scelta \u00e8 a tua discrezione.",
|
||||
"multi_account_warning_unknown": "A seconda del tipo di transazione che hai creato, il conto di origine e\/o destinazione delle successive suddivisioni pu\u00f2 essere sovrascritto da qualsiasi cosa sia definita nella prima suddivisione della transazione.",
|
||||
"multi_account_warning_withdrawal": "Ricorda che il conto di origine delle successive suddivisioni verr\u00e0 sovrascritto da quello definito nella prima suddivisione del prelievo.",
|
||||
"multi_account_warning_deposit": "Ricorda che il conto di destinazione delle successive suddivisioni verr\u00e0 sovrascritto da quello definito nella prima suddivisione del deposito.",
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"firefly": {
|
||||
"explain_pats": "Personal Access Tokens are long lived (with a maximum of 1 year) keys that allow direct and unlimited access to your Firefly III data. Tools like the Firefly III Data Importer and the Firefly III integration in Home Assistant use such tokens to connect to Firefly III and do their thing. When you create a token, it is only visible once. The token is also very long.",
|
||||
"profile_oauth_clients_explain": "An OAuth client can be used to connect \"smart\" applications to Firefly III: applications that are capable of redirecting you to your Firefly III, get your permission, and return you back. The Firefly III Data Importer is such an application. OAuth clients can be generated with or without a \"secret\". This secret is used to authenticate the client. Since not all clients are capable of storing the secret, so you have the option to generate a client without one.",
|
||||
"regenerate_secret": "Regenerate secret",
|
||||
"explain_pats": "\u4e2a\u4eba\u8bbf\u95ee\u4ee4\u724c\u662f\u4e00\u79cd\u957f\u671f\u6709\u6548\uff08\u6700\u957f1\u5e74\uff09\u7684\u4ee4\u724c\uff0c\u53ef\u8ba9\u60a8\u76f4\u63a5\u3001\u65e0\u9650\u5236\u5730\u8bbf\u95ee Firefly III \u6570\u636e\u3002\u8bf8\u5982 `Firefly III Data Importer` \u4ee5\u53ca Home Assistant \u4e2d\u7684 Firefly III \u96c6\u6210\u7b49\u5de5\u5177\uff0c\u6b63\u662f\u4f7f\u7528\u6b64\u7c7b\u4ee4\u724c\u8fde\u63a5\u81f3 Firefly III \u5e76\u5b8c\u6210\u5404\u81ea\u7684\u4efb\u52a1\u3002\u521b\u5efa\u4ee4\u724c\u65f6\uff0c\u5b83\u4ec5\u4f1a\u663e\u793a\u4e00\u6b21\u3002\u4ee4\u724c\u5b57\u7b26\u4e32\u901a\u5e38\u4f1a\u5f88\u957f\u3002",
|
||||
"profile_oauth_clients_explain": "OAuth\u5ba2\u6237\u7aef\u53ef\u7528\u4e8e\u5c06\u201c\u667a\u80fd\u201d\u5e94\u7528\u8fde\u63a5\u5230 Firefly III\uff1a\u5b83\u4eec\u80fd\u591f\u5c06\u60a8\u91cd\u5b9a\u5411\u5230\u60a8\u7684Firefly III\uff0c\u83b7\u53d6\u60a8\u7684\u6388\u6743\uff0c\u7136\u540e\u8fd4\u56de\u5e94\u7528\u3002`Firefly III Data Importer`\u5c31\u662f\u8fd9\u6837\u7684\u5e94\u7528\u3002\u751f\u6210OAuth\u5ba2\u6237\u7aef\u65f6\u53ef\u4ee5\u9009\u62e9\u662f\u5426\u9644\u5e26\u201c\u5bc6\u94a5\uff08secret\uff09\u201d\uff0c\u5b83\u7528\u4e8e\u9a8c\u8bc1\u5ba2\u6237\u7aef\u3002\u5e76\u975e\u6240\u6709\u5ba2\u6237\u7aef\u90fd\u80fd\u591f\u5b89\u5168\u5b58\u50a8\u5bc6\u94a5\uff0c\u6240\u4ee5\u60a8\u53ef\u4ee5\u9009\u62e9\u751f\u6210\u4e00\u4e2a\u4e0d\u5e26\u5bc6\u94a5\u7684\u5ba2\u6237\u7aef\u3002",
|
||||
"regenerate_secret": "\u91cd\u65b0\u751f\u6210\u5bc6\u94a5",
|
||||
"administrations_page_title": "\u8d22\u52a1\u7ba1\u7406",
|
||||
"administrations_index_menu": "\u8d22\u52a1\u7ba1\u7406",
|
||||
"expires_at": "\u8fc7\u671f\u4e8e",
|
||||
"temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.",
|
||||
"administration_currency_form_help": "It may take a long time for the page to load if you change the primary currency because transaction may need to be converted to your (new) primary currency.",
|
||||
"temp_administrations_introduction": "Firefly III \u4e0d\u4e45\u5c06\u652f\u6301\u7ba1\u7406\u591a\u4e2a\u8d22\u52a1\u7ba1\u7406\u3002\u76ee\u524d\u60a8\u53ea\u6709\u4e00\u4e2a\u3002\u60a8\u53ef\u4ee5\u8bbe\u7f6e\u8fd9\u4e2a\u8d22\u52a1\u7ba1\u7406\u7684\u540d\u79f0\u53ca\u5176\u4e3b\u8981\u8d27\u5e01\u3002\u8fd9\u53d6\u4ee3\u4e86\u4e4b\u524d\u201c\u9ed8\u8ba4\u8d27\u5e01\u201d\u7684\u8bbe\u7f6e\u3002\u8be5\u8bbe\u7f6e\u73b0\u5728\u5df2\u4e0e\u8d22\u52a1\u7ba1\u7406\u6302\u94a9\uff0c\u6bcf\u4e2a\u8d22\u52a1\u7ba1\u7406\u53ef\u4ee5\u6709\u4e0d\u540c\u8bbe\u7f6e\u3002",
|
||||
"administration_currency_form_help": "\u5982\u679c\u60a8\u66f4\u6539\u4e3b\u8981\u8d27\u5e01\uff0c\u9875\u9762\u52a0\u8f7d\u53ef\u80fd\u9700\u8981\u5f88\u957f\u65f6\u95f4\uff0c\u56e0\u4e3a\u4ea4\u6613\u53ef\u80fd\u9700\u8981\u8f6c\u6362\u4e3a\u60a8\u7684(\u65b0)\u4e3b\u8981\u8d27\u5e01\u3002",
|
||||
"administrations_page_edit_sub_title_js": "\u7f16\u8f91\u8d22\u52a1\u7ba1\u7406{title}",
|
||||
"table": "\u8868\u683c",
|
||||
"welcome_back": "\u4eca\u5929\u7406\u8d22\u4e86\u5417\uff1f",
|
||||
@@ -75,8 +75,8 @@
|
||||
"profile_whoops": "\u5f88\u62b1\u6b49\uff01",
|
||||
"profile_something_wrong": "\u53d1\u751f\u9519\u8bef\uff01",
|
||||
"profile_try_again": "\u53d1\u751f\u9519\u8bef\uff0c\u8bf7\u7a0d\u540e\u518d\u8bd5\u3002",
|
||||
"profile_oauth_clients": "OAuth Clients and Applications",
|
||||
"profile_oauth_no_clients": "You have not created any OAuth clients or applications.",
|
||||
"profile_oauth_clients": "OAuth \u5ba2\u6237\u7aef\u548c\u5e94\u7528\u7a0b\u5e8f",
|
||||
"profile_oauth_no_clients": "\u60a8\u8fd8\u6ca1\u6709\u521b\u5efa\u4efb\u4f55 OAuth \u5ba2\u6237\u7aef\u6216\u5e94\u7528\u7a0b\u5e8f\u3002",
|
||||
"profile_oauth_clients_header": "\u5ba2\u6237\u7aef",
|
||||
"profile_oauth_client_id": "\u5ba2\u6237\u7aef ID",
|
||||
"profile_oauth_client_name": "\u540d\u79f0",
|
||||
@@ -86,7 +86,7 @@
|
||||
"profile_oauth_edit_client": "\u7f16\u8f91\u5ba2\u6237\u7aef",
|
||||
"profile_oauth_name_help": "\u60a8\u7684\u7528\u6237\u53ef\u4ee5\u8bc6\u522b\u5e76\u4fe1\u4efb\u7684\u4fe1\u606f",
|
||||
"profile_oauth_redirect_url": "\u8df3\u8f6c\u7f51\u5740",
|
||||
"profile_oauth_clients_external_auth": "Please note that if you're using an external authentication provider like Authelia, OAuth Clients will not work. You can use Personal Access Tokens only.",
|
||||
"profile_oauth_clients_external_auth": "\u8bf7\u6ce8\u610f\uff1a\u5982\u679c\u60a8\u6b63\u5728\u4f7f\u7528\u5982 Authelia \u7684\u5916\u90e8\u8eab\u4efd\u9a8c\u8bc1\u63d0\u4f9b\u5546\uff0cOAuth \u5ba2\u6237\u7aef\u5c06\u65e0\u6cd5\u4f7f\u7528\u3002\u60a8\u53ea\u80fd\u4f7f\u7528\u4e2a\u4eba\u8bbf\u95ee\u4ee4\u724c\u3002",
|
||||
"profile_oauth_redirect_url_help": "\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u7684\u6388\u6743\u56de\u8c03\u7f51\u5740",
|
||||
"profile_authorized_apps": "\u5df2\u6388\u6743\u5e94\u7528",
|
||||
"profile_authorized_clients": "\u5df2\u6388\u6743\u5ba2\u6237\u7aef",
|
||||
@@ -104,24 +104,24 @@
|
||||
"piggy_bank": "\u5b58\u94b1\u7f50",
|
||||
"profile_oauth_client_secret_title": "\u5ba2\u6237\u7aef\u5bc6\u94a5",
|
||||
"profile_oauth_client_secret_expl": "\u8bf7\u59a5\u5584\u4fdd\u5b58\u60a8\u7684\u65b0\u5ba2\u6237\u7aef\u7684\u5bc6\u94a5\uff0c\u6b64\u5bc6\u94a5\u4ec5\u4f1a\u5728\u8fd9\u91cc\u5c55\u793a\u4e00\u6b21\u3002\u60a8\u73b0\u5728\u5df2\u53ef\u4ee5\u4f7f\u7528\u6b64\u5bc6\u94a5\u8fdb\u884c API \u8bf7\u6c42\u3002",
|
||||
"profile_oauth_confidential": "Keep a secret?",
|
||||
"profile_oauth_confidential_help": "Can the application you're using this for keep a secret? The Firefly III Data Importer CANNOT keep a secret, so UNCHECK the box. In other cases, it's up to you.",
|
||||
"profile_oauth_confidential": "\u9644\u5e26\u5bc6\u94a5\uff1f",
|
||||
"profile_oauth_confidential_help": "\u60a8\u8981\u8fde\u63a5\u7684\u5e94\u7528\u80fd\u5426\u5b89\u5168\u4fdd\u5b58\u5bc6\u94a5\uff1f`Firefly III Data Importer`\u65e0\u6cd5\u4fdd\u5b58\u5bc6\u94a5\uff0c\u56e0\u6b64\u8bf7\u53d6\u6d88\u52fe\u9009\u6b64\u6846\u3002\u5176\u4ed6\u60c5\u51b5\u7531\u60a8\u81ea\u884c\u51b3\u5b9a\u3002",
|
||||
"multi_account_warning_unknown": "\u6839\u636e\u60a8\u521b\u5efa\u7684\u4ea4\u6613\u7c7b\u578b\uff0c\u540e\u7eed\u62c6\u5206\u7684\u6765\u6e90\u548c\/\u6216\u76ee\u6807\u8d26\u6237\u53ef\u80fd\u88ab\u4ea4\u6613\u7684\u9996\u7b14\u62c6\u5206\u7684\u914d\u7f6e\u6240\u8986\u76d6\u3002",
|
||||
"multi_account_warning_withdrawal": "\u8bf7\u6ce8\u610f\uff0c\u540e\u7eed\u62c6\u5206\u7684\u6765\u6e90\u8d26\u6237\u5c06\u4f1a\u88ab\u652f\u51fa\u7684\u9996\u7b14\u62c6\u5206\u7684\u914d\u7f6e\u6240\u8986\u76d6\u3002",
|
||||
"multi_account_warning_deposit": "\u8bf7\u6ce8\u610f\uff0c\u540e\u7eed\u62c6\u5206\u7684\u76ee\u6807\u8d26\u6237\u5c06\u4f1a\u88ab\u6536\u5165\u7684\u9996\u7b14\u62c6\u5206\u7684\u914d\u7f6e\u6240\u8986\u76d6\u3002",
|
||||
"multi_account_warning_transfer": "\u8bf7\u6ce8\u610f\uff0c\u540e\u7eed\u62c6\u5206\u7684\u6765\u6e90\u548c\u76ee\u6807\u8d26\u6237\u5c06\u4f1a\u88ab\u8f6c\u8d26\u7684\u9996\u7b14\u62c6\u5206\u7684\u914d\u7f6e\u6240\u8986\u76d6\u3002",
|
||||
"webhook_trigger_ANY": "After any event",
|
||||
"webhook_trigger_ANY": "\u5728\u4efb\u4f55\u4e8b\u4ef6\u4e4b\u540e",
|
||||
"webhook_trigger_STORE_TRANSACTION": "\u4ea4\u6613\u521b\u5efa\u540e",
|
||||
"webhook_trigger_UPDATE_TRANSACTION": "\u4ea4\u6613\u66f4\u65b0\u540e",
|
||||
"webhook_trigger_DESTROY_TRANSACTION": "\u4ea4\u6613\u5220\u9664\u540e",
|
||||
"webhook_trigger_STORE_BUDGET": "After budget creation",
|
||||
"webhook_trigger_UPDATE_BUDGET": "After budget update",
|
||||
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
|
||||
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
|
||||
"webhook_trigger_STORE_BUDGET": "\u9884\u7b97\u521b\u5efa\u540e",
|
||||
"webhook_trigger_UPDATE_BUDGET": "\u9884\u7b97\u66f4\u65b0\u540e",
|
||||
"webhook_trigger_DESTROY_BUDGET": "\u9884\u7b97\u5220\u9664\u540e",
|
||||
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "\u9884\u7b97\u91d1\u989d\u53d8\u66f4\u540e",
|
||||
"webhook_response_TRANSACTIONS": "\u4ea4\u6613\u8be6\u60c5",
|
||||
"webhook_response_RELEVANT": "Relevant details",
|
||||
"webhook_response_RELEVANT": "\u76f8\u5173\u660e\u7ec6",
|
||||
"webhook_response_ACCOUNTS": "\u8d26\u6237\u8be6\u60c5",
|
||||
"webhook_response_NONE": "No details",
|
||||
"webhook_response_NONE": "\u65e0\u8be6\u7ec6\u4fe1\u606f",
|
||||
"webhook_delivery_JSON": "JSON",
|
||||
"actions": "\u64cd\u4f5c",
|
||||
"meta_data": "\u540e\u8bbe\u8d44\u6599",
|
||||
@@ -163,7 +163,7 @@
|
||||
"url": "\u7f51\u5740",
|
||||
"active": "\u542f\u7528",
|
||||
"interest_date": "\u5229\u606f\u65e5\u671f",
|
||||
"administration_currency": "Primary currency",
|
||||
"administration_currency": "\u4e3b\u8981\u8d27\u5e01",
|
||||
"title": "\u6807\u9898",
|
||||
"date": "\u65e5\u671f",
|
||||
"book_date": "\u767b\u8bb0\u65e5\u671f",
|
||||
@@ -183,7 +183,7 @@
|
||||
"list": {
|
||||
"title": "\u6807\u9898",
|
||||
"active": "\u662f\u5426\u542f\u7528\uff1f",
|
||||
"primary_currency": "Primary currency",
|
||||
"primary_currency": "\u4e3b\u8981\u8d27\u5e01",
|
||||
"trigger": "\u89e6\u53d1\u6761\u4ef6",
|
||||
"response": "\u7b54\u590d",
|
||||
"delivery": "\u4ea4\u4ed8",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"laravel-vite-plugin": "^3",
|
||||
"patch-package": "^8",
|
||||
"sass": "^1",
|
||||
"vite": "=8.0.8",
|
||||
"vite": "=8.0.13",
|
||||
"vite-plugin-manifest-sri": "^0.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1398,6 +1398,7 @@ return [
|
||||
'pref_locale' => 'Locale settings',
|
||||
'pref_languages_help' => 'Firefly III supports several languages. Which one do you prefer?',
|
||||
'pref_locale_help' => 'Firefly III allows you to set other local settings, like how currencies, numbers and dates are formatted. Entries in this list may not be supported by your system. Firefly III doesn\'t have the correct date settings for every locale; contact me for improvements.',
|
||||
'pref_locale_exception' => 'Firefly III can only change how you view dates. Entering a date will always be formatted according to your browser settings, not Firefly III settings. If your browser is set to "English (US)" you must always enter the date formatted as mm/dd/yyyy. No setting in Firefly III can change that.',
|
||||
'pref_locale_no_demo' => 'This feature won\'t work for the demo user.',
|
||||
'pref_convert_to_primary' => 'Display amounts in your primary currency',
|
||||
'pref_convert_to_primary_help' => 'This option will make Firefly III try to display and show your primary currency in as many places as possible, converting amounts where necessary. This sacrifices accuracy for ease of use, because conversion is not always exact. Please verify that Firefly III has the necessary conversion rates on the "exchange rates"-page.',
|
||||
@@ -1621,7 +1622,7 @@ return [
|
||||
'secure_pw_ff' => 'Do you use the same password all over the internet? If one site loses your password, hackers have access to all your data. Firefly III relies on you to choose a strong and unique password to protect your financial records.',
|
||||
'secure_pw_check_box' => 'To help you do that Firefly III can check if the password you want to use has been stolen in the past. If this is the case, Firefly III advises you NOT to use that password.',
|
||||
'secure_pw_working_title' => 'How does it work?',
|
||||
'secure_pw_working' => 'By checking the box, Firefly III will send the first five characters of the SHA1 hash of your password to <a href="https://www.troyhunt.com/introducing-306-million-freely-downloadable-pwned-passwords/">the website of Troy Hunt</a> to see if it is on the list. This will stop you from using unsafe passwords as is recommended in the latest <a href="https://pages.nist.gov/800-63-3/sp800-63b.html">NIST Special Publication</a> on this subject.',
|
||||
'secure_pw_working' => 'By checking the box, Firefly III will send the first five characters of the SHA1 hash of your password to <a href="https://haveibeenpwned.com/API/v3#PwnedPasswords">the website of Troy Hunt</a> to see if it is on the list. This will stop you from using unsafe passwords as is recommended in the latest <a href="https://pages.nist.gov/800-63-3/sp800-63b.html">NIST Special Publication</a> on this subject.',
|
||||
'secure_pw_should' => 'Should I check the box?',
|
||||
'secure_pw_long_password' => 'Yes. Always verify your password is safe.',
|
||||
'command_line_token' => 'Command line token',
|
||||
@@ -1813,6 +1814,13 @@ return [
|
||||
'options' => 'Options',
|
||||
|
||||
// budgets:
|
||||
'spent_this_period' => 'Spent on this budget',
|
||||
'spent_this_period_per_day' => 'Spent on this budget per day (:days day(s))',
|
||||
'spent_in_budget_limit_outside_period' => 'Spent on this budget, but NOT in this period',
|
||||
'spent_in_budget_limit_outside_period_per_day' => 'Spent on this budget, but NOT in this period per day (:days day(s))',
|
||||
'left_in_budget_limit_overview' => 'Left in this budget',
|
||||
'left_in_budget_limit_per_day' => 'Left in this budget per day (:days day(s))',
|
||||
'nothing_left_in_budget' => 'The budget is now empty',
|
||||
'daily_budgets' => 'Daily budgets',
|
||||
'weekly_budgets' => 'Weekly budgets',
|
||||
'monthly_budgets' => 'Monthly budgets',
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<input type="checkbox" id="verify_password" checked name="verify_password" value="1">
|
||||
<input type="checkbox" id="verify_password" name="verify_password" value="1">
|
||||
<label for="verify_password">
|
||||
{{ trans('form.verify_password') }}
|
||||
<a href="#"
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
{% for budgetLimit in budget.budgeted %}
|
||||
<span class="left_span" data-currency="{{ budgetLimit.currency_id }}" data-limit="{{ budgetLimit.id }}" data-value="{{ budgetLimit.left }}" class="amount_left">
|
||||
{# the amount left #}
|
||||
{{ formatAmountBySymbol(budgetLimit.left, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }}
|
||||
<span title="{{ 'left_in_budget_limit_overview'|_ }}">{{ formatAmountBySymbol(budgetLimit.left, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }}</span>
|
||||
|
||||
{# if the budget limit is in the past, this is not interesting. #}
|
||||
{# if there is nothing left, this is not interesting. #}
|
||||
{% if not budgetLimit.in_past and -1 == bccomp('0',budgetLimit.left) %}
|
||||
{% if 0 == budgetLimit.active_days_left %}
|
||||
({{ formatAmountBySymbol(budgetLimit.left, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})
|
||||
<span title="{{ trans('firefly.left_in_budget_limit_per_day', {days: 0}) }}">({{ formatAmountBySymbol(budgetLimit.left, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})
|
||||
{% else %}
|
||||
({{ formatAmountBySymbol(budgetLimit.left / budgetLimit.active_days_left, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})
|
||||
<span title="{{ trans('firefly.left_in_budget_limit_per_day', {days: budgetLimit.active_days_left}) }}">({{ formatAmountBySymbol(budgetLimit.left / budgetLimit.active_days_left, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{# if there is nothing left, just format 0.00 #}
|
||||
{% if not budgetLimit.in_past and -1 != bccomp('0',budgetLimit.left) %}
|
||||
({{ formatAmountBySymbol('0', budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})
|
||||
<span title="{{ 'nothing_left_in_budget'|_ }}">({{ formatAmountBySymbol('0', budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})</span>
|
||||
{% endif %}
|
||||
</span><br />
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{# this is spent in budget limits: #}
|
||||
{% for budgetLimit in budget.budgeted %}
|
||||
{{ formatAmountBySymbol(budgetLimit.spent, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }}
|
||||
<span title="{{ 'spent_this_period'|_ }}">{{ formatAmountBySymbol(budgetLimit.spent, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }}</span>
|
||||
{% if 0 == budgetLimit.active_days_passed %}
|
||||
({{ formatAmountBySymbol(budgetLimit.spent, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})
|
||||
<span title="{{ trans('firefly.spent_this_period_per_day', {days: 0}) }}">({{ formatAmountBySymbol(budgetLimit.spent, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})</span>
|
||||
{% else %}
|
||||
({{ formatAmountBySymbol(budgetLimit.spent / budgetLimit.active_days_passed, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})
|
||||
<span title="{{ trans('firefly.spent_this_period_per_day', {days: budgetLimit.active_days_passed}) }}">({{ formatAmountBySymbol(budgetLimit.spent / budgetLimit.active_days_passed, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})</span>
|
||||
{% endif %}
|
||||
<br />
|
||||
{% endfor %}
|
||||
@@ -12,11 +12,11 @@
|
||||
{# this is spent NOT in budget limits: #}
|
||||
{% for spent in budget.spent %}
|
||||
{% if 0 != bccomp('0', spent.spent_outside) %}
|
||||
{{ formatAmountBySymbol(spent.spent_outside, spent.currency_symbol, spent.currency_decimal_places) }}
|
||||
<span title="{{ 'spent_in_budget_limit_outside_period'|_ }}">{{ formatAmountBySymbol(spent.spent_outside, spent.currency_symbol, spent.currency_decimal_places) }}</span>
|
||||
{% if 0 == activeDaysPassed %}
|
||||
({{ formatAmountBySymbol(spent.spent_outside, spent.currency_symbol, spent.currency_decimal_places) }})
|
||||
<span title="{{ trans('firefly.spent_in_budget_limit_outside_period_per_day', {days: 0}) }}">({{ formatAmountBySymbol(spent.spent_outside, spent.currency_symbol, spent.currency_decimal_places) }})</span>
|
||||
{% else %}
|
||||
({{ formatAmountBySymbol(spent.spent_outside / activeDaysPassed, spent.currency_symbol, spent.currency_decimal_places) }})
|
||||
<span title="{{ trans('firefly.spent_in_budget_limit_outside_period_per_day', {days: activeDaysPassed}) }}">({{ formatAmountBySymbol(spent.spent_outside / activeDaysPassed, spent.currency_symbol, spent.currency_decimal_places) }})</span>
|
||||
{% endif %}
|
||||
<br />
|
||||
{% endif %}
|
||||
|
||||
@@ -86,14 +86,18 @@
|
||||
<script src="v1/js/lib/respond.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<![endif]-->
|
||||
|
||||
{# this entry is in the header so it's loaded early #}
|
||||
<script type="text/javascript" nonce="{{ JS_NONCE }}">var forceDemoOff = false;</script>
|
||||
|
||||
{# favicons #}
|
||||
{% include 'partials.favicons' %}
|
||||
|
||||
</head>
|
||||
<body class="skin-firefly-iii sidebar-mini hold-transition">
|
||||
{# this entry is in the header so it's loaded early #}
|
||||
<script type="text/javascript" nonce="{{ JS_NONCE }}">
|
||||
var forceDemoOff = false;
|
||||
if ('true' === localStorage.getItem('ff3_sidebar_collapsed')) {
|
||||
document.body.classList.add('sidebar-collapse');
|
||||
}
|
||||
</script>
|
||||
<div class="wrapper" id="app">
|
||||
|
||||
<header class="main-header">
|
||||
|
||||
@@ -104,10 +104,10 @@
|
||||
<code>{{ logEntry.after }}</code>
|
||||
{% endif %}
|
||||
{% if 'add_to_piggy' == logEntry.action %}
|
||||
{{ trans('firefly.ale_action_log_add', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.piggy})|raw }}
|
||||
{{ trans('firefly.ale_action_log_add', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.piggy|e})|raw }}
|
||||
{% endif %}
|
||||
{% if 'remove_from_piggy' == logEntry.action %}
|
||||
{{ trans('firefly.ale_action_log_remove', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.piggy})|raw }}
|
||||
{{ trans('firefly.ale_action_log_remove', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.piggy|e})|raw }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
{# language #}
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'pref_languages'|_ }}</h3>
|
||||
<p class="text-info">{{ 'pref_languages_help'|_ }}</p>
|
||||
<p>{{ 'pref_languages_help'|_ }}</p>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<select class="form-control" id="lang_holder" name="language">
|
||||
@@ -63,7 +63,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<p class="text-info">
|
||||
<p>
|
||||
<br/>
|
||||
{{ 'pref_languages_locale'|_ }}
|
||||
</p>
|
||||
@@ -73,7 +73,8 @@
|
||||
{% if not isDocker %}
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'pref_locale'|_ }}</h3>
|
||||
<p class="text-info">{{ 'pref_locale_help'|_ }}</p>
|
||||
<p>{{ 'pref_locale_help'|_ }}</p>
|
||||
<p class="text-warning">{{ 'pref_locale_exception'|_ }}</p>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<select class="form-control" id="locale_holder" name="locale">
|
||||
@@ -104,7 +105,7 @@
|
||||
{# fiscal year #}
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'pref_custom_fiscal_year'|_ }}</h3>
|
||||
<p class="text-info">
|
||||
<p>
|
||||
{{ 'pref_custom_fiscal_year_help'|_ }}
|
||||
</p>
|
||||
{% set isCustomFiscalYear = customFiscalYear == 1 ? true : false %}
|
||||
@@ -116,7 +117,7 @@
|
||||
{% if fireflyiiiconfig('enable_exchange_rates', true) %}
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'pref_convert_to_primary'|_ }}</h3>
|
||||
<p class="text-info">
|
||||
<p>
|
||||
{{ 'pref_convert_to_primary_help'|_ }}
|
||||
</p>
|
||||
{{ ExpandedForm.checkbox('convertToPrimary','1',convertToPrimary,{ 'label' : 'pref_convert_primary_help'|_ }) }}
|
||||
@@ -125,7 +126,7 @@
|
||||
{# conversion back to primary currency #}
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'pref_anonymous'|_ }}</h3>
|
||||
<p class="text-info">
|
||||
<p>
|
||||
{{ 'pref_anonymous_help'|_ }}
|
||||
</p>
|
||||
{{ ExpandedForm.checkbox('anonymous','1',anonymous,{ 'label' : 'pref_anonymous_label'|_ }) }}
|
||||
@@ -138,7 +139,7 @@
|
||||
{# transaction preferences #}
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'pref_optional_fields_transaction'|_ }}</h3>
|
||||
<p class="text-info">
|
||||
<p>
|
||||
{{ 'pref_optional_fields_transaction_help'|_ }}
|
||||
</p>
|
||||
<h4>{{ 'optional_tj_date_fields'|_ }}</h4>
|
||||
@@ -169,7 +170,7 @@
|
||||
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'pref_home_screen_accounts'|_ }}</h3>
|
||||
<p class="text-info">{{ 'pref_home_screen_accounts_help'|_ }}</p>
|
||||
<p>{{ 'pref_home_screen_accounts_help'|_ }}</p>
|
||||
{% for type, accounts in groupedAccounts %}
|
||||
<strong>{{ type }}</strong>
|
||||
{% for id, name in accounts %}
|
||||
@@ -206,7 +207,7 @@
|
||||
{# view range #}
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'pref_view_range'|_ }}</h3>
|
||||
<p class="text-info">{{ 'pref_view_range_help'|_ }}</p>
|
||||
<p>{{ 'pref_view_range_help'|_ }}</p>
|
||||
|
||||
<div class="radio">
|
||||
<label>
|
||||
@@ -311,12 +312,12 @@
|
||||
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'list_page_size_title'|_ }}</h3>
|
||||
<p class="text-info">{{ 'list_page_size_help'|_ }}</p>
|
||||
<p>{{ 'list_page_size_help'|_ }}</p>
|
||||
{{ ExpandedForm.integer('listPageSize',listPageSize,{'label' : 'list_page_size_label'|_}) }}
|
||||
</div>
|
||||
<div class="preferences-box">
|
||||
<h3>{{ 'dark_mode_preference'|_ }}</h3>
|
||||
<p class="text-info">{{ 'dark_mode_preference_help'|_ }}</p>
|
||||
<p>{{ 'dark_mode_preference_help'|_ }}</p>
|
||||
{% for mode in availableDarkModes %}
|
||||
<div class="radio">
|
||||
<label>
|
||||
|
||||
@@ -487,6 +487,9 @@
|
||||
{% if '' != link.foreign_amount %}
|
||||
({{ link.foreign_amount|raw }})
|
||||
{% endif %}
|
||||
{% if link.notes != "" %}
|
||||
({{ link.notes }})
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -387,6 +387,9 @@ Route::group(
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
// exchange rates controller
|
||||
Route::group(
|
||||
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\ExchangeRates', 'prefix' => 'exchange-rates', 'as' => 'exchange-rates.'],
|
||||
|
||||
Reference in New Issue
Block a user