Compare commits

...

57 Commits

Author SHA1 Message Date
James Cole
a29742fe1f Never mind lol. 2025-08-17 11:31:03 +02:00
James Cole
dd06838eec Reshuffle dependencies. 2025-08-17 11:27:36 +02:00
James Cole
5eb828bff8 Force order. But I think steps need dependencies, not jobs. 2025-08-17 11:24:16 +02:00
James Cole
c7f3701053 Add command to see where we end up. 2025-08-17 11:22:51 +02:00
James Cole
ab6799442c Fix job again. 2025-08-17 11:19:20 +02:00
James Cole
1fe9bf7d76 Clean up job. 2025-08-17 11:18:48 +02:00
James Cole
bd14797da6 Fix dependencies. 2025-08-17 11:16:55 +02:00
James Cole
93b4e6a8d0 Update build job. 2025-08-17 11:15:18 +02:00
github-actions[bot]
52abe3bbc2 Merge pull request #10765 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-08-16 19:54:36 +02:00
github-actions[bot]
34db9f41c2 Merge pull request #10764 from firefly-iii/release-1755366862
🤖 Automatically merge the PR into the develop branch.
2025-08-16 19:54:31 +02:00
JC5
72c31fbe6a 🤖 Auto commit for release 'v6.3.0' on 2025-08-16 2025-08-16 19:54:22 +02:00
github-actions[bot]
1d8dd41564 Merge pull request #10763 from firefly-iii/release-1755365805
🤖 Automatically merge the PR into the develop branch.
2025-08-16 19:36:56 +02:00
JC5
3409240a19 🤖 Auto commit for release 'develop' on 2025-08-16 2025-08-16 19:36:45 +02:00
James Cole
eccc58e75a Add piggy bank tests. 2025-08-16 19:31:59 +02:00
James Cole
24098f35bb Fix some commands. 2025-08-16 15:04:15 +02:00
James Cole
e7d9dc57d8 Add some tests. 2025-08-16 14:52:29 +02:00
James Cole
f04ed5b8f0 Restore and fix API tests 2025-08-16 14:10:24 +02:00
James Cole
8bd44f429b Merge pull request #10762 from firefly-iii/JC5-patch-1
Update lock.yml
2025-08-16 10:26:21 +02:00
James Cole
fb7866b165 Update lock.yml
Signed-off-by: James Cole <james@firefly-iii.org>
2025-08-16 10:26:09 +02:00
James Cole
98db6db1eb Fix references to charts. 2025-08-16 08:28:10 +02:00
James Cole
0a235ec523 Optimize category chart. 2025-08-16 06:34:28 +02:00
github-actions[bot]
cb4f7ad0a8 Merge pull request #10761 from firefly-iii/release-1755317595
🤖 Automatically merge the PR into the develop branch.
2025-08-16 06:13:25 +02:00
JC5
91ce1d3b88 🤖 Auto commit for release 'develop' on 2025-08-16 2025-08-16 06:13:15 +02:00
James Cole
8f9ab150ee Add command to reset rate limit for error emails. 2025-08-16 06:08:01 +02:00
James Cole
8d52f83b4f Fix nulls. 2025-08-16 06:02:26 +02:00
James Cole
fb19739b54 Fix missing enrichment. 2025-08-16 05:58:58 +02:00
James Cole
acd4f28ae3 Restore "default" API endpoint so data importer does not fail. 2025-08-15 21:37:49 +02:00
James Cole
e4d443db50 Remove some unused comments. 2025-08-15 21:10:30 +02:00
James Cole
fe9d36cd27 Clean up a variety of end points. 2025-08-15 20:06:28 +02:00
James Cole
a916cc7e78 Rename command. 2025-08-15 20:00:29 +02:00
James Cole
77d15f884b Extend acceptedRoles to chart controllers. 2025-08-15 19:57:32 +02:00
James Cole
ab773c9052 Extend acceptedRoles array in AC controllers. 2025-08-15 19:50:12 +02:00
James Cole
87d292ca27 Rename method from default to primary. 2025-08-15 14:33:14 +02:00
James Cole
946e272d1c Update changelog and templates. 2025-08-15 13:46:41 +02:00
github-actions[bot]
ffe2e310aa Merge pull request #10760 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-08-15 13:43:40 +02:00
github-actions[bot]
9fc6af0f2d Merge pull request #10759 from firefly-iii/release-1755258209
🤖 Automatically merge the PR into the develop branch.
2025-08-15 13:43:35 +02:00
JC5
71807b67fc 🤖 Auto commit for release 'v6.3.0-beta.2' on 2025-08-15 2025-08-15 13:43:29 +02:00
github-actions[bot]
ab61d0ce21 Merge pull request #10758 from firefly-iii/release-1755257846
🤖 Automatically merge the PR into the develop branch.
2025-08-15 13:37:34 +02:00
JC5
f2dc0d234b 🤖 Auto commit for release 'develop' on 2025-08-15 2025-08-15 13:37:27 +02:00
James Cole
4885dbc78e Fix currency exchange rate UI. 2025-08-15 13:33:22 +02:00
James Cole
0e29e282df Fix translations. 2025-08-15 11:38:25 +02:00
James Cole
0b3fd335ad Fix other endpoint. 2025-08-15 11:35:16 +02:00
James Cole
9b2263c7bb Match exchange rate API with API docs. 2025-08-15 11:28:23 +02:00
James Cole
1305fafd38 Add multi-currency for budget chart 2025-08-15 07:50:13 +02:00
James Cole
844b8d48c4 Update category overview to be multi-currency aware. 2025-08-15 07:44:14 +02:00
James Cole
fc9ef290f1 Clean up API endpoints. 2025-08-15 07:11:34 +02:00
James Cole
020c8ad933 Clean up and expand templates. 2025-08-13 10:15:36 +02:00
James Cole
33ea27b411 Fix template 2025-08-13 08:03:59 +02:00
James Cole
b39e3e5965 Expand template. 2025-08-13 07:56:31 +02:00
James Cole
ba7e504f12 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-08-13 07:54:53 +02:00
James Cole
a732ce2a20 Merge branch 'main' into develop
# Conflicts:
#	.github/workflows/release.yml
2025-08-13 07:54:44 +02:00
James Cole
2b76c4801f Remove steps no longer necessary. 2025-08-13 07:54:15 +02:00
github-actions[bot]
dd09dd59e9 Merge pull request #10752 from firefly-iii/release-1755064372
🤖 Automatically merge the PR into the develop branch.
2025-08-13 07:53:05 +02:00
JC5
d56088d086 🤖 Auto commit for release 'develop' on 2025-08-13 2025-08-13 07:52:52 +02:00
James Cole
f27f00cfa0 Change command. 2025-08-13 07:48:29 +02:00
James Cole
d762136e0b Clean up action. 2025-08-13 07:46:02 +02:00
James Cole
536cd78e29 Templates for new releases. 2025-08-13 07:43:11 +02:00
115 changed files with 1866 additions and 1302 deletions

View File

@@ -345,16 +345,16 @@
},
{
"name": "fidry/cpu-core-counter",
"version": "1.2.0",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/theofidry/cpu-core-counter.git",
"reference": "8520451a140d3f46ac33042715115e290cf5785f"
"reference": "db9508f7b1474469d9d3c53b86f817e344732678"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f",
"reference": "8520451a140d3f46ac33042715115e290cf5785f",
"url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678",
"reference": "db9508f7b1474469d9d3c53b86f817e344732678",
"shasum": ""
},
"require": {
@@ -364,10 +364,10 @@
"fidry/makefile": "^0.2.0",
"fidry/php-cs-fixer-config": "^1.1.2",
"phpstan/extension-installer": "^1.2.0",
"phpstan/phpstan": "^1.9.2",
"phpstan/phpstan-deprecation-rules": "^1.0.0",
"phpstan/phpstan-phpunit": "^1.2.2",
"phpstan/phpstan-strict-rules": "^1.4.4",
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-deprecation-rules": "^2.0.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^8.5.31 || ^9.5.26",
"webmozarts/strict-phpunit": "^7.5"
},
@@ -394,7 +394,7 @@
],
"support": {
"issues": "https://github.com/theofidry/cpu-core-counter/issues",
"source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0"
"source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0"
},
"funding": [
{
@@ -402,20 +402,20 @@
"type": "github"
}
],
"time": "2024-08-06T10:04:20+00:00"
"time": "2025-08-14T07:29:31+00:00"
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.85.1",
"version": "v3.86.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "2fb6d7f6c3398dca5786a1635b27405d73a417ba"
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2fb6d7f6c3398dca5786a1635b27405d73a417ba",
"reference": "2fb6d7f6c3398dca5786a1635b27405d73a417ba",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4a952bd19dc97879b0620f495552ef09b55f7d36",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36",
"shasum": ""
},
"require": {
@@ -499,7 +499,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.85.1"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.86.0"
},
"funding": [
{
@@ -507,7 +507,7 @@
"type": "github"
}
],
"time": "2025-07-29T22:22:50+00:00"
"time": "2025-08-13T22:36:21+00:00"
},
{
"name": "psr/container",

View File

@@ -1,6 +1,6 @@
parameters:
scanFiles:
- ../_ide_helper
- ../_ide_helper.php
paths:
- ../app
- ../database

21
.github/release-notes/alpha.md vendored Normal file
View File

@@ -0,0 +1,21 @@
Welcome to release %version of Firefly III. This **alpha** release contains the latest fixes, translations and features. It is probably buggy and may not work as expected. You can download the release below, and adventurous Docker users can find this release under the `alpha` tag.
> [!WARNING]
> Please be careful with this alpha release, as it may not work as expected.
Alpha releases are created to test new features and fixes before they are included in a stable release. They are not recommended for production use. This release was created on %date and may contain unexpected bugs. Data loss is rare but possible.
## Changelog (not final)
%changelog
## Installation and upgrade instructions
* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/).
* Alternatively, read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)
The release files are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

21
.github/release-notes/beta.md vendored Normal file
View File

@@ -0,0 +1,21 @@
Welcome to release %version of Firefly III. This **beta** release contains the latest fixes, translations and features. It may be buggy, nor work as expected. You can download the release below, and adventurous Docker users can find this release under the `beta` tag.
> [!WARNING]
> Please be careful with this beta release, as it may not work as expected.
Alpha releases are created to test new features and fixes before they are included in a stable release. They are not recommended for production use. This release was created on %date and may contain unexpected bugs. Data loss is rare but possible.
## Changelog (not final)
%changelog
## Installation and upgrade instructions
* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/).
* Alternatively, read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)
The release files are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

21
.github/release-notes/branch.md vendored Normal file
View File

@@ -0,0 +1,21 @@
Welcome to release %version of Firefly III. This branch-related release contains the latest fixes, translations and features. It is probably buggy and may not work as expected. You can download the release below, and adventurous Docker users can find this release under the `branch-*` tag.
> [!WARNING]
> Please be careful with this branch release, as it may not work as expected.
Branch releases are created to test large new features that are developed alongside the normal release flow. They are not recommended for production use. This release was created on %date and may contain unexpected bugs. Data loss is rare but possible.
## Changelog
There is no changelog for this release, as it is not final. However, [changelog.md](https://github.com/firefly-iii/firefly-iii/blob/develop/changelog.md) may already contain entries for the future release that this branch will be a part of.
## Installation and upgrade instructions
* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/).
* Alternatively, read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)
The release files are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

21
.github/release-notes/develop.md vendored Normal file
View File

@@ -0,0 +1,21 @@
Welcome to the latest development release of Firefly III. This test release contains the absolute latest fixes, translations and features. It is probably buggy and may not work as expected. You can download the release below, and adventurous Docker users can find this release under the `develop` tag.
> [!WARNING]
> Please be careful with this pre-release, as it may not work as expected.
This release was created on %date and may contain unexpected bugs. Data loss is rare but possible.
## Changelog
The changelog for this release may not be up-to-date, so it is not included. However, [changelog.md](https://github.com/firefly-iii/firefly-iii/blob/develop/changelog.md) may already contain entries for the future release.
## Installation and upgrade instructions
* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/).
* Alternatively, read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)
The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

16
.github/release-notes/release.md vendored Normal file
View File

@@ -0,0 +1,16 @@
Welcome to release %version of Firefly III. It contains the latest fixes, translations and features. Docker users can find this release under the `latest` tag.
## Changelog
%changelog
## Installation and upgrade instructions
* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/).
* Alternatively, read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)
The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/).
## Support Firefly III
Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. Please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information. Thank you for your consideration.

View File

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

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Import GPG key
@@ -233,122 +233,15 @@ jobs:
git push
env:
version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
- name: Extract changelog
id: extract-changelog
- name: Generate release description
id: release-description
uses: JC5/firefly-iii-dev@main
with:
action: 'ff3:extract-changelog'
action: "ff3:generate-release-notes firefly-iii ${{ github.event.inputs.version }}"
output: 'output'
env:
FIREFLY_III_ROOT: /github/workspace
GH_TOKEN: ""
- name: Describe new release
run: |
# describe the development release.
if [[ "develop" == "$version" ]]; then
echo 'Describe the latest develop release'
rm -f output.txt
touch output.txt
sudo chown -R runner:docker output.txt
echo "Weekly development release of Firefly III with the latest fixes, translations and features. Docker users can find this release under the \`develop\` tag." >> output.txt
echo "" >> output.txt
echo "This release was created on **$(date +'%Y-%m-%d %H:%M')** and may contain unexpected bugs. Data loss is rare but is not impossible. The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
echo "" >> output.txt
echo "* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
echo "" >> output.txt
echo ":warning: Please be careful with this pre-release, as it may not work as expected." >> output.txt
# donations!
echo '' >> output.txt
echo '### Support Firefly III' >> output.txt
echo 'Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. For more information, please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information.' >> output.txt
echo '' >> output.txt
fi
# describe a branch release
if [[ "$version" == branch* ]]; then
echo 'Describe a branch release'
rm -f output.txt
touch output.txt
sudo chown -R runner:docker output.txt
echo "Irregular BRANCH release of Firefly III. This release contains specific features or changes. Docker users can find this release under the \`$version\` tag." >> output.txt
echo "" >> output.txt
echo "This release was created on **$(date +'%Y-%m-%d %H:%M')** and may contain unexpected bugs. Data loss is rare but is not impossible. The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
echo "" >> output.txt
echo "* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
echo "" >> output.txt
echo ":warning: Please be careful with this branch pre-release, as it may not work as expected." >> output.txt
fi
# describe the main release
if [[ "develop" != "$version" ]] && [[ "$version" != branch* ]] && [[ "$version" != *alpha* ]] && [[ "$version" != *beta* ]]; then
echo 'Describe the latest release'
sudo chown -R runner:docker output.txt
# the changelog is in output.txt
mv output.txt output2.txt
touch output.txt
echo '' >> output.txt
echo "Welcome to release $version of Firefly III. It contains the the latest fixes, translations and features. Docker users can find this release under the \`latest\` tag." >> output.txt
echo '' >> output.txt
# add changelog to file.
cat output2.txt >> output.txt
echo '' >> output.txt
rm -f output2.txt
echo '### Instructions' >> output.txt
echo '' >> output.txt
echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
echo "* The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
# donations!
echo '' >> output.txt
echo '### Support Firefly III' >> output.txt
echo 'Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. For more information, please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information.' >> output.txt
echo '' >> output.txt
fi
# describe alpha release
if [[ "$version" == *alpha* ]]; then
echo 'Describe an ALPHA release'
rm -f output.txt
touch output.txt
sudo chown -R runner:docker output.txt
echo "Very early ALPHA release of Firefly III. This release contains specific features or changes. Docker users can find this release under the \`$version\` tag." >> output.txt
echo '' >> output.txt
echo "This release was created on **$(date +'%Y-%m-%d %H:%M')** and may contain unexpected bugs. Data loss is rare but is not impossible. The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
echo '' >> output.txt
echo '### Instructions' >> output.txt
echo '' >> output.txt
echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
echo "* The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
fi
# describe beta release
if [[ "$version" == *beta* ]]; then
echo 'Describe a BETA release'
rm -f output.txt
touch output.txt
sudo chown -R runner:docker output.txt
echo "Very early BETA release of Firefly III. This release contains specific features or changes. Docker users can find this release under the \`$version\` tag." >> output.txt
echo '' >> output.txt
echo "This release was created on **$(date +'%Y-%m-%d %H:%M')** and may contain unexpected bugs. Data loss is rare but is not impossible. The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
echo '' >> output.txt
echo '### Instructions' >> output.txt
echo '' >> output.txt
echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
echo "* The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
fi
env:
version: ${{ github.event_name == 'schedule' && 'develop' || github.event.inputs.version }}
- name: Merge all into working branch
run: |
MERGE_INTO=develop

