Compare commits

..

109 Commits

Author SHA1 Message Date
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
James Cole
5ef0889f50 Lol lets remove this file. 2025-08-12 20:45:42 +02:00
James Cole
c2ffedb506 Remove link. 2025-08-12 20:44:58 +02:00
github-actions[bot]
fe702a3030 Merge pull request #10751 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-08-12 20:27:03 +02:00
github-actions[bot]
2244d366bd Merge pull request #10750 from firefly-iii/release-1755023209
🤖 Automatically merge the PR into the develop branch.
2025-08-12 20:26:57 +02:00
JC5
ac16e0294e 🤖 Auto commit for release 'v6.3.0-beta.1' on 2025-08-12 2025-08-12 20:26:49 +02:00
github-actions[bot]
bf1b0ee3c2 Merge pull request #10749 from firefly-iii/release-1755022724
🤖 Automatically merge the PR into the develop branch.
2025-08-12 20:18:52 +02:00
JC5
ffec4bbfba 🤖 Auto commit for release 'develop' on 2025-08-12 2025-08-12 20:18:44 +02:00
James Cole
ae6e103c09 Fix translation. 2025-08-12 20:13:48 +02:00
James Cole
99d4471f75 Merge branch 'main' into develop 2025-08-11 19:19:22 +02:00
James Cole
7f1fb72298 Merge pull request #10744 from firefly-iii/dependabot/github_actions/github/command-2.0.2
Bump github/command from 2.0.1 to 2.0.2
2025-08-11 09:43:28 +02:00
dependabot[bot]
009c2ed5d9 Bump github/command from 2.0.1 to 2.0.2
Bumps [github/command](https://github.com/github/command) from 2.0.1 to 2.0.2.
- [Release notes](https://github.com/github/command/releases)
- [Commits](https://github.com/github/command/compare/v2.0.1...v2.0.2)

---
updated-dependencies:
- dependency-name: github/command
  dependency-version: 2.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 05:23:27 +00:00
github-actions[bot]
44fc00299c Merge pull request #10742 from firefly-iii/release-1754883400
🤖 Automatically merge the PR into the develop branch.
2025-08-11 05:36:47 +02:00
JC5
774fc4281c 🤖 Auto commit for release 'develop' on 2025-08-11 2025-08-11 05:36:40 +02:00
github-actions[bot]
c47955c069 Merge pull request #10741 from firefly-iii/release-1754821000
🤖 Automatically merge the PR into the develop branch.
2025-08-10 12:16:47 +02:00
JC5
e7569644f7 🤖 Auto commit for release 'develop' on 2025-08-10 2025-08-10 12:16:41 +02:00
James Cole
c567474043 Optimize balance collection. 2025-08-10 12:12:30 +02:00
github-actions[bot]
9f394e92fe Merge pull request #10740 from firefly-iii/release-1754804137
🤖 Automatically merge the PR into the develop branch.
2025-08-10 07:35:46 +02:00
JC5
66befc7e44 🤖 Auto commit for release 'develop' on 2025-08-10 2025-08-10 07:35:37 +02:00
James Cole
c3a28fc698 Fix three years ago. 2025-08-10 07:31:10 +02:00
github-actions[bot]
ef317d5b3c Merge pull request #10739 from firefly-iii/release-1754802161
🤖 Automatically merge the PR into the develop branch.
2025-08-10 07:02:58 +02:00
JC5
152301f9ee 🤖 Auto commit for release 'develop' on 2025-08-10 2025-08-10 07:02:41 +02:00
James Cole
645e9ba1f7 Go back 3 years max. 2025-08-10 06:58:06 +02:00
James Cole
56487c3a33 Fix equation. 2025-08-10 06:54:04 +02:00
James Cole
b8062a915c Fix equation. 2025-08-10 06:53:21 +02:00
James Cole
5780c9512a Optimize convert all balances. 2025-08-10 06:53:08 +02:00
github-actions[bot]
71d39707d9 Merge pull request #10738 from firefly-iii/release-1754800979
🤖 Automatically merge the PR into the develop branch.
2025-08-10 06:43:10 +02:00
JC5
9ccb8ae692 🤖 Auto commit for release 'develop' on 2025-08-10 2025-08-10 06:42:59 +02:00
James Cole
8cd50bb5bd Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-08-10 06:38:34 +02:00
James Cole
ae9e1278e5 Expand some timers, fix reports. 2025-08-10 06:38:23 +02:00
github-actions[bot]
58c03797b2 Merge pull request #10737 from firefly-iii/release-1754766186
🤖 Automatically merge the PR into the develop branch.
2025-08-09 21:03:19 +02:00
JC5
7db38b4c6c 🤖 Auto commit for release 'develop' on 2025-08-09 2025-08-09 21:03:06 +02:00
James Cole
da6b447e64 Add some extra debug info. 2025-08-09 20:42:49 +02:00
github-actions[bot]
c19ac2b0f3 Merge pull request #10736 from firefly-iii/release-1754764141
🤖 Automatically merge the PR into the develop branch.
2025-08-09 20:29:09 +02:00
JC5
d5ca2171b3 🤖 Auto commit for release 'develop' on 2025-08-09 2025-08-09 20:29:01 +02:00
James Cole
20972cb29f Merge errors. 2025-08-09 20:24:45 +02:00
James Cole
7b714d0866 Add some time logs. 2025-08-09 20:24:15 +02:00
github-actions[bot]
240ae8fa57 Merge pull request #10735 from firefly-iii/release-1754763336
🤖 Automatically merge the PR into the develop branch.
2025-08-09 20:16:11 +02:00
JC5
5a2f6b2652 🤖 Auto commit for release 'develop' on 2025-08-09 2025-08-09 20:15:36 +02:00
James Cole
4196ce31f0 Fix account thing overview. 2025-08-09 20:10:40 +02:00
James Cole
be8ca5db50 Update changelog. 2025-08-09 19:58:59 +02:00
github-actions[bot]
30a417ea3c Merge pull request #10734 from firefly-iii/release-1754750646
🤖 Automatically merge the PR into the develop branch.
2025-08-09 16:44:16 +02:00
JC5
695ed940e0 🤖 Auto commit for release 'develop' on 2025-08-09 2025-08-09 16:44:06 +02:00
James Cole
1353554cf8 Remove expensive call from loop. 2025-08-09 16:38:32 +02:00
James Cole
e1ba2732af Remove non-optimized method. 2025-08-09 16:34:25 +02:00
James Cole
42b57c0e0e Migrate to optimized method. 2025-08-09 16:31:11 +02:00
James Cole
a6072753b2 Remove PR 2025-08-09 16:01:44 +02:00
github-actions[bot]
e92c224c39 Merge pull request #10733 from firefly-iii/release-1754748022
🤖 Automatically merge the PR into the develop branch.
2025-08-09 16:00:30 +02:00
JC5
a3ed7ec8f6 🤖 Auto commit for release 'develop' on 2025-08-09 2025-08-09 16:00:22 +02:00
James Cole
17a2f99dff Order and palce in changelog. 2025-08-09 15:55:29 +02:00
James Cole
c14971543c Update changelog. 2025-08-09 15:43:36 +02:00
James Cole
55f899608d Add multi currency to piggy overview. 2025-08-09 15:33:03 +02:00
github-actions[bot]
83be63f27e Merge pull request #10731 from firefly-iii/release-1754731244
🤖 Automatically merge the PR into the develop branch.
2025-08-09 11:20:53 +02:00
JC5
ed48d190e5 🤖 Auto commit for release 'develop' on 2025-08-09 2025-08-09 11:20:44 +02:00
James Cole
3c3b6615e6 Make message singular / plural 2025-08-09 11:16:46 +02:00
James Cole
e71e5a877b Fix count for overdue bills. 2025-08-09 11:13:03 +02:00
James Cole
b2a65dc660 Small change in text. 2025-08-09 08:48:25 +02:00
James Cole
d66dccd076 Fix bad slack URL 2025-08-09 08:47:22 +02:00
github-actions[bot]
c1128b28f2 Merge pull request #10730 from firefly-iii/release-1754721510
🤖 Automatically merge the PR into the develop branch.
2025-08-09 08:38:39 +02:00
JC5
da8e78c28d 🤖 Auto commit for release 'develop' on 2025-08-09 2025-08-09 08:38:30 +02:00
James Cole
f50aa6b0ce Fix spam whoopsie. 2025-08-09 08:34:18 +02:00
github-actions[bot]
661e4e53e6 Merge pull request #10729 from firefly-iii/release-1754719442
🤖 Automatically merge the PR into the develop branch.
2025-08-09 08:04:11 +02:00
JC5
3eeda4a6aa 🤖 Auto commit for release 'develop' on 2025-08-09 2025-08-09 08:04:02 +02:00
James Cole
4dba9cea21 Fire warnings for bills and expand webhook message cron job (#10696 and #10703 and #6836) 2025-08-09 07:59:38 +02:00
James Cole
6aab5fab05 Fix #9650 2025-08-09 06:59:55 +02:00
github-actions[bot]
4b0597d19a Merge pull request #10728 from firefly-iii/release-1754679825
🤖 Automatically merge the PR into the develop branch.
2025-08-08 21:03:55 +02:00
JC5
92f534bcb3 🤖 Auto commit for release 'develop' on 2025-08-08 2025-08-08 21:03:45 +02:00
James Cole
76e91be4dc Optimize array. 2025-08-08 20:59:24 +02:00
James Cole
deca4fed56 Clean up API and display of transactions. 2025-08-08 20:18:04 +02:00
James Cole
73512b0365 Add a basic singleton to save on queries. 2025-08-08 15:44:15 +02:00
github-actions[bot]
aaffc125e7 Merge pull request #10724 from firefly-iii/release-1754630264
🤖 Automatically merge the PR into the develop branch.
2025-08-08 07:17:53 +02:00
JC5
41a48c39a0 🤖 Auto commit for release 'develop' on 2025-08-08 2025-08-08 07:17:44 +02:00
James Cole
2d96bd84b5 Fix #9640 2025-08-08 06:43:05 +02:00
James Cole
ad1c1d2254 Fix #10071 2025-08-08 06:42:54 +02:00
177 changed files with 2994 additions and 1927 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

@@ -13,7 +13,7 @@ jobs:
close_duplicates:
runs-on: ubuntu-latest
steps:
- uses: github/command@v2.0.1
- uses: github/command@v2.0.2
id: command
with:
allowed_contexts: "issue"

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,13 +27,14 @@ 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;
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;
@@ -45,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;
@@ -59,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);
}
@@ -79,17 +81,20 @@ class AccountController extends Controller
*/
public function accounts(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$types = $data['types'];
$query = $data['query'];
$date = $data['date'] ?? today(config('app.timezone'));
$return = [];
Timer::start(sprintf('AC accounts "%s"', $query));
$result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit'));
$data = $request->getData();
$types = $data['types'];
$query = $data['query'];
$date = $data['date'] ?? today(config('app.timezone'));
$return = [];
$timer = Timer::getInstance();
$timer->start(sprintf('AC accounts "%s"', $query));
$result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit'));
// set date to subday + end-of-day for account balance. so it is at $date 23:59:59
$date->endOfDay();
$allBalances = Steam::accountsBalancesOptimized($result, $date, $this->primaryCurrency, $this->convertToPrimary);
/** @var Account $account */
foreach ($result as $account) {
$nameWithBalance = $account->name;
@@ -98,15 +103,11 @@ class AccountController extends Controller
if (in_array($account->accountType->type, $this->balanceTypes, true)) {
// this one is correct.
Log::debug(sprintf('accounts: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
$balance = Steam::finalAccountBalance($account, $date);
$balance = $allBalances[$account->id] ?? [];
$key = $this->convertToPrimary && $currency->id !== $this->primaryCurrency->id ? 'pc_balance' : 'balance';
$useCurrency = $this->convertToPrimary && $currency->id !== $this->primaryCurrency->id ? $this->primaryCurrency : $currency;
$amount = $balance[$key] ?? '0';
$nameWithBalance = sprintf(
'%s (%s)',
$account->name,
app('amount')->formatAnything($useCurrency, $amount, false)
);
$nameWithBalance = sprintf('%s (%s)', $account->name, Amount::formatAnything($useCurrency, $amount, false));
}
$return[] = [
@@ -138,7 +139,7 @@ class AccountController extends Controller
return $posA - $posB;
}
);
Timer::stop(sprintf('AC accounts "%s"', $query));
$timer->stop(sprintf('AC accounts "%s"', $query));
return response()->api($return);
}

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,9 @@ 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->repository->setUserGroup($this->userGroup);
$this->repository->setUser($this->user);
return $next($request);
}
@@ -75,11 +70,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 +80,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 +89,7 @@ class AccountController extends Controller
$this->renderAccountData($queryParameters, $account);
}
return response()->json($this->chartData->render());
return response()->json($this->clean($this->chartData));
}
/**
@@ -105,29 +98,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 +158,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

@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
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\Facades\Amount;
use FireflyIII\Support\Http\Api\AccountBalanceGrouped;
use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
use Illuminate\Http\JsonResponse;
/**
* Class BalanceController
*/
class BalanceController extends Controller
{
use CleansChartData;
use CollectsAccountsFromFilter;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
private array $chartData = [];
private GroupCollectorInterface $collector;
private AccountRepositoryInterface $repository;
// private TransactionCurrency $default;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(AccountRepositoryInterface::class);
$this->collector = app(GroupCollectorInterface::class);
$this->repository->setUserGroup($this->userGroup);
$this->collector->setUserGroup($this->userGroup);
$this->repository->setUser($this->user);
$this->collector->setUser($this->user);
return $next($request);
}
);
}
/**
* The code is practically a duplicate of ReportController::operations.
*
* Currency is up to the account/transactions in question, but conversion to the default
* currency is possible.
*
* If the transaction being processed is already in native currency OR if the
* foreign amount is in the native currency, the amount will not be converted.
*
* @throws FireflyException
*/
public function balance(ChartRequest $request): JsonResponse
{
$queryParameters = $request->getParameters();
$accounts = $this->getAccountList($queryParameters);
// get journals for entire period:
$this->collector->setRange($queryParameters['start'], $queryParameters['end'])
->withAccountInformation()
->setXorAccounts($accounts)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::RECONCILIATION->value, TransactionTypeEnum::TRANSFER->value])
;
$journals = $this->collector->getExtractedJournals();
$object = new AccountBalanceGrouped();
$object->setPreferredRange($queryParameters['period']);
$object->setPrimary($this->primaryCurrency);
$object->setAccounts($accounts);
$object->setJournals($journals);
$object->setStart($queryParameters['start']);
$object->setEnd($queryParameters['end']);
$object->groupByCurrencyAndPeriod();
$data = $object->convertToChartData();
foreach ($data as $entry) {
$this->chartData[] = $entry;
}
$this->chartData = $this->clean($this->chartData);
return response()->json($this->chartData);
}
}

