Compare commits

..

191 Commits

Author SHA1 Message Date
James Cole
71325de44e Merge branch 'release/v6.0.22' 2023-09-01 19:41:15 +02:00
James Cole
b9d750bf59 Meta data for new release. 2023-09-01 19:40:17 +02:00
James Cole
461249737e Fix #7917, add missing return statement 2023-09-01 15:23:10 +02:00
James Cole
1aeaa8b77d Merge tag 'v6.0.21' into develop
v6.0.21
2023-09-01 05:33:52 +02:00
James Cole
a59d7ccdc2 Merge branch 'release/v6.0.21' 2023-09-01 05:33:50 +02:00
James Cole
2c1ca428db Update meta files for new release. 2023-09-01 05:33:02 +02:00
James Cole
224970f3bd New version 2023-09-01 05:16:58 +02:00
James Cole
742d934ddb Catch missing key. 2023-09-01 04:42:49 +02:00
James Cole
1699513023 Fix test 2023-08-31 19:18:16 +02:00
James Cole
488a8a7e86 Fix test 2023-08-31 19:13:06 +02:00
James Cole
dd571d6221 Fix test 2023-08-31 19:02:38 +02:00
James Cole
4cc5128b4c Use relative urls see what breaks. 2023-08-31 18:45:11 +02:00
James Cole
2636a6557a Less notice events. 2023-08-30 18:03:47 +02:00
James Cole
0bf97ccf22 Expand logging 2023-08-30 15:57:59 +02:00
James Cole
592fc71b4e Change some logging settings. 2023-08-30 15:38:15 +02:00
James Cole
0e7712d3b8 Add more tracing 2023-08-30 11:58:44 +02:00
James Cole
80f21d2a4f Fix test 2023-08-30 07:06:43 +02:00
James Cole
5513ec068e Fix test 2023-08-30 06:53:46 +02:00
James Cole
14a46a6197 Add more info to error message. 2023-08-30 06:40:12 +02:00
James Cole
b93ee5efd8 Sort accounts properly 2023-08-29 18:28:29 +02:00
James Cole
cdd4dc6065 Rebuild frontend 2023-08-29 07:34:41 +02:00
James Cole
0d8b2ae799 Fix https://github.com/firefly-iii/firefly-iii/issues/7910 2023-08-29 05:47:13 +02:00
James Cole
77dc79b638 Merge pull request #7898 from firefly-iii/dependabot/composer/develop/phpstan/phpstan-1.10.32 2023-08-28 05:51:54 +02:00
dependabot[bot]
f48723db40 chore(deps-dev): bump phpstan/phpstan from 1.10.29 to 1.10.32
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.29 to 1.10.32.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.29...1.10.32)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 03:50:38 +00:00
James Cole
ebf91078c5 Merge pull request #7899 from firefly-iii/dependabot/composer/develop/guzzlehttp/guzzle-7.8.0 2023-08-28 05:50:17 +02:00
James Cole
90d0d85dd6 Merge pull request #7900 from firefly-iii/dependabot/composer/develop/laravel/framework-10.20.0 2023-08-28 05:50:06 +02:00
James Cole
0c5b4f7f64 Merge pull request #7901 from firefly-iii/dependabot/composer/develop/laravel/sanctum-3.2.6 2023-08-28 05:49:57 +02:00
James Cole
9c4bb08ed1 Merge pull request #7902 from firefly-iii/dependabot/composer/develop/spatie/laravel-ignition-2.3.0 2023-08-28 05:49:46 +02:00
James Cole
1d3bbde4b0 Merge pull request #7903 from firefly-iii/dependabot/npm_and_yarn/develop/axios-1.5.0 2023-08-28 05:49:36 +02:00
James Cole
b6faee033d Merge pull request #7904 from firefly-iii/dependabot/npm_and_yarn/develop/alpinejs-3.13.0 2023-08-28 05:49:27 +02:00
James Cole
05e0f88d11 Merge pull request #7905 from firefly-iii/dependabot/npm_and_yarn/develop/chart.js-4.4.0 2023-08-28 05:49:17 +02:00
dependabot[bot]
ae9a151140 chore(deps): bump chart.js from 4.3.3 to 4.4.0
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.3.3 to 4.4.0.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.3.3...v4.4.0)