View File

@@ -27,6 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -34,7 +35,6 @@ use FireflyIII\Support\Debug\Timer;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
@@ -46,7 +46,8 @@ class AccountController extends Controller
use AccountFilter;
// this array only exists to test if the constructor will use it properly.
protected array $accepts = ['application/json', 'application/vnd.api+json'];
protected array $accepts = ['application/json', 'application/vnd.api+json'];
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
/** @var array<int, string> */
private array $balanceTypes;
@@ -60,10 +61,10 @@ class AccountController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->validateUserGroup($request);
$this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUser($user);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}

View File

@@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
@@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse;
class BillController extends Controller
{
private BillRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_SUBSCRIPTIONS];
/**
* BillController constructor.
@@ -46,10 +47,10 @@ class BillController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->validateUserGroup($request);
$this->repository = app(BillRepositoryInterface::class);
$this->repository->setUser($user);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}

View File

@@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
@@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse;
class BudgetController extends Controller
{
private BudgetRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_BUDGETS];
/**
* BudgetController constructor.
@@ -46,10 +47,10 @@ class BudgetController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->validateUserGroup($request);
$this->repository = app(BudgetRepositoryInterface::class);
$this->repository->setUser($user);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}

View File

@@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
@@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse;
class CategoryController extends Controller
{
private CategoryRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
/**
* CategoryController constructor.
@@ -46,10 +47,10 @@ class CategoryController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->validateUserGroup($request);
$this->repository = app(CategoryRepositoryInterface::class);
$this->repository->setUser($user);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}

View File

@@ -27,9 +27,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use Deprecated;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
@@ -38,6 +38,7 @@ use Illuminate\Http\JsonResponse;
class CurrencyController extends Controller
{
private CurrencyRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
/**
* CurrencyController constructor.
@@ -47,10 +48,10 @@ class CurrencyController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->validateUserGroup($request);
$this->repository = app(CurrencyRepositoryInterface::class);
$this->repository->setUser($user);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}

View File

@@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\ObjectGroup;
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
@@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse;
class ObjectGroupController extends Controller
{
private ObjectGroupRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
/**
* CurrencyController constructor.
@@ -46,10 +47,10 @@ class ObjectGroupController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->validateUserGroup($request);
$this->repository = app(ObjectGroupRepositoryInterface::class);
$this->repository->setUser($user);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}

View File

@@ -26,10 +26,10 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
@@ -39,6 +39,7 @@ class PiggyBankController extends Controller
{
private AccountRepositoryInterface $accountRepository;
private PiggyBankRepositoryInterface $piggyRepository;
protected array $acceptedRoles = [UserRoleEnum::READ_PIGGY_BANKS];
/**
* PiggyBankController constructor.
@@ -48,22 +49,19 @@ class PiggyBankController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->validateUserGroup($request);
$this->piggyRepository = app(PiggyBankRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->piggyRepository->setUser($user);
$this->accountRepository->setUser($user);
$this->piggyRepository->setUser($this->user);
$this->piggyRepository->setUserGroup($this->userGroup);
$this->accountRepository->setUser($this->user);
$this->accountRepository->setUserGroup($this->userGroup);
return $next($request);
}
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getPiggiesAC
*/
public function piggyBanks(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
@@ -90,10 +88,6 @@ class PiggyBankController extends Controller
return response()->api($response);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getPiggiesBalanceAC
*/
public function piggyBanksWithBalance(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Recurrence;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use Illuminate\Http\JsonResponse;
@@ -36,6 +37,7 @@ use Illuminate\Http\JsonResponse;
class RecurrenceController extends Controller
{
private RecurringRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_RECURRING];
/**
* RecurrenceController constructor.
@@ -45,19 +47,16 @@ class RecurrenceController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
$this->repository = app(RecurringRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}
);
}
/**
* This endpoint is documented at:
* * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRecurringAC
*/
public function recurring(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Rule;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use Illuminate\Http\JsonResponse;
@@ -36,6 +37,7 @@ use Illuminate\Http\JsonResponse;
class RuleController extends Controller
{
private RuleRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_RULES];
/**
* RuleController constructor.
@@ -45,18 +47,16 @@ class RuleController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
$this->repository = app(RuleRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}
);
}
/**
* This endpoint is documented at:
* * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRulesAC
*/
public function rules(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use Illuminate\Http\JsonResponse;
@@ -36,6 +37,7 @@ use Illuminate\Http\JsonResponse;
class RuleGroupController extends Controller
{
private RuleGroupRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_RULES];
/**
* RuleGroupController constructor.
@@ -45,18 +47,16 @@ class RuleGroupController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
$this->repository = app(RuleGroupRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}
);
}
/**
* This endpoint is documented at:
* * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getRuleGroupsAC
*/
public function ruleGroups(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();

View File

@@ -26,9 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
@@ -37,6 +37,7 @@ use Illuminate\Http\JsonResponse;
class TagController extends Controller
{
private TagRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
/**
* TagController constructor.
@@ -46,20 +47,16 @@ class TagController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->validateUserGroup($request);
$this->repository = app(TagRepositoryInterface::class);
$this->repository->setUser($user);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}
);
}
/**
* This endpoint is documented at:
* * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTagAC
*/
public function tags(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();

View File

@@ -31,7 +31,6 @@ use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
@@ -52,24 +51,19 @@ class TransactionController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$userGroup = $this->validateUserGroup($request);
$this->validateUserGroup($request);
$this->repository = app(JournalRepositoryInterface::class);
$this->groupRepository = app(TransactionGroupRepositoryInterface::class);
$this->repository->setUser($user);
$this->groupRepository->setUser($user);
$this->groupRepository->setUserGroup($userGroup);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
$this->groupRepository->setUser($this->user);
$this->groupRepository->setUserGroup($this->userGroup);
return $next($request);
}
);
}
/**
* This endpoint is documented at:
* * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionsAC
*/
public function transactions(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
@@ -92,10 +86,6 @@ class TransactionController extends Controller
return response()->api($array);
}
/**
* This endpoint is documented at:
* * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionsIDAC
*/
public function transactionsWithID(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface;
use Illuminate\Http\JsonResponse;
@@ -36,6 +37,7 @@ use Illuminate\Http\JsonResponse;
class TransactionTypeController extends Controller
{
private TransactionTypeRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
/**
* TransactionTypeController constructor.
@@ -45,17 +47,16 @@ class TransactionTypeController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
$this->repository = app(TransactionTypeRepositoryInterface::class);
$this->repository->setUser($this->user);
$this->repository->setUserGroup($this->userGroup);
return $next($request);
}
);
}
/**
* This endpoint is documented at
* * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getTransactionTypesAC
*/
public function transactionTypes(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();

View File

@@ -24,23 +24,17 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Chart\ChartRequest;
use FireflyIII\Api\V1\Requests\Data\DateRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Exceptions\ValidationException;
use FireflyIII\Models\Account;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Chart\ChartData;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\ApiSupport;
use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
@@ -50,9 +44,12 @@ use Illuminate\Support\Facades\Log;
class AccountController extends Controller
{
use ApiSupport;
use CleansChartData;
use CollectsAccountsFromFilter;
private ChartData $chartData;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
private array $chartData = [];
private AccountRepositoryInterface $repository;
/**
@@ -63,11 +60,10 @@ class AccountController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->chartData = new ChartData();
$this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUser($user);
$this->validateUserGroup($request);
$this->repository->setUserGroup($this->userGroup);
$this->repository->setUser($this->user);
return $next($request);
}
@@ -75,11 +71,9 @@ class AccountController extends Controller
}
/**
* TODO fix documentation
*
* @throws FireflyException
*/
public function dashboard(ChartRequest $request): JsonResponse
public function overview(ChartRequest $request): JsonResponse
{
$queryParameters = $request->getParameters();
$accounts = $this->getAccountList($queryParameters);
@@ -87,7 +81,7 @@ class AccountController extends Controller
// move date to end of day
$queryParameters['start']->startOfDay();
$queryParameters['end']->endOfDay();
Log::debug(sprintf('dashboard(), convert to primary: %s', var_export($this->convertToPrimary, true)));
// Log::debug(sprintf('dashboard(), convert to primary: %s', var_export($this->convertToPrimary, true)));
// loop each account, and collect info:
/** @var Account $account */
@@ -96,7 +90,7 @@ class AccountController extends Controller
$this->renderAccountData($queryParameters, $account);
}
return response()->json($this->chartData->render());
return response()->json($this->clean($this->chartData));
}
/**
@@ -105,29 +99,35 @@ class AccountController extends Controller
private function renderAccountData(array $params, Account $account): void
{
Log::debug(sprintf('Now in %s(array, #%d)', __METHOD__, $account->id));
$currency = $this->repository->getAccountCurrency($account);
$currentStart = clone $params['start'];
$range = Steam::finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToPrimary);
$currency = $this->repository->getAccountCurrency($account);
$currentStart = clone $params['start'];
$range = Steam::finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToPrimary);
$previous = array_values($range)[0]['balance'];
$pcPrevious = null;
$previous = array_values($range)[0]['balance'];
$pcPrevious = null;
if (!$currency instanceof TransactionCurrency) {
$currency = $this->default;
$currency = $this->primaryCurrency;
}
$currentSet = [
$currentSet = [
'label' => $account->name,
// the currency that belongs to the account.
'currency_id' => (string)$currency->id,
'currency_name' => $currency->name,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
// the primary currency
'primary_currency_id' => (string)$this->primaryCurrency->id,
// the default currency of the user (could be the same!)
'date' => $params['start']->toAtomString(),
'start' => $params['start']->toAtomString(),
'end' => $params['end']->toAtomString(),
'start_date' => $params['start']->toAtomString(),
'end_date' => $params['end']->toAtomString(),
'type' => 'line',
'yAxisID' => 0,
'period' => '1D',
'entries' => [],
];
@@ -159,106 +159,6 @@ class AccountController extends Controller
$currentStart->addDay();
}
$this->chartData->add($currentSet);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/charts/getChartAccountOverview
*
* @throws ValidationException
*/
public function overview(DateRequest $request): JsonResponse
{
// parameters for chart:
$dates = $request->getAll();
/** @var Carbon $start */
$start = $dates['start'];
/** @var Carbon $end */
$end = $dates['end'];
// set dates to end of day + start of day:
$start->startOfDay();
$end->endOfDay();
$frontPageIds = $this->getFrontPageAccountIds();
$accounts = $this->repository->getAccountsById($frontPageIds);
$chartData = [];
/** @var Account $account */
foreach ($accounts as $account) {
Log::debug(sprintf('Rendering chart data for account %s (%d)', $account->name, $account->id));
$currency = $this->repository->getAccountCurrency($account) ?? $this->primaryCurrency;
$currentStart = clone $start;
$range = Steam::finalAccountBalanceInRange($account, $start, clone $end, $this->convertToPrimary);
$previous = array_values($range)[0]['balance'];
$pcPrevious = null;
$currentSet = [
'label' => $account->name,
'currency_id' => (string)$currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'start_date' => $start->toAtomString(),
'end_date' => $end->toAtomString(),
'type' => 'line', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [],
];
// add "pc_entries" if convertToPrimary is true:
if ($this->convertToPrimary) {
$currentSet['pc_entries'] = [];
$currentSet['primary_currency_id'] = (string)$this->primaryCurrency->id;
$currentSet['primary_currency_code'] = $this->primaryCurrency->code;
$currentSet['primary_currency_symbol'] = $this->primaryCurrency->symbol;
$currentSet['primary_currency_decimal_places'] = $this->primaryCurrency->decimal_places;
$pcPrevious = array_values($range)[0]['pc_balance'];
}
// also get the primary balance if convertToPrimary is true:
while ($currentStart <= $end) {
$format = $currentStart->format('Y-m-d');
$label = $currentStart->toAtomString();
// balance is based on "balance" from the $range variable.
$balance = array_key_exists($format, $range) ? $range[$format]['balance'] : $previous;
$previous = $balance;
$currentSet['entries'][$label] = $balance;
// do the same for the primary balance, if relevant:
$pcBalance = null;
if ($this->convertToPrimary) {
$pcBalance = array_key_exists($format, $range) ? $range[$format]['pc_balance'] : $pcPrevious;
$pcPrevious = $pcBalance;
$currentSet['pc_entries'][$label] = $pcBalance;
}
$currentStart->addDay();
}
$chartData[] = $currentSet;
}
return response()->json($chartData);
}
private function getFrontPageAccountIds(): array
{
$defaultSet = $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value])->pluck('id')->toArray();
/** @var Preference $frontpage */
$frontpage = Preferences::get('frontpageAccounts', $defaultSet);
if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) {
$frontpage->data = $defaultSet;
$frontpage->save();
}
return $frontpage->data ?? $defaultSet;
$this->chartData[] = $currentSet;
}
}

View File

@@ -7,11 +7,11 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Chart\ChartRequest;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Chart\ChartData;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\AccountBalanceGrouped;
use FireflyIII\Support\Http\Api\CleansChartData;
@@ -25,8 +25,9 @@ class BalanceController extends Controller
{
use CleansChartData;
use CollectsAccountsFromFilter;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
private ChartData $chartData;
private array $chartData = [];
private GroupCollectorInterface $collector;
private AccountRepositoryInterface $repository;
@@ -37,13 +38,13 @@ class BalanceController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
$this->repository = app(AccountRepositoryInterface::class);
$this->collector = app(GroupCollectorInterface::class);
$userGroup = $this->validateUserGroup($request);
$this->repository->setUserGroup($userGroup);
$this->collector->setUserGroup($userGroup);
$this->chartData = new ChartData();
// $this->default = app('amount')->getPrimaryCurrency();
$this->repository->setUserGroup($this->userGroup);
$this->collector->setUserGroup($this->userGroup);
$this->repository->setUser($this->user);
$this->collector->setUser($this->user);
return $next($request);
}
@@ -66,10 +67,6 @@ class BalanceController extends Controller
$queryParameters = $request->getParameters();
$accounts = $this->getAccountList($queryParameters);
// prepare for currency conversion and data collection:
/** @var TransactionCurrency $primary */
$primary = Amount::getPrimaryCurrency();
// get journals for entire period:
$this->collector->setRange($queryParameters['start'], $queryParameters['end'])
@@ -81,7 +78,7 @@ class BalanceController extends Controller
$object = new AccountBalanceGrouped();
$object->setPreferredRange($queryParameters['period']);
$object->setPrimary($primary);
$object->setPrimary($this->primaryCurrency);
$object->setAccounts($accounts);
$object->setJournals($journals);
$object->setStart($queryParameters['start']);
@@ -89,9 +86,10 @@ class BalanceController extends Controller
$object->groupByCurrencyAndPeriod();
$data = $object->convertToChartData();
foreach ($data as $entry) {
$this->chartData->add($entry);
$this->chartData[] = $entry;
}
$this->chartData = $this->clean($this->chartData);
return response()->json($this->chartData->render());
return response()->json($this->chartData);
}
}

View File

@@ -26,7 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\DateRequest;
use FireflyIII\Api\V1\Requests\Data\SameDateRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Budget;
@@ -63,13 +63,16 @@ class BudgetController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
$this->repository = app(BudgetRepositoryInterface::class);
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$userGroup = $this->validateUserGroup($request);
$this->repository->setUserGroup($userGroup);
$this->opsRepository->setUserGroup($userGroup);
$this->blRepository->setUserGroup($userGroup);
$this->repository->setUserGroup($this->userGroup);
$this->opsRepository->setUserGroup($this->userGroup);
$this->blRepository->setUserGroup($this->userGroup);
$this->repository->setUser($this->user);
$this->opsRepository->setUser($this->user);
$this->blRepository->setUser($this->user);
return $next($request);
}
@@ -81,7 +84,7 @@ class BudgetController extends Controller
*
* @throws FireflyException
*/
public function dashboard(DateRequest $request): JsonResponse
public function overview(SameDateRequest $request): JsonResponse
{
$params = $request->getAll();
@@ -110,10 +113,12 @@ class BudgetController extends Controller
private function processBudget(Budget $budget, Carbon $start, Carbon $end): array
{
// get all limits:
$limits = $this->blRepository->getBudgetLimits($budget, $start, $end);
$rows = [];
$spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget]));
$expenses = $this->processExpenses($budget->id, $spent, $start, $end);
$limits = $this->blRepository->getBudgetLimits($budget, $start, $end);
$rows = [];
$spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget]));
$expenses = $this->processExpenses($budget->id, $spent, $start, $end);
$converter = new ExchangeRateConverter();
$currencies = [$this->primaryCurrency->id => $this->primaryCurrency];
/**
* @var int $currencyId
@@ -121,7 +126,14 @@ class BudgetController extends Controller
*/
foreach ($expenses as $currencyId => $row) {
// budgeted, left and overspent are now 0.
$limit = $this->filterLimit($currencyId, $limits);
$limit = $this->filterLimit($currencyId, $limits);
// primary currency entries
$row['pc_budgeted'] = '0';
$row['pc_spent'] = '0';
$row['pc_left'] = '0';
$row['pc_overspent'] = '0';
if (null !== $limit) {
$row['budgeted'] = $limit->amount;
$row['left'] = bcsub($row['budgeted'], bcmul($row['spent'], '-1'));
@@ -129,34 +141,59 @@ class BudgetController extends Controller
$row['left'] = 1 === bccomp($row['left'], '0') ? $row['left'] : '0';
$row['overspent'] = 1 === bccomp($row['overspent'], '0') ? $row['overspent'] : '0';
}
$rows[] = $row;
// convert data if necessary.
if (true === $this->convertToPrimary && $currencyId !== $this->primaryCurrency->id) {
$currencies[$currencyId] ??= TransactionCurrency::find($currencyId);
$row['pc_budgeted'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['budgeted']);
$row['pc_spent'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['spent']);
$row['pc_left'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['left']);
$row['pc_overspent'] = $converter->convert($currencies[$currencyId], $this->primaryCurrency, $start, $row['overspent']);
}
if (true === $this->convertToPrimary && $currencyId === $this->primaryCurrency->id) {
$row['pc_budgeted'] = $row['budgeted'];
$row['pc_spent'] = $row['spent'];
$row['pc_left'] = $row['left'];
$row['pc_overspent'] = $row['overspent'];
}
$rows[] = $row;
}
// if no limits
// if (0 === $limits->count()) {
// return as a single item in an array
// $rows = $this->noBudgetLimits($budget, $start, $end);
// }
// is always an array
$return = [];
$return = [];
foreach ($rows as $row) {
$current = [
'label' => $budget->name,
'currency_id' => (string)$row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_name' => $row['currency_name'],
'currency_decimal_places' => $row['currency_decimal_places'],
'period' => null,
'start' => $row['start'],
'end' => $row['end'],
'entries' => [
'label' => $budget->name,
'currency_id' => (string)$row['currency_id'],
'currency_name' => $row['currency_name'],
'currency_code' => $row['currency_code'],
'currency_decimal_places' => $row['currency_decimal_places'],
'primary_currency_id' => (string)$this->primaryCurrency->id,
'primary_currency_name' => $this->primaryCurrency->name,
'primary_currency_code' => $this->primaryCurrency->code,
'primary_currency_symbol' => $this->primaryCurrency->symbol,
'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places,
'period' => null,
'date' => $row['start'],
'start_date' => $row['start'],
'end_date' => $row['end'],
'yAxisID' => 0,
'type' => 'bar',
'entries' => [
'budgeted' => $row['budgeted'],
'spent' => $row['spent'],
'left' => $row['left'],
'overspent' => $row['overspent'],
],
'pc_entries' => [
'budgeted' => $row['pc_budgeted'],
'spent' => $row['pc_spent'],
'left' => $row['pc_left'],
'overspent' => $row['pc_overspent'],
],
];
$return[] = $current;
}

View File

@@ -26,7 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\DateRequest;
use FireflyIII\Api\V1\Requests\Data\SameDateRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Enums\UserRoleEnum;
@@ -34,6 +34,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
@@ -58,11 +59,13 @@ class CategoryController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
$this->accountRepos = app(AccountRepositoryInterface::class);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$userGroup = $this->validateUserGroup($request);
$this->accountRepos->setUserGroup($userGroup);
$this->currencyRepos->setUserGroup($userGroup);
$this->accountRepos->setUserGroup($this->userGroup);
$this->currencyRepos->setUserGroup($this->userGroup);
$this->accountRepos->setUser($this->user);
$this->currencyRepos->setUser($this->user);
return $next($request);
}
@@ -77,7 +80,7 @@ class CategoryController extends Controller
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
*/
public function dashboard(DateRequest $request): JsonResponse
public function overview(SameDateRequest $request): JsonResponse
{
/** @var Carbon $start */
$start = $this->parameters->get('start');
@@ -100,52 +103,70 @@ class CategoryController extends Controller
/** @var array $journal */
foreach ($journals as $journal) {
// find journal:
$journalCurrencyId = (int)$journal['currency_id'];
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
$currencies[$journalCurrencyId] = $currency;
$currencyId = (int)$currency->id;
$currencyName = (string)$currency->name;
$currencyCode = (string)$currency->code;
$currencySymbol = (string)$currency->symbol;
$currencyDecimalPlaces = (int)$currency->decimal_places;
$amount = app('steam')->positive($journal['amount']);
$journalCurrencyId = (int)$journal['currency_id'];
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
$currencies[$journalCurrencyId] = $currency;
$currencyId = (int)$currency->id;
$currencyName = (string)$currency->name;
$currencyCode = (string)$currency->code;
$currencySymbol = (string)$currency->symbol;
$currencyDecimalPlaces = (int)$currency->decimal_places;
$amount = Steam::positive($journal['amount']);
$pcAmount = null;
// overrule if necessary:
if ($this->convertToPrimary && $journalCurrencyId === $this->primaryCurrency->id) {
$pcAmount = $amount;
}
if ($this->convertToPrimary && $journalCurrencyId !== $this->primaryCurrency->id) {
$currencyId = (int)$this->primaryCurrency->id;
$currencyName = (string)$this->primaryCurrency->name;
$currencyCode = (string)$this->primaryCurrency->code;
$currencySymbol = (string)$this->primaryCurrency->symbol;
$currencyDecimalPlaces = (int)$this->primaryCurrency->decimal_places;
$convertedAmount = $converter->convert($currency, $this->primaryCurrency, $journal['date'], $amount);
Log::debug(sprintf('Converted %s %s to %s %s', $journal['currency_code'], $amount, $this->primaryCurrency->code, $convertedAmount));
$amount = $convertedAmount;
$pcAmount = $converter->convert($currency, $this->primaryCurrency, $journal['date'], $amount);
Log::debug(sprintf('Converted %s %s to %s %s', $journal['currency_code'], $amount, $this->primaryCurrency->code, $pcAmount));
}
$categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category');
$key = sprintf('%s-%s', $categoryName, $currencyCode);
$categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category');
$key = sprintf('%s-%s', $categoryName, $currencyCode);
// create arrays
$return[$key] ??= [
'label' => $categoryName,
'currency_id' => (string)$currencyId,
'currency_code' => $currencyCode,
'currency_name' => $currencyName,
'currency_symbol' => $currencySymbol,
'currency_decimal_places' => $currencyDecimalPlaces,
'period' => null,
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'amount' => '0',
'label' => $categoryName,
'currency_id' => (string)$currencyId,
'currency_name' => $currencyName,
'currency_code' => $currencyCode,
'currency_symbol' => $currencySymbol,
'currency_decimal_places' => $currencyDecimalPlaces,
'primary_currency_id' => (string)$this->primaryCurrency->id,
'primary_currency_name' => (string)$this->primaryCurrency->name,
'primary_currency_code' => (string)$this->primaryCurrency->code,
'primary_currency_symbol' => (string)$this->primaryCurrency->symbol,
'primary_currency_decimal_places' => (int)$this->primaryCurrency->decimal_places,
'period' => null,
'start_date' => $start->toAtomString(),
'end_date' => $end->toAtomString(),
'yAxisID' => 0,
'type' => 'bar',
'entries' => [
'spent' => '0',
],
'pc_entries' => [
'spent' => '0',
],
];
// add monies
$return[$key]['amount'] = bcadd($return[$key]['amount'], (string)$amount);
$return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], (string)$amount);
if (null !== $pcAmount) {
$return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], (string)$pcAmount);
}
}
$return = array_values($return);
// order by amount
usort($return, static fn (array $a, array $b) => (float)$a['amount'] < (float)$b['amount'] ? 1 : -1);
usort($return, static fn (array $a, array $b) => (float)$a['entries']['spent'] < (float)$b['entries']['spent'] ? 1 : -1);
return response()->json($this->clean($return));
}