View File

@@ -63,13 +63,17 @@ 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->validateUserGroup($request);
$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 +85,7 @@ class BudgetController extends Controller
*
* @throws FireflyException
*/
public function dashboard(DateRequest $request): JsonResponse
public function overview(DateRequest $request): JsonResponse
{
$params = $request->getAll();
@@ -110,10 +114,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 +127,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 +142,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

@@ -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;
@@ -60,9 +61,10 @@ class CategoryController extends Controller
function ($request, $next) {
$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 +79,7 @@ class CategoryController extends Controller
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
*/
public function dashboard(DateRequest $request): JsonResponse
public function overview(DateRequest $request): JsonResponse
{
/** @var Carbon $start */
$start = $this->parameters->get('start');
@@ -100,52 +102,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

@@ -46,8 +46,6 @@ class PurgeController extends Controller
{
/**
* 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 +64,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

@@ -54,6 +54,7 @@ class CronController extends Controller
$return['exchange_rates'] = $this->exchangeRatesCronJob($config['force'], $config['date']);
}
$return['bill_notifications'] = $this->billWarningCronJob($config['force'], $config['date']);
$return['webhooks'] = $this->webhookCronJob($config['force'], $config['date']);
return response()->api($return);
}

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

@@ -40,6 +40,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Console\Command;
@@ -72,6 +73,8 @@ class CorrectsPrimaryCurrencyAmounts extends Command
/** @var UserGroupRepositoryInterface $repository */
$repository = app(UserGroupRepositoryInterface::class);
Preferences::mark();
/** @var UserGroup $userGroup */
foreach ($repository->getAll() as $userGroup) {
$this->recalculateForGroup($userGroup);
@@ -87,8 +90,7 @@ class CorrectsPrimaryCurrencyAmounts extends Command
$this->recalculateAccounts($userGroup);
// do a check with the group's currency so we can skip some stuff.
Preferences::mark();
$currency = app('amount')->getPrimaryCurrencyByUserGroup($userGroup);
$currency = Amount::getPrimaryCurrencyByUserGroup($userGroup);
$this->recalculatePiggyBanks($userGroup, $currency);
$this->recalculateBudgets($userGroup, $currency);

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

@@ -32,6 +32,7 @@ use FireflyIII\Support\Cronjobs\BillWarningCronjob;
use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob;
use FireflyIII\Support\Cronjobs\RecurringCronjob;
use FireflyIII\Support\Cronjobs\UpdateCheckCronjob;
use FireflyIII\Support\Cronjobs\WebhookCronjob;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use InvalidArgumentException;
@@ -49,7 +50,8 @@ 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).}
';
public function handle(): int
@@ -57,8 +59,9 @@ 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('check-version');
&& !$this->option('send-subscription-warnings')
&& !$this->option('check-version')
&& !$this->option('send-webhook-messages');
$date = null;
try {
@@ -113,9 +116,19 @@ 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());
$this->friendlyError($e->getMessage());
}
}
// Fire webhook messages cron job.
if ($doAll || $this->option('send-webhook-messages')) {
try {
$this->webhookCronJob($force, $date);
} catch (FireflyException $e) {
app('log')->error($e->getMessage());
app('log')->error($e->getTraceAsString());
@@ -218,25 +231,47 @@ 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));
}
}
private function webhookCronJob(bool $force, ?Carbon $date): void
{
$webhook = new WebhookCronjob();
$webhook->setForce($force);
// set date in cron job:
if ($date instanceof Carbon) {
$webhook->setDate($date);
}
$webhook->fire();
if ($webhook->jobErrored) {
$this->friendlyError(sprintf('Error in "webhook" cron: %s', $webhook->message));
}
if ($webhook->jobFired) {
$this->friendlyInfo(sprintf('"Webhook" cron fired: %s', $webhook->message));
}
if ($webhook->jobSucceeded) {
$this->friendlyPositive(sprintf('"Webhook" cron ran with success: %s', $webhook->message));
}
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;
use FireflyIII\Events\Event;
use FireflyIII\Models\Bill;
use Illuminate\Queue\SerializesModels;
/**
* Class WarnUserAboutBill.
*/
class WarnUserAboutBill extends Event
{
use SerializesModels;
public function __construct(public Bill $bill, public string $field, public int $diff) {}
}

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Events\Model\Bill;
use FireflyIII\Events\Event;
use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
class WarnUserAboutOverdueSubscriptions extends Event
{
use SerializesModels;
public function __construct(public User $user, public array $overdue) {}
}

View File

@@ -35,6 +35,6 @@ class UserGroupChangedPrimaryCurrency extends Event
public function __construct(public UserGroup $userGroup)
{
Log::debug('User group changed default currency.');
Log::debug('User group changed primary currency.');
}
}