---
updated-dependencies:
- dependency-name: chart.js
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 03:25:21 +00:00
dependabot[bot]
da633e3c62 chore(deps): bump alpinejs from 3.12.3 to 3.13.0
Bumps [alpinejs](https://github.com/alpinejs/alpine/tree/HEAD/packages/alpinejs) from 3.12.3 to 3.13.0.
- [Release notes](https://github.com/alpinejs/alpine/releases)
- [Commits](https://github.com/alpinejs/alpine/commits/v3.13.0/packages/alpinejs)

---
updated-dependencies:
- dependency-name: alpinejs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 03:25:10 +00:00
dependabot[bot]
8cb384d3cf chore(deps-dev): bump axios from 1.4.0 to 1.5.0
Bumps [axios](https://github.com/axios/axios) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 03:24:59 +00:00
dependabot[bot]
de2a34c3ec chore(deps): bump spatie/laravel-ignition from 2.2.0 to 2.3.0
Bumps [spatie/laravel-ignition](https://github.com/spatie/laravel-ignition) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/spatie/laravel-ignition/releases)
- [Changelog](https://github.com/spatie/laravel-ignition/blob/main/CHANGELOG.md)
- [Commits](https://github.com/spatie/laravel-ignition/compare/2.2.0...2.3.0)

---
updated-dependencies:
- dependency-name: spatie/laravel-ignition
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 03:06:43 +00:00
dependabot[bot]
b7c65446a8 chore(deps): bump laravel/sanctum from 3.2.5 to 3.2.6
Bumps [laravel/sanctum](https://github.com/laravel/sanctum) from 3.2.5 to 3.2.6.
- [Release notes](https://github.com/laravel/sanctum/releases)
- [Changelog](https://github.com/laravel/sanctum/blob/3.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/sanctum/compare/v3.2.5...v3.2.6)

---
updated-dependencies:
- dependency-name: laravel/sanctum
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 03:06:31 +00:00
dependabot[bot]
bc648b187c chore(deps): bump laravel/framework from 10.19.0 to 10.20.0
Bumps [laravel/framework](https://github.com/laravel/framework) from 10.19.0 to 10.20.0.
- [Release notes](https://github.com/laravel/framework/releases)
- [Changelog](https://github.com/laravel/framework/blob/10.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/framework/compare/v10.19.0...v10.20.0)

---
updated-dependencies:
- dependency-name: laravel/framework
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 03:06:19 +00:00
dependabot[bot]
4de0828b5d chore(deps): bump guzzlehttp/guzzle from 7.7.0 to 7.8.0
Bumps [guzzlehttp/guzzle](https://github.com/guzzle/guzzle) from 7.7.0 to 7.8.0.
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/7.8/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.7.0...7.8.0)

---
updated-dependencies:
- dependency-name: guzzlehttp/guzzle
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 03:06:01 +00:00
James Cole
b2364d26ec Various color fixes. 2023-08-27 18:45:06 +02:00
James Cole
83d94cb792 Expand cache 2023-08-27 07:45:09 +02:00
James Cole
66cc3f48bc Mild code cleanup. 2023-08-26 18:42:18 +02:00
James Cole
63297c43b7 Possible fix for https://github.com/firefly-iii/firefly-iii/issues/7891 2023-08-26 18:40:23 +02:00
James Cole
5b0637558f Better error for invalid date 2023-08-24 05:53:17 +02:00
James Cole
e9a8e104be Change URLs to relative URLs 2023-08-24 05:47:28 +02:00
James Cole
0f524e7800 Add relative route 2023-08-23 07:05:41 +02:00
James Cole
969e0bccc9 Remove slash 2023-08-23 07:04:47 +02:00
James Cole
ba3e026927 Add base href 2023-08-23 07:02:30 +02:00
James Cole
0c6868d477 Add relative paths to JS 2023-08-23 07:00:09 +02:00
James Cole
7f70cf47ec Fix https://github.com/firefly-iii/firefly-iii/issues/7883 2023-08-22 16:08:19 +02:00
James Cole
fa4492287d Rebuild frontend 2023-08-22 08:28:52 +02:00
James Cole
60809c688e Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2023-08-22 08:25:16 +02:00
James Cole
2404f5299c New code for transaction processing and frontend 2023-08-22 08:25:06 +02:00
James Cole
21a394eaf5 Merge pull request #7873 from firefly-iii/dependabot/composer/develop/doctrine/dbal-3.6.6
chore(deps): bump doctrine/dbal from 3.6.5 to 3.6.6
2023-08-21 05:41:31 +02:00
dependabot[bot]
419d7846c9 chore(deps): bump doctrine/dbal from 3.6.5 to 3.6.6
Bumps [doctrine/dbal](https://github.com/doctrine/dbal) from 3.6.5 to 3.6.6.
- [Release notes](https://github.com/doctrine/dbal/releases)
- [Commits](https://github.com/doctrine/dbal/compare/3.6.5...3.6.6)

---
updated-dependencies:
- dependency-name: doctrine/dbal
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 03:38:59 +00:00
James Cole
0827accc39 Merge pull request #7872 from firefly-iii/dependabot/npm_and_yarn/develop/sass-1.66.1 2023-08-21 05:38:48 +02:00
James Cole
2e53f7d0b7 Merge pull request #7874 from firefly-iii/dependabot/composer/develop/predis/predis-2.2.1 2023-08-21 05:38:27 +02:00
James Cole
bdfcf8ec95 Merge pull request #7875 from firefly-iii/dependabot/composer/develop/laravel/framework-10.19.0 2023-08-21 05:38:18 +02:00
James Cole
6a20170e00 Merge pull request #7876 from firefly-iii/dependabot/composer/develop/phpstan/phpstan-1.10.29 2023-08-21 05:38:00 +02:00
James Cole
0c974f1ff7 Merge pull request #7877 from firefly-iii/dependabot/composer/develop/ergebnis/phpstan-rules-2.1.0 2023-08-21 05:37:48 +02:00
dependabot[bot]
9c098d45c5 chore(deps-dev): bump ergebnis/phpstan-rules from 2.0.0 to 2.1.0
Bumps [ergebnis/phpstan-rules](https://github.com/ergebnis/phpstan-rules) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/ergebnis/phpstan-rules/releases)
- [Changelog](https://github.com/ergebnis/phpstan-rules/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ergebnis/phpstan-rules/compare/2.0.0...2.1.0)

---
updated-dependencies:
- dependency-name: ergebnis/phpstan-rules
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 03:25:35 +00:00
dependabot[bot]
cfdf9aa8dc chore(deps-dev): bump phpstan/phpstan from 1.10.28 to 1.10.29
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.28 to 1.10.29.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.28...1.10.29)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 03:25:31 +00:00
dependabot[bot]
2ea74542e6 chore(deps): bump laravel/framework from 10.18.0 to 10.19.0
Bumps [laravel/framework](https://github.com/laravel/framework) from 10.18.0 to 10.19.0.
- [Release notes](https://github.com/laravel/framework/releases)
- [Changelog](https://github.com/laravel/framework/blob/10.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/framework/compare/v10.18.0...v10.19.0)

---
updated-dependencies:
- dependency-name: laravel/framework
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 03:25:26 +00:00
dependabot[bot]
7fcaa2b5fb chore(deps): bump predis/predis from 2.2.0 to 2.2.1
Bumps [predis/predis](https://github.com/predis/predis) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/predis/predis/releases)
- [Changelog](https://github.com/predis/predis/blob/v2.x/CHANGELOG.md)
- [Commits](https://github.com/predis/predis/compare/v2.2.0...v2.2.1)

---
updated-dependencies:
- dependency-name: predis/predis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 03:25:12 +00:00
dependabot[bot]
e9e905e495 chore(deps-dev): bump sass from 1.65.1 to 1.66.1
Bumps [sass](https://github.com/sass/dart-sass) from 1.65.1 to 1.66.1.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.65.1...1.66.1)

---
updated-dependencies:
- dependency-name: sass
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 03:12:29 +00:00
James Cole
f67ff98d78 More exact search for available budgets. #7853 2023-08-15 18:37:47 +02:00
James Cole
e3c4dde4ff Add cleanup method for issue #7853 and some more debug info. 2023-08-15 17:57:02 +02:00
James Cole
9787561000 Potential solution for #7853 2023-08-15 17:19:50 +02:00
James Cole
cd041b4c75 Add debug info for #7853 2023-08-15 16:22:54 +02:00
James Cole
60ee70c926 Check for rule triggers in #7853 2023-08-15 16:17:54 +02:00
James Cole
00de78b6f1 Also support Discord. 2023-08-15 13:52:30 +02:00
James Cole
33a841b831 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2023-08-15 13:51:51 +02:00
James Cole
df66dcf102 Various UI fixes 2023-08-15 13:51:38 +02:00
James Cole
086a7a0b1e Merge pull request #7848 from firefly-iii/dependabot/npm_and_yarn/develop/laravel-vite-plugin-0.8.0 2023-08-14 06:58:21 +02:00
dependabot[bot]
ec0ba3d212 chore(deps-dev): bump laravel-vite-plugin from 0.7.8 to 0.8.0
Bumps [laravel-vite-plugin](https://github.com/laravel/vite-plugin) from 0.7.8 to 0.8.0.
- [Release notes](https://github.com/laravel/vite-plugin/releases)
- [Changelog](https://github.com/laravel/vite-plugin/blob/main/CHANGELOG.md)
- [Commits](https://github.com/laravel/vite-plugin/compare/v0.7.8...v0.8.0)

---
updated-dependencies:
- dependency-name: laravel-vite-plugin
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-14 03:58:10 +00:00
James Cole
b0ab06b7eb Add rule error reporting to all rules. 2023-08-13 15:01:12 +02:00
James Cole
965acd6d45 Finetune index. 2023-08-13 14:30:16 +02:00
James Cole
4d156870ef Add nonce to scripts 2023-08-13 07:30:19 +02:00
James Cole
499720df46 Fix math issue in summary controller. 2023-08-13 07:24:51 +02:00
James Cole
610bc108e7 Add some slack notifications and a todo to fix the rest 2023-08-12 20:57:51 +02:00
James Cole
53f1b0218c Merge tag 'v6.0.20' into develop
v6.0.20
2023-08-12 19:56:11 +02:00
James Cole
0a7a099796 Merge branch 'release/v6.0.20' 2023-08-12 19:56:07 +02:00
James Cole
ed90ebc896 Update final meta 2023-08-12 19:55:31 +02:00
James Cole
87fe518d45 Rebuild JS and CSS 2023-08-12 19:41:59 +02:00
James Cole
5cb0891a27 Forgot to add a date range 2023-08-12 19:21:43 +02:00
James Cole
b4bd66bd58 Various fixes. 2023-08-12 18:21:29 +02:00
James Cole
64d95fe845 Add integrity things to JS things. 2023-08-12 18:12:20 +02:00
James Cole
a2d6d7a92c Add nonce. 2023-08-12 18:09:23 +02:00
James Cole
1d8a784586 Update meta files for new release. 2023-08-12 18:01:51 +02:00
James Cole
3c8bfae8ff Update API version 2023-08-12 17:47:13 +02:00
James Cole
f65eccfe4c Fix https://github.com/firefly-iii/firefly-iii/issues/7835 2023-08-12 17:46:56 +02:00
James Cole
7ce6dc9f16 Merge branch 'adminlte4' into develop 2023-08-12 17:44:08 +02:00
James Cole
4a73caf4c3 Rebuild frontend 2023-08-12 17:44:00 +02:00
James Cole
1b9d8dd3c3 Fix #7845 among other things 2023-08-12 17:43:24 +02:00
James Cole
b11bfb0aae update frontend 2023-08-12 17:41:56 +02:00
James Cole
024e16bf4b Cleanup charts 2023-08-12 07:53:11 +02:00
James Cole
daf753d76e Merge branch 'develop' into adminlte4 2023-08-11 19:43:18 +02:00
James Cole
4c90f66578 Minor code cleanup 2023-08-11 19:40:44 +02:00
James Cole
db94f18d46 Clean up migrations 2023-08-11 19:37:52 +02:00
James Cole
a9bd0f551d Make sure all objects also add user group 2023-08-11 19:37:28 +02:00
James Cole
e1ba2d9ad9 New translations 2023-08-11 06:24:50 +02:00
James Cole
939c636a74 Add new action that can switch the source and destination account of a transfer 2023-08-11 06:16:40 +02:00
James Cole
2b829cb645 Code cleanup 2023-08-11 06:05:02 +02:00
James Cole
4bbc898639 Expand bill API and v2 account API 2023-08-11 06:04:03 +02:00
James Cole
27d07d5807 Expand views 2023-08-11 06:03:31 +02:00
James Cole
f010ffefc1 Expand with list of piggy banks 2023-08-10 19:51:36 +02:00
James Cole
fc0fee161e Merge branch 'develop' into adminlte4 2023-08-09 14:14:57 +02:00
James Cole
72c99d3834 New API endpoints + transformer 2023-08-09 14:14:44 +02:00
James Cole
a2984f299b Extend API 2023-08-09 14:14:33 +02:00
James Cole
3925077223 Expand views 2023-08-09 14:13:32 +02:00
James Cole
deb51bd8de Merge branch 'develop' into adminlte4 2023-08-08 16:45:23 +02:00
James Cole
03f39f53d8 Fix nullpointer 2023-08-08 15:55:48 +02:00
James Cole
e72314778c Fix sankey chart 2023-08-08 15:55:26 +02:00
James Cole
ced3e9387a Merge branch 'develop' into adminlte4 2023-08-08 15:04:10 +02:00
James Cole
02c906afe6 Expand API to return transaction groups. 2023-08-08 15:03:58 +02:00
James Cole
7a2d4c9bd2 Log as error, not debug 2023-08-08 14:11:25 +02:00
James Cole
dffddfda18 Expand layout 2023-08-08 14:11:04 +02:00
James Cole
9bb62c865a Merge branch 'develop' into adminlte4 2023-08-07 19:05:41 +02:00
James Cole
a97ebf2b97 Move to chartJS 2023-08-07 19:05:23 +02:00
James Cole
9a62a6c514 Merge pull request #7832 from firefly-iii/dependabot/composer/develop/phpstan/phpstan-deprecation-rules-1.1.4
chore(deps-dev): bump phpstan/phpstan-deprecation-rules from 1.1.3 to 1.1.4
2023-08-07 05:52:09 +02:00
James Cole
04dc162270 Merge pull request #7831 from firefly-iii/dependabot/composer/develop/laravel/framework-10.17.1
chore(deps): bump laravel/framework from 10.16.1 to 10.17.1
2023-08-07 05:51:59 +02:00
James Cole
75d5a50328 Merge pull request #7830 from firefly-iii/dependabot/composer/develop/phpstan/phpstan-1.10.27
chore(deps-dev): bump phpstan/phpstan from 1.10.26 to 1.10.27
2023-08-07 05:51:51 +02:00
James Cole
eadb7d5dcb Merge pull request #7829 from firefly-iii/dependabot/composer/develop/mockery/mockery-1.6.5
chore(deps-dev): bump mockery/mockery from 1.6.4 to 1.6.5
2023-08-07 05:51:42 +02:00
James Cole
8dc07f1148 Merge pull request #7828 from firefly-iii/dependabot/composer/develop/league/csv-9.10.0
chore(deps): bump league/csv from 9.9.0 to 9.10.0
2023-08-07 05:51:32 +02:00
dependabot[bot]
217ce85a9b chore(deps-dev): bump phpstan/phpstan-deprecation-rules
Bumps [phpstan/phpstan-deprecation-rules](https://github.com/phpstan/phpstan-deprecation-rules) from 1.1.3 to 1.1.4.
- [Release notes](https://github.com/phpstan/phpstan-deprecation-rules/releases)
- [Commits](https://github.com/phpstan/phpstan-deprecation-rules/compare/1.1.3...1.1.4)

---
updated-dependencies:
- dependency-name: phpstan/phpstan-deprecation-rules
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-07 03:26:49 +00:00
dependabot[bot]
ef88b25dbd chore(deps): bump laravel/framework from 10.16.1 to 10.17.1
Bumps [laravel/framework](https://github.com/laravel/framework) from 10.16.1 to 10.17.1.
- [Release notes](https://github.com/laravel/framework/releases)
- [Changelog](https://github.com/laravel/framework/blob/10.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/framework/compare/v10.16.1...v10.17.1)

---
updated-dependencies:
- dependency-name: laravel/framework
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-07 03:26:44 +00:00
dependabot[bot]
4c2e2a3a5a chore(deps-dev): bump phpstan/phpstan from 1.10.26 to 1.10.27
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.26 to 1.10.27.
- [Release notes](https://github.com/phpstan/phpstan/releases)
- [Changelog](https://github.com/phpstan/phpstan/blob/1.11.x/CHANGELOG.md)
- [Commits](https://github.com/phpstan/phpstan/compare/1.10.26...1.10.27)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-07 03:26:29 +00:00
dependabot[bot]
1e8e1c6e51 chore(deps-dev): bump mockery/mockery from 1.6.4 to 1.6.5
Bumps [mockery/mockery](https://github.com/mockery/mockery) from 1.6.4 to 1.6.5.
- [Release notes](https://github.com/mockery/mockery/releases)
- [Changelog](https://github.com/mockery/mockery/blob/1.6.x/CHANGELOG.md)
- [Commits](https://github.com/mockery/mockery/compare/1.6.4...1.6.5)

---
updated-dependencies:
- dependency-name: mockery/mockery
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-07 03:26:22 +00:00
dependabot[bot]
a92944786c chore(deps): bump league/csv from 9.9.0 to 9.10.0
Bumps [league/csv](https://github.com/thephpleague/csv) from 9.9.0 to 9.10.0.
- [Release notes](https://github.com/thephpleague/csv/releases)
- [Changelog](https://github.com/thephpleague/csv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/thephpleague/csv/compare/9.9.0...9.10.0)

---
updated-dependencies:
- dependency-name: league/csv
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-07 03:26:17 +00:00
James Cole
8ce760a0bf Merge branch 'develop' into adminlte4
# Conflicts:
#	app/Api/V2/Controllers/Chart/CategoryController.php
2023-08-06 19:40:09 +02:00
James Cole
8d8e047d2c New charts 2023-08-06 19:37:51 +02:00
James Cole
06a923db94 Expand view 2023-08-06 19:36:55 +02:00
James Cole
551408b801 Expand layout 2023-08-06 18:33:29 +02:00
James Cole
e1915e365a Merge branch 'develop' into adminlte4
# Conflicts:
#	config/cors.php
#	config/hashing.php
2023-08-06 11:25:20 +02:00
James Cole
e8c4eec536 New API code for boxes. 2023-08-06 11:22:36 +02:00
James Cole
e3f4e75561 Fix boxes api 2023-08-06 11:21:20 +02:00
James Cole
fd640f9698 Merge branch 'develop' into adminlte4 2023-08-06 07:08:33 +02:00
James Cole
ffd8aef35f Expand API with v2 summary endpoint. 2023-08-06 07:04:09 +02:00
James Cole
46412bdc66 Fix #7810 2023-08-06 07:03:39 +02:00
James Cole
d73c3476c8 Expand layout 2023-08-06 07:02:55 +02:00
James Cole
ff737ae05e Clean up views, add top bar 2023-08-05 06:34:28 +02:00
James Cole
171ec0c630 Merge branch 'develop' into adminlte4 2023-08-05 06:19:53 +02:00
James Cole
b4a6c6fcbe Fix https://github.com/firefly-iii/firefly-iii/issues/7818 2023-08-04 19:11:01 +02:00
James Cole
4886084296 Add stuff 2023-08-04 19:10:49 +02:00
James Cole
1a3dfbdef6 Merge branch 'develop' into adminlte4 2023-08-01 19:55:08 +02:00
James Cole
0c087f33c2 Build administration-compatible budget chart. 2023-08-01 19:38:53 +02:00
James Cole
5f9f621fa6 Small chart fixes 2023-08-01 12:28:11 +02:00
James Cole
cd3de7b545 Title fix 2023-08-01 12:27:55 +02:00
James Cole
8aeb513d54 Merge branch 'develop' into adminlte4 2023-08-01 12:11:45 +02:00
James Cole
bfc5c5d154 Fix API version and match API 2023-08-01 11:15:19 +02:00
James Cole
7c9f7f04b7 Chart field name refactor 2023-08-01 10:26:26 +02:00
James Cole
3352c2cf3c JS rebuild 2023-08-01 10:26:09 +02:00
James Cole
e61a433999 Merge branch 'develop' into adminlte4 2023-08-01 09:54:40 +02:00
James Cole
3517452ea1 Expand upgrade command 2023-08-01 09:54:09 +02:00
James Cole
8a1190b9ee Update packages 2023-08-01 09:53:51 +02:00
James Cole
fc78c9a1d6 Merge branch 'develop' into adminlte4
# Conflicts:
#	yarn.lock
2023-08-01 09:47:30 +02:00
James Cole
8d55f7f2e5 Clean up code 2023-08-01 09:47:06 +02:00
James Cole
8162b22d43 Fix chart 2023-08-01 09:42:59 +02:00
James Cole
8504d55f17 First code for new balance chart 2023-08-01 09:27:39 +02:00
James Cole
75ae1bbde1 Show expanded transactions in all account overviews. 2023-08-01 06:24:26 +02:00
James Cole
a20668e91b Fix #7771 2023-07-31 20:25:47 +02:00
James Cole
eb559bbb03 Merge pull request #7803 from zqye/main 2023-07-31 19:11:46 +02:00
zqye
2eae96a895 exchangeRatesCronJob should only be fired when ENABLE_EXTERNAL_RATES is true. 2023-07-31 10:04:16 -07:00
James Cole
4bbf35bca4 Merge pull request #7801 from firefly-iii/dependabot/composer/develop/symfony/http-client-6.3.2 2023-07-31 06:40:19 +02:00
dependabot[bot]
f1b5e45488 chore(deps): bump symfony/http-client from 6.3.1 to 6.3.2
Bumps [symfony/http-client](https://github.com/symfony/http-client) from 6.3.1 to 6.3.2.
- [Release notes](https://github.com/symfony/http-client/releases)
- [Changelog](https://github.com/symfony/http-client/blob/6.3/CHANGELOG.md)
- [Commits](https://github.com/symfony/http-client/compare/v6.3.1...v6.3.2)

---
updated-dependencies:
- dependency-name: symfony/http-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 04:39:38 +00:00
James Cole
57ed1581ec Merge pull request #7800 from firefly-iii/dependabot/composer/develop/nunomaduro/larastan-2.6.4 2023-07-31 06:39:04 +02:00
James Cole
c668381ab0 Merge pull request #7802 from firefly-iii/dependabot/composer/develop/symfony/mailgun-mailer-6.3.2 2023-07-31 06:38:50 +02:00
dependabot[bot]
44f99991fc chore(deps): bump symfony/mailgun-mailer from 6.3.0 to 6.3.2
Bumps [symfony/mailgun-mailer](https://github.com/symfony/mailgun-mailer) from 6.3.0 to 6.3.2.
- [Release notes](https://github.com/symfony/mailgun-mailer/releases)
- [Changelog](https://github.com/symfony/mailgun-mailer/blob/6.3/CHANGELOG.md)
- [Commits](https://github.com/symfony/mailgun-mailer/compare/v6.3.0...v6.3.2)

---
updated-dependencies:
- dependency-name: symfony/mailgun-mailer
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 03:42:42 +00:00
dependabot[bot]
696c76102b chore(deps-dev): bump nunomaduro/larastan from 2.6.3 to 2.6.4
Bumps [nunomaduro/larastan](https://github.com/nunomaduro/larastan) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/nunomaduro/larastan/releases)
- [Changelog](https://github.com/nunomaduro/larastan/blob/master/RELEASE.md)
- [Commits](https://github.com/nunomaduro/larastan/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: nunomaduro/larastan
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 03:42:28 +00:00
James Cole
fe4e00dc5c Fix #7792 2023-07-29 06:52:21 +02:00
James Cole
1a1b0ee27d Fix https://github.com/firefly-iii/firefly-iii/issues/7787 2023-07-29 06:17:09 +02:00
James Cole
298c4ec654 Merge tag 'v6.0.19' into develop
v6.0.19
2023-07-28 16:17:25 +02:00
James Cole
b2084a94e5 Expand layout 2023-07-26 07:08:00 +02:00
James Cole
ddcb246955 Add options 2023-07-26 07:07:53 +02:00
James Cole
8796168580 Merge branch 'develop' into adminlte4
# Conflicts:
#	yarn.lock
2023-07-25 16:12:58 +02:00
James Cole
a6503fda39 Lots of new code. 2023-07-24 18:58:35 +02:00
James Cole
f1173263b6 Fall back on default layout if no page is present (yet) 2023-07-23 08:33:37 +02:00
James Cole
68da1a7039 Rebuild css and js 2023-07-23 08:31:41 +02:00
James Cole
64414edf58 Rename and move files 2023-07-23 08:28:57 +02:00
James Cole
6b136d2c8c Remove unused assets 2023-07-23 08:26:41 +02:00
James Cole
752a877b91 Move more to v2 2023-07-23 08:26:12 +02:00
James Cole
d11c36e476 Merge branch 'develop' into adminlte4 2023-07-23 08:20:39 +02:00
James Cole
b13232f06b Rename some things from v4 to v2 2023-07-23 08:18:42 +02:00
James Cole
be531d777e Continued work on v4 2023-07-23 07:10:31 +02:00
James Cole
190508fa54 Basic set of stuff working! 2023-07-22 16:42:33 +02:00
James Cole
2c2dddc071 It works! 2023-07-22 07:51:02 +02:00
James Cole
c60858de5c Merge branch 'develop' into adminlte4
# Conflicts:
#	composer.lock
#	yarn.lock
2023-07-21 18:26:19 +02:00
James Cole
e58da3c41d More work on v4 2023-07-15 15:25:04 +02:00
James Cole
ab3dbf9218 Various fixes 2023-07-14 06:04:48 +02:00
James Cole
d943a5ae9b A little further ahead with the app in Alpine 2023-07-12 07:07:06 +02:00
James Cole
449058dad7 Get the basics working 2023-07-11 14:42:57 +02:00
James Cole
b9cf8b3ef2 Initial resources kit. 2023-07-11 11:45:55 +02:00
497 changed files with 34328 additions and 10160 deletions

View File

@@ -79,16 +79,16 @@
},
{
"name": "composer/semver",
"version": "3.3.2",
"version": "3.4.0",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9"
"reference": "35e8d0af4486141bc745f23a29cc2091eb624a32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9",
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9",
"url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32",
"reference": "35e8d0af4486141bc745f23a29cc2091eb624a32",
"shasum": ""
},
"require": {
@@ -138,9 +138,9 @@
"versioning"
],
"support": {
"irc": "irc://irc.freenode.org/composer",
"irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.3.2"
"source": "https://github.com/composer/semver/tree/3.4.0"
},
"funding": [
{
@@ -156,7 +156,7 @@
"type": "tidelift"
}
],
"time": "2022-04-01T19:23:25+00:00"
"time": "2023-08-31T09:50:34+00:00"
},
{
"name": "composer/xdebug-handler",
@@ -224,178 +224,23 @@
],
"time": "2022-02-25T21:32:43+00:00"
},
{
"name": "doctrine/annotations",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"shasum": ""
},
"require": {
"doctrine/lexer": "^2 || ^3",
"ext-tokenizer": "*",
"php": "^7.2 || ^8.0",
"psr/cache": "^1 || ^2 || ^3"
},
"require-dev": {
"doctrine/cache": "^2.0",
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.8.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"symfony/cache": "^5.4 || ^6",
"vimeo/psalm": "^4.10"
},
"suggest": {
"php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Docblock Annotations Parser",
"homepage": "https://www.doctrine-project.org/projects/annotations.html",
"keywords": [
"annotations",
"docblock",
"parser"
],
"support": {
"issues": "https://github.com/doctrine/annotations/issues",
"source": "https://github.com/doctrine/annotations/tree/2.0.1"
},
"time": "2023-02-02T22:02:53+00:00"
},
{
"name": "doctrine/lexer",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "84a527db05647743d50373e0ec53a152f2cde568"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568",
"reference": "84a527db05647743d50373e0ec53a152f2cde568",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^9.5",
"psalm/plugin-phpunit": "^0.18.3",
"vimeo/psalm": "^5.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Lexer\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
"keywords": [
"annotations",
"docblock",
"lexer",
"parser",
"php"
],
"support": {
"issues": "https://github.com/doctrine/lexer/issues",
"source": "https://github.com/doctrine/lexer/tree/3.0.0"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
"type": "tidelift"
}
],
"time": "2022-12-15T16:57:16+00:00"
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.22.0",
"version": "v3.25.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3"
"reference": "9025b7d2b6e1d90a63d0ac0905018ce5d03ec88d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/92b019f6c8d79aa26349d0db7671d37440dc0ff3",
"reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/9025b7d2b6e1d90a63d0ac0905018ce5d03ec88d",
"reference": "9025b7d2b6e1d90a63d0ac0905018ce5d03ec88d",
"shasum": ""
},
"require": {
"composer/semver": "^3.3",
"composer/xdebug-handler": "^3.0.3",
"doctrine/annotations": "^2",
"doctrine/lexer": "^2 || ^3",
"ext-json": "*",
"ext-tokenizer": "*",
"php": "^7.4 || ^8.0",
@@ -464,7 +309,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.22.0"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.25.0"
},
"funding": [
{
@@ -472,56 +317,7 @@
"type": "github"
}
],
"time": "2023-07-16T23:08:06+00:00"
},
{
"name": "psr/cache",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/cache.git",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for caching libraries",
"keywords": [
"cache",
"psr",
"psr-6"
],
"support": {
"source": "https://github.com/php-fig/cache/tree/3.0.0"
},
"time": "2021-02-03T23:26:27+00:00"
"time": "2023-08-31T21:27:18+00:00"
},
{
"name": "psr/container",
@@ -745,16 +541,16 @@
},
{
"name": "symfony/console",
"version": "v6.3.0",
"version": "v6.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7"
"reference": "eca495f2ee845130855ddf1cf18460c38966c8b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
"reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
"url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6",
"reference": "eca495f2ee845130855ddf1cf18460c38966c8b6",
"shasum": ""
},
"require": {
@@ -815,7 +611,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.3.0"
"source": "https://github.com/symfony/console/tree/v6.3.4"
},
"funding": [
{
@@ -831,7 +627,7 @@
"type": "tidelift"
}
],
"time": "2023-05-29T12:49:39+00:00"
"time": "2023-08-16T10:10:12+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -902,16 +698,16 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v6.3.0",
"version": "v6.3.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa"
"reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa",
"reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e",
"reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e",
"shasum": ""
},
"require": {
@@ -962,7 +758,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/event-dispatcher/tree/v6.3.0"
"source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2"
},
"funding": [
{
@@ -978,7 +774,7 @@
"type": "tidelift"
}
],
"time": "2023-04-21T14:41:17+00:00"
"time": "2023-07-06T06:56:43+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@@ -1121,16 +917,16 @@
},
{
"name": "symfony/finder",
"version": "v6.3.0",
"version": "v6.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2"
"reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/d9b01ba073c44cef617c7907ce2419f8d00d75e2",
"reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2",
"url": "https://api.github.com/repos/symfony/finder/zipball/9915db259f67d21eefee768c1abcf1cc61b1fc9e",
"reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e",
"shasum": ""
},
"require": {
@@ -1165,7 +961,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/finder/tree/v6.3.0"
"source": "https://github.com/symfony/finder/tree/v6.3.3"
},
"funding": [
{
@@ -1181,7 +977,7 @@
"type": "tidelift"
}
],
"time": "2023-04-02T01:25:41+00:00"
"time": "2023-07-31T08:31:44+00:00"
},
{
"name": "symfony/options-resolver",
@@ -1252,16 +1048,16 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"shasum": ""
},
"require": {
@@ -1276,7 +1072,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1314,7 +1110,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
},
"funding": [
{
@@ -1330,20 +1126,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354"
"reference": "875e90aeea2777b6f135677f618529449334a612"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
"reference": "875e90aeea2777b6f135677f618529449334a612",
"shasum": ""
},
"require": {
@@ -1355,7 +1151,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1395,7 +1191,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
},
"funding": [
{
@@ -1411,20 +1207,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"shasum": ""
},
"require": {
@@ -1436,7 +1232,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1479,7 +1275,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
},
"funding": [
{
@@ -1495,20 +1291,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
"reference": "42292d99c55abe617799667f454222c54c60e229"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": ""
},
"require": {
@@ -1523,7 +1319,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1562,7 +1358,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
},
"funding": [
{
@@ -1578,20 +1374,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-07-28T09:04:16+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": ""
},
"require": {
@@ -1600,7 +1396,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1645,7 +1441,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
},
"funding": [
{
@@ -1661,20 +1457,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-php81",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
"reference": "7581cd600fa9fd681b797d00b02f068e2f13263b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b",
"reference": "7581cd600fa9fd681b797d00b02f068e2f13263b",
"shasum": ""
},
"require": {
@@ -1683,7 +1479,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1724,7 +1520,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0"
},
"funding": [
{
@@ -1740,20 +1536,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/process",
"version": "v6.3.0",
"version": "v6.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628"
"reference": "0b5c29118f2e980d455d2e34a5659f4579847c54"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/8741e3ed7fe2e91ec099e02446fb86667a0f1628",
"reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628",
"url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54",
"reference": "0b5c29118f2e980d455d2e34a5659f4579847c54",
"shasum": ""
},
"require": {
@@ -1785,7 +1581,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v6.3.0"
"source": "https://github.com/symfony/process/tree/v6.3.4"
},
"funding": [
{
@@ -1801,7 +1597,7 @@
"type": "tidelift"
}
],
"time": "2023-05-19T08:06:44+00:00"
"time": "2023-08-07T10:39:22+00:00"
},
{
"name": "symfony/service-contracts",
@@ -1949,16 +1745,16 @@
},
{
"name": "symfony/string",
"version": "v6.3.0",
"version": "v6.3.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f"
"reference": "53d1a83225002635bca3482fcbf963001313fb68"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
"reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
"url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68",
"reference": "53d1a83225002635bca3482fcbf963001313fb68",
"shasum": ""
},
"require": {
@@ -2015,7 +1811,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.3.0"
"source": "https://github.com/symfony/string/tree/v6.3.2"
},
"funding": [
{
@@ -2031,7 +1827,7 @@
"type": "tidelift"
}
],
"time": "2023-03-21T21:06:29+00:00"
"time": "2023-07-05T08:41:27+00:00"
}
],
"packages-dev": [],

18
.editorconfig Normal file
View File

@@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4

View File

@@ -45,12 +45,6 @@ TRUSTED_PROXIES=
# Default setting 'stack' will log to 'daily' and to 'stdout' at the same time.
LOG_CHANNEL=stack
#
# Used when logging to papertrail:
#
PAPERTRAIL_HOST=
PAPERTRAIL_PORT=
# Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
@@ -58,8 +52,30 @@ PAPERTRAIL_PORT=
APP_LOG_LEVEL=notice
# Audit log level.
# Set this to "emergency" if you dont want to store audit logs, leave on info otherwise.
AUDIT_LOG_LEVEL=info
# The audit log is used to log notable Firefly III events on a separate channel.
# These log entries may contain sensitive financial information.
# The audit log is disabled by default.
#
# To enable it, set AUDIT_LOG_LEVEL to "info"
# To disable it, set AUDIT_LOG_LEVEL to "emergency"
AUDIT_LOG_LEVEL=emergency
#
# If you want, you can redirect the audit logs to another channel.
# Set 'audit_stdout', 'audit_syslog', 'audit_errorlog' to log to the system itself.
# Use audit_daily to log to a rotating file.
# Use audit_papertrail to log to papertrail.
#
# If you do this, the audit logs may be mixed with normal logs because the settings for these channels
# are often the same as the settings for the normal logs.
AUDIT_LOG_CHANNEL=
#
# Used when logging to papertrail:
# Also used when audit logs log to papertrail:
#
PAPERTRAIL_HOST=
PAPERTRAIL_PORT=
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
# For other database types, please see the FAQ: https://docs.firefly-iii.org/firefly-iii/faq/self-hosted/#i-want-to-use-sqlite

17
.gitattributes vendored
View File

@@ -1,8 +1,11 @@
* text=auto
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
/tests export-ignore
/phpunit.xml export-ignore
/.ci export-ignore
.styleci.yml export-ignore

View File

@@ -102,6 +102,7 @@ abstract class Controller extends BaseController
} catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field));
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$value = null;
}
$obj = null;
@@ -130,6 +131,7 @@ abstract class Controller extends BaseController
} catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $integer));
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$value = null;
}
if (null !== $value) {
@@ -154,6 +156,7 @@ abstract class Controller extends BaseController
} catch (BadRequestException $e) {
Log::error('Request field "sort" contains a non-scalar value. Value set to NULL.');
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$param = '';
}
if ('' === $param) {

View File

@@ -78,7 +78,8 @@ class UpdateController extends Controller
public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse
{
Log::debug('Now in update routine for transaction group!');
$data = $request->getAll();
$data = $request->getAll();
$transactionGroup = $this->groupRepository->update($transactionGroup, $data);
$manager = $this->getManager();

View File

@@ -322,16 +322,11 @@ class BasicController extends Controller
'currency_decimal_places' => $row['currency_decimal_places'],
'value_parsed' => app('amount')->formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $leftToSpend, false),
'local_icon' => 'money',
'sub_title' => (string)trans(
'firefly.box_spend_per_day',
[
'amount' => app('amount')->formatFlat(
$row['currency_symbol'],
$row['currency_decimal_places'],
$perDay,
false
),
]
'sub_title' => app('amount')->formatFlat(
$row['currency_symbol'],
$row['currency_decimal_places'],
$perDay,
false
),
];
}

View File

@@ -58,7 +58,7 @@ class CronController extends Controller
$return = [];
$return['recurring_transactions'] = $this->runRecurring($config['force'], $config['date']);
$return['auto_budgets'] = $this->runAutoBudget($config['force'], $config['date']);
if (true === config('cer.enabled')) {
if (true === config('cer.download_enabled')) {
$return['exchange_rates'] = $this->exchangeRatesCronJob($config['force'], $config['date']);
}
$return['bill_warnings'] = $this->billWarningCronJob($config['force'], $config['date']);

View File

@@ -33,7 +33,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter;
use Illuminate\Http\JsonResponse;
use JsonException;
/**
* Class AccountController
@@ -65,12 +64,16 @@ class AccountController extends Controller
/**
* Documentation for this endpoint:
* TODO endpoint is not documented.
* TODO list of checks
* 1. use dates from ParameterBag
* 2. Request validates dates
* 3. Request includes user_group_id as administration_id
* 4. Endpoint is documented.
* 5. Collector uses administration_id
*
* @param AutocompleteRequest $request
*
* @return JsonResponse
* @throws JsonException
* @throws FireflyException
* @throws FireflyException
*/
@@ -79,7 +82,7 @@ class AccountController extends Controller
$data = $request->getData();
$types = $data['types'];
$query = $data['query'];
$date = $data['date'] ?? today(config('app.timezone'));
$date = $this->parameters->get('date') ?? today(config('app.timezone'));
$this->adminRepository->setAdministrationId($data['administration_id']);
$return = [];

View File

@@ -27,11 +27,12 @@ namespace FireflyIII\Api\V2\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Generic\DateRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Api\ConvertsExchangeRates;
use FireflyIII\User;
use FireflyIII\Support\Http\Api\CleansChartData;
use Illuminate\Http\JsonResponse;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
@@ -41,7 +42,7 @@ use Psr\Container\NotFoundExceptionInterface;
*/
class AccountController extends Controller
{
use ConvertsExchangeRates;
use CleansChartData;
private AccountRepositoryInterface $repository;
@@ -54,7 +55,7 @@ class AccountController extends Controller
$this->middleware(
function ($request, $next) {
$this->repository = app(AccountRepositoryInterface::class);
$this->repository->setAdministrationId(auth()->user()->user_group_id);
return $next($request);
}
);
@@ -66,39 +67,38 @@ class AccountController extends Controller
*
* The native currency is the preferred currency on the page /currencies.
*
* If a transaction has foreign currency = native currency, the foreign amount will be used, no conversion
* will take place.
*
* TODO validate and set administration_id from request
*
* @param DateRequest $request
*
* @return JsonResponse
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws FireflyException
*/
public function dashboard(DateRequest $request): JsonResponse
{
// parameters for chart:
$dates = $request->getAll();
/** @var Carbon $start */
$start = $dates['start'];
$start = $this->parameters->get('start');
/** @var Carbon $end */
$end = $dates['end'];
/** @var User $user */
$user = auth()->user();
// group ID
$administrationId = $user->getAdministrationId();
$this->repository->setAdministrationId($administrationId);
$end = $this->parameters->get('end');
$end->endOfDay();
// user's preferences
$defaultSet = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])->pluck('id')->toArray();
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
$default = app('amount')->getDefaultCurrency();
$accounts = $this->repository->getAccountsById($frontPage->data);
$chartData = [];
/** @var TransactionCurrency $default */
$default = app('amount')->getDefaultCurrency();
$accounts = $this->repository->getAccountsById($frontPage->data);
$chartData = [];
if (!(is_array($frontPage->data) && count($frontPage->data) > 0)) {
$frontPage->data = $defaultSet;
$frontPage->save();
}
/** @var Account $account */
foreach ($accounts as $account) {
$currency = $this->repository->getAccountCurrency($account);
@@ -113,15 +113,16 @@ class AccountController extends Controller
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
// the default currency of the user (may be the same!)
'native_id' => $default->id,
// the default currency of the user (could be the same!)
'native_id' => (int)$default->id,
'native_code' => $default->code,
'native_symbol' => $default->symbol,
'native_decimal_places' => $default->decimal_places,
'start_date' => $start->toAtomString(),
'end_date' => $end->toAtomString(),
'native_decimal_places' => (int)$default->decimal_places,
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'period' => '1D',
'entries' => [],
'converted_entries' => [],
'native_entries' => [],
];
$currentStart = clone $start;
$range = app('steam')->balanceInRange($account, $start, clone $end, $currency);
@@ -138,13 +139,13 @@ class AccountController extends Controller
$previousConverted = $balanceConverted;
$currentStart->addDay();
$currentSet['entries'][$label] = $balance;
$currentSet['converted_entries'][$label] = $balanceConverted;
$currentSet['entries'][$label] = $balance;
$currentSet['native_entries'][$label] = $balanceConverted;
}
$currentSet = $this->cerChartSet($currentSet);
$chartData[] = $currentSet;
}
return response()->json($chartData);
return response()->json($this->clean($chartData));
}
}

View File

@@ -0,0 +1,260 @@
<?php
declare(strict_types=1);
/*
* BalanceController.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V2\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Chart\BalanceChartRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
/**
* Class BalanceController
*/
class BalanceController extends Controller
{
use CleansChartData;
private AccountRepositoryInterface $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(AccountRepositoryInterface::class);
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.
*
* TODO validate and set administration_id
* TODO collector set group, not user
*
* @param BalanceChartRequest $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function balance(BalanceChartRequest $request): JsonResponse
{
$params = $request->getAll();
/** @var Carbon $start */
$start = $this->parameters->get('start');
/** @var Carbon $end */
$end = $this->parameters->get('end');
$end->endOfDay();
/** @var Collection $accounts */
$accounts = $params['accounts'];
$preferredRange = $params['period'];
// set some formats, based on input parameters.
$format = app('navigation')->preferredCarbonFormatByPeriod($preferredRange);
// prepare for currency conversion and data collection:
$ids = $accounts->pluck('id')->toArray();
/** @var TransactionCurrency $default */
$default = app('amount')->getDefaultCurrency();
$converter = new ExchangeRateConverter();
$currencies = [(int)$default->id => $default,]; // currency cache
$data = [];
$chartData = [];
// get journals for entire period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->withAccountInformation();
$collector->setXorAccounts($accounts);
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::RECONCILIATION, TransactionType::TRANSFER]);
$journals = $collector->getExtractedJournals();
// set array for default currency (even if unused later on)
$defaultCurrencyId = (int)$default->id;
$data[$defaultCurrencyId] = [
'currency_id' => $defaultCurrencyId,
'currency_symbol' => $default->symbol,
'currency_code' => $default->code,
'currency_name' => $default->name,
'currency_decimal_places' => (int)$default->decimal_places,
'native_id' => $defaultCurrencyId,
'native_symbol' => $default->symbol,
'native_code' => $default->code,
'native_name' => $default->name,
'native_decimal_places' => (int)$default->decimal_places,
];
// loop. group by currency and by period.
/** @var array $journal */
foreach ($journals as $journal) {
// format the date according to the period
$period = $journal['date']->format($format);
// collect (and cache) currency information for this journal.
$currencyId = (int)$journal['currency_id'];
$currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$currencies[$currencyId] = $currency; // may just re-assign itself, don't mind.
// set the array with monetary info, if it does not exist.
$data[$currencyId] = $data[$currencyId] ?? [
'currency_id' => $currencyId,
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_name' => $journal['currency_name'],
'currency_decimal_places' => $journal['currency_decimal_places'],
// native currency info (could be the same)
'native_id' => (int)$default->id,
'native_code' => $default->code,
'native_symbol' => $default->symbol,
'native_decimal_places' => (int)$default->decimal_places,
];
// set the array (in monetary info) with spent/earned in this $period, if it does not exist.
$data[$currencyId][$period] = $data[$currencyId][$period] ?? [
'period' => $period,
'spent' => '0',
'earned' => '0',
'native_spent' => '0',
'native_earned' => '0',
];
// is this journal's amount in- our outgoing?
$key = 'spent';
$amount = app('steam')->negative($journal['amount']);
// deposit = incoming
// transfer or reconcile or opening balance, and these accounts are the destination.
if (
TransactionType::DEPOSIT === $journal['transaction_type_type']
||
(
(
TransactionType::TRANSFER === $journal['transaction_type_type']
|| TransactionType::RECONCILIATION === $journal['transaction_type_type']
|| TransactionType::OPENING_BALANCE === $journal['transaction_type_type']
)
&& in_array($journal['destination_account_id'], $ids, true)
)
) {
$key = 'earned';
$amount = app('steam')->positive($journal['amount']);
}
// get conversion rate
$rate = $converter->getCurrencyRate($currency, $default, $journal['date']);
$amountConverted = bcmul($amount, $rate);
// perhaps transaction already has the foreign amount in the native currency.
if ((int)$journal['foreign_currency_id'] === (int)$default->id) {
$amountConverted = $journal['foreign_amount'] ?? '0';
$amountConverted = 'earned' === $key ? app('steam')->positive($amountConverted) : app('steam')->negative($amountConverted);
}
// add normal entry
$data[$currencyId][$period][$key] = bcadd($data[$currencyId][$period][$key], $amount);
// add converted entry
$convertedKey = sprintf('native_%s', $key);
$data[$currencyId][$period][$convertedKey] = bcadd($data[$currencyId][$period][$convertedKey], $amountConverted);
}
// loop this data, make chart bars for each currency:
/** @var array $currency */
foreach ($data as $currency) {
// income and expense array prepped:
$income = [
'label' => 'earned',
'currency_id' => $currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'native_id' => $currency['native_id'],
'native_symbol' => $currency['native_symbol'],
'native_code' => $currency['native_code'],
'native_decimal_places' => $currency['native_decimal_places'],
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'period' => $preferredRange,
'entries' => [],
'native_entries' => [],
];
$expense = [
'label' => 'spent',
'currency_id' => $currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'native_id' => $currency['native_id'],
'native_symbol' => $currency['native_symbol'],
'native_code' => $currency['native_code'],
'native_decimal_places' => $currency['native_decimal_places'],
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'period' => $preferredRange,
'entries' => [],
'native_entries' => [],
];
// loop all possible periods between $start and $end, and add them to the correct dataset.
$currentStart = clone $start;
while ($currentStart <= $end) {
$key = $currentStart->format($format);
$label = $currentStart->toAtomString();
// normal entries
$income['entries'][$label] = app('steam')->bcround(($currency[$key]['earned'] ?? '0'), $currency['currency_decimal_places']);
$expense['entries'][$label] = app('steam')->bcround(($currency[$key]['spent'] ?? '0'), $currency['currency_decimal_places']);
// converted entries
$income['native_entries'][$label] = app('steam')->bcround(($currency[$key]['native_earned'] ?? '0'), $currency['native_decimal_places']);
$expense['native_entries'][$label] = app('steam')->bcround(($currency[$key]['native_spent'] ?? '0'), $currency['native_decimal_places']);
// next loop
$currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0);
}
$chartData[] = $income;
$chartData[] = $expense;
}
return response()->json($this->clean($chartData));
}
}

View File

@@ -0,0 +1,313 @@
<?php
declare(strict_types=1);
/*
* BudgetController.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V2\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Generic\DateRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Administration\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Administration\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
/**
* Class BudgetController
*/
class BudgetController extends Controller
{
use CleansChartData;
protected OperationsRepositoryInterface $opsRepository;
private BudgetLimitRepositoryInterface $blRepository;
private array $currencies = [];
private TransactionCurrency $currency;
private BudgetRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(BudgetRepositoryInterface::class);
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->currency = app('amount')->getDefaultCurrency();
return $next($request);
}
);
}
/**
* @param DateRequest $request
*
* TODO see autocomplete/accountcontroller
*
* @return JsonResponse
* @throws FireflyException
*/
public function dashboard(DateRequest $request): JsonResponse
{
// get user.
/** @var User $user */
$user = auth()->user();
// group ID
$administrationId = $user->getAdministrationId();
$this->repository->setAdministrationId($administrationId);
$this->opsRepository->setAdministrationId($administrationId);
$params = $request->getAll();
/** @var Carbon $start */
$start = $params['start'];
/** @var Carbon $end */
$end = $params['end'];
// code from FrontpageChartGenerator, but not in separate class
$budgets = $this->repository->getActiveBudgets();
$data = [];
/** @var Budget $budget */
foreach ($budgets as $budget) {
// could return multiple arrays, so merge.
$data = array_merge($data, $this->processBudget($budget, $start, $end));
}
return response()->json($this->clean($data));
}
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return array
* @throws FireflyException
*/
private function processBudget(Budget $budget, Carbon $start, Carbon $end): array
{
// get all limits:
$limits = $this->blRepository->getBudgetLimits($budget, $start, $end);
$rows = [];
// if no limits
if (0 === $limits->count()) {
// return as a single item in an array
$rows = $this->noBudgetLimits($budget, $start, $end);
}
if ($limits->count() > 0) {
$rows = $this->budgetLimits($budget, $limits);
}
// is always an array
$return = [];
foreach ($rows as $row) {
$current = [
'label' => $budget->name,
'currency_id' => $row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_name' => $row['currency_name'],
'currency_decimal_places' => $row['currency_decimal_places'],
'native_id' => $row['native_id'],
'native_code' => $row['native_code'],
'native_name' => $row['native_name'],
'native_decimal_places' => $row['native_decimal_places'],
'period' => null,
'start' => $row['start'],
'end' => $row['end'],
'entries' => [
'spent' => $row['spent'],
'left' => $row['left'],
'overspent' => $row['overspent'],
],
'native_entries' => [
'spent' => $row['native_spent'],
'left' => $row['native_left'],
'overspent' => $row['native_overspent'],
],
];
$return[] = $current;
}
return $return;
}
/**
* When no budget limits are present, the expenses of the whole period are collected and grouped.
* This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty.
*
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return array
* @throws FireflyException
*/
private function noBudgetLimits(Budget $budget, Carbon $start, Carbon $end): array
{
$budgetId = (int)$budget->id;
$spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget]));
return $this->processExpenses($budgetId, $spent, $start, $end);
}
/**
* Shared between the "noBudgetLimits" function and "processLimit".
*
* Will take a single set of expenses and return its info.
*
* @param int $budgetId
* @param array $array
*
* @return array
* @throws FireflyException
*/
private function processExpenses(int $budgetId, array $array, Carbon $start, Carbon $end): array
{
$converter = new ExchangeRateConverter();
$return = [];
/**
* This array contains the expenses in this budget. Grouped per currency.
* The grouping is on the main currency only.
*
* @var int $currencyId
* @var array $block
*/
foreach ($array as $currencyId => $block) {
$this->currencies[$currencyId] = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$return[$currencyId] = $return[$currencyId] ?? [
'currency_id' => $currencyId,
'currency_code' => $block['currency_code'],
'currency_name' => $block['currency_name'],
'currency_symbol' => $block['currency_symbol'],
'currency_decimal_places' => (int)$block['currency_decimal_places'],
'native_id' => (int)$this->currency->id,
'native_code' => $this->currency->code,
'native_name' => $this->currency->name,
'native_symbol' => $this->currency->symbol,
'native_decimal_places' => (int)$this->currency->decimal_places,
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'spent' => '0',
'native_spent' => '0',
'left' => '0',
'native_left' => '0',
'overspent' => '0',
'native_overspent' => '0',
];
$currentBudgetArray = $block['budgets'][$budgetId];
//var_dump($return);
/** @var array $journal */
foreach ($currentBudgetArray['transaction_journals'] as $journal) {
// convert the amount to the native currency.
$rate = $converter->getCurrencyRate($this->currencies[$currencyId], $this->currency, $journal['date']);
$convertedAmount = bcmul($journal['amount'], $rate);
if ($journal['foreign_currency_id'] === $this->currency->id) {
$convertedAmount = $journal['foreign_amount'];
}
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $journal['amount']);
$return[$currencyId]['native_spent'] = bcadd($return[$currencyId]['native_spent'], $convertedAmount);
}
}
return $return;
}
/**
* Function that processes each budget limit (per budget).
*
* If you have a budget limit in EUR, only transactions in EUR will be considered.
* If you have a budget limit in GBP, only transactions in GBP will be considered.
*
* If you have a budget limit in EUR, and a transaction in GBP, it will not be considered for the EUR budget limit.
*
* @param Budget $budget
* @param Collection $limits
*
* @return array
* @throws FireflyException
*/
private function budgetLimits(Budget $budget, Collection $limits): array
{
app('log')->debug(sprintf('Now in budgetLimits(#%d)', $budget->id));
$data = [];
/** @var BudgetLimit $limit */
foreach ($limits as $limit) {
$data = array_merge($data, $this->processLimit($budget, $limit));
}
return $data;
}
/**
* @param Budget $budget
* @param BudgetLimit $limit
*
* @return array
* @throws FireflyException
*/
private function processLimit(Budget $budget, BudgetLimit $limit): array
{
$budgetId = (int)$budget->id;
$end = clone $limit->end_date;
$end->endOfDay();
$spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection([$budget]));
$limitCurrencyId = (int)$limit->transaction_currency_id;
$limitCurrency = $limit->transactionCurrency;
$converter = new ExchangeRateConverter();
$filtered = [];
$rate = $converter->getCurrencyRate($limitCurrency, $this->currency, $limit->start_date);
$convertedLimitAmount = bcmul($limit->amount, $rate);
/** @var array $entry */
foreach ($spent as $currencyId => $entry) {
// only spent the entry where the entry's currency matches the budget limit's currency
// so $filtered will only have 1 or 0 entries
if ($entry['currency_id'] === $limitCurrencyId) {
$filtered[$currencyId] = $entry;
}
}
$result = $this->processExpenses($budgetId, $filtered, $limit->start_date, $end);
if (1 === count($result)) {
$compare = bccomp((string)$limit->amount, app('steam')->positive($result[$limitCurrencyId]['spent']));
if (1 === $compare) {
// convert this amount into the native currency:
$result[$limitCurrencyId]['left'] = bcadd($limit->amount, $result[$limitCurrencyId]['spent']);
$result[$limitCurrencyId]['native_left'] = bcadd($convertedLimitAmount, $result[$limitCurrencyId]['native_spent']);
}
if ($compare <= 0) {
$result[$limitCurrencyId]['overspent'] = app('steam')->positive(bcadd($limit->amount, $result[$limitCurrencyId]['spent']));
$result[$limitCurrencyId]['native_overspent'] = app('steam')->positive(bcadd($convertedLimitAmount, $result[$limitCurrencyId]['native_spent']));
}
}
return $result;
}
}

View File

@@ -0,0 +1,137 @@
<?php
declare(strict_types=1);
/*
* BudgetController.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V2\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Generic\DateRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Http\JsonResponse;
/**
* Class BudgetController
*/
class CategoryController extends Controller
{
use CleansChartData;
private AccountRepositoryInterface $accountRepos;
private CurrencyRepositoryInterface $currencyRepos;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->accountRepos = app(AccountRepositoryInterface::class);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->accountRepos->setAdministrationId(auth()->user()->user_group_id);
return $next($request);
}
);
}
/**
* TODO may be worth to move to a handler but the data is simple enough.
* TODO see autoComplete/account controller
*
* @param DateRequest $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function dashboard(DateRequest $request): JsonResponse
{
/** @var Carbon $start */
$start = $this->parameters->get('start');
/** @var Carbon $end */
$end = $this->parameters->get('end');
$accounts = $this->accountRepos->getAccountsByType([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::ASSET, AccountType::DEFAULT]);
$default = app('amount')->getDefaultCurrency();
$converter = new ExchangeRateConverter();
$currencies = [];
$return = [];
// get journals for entire period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->withAccountInformation();
$collector->setXorAccounts($accounts)->withCategoryInformation();
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::RECONCILIATION]);
$journals = $collector->getExtractedJournals();
/** @var array $journal */
foreach ($journals as $journal) {
$currencyId = (int)$journal['currency_id'];
$currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId);
$currencies[$currencyId] = $currency;
$categoryName = null === $journal['category_name'] ? (string)trans('firefly.no_category') : $journal['category_name'];
$amount = app('steam')->positive($journal['amount']);
$nativeAmount = $converter->convert($default, $currency, $journal['date'], $amount);
$key = sprintf('%s-%s', $categoryName, $currency->code);
if ((int)$journal['foreign_currency_id'] === (int)$default->id) {
$nativeAmount = app('steam')->positive($journal['foreign_amount']);
}
// create arrays
$return[$key] = $return[$key] ?? [
'label' => $categoryName,
'currency_id' => (int)$currency->id,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => (int)$currency->decimal_places,
'native_id' => (int)$default->id,
'native_code' => $default->code,
'native_name' => $default->name,
'native_symbol' => $default->symbol,
'native_decimal_places' => (int)$default->decimal_places,
'period' => null,
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'amount' => '0',
'native_amount' => '0',
];
// add monies
$return[$key]['amount'] = bcadd($return[$key]['amount'], $amount);
$return[$key]['native_amount'] = bcadd($return[$key]['native_amount'], $nativeAmount);
}
$return = array_values($return);
// order by native amount
usort($return, function (array $a, array $b) {
return (float)$a['native_amount'] < (float)$b['native_amount'] ? 1 : -1;
});
return response()->json($this->clean($return));
}
}

View File

@@ -93,21 +93,26 @@ class Controller extends BaseController
// some date fields:
foreach ($dates as $field) {
$date = null;
$obj = null;
try {
$date = request()->query->get($field);
} catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field));
Log::error($e->getMessage());
$value = null;
Log::error($e->getTraceAsString());
}
$obj = null;
if (null !== $date) {
try {
$obj = Carbon::parse($date);
$obj = Carbon::parse($date, config('app.timezone'));
} catch (InvalidDateException | InvalidFormatException $e) {
// don't care
app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr($date, 0, 20), $e->getMessage()));
}
// out of range? set to null.
if (null !== $obj && ($obj->year <= 1900 || $obj->year > 2099)) {
app('log')->warning(sprintf('Refuse to use date "%s" in API v2 controller parameter check: %s', $field, $obj->toAtomString()));
$obj = null;
}
}
$bag->set($field, $obj);
}
@@ -148,7 +153,7 @@ class Controller extends BaseController
$objects = $paginator->getCollection();
// the transformer, at this point, needs to collect information that ALL items in the collection
// require, like meta data and stuff like that, and save it for later.
// require, like meta-data and stuff like that, and save it for later.
$transformer->collectMetaData($objects);
$resource = new FractalCollection($objects, $transformer, $key);

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/*
* ShowController.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V2\Controllers\Model\Bill;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface;
use FireflyIII\Transformers\V2\BillTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* Class ShowController
*/
class ShowController extends Controller
{
private BillRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(BillRepositoryInterface::class);
$this->repository->setAdministrationId(auth()->user()->user_group_id);
return $next($request);
}
);
}
/**
* @param Request $request
*
* TODO see autocomplete/accountcontroller for list.
*
* @return JsonResponse
*/
public function index(Request $request): JsonResponse
{
$this->repository->correctOrder();
$bills = $this->repository->getBills();
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$count = $bills->count();
$bills = $bills->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page'));
$transformer = new BillTransformer();
$transformer->setParameters($this->parameters); // give params to transformer
return response()
->json($this->jsonApiList('subscriptions', $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE);
}
}

View File

@@ -26,8 +26,7 @@ namespace FireflyIII\Api\V2\Controllers\Model\Bill;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Generic\DateRequest;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\Http\Api\ConvertsExchangeRates;
use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface;
use Illuminate\Http\JsonResponse;
/**
@@ -35,8 +34,6 @@ use Illuminate\Http\JsonResponse;
*/
class SumController extends Controller
{
use ConvertsExchangeRates;
private BillRepositoryInterface $repository;
/**
@@ -58,35 +55,37 @@ class SumController extends Controller
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/transactions-sum/getBillsPaidTrSum
*
* TODO see autocomplete/accountcontroller for list.
*
* @param DateRequest $request
*
* @return JsonResponse
*/
public function paid(DateRequest $request): JsonResponse
{
$dates = $request->getAll();
$result = $this->repository->sumPaidInRange($dates['start'], $dates['end']);
$converted = $this->cerSum($result);
$this->repository->setAdministrationId(auth()->user()->user_group_id);
$result = $this->repository->sumPaidInRange($this->parameters->get('start'), $this->parameters->get('end'));
// convert to JSON response:
return response()->api($converted);
return response()->api(array_values($result));
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/transactions-sum/getBillsUnpaidTrSum
*
* TODO see autocomplete/accountcontroller for list.
*
* @param DateRequest $request
*
* @return JsonResponse
*/
public function unpaid(DateRequest $request): JsonResponse
{
$dates = $request->getAll();
$result = $this->repository->sumUnpaidInRange($dates['start'], $dates['end']);
$converted = $this->cerSum($result);
$this->repository->setAdministrationId(auth()->user()->user_group_id);
$result = $this->repository->sumUnpaidInRange($this->parameters->get('start'), $this->parameters->get('end'));
// convert to JSON response:
return response()->api($converted);
return response()->api(array_values($result));
}
}

View File

@@ -60,6 +60,8 @@ class ListController extends Controller
*/
public function index(Request $request): JsonResponse
{
echo 'this needs move to Administration';
exit;
$collection = $this->repository->getActiveBudgets();
$total = $collection->count();
$collection->slice($this->pageSize * $this->parameters->get('page'), $this->pageSize);

View File

@@ -29,7 +29,6 @@ use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Generic\DateRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\Http\Api\ConvertsExchangeRates;
use Illuminate\Http\JsonResponse;
/**
@@ -37,8 +36,6 @@ use Illuminate\Http\JsonResponse;
*/
class ShowController extends Controller
{
use ConvertsExchangeRates;
private BudgetRepositoryInterface $repository;
/**
@@ -63,6 +60,7 @@ class ShowController extends Controller
*/
public function budgeted(DateRequest $request, Budget $budget): JsonResponse
{
die('deprecated use of thing.');
$data = $request->getAll();
$result = $this->repository->budgetedInPeriodForBudget($budget, $data['start'], $data['end']);
$converted = $this->cerSum(array_values($result));
@@ -77,6 +75,7 @@ class ShowController extends Controller
*/
public function spent(DateRequest $request, Budget $budget): JsonResponse
{
die('deprecated use of thing.');
$data = $request->getAll();
$result = $this->repository->spentInPeriodForBudget($budget, $data['start'], $data['end']);
$converted = $this->cerSum(array_values($result));

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\Api\V2\Controllers\Model\Budget;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Generic\DateRequest;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\Http\Api\ConvertsExchangeRates;
use Illuminate\Http\JsonResponse;
/**
@@ -35,8 +34,6 @@ use Illuminate\Http\JsonResponse;
*/
class SumController extends Controller
{
use ConvertsExchangeRates;
private BudgetRepositoryInterface $repository;
/**
@@ -64,6 +61,7 @@ class SumController extends Controller
*/
public function budgeted(DateRequest $request): JsonResponse
{
die('deprecated use of thing.');
$data = $request->getAll();
$result = $this->repository->budgetedInPeriod($data['start'], $data['end']);
$converted = $this->cerSum(array_values($result));
@@ -81,6 +79,7 @@ class SumController extends Controller
*/
public function spent(DateRequest $request): JsonResponse
{
die('deprecated use of thing.');
$data = $request->getAll();
$result = $this->repository->spentInPeriod($data['start'], $data['end']);
$converted = $this->cerSum(array_values($result));

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
/*
* ShowController.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V2\Controllers\Model\PiggyBank;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Repositories\Administration\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Transformers\V2\PiggyBankTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* Class ShowController
*/
class ShowController extends Controller
{
private PiggyBankRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(PiggyBankRepositoryInterface::class);
$this->repository->setAdministrationId(auth()->user()->user_group_id);
return $next($request);
}
);
}
/**
* @param Request $request
*
* TODO see autocomplete/accountcontroller for list.
*
* @return JsonResponse
*/
public function index(Request $request): JsonResponse
{
$piggies = $this->repository->getPiggyBanks();
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$count = $piggies->count();
$piggies = $piggies->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($piggies, $count, $pageSize, $this->parameters->get('page'));
$transformer = new PiggyBankTransformer();
$transformer->setParameters($this->parameters); // give params to transformer
return response()
->json($this->jsonApiList('piggy-banks', $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE);
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/*
* StoreController.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V2\Controllers\Model\Transaction;
use FireflyIII\Api\V2\Controllers\Controller;
use Illuminate\Http\JsonResponse;
/**
* Class StoreController
*/
class StoreController extends Controller
{
/**
* @return JsonResponse
*/
public function post(): JsonResponse
{
return response()->json([]);
}
}

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Api\V2\Controllers;
use FireflyIII\Api\V2\Request\Generic\SingleDateRequest;
use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Support\Http\Api\ConvertsExchangeRates;
use Illuminate\Http\JsonResponse;
/**
@@ -34,8 +33,6 @@ use Illuminate\Http\JsonResponse;
*/
class NetWorthController extends Controller
{
use ConvertsExchangeRates;
private NetWorthInterface $netWorth;
/**
@@ -64,6 +61,7 @@ class NetWorthController extends Controller
*/
public function get(SingleDateRequest $request): JsonResponse
{
die('deprecated use of thing.');
$date = $request->getDate();
$result = $this->netWorth->sumNetWorthByCurrency($date);
$converted = $this->cerSum($result);

View File

@@ -0,0 +1,552 @@
<?php
/*
* SummaryController.php
* Copyright (c) 2021 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\V2\Controllers\Summary;
use Carbon\Carbon;
use Exception;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Generic\DateRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Administration\Budget\AvailableBudgetRepositoryInterface;
use FireflyIII\Repositories\Administration\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Administration\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
* Class BasicController
*/
class BasicController extends Controller
{
private AvailableBudgetRepositoryInterface $abRepository;
private AccountRepositoryInterface $accountRepository;
private BillRepositoryInterface $billRepository;
private BudgetRepositoryInterface $budgetRepository;
private CurrencyRepositoryInterface $currencyRepos;
private OperationsRepositoryInterface $opsRepository;
/**
* BasicController constructor.
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->abRepository = app(AvailableBudgetRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->billRepository = app(BillRepositoryInterface::class);
$this->budgetRepository = app(BudgetRepositoryInterface::class);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->abRepository->setAdministrationId($user->user_group_id);
$this->accountRepository->setAdministrationId($user->user_group_id);
$this->billRepository->setAdministrationId($user->user_group_id);
$this->budgetRepository->setAdministrationId($user->user_group_id);
$this->currencyRepos->setUser($user);
$this->opsRepository->setAdministrationId($user->user_group_id);
return $next($request);
}
);
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/summary/getBasicSummary
*
* @param DateRequest $request
*
* @return JsonResponse
* @throws Exception
*/
public function basic(DateRequest $request): JsonResponse
{
// parameters for boxes:
$start = $this->parameters->get('start');
$end = $this->parameters->get('end');
// balance information:
$balanceData = $this->getBalanceInformation($start, $end);
$billData = $this->getBillInformation($start, $end);
$spentData = $this->getLeftToSpendInfo($start, $end);
$netWorthData = $this->getNetWorthInfo($start, $end);
$total = array_merge($balanceData, $billData, $spentData, $netWorthData);
return response()->json($total);
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
* @throws FireflyException
*/
private function getBalanceInformation(Carbon $start, Carbon $end): array
{
// prep some arrays:
$incomes = [
'native' => '0',
];
$expenses = [
'native' => '0',
];
$sums = [
'native' => '0',
];
$return = [];
$currencies = [];
$converter = new ExchangeRateConverter();
$default = app('amount')->getDefaultCurrency();
/** @var User $user */
$user = auth()->user();
// collect income of user using the new group collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setRange($start, $end)
->setUserGroup($user->userGroup)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes([TransactionType::DEPOSIT])
->setRange($start, $end);
$set = $collector->getExtractedJournals();
/** @var array $transactionJournal */
foreach ($set as $transactionJournal) {
// transaction info:
$currencyId = (int)$transactionJournal['currency_id'];
$amount = bcmul($transactionJournal['amount'], '-1');
$currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$currencies[$currencyId] = $currency;
$nativeAmount = $converter->convert($currency, $default, $transactionJournal['date'], $amount);
if ((int)$transactionJournal['foreign_currency_id'] === (int)$default->id) {
// use foreign amount instead
$nativeAmount = $transactionJournal['foreign_amount'];
}
// prep the arrays
$incomes[$currencyId] = $incomes[$currencyId] ?? '0';
$incomes['native'] = $incomes['native'] ?? '0';
$sums[$currencyId] = $sums[$currencyId] ?? '0';
$sums['native'] = $sums['native'] ?? '0';
// add values:
$incomes[$currencyId] = bcadd($incomes[$currencyId], $amount);
$sums[$currencyId] = bcadd($sums[$currencyId], $amount);
$incomes['native'] = bcadd($incomes['native'], $nativeAmount);
$sums['native'] = bcadd($sums['native'], $nativeAmount);
}
// collect expenses of user using the new group collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setRange($start, $end)
->setUserGroup($user->userGroup)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes([TransactionType::WITHDRAWAL])
->setRange($start, $end);
$set = $collector->getExtractedJournals();
/** @var array $transactionJournal */
foreach ($set as $transactionJournal) {
// transaction info
$currencyId = (int)$transactionJournal['currency_id'];
$amount = $transactionJournal['amount'];
$currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId);
$currencies[$currencyId] = $currency;
$nativeAmount = $converter->convert($currency, $default, $transactionJournal['date'], $amount);
if ((int)$transactionJournal['foreign_currency_id'] === (int)$default->id) {
// use foreign amount instead
$nativeAmount = $transactionJournal['foreign_amount'];
}
// prep arrays
$expenses[$currencyId] = $expenses[$currencyId] ?? '0';
$expenses['native'] = $expenses['native'] ?? '0';
$sums[$currencyId] = $sums[$currencyId] ?? '0';
$sums['native'] = $sums['native'] ?? '0';
// add values
$expenses[$currencyId] = bcadd($expenses[$currencyId], $amount);
$sums[$currencyId] = bcadd($sums[$currencyId], $amount);
$expenses['native'] = bcadd($expenses['native'], $nativeAmount);
$sums['native'] = bcadd($sums['native'], $nativeAmount);
}
// create special array for native currency:
$return[] = [
'key' => 'balance-in-native',
'value' => $sums['native'],
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
];
$return[] = [
'key' => 'spent-in-native',
'value' => $expenses['native'],
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
];
$return[] = [
'key' => 'earned-in-native',
'value' => $incomes['native'],
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
];
// format amounts:
$keys = array_keys($sums);
foreach ($keys as $currencyId) {
if ('native' === $currencyId) {
// skip native entries.
continue;
}
$currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId);
$currencies[$currencyId] = $currency;
// create objects for big array.
$return[] = [
'key' => sprintf('balance-in-%s', $currency->code),
'value' => $sums[$currencyId] ?? '0',
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
'value' => $expenses[$currencyId] ?? '0',
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
];
$return[] = [
'key' => sprintf('earned-in-%s', $currency->code),
'value' => $incomes[$currencyId] ?? '0',
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
];
}
return $return;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getBillInformation(Carbon $start, Carbon $end): array
{
/*
* Since both this method and the chart use the exact same data, we can suffice
* with calling the one method in the bill repository that will get this amount.
*/
$paidAmount = $this->billRepository->sumPaidInRange($start, $end);
$unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end);
$return = [];
/**
* @var array $info
*/
foreach ($paidAmount as $info) {
$amount = bcmul($info['sum'], '-1');
$nativeAmount = bcmul($info['native_sum'], '-1');
$return[] = [
'key' => sprintf('bills-paid-in-%s', $info['currency_code']),
'value' => $amount,
'currency_id' => $info['currency_id'],
'currency_code' => $info['currency_code'],
'currency_symbol' => $info['currency_symbol'],
'currency_decimal_places' => $info['currency_decimal_places'],
];
$return[] = [
'key' => 'bills-paid-in-native',
'value' => $nativeAmount,
'currency_id' => $info['native_id'],
'currency_code' => $info['native_code'],
'currency_symbol' => $info['native_symbol'],
'currency_decimal_places' => $info['native_decimal_places'],
];
}
/**
* @var array $info
*/
foreach ($unpaidAmount as $info) {
$amount = bcmul($info['sum'], '-1');
$nativeAmount = bcmul($info['native_sum'], '-1');
$return[] = [
'key' => sprintf('bills-unpaid-in-%s', $info['currency_code']),
'value' => $amount,
'currency_id' => $info['currency_id'],
'currency_code' => $info['currency_code'],
'currency_symbol' => $info['currency_symbol'],
'currency_decimal_places' => $info['currency_decimal_places'],
];
$return[] = [
'key' => 'bills-unpaid-in-native',
'value' => $nativeAmount,
'currency_id' => $info['native_id'],
'currency_code' => $info['native_code'],
'currency_symbol' => $info['native_symbol'],
'currency_decimal_places' => $info['native_decimal_places'],
];
}
return $return;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
* @throws Exception
*/
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
{
app('log')->debug('Now in getLeftToSpendInfo');
$return = [];
$today = today(config('app.timezone'));
$available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
$budgets = $this->budgetRepository->getActiveBudgets();
$spent = $this->opsRepository->listExpenses($start, $end, null, $budgets);
$default = app('amount')->getDefaultCurrency();
$currencies = [];
$converter = new ExchangeRateConverter();
// native info:
$nativeLeft = [
'key' => 'left-to-spend-in-native',
'value' => '0',
'currency_id' => (string)$default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => (int)$default->decimal_places,
];
$nativePerDay = [
'key' => 'left-per-day-to-spend-in-native',
'value' => '0',
'currency_id' => (string)$default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => (int)$default->decimal_places,
];
/**
* @var int $currencyId
* @var array $row
*/
foreach ($spent as $currencyId => $row) {
app('log')->debug(sprintf('Processing spent array in currency #%d', $currencyId));
$currencyId = (int)$currencyId;
$spent = '0';
$spentNative = '0';
// get the sum from the array of transactions (double loop but who cares)
/** @var array $budget */
foreach ($row['budgets'] as $budget) {
app('log')->debug(sprintf('Processing expenses in budget "%s".', $budget['name']));
/** @var array $journal */
foreach ($budget['transaction_journals'] as $journal) {
$journalCurrencyId = $journal['currency_id'];
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
$currencies[$currencyId] = $currency;
$amount = app('steam')->negative($journal['amount']);
$amountNative = $converter->convert($default, $currency, $start, $amount);
if ((int)$journal['foreign_currency_id'] === (int)$default->id) {
$amountNative = $journal['foreign_amount'];
}
$spent = bcadd($spent, $amount);
$spentNative = bcadd($spentNative, $amountNative);
}
app('log')->debug(sprintf('Total spent in budget "%s" is %s', $budget['name'], $spent));
}
// either an amount was budgeted or 0 is available.
$currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId);
$currencies[$currencyId] = $currency;
$amount = $available[$currencyId]['amount'] ?? '0';
$amountNative = $available[$currencyId]['native_amount'] ?? '0';
$left = bcadd($amount, $spent);
$leftNative = bcadd($amountNative, $spentNative);
app('log')->debug(sprintf('Available amount is %s', $amount));
app('log')->debug(sprintf('Amount left is %s', $left));
// how much left per day?
$days = $today->diffInDays($end) + 1;
$perDay = '0';
$perDayNative = '0';
if (0 !== $days && bccomp($left, '0') > -1) {
$perDay = bcdiv($left, (string)$days);
}
if (0 !== $days && bccomp($leftNative, '0') > -1) {
$perDayNative = bcdiv($leftNative, (string)$days);
}
// left
$return[] = [
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'value' => $left,
'currency_id' => (string)$row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_symbol' => $row['currency_symbol'],
'currency_decimal_places' => (int)$row['currency_decimal_places'],
];
// left (native)
$nativeLeft['value'] = $leftNative;
// left per day:
$return[] = [
'key' => sprintf('left-per-day-to-spend-in-%s', $row['currency_code']),
'value' => $perDay,
'currency_id' => (string)$row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_symbol' => $row['currency_symbol'],
'currency_decimal_places' => (int)$row['currency_decimal_places'],
];
// left per day (native)
$nativePerDay['value'] = $perDayNative;
}
$return[] = $nativeLeft;
$return[] = $nativePerDay;
return $return;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getNetWorthInfo(Carbon $start, Carbon $end): array
{
/** @var UserGroup $userGroup */
$userGroup = auth()->user()->userGroup;
$date = today(config('app.timezone'))->startOfDay();
// start and end in the future? use $end
if ($this->notInDateRange($date, $start, $end)) {
/** @var Carbon $date */
$date = session('end', today(config('app.timezone'))->endOfMonth());
}
/** @var NetWorthInterface $netWorthHelper */
$netWorthHelper = app(NetWorthInterface::class);
$netWorthHelper->setUserGroup($userGroup);
$allAccounts = $this->accountRepository->getActiveAccountsByType(
[AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT]
);
// filter list on preference of being included.
$filtered = $allAccounts->filter(
function (Account $account) {
$includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth');
return null === $includeNetWorth || '1' === $includeNetWorth;
}
);
$netWorthSet = $netWorthHelper->byAccounts($filtered, $date);
$return = [];
// in native amount
$return[] = [
'key' => 'net-worth-in-native',
'value' => $netWorthSet['native']['balance'],
'currency_id' => $netWorthSet['native']['currency_id'],
'currency_code' => $netWorthSet['native']['currency_code'],
'currency_symbol' => $netWorthSet['native']['currency_symbol'],
'currency_decimal_places' => $netWorthSet['native']['currency_decimal_places'],
];
foreach ($netWorthSet as $key => $data) {
if ('native' === $key) {
continue;
}
$return[] = [
'key' => sprintf('net-worth-in-%s', $data['currency_code']),
'value' => $data['balance'],
'currency_id' => $data['currency_id'],
'currency_code' => $data['currency_code'],
'currency_symbol' => $data['currency_symbol'],
'currency_decimal_places' => $data['currency_decimal_places'],
];
}
return $return;
}
/**
* Check if date is outside session range.
*
* @param Carbon $date
*
* @param Carbon $start
* @param Carbon $end
*
* @return bool
*/
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
{
$result = false;
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
$result = true;
}
// start and end in the past? use $end
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
$result = true;
}
return $result;
}
}

View File

@@ -49,19 +49,17 @@ class AccountController extends Controller
*
* @return JsonResponse
*/
public function listTransactions(ListRequest $request, Account $account): JsonResponse
public function list(ListRequest $request, Account $account): JsonResponse
{
// collect transactions:
$type = $request->get('type') ?? 'default';
$limit = (int)$request->get('limit');
$page = (int)$request->get('page');
$limit = $request->getLimit();
$page = $request->getPage();
$page = max($page, 1);
if ($limit > 0 && $limit <= $this->pageSize) {
$this->pageSize = $limit;
}
$types = $this->mapTransactionTypes($type);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
@@ -69,15 +67,25 @@ class AccountController extends Controller
->withAPIInformation()
->setLimit($this->pageSize)
->setPage($page)
->setTypes($types);
->setTypes($request->getTransactionTypes());
// TODO date filter
//if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
// $collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
//}
$start = $request->getStartDate();
$end = $request->getEndDate();
if (null !== $start) {
$collector->setStart($start);
}
if (null !== $end) {
$collector->setEnd($start);
}
$paginator = $collector->getPaginatedGroups();
$paginator->setPath(route('api.v2.accounts.transactions', [$account->id])); // TODO . $this->buildParams()
$paginator->setPath(
sprintf(
'%s?%s',
route('api.v2.accounts.transactions', [$account->id]),
$request->buildParams()
)
);
return response()
->json($this->jsonApiList('transactions', $paginator, new TransactionGroupTransformer()))

View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
/*
* TransactionController.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V2\Controllers\Transaction\List;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Transaction\ListRequest;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Transformers\V2\TransactionGroupTransformer;
use Illuminate\Http\JsonResponse;
/**
* Class TransactionController
*/
class TransactionController extends Controller
{
/**
* @param ListRequest $request
*
* @return JsonResponse
*/
public function list(ListRequest $request): JsonResponse
{
// collect transactions:
$limit = $request->getLimit();
$page = $request->getPage();
$page = max($page, 1);
if ($limit > 0 && $limit <= $this->pageSize) {
$this->pageSize = $limit;
}
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUserGroup(auth()->user()->userGroup)
->withAPIInformation()
->setLimit($this->pageSize)
->setPage($page)
->setTypes($request->getTransactionTypes());
$start = $this->parameters->get('start');
$end = $this->parameters->get('end');
if (null !== $start) {
$collector->setStart($start);
}
if (null !== $end) {
$collector->setEnd($end);
}
// $collector->dumpQuery();
// exit;
$paginator = $collector->getPaginatedGroups();
$paginator->setPath(
sprintf(
'%s?%s',
route('api.v2.transactions.list'),
$request->buildParams()
)
);
return response()
->json($this->jsonApiList('transactions', $paginator, new TransactionGroupTransformer()))
->header('Content-Type', self::CONTENT_TYPE);
}
}

View File

@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
/*
* BalanceChartRequest.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V2\Request\Chart;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Validator;
class BalanceChartRequest extends FormRequest
{
use ConvertsDataTypes;
use ChecksLogin;
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
return [
'accounts' => $this->getAccountList(),
'period' => $this->convertString('period'),
];
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
return [
'start' => 'required|date|after:1900-01-01|before:2099-12-31',
'end' => 'required|date|after_or_equal:start|before:2099-12-31|after:1900-01-01',
'accounts.*' => 'required|exists:accounts,id',
'period' => sprintf('required|in:%s', join(',', config('firefly.valid_view_ranges'))),
];
}
/**
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator) {
// validate transaction query data.
$data = $validator->getData();
if (!array_key_exists('accounts', $data)) {
$validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts']));
return;
}
if (!is_array($data['accounts'])) {
$validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts']));
}
}
);
}
}

View File

@@ -47,7 +47,7 @@ class DateRequest extends FormRequest
{
return [
'start' => $this->getCarbonDate('start'),
'end' => $this->getCarbonDate('end'),
'end' => $this->getCarbonDate('end')->endOfDay(),
];
}

View File

@@ -24,15 +24,84 @@ declare(strict_types=1);
namespace FireflyIII\Api\V2\Request\Transaction;
use Carbon\Carbon;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
/**
* Class ListRequest
* Used specifically to list transactions.
*/
class ListRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
use TransactionFilter;
/**
* @return string
*/
public function buildParams(): string
{
$array = [
'page' => $this->getPage(),
];
$start = $this->getStartDate();
$end = $this->getEndDate();
if (null !== $start && null !== $end) {
$array['start'] = $start->format('Y-m-d');
$array['end'] = $end->format('Y-m-d');
}
if (0 !== $this->getLimit()) {
$array['limit'] = $this->getLimit();
}
return http_build_query($array);
}
/**
* @return int
*/
public function getPage(): int
{
$page = $this->convertInteger('page');
return 0 === $page || $page > 65536 ? 1 : $page;
}
/**
* @return Carbon|null
*/
public function getStartDate(): ?Carbon
{
return $this->getCarbonDate('start');
}
/**
* @return Carbon|null
*/
public function getEndDate(): ?Carbon
{
return $this->getCarbonDate('end');
}
/**
* @return int
*/
public function getLimit(): int
{
return $this->convertInteger('limit');
}
/**
* @return array
*/
public function getTransactionTypes(): array
{
$type = (string)$this->get('type', 'default');
return $this->mapTransactionTypes($type);
}
/**
* @return array

View File

@@ -34,6 +34,7 @@ use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\RuleTrigger;
use Illuminate\Console\Command;
use ValueError;
/**
* Class ReportSkeleton
@@ -247,7 +248,16 @@ class CorrectAmounts extends Command
/** @var RuleTrigger $item */
foreach ($set as $item) {
// basic check:
if (-1 === bccomp((string)$item->trigger_value, '0')) {
$check = 0;
try {
$check = bccomp((string)$item->trigger_value, '0');
} catch (ValueError $e) {
$this->friendlyError(sprintf('Rule #%d contained invalid %s-trigger "%s". The trigger has been removed, and the rule is disabled.', $item->rule_id, $item->trigger_type, $item->trigger_value));
$item->rule->active = false;
$item->rule->save();
$item->forceDelete();
}
if (-1 === $check) {
$fixed++;
$item->trigger_value = app('steam')->positive((string)$item->trigger_value);
$item->save();

View File

@@ -82,6 +82,7 @@ class DeleteEmptyJournals extends Command
TransactionJournal::find((int)$row->transaction_journal_id)->delete();
} catch (QueryException $e) {
Log::info(sprintf('Could not delete journal: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
}
@@ -113,6 +114,7 @@ class DeleteEmptyJournals extends Command
TransactionJournal::find($entry->id)->delete();
} catch (QueryException $e) {
Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
}

View File

@@ -32,6 +32,7 @@ use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
@@ -93,6 +94,7 @@ class UpdateGroupInformation extends Command
Bill::class,
Budget::class,
Category::class,
ObjectGroup::class,
CurrencyExchangeRate::class,
Recurrence::class,
RuleGroup::class,

View File

@@ -69,6 +69,9 @@ class UpgradeDatabase extends Command
'firefly-iii:liabilities-600',
'firefly-iii:budget-limit-periods',
'firefly-iii:restore-oauth-keys',
// also just in case, some integrity commands:
'firefly-iii:create-group-memberships',
'firefly-iii:upgrade-group-information',
];
$args = [];
if ($this->option('force')) {

View File

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

View File

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

View File

@@ -92,6 +92,7 @@ class AccountFactory
$return = $this->create(
[
'user_id' => $this->user->id,
'user_group_id' => $this->user->user_group_id,
'name' => $accountName,
'account_type_id' => $type->id,
'account_type_name' => null,
@@ -199,6 +200,7 @@ class AccountFactory
$active = array_key_exists('active', $data) ? $data['active'] : true;
$databaseData = [
'user_id' => $this->user->id,
'user_group_id' => $this->user->user_group_id,
'account_type_id' => $type->id,
'name' => $data['name'],
'order' => 25000,
@@ -216,6 +218,7 @@ class AccountFactory
}
// create account!
$account = Account::create($databaseData);
Log::channel('audit')->info(sprintf('Account #%d ("%s") has been created.', $account->id, $account->name));
// update meta data:
$data = $this->cleanMetaDataArray($account, $data);
@@ -226,6 +229,7 @@ class AccountFactory
$this->storeOpeningBalance($account, $data);
} catch (FireflyException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
// create credit liability data (only liabilities)
@@ -233,6 +237,7 @@ class AccountFactory
$this->storeCreditLiability($account, $data);
} catch (FireflyException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
// create notes

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Factory;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use Illuminate\Support\Facades\Log;
/**
* Class AccountMetaFactory

View File

@@ -67,6 +67,7 @@ class BillFactory
'match' => 'MIGRATED_TO_RULES',
'amount_min' => $data['amount_min'],
'user_id' => $this->user->id,
'user_group_id' => $this->user->user_group_id,
'transaction_currency_id' => $currency->id,
'amount_max' => $data['amount_max'],
'date' => $data['date'],

View File

@@ -70,12 +70,14 @@ class CategoryFactory
try {
return Category::create(
[
'user_id' => $this->user->id,
'name' => $categoryName,
'user_id' => $this->user->id,
'user_group_id' => $this->user->user_group_id,
'name' => $categoryName,
]
);
} catch (QueryException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException('400003: Could not store new category.', 0, $e);
}
}

View File

@@ -107,6 +107,7 @@ class RecurrenceFactory
$recurrence = new Recurrence(
[
'user_id' => $this->user->id,
'user_group_id' => $this->user->user_group_id,
'transaction_type_id' => $type->id,
'title' => $title,
'description' => $description,
@@ -129,6 +130,7 @@ class RecurrenceFactory
$this->createTransactions($recurrence, $data['transactions'] ?? []);
} catch (FireflyException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$recurrence->forceDelete();
$message = sprintf('Could not create recurring transaction: %s', $e->getMessage());
$this->errors->add('store', $message);

View File

@@ -83,14 +83,15 @@ class TagFactory
$latitude = 0.0 === (float)$data['latitude'] ? null : (float)$data['latitude']; // intentional float
$longitude = 0.0 === (float)$data['longitude'] ? null : (float)$data['longitude']; // intentional float
$array = [
'user_id' => $this->user->id,
'tag' => trim($data['tag']),
'tagMode' => 'nothing',
'date' => $data['date'],
'description' => $data['description'],
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'user_id' => $this->user->id,
'user_group_id' => $this->user->user_group_id,
'tag' => trim($data['tag']),
'tagMode' => 'nothing',
'date' => $data['date'],
'description' => $data['description'],
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
];
$tag = Tag::create($array);
if (null !== $tag && null !== $latitude && null !== $longitude) {

View File

@@ -69,6 +69,7 @@ class TransactionCurrencyFactory
} catch (QueryException $e) {
$result = null;
Log::error(sprintf('Could not create new currency: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
throw new FireflyException('400004: Could not store new currency.', 0, $e);
}

View File

@@ -110,7 +110,7 @@ class TransactionFactory
Log::error(sprintf('Could not create transaction: %s', $e->getMessage()), $data);
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException('Query exception when creating transaction.', 0, $e);
throw new FireflyException(sprintf('Query exception when creating transaction: %s', $e->getMessage()), 0, $e);
}
if (null === $result) {
throw new FireflyException('Transaction is NULL.');

View File

@@ -81,6 +81,7 @@ class TransactionGroupFactory
$group = new TransactionGroup();
$group->user()->associate($this->user);
$group->userGroup()->associate($this->user->userGroup);
$group->title = $title;
$group->save();

View File

@@ -225,6 +225,7 @@ class TransactionJournalFactory
$journal = TransactionJournal::create(
[
'user_id' => $this->user->id,
'user_group_id' => $this->user->user_group_id,
'transaction_type_id' => $type->id,
'bill_id' => $billId,
'transaction_currency_id' => $currency->id,
@@ -533,7 +534,7 @@ class TransactionJournalFactory
{
$description = '' === $description ? '(empty description)' : $description;
return substr($description, 0, 255);
return substr($description, 0, 1024);
}
/**

View File

@@ -75,6 +75,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.account.report: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = sprintf('Could not render report view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e);
}

View File

@@ -77,6 +77,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.category.month: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = sprintf('Could not render report view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e);
}

View File

@@ -59,6 +59,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
return view('reports.default.month', compact('accountIds', 'reportType'))->with('start', $this->start)->with('end', $this->end)->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.default.month: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render report view.';
throw new FireflyException($result, 0, $e);
}

View File

@@ -63,6 +63,7 @@ class MultiYearReportGenerator implements ReportGeneratorInterface
)->with('start', $this->start)->with('end', $this->end)->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.default.multi-year: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = sprintf('Could not render report view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e);
}

View File

@@ -63,6 +63,7 @@ class YearReportGenerator implements ReportGeneratorInterface
)->with('start', $this->start)->with('end', $this->end)->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.account.report: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render report view.';
throw new FireflyException($result, 0, $e);
}

View File

@@ -71,6 +71,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
)->with('start', $this->start)->with('end', $this->end)->with('tags', $this->tags)->with('accounts', $this->accounts)->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.tag.month: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = sprintf('Could not render report view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e);
}

View File

@@ -174,6 +174,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
Log::error(
sprintf('The transformer could not include the requested transaction group for webhook #%d: %s', $webhook->id, $e->getMessage())
);
Log::error($e->getTraceAsString());
return;
}

View File

@@ -131,6 +131,7 @@ class BudgetLimitHandler
$availableBudget = new AvailableBudget(
[
'user_id' => $budgetLimit->budget->user->id,
'user_group_id' => $budgetLimit->budget->user->user_group_id,
'transaction_currency_id' => $budgetLimit->transaction_currency_id,
'start_date' => $current,
'end_date' => $currentEnd,
@@ -165,7 +166,7 @@ class BudgetLimitHandler
$availableBudget->end_date->format('Y-m-d')
)
);
// have to recalc everything just in case.
// have to recalculate everything just in case.
$set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date);
Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count()));
/** @var BudgetLimit $budgetLimit */
@@ -185,15 +186,18 @@ class BudgetLimitHandler
precision : Precision::DAY(),
boundaries: Boundaries::EXCLUDE_NONE()
);
// if both equal eachother, amount from this BL must be added to the AB
// if both equal each other, amount from this BL must be added to the AB
if ($limitPeriod->equals($abPeriod)) {
app('log')->debug('This budget limit is equal to the available budget period.');
$newAmount = bcadd($newAmount, $budgetLimit->amount);
}
// if budget limit period inside AB period, can be added in full.
// if budget limit period is inside AB period, it can be added in full.
if (!$limitPeriod->equals($abPeriod) && $abPeriod->contains($limitPeriod)) {
app('log')->debug('This budget limit is smaller than the available budget period.');
$newAmount = bcadd($newAmount, $budgetLimit->amount);
}
if (!$limitPeriod->equals($abPeriod) && $abPeriod->overlapsWith($limitPeriod)) {
if (!$limitPeriod->equals($abPeriod) && !$abPeriod->contains($limitPeriod) && $abPeriod->overlapsWith($limitPeriod)) {
app('log')->debug('This budget limit is something else entirely!');
$overlap = $abPeriod->overlap($limitPeriod);
if (null !== $overlap) {
$length = $overlap->length();
@@ -208,7 +212,7 @@ class BudgetLimitHandler
return;
}
Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount));
$availableBudget->amount = $newAmount;
$availableBudget->amount = app('steam')->bcround($newAmount, $availableBudget->transactionCurrency->decimal_places);
$availableBudget->save();
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
/*
* RuleHandler.php
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Handlers\Events\Model;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Notifications\User\RuleActionFailed;
use FireflyIII\Support\Facades\Preferences;
use Illuminate\Support\Facades\Notification;
/**
* Class RuleHandler
*/
class RuleHandler
{
/**
* @param RuleActionFailedOnArray $event
*
* @return void
*/
public function ruleActionFailedOnArray(RuleActionFailedOnArray $event): void
{
$ruleAction = $event->ruleAction;
$rule = $ruleAction->rule;
$preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data;
if (false === $preference) {
return;
}
app('log')->debug('Now in ruleActionFailedOnArray');
$journal = $event->journal;
$error = $event->error;
$user = $ruleAction->rule->user;
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal['transaction_group_id'], 'error' => $error]);
$groupTitle = $journal['description'] ?? '';
$groupLink = route('transactions.show', [$journal['transaction_group_id']]);
$ruleTitle = $rule->title;
$ruleLink = route('rules.edit', [$rule->id]);
$params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink];
Notification::send($user, new RuleActionFailed($params));
}
/**
* @param RuleActionFailedOnObject $event
*
* @return void
*/
public function ruleActionFailedOnObject(RuleActionFailedOnObject $event): void
{
$ruleAction = $event->ruleAction;
$rule = $ruleAction->rule;
$preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data;
if (false === $preference) {
return;
}
app('log')->debug('Now in ruleActionFailedOnObject');
$journal = $event->journal;
$error = $event->error;
$user = $ruleAction->rule->user;
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal->transaction_group_id, 'error' => $error]);
$groupTitle = $journal->description ?? '';
$groupLink = route('transactions.show', [$journal->transaction_group_id]);
$ruleTitle = $rule->title;
$ruleLink = route('rules.edit', [$rule->id]);
$params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink];
Notification::send($user, new RuleActionFailed($params));
}
}

View File

@@ -275,6 +275,7 @@ class UserEventHandler
Mail::to($newEmail)->send(new ConfirmEmailChangeMail($newEmail, $oldEmail, $url));
} catch (Exception $e) { // intentional generic exception
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException($e->getMessage(), 0, $e);
}
}
@@ -299,6 +300,7 @@ class UserEventHandler
Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $url));
} catch (Exception $e) { // intentional generic exception
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException($e->getMessage(), 0, $e);
}
}
@@ -342,6 +344,7 @@ class UserEventHandler
Mail::to($invitee)->send(new InvitationMail($invitee, $admin, $url));
} catch (Exception $e) { // intentional generic exception
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException($e->getMessage(), 0, $e);
}
}

View File

@@ -57,7 +57,7 @@ class VersionCheckEventHandler
$permission = app('fireflyconfig')->get('permission_update_check', -1);
$value = (int)$permission->data;
if (1 !== $value) {
Log::info('Update check is not enabled.');
Log::debug('Update check is not enabled.');
$this->warnToCheckForUpdates($event);
return;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Collector\Extensions;
use FireflyIII\Models\UserGroup;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -33,22 +34,29 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
trait CollectorProperties
{
public const TEST = 'Test';
private bool $expandGroupSearch;
private array $fields;
private bool $hasAccountInfo;
private bool $hasBillInformation;
private bool $hasBudgetInformation;
private bool $hasCatInformation;
private bool $hasJoinedAttTables;
private bool $hasJoinedMetaTables;
private bool $hasJoinedTagTables;
private bool $hasNotesInformation;
private array $integerFields;
private ?int $limit;
private ?int $page;
private array $postFilters;
private bool $expandGroupSearch;
private array $fields;
private bool $hasAccountInfo;
private bool $hasBillInformation;
private bool $hasBudgetInformation;
private bool $hasCatInformation;
private bool $hasJoinedAttTables;
private bool $hasJoinedMetaTables;
private bool $hasJoinedTagTables;
private bool $hasNotesInformation;
private array $integerFields;
private ?int $limit;
private ?int $page;
private array $postFilters;
private HasMany $query;
private array $stringFields;
private int $total;
private ?User $user;
private array $stringFields;
/*
* This array is used to collect ALL tags the user may search for (using 'setTags').
* This way the user can call 'setTags' multiple times and get a joined result.
*
*/
private array $tags;
private int $total;
private ?User $user;
private ?UserGroup $userGroup;
}

View File

@@ -64,8 +64,8 @@ trait MetaCollection
// join bill table
$this->query->leftJoin('bills', 'bills.id', '=', 'transaction_journals.bill_id');
// add fields
$this->fields[] = 'bills.id as bill_id';
$this->fields[] = 'bills.name as bill_name';
$this->fields[] = 'bills.id as bill_id';
$this->fields[] = 'bills.name as bill_name';
$this->hasBillInformation = true;
}
@@ -104,8 +104,8 @@ trait MetaCollection
// join cat table
$this->query->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id');
// add fields
$this->fields[] = 'budgets.id as budget_id';
$this->fields[] = 'budgets.name as budget_name';
$this->fields[] = 'budgets.id as budget_id';
$this->fields[] = 'budgets.name as budget_name';
$this->hasBudgetInformation = true;
}
@@ -157,8 +157,8 @@ trait MetaCollection
// join cat table
$this->query->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id');
// add fields
$this->fields[] = 'categories.id as category_id';
$this->fields[] = 'categories.name as category_name';
$this->fields[] = 'categories.id as category_id';
$this->fields[] = 'categories.name as category_name';
$this->hasCatInformation = true;
}
@@ -603,7 +603,7 @@ trait MetaCollection
}
);
// add fields
$this->fields[] = 'notes.text as notes';
$this->fields[] = 'notes.text as notes';
$this->hasNotesInformation = true;
}
@@ -896,7 +896,8 @@ trait MetaCollection
public function setTags(Collection $tags): GroupCollectorInterface
{
$this->withTagInformation();
$this->query->whereIn('tag_transaction_journal.tag_id', $tags->pluck('id')->toArray());
$this->tags = array_merge($this->tags, $tags->pluck('id')->toArray());
$this->query->whereIn('tag_transaction_journal.tag_id', $this->tags);
return $this;
}
@@ -913,8 +914,8 @@ trait MetaCollection
$this->withTagInformation();
// this method adds a "postFilter" to the collector.
$list = $tags->pluck('tag')->toArray();
$filter = function (int $index, array $object) use ($list): bool {
$list = $tags->pluck('tag')->toArray();
$filter = function (int $index, array $object) use ($list): bool {
foreach ($object['transactions'] as $transaction) {
foreach ($transaction['tags'] as $tag) {
if (in_array($tag['name'], $list, true)) {

View File

@@ -675,6 +675,23 @@ trait TimeCollection
return $this;
}
/**
* Set the end time of the results to return.
*
* @param Carbon $end
*
* @return GroupCollectorInterface
*/
public function setEnd(Carbon $end): GroupCollectorInterface
{
// always got to end of day / start of day for ranges.
$endStr = $end->format('Y-m-d 23:59:59');
$this->query->where('transaction_journals.date', '<=', $endStr);
return $this;
}
/**
* @param Carbon $date
* @param string $field
@@ -822,6 +839,22 @@ trait TimeCollection
return $this;
}
/**
* Set the start time of the results to return.
*
* @param Carbon $start
*
* @return GroupCollectorInterface
*/
public function setStart(Carbon $start): GroupCollectorInterface
{
$startStr = $start->format('Y-m-d 00:00:00');
$this->query->where('transaction_journals.date', '>=', $startStr);
return $this;
}
/**
* Collect transactions updated on a specific date.
*

View File

@@ -38,6 +38,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Models\UserGroup;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\JoinClause;
@@ -65,7 +66,9 @@ class GroupCollector implements GroupCollectorInterface
public function __construct()
{
$this->postFilters = [];
$this->tags = [];
$this->user = null;
$this->userGroup = null;
$this->limit = null;
$this->page = null;
@@ -81,6 +84,7 @@ class GroupCollector implements GroupCollectorInterface
$this->integerFields = [
'transaction_group_id',
'user_id',
'user_group_id',
'transaction_journal_id',
'transaction_type_id',
'order',
@@ -101,6 +105,7 @@ class GroupCollector implements GroupCollectorInterface
# group
'transaction_groups.id as transaction_group_id',
'transaction_groups.user_id as user_id',
'transaction_groups.user_group_id as user_group_id',
'transaction_groups.created_at as created_at',
'transaction_groups.updated_at as updated_at',
'transaction_groups.title as transaction_group_title',
@@ -299,7 +304,20 @@ class GroupCollector implements GroupCollectorInterface
*/
public function dumpQuery(): void
{
echo $this->query->select($this->fields)->toSql();
$query = $this->query->select($this->fields)->toSql();
$params = $this->query->getBindings();
foreach ($params as $param) {
$replace = sprintf('"%s"', $param);
if (is_int($param)) {
$replace = (string)$param;
}
$pos = strpos($query, '?');
if ($pos !== false) {
$query = substr_replace($query, $replace, $pos, 1);
}
}
echo $query;
echo '<pre>';
print_r($this->query->getBindings());
echo '</pre>';
@@ -547,6 +565,7 @@ class GroupCollector implements GroupCollectorInterface
$groupArray = [
'id' => (int)$augumentedJournal->transaction_group_id,
'user_id' => (int)$augumentedJournal->user_id,
'user_group_id' => (int)$augumentedJournal->user_group_id,
// Field transaction_group_title was added by the query.
'title' => $augumentedJournal->transaction_group_title, // @phpstan-ignore-line
'transaction_type' => $parsedGroup['transaction_type_type'],
@@ -852,7 +871,7 @@ class GroupCollector implements GroupCollectorInterface
public function setLimit(int $limit): GroupCollectorInterface
{
$this->limit = $limit;
app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit));
//app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit));
return $this;
}
@@ -957,7 +976,7 @@ class GroupCollector implements GroupCollectorInterface
{
$page = 0 === $page ? 1 : $page;
$this->page = $page;
app('log')->debug(sprintf('GroupCollector: page is now %d', $page));
//app('log')->debug(sprintf('GroupCollector: page is now %d', $page));
return $this;
}
@@ -1086,6 +1105,64 @@ class GroupCollector implements GroupCollectorInterface
->orderBy('source.amount', 'DESC');
}
/**
* Set the user object and start the query.
*
* @param User $user
*
* @return GroupCollectorInterface
*/
public function setUserGroup(UserGroup $userGroup): GroupCollectorInterface
{
if (null === $this->userGroup) {
$this->userGroup = $userGroup;
$this->startQueryForGroup();
}
return $this;
}
/**
* Build the query.
*/
private function startQueryForGroup(): void
{
//app('log')->debug('GroupCollector::startQuery');
$this->query = $this->userGroup
->transactionJournals()
->leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
// join source transaction.
->leftJoin(
'transactions as source',
function (JoinClause $join) {
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
->where('source.amount', '<', 0);
}
)
// join destination transaction
->leftJoin(
'transactions as destination',
function (JoinClause $join) {
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
->where('destination.amount', '>', 0);
}
)
// left join transaction type.
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transaction_currencies as currency', 'currency.id', '=', 'source.transaction_currency_id')
->leftJoin('transaction_currencies as foreign_currency', 'foreign_currency.id', '=', 'source.foreign_currency_id')
->whereNull('transaction_groups.deleted_at')
->whereNull('transaction_journals.deleted_at')
->whereNull('source.deleted_at')
->whereNull('destination.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->orderBy('source.amount', 'DESC');
}
/**
* Automatically include all stuff required to make API calls work.
*

View File

@@ -30,6 +30,7 @@ use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\UserGroup;
use FireflyIII\User;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
@@ -1077,6 +1078,15 @@ interface GroupCollectorInterface
*/
public function setDestinationAccounts(Collection $accounts): GroupCollectorInterface;
/**
* Set the end time of the results to return.
*
* @param Carbon $end
*
* @return GroupCollectorInterface
*/
public function setEnd(Carbon $end): GroupCollectorInterface;
/**
* @param bool $expandGroupSearch
*/
@@ -1261,6 +1271,15 @@ interface GroupCollectorInterface
*/
public function setSourceAccounts(Collection $accounts): GroupCollectorInterface;
/**
* Set the start time of the results to return.
*
* @param Carbon $start
*
* @return GroupCollectorInterface
*/
public function setStart(Carbon $start): GroupCollectorInterface;
/**
* Limit results to a specific tag.
*
@@ -1315,6 +1334,15 @@ interface GroupCollectorInterface
*/
public function setUser(User $user): GroupCollectorInterface;
/**
* Set the user group object and start the query.
*
* @param UserGroup $userGroup
*
* @return GroupCollectorInterface
*/
public function setUserGroup(UserGroup $userGroup): GroupCollectorInterface;
/**
* Only when does not have these tags
*

View File

@@ -27,9 +27,12 @@ use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Collection;
@@ -41,10 +44,98 @@ use JsonException;
*/
class NetWorth implements NetWorthInterface
{
private AccountRepositoryInterface $accountRepository;
private AccountRepositoryInterface $accountRepository;
private AdminAccountRepositoryInterface $adminAccountRepository;
private CurrencyRepositoryInterface $currencyRepos;
private User $user;
private UserGroup $userGroup;
/**
* @param Collection $accounts
* @param Carbon $date
*
* @return array
* @throws FireflyException
*/
public function byAccounts(Collection $accounts, Carbon $date): array
{
// start in the past, end in the future? use $date
$ids = implode(',', $accounts->pluck('id')->toArray());
$cache = new CacheProperties();
$cache->addProperty($date);
$cache->addProperty('net-worth-by-accounts');
$cache->addProperty($ids);
if ($cache->has()) {
//return $cache->get();
}
app('log')->debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d')));
$default = app('amount')->getDefaultCurrency();
$converter = new ExchangeRateConverter();
// default "native" currency has everything twice, for consistency.
$netWorth = [
'native' => [
'balance' => '0',
'native_balance' => '0',
'currency_id' => (int)$default->id,
'currency_code' => $default->code,
'currency_name' => $default->name,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => (int)$default->decimal_places,
'native_id' => (int)$default->id,
'native_code' => $default->code,
'native_name' => $default->name,
'native_symbol' => $default->symbol,
'native_decimal_places' => (int)$default->decimal_places,
],
];
$balances = app('steam')->balancesByAccountsConverted($accounts, $date);
/** @var Account $account */
foreach ($accounts as $account) {
app('log')->debug(sprintf('Now at account #%d ("%s")', $account->id, $account->name));
$currency = $this->adminAccountRepository->getAccountCurrency($account);
$currencyId = (int)$currency->id;
$balance = '0';
$nativeBalance = '0';
if (array_key_exists((int)$account->id, $balances)) {
$balance = $balances[(int)$account->id]['balance'] ?? '0';
$nativeBalance = $balances[(int)$account->id]['native_balance'] ?? '0';
}
app('log')->debug(sprintf('Balance is %s, native balance is %s', $balance, $nativeBalance));
// always subtract virtual balance
$virtualBalance = (string)$account->virtual_balance;
if ('' !== $virtualBalance) {
$balance = bcsub($balance, $virtualBalance);
$nativeVirtualBalance = $converter->convert($default, $currency, $account->created_at, $virtualBalance);
$nativeBalance = bcsub($nativeBalance, $nativeVirtualBalance);
}
$netWorth[$currencyId] = $netWorth[$currencyId] ?? [
'balance' => '0',
'native_balance' => '0',
'currency_id' => $currencyId,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => (int)$currency->decimal_places,
'native_id' => (int)$default->id,
'native_code' => $default->code,
'native_name' => $default->name,
'native_symbol' => $default->symbol,
'native_decimal_places' => (int)$default->decimal_places,
];
$netWorth[$currencyId]['balance'] = bcadd($balance, $netWorth[$currencyId]['balance']);
$netWorth[$currencyId]['native_balance'] = bcadd($nativeBalance, $netWorth[$currencyId]['native_balance']);
$netWorth['native']['balance'] = bcadd($nativeBalance, $netWorth['native']['balance']);
$netWorth['native']['native_balance'] = bcadd($nativeBalance, $netWorth['native']['native_balance']);
}
$cache->store($netWorth);
return $netWorth;
}
/**
* Returns the user's net worth in an array with the following layout:
@@ -146,6 +237,17 @@ class NetWorth implements NetWorthInterface
$this->currencyRepos->setUser($this->user);
}
/**
* @inheritDoc
* @throws FireflyException
*/
public function setUserGroup(UserGroup $userGroup): void
{
$this->userGroup = $userGroup;
$this->adminAccountRepository = app(AdminAccountRepositoryInterface::class);
$this->adminAccountRepository->setAdministrationId($userGroup->id);
}
/**
* @inheritDoc
*/

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Models\UserGroup;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Collection;
@@ -34,6 +35,21 @@ use Illuminate\Support\Collection;
*/
interface NetWorthInterface
{
/**
* Collect net worth based on the given set of accounts.
*
* Returns X arrays with the net worth in each given currency, and the net worth in
* of that amount in the native currency.
*
* Includes extra array with the total(!) net worth in the native currency.
*
* @param Collection $accounts
* @param Carbon $date
*
* @return array
*/
public function byAccounts(Collection $accounts, Carbon $date): array;
/**
* TODO unsure why this is deprecated.
*
@@ -60,6 +76,11 @@ interface NetWorthInterface
*/
public function setUser(User | Authenticatable | null $user): void;
/**
* @param UserGroup $userGroup
*/
public function setUserGroup(UserGroup $userGroup): void;
/**
* TODO move to repository
*

View File

@@ -198,6 +198,11 @@ class ShowController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page)->withAccountInformation()->withCategoryInformation();
// this search will not include transaction groups where this asset account (or liability)
// is just part of ONE of the journals. To force this:
$collector->setExpandGroupSearch(true);
$groups = $collector->getPaginatedGroups();
$groups->setPath(route('accounts.show.all', [$account->id]));
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);

View File

@@ -27,6 +27,7 @@ use FireflyIII\Events\AdminRequestedTestMessage;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
@@ -99,7 +100,7 @@ class HomeController extends Controller
if ('' === $url) {
FireflyConfig::delete('slack_webhook_url');
}
if (str_starts_with($url, 'https://hooks.slack.com/services/')) {
if (UrlValidator::isValidWebhookURL($url)) {
FireflyConfig::set('slack_webhook_url', $url);
}

View File

@@ -90,7 +90,7 @@ class LoginController extends Controller
Log::info('User is trying to login.');
$this->validateLogin($request);
Log::debug('Login data is valid.');
Log::debug('Login data is present.');
/** Copied directly from AuthenticatesUsers, but with logging added: */
// If the class is using the ThrottlesLogins trait, we can automatically throttle

View File

@@ -146,7 +146,7 @@ class IndexController extends Controller
private function getSums(array $bills): array
{
$sums = [];
$range = app('navigation')->getViewRange(false);
$range = app('navigation')->getViewRange(true);
/** @var array $group */
foreach ($bills as $groupOrder => $group) {
@@ -177,7 +177,6 @@ class IndexController extends Controller
$sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $this->amountPerPeriod($bill, $range));
}
}
return $sums;
}

View File

@@ -101,6 +101,7 @@ class IndexController extends Controller
*/
public function index(Request $request, Carbon $start = null, Carbon $end = null)
{
$this->abRepository->cleanup();
Log::debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d')));
// collect some basic vars:
@@ -337,6 +338,7 @@ class IndexController extends Controller
*/
public function reorder(Request $request, BudgetRepositoryInterface $repository): JsonResponse
{
$this->abRepository->cleanup();
$budgetIds = $request->get('budgetIds');
foreach ($budgetIds as $index => $budgetId) {

View File

@@ -138,6 +138,9 @@ class HomeController extends Controller
$accounts = $repository->getAccountsById($frontPage->data);
$today = today(config('app.timezone'));
// sort frontpage accounts by order
$accounts = $accounts->sortBy('order');
Log::debug('Frontpage accounts are ', $frontPage->data);
/** @var BillRepositoryInterface $billRepository */

View File

@@ -55,10 +55,12 @@ class BoxController extends Controller
*/
public function available(): JsonResponse
{
app('log')->debug('Now in available()');
/** @var OperationsRepositoryInterface $opsRepository */
$opsRepository = app(OperationsRepositoryInterface::class);
/** @var AvailableBudgetRepositoryInterface $abRepository */
$abRepository = app(AvailableBudgetRepositoryInterface::class);
$abRepository->cleanup();
/** @var Carbon $start */
$start = session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */
@@ -73,40 +75,57 @@ class BoxController extends Controller
$cache->addProperty($today);
$cache->addProperty('box-available');
if ($cache->has()) {
return response()->json($cache->get());
//return response()->json($cache->get());
}
$leftPerDayAmount = '0';
$leftToSpendAmount = '0';
$currency = app('amount')->getDefaultCurrency();
$availableBudgets = $abRepository->getAvailableBudgetsByDate($start, $end);
$currency = app('amount')->getDefaultCurrency();
app('log')->debug(sprintf('Default currency is %s', $currency->code));
$availableBudgets = $abRepository->getAvailableBudgetsByExactDate($start, $end);
app('log')->debug(sprintf('Found %d available budget(s)', $availableBudgets->count()));
$availableBudgets = $availableBudgets->filter(
static function (AvailableBudget $availableBudget) use ($currency) {
if ($availableBudget->transaction_currency_id === $currency->id) {
app('log')->debug(sprintf(
'Will include AB #%d: from %s-%s amount %s',
$availableBudget->id,
$availableBudget->start_date->format('Y-m-d'),
$availableBudget->end_date->format('Y-m-d'),
$availableBudget->amount
));
return $availableBudget;
}
return null;
}
);
app('log')->debug(sprintf('Filtered back to %d available budgets', $availableBudgets->count()));
// spent in this period, in budgets, for default currency.
// also calculate spent per day.
$spent = $opsRepository->sumExpenses($start, $end, null, null, $currency);
$spentAmount = $spent[(int)$currency->id]['sum'] ?? '0';
app('log')->debug(sprintf('Spent for default currency for all budgets in this period: %s', $spentAmount));
$days = $today->between($start, $end) ? $today->diffInDays($start) + 1 : $end->diffInDays($start) + 1;
$days = $today->between($start, $end) ? $today->diffInDays($start) + 1 : $end->diffInDays($start) + 1;
app('log')->debug(sprintf('Number of days left: %d', $days));
$spentPerDay = bcdiv($spentAmount, (string)$days);
app('log')->debug(sprintf('Available to spend per day: %s', $spentPerDay));
if ($availableBudgets->count() > 0) {
$display = 0; // assume user overspent
$boxTitle = (string)trans('firefly.overspent');
$totalAvailableSum = (string)$availableBudgets->sum('amount');
app('log')->debug(sprintf('Total available sum is %s', $totalAvailableSum));
// calculate with available budget.
$leftToSpendAmount = bcadd($totalAvailableSum, $spentAmount);
app('log')->debug(sprintf('So left to spend is %s', $leftToSpendAmount));
if (1 === bccomp($leftToSpendAmount, '0')) {
app('log')->debug(sprintf('Left to spend is positive!'));
$boxTitle = (string)trans('firefly.left_to_spend');
$days = $today->diffInDays($end) + 1;
$display = 1; // not overspent
$leftPerDayAmount = bcdiv($leftToSpendAmount, (string)$days);
app('log')->debug(sprintf('Left to spend per day is %s', $leftPerDayAmount));
}
}
@@ -118,9 +137,10 @@ class BoxController extends Controller
'left_per_day' => app('amount')->formatAnything($currency, $leftPerDayAmount, false),
'title' => $boxTitle,
];
app('log')->debug('Final output', $return);
$cache->store($return);
app('log')->debug('Now done with available()');
return response()->json($return);
}

View File

@@ -75,6 +75,7 @@ class FrontpageController extends Controller
$html = view('json.piggy-banks', compact('info'))->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render json.piggy-banks: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$html = 'Could not render view.';
throw new FireflyException($html, 0, $e);
}

View File

@@ -157,6 +157,7 @@ class ReconcileController extends Controller
)->render();
} catch (Throwable $e) {
Log::debug(sprintf('View error: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$view = sprintf('Could not render accounts.reconcile.overview: %s', $e->getMessage());
throw new FireflyException($view, 0, $e);
}
@@ -258,6 +259,7 @@ class ReconcileController extends Controller
)->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$html = sprintf('Could not render accounts.reconcile.transactions: %s', $e->getMessage());
throw new FireflyException($html, 0, $e);
}

View File

@@ -55,6 +55,7 @@ class RuleController extends Controller
$view = view('rules.partials.action', compact('actions', 'count'))->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render rules.partials.action: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$view = 'Could not render view.';
throw new FireflyException($view, 0, $e);
}
@@ -86,6 +87,7 @@ class RuleController extends Controller
$view = view('rules.partials.trigger', compact('triggers', 'count'))->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render rules.partials.trigger: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$view = 'Could not render view.';
throw new FireflyException($view, 0, $e);
}

View File

@@ -28,6 +28,7 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Notifications\UrlValidator;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -207,7 +208,7 @@ class PreferencesController extends Controller
// slack URL:
if (!auth()->user()->hasRole('demo')) {
$url = (string)$request->get('slackUrl');
if (str_starts_with($url, 'https://hooks.slack.com/services/')) {
if (UrlValidator::isValidWebhookURL($url)) {
app('preferences')->set('slack_webhook_url', $url);
}
if ('' === $url) {

View File

@@ -65,7 +65,8 @@ class AccountController extends Controller
try {
$result = view('reports.partials.accounts', compact('accountReport'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.accounts: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.accounts: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.';
throw new FireflyException($result, 0, $e);
}

View File

@@ -141,7 +141,8 @@ class BalanceController extends Controller
try {
$result = view('reports.partials.balance', compact('report'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.balance: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.balance: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.';
throw new FireflyException($result, 0, $e);
}

View File

@@ -61,7 +61,8 @@ class BillController extends Controller
try {
$result = view('reports.partials.bills', compact('report'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budgets: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budgets: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.';
throw new FireflyException($result, 0, $e);
}

View File

@@ -195,8 +195,9 @@ class BudgetController extends Controller
try {
$result = view('reports.budget.partials.avg-expenses', compact('result'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException($result, 0, $e);
}
@@ -353,7 +354,8 @@ class BudgetController extends Controller
try {
$result = view('reports.partials.budget-period', compact('report', 'periods'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.';
throw new FireflyException($result, 0, $e);
}
@@ -406,7 +408,7 @@ class BudgetController extends Controller
try {
$result = view('reports.budget.partials.top-expenses', compact('result'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e);
}

View File

@@ -315,7 +315,7 @@ class CategoryController extends Controller
try {
$result = view('reports.category.partials.avg-expenses', compact('result'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e);
}
@@ -368,7 +368,7 @@ class CategoryController extends Controller
try {
$result = view('reports.category.partials.avg-income', compact('result'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e);
}

View File

@@ -115,7 +115,7 @@ class DoubleController extends Controller
try {
$result = view('reports.double.partials.avg-expenses', compact('result'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($e->getMessage(), 0, $e);
}
@@ -168,7 +168,7 @@ class DoubleController extends Controller
try {
$result = view('reports.double.partials.avg-income', compact('result'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($e->getMessage(), 0, $e);
}
@@ -463,7 +463,7 @@ class DoubleController extends Controller
try {
$result = view('reports.double.partials.top-expenses', compact('result'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($e->getMessage(), 0, $e);
}
@@ -514,7 +514,7 @@ class DoubleController extends Controller
try {
$result = view('reports.double.partials.top-income', compact('result'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($e->getMessage(), 0, $e);
}

View File

@@ -85,7 +85,8 @@ class OperationsController extends Controller
try {
$result = view('reports.partials.income-expenses', compact('report', 'type'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.income-expense: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.income-expense: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.';
throw new FireflyException($result, 0, $e);
}
@@ -121,7 +122,8 @@ class OperationsController extends Controller
try {
$result = view('reports.partials.income-expenses', compact('report', 'type'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.income-expenses: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.income-expenses: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.';
throw new FireflyException($result, 0, $e);
}
@@ -177,7 +179,8 @@ class OperationsController extends Controller
try {
$result = view('reports.partials.operations', compact('sums'))->render();
} catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.operations: %s', $e->getMessage()));
Log::error(sprintf('Could not render reports.partials.operations: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.';
throw new FireflyException($result, 0, $e);
}

View File

@@ -128,6 +128,7 @@ class SearchController extends Controller
$html = view('search.search', compact('groups', 'hasPages', 'searchTime'))->render();
} catch (Throwable $e) {
Log::error(sprintf('Cannot render search.search: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$html = 'Could not render view.';
throw new FireflyException($html, 0, $e);
}

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Http\Middleware;
use Closure;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Vite;
/**
*
@@ -46,6 +47,7 @@ class SecureHeaders
{
// generate and share nonce.
$nonce = base64_encode(random_bytes(16));
Vite::useCspNonce($nonce);
app('view')->share('JS_NONCE', $nonce);
$response = $next($request);

View File

@@ -158,6 +158,7 @@ class ReportFormRequest extends FormRequest
} catch (Exception $e) { // intentional generic exception
$error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage());
Log::error($error);
Log::error($e->getTraceAsString());
throw new FireflyException($error, 0, $e);
}
return $date;
@@ -192,6 +193,7 @@ class ReportFormRequest extends FormRequest
} catch (Exception $e) { // intentional generic exception
$error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage());
Log::error($error);
Log::error($e->getTraceAsString());
throw new FireflyException($error, 0, $e);
}
return $date;
@@ -220,19 +222,18 @@ class ReportFormRequest extends FormRequest
}
if (!is_array($set)) {
Log::error(sprintf('Set is not an array! "%s"', $set));
return $collection;
}
if (is_array($set)) {
foreach ($set as $tagTag) {
Log::debug(sprintf('Now searching for "%s"', $tagTag));
$tag = $repository->findByTag($tagTag);
if (null !== $tag) {
$collection->push($tag);
continue;
}
$tag = $repository->find((int)$tagTag);
if (null !== $tag) {
$collection->push($tag);
}
foreach ($set as $tagTag) {
Log::debug(sprintf('Now searching for "%s"', $tagTag));
$tag = $repository->findByTag($tagTag);
if (null !== $tag) {
$collection->push($tag);
continue;
}
$tag = $repository->find((int)$tagTag);
if (null !== $tag) {
$collection->push($tag);
}
}

View File

@@ -125,7 +125,7 @@ class Account extends Model
'encrypted' => 'boolean',
];
/** @var array Fields that can be filled */
protected $fillable = ['user_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban'];
protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban'];
/** @var array Hidden from view */
protected $hidden = ['encrypted'];
private bool $joinedAccountTypes = false;

View File

@@ -85,7 +85,7 @@ class AvailableBudget extends Model
'transaction_currency_id' => 'int',
];
/** @var array Fields that can be filled */
protected $fillable = ['user_id', 'transaction_currency_id', 'amount', 'start_date', 'end_date'];
protected $fillable = ['user_id', 'user_group_id', 'transaction_currency_id', 'amount', 'start_date', 'end_date'];
/**
* Route binder. Converts the key in the URL to the specified object (or throw 404).

View File

@@ -130,6 +130,7 @@ class Bill extends Model
'match',
'amount_min',
'user_id',
'user_group_id',
'amount_max',
'date',
'repeat_freq',

View File

@@ -99,7 +99,7 @@ class Budget extends Model
'encrypted' => 'boolean',
];
/** @var array Fields that can be filled */
protected $fillable = ['user_id', 'name', 'active', 'order'];
protected $fillable = ['user_id', 'name', 'active', 'order', 'user_group_id'];
/** @var array Hidden from view */
protected $hidden = ['encrypted'];

View File

@@ -89,7 +89,7 @@ class Category extends Model
'encrypted' => 'boolean',
];
/** @var array Fields that can be filled */
protected $fillable = ['user_id', 'name'];
protected $fillable = ['user_id', 'user_group_id', 'name'];
/** @var array Hidden from view */
protected $hidden = ['encrypted'];

View File

@@ -61,6 +61,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static Builder|ObjectGroup whereTitle($value)
* @method static Builder|ObjectGroup whereUpdatedAt($value)
* @method static Builder|ObjectGroup whereUserId($value)
* @property int|null $user_group_id
* @method static Builder|ObjectGroup whereUserGroupId($value)
* @mixin Eloquent
*/
class ObjectGroup extends Model
@@ -77,7 +79,7 @@ class ObjectGroup extends Model
'user_id' => 'integer',
'deleted_at' => 'datetime',
];
protected $fillable = ['title', 'order', 'user_id'];
protected $fillable = ['title', 'order', 'user_id', 'user_group_id'];
/**
* Route binder. Converts the key in the URL to the specified object (or throw 404).

View File

@@ -76,6 +76,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static Builder|Rule withoutTrashed()
* @property int|null $user_group_id
* @method static \Illuminate\Database\Eloquent\Builder|Rule whereUserGroupId($value)
* @property-read \FireflyIII\Models\UserGroup|null $userGroup
* @mixin Eloquent
*/
class Rule extends Model
@@ -165,4 +166,12 @@ class Rule extends Model
{
$this->attributes['description'] = e($value);
}
/**
* @return BelongsTo
*/
public function userGroup(): BelongsTo
{
return $this->belongsTo(UserGroup::class);
}
}

View File

@@ -99,7 +99,7 @@ class Tag extends Model
'longitude' => 'float',
];
/** @var array Fields that can be filled */
protected $fillable = ['user_id', 'tag', 'date', 'description', 'tagMode'];
protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'description', 'tagMode'];
protected $hidden = ['zoomLevel', 'latitude', 'longitude'];

View File

@@ -61,6 +61,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static Builder|TransactionGroup withoutTrashed()
* @property int|null $user_group_id
* @method static \Illuminate\Database\Eloquent\Builder|TransactionGroup whereUserGroupId($value)
* @property-read \FireflyIII\Models\UserGroup|null $userGroup
* @mixin Eloquent
*/
class TransactionGroup extends Model
@@ -130,4 +131,12 @@ class TransactionGroup extends Model
{
return $this->hasMany(TransactionJournal::class);
}
/**
* @return BelongsTo
*/
public function userGroup(): BelongsTo
{
return $this->belongsTo(UserGroup::class);
}
}

View File

@@ -148,6 +148,7 @@ class TransactionJournal extends Model
protected $fillable
= [
'user_id',
'user_group_id',
'transaction_type_id',
'bill_id',
'tag_count',

View File

@@ -29,6 +29,7 @@ use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Support\Carbon;
/**
@@ -51,6 +52,16 @@ use Illuminate\Support\Carbon;
* @method static Builder|UserGroup whereUpdatedAt($value)
* @property-read Collection<int, Account> $accounts
* @property-read int|null $accounts_count
* @property-read Collection<int, \FireflyIII\Models\AvailableBudget> $availableBudgets
* @property-read int|null $available_budgets_count
* @property-read Collection<int, \FireflyIII\Models\Bill> $bills
* @property-read int|null $bills_count
* @property-read Collection<int, \FireflyIII\Models\Budget> $budgets
* @property-read int|null $budgets_count
* @property-read Collection<int, \FireflyIII\Models\PiggyBank> $piggyBanks
* @property-read int|null $piggy_banks_count
* @property-read Collection<int, \FireflyIII\Models\TransactionJournal> $transactionJournals
* @property-read int|null $transaction_journals_count
* @mixin Eloquent
*/
class UserGroup extends Model
@@ -67,6 +78,36 @@ class UserGroup extends Model
return $this->hasMany(Account::class);
}
/**
* Link to bills.
*
* @return HasMany
*/
public function availableBudgets(): HasMany
{
return $this->hasMany(AvailableBudget::class);
}
/**
* Link to bills.
*
* @return HasMany
*/
public function bills(): HasMany
{
return $this->hasMany(Bill::class);
}
/**
* Link to budgets.
*
* @return HasMany
*/
public function budgets(): HasMany
{
return $this->hasMany(Budget::class);
}
/**
*
* @return HasMany
@@ -75,4 +116,24 @@ class UserGroup extends Model
{
return $this->hasMany(GroupMembership::class);
}
/**
* Link to piggy banks.
*
* @return HasManyThrough
*/
public function piggyBanks(): HasManyThrough
{
return $this->hasManyThrough(PiggyBank::class, Account::class);
}
/**
* Link to transaction journals.
*
* @return HasMany
*/
public function transactionJournals(): HasMany
{
return $this->hasMany(TransactionJournal::class);
}
}

View File

@@ -88,7 +88,7 @@ class Webhook extends Model
'response' => 'integer',
'delivery' => 'integer',
];
protected $fillable = ['active', 'trigger', 'response', 'delivery', 'user_id', 'url', 'title', 'secret'];
protected $fillable = ['active', 'trigger', 'response', 'delivery', 'user_id', 'user_group_id', 'url', 'title', 'secret'];
/**
* @return array

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Admin;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
@@ -99,9 +100,9 @@ class TestNotification extends Notification
public function via($notifiable)
{
/** @var User|null $user */
$user = auth()->user();
$user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) {
if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack'];
}
return ['mail'];

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Admin;
use FireflyIII\Models\InvitedUser;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
@@ -102,9 +103,9 @@ class UserInvitation extends Notification
public function via($notifiable)
{
/** @var User|null $user */
$user = auth()->user();
$user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) {
if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack'];
}
return ['mail'];

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Admin;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
@@ -99,9 +100,9 @@ class UserRegistration extends Notification
public function via($notifiable)
{
/** @var User|null $user */
$user = auth()->user();
$user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) {
if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack'];
}
return ['mail'];

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Admin;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
@@ -87,21 +88,10 @@ class VersionCheckResult extends Notification
*/
public function toSlack($notifiable)
{
// return (new SlackMessage())->text($this->message)
// ->sectionBlock(function (SectionBlock $block) {
// $button = new ButtonElement('Button');
// $button->url('https://github.com/firefly-iii/firefly-iii/releases');
// $block->accessory($button);
// });
//// ->attachment(function ($attachment) {
//// $attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases');
//// });
return (new SlackMessage())->content($this->message)
->attachment(function ($attachment) {
$attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases');
});
->attachment(function ($attachment) {
$attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases');
});
}
/**
@@ -114,9 +104,9 @@ class VersionCheckResult extends Notification
public function via($notifiable)
{
/** @var User|null $user */
$user = auth()->user();
$user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) {
if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack'];
}
return ['mail'];

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User;
use FireflyIII\Models\Bill;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
@@ -38,8 +39,8 @@ class BillReminder extends Notification
{
use Queueable;
private Bill $bill;
private int $diff;
private Bill $bill;
private int $diff;
private string $field;
/**
@@ -49,9 +50,9 @@ class BillReminder extends Notification
*/
public function __construct(Bill $bill, string $field, int $diff)
{
$this->bill = $bill;
$this->bill = $bill;
$this->field = $field;
$this->diff = $diff;
$this->diff = $diff;
}
/**
@@ -101,7 +102,7 @@ class BillReminder extends Notification
$message = (string)trans(sprintf('email.bill_warning_subject_now_%s', $this->field), ['diff' => $this->diff, 'name' => $this->bill->name]);
}
$bill = $this->bill;
$url = route('bills.show', [$bill->id]);
$url = route('bills.show', [$bill->id]);
return (new SlackMessage())
->warning()
->attachment(function ($attachment) use ($bill, $url) {
@@ -120,9 +121,9 @@ class BillReminder extends Notification
public function via($notifiable)
{
/** @var User|null $user */
$user = auth()->user();
$user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) {
if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack'];
}
return ['mail'];

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