View File

@@ -64,7 +64,7 @@ abstract class Controller extends BaseController
protected const string CONTENT_TYPE = 'application/vnd.api+json';
protected const string JSON_CONTENT_TYPE = 'application/json';
protected array $accepts = ['application/json', 'application/vnd.api+json'];
protected array $accepts = ['application/json', 'application/vnd.api+json'];
/** @var array<int, string> */
protected array $allowedSort;
@@ -107,7 +107,7 @@ abstract class Controller extends BaseController
private function getParameters(): ParameterBag
{
$bag = new ParameterBag();
$page = (int) request()->get('page');
$page = (int)request()->get('page');
if ($page < 1) {
$page = 1;
}
@@ -131,13 +131,13 @@ abstract class Controller extends BaseController
$obj = null;
if (null !== $date) {
try {
$obj = Carbon::parse((string) $date);
$obj = Carbon::parse((string)$date);
} catch (InvalidFormatException $e) {
// don't care
Log::warning(
sprintf(
'Ignored invalid date "%s" in API controller parameter check: %s',
substr((string) $date, 0, 20),
substr((string)$date, 0, 20),
$e->getMessage()
)
);
@@ -158,7 +158,7 @@ abstract class Controller extends BaseController
$value = null;
}
if (null !== $value) {
$value = (int) $value;
$value = (int)$value;
if ($value < 1) {
$value = 1;
}
@@ -176,7 +176,7 @@ abstract class Controller extends BaseController
$user = auth()->user();
/** @var Preference $pageSize */
$pageSize = (int) app('preferences')->getForUser($user, 'listPageSize', 50)->data;
$pageSize = (int)app('preferences')->getForUser($user, 'listPageSize', 50)->data;
$bag->set($integer, $pageSize);
}
}
@@ -190,7 +190,7 @@ abstract class Controller extends BaseController
$sortParameters = [];
try {
$param = (string) request()->query->get('sort');
$param = (string)request()->query->get('sort');
} catch (BadRequestException $e) {
Log::error('Request field "sort" contains a non-scalar value. Value set to NULL.');
Log::error($e->getMessage());

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Data\Bulk;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\Bulk\TransactionRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
use Illuminate\Http\JsonResponse;
@@ -44,23 +45,23 @@ class TransactionController extends Controller
{
private AccountRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::MANAGE_TRANSACTIONS];
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
$this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUserGroup($this->userGroup);
$this->repository->setUser($this->user);
return $next($request);
}
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/bulkUpdateTransactions
*/
public function update(TransactionRequest $request): JsonResponse
{
$query = $request->getAll();

View File

@@ -28,6 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\DestroyRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
@@ -55,12 +56,20 @@ class DestroyController extends Controller
{
private bool $unused;
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/destroyData
*
* @throws FireflyException
*/
protected array $acceptedRoles = [UserRoleEnum::FULL];
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
return $next($request);
}
);
}
public function destroy(DestroyRequest $request): JsonResponse
{
$objects = $request->getObjects();

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Data\Export;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\Export\ExportRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Export\ExportDataGenerator;
use Illuminate\Http\Response as LaravelResponse;
@@ -39,6 +40,7 @@ use function Safe\date;
class ExportController extends Controller
{
private ExportDataGenerator $exporter;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
/**
* ExportController constructor.
@@ -48,8 +50,10 @@ class ExportController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
$this->exporter = app(ExportDataGenerator::class);
$this->exporter->setUser(auth()->user());
$this->exporter->setUserGroup($this->userGroup);
$this->exporter->setUser($this->user);
return $next($request);
}
@@ -57,9 +61,6 @@ class ExportController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportAccounts
*
* @throws FireflyException
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
@@ -99,9 +100,6 @@ class ExportController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBills
*
* @throws FireflyException
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
@@ -114,9 +112,6 @@ class ExportController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBudgets
*
* @throws FireflyException
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
@@ -129,9 +124,6 @@ class ExportController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportCategories
*
* @throws FireflyException
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
@@ -144,9 +136,6 @@ class ExportController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportPiggies
*
* @throws FireflyException
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
@@ -159,9 +148,6 @@ class ExportController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportRecurring
*
* @throws FireflyException
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
@@ -174,9 +160,6 @@ class ExportController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportRules
*
* @throws FireflyException
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
@@ -189,9 +172,6 @@ class ExportController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportTags
*
* @throws FireflyException
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
@@ -204,9 +184,6 @@ class ExportController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportTransactions
*
* @throws FireflyException
*/
public function transactions(ExportRequest $request): LaravelResponse

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Data;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
@@ -44,10 +45,22 @@ use Illuminate\Http\JsonResponse;
*/
class PurgeController extends Controller
{
protected array $acceptedRoles = [UserRoleEnum::FULL];
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->validateUserGroup($request);
return $next($request);
}
);
}
/**
* TODO cleanup and use repositories.
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/purgeData
*/
public function purge(): JsonResponse
{
@@ -66,14 +79,6 @@ class PurgeController extends Controller
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUser($user);
$repository->purgeAll();
// $set = PiggyBank::leftJoin('accounts', 'accounts.id', 'piggy_banks.account_id')
// ->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*'])
// ;
//
// /** @var PiggyBank $piggy */
// foreach ($set as $piggy) {
// $piggy->forceDelete();
// }
// rule group
RuleGroup::whereUserId($user->id)->onlyTrashed()->forceDelete();

View File

@@ -64,10 +64,6 @@ class AccountController extends Controller
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseAsset
*/
public function asset(GenericRequest $request): JsonResponse
{
$start = $request->getStart();
@@ -91,10 +87,6 @@ class AccountController extends Controller
return response()->json($result);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseExpense
*/
public function expense(GenericRequest $request): JsonResponse
{
$start = $request->getStart();

View File

@@ -58,9 +58,6 @@ class BillController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseBill
*
* Expenses per bill, possibly filtered by bill and account.
*/
public function bill(GenericRequest $request): JsonResponse
@@ -122,9 +119,6 @@ class BillController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseNoBill
*
* Expenses for no bill filtered by account.
*/
public function noBill(GenericRequest $request): JsonResponse

View File

@@ -63,10 +63,6 @@ class BudgetController extends Controller
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseBudget
*/
public function budget(GenericRequest $request): JsonResponse
{
$start = $request->getStart();
@@ -98,10 +94,6 @@ class BudgetController extends Controller
return response()->json($result);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseNoBudget
*/
public function noBudget(GenericRequest $request): JsonResponse
{
$start = $request->getStart();

View File

@@ -63,10 +63,6 @@ class CategoryController extends Controller
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferCategory
*/
public function category(GenericRequest $request): JsonResponse
{
$start = $request->getStart();
@@ -98,10 +94,6 @@ class CategoryController extends Controller
return response()->json($result);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferNoCategory
*/
public function noCategory(GenericRequest $request): JsonResponse
{
$start = $request->getStart();

View File

@@ -37,10 +37,6 @@ use Illuminate\Support\Facades\Log;
*/
class PeriodController extends Controller
{
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseTotal
*/
public function total(GenericRequest $request): JsonResponse
{
$accounts = $request->getAssetAccounts();

View File

@@ -57,9 +57,6 @@ class TagController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseNoTag
*
* Expenses for no tag filtered by account.
*/
public function noTag(GenericRequest $request): JsonResponse
@@ -115,9 +112,6 @@ class TagController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightExpenseTag
*
* Expenses per tag, possibly filtered by tag and account.
*/
public function tag(GenericRequest $request): JsonResponse

View File

@@ -64,10 +64,6 @@ class AccountController extends Controller
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeAsset
*/
public function asset(GenericRequest $request): JsonResponse
{
$start = $request->getStart();
@@ -92,10 +88,6 @@ class AccountController extends Controller
return response()->json($result);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeRevenue
*/
public function revenue(GenericRequest $request): JsonResponse
{
$start = $request->getStart();

View File

@@ -63,10 +63,6 @@ class CategoryController extends Controller
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeCategory
*/
public function category(GenericRequest $request): JsonResponse
{
$start = $request->getStart();
@@ -98,10 +94,6 @@ class CategoryController extends Controller
return response()->json($result);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeNoCategory
*/
public function noCategory(GenericRequest $request): JsonResponse
{
$start = $request->getStart();

View File

@@ -36,10 +36,6 @@ use Illuminate\Http\JsonResponse;
*/
class PeriodController extends Controller
{
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeTotal
*/
public function total(GenericRequest $request): JsonResponse
{
$accounts = $request->getAssetAccounts();

View File

@@ -57,9 +57,6 @@ class TagController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeTag
*
* Expenses for no tag filtered by account.
*/
public function noTag(GenericRequest $request): JsonResponse
@@ -109,9 +106,6 @@ class TagController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightIncomeNoTag
*
* Expenses per tag, possibly filtered by tag and account.
*/
public function tag(GenericRequest $request): JsonResponse

View File

@@ -56,10 +56,6 @@ class AccountController extends Controller
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransfers
*/
public function asset(GenericRequest $request): JsonResponse
{
$start = $request->getStart();

View File

@@ -63,10 +63,6 @@ class CategoryController extends Controller
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferCategory
*/
public function category(GenericRequest $request): JsonResponse
{
$start = $request->getStart();
@@ -98,10 +94,6 @@ class CategoryController extends Controller
return response()->json($result);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferNoCategory
*/
public function noCategory(GenericRequest $request): JsonResponse
{
$start = $request->getStart();

View File

@@ -36,10 +36,6 @@ use Illuminate\Http\JsonResponse;
*/
class PeriodController extends Controller
{
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferTotal
*/
public function total(GenericRequest $request): JsonResponse
{
$accounts = $request->getAssetAccounts();

View File

@@ -56,10 +56,6 @@ class TagController extends Controller
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferNoTag
*/
public function noTag(GenericRequest $request): JsonResponse
{
$accounts = $request->getAssetAccounts();
@@ -108,9 +104,6 @@ class TagController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/insight/insightTransferTag
*
* Transfers per tag, possibly filtered by tag and account.
*/
public function tag(GenericRequest $request): JsonResponse

View File

@@ -55,9 +55,6 @@ class DestroyController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/deleteAccount
*
* Remove the specified resource from storage.
*/
public function destroy(Account $account): JsonResponse

View File

@@ -71,9 +71,6 @@ class ListController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/listAttachmentByAccount
*
* @throws FireflyException
*/
public function attachments(Account $account): JsonResponse
@@ -100,9 +97,6 @@ class ListController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/listPiggyBankByAccount
*
* @throws FireflyException
*/
public function piggyBanks(Account $account): JsonResponse
@@ -140,9 +134,6 @@ class ListController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/listTransactionByAccount
*
* Show all transaction groups related to the account.
*
* @throws FireflyException

View File

@@ -67,9 +67,6 @@ class ShowController extends Controller
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/accounts/listAccount
*
* Display a listing of the resource.
*
* @throws FireflyException

View File

@@ -31,6 +31,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\User;
@@ -76,6 +77,16 @@ class ShowController extends Controller
*/
public function index(Budget $budget): JsonResponse
{
/** @var User $admin */
$admin = auth()->user();
// enrich budget:
$enrichment = new BudgetEnrichment();
$enrichment->setUser($admin);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$budget = $enrichment->enrichSingle($budget);
$manager = $this->getManager();
$manager->parseIncludes('budget');
$pageSize = $this->parameters->get('limit');
@@ -87,8 +98,6 @@ class ShowController extends Controller
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimits = $enrichment->enrich($budgetLimits);

View File

@@ -28,13 +28,11 @@ use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\DestroyRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\ValidationException;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class DestroyController extends Controller
{
@@ -59,23 +57,25 @@ class DestroyController extends Controller
public function destroy(DestroyRequest $request, TransactionCurrency $from, TransactionCurrency $to): JsonResponse
{
$date = $request->getDate();
if (!$date instanceof Carbon) {
throw new ValidationException('Date is required');
}
$rate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (!$rate instanceof CurrencyExchangeRate) {
throw new NotFoundHttpException();
}
$this->repository->deleteRate($rate);
$this->repository->deleteRates($from, $to);
return response()->json([], 204);
}
public function destroySingle(CurrencyExchangeRate $exchangeRate): JsonResponse
public function destroySingleById(CurrencyExchangeRate $exchangeRate): JsonResponse
{
$this->repository->deleteRate($exchangeRate);
return response()->json([], 204);
}
public function destroySingleByDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse
{
$exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null !== $exchangeRate) {
$this->repository->deleteRate($exchangeRate);
}
return response()->json([], 204);
}
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Models\CurrencyExchangeRate;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\CurrencyExchangeRate;
@@ -33,6 +34,7 @@ use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Transformers\ExchangeRateTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class ShowController
@@ -76,7 +78,7 @@ class ShowController extends Controller
;
}
public function showSingle(CurrencyExchangeRate $exchangeRate): JsonResponse
public function showSingleById(CurrencyExchangeRate $exchangeRate): JsonResponse
{
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters);
@@ -86,4 +88,20 @@ class ShowController extends Controller
->header('Content-Type', self::CONTENT_TYPE)
;
}
public function showSingleByDate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse
{
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters);
$exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null === $exchangeRate) {
throw new NotFoundHttpException();
}
return response()
->api($this->jsonApiObject(self::RESOURCE_KEY, $exchangeRate, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
}

View File

@@ -24,21 +24,27 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Models\CurrencyExchangeRate;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\StoreByCurrenciesRequest;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\StoreByDateRequest;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\StoreRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\StoreRequest;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Transformers\ExchangeRateTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
class StoreController extends Controller
{
use ValidatesUserGroupTrait;
public const string RESOURCE_KEY = 'exchange-rates';
protected array $acceptedRoles = [UserRoleEnum::OWNER];
public const string RESOURCE_KEY = 'exchange-rates';
protected array $acceptedRoles = [UserRoleEnum::OWNER];
private ExchangeRateRepositoryInterface $repository;
public function __construct()
@@ -54,6 +60,71 @@ class StoreController extends Controller
);
}
public function storeByCurrencies(StoreByCurrenciesRequest $request, TransactionCurrency $from, TransactionCurrency $to): JsonResponse
{
$data = $request->getAll();
$collection = new Collection();
foreach ($data as $date => $rate) {
$date = Carbon::createFromFormat('Y-m-d', $date);
$existing = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null !== $existing) {
// update existing rate.
$existing = $this->repository->updateExchangeRate($existing, $rate);
$collection->push($existing);
continue;
}
$new = $this->repository->storeExchangeRate($from, $to, $rate, $date);
$collection->push($new);
}
$count = $collection->count();
$paginator = new LengthAwarePaginator($collection, $count, $count, 1);
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters); // give params to transformer
return response()
->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
public function storeByDate(StoreByDateRequest $request, Carbon $date): JsonResponse
{
$data = $request->getAll();
$from = $request->getFromCurrency();
$collection = new Collection();
foreach ($data['rates'] as $key => $rate) {
$to = TransactionCurrency::where('code', $key)->first();
if (null === $to) {
continue; // should not happen.
}
$existing = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null !== $existing) {
// update existing rate.
$existing = $this->repository->updateExchangeRate($existing, $rate);
$collection->push($existing);
continue;
}
$new = $this->repository->storeExchangeRate($from, $to, $rate, $date);
$collection->push($new);
}
$count = $collection->count();
$paginator = new LengthAwarePaginator($collection, $count, $count, 1);
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters); // give params to transformer
return response()
->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
public function store(StoreRequest $request): JsonResponse
{
$date = $request->getDate();

View File

@@ -24,21 +24,24 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Models\CurrencyExchangeRate;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\UpdateRequest;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate\UpdateRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Transformers\ExchangeRateTransformer;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class UpdateController extends Controller
{
use ValidatesUserGroupTrait;
public const string RESOURCE_KEY = 'exchange-rates';
protected array $acceptedRoles = [UserRoleEnum::OWNER];
public const string RESOURCE_KEY = 'exchange-rates';
protected array $acceptedRoles = [UserRoleEnum::OWNER];
private ExchangeRateRepositoryInterface $repository;
public function __construct()
@@ -54,7 +57,7 @@ class UpdateController extends Controller
);
}
public function update(UpdateRequest $request, CurrencyExchangeRate $exchangeRate): JsonResponse
public function updateById(UpdateRequest $request, CurrencyExchangeRate $exchangeRate): JsonResponse
{
$date = $request->getDate();
$rate = $request->getRate();
@@ -67,4 +70,23 @@ class UpdateController extends Controller
->header('Content-Type', self::CONTENT_TYPE)
;
}
public function updateByDate(UpdateRequest $request, TransactionCurrency $from, TransactionCurrency $to, Carbon $date): JsonResponse
{
$exchangeRate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null === $exchangeRate) {
throw new NotFoundHttpException();
}
$date = $request->getDate();
$rate = $request->getRate();
$exchangeRate = $this->repository->updateExchangeRate($exchangeRate, $rate, $date);
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters);
return response()
->api($this->jsonApiObject(self::RESOURCE_KEY, $exchangeRate, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
}

View File

@@ -126,7 +126,7 @@ class ShowController extends Controller
*
* @throws FireflyException
*/
public function showDefault(): JsonResponse
public function showPrimary(): JsonResponse
{
/** @var User $user */
$user = auth()->user();

View File

@@ -73,7 +73,7 @@ class StoreController extends Controller
{
$currency = $this->repository->store($request->getAll());
if (true === $request->boolean('default')) {
$this->repository->makeDefault($currency);
$this->repository->makePrimary($currency);
app('preferences')->mark();
}
$manager = $this->getManager();

View File

@@ -101,12 +101,12 @@ class UpdateController extends Controller
/**
* @throws FireflyException
*/
public function makeDefault(TransactionCurrency $currency): JsonResponse
public function makePrimary(TransactionCurrency $currency): JsonResponse
{
/** @var User $user */
$user = auth()->user();
$this->repository->enable($currency);
$this->repository->makeDefault($currency);
$this->repository->makePrimary($currency);
app('preferences')->mark();

View File

@@ -45,7 +45,7 @@ class DestroyRequest extends FormRequest
public function rules(): array
{
return [
'date' => 'required|date|after:1970-01-02|before:2038-01-17',
// 'date' => 'required|date|after:1970-01-02|before:2038-01-17',
];
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* StoreRequest.php
* Copyright (c) 2025 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
class StoreByCurrenciesRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
public function getAll(): array
{
return $this->all();
}
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'*' => 'required|numeric|min:0.0000000001',
];
}
public function withValidator(Validator $validator): void
{
$validator->after(
static function (Validator $validator): void {
$data = $validator->getData() ?? [];
foreach ($data as $date => $rate) {
try {
$date = Carbon::createFromFormat('Y-m-d', $date);
} catch (InvalidFormatException $e) {
$validator->errors()->add('date', trans('validation.date', ['attribute' => 'date']));
return;
}
if (!is_numeric($rate)) {
$validator->errors()->add('rate', trans('validation.number', ['attribute' => 'rate']));
return;
}
}
}
);
}
}

View File

@@ -0,0 +1,90 @@
<?php
/*
* StoreRequest.php
* Copyright (c) 2025 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
class StoreByDateRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
public function getAll(): array
{
return [
'from' => $this->get('from'),
'rates' => $this->get('rates', []),
];
}
public function getFromCurrency(): TransactionCurrency
{
return TransactionCurrency::where('code', $this->get('from'))->first();
}
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'from' => 'required|exists:transaction_currencies,code',
'rates' => 'required|array',
'rates.*' => 'required|numeric|min:0.0000000001',
];
}
public function withValidator(Validator $validator): void
{
$from = $this->getFromCurrency();
$validator->after(
static function (Validator $validator) use ($from): void {
$data = $validator->getData();
$rates = $data['rates'] ?? [];
if (0 === count($rates)) {
$validator->errors()->add('rates', 'No rates given.');
return;
}
foreach ($rates as $key => $entry) {
if ($key === $from->code) {
$validator->errors()->add(sprintf('rates.%s', $key), trans('validation.convert_to_itself', ['code' => $key]));
continue;
}
$to = TransactionCurrency::where('code', $key)->first();
if (null === $to) {
$validator->errors()->add(sprintf('rates.%s', $key), trans('validation.invalid_currency_code', ['code' => $key]));
}
}
}
);
}
}

View File

@@ -52,6 +52,8 @@ class UpdateRequest extends FormRequest
return [
'date' => 'date|after:1970-01-02|before:2038-01-17',
'rate' => 'required|numeric|gt:0',
'from' => 'nullable|exists:transaction_currencies,code',
'to' => 'nullable|exists:transaction_currencies,code',
];
}
}

View File

@@ -49,14 +49,23 @@ class ValidatesEnvironmentVariables extends Command
*/
public function handle(): int
{
$this->validateLanguage();
$this->validateGuard();
$this->validateStaticToken();
$result = $this->validateLanguage();
if (false === $result) {
return Command::FAILURE;
}
$result = $this->validateGuard();
if (false === $result) {
return Command::FAILURE;
}
$result = $this->validateStaticToken();
if (false === $result) {
return Command::FAILURE;
}
return Command::SUCCESS;
}
private function validateLanguage(): void
private function validateLanguage(): bool
{
$language = config('firefly.default_language');
$locale = config('firefly.default_locale');
@@ -67,7 +76,7 @@ class ValidatesEnvironmentVariables extends Command
$this->friendlyError('Please check your .env file and make sure you use a valid setting.');
$this->friendlyError(sprintf('Valid languages are: %s', implode(', ', $options)));
exit(1);
return false;
}
$options[] = 'equal';
if (!in_array($locale, $options, true)) {
@@ -75,11 +84,13 @@ class ValidatesEnvironmentVariables extends Command
$this->friendlyError('Please check your .env file and make sure you use a valid setting.');
$this->friendlyError(sprintf('Valid locales are: %s', implode(', ', $options)));
exit(1);
return false;
}
return true;
}
private function validateGuard(): void
private function validateGuard(): bool
{
$guard = config('auth.defaults.guard');
if ('web' !== $guard && 'remote_user_guard' !== $guard) {
@@ -87,18 +98,22 @@ class ValidatesEnvironmentVariables extends Command
$this->friendlyError('Please check your .env file and make sure you use a valid setting.');
$this->friendlyError('Valid guards are: web, remote_user_guard');
exit(1);
return false;
}
return true;
}
private function validateStaticToken(): void
private function validateStaticToken(): bool
{
$token = (string) config('firefly.static_cron_token');
$token = (string)config('firefly.static_cron_token');
if ('' !== $token && 32 !== strlen($token)) {
$this->friendlyError('STATIC_CRON_TOKEN must be empty or a 32-character string.');
$this->friendlyError('Please check your .env file and make sure you use a valid setting.');
exit(1);
return false;
}
return true;
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command;
use Symfony\Component\Console\Command\Command as CommandAlias;
class ResetsErrorMailLimit extends Command
{
use ShowsFriendlyMessages;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:reset-error-mail-limit';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Resets the number of error mails sent.';
/**
* Execute the console command.
*/
public function handle(): int
{
$file = storage_path('framework/cache/error-count.json');
$directory = storage_path('framework/cache');
$limits = [];
if (!is_writable($directory)) {
$this->friendlyError(sprintf('Cannot write to directory "%s", cannot rate limit errors.', $directory));
return CommandAlias::FAILURE;
}
if (!file_exists($file)) {
$this->friendlyInfo(sprintf('Created new limits file at "%s"', $file));
file_put_contents($file, json_encode($limits, JSON_PRETTY_PRINT));
return CommandAlias::SUCCESS;
}
if (!is_writable($file)) {
$this->friendlyError(sprintf('Cannot write to "%s", cannot rate limit errors.', $file));
return CommandAlias::FAILURE;
}
$this->friendlyInfo(sprintf('Successfully reset the error rate-limits file located at "%s"', $file));
file_put_contents($file, json_encode($limits, JSON_PRETTY_PRINT));
return CommandAlias::SUCCESS;
}
}

View File

@@ -29,12 +29,16 @@ use FireflyIII\Models\Attachment;
use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Safe\Exceptions\FileinfoException;
use Safe\Exceptions\FilesystemException;
use Safe\Exceptions\StringsException;
use function Safe\tempnam;
use function Safe\file_put_contents;
use function Safe\md5_file;
use function Safe\mime_content_type;
use function Safe\tempnam;
class ScansAttachments extends Command
{
@@ -46,6 +50,10 @@ class ScansAttachments extends Command
/**
* Execute the console command.
*
* @throws FilesystemException
* @throws StringsException
* @throws FileinfoException
*/
public function handle(): int
{
@@ -57,7 +65,7 @@ class ScansAttachments extends Command
$fileName = $attachment->fileName();
$encryptedContent = $disk->get($fileName);
if (null === $encryptedContent) {
app('log')->error(sprintf('No content for attachment #%d under filename "%s"', $attachment->id, $fileName));
Log::error(sprintf('No content for attachment #%d under filename "%s"', $attachment->id, $fileName));
continue;
}
@@ -65,18 +73,13 @@ class ScansAttachments extends Command
try {
$decryptedContent = Crypt::decrypt($encryptedContent); // verified
} catch (DecryptException $e) {
app('log')->error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage()));
Log::error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage()));
$decryptedContent = $encryptedContent;
}
$tempFileName = tempnam(sys_get_temp_dir(), 'FireflyIII');
if (false === $tempFileName) {
app('log')->error(sprintf('Could not create temporary file for attachment #%d', $attachment->id));
exit(1);
}
file_put_contents($tempFileName, $decryptedContent);
$attachment->md5 = (string) md5_file($tempFileName);
$attachment->mime = (string) mime_content_type($tempFileName);
$attachment->md5 = (string)md5_file($tempFileName);
$attachment->mime = (string)mime_content_type($tempFileName);
$attachment->save();
$this->friendlyInfo(sprintf('Fixed attachment #%d', $attachment->id));
}

View File

@@ -50,7 +50,7 @@ class Cron extends Command
{--download-cer : Download exchange rates. Other tasks will be skipped unless also requested.}
{--create-recurring : Create recurring transactions. Other tasks will be skipped unless also requested.}
{--create-auto-budgets : Create auto budgets. Other tasks will be skipped unless also requested.}
{--send-bill-warnings : Send bill warnings. Other tasks will be skipped unless also requested.}
{--send-subscription-warnings : Send subscription warnings. Other tasks will be skipped unless also requested.}
{--send-webhook-messages : Sends any stray webhook messages (with a maximum of 5).}
';
@@ -59,7 +59,7 @@ class Cron extends Command
$doAll = !$this->option('download-cer')
&& !$this->option('create-recurring')
&& !$this->option('create-auto-budgets')
&& !$this->option('send-bill-warnings')
&& !$this->option('send-subscription-warnings')
&& !$this->option('check-version')
&& !$this->option('send-webhook-messages');
$date = null;
@@ -116,9 +116,9 @@ class Cron extends Command
}
// Fire bill warning cron job
if ($doAll || $this->option('send-bill-warnings')) {
if ($doAll || $this->option('send-subscription-warnings')) {
try {
$this->billWarningCronJob($force, $date);
$this->subscriptionWarningCronJob($force, $date);
} catch (FireflyException $e) {
app('log')->error($e->getMessage());
app('log')->error($e->getTraceAsString());
@@ -231,25 +231,25 @@ class Cron extends Command
/**
* @throws FireflyException
*/
private function billWarningCronJob(bool $force, ?Carbon $date): void
private function subscriptionWarningCronJob(bool $force, ?Carbon $date): void
{
$autoBudget = new BillWarningCronjob();
$autoBudget->setForce($force);
$subscriptionWarningJob = new BillWarningCronjob();
$subscriptionWarningJob->setForce($force);
// set date in cron job:
if ($date instanceof Carbon) {
$autoBudget->setDate($date);
$subscriptionWarningJob->setDate($date);
}
$autoBudget->fire();
$subscriptionWarningJob->fire();
if ($autoBudget->jobErrored) {
$this->friendlyError(sprintf('Error in "bill warnings" cron: %s', $autoBudget->message));
if ($subscriptionWarningJob->jobErrored) {
$this->friendlyError(sprintf('Error in "subscription warnings" cron: %s', $subscriptionWarningJob->message));
}
if ($autoBudget->jobFired) {
$this->friendlyInfo(sprintf('"Send bill warnings" cron fired: %s', $autoBudget->message));
if ($subscriptionWarningJob->jobFired) {
$this->friendlyInfo(sprintf('"Send subscription warnings" cron fired: %s', $subscriptionWarningJob->message));
}
if ($autoBudget->jobSucceeded) {
$this->friendlyPositive(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message));
if ($subscriptionWarningJob->jobSucceeded) {
$this->friendlyPositive(sprintf('"Send subscription warnings" cron ran with success: %s', $subscriptionWarningJob->message));
}
}

View File

@@ -839,7 +839,7 @@ class GroupCollector implements GroupCollectorInterface
return 'zzz';
}
exit('here we are 2');
return 'zzz';
});
}

View File

@@ -36,6 +36,7 @@ use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
use Psr\Container\ContainerExceptionInterface;
@@ -94,7 +95,7 @@ class RegisterController extends Controller
$this->validator($request->all())->validate();
$user = $this->createUser($request->all());
app('log')->info(sprintf('Registered new user %s', $user->email));
Log::info(sprintf('Registered new user %s', $user->email));
$owner = new OwnerNotifiable();
event(new RegisteredUser($owner, $user));

View File

@@ -112,7 +112,7 @@ class NewUserController extends Controller
$this->createCashWalletAccount($currency, $language); // create cash wallet account
// store currency preference:
$currencyRepository->makeDefault($currency);
$currencyRepository->makePrimary($currency);
// store frontpage preferences:
$accounts = $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value])->pluck('id')->toArray();

View File

@@ -417,7 +417,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
// currency must be made default.
if (true === $default) {
$this->makeDefault($currency);
$this->makePrimary($currency);
}
/** @var CurrencyUpdateService $service */
@@ -426,7 +426,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
return $service->update($currency, $data);
}
public function makeDefault(TransactionCurrency $currency): void
public function makePrimary(TransactionCurrency $currency): void
{
$current = app('amount')->getPrimaryCurrencyByUserGroup($this->userGroup);
Log::debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->userGroup->id));

View File

@@ -102,7 +102,7 @@ interface CurrencyRepositoryInterface
public function isFallbackCurrency(TransactionCurrency $currency): bool;
public function makeDefault(TransactionCurrency $currency): void;
public function makePrimary(TransactionCurrency $currency): void;
public function searchCurrency(string $search, int $limit): Collection;

View File

@@ -112,4 +112,13 @@ class ExchangeRateRepository implements ExchangeRateRepositoryInterface, UserGro
return $object;
}
public function deleteRates(TransactionCurrency $from, TransactionCurrency $to): void
{
$this->userGroup->currencyExchangeRates()
->where('from_currency_id', $from->id)
->where('to_currency_id', $to->id)
->delete()
;
}
}

View File

@@ -47,6 +47,8 @@ interface ExchangeRateRepositoryInterface
{
public function deleteRate(CurrencyExchangeRate $rate): void;
public function deleteRates(TransactionCurrency $from, TransactionCurrency $to): void;
public function getAll(): Collection;
public function getRates(TransactionCurrency $from, TransactionCurrency $to): Collection;

View File

@@ -209,7 +209,7 @@ class UserGroupRepository implements UserGroupRepositoryInterface, UserGroupInte
$currency = $repository->find((int) $data['primary_currency_id']);
}
if (null !== $currency) {
$repository->makeDefault($currency);
$repository->makePrimary($currency);
}

View File

@@ -365,7 +365,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
// currency must be made default.
if (true === $default) {
$this->makeDefault($currency);
$this->makePrimary($currency);
}
/** @var CurrencyUpdateService $service */
@@ -374,7 +374,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
return $service->update($currency, $data);
}
public function makeDefault(TransactionCurrency $currency): void
public function makePrimary(TransactionCurrency $currency): void
{
$current = app('amount')->getPrimaryCurrencyByUserGroup($this->userGroup);
Log::debug(sprintf('Enabled + made default currency %s for user #%d', $currency->code, $this->userGroup->id));

View File

@@ -89,7 +89,7 @@ interface CurrencyRepositoryInterface
public function isFallbackCurrency(TransactionCurrency $currency): bool;
public function makeDefault(TransactionCurrency $currency): void;
public function makePrimary(TransactionCurrency $currency): void;
public function searchCurrency(string $search, int $limit): Collection;

View File

@@ -65,55 +65,27 @@ class FrontpageChartGenerator
public function generate(): array
{
Log::debug('Now in generate()');
Log::debug(sprintf('Now in %s', __METHOD__));
$categories = $this->repository->getCategories();
$accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]);
// get expenses + income per category:
$collection = [];
/** @var Category $category */
foreach ($categories as $category) {
// get expenses
$collection[] = $this->collectExpenses($category, $accounts);
}
$collection = $this->collectExpensesAll($categories, $accounts);
// collect for no-category:
$collection[] = $this->collectNoCatExpenses($accounts);
$tempData = array_merge(...$collection);
$noCategory = $this->collectNoCatExpenses($accounts);
$collection = array_merge($collection, $noCategory);
// sort temp array by amount.
$amounts = array_column($tempData, 'sum_float');
array_multisort($amounts, SORT_ASC, $tempData);
$amounts = array_column($collection, 'sum_float');
array_multisort($amounts, SORT_ASC, $collection);
$currencyData = $this->createCurrencyGroups($tempData);
$currencyData = $this->createCurrencyGroups($collection);
return $this->insertValues($currencyData, $tempData);
}
private function collectExpenses(Category $category, Collection $accounts): array
{
Log::debug(sprintf('Collect expenses for category #%d ("%s")', $category->id, $category->name));
$spent = $this->opsRepos->sumExpenses($this->start, $this->end, $accounts, new Collection([$category]));
$tempData = [];
foreach ($spent as $currency) {
Log::debug(sprintf('Spent %s %s', $currency['currency_code'], $currency['sum']));
$this->addCurrency($currency);
$tempData[] = [
'name' => $category->name,
'sum' => $currency['sum'],
'sum_float' => round((float) $currency['sum'], $currency['currency_decimal_places']),
'currency_id' => (int) $currency['currency_id'],
];
}
return $tempData;
return $this->insertValues($currencyData, $collection);
}
private function addCurrency(array $currency): void
{
$currencyId = (int) $currency['currency_id'];
$currencyId = (int)$currency['currency_id'];
$this->currencies[$currencyId] ??= [
'currency_id' => $currencyId,
@@ -133,8 +105,8 @@ class FrontpageChartGenerator
$tempData[] = [
'name' => trans('firefly.no_category'),
'sum' => $currency['sum'],
'sum_float' => round((float) $currency['sum'], $currency['currency_decimal_places'] ?? 2), // intentional float
'currency_id' => (int) $currency['currency_id'],
'sum_float' => round((float)$currency['sum'], $currency['currency_decimal_places'] ?? 2), // intentional float
'currency_id' => (int)$currency['currency_id'],
];
}
@@ -152,7 +124,7 @@ class FrontpageChartGenerator
foreach ($this->currencies as $currencyId => $currency) {
$key = sprintf('spent-%d', $currencyId);
$return[$key] = [
'label' => sprintf('%s (%s)', (string) trans('firefly.spent'), $currency['currency_name']),
'label' => sprintf('%s (%s)', (string)trans('firefly.spent'), $currency['currency_name']),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
'entries' => $names,
@@ -175,4 +147,28 @@ class FrontpageChartGenerator
return $currencyData;
}
private function collectExpensesAll(Collection $categories, Collection $accounts): array
{
Log::debug(sprintf('Collect expenses for %d category(ies).', count($categories)));
$spent = $this->opsRepos->collectExpenses($this->start, $this->end, $accounts, $categories);
$tempData = [];
foreach ($categories as $category) {
$sums = $this->opsRepos->sumCollectedTransactionsByCategory($spent, $category, 'negative', $this->convertToPrimary);
if (0 === count($sums)) {
continue;
}
foreach ($sums as $currency) {
$this->addCurrency($currency);
$tempData[] = [
'name' => $category->name,
'sum' => $currency['sum'],
'sum_float' => round((float)$currency['sum'], $currency['currency_decimal_places']),
'currency_id' => (int)$currency['currency_id'],
];
}
}
return $tempData;
}
}

View File

@@ -26,6 +26,9 @@ namespace FireflyIII\Support\Chart;
use FireflyIII\Exceptions\FireflyException;
/**
* @deprecated
*/
class ChartData
{
private array $series;

View File

@@ -42,6 +42,7 @@ use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Models\Tag;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
@@ -84,6 +85,7 @@ class ExportDataGenerator
private bool $exportTransactions;
private Carbon $start;
private User $user;
private UserGroup $userGroup;
public function __construct()
{
@@ -906,4 +908,9 @@ class ExportDataGenerator
{
$this->start = $start;
}
public function setUserGroup(UserGroup $userGroup): void
{
$this->userGroup = $userGroup;
}
}

View File

@@ -28,6 +28,8 @@ use Carbon\Carbon;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Navigation;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
@@ -66,34 +68,38 @@ class AccountBalanceGrouped
// income and expense array prepped:
$income = [
'label' => 'earned',
'currency_id' => (string) $currency['currency_id'],
'currency_id' => (string)$currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'primary_currency_id' => (string) $currency['primary_currency_id'],
'primary_currency_id' => (string)$currency['primary_currency_id'],
'primary_currency_symbol' => $currency['primary_currency_symbol'],
'primary_currency_code' => $currency['primary_currency_code'],
'primary_currency_decimal_places' => $currency['primary_currency_decimal_places'],
'date' => $this->start->toAtomString(),
'start' => $this->start->toAtomString(),
'end' => $this->end->toAtomString(),
'start_date' => $this->start->toAtomString(),
'end_date' => $this->end->toAtomString(),
'yAxisID' => 0,
'type' => 'line',
'period' => $this->preferredRange,
'entries' => [],
'primary_entries' => [],
'pc_entries' => [],
];
$expense = [
'label' => 'spent',
'currency_id' => (string) $currency['currency_id'],
'currency_id' => (string)$currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'primary_currency_id' => (string) $currency['primary_currency_id'],
'primary_currency_id' => (string)$currency['primary_currency_id'],
'primary_currency_symbol' => $currency['primary_currency_symbol'],
'primary_currency_code' => $currency['primary_currency_code'],
'primary_currency_decimal_places' => $currency['primary_currency_decimal_places'],
'date' => $this->start->toAtomString(),
'start' => $this->start->toAtomString(),
'end' => $this->end->toAtomString(),
'start_date' => $this->start->toAtomString(),
'end_date' => $this->end->toAtomString(),
'type' => 'line',
'yAxisID' => 0,
'period' => $this->preferredRange,
'entries' => [],
'pc_entries' => [],
@@ -104,15 +110,15 @@ class AccountBalanceGrouped
$key = $currentStart->format($this->carbonFormat);
$label = $currentStart->toAtomString();
// normal entries
$income['entries'][$label] = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']);
$expense['entries'][$label] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']);
$income['entries'][$label] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']);
$expense['entries'][$label] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']);
// converted entries
$income['pc_entries'][$label] = app('steam')->bcround($currency[$key]['pc_earned'] ?? '0', $currency['primary_currency_decimal_places']);
$expense['pc_entries'][$label] = app('steam')->bcround($currency[$key]['pc_spent'] ?? '0', $currency['primary_currency_decimal_places']);
$income['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_earned'] ?? '0', $currency['primary_currency_decimal_places']);
$expense['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_spent'] ?? '0', $currency['primary_currency_decimal_places']);
// next loop
$currentStart = app('navigation')->addPeriod($currentStart, $this->preferredRange, 0);
$currentStart = Navigation::addPeriod($currentStart, $this->preferredRange, 0);
}
$chartData[] = $income;
@@ -143,7 +149,7 @@ class AccountBalanceGrouped
{
// format the date according to the period
$period = $journal['date']->format($this->carbonFormat);
$currencyId = (int) $journal['currency_id'];
$currencyId = (int)$journal['currency_id'];
$currency = $this->findCurrency($currencyId);
// set the array with monetary info, if it does not exist.
@@ -153,24 +159,24 @@ class AccountBalanceGrouped
// is this journal's amount in- our outgoing?
$key = $this->getDataKey($journal);
$amount = 'spent' === $key ? app('steam')->negative($journal['amount']) : app('steam')->positive($journal['amount']);
$amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']);
// get conversion rate
$rate = $this->getRate($currency, $journal['date']);
$amountConverted = bcmul((string) $amount, $rate);
$amountConverted = bcmul((string)$amount, $rate);
// perhaps transaction already has the foreign amount in the primary currency.
if ((int) $journal['foreign_currency_id'] === $this->primary->id) {
if ((int)$journal['foreign_currency_id'] === $this->primary->id) {
$amountConverted = $journal['foreign_amount'] ?? '0';
$amountConverted = 'earned' === $key ? app('steam')->positive($amountConverted) : app('steam')->negative($amountConverted);
$amountConverted = 'earned' === $key ? Steam::positive($amountConverted) : Steam::negative($amountConverted);
}
// add normal entry
$this->data[$currencyId][$period][$key] = bcadd((string) $this->data[$currencyId][$period][$key], (string) $amount);
$this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], (string)$amount);
// add converted entry
$convertedKey = sprintf('pc_%s', $key);
$this->data[$currencyId][$period][$convertedKey] = bcadd((string) $this->data[$currencyId][$period][$convertedKey], (string) $amountConverted);
$this->data[$currencyId][$period][$convertedKey] = bcadd((string)$this->data[$currencyId][$period][$convertedKey], (string)$amountConverted);
}
private function findCurrency(int $currencyId): TransactionCurrency
@@ -185,15 +191,15 @@ class AccountBalanceGrouped
private function createDefaultDataEntry(array $journal): void
{
$currencyId = (int) $journal['currency_id'];
$currencyId = (int)$journal['currency_id'];
$this->data[$currencyId] ??= [
'currency_id' => (string) $currencyId,
'currency_id' => (string)$currencyId,
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_name' => $journal['currency_name'],
'currency_decimal_places' => $journal['currency_decimal_places'],
// primary currency info (could be the same)
'primary_currency_id' => (string) $this->primary->id,
'primary_currency_id' => (string)$this->primary->id,
'primary_currency_code' => $this->primary->code,
'primary_currency_symbol' => $this->primary->symbol,
'primary_currency_decimal_places' => $this->primary->decimal_places,
@@ -202,14 +208,14 @@ class AccountBalanceGrouped
private function createDefaultPeriodEntry(array $journal): void
{
$currencyId = (int) $journal['currency_id'];
$currencyId = (int)$journal['currency_id'];
$period = $journal['date']->format($this->carbonFormat);
$this->data[$currencyId][$period] ??= [
'period' => $period,
'spent' => '0',
'earned' => '0',
'pc_spent' => '0',
'pc_earned' => '0',
'period' => $period,
'spent' => '0',
'earned' => '0',
'pc_spent' => '0',
'pc_earned' => '0',
];
}
@@ -258,12 +264,12 @@ class AccountBalanceGrouped
$primaryCurrencyId = $primary->id;
$this->currencies = [$primary->id => $primary]; // currency cache
$this->data[$primaryCurrencyId] = [
'currency_id' => (string) $primaryCurrencyId,
'currency_id' => (string)$primaryCurrencyId,
'currency_symbol' => $primary->symbol,
'currency_code' => $primary->code,
'currency_name' => $primary->name,
'currency_decimal_places' => $primary->decimal_places,
'primary_currency_id' => (string) $primaryCurrencyId,
'primary_currency_id' => (string)$primaryCurrencyId,
'primary_currency_symbol' => $primary->symbol,
'primary_currency_code' => $primary->code,
'primary_currency_name' => $primary->name,
@@ -284,7 +290,7 @@ class AccountBalanceGrouped
public function setPreferredRange(string $preferredRange): void
{
$this->preferredRange = $preferredRange;
$this->carbonFormat = app('navigation')->preferredCarbonFormatByPeriod($preferredRange);
$this->carbonFormat = Navigation::preferredCarbonFormatByPeriod($preferredRange);
}
public function setStart(Carbon $start): void

View File

@@ -47,24 +47,31 @@ trait CleansChartData
* @var array $array
*/
foreach ($data as $index => $array) {
if (array_key_exists('currency_id', $array)) {
$array['currency_id'] = (string) $array['currency_id'];
}
if (array_key_exists('primary_currency_id', $array)) {
$array['primary_currency_id'] = (string) $array['primary_currency_id'];
}
if (!array_key_exists('start', $array)) {
throw new FireflyException(sprintf('Data-set "%s" is missing the "start"-variable.', $index));
}
if (!array_key_exists('end', $array)) {
throw new FireflyException(sprintf('Data-set "%s" is missing the "end"-variable.', $index));
}
if (!array_key_exists('period', $array)) {
throw new FireflyException(sprintf('Data-set "%s" is missing the "period"-variable.', $index));
}
$array = $this->cleanSingleArray($index, $array);
$return[] = $array;
}
return $return;
}
private function cleanSingleArray(mixed $index, array $array): array
{
if (array_key_exists('currency_id', $array)) {
$array['currency_id'] = (string)$array['currency_id'];
}
if (array_key_exists('primary_currency_id', $array)) {
$array['primary_currency_id'] = (string)$array['primary_currency_id'];
}
$required = [
'start_date', 'end_date', 'period', 'yAxisID', 'type', 'entries', 'pc_entries',
'currency_id', 'primary_currency_id',
];
foreach ($required as $field) {
if (!array_key_exists($field, $array)) {
throw new FireflyException(sprintf('Data-set "%s" is missing the "%s"-variable.', $index, $field));
}
}
return $array;
}
}

View File

@@ -67,7 +67,7 @@ trait CollectsAccountsFromFilter
if ('all' === $queryParameters['preselected']) {
return $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]);
}
if ('assets' === $queryParameters['preselected']) {
if ('assets' === $queryParameters['preselected'] || 'Asset account' === $queryParameters['preselected']) {
return $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]);
}
if ('liabilities' === $queryParameters['preselected']) {

View File

@@ -39,6 +39,7 @@ use Illuminate\Support\Facades\Log;
trait ValidatesUserGroupTrait
{
protected ?UserGroup $userGroup = null;
protected ?User $user = null;
/**
* An "undocumented" filter
@@ -101,6 +102,7 @@ trait ValidatesUserGroupTrait
if ($user->hasRoleInGroupOrOwner($group, $role)) {
Log::debug(sprintf('validateUserGroup: User has role "%s" in group #%d, return the group.', $role->value, $groupId));
$this->userGroup = $group;
$this->user = $user;
return $group;
}

View File

@@ -74,8 +74,8 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private function collectIds(): void
{
$this->start = $this->collection->min('start_date');
$this->end = $this->collection->max('end_date');
$this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth();
$this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth();
/** @var BudgetLimit $limit */
foreach ($this->collection as $limit) {

View File

@@ -464,7 +464,7 @@ class Navigation
$displayFormat = (string) trans('config.month_and_day_js', [], $locale);
$diff = $start->diffInMonths($end, true);
// increment by month (for year)
if ($diff >= 1.0001) {
if ($diff >= 1.0001 && $diff < 12.001) {
$increment = 'addMonth';
$displayFormat = (string) trans('config.month_js');
}
@@ -495,7 +495,7 @@ class Navigation
$format = 'Y-m-d';
$diff = $start->diffInMonths($end, true);
// Log::debug(sprintf('preferredCarbonFormat(%s, %s) = %f', $start->format('Y-m-d'), $end->format('Y-m-d'), $diff));
if ($diff >= 1.001) {
if ($diff >= 1.001 && $diff < 12.001) {
// Log::debug(sprintf('Return Y-m because %s', $diff));
$format = 'Y-m';
}
@@ -566,7 +566,7 @@ class Navigation
{
$locale = app('steam')->getLocale();
$diff = $start->diffInMonths($end, true);
if ($diff >= 1.001) {
if ($diff >= 1.001 && $diff < 12.001) {
return (string) trans('config.month_js', [], $locale);
}
@@ -584,7 +584,7 @@ class Navigation
public function preferredEndOfPeriod(Carbon $start, Carbon $end): string
{
$diff = $start->diffInMonths($end, true);
if ($diff >= 1.001) {
if ($diff >= 1.001 && $diff < 12.001) {
return 'endOfMonth';
}
@@ -602,7 +602,7 @@ class Navigation
public function preferredRangeFormat(Carbon $start, Carbon $end): string
{
$diff = $start->diffInMonths($end, true);
if ($diff >= 1.001) {
if ($diff >= 1.001 && $diff < 12.001) {
return '1M';
}
@@ -620,7 +620,7 @@ class Navigation
public function preferredSqlFormat(Carbon $start, Carbon $end): string
{
$diff = $start->diffInMonths($end, true);
if ($diff >= 1.001) {
if ($diff >= 1.001 && $diff < 12.001) {
return '%Y-%m';
}

View File

@@ -66,14 +66,15 @@ trait UserGroupTrait
if ($user instanceof User) {
$this->user = $user;
if (null === $user->userGroup) {
throw new FireflyException(sprintf('User #%d has no user group.', $user->id));
throw new FireflyException(sprintf('User #%d ("%s") has no user group.', $user->id, $user->email));
}
$this->userGroup = $user->userGroup;
return;
}
$class = null === $user ? 'NULL' : $user::class;
throw new FireflyException(sprintf('Object is of class %s, not User.', $user::class));
throw new FireflyException(sprintf('Object is %s, not User.', $class));
}
public function getUserGroup(): ?UserGroup

View File

@@ -24,10 +24,15 @@ declare(strict_types=1);
namespace FireflyIII\Support\Request;
use FireflyIII\Exceptions\FireflyException;
trait GetFilterInstructions
{
private const string INVALID_FILTER = '%INVALID_JAMES_%';
/**
* @throws FireflyException
*/
final public function getFilterInstructions(string $key): array
{
$config = config(sprintf('firefly.filters.allowed.%s', $key));
@@ -48,7 +53,7 @@ trait GetFilterInstructions
switch ($filterType) {
default:
exit(sprintf('Do not support filter type "%s"', $filterType));
throw new FireflyException(sprintf('Do not support filter type "%s"', $filterType));
case 'boolean':
$filterValue = $this->booleanInstruction($filterValue);

View File

@@ -79,8 +79,11 @@ class CategoryTransformer extends AbstractTransformer
];
}
private function beautify(array $array): array
private function beautify(?array $array): ?array
{
if (null === $array) {
return null;
}
$return = [];
foreach ($array as $data) {
$data['sum'] = Steam::bcround($data['sum'], (int)$data['currency_decimal_places']);

View File

@@ -48,11 +48,13 @@ class ExchangeRateTransformer extends AbstractTransformer
'updated_at' => $rate->updated_at->toAtomString(),
'from_currency_id' => (string) $rate->fromCurrency->id,
'from_currency_name' => $rate->fromCurrency->name,
'from_currency_code' => $rate->fromCurrency->code,
'from_currency_symbol' => $rate->fromCurrency->symbol,
'from_currency_decimal_places' => $rate->fromCurrency->decimal_places,
'to_currency_id' => (string) $rate->toCurrency->id,
'to_currency_name' => $rate->toCurrency->name,
'to_currency_code' => $rate->toCurrency->code,
'to_currency_symbol' => $rate->toCurrency->symbol,
'to_currency_decimal_places' => $rate->toCurrency->decimal_places,

View File

@@ -74,7 +74,7 @@ class User extends Authenticatable
use HasApiTokens;
use Notifiable;
use ReturnsIntegerIdTrait;
protected $fillable = ['email', 'password', 'blocked', 'blocked_code'];
protected $fillable = ['email', 'password', 'blocked', 'blocked_code', 'user_group_id'];
protected $hidden = ['password', 'remember_token'];
protected $table = 'users';

View File

@@ -5,7 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## 6.3.0 - 2025-08-xx
> ⚠️ Firefly III v6.3.0 introduces a lot of API changes that deal with multi-currency support. Make sure your beloved apps are updated to support this.
> [!WARNING]
> Firefly III v6.3.0 introduces a lot of API changes that deal with multi-currency support. Make sure your beloved apps are updated to support this.
### Added

76
composer.lock generated
View File

@@ -1878,16 +1878,16 @@
},
{
"name": "laravel/framework",
"version": "v12.23.1",
"version": "v12.24.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "2a0e9331a0db904236143fe915c281ff4be274a3"
"reference": "6dcf2c46da23d159f35d6246234953a74b740d83"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/2a0e9331a0db904236143fe915c281ff4be274a3",
"reference": "2a0e9331a0db904236143fe915c281ff4be274a3",
"url": "https://api.github.com/repos/laravel/framework/zipball/6dcf2c46da23d159f35d6246234953a74b740d83",
"reference": "6dcf2c46da23d159f35d6246234953a74b740d83",
"shasum": ""
},
"require": {
@@ -1997,7 +1997,7 @@
"league/flysystem-read-only": "^3.25.1",
"league/flysystem-sftp-v3": "^3.25.1",
"mockery/mockery": "^1.6.10",
"orchestra/testbench-core": "^10.0.0",
"orchestra/testbench-core": "^10.6.0",
"pda/pheanstalk": "^5.0.6|^7.0.0",
"php-http/discovery": "^1.15",
"phpstan/phpstan": "^2.0",
@@ -2091,7 +2091,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2025-08-12T17:35:05+00:00"
"time": "2025-08-13T20:30:36+00:00"
},
{
"name": "laravel/passport",
@@ -10987,16 +10987,16 @@
},
{
"name": "nikic/php-parser",
"version": "v5.6.0",
"version": "v5.6.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56"
"reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56",
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
"reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
"shasum": ""
},
"require": {
@@ -11015,7 +11015,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.0-dev"
"dev-master": "5.x-dev"
}
},
"autoload": {
@@ -11039,9 +11039,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1"
},
"time": "2025-07-27T20:03:57+00:00"
"time": "2025-08-13T20:13:15+00:00"
},
{
"name": "phar-io/manifest",
@@ -11771,16 +11771,16 @@
},
{
"name": "phpunit/phpunit",
"version": "12.3.4",
"version": "12.3.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "429095031bd38cb5070ca44166bd9dd5a9245dd6"
"reference": "f10ba5f12a256026ad3c7ee4894ffe47f60d7dc7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/429095031bd38cb5070ca44166bd9dd5a9245dd6",
"reference": "429095031bd38cb5070ca44166bd9dd5a9245dd6",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f10ba5f12a256026ad3c7ee4894ffe47f60d7dc7",
"reference": "f10ba5f12a256026ad3c7ee4894ffe47f60d7dc7",
"shasum": ""
},
"require": {
@@ -11802,7 +11802,7 @@
"sebastian/cli-parser": "^4.0.0",
"sebastian/comparator": "^7.1.2",
"sebastian/diff": "^7.0.0",
"sebastian/environment": "^8.0.2",
"sebastian/environment": "^8.0.3",
"sebastian/exporter": "^7.0.0",
"sebastian/global-state": "^8.0.0",
"sebastian/object-enumerator": "^7.0.0",
@@ -11848,7 +11848,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.4"
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.5"
},
"funding": [
{
@@ -11872,20 +11872,20 @@
"type": "tidelift"
}
],
"time": "2025-08-12T07:35:30+00:00"
"time": "2025-08-16T05:20:09+00:00"
},
{
"name": "rector/rector",
"version": "2.1.2",
"version": "2.1.4",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
"reference": "40a71441dd73fa150a66102f5ca1364c44fc8fff"
"reference": "fe613c528819222f8686a9a037a315ef9d4915b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/40a71441dd73fa150a66102f5ca1364c44fc8fff",
"reference": "40a71441dd73fa150a66102f5ca1364c44fc8fff",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/fe613c528819222f8686a9a037a315ef9d4915b3",
"reference": "fe613c528819222f8686a9a037a315ef9d4915b3",
"shasum": ""
},
"require": {
@@ -11924,7 +11924,7 @@
],
"support": {
"issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/2.1.2"
"source": "https://github.com/rectorphp/rector/tree/2.1.4"
},
"funding": [
{
@@ -11932,7 +11932,7 @@
"type": "github"
}
],
"time": "2025-07-17T19:30:06+00:00"
"time": "2025-08-15T14:41:36+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -12598,16 +12598,16 @@
},
{
"name": "sebastian/recursion-context",
"version": "7.0.0",
"version": "7.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c"
"reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c",
"reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c",
"reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c",
"shasum": ""
},
"require": {
@@ -12650,15 +12650,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
"security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
"source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0"
"source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
"type": "tidelift"
}
],
"time": "2025-02-07T05:00:01+00:00"
"time": "2025-08-13T04:44:59+00:00"
},
{
"name": "sebastian/type",

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
'version' => '6.3.0-beta.1',
'build_time' => 1755023090,
'version' => '6.3.0',
'build_time' => 1755366750,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 26,

402
package-lock.json generated
View File

@@ -53,22 +53,22 @@
}
},
"node_modules/@babel/core": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.0",
"@babel/generator": "^7.28.3",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-module-transforms": "^7.27.3",
"@babel/helpers": "^7.27.6",
"@babel/parser": "^7.28.0",
"@babel/helper-module-transforms": "^7.28.3",
"@babel/helpers": "^7.28.3",
"@babel/parser": "^7.28.3",
"@babel/template": "^7.27.2",
"@babel/traverse": "^7.28.0",
"@babel/types": "^7.28.0",
"@babel/traverse": "^7.28.3",
"@babel/types": "^7.28.2",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -94,14 +94,14 @@
}
},
"node_modules/@babel/generator": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz",
"integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.0",
"@babel/types": "^7.28.0",
"@babel/parser": "^7.28.3",
"@babel/types": "^7.28.2",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -151,18 +151,18 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz",
"integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz",
"integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.27.1",
"@babel/helper-annotate-as-pure": "^7.27.3",
"@babel/helper-member-expression-to-functions": "^7.27.1",
"@babel/helper-optimise-call-expression": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
"@babel/traverse": "^7.27.1",
"@babel/traverse": "^7.28.3",
"semver": "^6.3.1"
},
"engines": {
@@ -266,15 +266,15 @@
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.27.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
"integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1",
"@babel/traverse": "^7.27.3"
"@babel/traverse": "^7.28.3"
},
"engines": {
"node": ">=6.9.0"
@@ -387,24 +387,24 @@
}
},
"node_modules/@babel/helper-wrap-function": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz",
"integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz",
"integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.27.1",
"@babel/traverse": "^7.27.1",
"@babel/types": "^7.27.1"
"@babel/template": "^7.27.2",
"@babel/traverse": "^7.28.3",
"@babel/types": "^7.28.2"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz",
"integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
"integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -416,13 +416,13 @@
}
},
"node_modules/@babel/parser": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.0"
"@babel/types": "^7.28.2"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -499,14 +499,14 @@
}
},
"node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz",
"integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz",
"integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/traverse": "^7.27.1"
"@babel/traverse": "^7.28.3"
},
"engines": {
"node": ">=6.9.0"
@@ -726,13 +726,13 @@
}
},
"node_modules/@babel/plugin-transform-class-static-block": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz",
"integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz",
"integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.27.1",
"@babel/helper-create-class-features-plugin": "^7.28.3",
"@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
@@ -743,9 +743,9 @@
}
},
"node_modules/@babel/plugin-transform-classes": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz",
"integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz",
"integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -754,7 +754,7 @@
"@babel/helper-globals": "^7.28.0",
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1",
"@babel/traverse": "^7.28.0"
"@babel/traverse": "^7.28.3"
},
"engines": {
"node": ">=6.9.0"
@@ -1284,9 +1284,9 @@
}
},
"node_modules/@babel/plugin-transform-regenerator": {
"version": "7.28.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz",
"integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz",
"integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1333,9 +1333,9 @@
}
},
"node_modules/@babel/plugin-transform-runtime": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.0.tgz",
"integrity": "sha512-dGopk9nZrtCs2+nfIem25UuHyt5moSJamArzIoh9/vezUQPmYDOzjaHDCkAzuGJibCIkPup8rMT2+wYB6S73cA==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz",
"integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1512,9 +1512,9 @@
}
},
"node_modules/@babel/preset-env": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz",
"integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz",
"integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1526,7 +1526,7 @@
"@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1",
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3",
"@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
"@babel/plugin-syntax-import-assertions": "^7.27.1",
"@babel/plugin-syntax-import-attributes": "^7.27.1",
@@ -1537,8 +1537,8 @@
"@babel/plugin-transform-block-scoped-functions": "^7.27.1",
"@babel/plugin-transform-block-scoping": "^7.28.0",
"@babel/plugin-transform-class-properties": "^7.27.1",
"@babel/plugin-transform-class-static-block": "^7.27.1",
"@babel/plugin-transform-classes": "^7.28.0",
"@babel/plugin-transform-class-static-block": "^7.28.3",
"@babel/plugin-transform-classes": "^7.28.3",
"@babel/plugin-transform-computed-properties": "^7.27.1",
"@babel/plugin-transform-destructuring": "^7.28.0",
"@babel/plugin-transform-dotall-regex": "^7.27.1",
@@ -1570,7 +1570,7 @@
"@babel/plugin-transform-private-methods": "^7.27.1",
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
"@babel/plugin-transform-property-literals": "^7.27.1",
"@babel/plugin-transform-regenerator": "^7.28.0",
"@babel/plugin-transform-regenerator": "^7.28.3",
"@babel/plugin-transform-regexp-modifiers": "^7.27.1",
"@babel/plugin-transform-reserved-words": "^7.27.1",
"@babel/plugin-transform-shorthand-properties": "^7.27.1",
@@ -1622,9 +1622,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz",
"integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
"integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1646,18 +1646,18 @@
}
},
"node_modules/@babel/traverse": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz",
"integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
"integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.0",
"@babel/generator": "^7.28.3",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.28.0",
"@babel/parser": "^7.28.3",
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.0",
"@babel/types": "^7.28.2",
"debug": "^4.3.1"
},
"engines": {
@@ -1707,9 +1707,9 @@
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
"integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
"cpu": [
"ppc64"
],
@@ -1724,9 +1724,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
"integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
"cpu": [
"arm"
],
@@ -1741,9 +1741,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
"integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
"cpu": [
"arm64"
],
@@ -1758,9 +1758,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
"integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
"cpu": [
"x64"
],
@@ -1775,9 +1775,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
"integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
"cpu": [
"arm64"
],
@@ -1792,9 +1792,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
"integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
"cpu": [
"x64"
],
@@ -1809,9 +1809,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
"integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
"cpu": [
"arm64"
],
@@ -1826,9 +1826,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
"integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
"cpu": [
"x64"
],
@@ -1843,9 +1843,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
"integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
"cpu": [
"arm"
],
@@ -1860,9 +1860,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
"integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
"cpu": [
"arm64"
],
@@ -1877,9 +1877,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
"integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
"cpu": [
"ia32"
],
@@ -1894,9 +1894,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
"integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
"cpu": [
"loong64"
],
@@ -1911,9 +1911,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
"integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
"cpu": [
"mips64el"
],
@@ -1928,9 +1928,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
"integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
"cpu": [
"ppc64"
],
@@ -1945,9 +1945,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
"integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
"cpu": [
"riscv64"
],
@@ -1962,9 +1962,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
"integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
"cpu": [
"s390x"
],
@@ -1979,9 +1979,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
"integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
"cpu": [
"x64"
],
@@ -1996,9 +1996,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
"integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
"cpu": [
"arm64"
],
@@ -2013,9 +2013,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
"integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
"cpu": [
"x64"
],
@@ -2030,9 +2030,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
"integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
"cpu": [
"arm64"
],
@@ -2047,9 +2047,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
"integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
"cpu": [
"x64"
],
@@ -2064,9 +2064,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
"integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
"cpu": [
"arm64"
],
@@ -2081,9 +2081,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
"integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
"cpu": [
"x64"
],
@@ -2098,9 +2098,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
"integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
"cpu": [
"arm64"
],
@@ -2115,9 +2115,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
"integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
"cpu": [
"ia32"
],
@@ -2132,9 +2132,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
"integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
"cpu": [
"x64"
],
@@ -3148,9 +3148,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.2.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
"version": "24.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4486,9 +4486,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001734",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz",
"integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==",
"version": "1.0.30001735",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz",
"integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==",
"dev": true,
"funding": [
{
@@ -5700,9 +5700,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.5.200",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.200.tgz",
"integrity": "sha512-rFCxROw7aOe4uPTfIAx+rXv9cEcGx+buAF4npnhtTqCJk5KDFRnh3+KYj7rdVh6lsFt5/aPs+Irj9rZ33WMA7w==",
"version": "1.5.203",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz",
"integrity": "sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==",
"dev": true,
"license": "ISC"
},
@@ -5863,9 +5863,9 @@
}
},
"node_modules/esbuild": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
"integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -5876,32 +5876,32 @@
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.8",
"@esbuild/android-arm": "0.25.8",
"@esbuild/android-arm64": "0.25.8",
"@esbuild/android-x64": "0.25.8",
"@esbuild/darwin-arm64": "0.25.8",
"@esbuild/darwin-x64": "0.25.8",
"@esbuild/freebsd-arm64": "0.25.8",
"@esbuild/freebsd-x64": "0.25.8",
"@esbuild/linux-arm": "0.25.8",
"@esbuild/linux-arm64": "0.25.8",
"@esbuild/linux-ia32": "0.25.8",
"@esbuild/linux-loong64": "0.25.8",
"@esbuild/linux-mips64el": "0.25.8",
"@esbuild/linux-ppc64": "0.25.8",
"@esbuild/linux-riscv64": "0.25.8",
"@esbuild/linux-s390x": "0.25.8",
"@esbuild/linux-x64": "0.25.8",
"@esbuild/netbsd-arm64": "0.25.8",
"@esbuild/netbsd-x64": "0.25.8",
"@esbuild/openbsd-arm64": "0.25.8",
"@esbuild/openbsd-x64": "0.25.8",
"@esbuild/openharmony-arm64": "0.25.8",
"@esbuild/sunos-x64": "0.25.8",
"@esbuild/win32-arm64": "0.25.8",
"@esbuild/win32-ia32": "0.25.8",
"@esbuild/win32-x64": "0.25.8"
"@esbuild/aix-ppc64": "0.25.9",
"@esbuild/android-arm": "0.25.9",
"@esbuild/android-arm64": "0.25.9",
"@esbuild/android-x64": "0.25.9",
"@esbuild/darwin-arm64": "0.25.9",
"@esbuild/darwin-x64": "0.25.9",
"@esbuild/freebsd-arm64": "0.25.9",
"@esbuild/freebsd-x64": "0.25.9",
"@esbuild/linux-arm": "0.25.9",
"@esbuild/linux-arm64": "0.25.9",
"@esbuild/linux-ia32": "0.25.9",
"@esbuild/linux-loong64": "0.25.9",
"@esbuild/linux-mips64el": "0.25.9",
"@esbuild/linux-ppc64": "0.25.9",
"@esbuild/linux-riscv64": "0.25.9",
"@esbuild/linux-s390x": "0.25.9",
"@esbuild/linux-x64": "0.25.9",
"@esbuild/netbsd-arm64": "0.25.9",
"@esbuild/netbsd-x64": "0.25.9",
"@esbuild/openbsd-arm64": "0.25.9",
"@esbuild/openbsd-x64": "0.25.9",
"@esbuild/openharmony-arm64": "0.25.9",
"@esbuild/sunos-x64": "0.25.9",
"@esbuild/win32-arm64": "0.25.9",
"@esbuild/win32-ia32": "0.25.9",
"@esbuild/win32-x64": "0.25.9"
}
},
"node_modules/escalade": {
@@ -7052,9 +7052,9 @@
}
},
"node_modules/i18next": {
"version": "25.3.4",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.4.tgz",
"integrity": "sha512-AHklEYFLiRRxW1Cb6zE9lfnEtYvsydRC8nRS3RSKGX3zCqZ8nLZwMaUsrb80YuccPNv2RNokDL8LkTNnp+6mDw==",
"version": "25.3.6",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.6.tgz",
"integrity": "sha512-dThZ0CTCM3sUG/qS0ZtQYZQcUI6DtBN8yBHK+SKEqihPcEYmjVWh/YJ4luic73Iq6Uxhp6q7LJJntRK5+1t7jQ==",
"funding": [
{
"type": "individual",
@@ -11170,11 +11170,14 @@
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
@@ -11617,11 +11620,14 @@
"license": "MIT"
},
"node_modules/vite/node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
@@ -11844,9 +11850,9 @@
"license": "BSD-2-Clause"
},
"node_modules/webpack": {
"version": "5.101.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.1.tgz",
"integrity": "sha512-rHY3vHXRbkSfhG6fH8zYQdth/BtDgXXuR2pHF++1f/EBkI8zkgM5XWfsC3BvOoW9pr1CvZ1qQCxhCEsbNgT50g==",
"version": "5.101.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.2.tgz",
"integrity": "sha512-4JLXU0tD6OZNVqlwzm3HGEhAHufSiyv+skb7q0d2367VDMzrU1Q/ZeepvkcHH0rZie6uqEtTQQe0OEOOluH3Mg==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -31,12 +31,18 @@
failOnRisky="true"
failOnWarning="true"
processIsolation="false"
stopOnError="true"
stopOnFailure="true">
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="APP_LOG_ENV" value="notice"/>
<env name="DB_DATABASE" value=""/>
<env name="APP_LOG_LEVEL" value="notice"/>
<env name="AUDIT_LOG_LEVEL" value="emergency" />
<env name="QUERY_PARSER_IMPLEMENTATION" value="new"/>
<ini name="xdebug.mode" value="coverage"/>
</php>
<testsuites>
<testsuite name="unit">

View File

@@ -284,9 +284,9 @@ export default {
// console.log(parts);
// delete A to B
axios.delete("./api/v1/exchange-rates/rates/" + parts.from + '/' + parts.to + '?date=' + format(parts.date, 'yyyy-MM-dd'));
axios.delete("./api/v1/exchange-rates/" + parts.from + '/' + parts.to + '/' + format(parts.date, 'yyyy-MM-dd'));
// delete B to A.
axios.delete("./api/v1/exchange-rates/rates/" + parts.to + '/' + parts.from + '?date=' + format(parts.date, 'yyyy-MM-dd'));
axios.delete("./api/v1/exchange-rates/" + parts.to + '/' + parts.from + '/' + format(parts.date, 'yyyy-MM-dd'));
this.rates.splice(index, 1);
},
@@ -327,7 +327,7 @@ export default {
downloadRates: function (page) {
this.tempRates = {};
this.loading = true;
axios.get("./api/v1/exchange-rates/rates/" + this.from_code + '/' + this.to_code + '?page=' + page).then((response) => {
axios.get("./api/v1/exchange-rates/" + this.from_code + '/' + this.to_code + '?page=' + page).then((response) => {
for (let i in response.data.data) {
if (response.data.data.hasOwnProperty(i)) {
let current = response.data.data[i];

View File

@@ -102,7 +102,7 @@
"profile_oauth_client_secret_title": "Client Secret",
"profile_oauth_client_secret_expl": "Hier ist Ihr neuer pers\u00f6nlicher Zugangsschl\u00fcssel. Dies ist das einzige Mal, dass er angezeigt wird, also verlieren Sie ihn nicht! Sie k\u00f6nnen diesen Token jetzt verwenden, um API-Anfragen zu stellen.",
"profile_oauth_confidential": "Vertraulich",
"profile_oauth_confidential_help": "Require the client to authenticate with a secret. Confidential clients can hold credentials in a secure way without exposing them to unauthorized parties. Public applications, such as native desktop or JavaScript SPA applications, are unable to hold secrets securely.",
"profile_oauth_confidential_help": "Verlangen Sie vom Client, sich mit einem Geheimnis zu authentifizieren. Vertrauliche Clients k\u00f6nnen Anmeldedaten auf sichere Weise speichern, ohne sie Unbefugten zug\u00e4nglich zu machen. \u00d6ffentliche Anwendungen, wie native Desktop- oder JavaScript-SPA-Anwendungen, sind nicht f\u00e4hig, Geheimnisse sicher zu speichern.",
"multi_account_warning_unknown": "Abh\u00e4ngig von der Art der Buchung, die Sie anlegen, kann das Quell- und\/oder Zielkonto nachfolgender Aufteilungen durch das \u00fcberschrieben werden, was in der ersten Aufteilung der Buchung definiert wurde.",
"multi_account_warning_withdrawal": "Bedenken Sie, dass das Quellkonto nachfolgender Aufteilungen von dem, was in der ersten Aufteilung der Abhebung definiert ist, au\u00dfer Kraft gesetzt wird.",
"multi_account_warning_deposit": "Bedenken Sie, dass das Zielkonto nachfolgender Aufteilungen von dem, was in der ersten Aufteilung der Einnahmen definiert ist, au\u00dfer Kraft gesetzt wird.",

View File

@@ -102,7 +102,7 @@
"profile_oauth_client_secret_title": "Secret du client",
"profile_oauth_client_secret_expl": "Voici votre nouveau secret de client. C'est la seule fois qu'il sera affich\u00e9, donc ne le perdez pas ! Vous pouvez maintenant utiliser ce secret pour faire des requ\u00eates d'API.",
"profile_oauth_confidential": "Confidentiel",
"profile_oauth_confidential_help": "Require the client to authenticate with a secret. Confidential clients can hold credentials in a secure way without exposing them to unauthorized parties. Public applications, such as native desktop or JavaScript SPA applications, are unable to hold secrets securely.",
"profile_oauth_confidential_help": "Exiger que le client s'authentifie avec un secret. Les clients confidentiels peuvent d\u00e9tenir des informations d'identification de mani\u00e8re s\u00e9curis\u00e9e sans les exposer \u00e0 des tiers non autoris\u00e9s. Les applications publiques, telles que les applications de bureau natif ou les SPA JavaScript, ne peuvent pas tenir des secrets en toute s\u00e9curit\u00e9.",
"multi_account_warning_unknown": "Selon le type d'op\u00e9ration que vous cr\u00e9ez, le(s) compte(s) source et\/ou de destination des s\u00e9parations suivantes peuvent \u00eatre remplac\u00e9s par celui de la premi\u00e8re s\u00e9paration de l'op\u00e9ration.",
"multi_account_warning_withdrawal": "Gardez en t\u00eate que le compte source des s\u00e9parations suivantes peut \u00eatre remplac\u00e9 par celui de la premi\u00e8re s\u00e9paration de la d\u00e9pense.",
"multi_account_warning_deposit": "Gardez en t\u00eate que le compte de destination des s\u00e9parations suivantes peut \u00eatre remplac\u00e9 par celui de la premi\u00e8re s\u00e9paration du d\u00e9p\u00f4t.",

View File

@@ -25,7 +25,7 @@ export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v1/chart/account/dashboard', {params: {start: startStr, end: endStr}});
return api.get('/api/v1/chart/account/overview', {params: {start: startStr, end: endStr}});
}
expense(start, end) {

View File

@@ -25,6 +25,6 @@ export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v1/chart/budget/dashboard', {params: {start: startStr, end: endStr}});
return api.get('/api/v1/chart/budget/overview', {params: {start: startStr, end: endStr}});
}
}

View File

@@ -25,6 +25,6 @@ export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v1/chart/category/dashboard', {params: {start: startStr, end: endStr}});
return api.get('/api/v1/chart/category/overview', {params: {start: startStr, end: endStr}});
}
}

View File

@@ -25,7 +25,7 @@ export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v1/chart/account/dashboard', {params: {start: startStr, end: endStr}});
return api.get('/api/v1/chart/account/overview', {params: {start: startStr, end: endStr}});
}
expense(start, end) {

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