View File

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

View File

@@ -24,48 +24,118 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\WarnUserAboutBill;
use FireflyIII\Notifications\User\BillReminder;
use Illuminate\Support\Facades\Notification;
use Exception;
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
use FireflyIII\Models\Bill;
use FireflyIII\Notifications\User\BillReminder;
use FireflyIII\Notifications\User\SubscriptionsOverdueReminder;
use FireflyIII\Support\Facades\Preferences;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
/**
* Class BillEventHandler
*/
class BillEventHandler
{
public function warnAboutOverdueSubscriptions(WarnUserAboutOverdueSubscriptions $event): void
{
Log::debug(sprintf('Now in %s', __METHOD__));
// make sure user does not get the warning twice.
$overdue = $event->overdue;
$user = $event->user;
$toBeWarned = [];
Log::debug(sprintf('%d bills to warn about.', count($overdue)));
foreach ($overdue as $item) {
/** @var Bill $bill */
$bill = $item['bill'];
$key = sprintf('bill_overdue_%s_%s', $bill->id, substr(hash('sha256', json_encode($item['dates']['pay_dates'], JSON_THROW_ON_ERROR)), 0, 10));
$pref = Preferences::getForUser($bill->user, $key, false);
if (true === $pref->data) {
Log::debug(sprintf('User #%d has already been warned about overdue subscription #%d.', $bill->user->id, $bill->id));
continue;
}
$toBeWarned[] = $item;
}
unset($bill);
Log::debug(sprintf('Now %d bills to warn about.', count($toBeWarned)));
/** @var bool $sendNotification */
$sendNotification = Preferences::getForUser($user, 'notification_bill_reminder', true)->data;
if (false === $sendNotification) {
Log::debug('User has disabled bill reminders.');
return;
}
Log::debug(sprintf('Will warn about %d overdue subscription(s).', count($toBeWarned)));
if (0 === count($toBeWarned)) {
Log::debug('No overdue subscriptions to warn about.');
return;
}
foreach ($toBeWarned as $item) {
/** @var Bill $bill */
$bill = $item['bill'];
$key = sprintf('bill_overdue_%s_%s', $bill->id, substr(hash('sha256', json_encode($item['dates']['pay_dates'], JSON_THROW_ON_ERROR)), 0, 10));
Preferences::setForUser($bill->user, $key, true);
}
Log::warning('should hit this ONCE');
try {
Notification::send($user, new SubscriptionsOverdueReminder($toBeWarned));
} catch (Exception $e) {
$message = $e->getMessage();
if (str_contains($message, 'Bcc')) {
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
return;
}
if (str_contains($message, 'RFC 2822')) {
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
return;
}
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
}
public function warnAboutBill(WarnUserAboutBill $event): void
{
app('log')->debug(sprintf('Now in %s', __METHOD__));
Log::debug(sprintf('Now in %s', __METHOD__));
$bill = $event->bill;
$bill = $event->bill;
/** @var bool $preference */
$preference = app('preferences')->getForUser($bill->user, 'notification_bill_reminder', true)->data;
Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
if (true === $preference) {
app('log')->debug('Bill reminder is true!');
Log::debug('Bill reminder is true!');
try {
Notification::send($bill->user, new BillReminder($bill, $event->field, $event->diff));
} catch (Exception $e) {
$message = $e->getMessage();
if (str_contains($message, 'Bcc')) {
app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
return;
}
if (str_contains($message, 'RFC 2822')) {
app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
return;
}
app('log')->error($e->getMessage());
app('log')->error($e->getTraceAsString());
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
return;
}
if (false === $preference) {
app('log')->debug('User has disabled bill reminders.');
}
Log::debug('User has disabled bill reminders.');
}
}

View File

@@ -73,6 +73,7 @@ class PreferencesEventHandler
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUserGroup($userGroup);
$piggyBanks = $repository->getPiggyBanks();
Log::debug(sprintf('Resetting %d piggy bank(s).', $piggyBanks->count()));
/** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) {
@@ -104,6 +105,8 @@ class PreferencesEventHandler
$repository->setUserGroup($userGroup);
$set = $repository->getBudgets();
Log::debug(sprintf('Resetting %d budget(s).', $set->count()));
/** @var Budget $budget */
foreach ($set as $budget) {
foreach ($budget->autoBudgets as $autoBudget) {

View File

@@ -77,8 +77,8 @@ class NetWorth implements NetWorthInterface
Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s')));
$primary = Amount::getPrimaryCurrency();
$netWorth = [];
Log::debug(sprintf('NetWorth: finalAccountsBalance("%s")', $date->format('Y-m-d H:i:s')));
$balances = Steam::finalAccountsBalance($accounts, $date);
Log::debug(sprintf('NetWorth: accountsBalancesOptimized("%s")', $date->format('Y-m-d H:i:s')));
$balances = Steam::accountsBalancesOptimized($accounts, $date, null, $convertToPrimary);
/** @var Account $account */
foreach ($accounts as $account) {
@@ -143,8 +143,8 @@ class NetWorth implements NetWorthInterface
*/
$accounts = $this->getAccounts();
$return = [];
Log::debug(sprintf('SumNetWorth: finalAccountsBalance("%s")', $date->format('Y-m-d H:i:s')));
$balances = Steam::finalAccountsBalance($accounts, $date);
Log::debug(sprintf('SumNetWorth: accountsBalancesOptimized("%s")', $date->format('Y-m-d H:i:s')));
$balances = Steam::accountsBalancesOptimized($accounts, $date);
foreach ($accounts as $account) {
$currency = $this->accountRepository->getAccountCurrency($account);
$balance = $balances[$account->id]['balance'] ?? '0';

View File

@@ -93,10 +93,10 @@ class IndexController extends Controller
$start->subSecond();
$ids = $accounts->pluck('id')->toArray();
Log::debug(sprintf('inactive start: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
Log::debug(sprintf('inactive end: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::finalAccountsBalance($accounts, $start);
$endBalances = Steam::finalAccountsBalance($accounts, $end);
Log::debug(sprintf('inactive start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
Log::debug(sprintf('inactive end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
$activities = Steam::getLastActivities($ids);
@@ -170,10 +170,10 @@ class IndexController extends Controller
$start->subSecond();
$ids = $accounts->pluck('id')->toArray();
Log::debug(sprintf('index start: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
Log::debug(sprintf('index end: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::finalAccountsBalance($accounts, $start);
$endBalances = Steam::finalAccountsBalance($accounts, $end);
Log::debug(sprintf('index start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
Log::debug(sprintf('index end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
$activities = Steam::getLastActivities($ids);

View File

@@ -40,6 +40,7 @@ use Illuminate\Routing\Redirector;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class ShowController
@@ -81,7 +82,9 @@ class ShowController extends Controller
* */
public function show(Request $request, Account $account, ?Carbon $start = null, ?Carbon $end = null)
{
if (0 === $account->id) {
throw new NotFoundHttpException();
}
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
if (!$this->isEditableAccount($account)) {
@@ -115,20 +118,27 @@ class ShowController extends Controller
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$firstTransaction = $this->repository->oldestJournalDate($account) ?? $start;
Log::debug('Start period overview');
Timer::start('period-overview');
// go back max 3 years.
$threeYearsAgo = clone $start;
$threeYearsAgo->startOfYear()->subYears(3);
if ($firstTransaction->lt($threeYearsAgo)) {
$firstTransaction = clone $threeYearsAgo;
}
Log::debug('Start period overview');
$timer = Timer::getInstance();
$timer->start('period-overview');
$periods = $this->getAccountPeriodOverview($account, $firstTransaction, $end);
Log::debug('End period overview');
Timer::stop('period-overview');
$timer->stop('period-overview');
// if layout = v2, overrule the page title.
if ('v1' !== config('view.layout')) {
$subTitle = (string) trans('firefly.all_journals_for_account', ['name' => $account->name]);
}
Log::debug('Collect transactions');
Timer::start('collection');
$timer->start('collection');
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
@@ -147,7 +157,7 @@ class ShowController extends Controller
Log::debug('End collect transactions');
Timer::stop('collection');
$timer->stop('collection');
// enrich data in arrays.

View File

@@ -57,7 +57,7 @@ class IndexController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.bills'));
app('view')->share('title', (string)trans('firefly.bills'));
app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->repository = app(BillRepositoryInterface::class);
@@ -79,7 +79,6 @@ class IndexController extends Controller
$total = $collection->count();
$parameters = new ParameterBag();
// sub one day from temp start so the last paid date is one day before it should be.
$tempStart = clone $start;
@@ -112,7 +111,7 @@ class IndexController extends Controller
$bills = [
0 => [ // the index is the order, not the ID.
'object_group_id' => 0,
'object_group_title' => (string) trans('firefly.default_group_title_name'),
'object_group_title' => (string)trans('firefly.default_group_title_name'),
'bills' => [],
],
];
@@ -120,7 +119,7 @@ class IndexController extends Controller
/** @var Bill $bill */
foreach ($collection as $bill) {
$array = $transformer->transform($bill);
$groupOrder = (int) $array['object_group_order'];
$groupOrder = (int)$array['object_group_order'];
// make group array if necessary:
$bills[$groupOrder] ??= [
'object_group_id' => $array['object_group_id'],
@@ -173,16 +172,28 @@ class IndexController extends Controller
'currency_symbol' => $bill['currency_symbol'],
'currency_decimal_places' => $bill['currency_decimal_places'],
'avg' => '0',
'total_left_to_pay' => '0',
'period' => $range,
'per_period' => '0',
];
// only fill in avg when bill is active.
if (null !== $bill['next_expected_match']) {
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
$avg = bcmul($avg, (string) count($bill['pay_dates']));
$avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2');
$avg = bcmul($avg, (string)count($bill['pay_dates']));
$sums[$groupOrder][$currencyId]['avg'] = bcadd($sums[$groupOrder][$currencyId]['avg'], $avg);
}
// only fill in total_left_to_pay when bill is not yet paid.
if (count($bill['paid_dates']) < count($bill['pay_dates'])) {
$count = count($bill['pay_dates']) - count($bill['paid_dates']);
if ($count > 0) {
$avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2');
$avg = bcmul($avg, (string)$count);
$sums[$groupOrder][$currencyId]['total_left_to_pay'] = bcadd($sums[$groupOrder][$currencyId]['total_left_to_pay'], $avg);
}
}
// fill in per period regardless:
$sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $this->amountPerPeriod($bill, $range));
}
@@ -193,7 +204,7 @@ class IndexController extends Controller
private function amountPerPeriod(array $bill, string $range): string
{
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
$avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2');
app('log')->debug(sprintf('Amount per period for bill #%d "%s"', $bill['id'], $bill['name']));
app('log')->debug(sprintf('Average is %s', $avg));
@@ -206,8 +217,8 @@ class IndexController extends Controller
'weekly' => '52.17',
'daily' => '365.24',
];
$yearAmount = bcmul($avg, bcdiv($multiplies[$bill['repeat_freq']], (string) ($bill['skip'] + 1)));
app('log')->debug(sprintf('Amount per year is %s (%s * %s / %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']], (string) ($bill['skip'] + 1)));
$yearAmount = bcmul($avg, bcdiv($multiplies[$bill['repeat_freq']], (string)($bill['skip'] + 1)));
app('log')->debug(sprintf('Amount per year is %s (%s * %s / %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']], (string)($bill['skip'] + 1)));
// per period:
$division = [
@@ -258,8 +269,8 @@ class IndexController extends Controller
'period' => $entry['period'],
'per_period' => '0',
];
$totals[$currencyId]['avg'] = bcadd($totals[$currencyId]['avg'], (string) $entry['avg']);
$totals[$currencyId]['per_period'] = bcadd($totals[$currencyId]['per_period'], (string) $entry['per_period']);
$totals[$currencyId]['avg'] = bcadd($totals[$currencyId]['avg'], (string)$entry['avg']);
$totals[$currencyId]['per_period'] = bcadd($totals[$currencyId]['per_period'], (string)$entry['per_period']);
}
}
@@ -271,8 +282,8 @@ class IndexController extends Controller
*/
public function setOrder(Request $request, Bill $bill): JsonResponse
{
$objectGroupTitle = (string) $request->get('objectGroupTitle');
$newOrder = (int) $request->get('order');
$objectGroupTitle = (string)$request->get('objectGroupTitle');
$newOrder = (int)$request->get('order');
$this->repository->setOrder($bill, $newOrder);
if ('' !== $objectGroupTitle) {
$this->repository->setObjectGroup($bill, $objectGroupTitle);

View File

@@ -35,6 +35,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Controllers\AugumentData;
use FireflyIII\Support\Http\Controllers\ChartGeneration;
@@ -78,6 +79,7 @@ class AccountController extends Controller
/**
* Shows the balances for all the user's expense accounts (on the front page).
* 2025-08-06 validated for multi (primary) currency
*
* This chart is (multi) currency aware.
*/
@@ -112,11 +114,11 @@ class AccountController extends Controller
$accountNames = $this->extractNames($accounts);
// grab all balances
Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::finalAccountsBalance($accounts, $start);
$endBalances = Steam::finalAccountsBalance($accounts, $end);
Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
Log::debug('Done collecting balances');
// loop the accounts, then check for balance and currency info.
foreach ($accounts as $account) {
// Log::debug(sprintf('[a] Now in account #%d ("%s")', $account->id, $account->name));
@@ -157,7 +159,7 @@ class AccountController extends Controller
$tempData[] = [
'name' => $accountNames[$account->id],
'difference' => $diff,
'diff_float' => (float) $diff, // intentional float
'diff_float' => (float)$diff, // intentional float
'currency_id' => $currencies[$searchCode]->id,
];
}
@@ -182,7 +184,7 @@ class AccountController extends Controller
foreach ($currencies as $currencyId => $currency) {
$dataSet
= [
'label' => (string) trans('firefly.spent'),
'label' => (string)trans('firefly.spent'),
'type' => 'bar',
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
@@ -195,7 +197,7 @@ class AccountController extends Controller
foreach ($tempData as $entry) {
$currencyId = $entry['currency_id'];
$name = $entry['name'];
$chartData[$currencyId]['entries'][$name] = (float) $entry['difference'];
$chartData[$currencyId]['entries'][$name] = (float)$entry['difference'];
}
$data = $this->generator->multiSet($chartData);
@@ -223,6 +225,7 @@ class AccountController extends Controller
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($this->convertToPrimary);
$cache->addProperty($end);
$cache->addProperty('chart.account.expense-budget');
if ($cache->has()) {
@@ -231,7 +234,10 @@ class AccountController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$collector->setAccounts(new Collection([$account]))
->setRange($start, $end)
->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
;
$journals = $collector->getExtractedJournals();
$chartData = [];
$result = [];
@@ -239,19 +245,37 @@ class AccountController extends Controller
/** @var array $journal */
foreach ($journals as $journal) {
$budgetId = (int) $journal['budget_id'];
$budgetId = (int)$journal['budget_id'];
$key = sprintf('%d-%d', $budgetId, $journal['currency_id']);
$budgetIds[] = $budgetId;
// currency info:
$currencyId = (int)$journal['currency_id'];
$currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$field = 'amount';
if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) {
$field = 'pc_amount';
$currencyName = $this->primaryCurrency->name;
$currencySymbol = $this->primaryCurrency->symbol;
$currencyCode = $this->primaryCurrency->code;
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
}
if (!array_key_exists($key, $result)) {
$result[$key] = [
'total' => '0',
'budget_id' => $budgetId,
'currency_name' => $journal['currency_name'],
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'total' => '0',
'budget_id' => $budgetId,
'currency_name' => $currencyName,
'currency_symbol' => $currencySymbol,
'currency_code' => $currencyCode,
'currency_decimal_places' => $currencyDecimalPlaces,
];
}
$result[$key]['total'] = bcadd((string) $journal['amount'], $result[$key]['total']);
$result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']);
}
$names = $this->getBudgetNames($budgetIds);
@@ -259,7 +283,7 @@ class AccountController extends Controller
foreach ($result as $row) {
$budgetId = $row['budget_id'];
$name = $names[$budgetId];
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$label = (string)trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
}
@@ -289,6 +313,7 @@ class AccountController extends Controller
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($this->convertToPrimary);
$cache->addProperty('chart.account.expense-category');
if ($cache->has()) {
return response()->json($cache->get());
@@ -305,22 +330,39 @@ class AccountController extends Controller
foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
if (!array_key_exists($key, $result)) {
$result[$key] = [
'total' => '0',
'category_id' => (int) $journal['category_id'],
'currency_name' => $journal['currency_name'],
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
// currency info:
$currencyId = (int)$journal['currency_id'];
$currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$field = 'amount';
if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) {
$field = 'pc_amount';
$currencyName = $this->primaryCurrency->name;
$currencySymbol = $this->primaryCurrency->symbol;
$currencyCode = $this->primaryCurrency->code;
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
}
$result[$key] = [
'total' => '0',
'category_id' => (int)$journal['category_id'],
'currency_name' => $currencyName,
'currency_code' => $currencyCode,
'currency_symbol' => $currencySymbol,
'currency_decimal_places' => $currencyDecimalPlaces,
];
}
$result[$key]['total'] = bcadd((string) $journal['amount'], $result[$key]['total']);
$result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']);
}
$names = $this->getCategoryNames(array_keys($result));
foreach ($result as $row) {
$categoryId = $row['category_id'];
$name = $names[$categoryId] ?? '(unknown)';
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$label = (string)trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
}
@@ -341,11 +383,11 @@ class AccountController extends Controller
$end = clone session('end', today(config('app.timezone'))->endOfMonth());
$defaultSet = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value])->pluck('id')->toArray();
// Log::debug('Default set is ', $defaultSet);
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
$frontpage = Preferences::get('frontpageAccounts', $defaultSet);
$frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data;
Log::debug('Frontpage preference set is ', $frontpageArray);
if (0 === count($frontpageArray)) {
app('preferences')->set('frontpageAccounts', $defaultSet);
Preferences::set('frontpageAccounts', $defaultSet);
Log::debug('frontpage set is empty!');
}
$accounts = $repository->getAccountsById($frontpageArray);
@@ -375,6 +417,7 @@ class AccountController extends Controller
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($this->convertToPrimary);
$cache->addProperty($end);
$cache->addProperty('chart.account.income-category');
if ($cache->has()) {
@@ -394,22 +437,39 @@ class AccountController extends Controller
foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
if (!array_key_exists($key, $result)) {
$result[$key] = [
'total' => '0',
'category_id' => $journal['category_id'],
'currency_name' => $journal['currency_name'],
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
// currency info:
$currencyId = (int)$journal['currency_id'];
$currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$field = 'amount';
if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) {
$field = 'pc_amount';
$currencyName = $this->primaryCurrency->name;
$currencySymbol = $this->primaryCurrency->symbol;
$currencyCode = $this->primaryCurrency->code;
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
}
$result[$key] = [
'total' => '0',
'category_id' => $journal['category_id'],
'currency_name' => $currencyName,
'currency_code' => $currencyCode,
'currency_symbol' => $currencySymbol,
'currency_decimal_places' => $currencyDecimalPlaces,
];
}
$result[$key]['total'] = bcadd((string) $journal['amount'], $result[$key]['total']);
$result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']);
}
$names = $this->getCategoryNames(array_keys($result));
foreach ($result as $row) {
$categoryId = $row['category_id'];
$name = $names[$categoryId] ?? '(unknown)';
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$label = (string)trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
}
$data = $this->generator->multiCurrencyPieChart($chartData);
@@ -450,7 +510,7 @@ class AccountController extends Controller
// This period depends on the size of the chart
$current = clone $start;
$current = app('navigation')->endOfX($current, $step, null);
$format = (string) trans('config.month_and_day_js', [], $locale);
$format = (string)trans('config.month_and_day_js', [], $locale);
$accountCurrency = $this->accountRepository->getAccountCurrency($account);
$range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary);
@@ -512,7 +572,7 @@ class AccountController extends Controller
foreach ($return as $key => $info) {
if ('balance' !== $key && 'pc_balance' !== $key) {
// assume it's a currency:
$setCurrency = $this->currencyRepository->findByCode((string) $key);
$setCurrency = $this->currencyRepository->findByCode((string)$key);
$info['currency_symbol'] = $setCurrency->symbol;
$info['currency_code'] = $setCurrency->code;
$info['label'] = sprintf('%s (%s)', $account->name, $setCurrency->symbol);
@@ -525,7 +585,7 @@ class AccountController extends Controller
if ('pc_balance' === $key) {
$info['currency_symbol'] = $this->primaryCurrency->symbol;
$info['currency_code'] = $this->primaryCurrency->code;
$info['label'] = sprintf('%s (%s) (%s)', $account->name, (string) trans('firefly.sum'), $this->primaryCurrency->symbol);
$info['label'] = sprintf('%s (%s) (%s)', $account->name, (string)trans('firefly.sum'), $this->primaryCurrency->symbol);
}
$chartData[] = $info;
}
@@ -594,10 +654,10 @@ class AccountController extends Controller
$accountNames = $this->extractNames($accounts);
// grab all balances
Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::finalAccountsBalance($accounts, $start);
$endBalances = Steam::finalAccountsBalance($accounts, $end);
Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
// loop the accounts, then check for balance and currency info.
@@ -640,7 +700,7 @@ class AccountController extends Controller
$tempData[] = [
'name' => $accountNames[$account->id],
'difference' => $diff,
'diff_float' => (float) $diff, // intentional float
'diff_float' => (float)$diff, // intentional float
'currency_id' => $currencies[$searchCode]->id,
];
}
@@ -667,7 +727,7 @@ class AccountController extends Controller
foreach ($currencies as $currencyId => $currency) {
$dataSet
= [
'label' => (string) trans('firefly.earned'),
'label' => (string)trans('firefly.earned'),
'type' => 'bar',
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Events\RequestedSendWebhookMessages;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
@@ -33,6 +34,7 @@ use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Route;
@@ -141,6 +143,14 @@ abstract class Controller extends BaseController
View::share('shownDemo', $shownDemo);
View::share('current_route_name', $page);
View::share('original_route_name', Route::currentRouteName());
// lottery to send any remaining webhooks:
if (7 === random_int(1, 10)) {
// trigger event to send them:
Log::debug('send event RequestedSendWebhookMessages through lottery');
event(new RequestedSendWebhookMessages());
}
}
View::share('darkMode', $darkMode);

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

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Support\Singleton\PreferencesSingleton;
use JsonException;
use Carbon\Carbon;
use FireflyIII\Enums\AccountTypeEnum;
@@ -269,7 +270,9 @@ class PreferencesController extends Controller
if ($convertToPrimary && !$this->convertToPrimary) {
// set to true!
Log::debug('User sets convertToPrimary to true.');
Preferences::set('convert_to_primary', $convertToPrimary);
Preferences::set('convert_to_primary', true);
$singleton = PreferencesSingleton::getInstance();
$singleton->resetPreferences();
event(new UserGroupChangedPrimaryCurrency(auth()->user()->userGroup));
}
Preferences::set('convert_to_primary', $convertToPrimary);

View File

@@ -188,16 +188,7 @@ class ReportController extends Controller
$start->endOfDay(); // end of day so the final balance is at the end of that day.
$end->endOfDay();
app('view')->share(
'subTitle',
trans(
'firefly.report_default',
[
'start' => $start->isoFormat($this->monthAndDayFormat),
'end' => $end->isoFormat($this->monthAndDayFormat),
]
)
);
app('view')->share('subTitle', trans('firefly.report_default', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]));
$generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end);
$generator->setAccounts($accounts);
@@ -222,16 +213,7 @@ class ReportController extends Controller
$start->endOfDay(); // end of day so the final balance is at the end of that day.
$end->endOfDay();
app('view')->share(
'subTitle',
trans(
'firefly.report_double',
[
'start' => $start->isoFormat($this->monthAndDayFormat),
'end' => $end->isoFormat($this->monthAndDayFormat),
]
)
);
app('view')->share('subTitle', trans('firefly.report_double', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]));
$generator = ReportGeneratorFactory::reportGenerator('Account', $start, $end);
$generator->setAccounts($accounts);

View File

@@ -270,6 +270,7 @@ class CreateController extends Controller
$data = $request->getRuleData();
$rule = $this->ruleRepos->store($data);
session()->flash('success_url', route('rules.select-transactions', [$rule->id]));
session()->flash('success', (string) trans('firefly.stored_new_rule', ['title' => $rule->title]));
app('preferences')->mark();

View File

@@ -25,16 +25,20 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Transaction;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\JsonResponse;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class ShowController
@@ -57,7 +61,7 @@ class ShowController extends Controller
$this->repository = app(TransactionGroupRepositoryInterface::class);
$this->aleRepository = app(ALERepositoryInterface::class);
app('view')->share('title', (string) trans('firefly.transactions'));
app('view')->share('title', (string)trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-exchange');
return $next($request);
@@ -80,38 +84,62 @@ class ShowController extends Controller
*/
public function show(TransactionGroup $transactionGroup)
{
/** @var User $admin */
$admin = auth()->user();
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($admin)->setTransactionGroup($transactionGroup)->withAPIInformation();
$selectedGroup = $collector->getGroups()->first();
if (null === $selectedGroup) {
throw new NotFoundHttpException();
}
// enrich
$enrichment = new TransactionGroupEnrichment();
$enrichment->setUser($admin);
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
/** @var null|TransactionJournal $first */
$first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']);
$splits = $transactionGroup->transactionJournals()->count();
$splits = count($selectedGroup['transactions']);
$keys = array_keys($selectedGroup['transactions']);
$first = $selectedGroup['transactions'][array_shift($keys)];
unset($keys);
if (null === $first) {
throw new FireflyException('This transaction is broken :(.');
}
$type = (string) trans(sprintf('firefly.%s', $first->transactionType->type));
$title = 1 === $splits ? $first->description : $transactionGroup->title;
$type = (string)trans(sprintf('firefly.%s', $first['transaction_type_type']));
$title = 1 === $splits ? $first['description'] : $selectedGroup['title'];
$subTitle = sprintf('%s: "%s"', $type, $title);
// enrich
$enrichment = new TransactionGroupEnrichment();
$enrichment->setUser($admin);
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters(new ParameterBag());
$groupArray = $transformer->transformObject($transactionGroup);
// do some calculations:
$amounts = $this->getAmounts($groupArray);
$accounts = $this->getAccounts($groupArray);
$amounts = $this->getAmounts($selectedGroup);
$accounts = $this->getAccounts($selectedGroup);
foreach (array_keys($groupArray['transactions']) as $index) {
$groupArray['transactions'][$index]['tags'] = $this->repository->getTagObjects(
(int) $groupArray['transactions'][$index]['transaction_journal_id']
);
foreach (array_keys($selectedGroup['transactions']) as $index) {
$selectedGroup['transactions'][$index]['tags'] = $this->repository->getTagObjects((int)$selectedGroup['transactions'][$index]['transaction_journal_id']);
}
// get audit log entries:
$groupLogEntries = $this->aleRepository->getForObject($transactionGroup);
$logEntries = [];
foreach ($transactionGroup->transactionJournals as $journal) {
$logEntries[$journal->id] = $this->aleRepository->getForObject($journal);
foreach ($selectedGroup['transactions'] as $journal) {
$logEntries[$journal['transaction_journal_id']] = $this->aleRepository->getForId(TransactionJournal::class, $journal['transaction_journal_id']);
}
$events = $this->repository->getPiggyEvents($transactionGroup);
@@ -129,6 +157,7 @@ class ShowController extends Controller
'groupLogEntries',
'subTitle',
'splits',
'selectedGroup',
'groupArray',
'events',
'attachments',
@@ -142,34 +171,38 @@ class ShowController extends Controller
{
$amounts = [];
foreach ($group['transactions'] as $transaction) {
// add normal amount:
$symbol = $transaction['currency_symbol'];
if (!array_key_exists($symbol, $amounts)) {
$amounts[$symbol] = [
'amount' => '0',
'symbol' => $symbol,
'decimal_places' => $transaction['currency_decimal_places'],
];
}
$amounts[$symbol]['amount'] = bcadd($amounts[$symbol]['amount'], (string) $transaction['amount']);
if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount']
&& 0 !== bccomp(
'0',
(string) $transaction['foreign_amount']
)) {
$amounts[$symbol] ??= [
'amount' => '0',
'symbol' => $symbol,
'decimal_places' => $transaction['currency_decimal_places'],
];
$amounts[$symbol]['amount'] = bcadd($amounts[$symbol]['amount'], (string)$transaction['amount']);
// add foreign amount:
if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string)$transaction['foreign_amount'])) {
// same for foreign currency:
$foreignSymbol = $transaction['foreign_currency_symbol'];
if (!array_key_exists($foreignSymbol, $amounts)) {
$amounts[$foreignSymbol] = [
'amount' => '0',
'symbol' => $foreignSymbol,
'decimal_places' => $transaction['foreign_currency_decimal_places'],
];
}
$amounts[$foreignSymbol]['amount'] = bcadd(
$amounts[$foreignSymbol]['amount'],
(string) $transaction['foreign_amount']
);
$amounts[$foreignSymbol] ??= [
'amount' => '0',
'symbol' => $foreignSymbol,
'decimal_places' => $transaction['foreign_currency_decimal_places'],
];
$amounts[$foreignSymbol]['amount'] = bcadd($amounts[$foreignSymbol]['amount'], (string)$transaction['foreign_amount']);
}
// add primary currency amount
if (null !== $transaction['pc_amount'] && $transaction['currency_id'] !== $this->primaryCurrency->id) {
// same for foreign currency:
$primarySymbol = $this->primaryCurrency->symbol;
$amounts[$primarySymbol] ??= [
'amount' => '0',
'symbol' => $this->primaryCurrency->symbol,
'decimal_places' => $this->primaryCurrency->decimal_places,
];
$amounts[$primarySymbol]['amount'] = bcadd($amounts[$primarySymbol]['amount'], (string)$transaction['pc_amount']);
}
}
return $amounts;
@@ -184,16 +217,16 @@ class ShowController extends Controller
foreach ($group['transactions'] as $transaction) {
$accounts['source'][] = [
'type' => $transaction['source_type'],
'id' => $transaction['source_id'],
'name' => $transaction['source_name'],
'iban' => $transaction['source_iban'],
'type' => $transaction['source_account_type'],
'id' => $transaction['source_account_id'],
'name' => $transaction['source_account_name'],
'iban' => $transaction['source_account_iban'],
];
$accounts['destination'][] = [
'type' => $transaction['destination_type'],
'id' => $transaction['destination_id'],
'name' => $transaction['destination_name'],
'iban' => $transaction['destination_iban'],
'type' => $transaction['destination_account_type'],
'id' => $transaction['destination_account_id'],
'name' => $transaction['destination_account_name'],
'iban' => $transaction['destination_account_iban'],
];
}

View File

@@ -25,13 +25,18 @@ declare(strict_types=1);
namespace FireflyIII\Jobs;
use Carbon\Carbon;
use FireflyIII\Events\WarnUserAboutBill;
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
use FireflyIII\Models\Bill;
use FireflyIII\Support\Facades\Navigation;
use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
/**
* Class WarnAboutBills
@@ -63,7 +68,7 @@ class WarnAboutBills implements ShouldQueue
$this->force = false;
app('log')->debug(sprintf('Created new WarnAboutBills("%s")', $this->date->format('Y-m-d')));
Log::debug(sprintf('Created new WarnAboutBills("%s")', $this->date->format('Y-m-d')));
}
/**
@@ -71,36 +76,42 @@ class WarnAboutBills implements ShouldQueue
*/
public function handle(): void
{
app('log')->debug(sprintf('Now at start of WarnAboutBills() job for %s.', $this->date->format('D d M Y')));
$bills = Bill::all();
Log::debug(sprintf('Now at start of WarnAboutBills() job for %s.', $this->date->format('D d M Y')));
foreach (User::all() as $user) {
$bills = $user->bills()->where('active', true)->get();
$overdue = [];
/** @var Bill $bill */
foreach ($bills as $bill) {
app('log')->debug(sprintf('Now checking bill #%d ("%s")', $bill->id, $bill->name));
if ($this->hasDateFields($bill)) {
if ($this->needsWarning($bill, 'end_date')) {
$this->sendWarning($bill, 'end_date');
/** @var Bill $bill */
foreach ($bills as $bill) {
Log::debug(sprintf('Now checking bill #%d ("%s")', $bill->id, $bill->name));
$dates = $this->getDates($bill);
if ($this->needsOverdueAlert($dates)) {
$overdue[] = ['bill' => $bill, 'dates' => $dates];
}
if ($this->needsWarning($bill, 'extension_date')) {
$this->sendWarning($bill, 'extension_date');
if ($this->hasDateFields($bill)) {
if ($this->needsWarning($bill, 'end_date')) {
$this->sendWarning($bill, 'end_date');
}
if ($this->needsWarning($bill, 'extension_date')) {
$this->sendWarning($bill, 'extension_date');
}
}
}
$this->sendOverdueAlerts($user, $overdue);
}
app('log')->debug('Done with handle()');
Log::debug('Done with handle()');
// clear cache:
app('preferences')->mark();
}
private function hasDateFields(Bill $bill): bool
{
if (false === $bill->active) {
app('log')->debug('Bill is not active.');
Log::debug('Bill is not active.');
return false;
}
if (null === $bill->end_date && null === $bill->extension_date) {
app('log')->debug('Bill has no date fields.');
Log::debug('Bill has no date fields.');
return false;
}
@@ -115,7 +126,7 @@ class WarnAboutBills implements ShouldQueue
}
$diff = $this->getDiff($bill, $field);
$list = config('firefly.bill_reminder_periods');
app('log')->debug(sprintf('Difference in days for field "%s" ("%s") is %d day(s)', $field, $bill->{$field}->format('Y-m-d'), $diff));
Log::debug(sprintf('Difference in days for field "%s" ("%s") is %d day(s)', $field, $bill->{$field}->format('Y-m-d'), $diff));
if (in_array($diff, $list, true)) {
return true;
}
@@ -128,13 +139,13 @@ class WarnAboutBills implements ShouldQueue
$today = clone $this->date;
$carbon = clone $bill->{$field};
return (int) $today->diffInDays($carbon);
return (int)$today->diffInDays($carbon);
}
private function sendWarning(Bill $bill, string $field): void
{
$diff = $this->getDiff($bill, $field);
app('log')->debug('Will now send warning!');
Log::debug('Will now send warning!');
event(new WarnUserAboutBill($bill, $field, $diff));
}
@@ -149,4 +160,49 @@ class WarnAboutBills implements ShouldQueue
{
$this->force = $force;
}
private function getDates(Bill $bill): array
{
$start = clone $this->date;
$start = Navigation::startOfPeriod($start, $bill->repeat_freq);
$end = clone $start;
$end = Navigation::endOfPeriod($end, $bill->repeat_freq);
$enrichment = new SubscriptionEnrichment();
$enrichment->setUser($bill->user);
$enrichment->setStart($start);
$enrichment->setEnd($end);
$single = $enrichment->enrichSingle($bill);
return [
'pay_dates' => $single->meta['pay_dates'] ?? [],
'paid_dates' => $single->meta['paid_dates'] ?? [],
];
}
private function needsOverdueAlert(array $dates): bool
{
$count = count($dates['pay_dates']) - count($dates['paid_dates']);
if (0 === $count || 0 === count($dates['pay_dates'])) {
return false;
}
// the earliest date in the list of pay dates must be 48hrs or more ago.
$earliest = new Carbon($dates['pay_dates'][0]);
$earliest->startOfDay();
Log::debug(sprintf('Earliest expected pay date is %s', $earliest->toAtomString()));
$diff = $earliest->diffInDays($this->date);
Log::debug(sprintf('Difference in days is %s', $diff));
if ($diff < 2) {
return false;
}
return true;
}
private function sendOverdueAlerts(User $user, array $overdue): void
{
if (count($overdue) > 0) {
Log::debug(sprintf('Will now send warning about overdue bill for user #%d.', $user->id));
event(new WarnUserAboutOverdueSubscriptions($user, $overdue));
}
}
}

View File

@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Notifications\User;
use Carbon\Carbon;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use NotificationChannels\Pushover\PushoverMessage;
class SubscriptionsOverdueReminder extends Notification
{
use Queueable;
public function __construct(private array $overdue) {}
/**
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
*/
public function toArray(User $notifiable): array
{
return [
];
}
/**
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
*/
public function toMail(User $notifiable): MailMessage
{
// format the data
$info = [];
$count = 0;
foreach ($this->overdue as $item) {
$current = [
'bill' => $item['bill'],
];
$current['pay_dates'] = array_map(
static function (string $date): string {
return new Carbon($date)->isoFormat((string)trans('config.month_and_day_moment_js'));
},
$item['dates']['pay_dates']
);
$info[] = $current;
++$count;
}
return new MailMessage()
->markdown('emails.subscriptions-overdue-warning', ['info' => $info, 'count' => $count])
->subject($this->getSubject())
;
}
private function getSubject(): string
{
if (count($this->overdue) > 1) {
return (string)trans('email.subscriptions_overdue_subject_multi', ['count' => count($this->overdue)]);
}
return (string)trans('email.subscriptions_overdue_subject_single');
}
public function toNtfy(User $notifiable): Message
{
$settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable);
$message = new Message();
$message->topic($settings['ntfy_topic']);
$message->title($this->getSubject());
$message->body((string)trans('email.bill_warning_please_action'));
return $message;
}
/**
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
*/
public function toPushover(User $notifiable): PushoverMessage
{
return PushoverMessage::create((string)trans('email.bill_warning_please_action'))
->title($this->getSubject())
;
}
/**
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
*/
public function toSlack(User $notifiable): SlackMessage
{
$url = route('bills.index');
return new SlackMessage()
->warning()
->attachment(static function ($attachment) use ($url): void {
$attachment->title((string)trans('firefly.visit_bills'), $url);
})
->content($this->getSubject())
;
}
/**
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
*/
public function via(User $notifiable): array
{
return ReturnsAvailableChannels::returnChannels('user', $notifiable);
}
}

View File

@@ -27,6 +27,8 @@ use FireflyIII\Events\ActuallyLoggedIn;
use FireflyIII\Events\Admin\InvitationCreated;
use FireflyIII\Events\DestroyedTransactionGroup;
use FireflyIII\Events\DetectedNewIPAddress;
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated;
@@ -58,7 +60,6 @@ use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Events\UpdatedAccount;
use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Events\UserChangedEmail;
use FireflyIII\Events\WarnUserAboutBill;
use FireflyIII\Handlers\Observer\AccountObserver;
use FireflyIII\Handlers\Observer\AttachmentObserver;
use FireflyIII\Handlers\Observer\AutoBudgetObserver;
@@ -114,150 +115,153 @@ class EventServiceProvider extends ServiceProvider
protected $listen
= [
// is a User related event.
RegisteredUser::class => [
RegisteredUser::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationMail',
'FireflyIII\Handlers\Events\UserEventHandler@sendAdminRegistrationNotification',
'FireflyIII\Handlers\Events\UserEventHandler@attachUserRole',
'FireflyIII\Handlers\Events\UserEventHandler@createGroupMembership',
'FireflyIII\Handlers\Events\UserEventHandler@createExchangeRates',
],
UserAttemptedLogin::class => [
UserAttemptedLogin::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendLoginAttemptNotification',
],
// is a User related event.
Login::class => [
Login::class => [
'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin',
'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish',
],
ActuallyLoggedIn::class => [
ActuallyLoggedIn::class => [
'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress',
],
DetectedNewIPAddress::class => [
DetectedNewIPAddress::class => [
'FireflyIII\Handlers\Events\UserEventHandler@notifyNewIPAddress',
],
RequestedVersionCheckStatus::class => [
RequestedVersionCheckStatus::class => [
'FireflyIII\Handlers\Events\VersionCheckEventHandler@checkForUpdates',
],
RequestedReportOnJournals::class => [
RequestedReportOnJournals::class => [
'FireflyIII\Handlers\Events\AutomationHandler@reportJournals',
],
// is a User related event.
RequestedNewPassword::class => [
RequestedNewPassword::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendNewPassword',
],
UserTestNotificationChannel::class => [
UserTestNotificationChannel::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendTestNotification',
],
// is a User related event.
UserChangedEmail::class => [
UserChangedEmail::class => [
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeConfirmMail',
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeUndoMail',
],
// admin related
OwnerTestNotificationChannel::class => [
OwnerTestNotificationChannel::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendTestNotification',
],
NewVersionAvailable::class => [
NewVersionAvailable::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendNewVersion',
],
InvitationCreated::class => [
InvitationCreated::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendInvitationNotification',
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationInvite',
],
UnknownUserAttemptedLogin::class => [
UnknownUserAttemptedLogin::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendLoginAttemptNotification',
],
// is a Transaction Journal related event.
StoredTransactionGroup::class => [
StoredTransactionGroup::class => [
'FireflyIII\Handlers\Events\StoredGroupEventHandler@runAllHandlers',
],
// is a Transaction Journal related event.
UpdatedTransactionGroup::class => [
UpdatedTransactionGroup::class => [
'FireflyIII\Handlers\Events\UpdatedGroupEventHandler@runAllHandlers',
],
DestroyedTransactionGroup::class => [
DestroyedTransactionGroup::class => [
'FireflyIII\Handlers\Events\DestroyedGroupEventHandler@runAllHandlers',
],
// API related events:
AccessTokenCreated::class => [
AccessTokenCreated::class => [
'FireflyIII\Handlers\Events\APIEventHandler@accessTokenCreated',
],
// Webhook related event:
RequestedSendWebhookMessages::class => [
RequestedSendWebhookMessages::class => [
'FireflyIII\Handlers\Events\WebhookEventHandler@sendWebhookMessages',
],
// account related events:
StoredAccount::class => [
StoredAccount::class => [
'FireflyIII\Handlers\Events\StoredAccountEventHandler@recalculateCredit',
],
UpdatedAccount::class => [
UpdatedAccount::class => [
'FireflyIII\Handlers\Events\UpdatedAccountEventHandler@recalculateCredit',
],
// bill related events:
WarnUserAboutBill::class => [
WarnUserAboutBill::class => [
'FireflyIII\Handlers\Events\BillEventHandler@warnAboutBill',
],
WarnUserAboutOverdueSubscriptions::class => [
'FireflyIII\Handlers\Events\BillEventHandler@warnAboutOverdueSubscriptions',
],
// audit log events:
TriggeredAuditLog::class => [
TriggeredAuditLog::class => [
'FireflyIII\Handlers\Events\AuditEventHandler@storeAuditEvent',
],
// piggy bank related events:
ChangedAmount::class => [
ChangedAmount::class => [
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changePiggyAmount',
],
ChangedName::class => [
ChangedName::class => [
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
],
// budget related events: CRUD budget limit
Created::class => [
Created::class => [
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@created',
],
Updated::class => [
Updated::class => [
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@updated',
],
Deleted::class => [
Deleted::class => [
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
],
// rule actions
RuleActionFailedOnArray::class => [
RuleActionFailedOnArray::class => [
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
],
RuleActionFailedOnObject::class => [
RuleActionFailedOnObject::class => [
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnObject',
],
// security related
EnabledMFA::class => [
EnabledMFA::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAEnabledMail',
],
DisabledMFA::class => [
DisabledMFA::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFADisabledMail',
],
MFANewBackupCodes::class => [
MFANewBackupCodes::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendNewMFABackupCodesMail',
],
MFAUsedBackupCode::class => [
MFAUsedBackupCode::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendUsedBackupCodeMail',
],
MFABackupFewLeft::class => [
MFABackupFewLeft::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupFewLeftMail',
],
MFABackupNoLeft::class => [
MFABackupNoLeft::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupNoLeftMail',
],
MFAManyFailedAttempts::class => [
MFAManyFailedAttempts::class => [
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAFailedAttemptsMail',
],
// preferences
UserGroupChangedPrimaryCurrency::class => [
UserGroupChangedPrimaryCurrency::class => [
'FireflyIII\Handlers\Events\PreferencesEventHandler@resetPrimaryCurrencyAmounts',
],
];

View File

@@ -562,7 +562,13 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
'foreign_currencies.decimal_places as foreign_currency_decimal_places',
// fields
'transaction_journals.date', 'transaction_types.type', 'transaction_journals.transaction_currency_id', 'transactions.amount'])
'transaction_journals.date',
'transaction_types.type',
'transaction_journals.transaction_currency_id',
'transactions.amount',
'transactions.native_amount as pc_amount',
'transactions.foreign_amount',
])
->toArray()
;

View File

@@ -50,10 +50,10 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface
$yesterday = clone $start;
$yesterday->subDay()->endOfDay(); // exactly up until $start but NOT including.
$end->endOfDay(); // needs to be end of day to be correct.
Log::debug(sprintf('getAccountReport: finalAccountsBalance("%s")', $yesterday->format('Y-m-d H:i:s')));
Log::debug(sprintf('getAccountReport: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
$startSet = Steam::finalAccountsBalance($accounts, $yesterday);
$endSet = Steam::finalAccountsBalance($accounts, $end);
Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $yesterday->format('Y-m-d H:i:s')));
Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
$startSet = Steam::accountsBalancesOptimized($accounts, $yesterday);
$endSet = Steam::accountsBalancesOptimized($accounts, $end);
Log::debug('Start of accountreport');
/** @var AccountRepositoryInterface $repository */

View File

@@ -52,4 +52,10 @@ class ALERepository implements ALERepositoryInterface
return $auditLogEntry;
}
public function getForId(string $model, int $modelId): Collection
{
// all Models have an ID.
return AuditLogEntry::where('auditable_id', $modelId)->where('auditable_type', $model)->get();
}
}

View File

@@ -46,5 +46,7 @@ interface ALERepositoryInterface
{
public function getForObject(Model $model): Collection;
public function getForId(string $model, int $modelId): Collection;
public function store(array $data): AuditLogEntry;
}

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

@@ -32,6 +32,7 @@ use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Log;
use JsonException;
use function Safe\json_encode;
@@ -65,9 +66,9 @@ class StandardWebhookSender implements WebhookSenderInterface
try {
$signature = $signatureGenerator->generate($this->message);
} catch (FireflyException $e) {
app('log')->error('Did not send message because of a Firefly III Exception.');
app('log')->error($e->getMessage());
app('log')->error($e->getTraceAsString());
Log::error('Did not send message because of a Firefly III Exception.');
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$attempt = new WebhookAttempt();
$attempt->webhookMessage()->associate($this->message);
$attempt->status_code = 0;
@@ -80,14 +81,14 @@ class StandardWebhookSender implements WebhookSenderInterface
return;
}
app('log')->debug(sprintf('Trying to send webhook message #%d', $this->message->id));
Log::debug(sprintf('Trying to send webhook message #%d', $this->message->id));
try {
$json = json_encode($this->message->message, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
app('log')->error('Did not send message because of a JSON error.');
app('log')->error($e->getMessage());
app('log')->error($e->getTraceAsString());
Log::error('Did not send message because of a JSON error.');
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$attempt = new WebhookAttempt();
$attempt->webhookMessage()->associate($this->message);
$attempt->status_code = 0;
@@ -115,9 +116,9 @@ class StandardWebhookSender implements WebhookSenderInterface
try {
$res = $client->request('POST', $this->message->webhook->url, $options);
} catch (ConnectException|RequestException $e) {
app('log')->error('The webhook could NOT be submitted but Firefly III caught the error below.');
app('log')->error($e->getMessage());
app('log')->error($e->getTraceAsString());
Log::error('The webhook could NOT be submitted but Firefly III caught the error below.');
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$logs = sprintf("%s\n%s", $e->getMessage(), $e->getTraceAsString());
@@ -130,9 +131,9 @@ class StandardWebhookSender implements WebhookSenderInterface
$attempt->status_code = 0;
if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse')) {
$attempt->status_code = $e->hasResponse() ? $e->getResponse()->getStatusCode() : 0;
app('log')->error(sprintf('The status code of the error response is: %d', $attempt->status_code));
Log::error(sprintf('The status code of the error response is: %d', $attempt->status_code));
$body = (string) ($e->hasResponse() ? $e->getResponse()->getBody() : '');
app('log')->error(sprintf('The body of the error response is: %s', $body));
Log::error(sprintf('The body of the error response is: %s', $body));
}
$attempt->logs = $logs;
$attempt->save();
@@ -142,9 +143,9 @@ class StandardWebhookSender implements WebhookSenderInterface
$this->message->sent = true;
$this->message->save();
app('log')->debug(sprintf('Webhook message #%d was sent. Status code %d', $this->message->id, $res->getStatusCode()));
app('log')->debug(sprintf('Webhook request body size: %d bytes', strlen($json)));
app('log')->debug(sprintf('Response body: %s', $res->getBody()));
Log::debug(sprintf('Webhook message #%d was sent. Status code %d', $this->message->id, $res->getStatusCode()));
Log::debug(sprintf('Webhook request body size: %d bytes', strlen($json)));
Log::debug(sprintf('Response body: %s', $res->getBody()));
}
public function setMessage(WebhookMessage $message): void

View File

@@ -29,9 +29,9 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\UserGroup;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Singleton\PreferencesSingleton;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use NumberFormatter;
/**
@@ -116,11 +116,28 @@ class Amount
public function convertToPrimary(?User $user = null): bool
{
$instance = PreferencesSingleton::getInstance();
if (!$user instanceof User) {
return true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled');
$pref = $instance->getPreference('convert_to_primary_no_user');
if (null === $pref) {
$res = true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled');
$instance->setPreference('convert_to_primary_no_user', $res);
return $res;
}
return $pref;
}
$key = sprintf('convert_to_primary_%d', $user->id);
$pref = $instance->getPreference($key);
if (null === $pref) {
$res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled');
$instance->setPreference($key, $res);
return $res;
}
return true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled');
return $pref;
}
public function getPrimaryCurrency(): TransactionCurrency

View File

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

View File

@@ -28,6 +28,7 @@ use Carbon\Carbon;
use FireflyIII\Jobs\CreateAutoBudgetLimits;
use FireflyIII\Models\Configuration;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Support\Facades\Log;
/**
* Class AutoBudgetCronjob
@@ -42,22 +43,22 @@ class AutoBudgetCronjob extends AbstractCronjob
$diff = Carbon::now()->getTimestamp() - $lastTime;
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
if (0 === $lastTime) {
app('log')->info('Auto budget cron-job has never fired before.');
Log::info('Auto budget cron-job has never fired before.');
}
// less than half a day ago:
if ($lastTime > 0 && $diff <= 43200) {
app('log')->info(sprintf('It has been %s since the auto budget cron-job has fired.', $diffForHumans));
Log::info(sprintf('It has been %s since the auto budget cron-job has fired.', $diffForHumans));
if (false === $this->force) {
app('log')->info('The auto budget cron-job will not fire now.');
Log::info('The auto budget cron-job will not fire now.');
$this->message = sprintf('It has been %s since the auto budget cron-job has fired. It will not fire now.', $diffForHumans);
return;
}
app('log')->info('Execution of the auto budget cron-job has been FORCED.');
Log::info('Execution of the auto budget cron-job has been FORCED.');
}
if ($lastTime > 0 && $diff > 43200) {
app('log')->info(sprintf('It has been %s since the auto budget cron-job has fired. It will fire now!', $diffForHumans));
Log::info(sprintf('It has been %s since the auto budget cron-job has fired. It will fire now!', $diffForHumans));
}
$this->fireAutoBudget();
@@ -66,7 +67,7 @@ class AutoBudgetCronjob extends AbstractCronjob
private function fireAutoBudget(): void
{
app('log')->info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d')));
Log::info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d')));
/** @var CreateAutoBudgetLimits $job */
$job = app(CreateAutoBudgetLimits::class, [$this->date]);
@@ -80,6 +81,6 @@ class AutoBudgetCronjob extends AbstractCronjob
$this->message = 'Auto-budget cron job fired successfully.';
FireflyConfig::set('last_ab_job', (int) $this->date->format('U'));
app('log')->info('Done with auto budget cron job task.');
Log::info('Done with auto budget cron job task.');
}
}

View File

@@ -28,6 +28,8 @@ use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Jobs\WarnAboutBills;
use FireflyIII\Models\Configuration;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Support\Facades\Log;
/**
* Class BillWarningCronjob
@@ -39,22 +41,22 @@ class BillWarningCronjob extends AbstractCronjob
*/
public function fire(): void
{
app('log')->debug(sprintf('Now in %s', __METHOD__));
Log::debug(sprintf('Now in %s', __METHOD__));
/** @var Configuration $config */
$config = app('fireflyconfig')->get('last_bw_job', 0);
$config = FireflyConfig::get('last_bw_job', 0);
$lastTime = (int) $config->data;
$diff = Carbon::now()->getTimestamp() - $lastTime;
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
if (0 === $lastTime) {
app('log')->info('The bill notification cron-job has never fired before.');
Log::info('The bill notification cron-job has never fired before.');
}
// less than half a day ago:
if ($lastTime > 0 && $diff <= 43200) {
app('log')->info(sprintf('It has been %s since the bill notification cron-job has fired.', $diffForHumans));
Log::info(sprintf('It has been %s since the bill notification cron-job has fired.', $diffForHumans));
if (false === $this->force) {
app('log')->info('The cron-job will not fire now.');
Log::info('The cron-job will not fire now.');
$this->message = sprintf('It has been %s since the bill notification cron-job has fired. It will not fire now.', $diffForHumans);
$this->jobFired = false;
$this->jobErrored = false;
@@ -63,11 +65,11 @@ class BillWarningCronjob extends AbstractCronjob
return;
}
app('log')->info('Execution of the bill notification cron-job has been FORCED.');
Log::info('Execution of the bill notification cron-job has been FORCED.');
}
if ($lastTime > 0 && $diff > 43200) {
app('log')->info(sprintf('It has been %s since the bill notification cron-job has fired. It will fire now!', $diffForHumans));
Log::info(sprintf('It has been %s since the bill notification cron-job has fired. It will fire now!', $diffForHumans));
}
$this->fireWarnings();
@@ -77,7 +79,7 @@ class BillWarningCronjob extends AbstractCronjob
private function fireWarnings(): void
{
app('log')->info(sprintf('Will now fire bill notification job task for date "%s".', $this->date->format('Y-m-d H:i:s')));
Log::info(sprintf('Will now fire bill notification job task for date "%s".', $this->date->format('Y-m-d H:i:s')));
/** @var WarnAboutBills $job */
$job = app(WarnAboutBills::class);
@@ -91,8 +93,8 @@ class BillWarningCronjob extends AbstractCronjob
$this->jobSucceeded = true;
$this->message = 'Bill notification cron job fired successfully.';
app('fireflyconfig')->set('last_bw_job', (int) $this->date->format('U'));
app('log')->info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U')));
app('log')->info('Done with bill notification cron job task.');
FireflyConfig::set('last_bw_job', (int) $this->date->format('U'));
Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U')));
Log::info('Done with bill notification cron job task.');
}
}

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