Compare commits

..

87 Commits

Author SHA1 Message Date
James Cole
67d29b8416 Merge branch 'release/v6.0.23' 2023-09-03 18:09:17 +02:00
James Cole
f62e93b487 Add spans to stop parsing 2023-09-03 18:01:12 +02:00
James Cole
a7cc70b975 Remove whitespace 2023-09-03 18:00:07 +02:00
James Cole
60f0d8074a Meta files for new release. 2023-09-03 17:53:22 +02:00
James Cole
ed1fdf9382 Update files for new release 2023-09-03 17:38:54 +02:00
James Cole
9ea3c4224e Remove extra slashes 2023-09-02 10:47:10 +02:00
James Cole
71325de44e Merge branch 'release/v6.0.22' 2023-09-01 19:41:15 +02:00
James Cole
cdb041e647 Merge tag 'v6.0.22' into develop
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
254 changed files with 8092 additions and 4341 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.2",
"version": "v6.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898"
"reference": "eca495f2ee845130855ddf1cf18460c38966c8b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/aa5d64ad3f63f2e48964fc81ee45cb318a723898",
"reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898",
"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.2"
"source": "https://github.com/symfony/console/tree/v6.3.4"
},
"funding": [
{
@@ -831,7 +627,7 @@
"type": "tidelift"
}
],
"time": "2023-07-19T20:17:28+00:00"
"time": "2023-08-16T10:10:12+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -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.2",
"version": "v6.3.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d"
"reference": "0b5c29118f2e980d455d2e34a5659f4579847c54"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d",
"reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d",
"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.2"
"source": "https://github.com/symfony/process/tree/v6.3.4"
},
"funding": [
{
@@ -1801,7 +1597,7 @@
"type": "tidelift"
}
],
"time": "2023-07-12T16:00:22+00:00"
"time": "2023-08-07T10:39:22+00:00"
},
{
"name": "symfony/service-contracts",

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

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

@@ -99,6 +99,7 @@ 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());
}
if (null !== $date) {
try {

View File

@@ -24,7 +24,9 @@ declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\Model\Bill;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface;
use FireflyIII\Transformers\V2\AccountTransformer;
use FireflyIII\Transformers\V2\BillTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -71,4 +73,17 @@ class ShowController extends Controller
->json($this->jsonApiList('subscriptions', $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE);
}
/**
* TODO this endpoint is not documented
*/
public function show(Request $request, Bill $bill): JsonResponse
{
$transformer = new BillTransformer();
$transformer->setParameters($this->parameters);
return response()
->api($this->jsonApiObject('subscriptions', $bill, $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

@@ -105,10 +105,6 @@ class BasicController extends Controller
$end = $this->parameters->get('end');
// balance information:
$balanceData = [];
$billData = [];
$spentData = [];
$netWorthData = [];
$balanceData = $this->getBalanceInformation($start, $end);
$billData = $this->getBillInformation($start, $end);
$spentData = $this->getLeftToSpendInfo($start, $end);
@@ -127,9 +123,15 @@ class BasicController extends Controller
private function getBalanceInformation(Carbon $start, Carbon $end): array
{
// prep some arrays:
$incomes = [];
$expenses = [];
$sums = [];
$incomes = [
'native' => '0',
];
$expenses = [
'native' => '0',
];
$sums = [
'native' => '0',
];
$return = [];
$currencies = [];
$converter = new ExchangeRateConverter();
@@ -354,6 +356,7 @@ class BasicController extends Controller
*/
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);
@@ -367,7 +370,7 @@ class BasicController extends Controller
$nativeLeft = [
'key' => 'left-to-spend-in-native',
'value' => '0',
'currency_id' => (int)$default->id,
'currency_id' => (string)$default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => (int)$default->decimal_places,
@@ -375,7 +378,7 @@ class BasicController extends Controller
$nativePerDay = [
'key' => 'left-per-day-to-spend-in-native',
'value' => '0',
'currency_id' => (int)$default->id,
'currency_id' => (string)$default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => (int)$default->decimal_places,
@@ -386,17 +389,20 @@ class BasicController extends Controller
* @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 = bcmul($journal['amount'], '-1');
$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'];
@@ -404,15 +410,18 @@ class BasicController extends Controller
$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 = $converter->convert($default, $currency, $start, $amount);
$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;
@@ -429,10 +438,10 @@ class BasicController extends Controller
$return[] = [
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'value' => $left,
'currency_id' => $row['currency_id'],
'currency_id' => (string)$row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_symbol' => $row['currency_symbol'],
'currency_decimal_places' => $row['currency_decimal_places'],
'currency_decimal_places' => (int)$row['currency_decimal_places'],
];
// left (native)
$nativeLeft['value'] = $leftNative;
@@ -441,10 +450,10 @@ class BasicController extends Controller
$return[] = [
'key' => sprintf('left-per-day-to-spend-in-%s', $row['currency_code']),
'value' => $perDay,
'currency_id' => $row['currency_id'],
'currency_id' => (string)$row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_symbol' => $row['currency_symbol'],
'currency_decimal_places' => $row['currency_decimal_places'],
'currency_decimal_places' => (int)$row['currency_decimal_places'],
];
// left per day (native)

View File

@@ -42,7 +42,7 @@ class BalanceChartRequest extends FormRequest
{
return [
'accounts' => $this->getAccountList(),
'period' => $this->string('period'),
'period' => $this->convertString('period'),
];
}

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

@@ -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

@@ -218,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);
@@ -228,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)
@@ -235,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

@@ -77,6 +77,7 @@ class CategoryFactory
);
} catch (QueryException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException('400003: Could not store new category.', 0, $e);
}
}

View File

@@ -130,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

@@ -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

@@ -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

@@ -166,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 */
@@ -186,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();
@@ -209,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

@@ -871,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;
}
@@ -976,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;
}

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

@@ -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:
@@ -226,6 +227,7 @@ class IndexController extends Controller
Log::debug(sprintf('Working on budget #%d ("%s")', $current->id, $current->name));
$array = $current->toArray();
$array['spent'] = [];
$array['spent_total'] = [];
$array['budgeted'] = [];
$array['attachments'] = $this->repository->getAttachments($current);
$array['auto_budget'] = $this->repository->getAutoBudget($current);
@@ -234,9 +236,10 @@ class IndexController extends Controller
foreach ($budgetLimits as $limit) {
Log::debug(sprintf('Working on budget limit #%d', $limit->id));
$currency = $limit->transactionCurrency ?? $defaultCurrency;
$amount = app('steam')->bcround($limit->amount, $currency->decimal_places);
$array['budgeted'][] = [
'id' => $limit->id,
'amount' => app('steam')->bcround($limit->amount, $currency->decimal_places),
'amount' => $amount,
'start_date' => $limit->start_date->isoFormat($this->monthAndDayFormat),
'end_date' => $limit->end_date->isoFormat($this->monthAndDayFormat),
'in_range' => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end),
@@ -245,6 +248,7 @@ class IndexController extends Controller
'currency_name' => $currency->name,
'currency_decimal_places' => $currency->decimal_places,
];
Log::debug(sprintf('The amount budgeted for budget limit #%d is %s %s', $limit->id, $currency->code, $amount));
}
/** @var TransactionCurrency $currency */
@@ -337,6 +341,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

@@ -28,7 +28,10 @@ use Exception;
use FireflyConfig;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\Http\Controllers\GetConfigurationData;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -37,6 +40,8 @@ use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Monolog\Handler\RotatingFileHandler;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* Class DebugController
@@ -117,86 +122,9 @@ class DebugController extends Controller
*/
public function index(Request $request)
{
// basic scope information:
$now = now(config('app.timezone'))->format('Y-m-d H:i:s e');
$buildNr = '(unknown)';
$buildDate = '(unknown)';
$baseBuildNr = '(unknown)';
$baseBuildDate = '(unknown)';
$expectedDBversion = config('firefly.db_version');
$foundDBversion = FireflyConfig::get('db_version', 1)->data;
try {
if (file_exists('/var/www/counter-main.txt')) {
$buildNr = trim(file_get_contents('/var/www/counter-main.txt'));
}
} catch (Exception $e) { // generic catch for open basedir.
Log::debug('Could not check counter.');
Log::warning($e->getMessage());
}
try {
if (file_exists('/var/www/build-date-main.txt')) {
$buildDate = trim(file_get_contents('/var/www/build-date-main.txt'));
}
} catch (Exception $e) { // generic catch for open basedir.
Log::debug('Could not check date.');
Log::warning($e->getMessage());
}
if ('' !== (string)env('BASE_IMAGE_BUILD')) {
$baseBuildNr = env('BASE_IMAGE_BUILD');
}
if ('' !== (string)env('BASE_IMAGE_DATE')) {
$baseBuildDate = env('BASE_IMAGE_DATE');
}
$phpVersion = PHP_VERSION;
$phpOs = PHP_OS;
// system information
$tz = env('TZ');
$appEnv = config('app.env');
$appDebug = var_export(config('app.debug'), true);
$cacheDriver = config('cache.default');
$logChannel = config('logging.default');
$appLogLevel = config('logging.level');
$maxFileSize = app('steam')->phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = app('steam')->phpBytes(ini_get('post_max_size'));
$uploadSize = min($maxFileSize, $maxPostSize);
$displayErrors = ini_get('display_errors');
$errorReporting = $this->errorReporting((int)ini_get('error_reporting'));
$interface = PHP_SAPI;
$defaultLanguage = (string)config('firefly.default_language');
$defaultLocale = (string)config('firefly.default_locale');
$bcscale = bcscale();
$drivers = implode(', ', DB::availableDrivers());
$currentDriver = DB::getDriverName();
$trustedProxies = config('firefly.trusted_proxies');
// user info
$loginProvider = config('auth.providers.users.driver');
$userGuard = config('auth.defaults.guard');
$remoteHeader = $userGuard === 'remote_user_guard' ? config('auth.guard_header') : 'N/A';
$remoteMailHeader = $userGuard === 'remote_user_guard' ? config('auth.guard_email') : 'N/A';
$userLanguage = app('steam')->getLanguage();
$userLocale = app('steam')->getLocale();
$userAgent = $request->header('user-agent');
$stateful = join(', ', config('sanctum.stateful'));
// expected + found DB version:
// some new vars.
$isDocker = env('IS_DOCKER', false);
// set languages, see what happens:
$original = setlocale(LC_ALL, 0);
$localeAttempts = [];
$parts = app('steam')->getLocaleArray(app('steam')->getLocale());
foreach ($parts as $code) {
$code = trim($code);
Log::debug(sprintf('Trying to set %s', $code));
$localeAttempts[$code] = var_export(setlocale(LC_ALL, $code), true);
}
setlocale(LC_ALL, $original);
$table = $this->generateTable();
$table = str_replace(["\n", "\t", ' '], '', $table);
$now = now(config('app.timezone'))->format('Y-m-d H:i:s');
// get latest log file:
$logger = Log::driver();
@@ -213,50 +141,190 @@ class DebugController extends Controller
}
if ('' !== $logContent) {
// last few lines
$logContent = 'Truncated from this point <----|' . substr($logContent, -8192);
$logContent = 'Truncated from this point <----|' . substr($logContent, -16384);
}
return view(
'debug',
compact(
'phpVersion',
'localeAttempts',
'expectedDBversion',
'foundDBversion',
'appEnv',
'appDebug',
'logChannel',
'stateful',
'tz',
'uploadSize',
'appLogLevel',
'remoteHeader',
'remoteMailHeader',
'now',
'drivers',
'currentDriver',
'userGuard',
'loginProvider',
'buildNr',
'buildDate',
'baseBuildNr',
'baseBuildDate',
'bcscale',
'userAgent',
'displayErrors',
'errorReporting',
'phpOs',
'interface',
'logContent',
'cacheDriver',
'trustedProxies',
'userLanguage',
'userLocale',
'defaultLanguage',
'defaultLocale',
'isDocker'
)
);
return view('debug', compact('table', 'now', 'logContent'));
}
/**
* @return string
*/
private function generateTable(): string
{
// system information:
$system = $this->getSystemInformation();
$docker = $this->getBuildInfo();
$app = $this->getAppInfo();
$user = $this->getuserInfo();
return (string)view('partials.debug-table', compact('system', 'docker', 'app', 'user'));
}
/**
* @return array
*/
private function getSystemInformation(): array
{
$maxFileSize = app('steam')->phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = app('steam')->phpBytes(ini_get('post_max_size'));
$drivers = DB::availableDrivers();
$currentDriver = DB::getDriverName();
return [
'db_version' => app('fireflyconfig')->get('db_version', 1)->data,
'php_version' => PHP_VERSION,
'php_os' => PHP_OS,
'interface' => PHP_SAPI,
'bcscale' => bcscale(),
'display_errors' => ini_get('display_errors'),
'error_reporting' => $this->errorReporting((int)ini_get('error_reporting')),
'upload_size' => min($maxFileSize, $maxPostSize),
'all_drivers' => $drivers,
'current_driver' => $currentDriver,
];
}
/**
* @return array
*/
private function getBuildInfo(): array
{
$return = [
'is_docker' => env('IS_DOCKER', false),
'build' => '(unknown)',
'build_date' => '(unknown)',
'base_build' => '(unknown)',
'base_build_date' => '(unknown)',
];
try {
if (file_exists('/var/www/counter-main.txt')) {
$return['build'] = trim(file_get_contents('/var/www/counter-main.txt'));
}
} catch (Exception $e) { // generic catch for open basedir.
Log::debug('Could not check build counter, but thats ok.');
Log::warning($e->getMessage());
}
try {
if (file_exists('/var/www/build-date-main.txt')) {
$return['build_date'] = trim(file_get_contents('/var/www/build-date-main.txt'));
}
} catch (Exception $e) { // generic catch for open basedir.
Log::debug('Could not check build date, but thats ok.');
Log::warning($e->getMessage());
}
if ('' !== (string)env('BASE_IMAGE_BUILD')) {
$return['base_build'] = env('BASE_IMAGE_BUILD');
}
if ('' !== (string)env('BASE_IMAGE_DATE')) {
$return['base_build_date'] = env('BASE_IMAGE_DATE');
}
return $return;
}
/**
* @return array
*/
private function getAppInfo(): array
{
$userGuard = config('auth.defaults.guard');
return [
'tz' => env('TZ'),
'debug' => var_export(config('app.debug'), true),
'log_channel' => env('LOG_CHANNEL'),
'audit_log_channel' => envNonEmpty('AUDIT_LOG_CHANNEL', '(empty)'),
'default_language' => (string)config('firefly.default_language'),
'default_locale' => (string)config('firefly.default_locale'),
'remote_header' => $userGuard === 'remote_user_guard' ? config('auth.guard_header') : 'N/A',
'remote_mail_header' => $userGuard === 'remote_user_guard' ? config('auth.guard_email') : 'N/A',
'stateful_domains' => join(', ', config('sanctum.stateful')),
];
}
/**
* @return array
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function getuserInfo(): array
{
$userFlags = $this->getUserFlags();
// user info
$userAgent = request()->header('user-agent');
// set languages, see what happens:
$original = setlocale(LC_ALL, 0);
$localeAttempts = [];
$parts = app('steam')->getLocaleArray(app('steam')->getLocale());
foreach ($parts as $code) {
$code = trim($code);
Log::debug(sprintf('Trying to set %s', $code));
$result = setlocale(LC_ALL, $code);
$localeAttempts[$code] = $result === $code;
}
setlocale(LC_ALL, $original);
return [
'user_id' => auth()->user()->id,
'user_count' => User::count(),
'user_flags' => $userFlags,
'user_agent' => $userAgent,
'locale_attempts' => $localeAttempts,
'locale' => app('steam')->getLocale(),
'language' => app('steam')->getLanguage(),
'view_range' => app('preferences')->get('viewRange', '1M')->data,
];
}
/**
* @return string
*/
private function getUserFlags(): string
{
$flags = [];
/** @var User $user */
$user = auth()->user();
// has liabilities
if ($user->accounts()->accountTypeIn([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE])->count() > 0) {
$flags[] = ':credit_card:';
}
// has piggies
if ($user->piggyBanks()->count() > 0) {
$flags[] = ':pig:';
}
// has stored reconciliations
$type = TransactionType::whereType(TransactionType::RECONCILIATION)->first();
if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count()) {
$flags[] = ':ledger:';
}
// has used importer?
// has rules
if ($user->rules()->count() > 0) {
$flags[] = ':wrench:';
}
// has recurring transactions
if ($user->recurrences()->count() > 0) {
$flags[] = ':clock130:';
}
// has groups
if ($user->objectGroups()->count() > 0) {
$flags[] = ':bookmark_tabs:';
}
// uses bills
if ($user->bills()->count() > 0) {
$flags[] = ':email:';
}
return join(' ', $flags);
}
/**
@@ -275,4 +343,5 @@ class DebugController extends Controller
return redirect(route('home'));
}
}

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

@@ -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

@@ -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

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

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

View File

@@ -52,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

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'];

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
@@ -96,9 +97,9 @@ class NewAccessToken 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

@@ -0,0 +1,117 @@
<?php
/*
* NewAccessToken.php
* Copyright (c) 2022 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\Notifications\User;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
/**
* Class RuleActionFailed
*/
class RuleActionFailed extends Notification
{
use Queueable;
private string $groupLink;
private string $groupTitle;
private string $message;
private string $ruleLink;
private string $ruleTitle;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(array $params)
{
[$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink] = $params;
$this->message = $mainMessage;
$this->groupTitle = $groupTitle;
$this->groupLink = $groupLink;
$this->ruleTitle = $ruleTitle;
$this->ruleLink = $ruleLink;
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
*
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
/**
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
*
* @return SlackMessage
*/
public function toSlack($notifiable)
{
$groupTitle = $this->groupTitle;
$groupLink = $this->groupLink;
$ruleTitle = $this->ruleTitle;
$ruleLink = $this->ruleLink;
return (new SlackMessage())->content($this->message)->attachment(function ($attachment) use ($groupTitle, $groupLink) {
$attachment->title((string)trans('rules.inspect_transaction', ['title' => $groupTitle]), $groupLink);
})->attachment(function ($attachment) use ($ruleTitle, $ruleLink) {
$attachment->title((string)trans('rules.inspect_rule', ['title' => $ruleTitle]), $ruleLink);
});
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
*
* @return array
*/
public function via($notifiable)
{
/** @var User|null $user */
$user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (UrlValidator::isValidWebhookURL($slackUrl)) {
app('log')->debug('Will send ruleActionFailed through Slack!');
return ['slack'];
}
app('log')->debug('Will NOT send ruleActionFailed through Slack');
return [];
}
}

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
@@ -124,9 +125,9 @@ class UserLogin 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

@@ -32,6 +32,8 @@ use FireflyIII\Events\DetectedNewIPAddress;
use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Events\NewVersionAvailable;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword;
@@ -170,6 +172,14 @@ class EventServiceProvider extends ServiceProvider
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
],
// rule actions
RuleActionFailedOnArray::class => [
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
],
RuleActionFailedOnObject::class => [
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnObject',
],
];
/**

View File

@@ -39,6 +39,49 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
{
private User $user;
/**
* @inheritDoc
*/
public function cleanup(): void
{
$exists = [];
$availableBudgets = $this->user->availableBudgets()->get();
/** @var AvailableBudget $availableBudget */
foreach ($availableBudgets as $availableBudget) {
$start = $availableBudget->start_date->format('Y-m-d');
$end = $availableBudget->end_date->format('Y-m-d');
$key = sprintf('%s-%s-%s', $availableBudget->transaction_currency_id, $start, $end);
if (array_key_exists($key, $exists)) {
app('log')->debug(sprintf('Found duplicate AB: %s %s, %s-%s. Has been deleted', $availableBudget->transaction_currency_id, $availableBudget->amount, $start, $end));
$availableBudget->delete();
}
$exists[$key] = true;
}
}
/**
* Return a list of all available budgets (in all currencies) (for the selected period).
*
* @param Carbon|null $start
* @param Carbon|null $end
*
* @return Collection
*/
public function get(?Carbon $start = null, ?Carbon $end = null): Collection
{
$query = $this->user->availableBudgets()->with(['transactionCurrency']);
if (null !== $start && null !== $end) {
$query->where(
static function (Builder $q1) use ($start, $end) {
$q1->where('start_date', '=', $start->format('Y-m-d'));
$q1->where('end_date', '=', $end->format('Y-m-d'));
}
);
}
return $query->get(['available_budgets.*']);
}
/**
* Delete all available budgets.
*/
@@ -122,29 +165,6 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
return $return;
}
/**
* Return a list of all available budgets (in all currencies) (for the selected period).
*
* @param Carbon|null $start
* @param Carbon|null $end
*
* @return Collection
*/
public function get(?Carbon $start = null, ?Carbon $end = null): Collection
{
$query = $this->user->availableBudgets()->with(['transactionCurrency']);
if (null !== $start && null !== $end) {
$query->where(
static function (Builder $q1) use ($start, $end) {
$q1->where('start_date', '=', $start->format('Y-m-d'));
$q1->where('end_date', '=', $end->format('Y-m-d'));
}
);
}
return $query->get(['available_budgets.*']);
}
/**
* Returns all available budget objects.
*
@@ -180,6 +200,23 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
return $query->get();
}
/**
* Returns all available budget objects.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*
*/
public function getAvailableBudgetsByExactDate(Carbon $start, Carbon $end): Collection
{
return $this->user->availableBudgets()
->where('start_date', '=', $start->format('Y-m-d'))
->where('end_date', '=', $end->format('Y-m-d'))
->get();
}
/**
* @inheritDoc
*/

View File

@@ -35,6 +35,11 @@ use Illuminate\Support\Collection;
*/
interface AvailableBudgetRepositoryInterface
{
/**
* @return void
*/
public function cleanup(): void;
/**
* Delete all available budgets.
*/
@@ -111,6 +116,14 @@ interface AvailableBudgetRepositoryInterface
*/
public function getAvailableBudgetsByDate(?Carbon $start, ?Carbon $end): Collection;
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getAvailableBudgetsByExactDate(Carbon $start, Carbon $end): Collection;
/**
* Get by transaction currency and date. Should always result in one entry or NULL.
*

View File

@@ -0,0 +1,58 @@
<?php
/**
* AccountList.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\Binder;
use FireflyIII\Models\Bill;
use FireflyIII\User;
use Illuminate\Routing\Route;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class UserGroupBill.
*/
class UserGroupBill implements BinderInterface
{
/**
* @param string $value
* @param Route $route
*
* @return Bill
* @throws NotFoundHttpException
*
*/
public static function routeBinder(string $value, Route $route): Bill
{
if (auth()->check()) {
/** @var User $user */
$user = auth()->user();
$currency = Bill::where('id', (int)$value)
->where('user_group_id', $user->user_group_id)
->first();
if (null !== $currency) {
return $currency;
}
}
throw new NotFoundHttpException();
}
}

View File

@@ -294,7 +294,7 @@ trait RenderPartialViews
} catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e);
throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
}
++$index;
@@ -349,7 +349,7 @@ trait RenderPartialViews
} catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e);
throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
}
++$index;

View File

@@ -62,7 +62,7 @@ trait RuleManagement
} catch (Throwable $e) {
Log::error(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e);
throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
}
$index++;
}
@@ -109,7 +109,7 @@ trait RuleManagement
} catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e);
throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
}
$index++;
}
@@ -153,7 +153,7 @@ trait RuleManagement
} catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()));
Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e);
throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
}
$index++;
}

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/*
* UrlValidator.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\Support\Notifications;
/**
* Class UrlValidator
*/
class UrlValidator
{
/**
* @param string $url
*
* @return bool
*/
public static function isValidWebhookURL(string $url): bool
{
return str_starts_with($url, 'https://hooks.slack.com/services/') || str_starts_with($url, 'https://discord.com/api/webhooks/');
}
}

View File

@@ -1004,151 +1004,151 @@ class OperatorQuerySearch implements SearchInterface
//
case '-date_on':
case 'date_on':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setExactDateParams($range, $prohibited);
return false;
case 'date_before':
case '-date_after':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setDateBeforeParams($range);
return false;
case 'date_after':
case '-date_before':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setDateAfterParams($range);
return false;
case 'interest_date_on':
case '-interest_date_on':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('interest_date', $range, $prohibited);
return false;
case 'interest_date_before':
case '-interest_date_after':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('interest_date', $range);
return false;
case 'interest_date_after':
case '-interest_date_before':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('interest_date', $range);
return false;
case 'book_date_on':
case '-book_date_on':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('book_date', $range, $prohibited);
return false;
case 'book_date_before':
case '-book_date_after':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('book_date', $range);
return false;
case 'book_date_after':
case '-book_date_before':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('book_date', $range);
return false;
case 'process_date_on':
case '-process_date_on':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('process_date', $range, $prohibited);
return false;
case 'process_date_before':
case '-process_date_after':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('process_date', $range);
return false;
case 'process_date_after':
case '-process_date_before':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('process_date', $range);
return false;
case 'due_date_on':
case '-due_date_on':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('due_date', $range, $prohibited);
return false;
case 'due_date_before':
case '-due_date_after':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('due_date', $range);
return false;
case 'due_date_after':
case '-due_date_before':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('due_date', $range);
return false;
case 'payment_date_on':
case '-payment_date_on':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('payment_date', $range, $prohibited);
return false;
case 'payment_date_before':
case '-payment_date_after':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('payment_date', $range);
return false;
case 'payment_date_after':
case '-payment_date_before':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('payment_date', $range);
return false;
case 'invoice_date_on':
case '-invoice_date_on':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('invoice_date', $range, $prohibited);
return false;
case 'invoice_date_before':
case '-invoice_date_after':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('invoice_date', $range);
return false;
case 'invoice_date_after':
case '-invoice_date_before':
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('invoice_date', $range);
return false;
case 'created_at_on':
case '-created_at_on':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setExactObjectDateParams('created_at', $range, $prohibited);
return false;
case 'created_at_before':
case '-created_at_after':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setObjectDateBeforeParams('created_at', $range);
return false;
case 'created_at_after':
case '-created_at_before':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setObjectDateAfterParams('created_at', $range);
return false;
case 'updated_at_on':
case '-updated_at_on':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setExactObjectDateParams('updated_at', $range, $prohibited);
return false;
case 'updated_at_before':
case '-updated_at_after':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setObjectDateBeforeParams('updated_at', $range);
return false;
case 'updated_at_after':
case '-updated_at_before':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value);
$range = $this->parseDateRange($operator, $value);
$this->setObjectDateAfterParams('updated_at', $range);
return false;
//
@@ -1550,13 +1550,22 @@ class OperatorQuerySearch implements SearchInterface
* @return array
* @throws FireflyException
*/
private function parseDateRange(string $value): array
private function parseDateRange(string $type, string $value): array
{
$parser = new ParseDateString();
if ($parser->isDateRange($value)) {
return $parser->parseRange($value);
}
$parsedDate = $parser->parseDate($value);
try {
$parsedDate = $parser->parseDate($value);
} catch (FireflyException $e) {
Log::debug(sprintf('Could not parse date "%s", will return empty array.', $value));
$this->invalidOperators[] = [
'type' => $type,
'value' => (string)$value,
];
return [];
}
return [
'exact' => $parsedDate,

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Factory\TagFactory;
use FireflyIII\Models\RuleAction;
@@ -61,7 +62,7 @@ class AddTag implements ActionInterface
if (null === $tag) {
// could not find, could not create tag.
Log::error(sprintf('RuleAction AddTag. Could not find or create tag "%s"', $this->action->action_value));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.find_or_create_tag_failed', ['tag' => $this->action->action_value])));
return false;
}
@@ -78,12 +79,12 @@ class AddTag implements ActionInterface
// event for audit log entry
event(new TriggeredAuditLog($this->action->rule, $object, 'add_tag', null, $tag->tag));
return true;
}
Log::debug(
sprintf('RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.tag_already_added', ['tag' => $this->action->action_value])));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction;
@@ -56,6 +57,7 @@ class AppendDescriptionToNotes implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
return false;
}
$note = $object->notes()->first();

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction;
@@ -60,6 +61,7 @@ class AppendNotesToDescription implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
return false;
}
$note = $object->notes()->first();
@@ -80,6 +82,7 @@ class AppendNotesToDescription implements ActionInterface
return true;
}
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.new_notes_empty')));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
@@ -56,6 +57,7 @@ class ClearBudget implements ActionInterface
$budget = $object->budgets()->first();
if (null === $budget) {
Log::debug(sprintf('RuleAction ClearBudget, no budget in journal #%d.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_budget')));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
@@ -56,6 +57,7 @@ class ClearCategory implements ActionInterface
$category = $object->categories()->first();
if (null === $category) {
Log::debug(sprintf('RuleAction ClearCategory, no category in journal #%d.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_category')));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
@@ -55,6 +56,7 @@ class ClearNotes implements ActionInterface
$notes = $object->notes()->first();
if (null === $notes) {
Log::debug(sprintf('RuleAction ClearNotes, journal #%d has no notes.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_notes')));
return false;
}
$before = $notes->text;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory;
@@ -65,11 +66,13 @@ class ConvertToDeposit implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot convert to deposit.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found')));
return false;
}
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to deposit.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
return false;
}
@@ -77,7 +80,7 @@ class ConvertToDeposit implements ActionInterface
$type = $object->transactionType->type;
if (TransactionType::DEPOSIT === $type) {
Log::error(sprintf('Journal #%d is already a deposit (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_deposit')));
return false;
}
@@ -89,6 +92,7 @@ class ConvertToDeposit implements ActionInterface
} catch (JsonException | FireflyException $e) {
Log::debug('Could not convert withdrawal to deposit.');
Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false;
}
@@ -104,13 +108,14 @@ class ConvertToDeposit implements ActionInterface
} catch (JsonException | FireflyException $e) {
Log::debug('Could not convert transfer to deposit.');
Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false;
}
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::DEPOSIT));
return $res;
}
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_deposit', ['type' => $type])));
return false;
}

View File

@@ -24,6 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
@@ -62,11 +64,13 @@ class ConvertToTransfer implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot convert to transfer.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found')));
return false;
}
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to transfer.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
return false;
}
@@ -77,15 +81,19 @@ class ConvertToTransfer implements ActionInterface
Log::error(
sprintf('Journal #%d is already a transfer so cannot be converted (rule #%d).', $object->id, $this->action->rule_id)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_transfer')));
return false;
}
if (TransactionType::DEPOSIT !== $type && TransactionType::WITHDRAWAL !== $type) {
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_transfer', ['type' => $type])));
return false;
}
// find the asset account in the action value.
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($user);
$opposing = null;
$expectedType = null;
if (TransactionType::WITHDRAWAL === $type) {
$expectedType = $this->getSourceType($journalId);
@@ -107,6 +115,7 @@ class ConvertToTransfer implements ActionInterface
$this->action->rule_id
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_valid_opposing', ['name' => $this->action->action_value])));
return false;
}
@@ -118,9 +127,12 @@ class ConvertToTransfer implements ActionInterface
} catch (FireflyException $e) {
Log::debug('Could not convert withdrawal to transfer.');
Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false;
}
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::WITHDRAWAL, TransactionType::TRANSFER));
if (false !== $res) {
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::WITHDRAWAL, TransactionType::TRANSFER));
}
return $res;
}
if (TransactionType::DEPOSIT === $type) {
@@ -130,12 +142,15 @@ class ConvertToTransfer implements ActionInterface
} catch (FireflyException $e) {
Log::debug('Could not convert deposit to transfer.');
Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false;
}
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::TRANSFER));
if (false !== $res) {
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::TRANSFER));
}
return $res;
}
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_transfer', ['type' => $type])));
return false;
}
@@ -192,6 +207,7 @@ class ConvertToTransfer implements ActionInterface
[$journal->id, $opposing->name, $this->action->rule_id]
)
);
event(new RuleActionFailedOnObject($this->action, $journal, trans('rules.already_has_source_asset', ['name' => $opposing->name])));
return false;
}
@@ -250,6 +266,7 @@ class ConvertToTransfer implements ActionInterface
[$journal->id, $opposing->name, $this->action->rule_id]
)
);
event(new RuleActionFailedOnObject($this->action, $journal, trans('rules.already_has_destination_asset', ['name' => $opposing->name])));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory;
@@ -65,21 +66,26 @@ class ConvertToWithdrawal implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot convert to withdrawal.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found')));
return false;
}
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to withdrawal.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
return false;
}
$type = $object->transactionType->type;
if (TransactionType::WITHDRAWAL === $type) {
Log::error(sprintf('Journal #%d is already a withdrawal (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_withdrawal')));
return false;
}
if (TransactionType::DEPOSIT !== $type && TransactionType::TRANSFER !== $type) {
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_withdrawal', ['type' => $type])));
return false;
}
if (TransactionType::DEPOSIT === $type) {
Log::debug('Going to transform a deposit to a withdrawal.');
try {
@@ -87,6 +93,7 @@ class ConvertToWithdrawal implements ActionInterface
} catch (JsonException | FireflyException $e) {
Log::debug('Could not convert transfer to deposit.');
Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false;
}
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::WITHDRAWAL));
@@ -101,13 +108,14 @@ class ConvertToWithdrawal implements ActionInterface
} catch (JsonException | FireflyException $e) {
Log::debug('Could not convert transfer to deposit.');
Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false;
}
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::WITHDRAWAL));
return $res;
}
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_withdrawal', ['type' => $type])));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
@@ -73,6 +74,7 @@ class LinkToBill implements ActionInterface
$billName
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_linked_to_subscription', ['name' => $billName])));
return false;
}
@@ -97,7 +99,7 @@ class LinkToBill implements ActionInterface
$billName
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_subscription', ['name' => $billName])));
return false;
}
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction;
@@ -57,6 +58,7 @@ class MoveDescriptionToNotes implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
return false;
}
$note = $object->notes()->first();

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
@@ -63,16 +64,19 @@ class MoveNotesToDescription implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
return false;
}
$note = $object->notes()->first();
if (null === $note) {
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_notes_to_move')));
// nothing to move, return null
return false;
}
if ('' === $note->text) {
// nothing to move, return null
$note->delete();
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_notes_to_move')));
return false;
}
$before = $object->description;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
@@ -55,6 +56,7 @@ class RemoveAllTags implements ActionInterface
$count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->count();
if (0 === $count) {
Log::debug(sprintf('RuleAction RemoveAllTags, journal #%d has no tags.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_tags_to_remove')));
return false;
}
Log::debug(sprintf('RuleAction RemoveAllTags removed all tags from journal %d.', $journal['transaction_journal_id']));

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
@@ -61,6 +62,7 @@ class RemoveTag implements ActionInterface
Log::debug(
sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag exists.', $name, $journal['transaction_journal_id'])
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_tag', ['tag' => $name])));
return false;
}
$count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->where('tag_id', $tag->id)->count();
@@ -68,6 +70,7 @@ class RemoveTag implements ActionInterface
Log::debug(
sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag is linked.', $name, $journal['transaction_journal_id'])
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_unlink_tag', ['tag' => $name])));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
@@ -65,7 +66,7 @@ class SetBudget implements ActionInterface
$search
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_budget', ['name' => $search])));
return false;
}
@@ -78,7 +79,7 @@ class SetBudget implements ActionInterface
$journal['transaction_type_type']
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_set_budget', ['type' => $journal['transaction_type_type'], 'name' => $search])));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Models\RuleAction;
@@ -57,7 +58,7 @@ class SetCategory implements ActionInterface
$search = $this->action->action_value;
if (null === $user) {
Log::error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal')));
return false;
}
@@ -73,7 +74,7 @@ class SetCategory implements ActionInterface
$search
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_category', ['name' => $search])));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Account;
use FireflyIII\Models\RuleAction;
@@ -65,7 +66,7 @@ class SetDestinationAccount implements ActionInterface
if (null === $object) {
Log::error('Could not find journal.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal')));
return false;
}
$type = $object->transactionType->type;
@@ -81,7 +82,7 @@ class SetDestinationAccount implements ActionInterface
$this->action->action_value
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value])));
return false;
}
@@ -90,13 +91,13 @@ class SetDestinationAccount implements ActionInterface
$source = $object->transactions()->where('amount', '<', 0)->first();
if (null === $source) {
Log::error('Could not find source transaction.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction')));
return false;
}
// account must not be deleted (in the meantime):
if (null === $source->account) {
Log::error('Could not find source transaction account.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction_account')));
return false;
}
if (null !== $newAccount && (int)$newAccount->id === (int)$source->account_id) {
@@ -108,6 +109,7 @@ class SetDestinationAccount implements ActionInterface
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_destination', ['name' => $newAccount->name])));
return false;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Account;
use FireflyIII\Models\RuleAction;
@@ -64,7 +65,7 @@ class SetSourceAccount implements ActionInterface
$this->repository = app(AccountRepositoryInterface::class);
if (null === $object) {
Log::error('Could not find journal.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal')));
return false;
}
$type = $object->transactionType->type;
@@ -76,7 +77,7 @@ class SetSourceAccount implements ActionInterface
Log::error(
sprintf('Cant change source account of journal #%d because no asset account with name "%s" exists.', $object->id, $this->action->action_value)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value])));
return false;
}
@@ -85,13 +86,13 @@ class SetSourceAccount implements ActionInterface
$destination = $object->transactions()->where('amount', '>', 0)->first();
if (null === $destination) {
Log::error('Could not find destination transaction.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction')));
return false;
}
// account must not be deleted (in the meantime):
if (null === $destination->account) {
Log::error('Could not find destination transaction account.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction_account')));
return false;
}
if (null !== $newAccount && (int)$newAccount->id === (int)$destination->account_id) {
@@ -102,12 +103,12 @@ class SetSourceAccount implements ActionInterface
$destination->account_id
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_source', ['name' => $newAccount->name])));
return false;
}
// if this is a deposit, the new source account must be a revenue account and may be created:
// or its a liability
// or it's a liability
if (TransactionType::DEPOSIT === $type) {
$newAccount = $this->findDepositSourceAccount();
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;
@@ -59,18 +60,20 @@ class SwitchAccounts implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot switch accounts.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal')));
return false;
}
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot switch accounts.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
return false;
}
$type = $object->transactionType->type;
if (TransactionType::TRANSFER !== $type) {
Log::error(sprintf('Journal #%d is NOT a transfer (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_not_transfer')));
return false;
}
@@ -80,7 +83,7 @@ class SwitchAccounts implements ActionInterface
$destTransaction = $object->transactions()->where('amount', '>', 0)->first();
if (null === $sourceTransaction || null === $destTransaction) {
Log::error(sprintf('Journal #%d has no source or destination transaction (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_accounts')));
return false;
}
$sourceAccountId = (int)$sourceTransaction->account_id;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\RuleAction;
@@ -61,16 +62,15 @@ class UpdatePiggybank implements ActionInterface
// refresh the transaction type.
$user = User::find($journal['user_id']);
/** @var TransactionJournal $journalObj */
$journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']);
$type = TransactionType::find((int)$journalObj->transaction_type_id);
$journal['transaction_type_type'] = $type->type;
$journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']);
$type = TransactionType::find((int)$journalObj->transaction_type_id);
$piggyBank = $this->findPiggyBank($user);
if (null === $piggyBank) {
Log::info(
sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $this->action->action_value, $this->action->id, $this->action->rule_id)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $this->action->action_value])));
return false;
}
@@ -130,7 +130,7 @@ class UpdatePiggybank implements ActionInterface
$destination->account_id
)
);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $this->action->action_value])));
return false;
}

View File

@@ -28,6 +28,7 @@ use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection;
@@ -38,6 +39,7 @@ use Illuminate\Support\Collection;
class AccountTransformer extends AbstractTransformer
{
private array $accountMeta;
private array $accountTypes;
private array $balances;
private array $convertedBalances;
private array $currencies;
@@ -51,6 +53,7 @@ class AccountTransformer extends AbstractTransformer
{
$this->currencies = [];
$this->accountMeta = [];
$this->accountTypes = [];
$this->balances = app('steam')->balancesByAccounts($objects, $this->getDate());
$this->convertedBalances = app('steam')->balancesByAccountsConverted($objects, $this->getDate());
$repository = app(CurrencyRepositoryInterface::class);
@@ -72,6 +75,15 @@ class AccountTransformer extends AbstractTransformer
$id = (int)$entry->account_id;
$this->accountMeta[$id][$entry->name] = $entry->data;
}
// get account types:
// select accounts.id, account_types.type from account_types left join accounts on accounts.account_type_id = account_types.id;
$accountTypes = AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id')
->whereIn('accounts.id', $accountIds)
->get(['accounts.id', 'account_types.type']);
/** @var AccountType $row */
foreach ($accountTypes as $row) {
$this->accountTypes[(int)$row->id] = (string)config(sprintf('firefly.shortNamesByFullName.%s', $row->type));
}
}
/**
@@ -96,10 +108,13 @@ class AccountTransformer extends AbstractTransformer
*/
public function transform(Account $account): array
{
//$fullType = $account->accountType->type;
//$accountType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $fullType));
$id = (int)$account->id;
// various meta
$accountRole = $this->accountMeta[$id]['account_role'] ?? null;
$accountType = $this->accountTypes[$id];
$order = (int)$account->order;
// no currency? use default
$currency = $this->default;
if (0 !== (int)$this->accountMeta[$id]['currency_id']) {
@@ -109,16 +124,21 @@ class AccountTransformer extends AbstractTransformer
$balance = $this->balances[$id] ?? null;
$nativeBalance = $this->convertedBalances[$id]['native_balance'] ?? null;
// no order for some accounts:
if (!in_array(strtolower($accountType), ['liability', 'liabilities', 'asset'], true)) {
$order = null;
}
return [
'id' => (string)$account->id,
'created_at' => $account->created_at->toAtomString(),
'updated_at' => $account->updated_at->toAtomString(),
'active' => $account->active,
//'order' => $order,
'order' => $order,
'name' => $account->name,
'iban' => '' === $account->iban ? null : $account->iban,
// 'type' => strtolower($accountType),
// 'account_role' => $accountRole,
'type' => strtolower($accountType),
'account_role' => $accountRole,
'currency_id' => (string)$currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,

View File

@@ -28,9 +28,11 @@ use Carbon\CarbonInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Note;
use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Log;
@@ -40,6 +42,7 @@ use Log;
*/
class BillTransformer extends AbstractTransformer
{
private ExchangeRateConverter $converter;
private array $currencies;
private TransactionCurrency $default;
private array $groups;
@@ -102,21 +105,71 @@ class BillTransformer extends AbstractTransformer
];
}
$this->default = app('amount')->getDefaultCurrency();
$this->default = app('amount')->getDefaultCurrency();
$this->converter = new ExchangeRateConverter();
// grab all paid dates:
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$journals = TransactionJournal::whereIn('bill_id', $bills)
->where('date', '>=', $this->parameters->get('start'))
->where('date', '<=', $this->parameters->get('end'))
->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']);
$journals = TransactionJournal::whereIn('bill_id', $bills)
->where('date', '>=', $this->parameters->get('start'))
->where('date', '<=', $this->parameters->get('end'))
->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']);
$journalIds = $journals->pluck('id')->toArray();
// grab transactions for amount:
$set = Transaction::whereIn('transaction_journal_id', $journalIds)
->where('transactions.amount', '<', 0)
->get(['transactions.id', 'transactions.transaction_journal_id', 'transactions.amount', 'transactions.foreign_amount', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
// convert to array for easy finding:
$transactions = [];
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$journalId = (int)$transaction->transaction_journal_id;
$transactions[$journalId] = $transaction->toArray();
}
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$billId = (int)$journal->bill_id;
$transaction = $transactions[(int)$journal->id] ?? [];
$billId = (int)$journal->bill_id;
$currencyId = (int)$transaction['transaction_currency_id'] ?? 0;
$currencies[$currencyId] = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
// foreign currency
$foreignCurrencyId = null;
$foreignCurrencyCode = null;
$foreignCurrencyName = null;
$foreignCurrencySymbol = null;
$foreignCurrencyDp = null;
if (null !== $transaction['foreign_currency_id']) {
$foreignCurrencyId = (int)$transaction['foreign_currency_id'];
$currencies[$foreignCurrencyId] = $currencies[$foreignCurrencyId] ?? TransactionCurrency::find($foreignCurrencyId);
$foreignCurrencyCode = $currencies[$foreignCurrencyId]->code;
$foreignCurrencyName = $currencies[$foreignCurrencyId]->name;
$foreignCurrencySymbol = $currencies[$foreignCurrencyId]->symbol;
$foreignCurrencyDp = (int)$currencies[$foreignCurrencyId]->decimal_places;
}
$this->paidDates[$billId][] = [
'transaction_group_id' => (string)$journal->id,
'transaction_journal_id' => (string)$journal->transaction_group_id,
'date' => $journal->date->toAtomString(),
'transaction_group_id' => (string)$journal->id,
'transaction_journal_id' => (string)$journal->transaction_group_id,
'date' => $journal->date->toAtomString(),
'currency_id' => (int)$currencies[$currencyId]->id,
'currency_code' => $currencies[$currencyId]->code,
'currency_name' => $currencies[$currencyId]->name,
'currency_symbol' => $currencies[$currencyId]->symbol,
'currency_decimal_places' => (int)$currencies[$currencyId]->decimal_places,
'native_id' => (int)$currencies[$currencyId]->id,
'native_code' => $currencies[$currencyId]->code,
'native_symbol' => $currencies[$currencyId]->symbol,
'native_decimal_places' => (int)$currencies[$currencyId]->decimal_places,
'foreign_currency_id' => $foreignCurrencyId,
'foreign_currency_code' => $foreignCurrencyCode,
'foreign_currency_name' => $foreignCurrencyName,
'foreign_currency_symbol' => $foreignCurrencySymbol,
'foreign_currency_decimal_places' => $foreignCurrencyDp,
'amount' => $transaction['amount'],
'foreign_amount' => $transaction['foreign_amount'],
'native_amount' => $this->converter->convert($currencies[$currencyId], $this->default, $journal->date, $transaction['amount']),
'foreign_native_amount' => null === $transaction['foreign_amount'] ? null : $this->converter->convert($currencies[$foreignCurrencyId], $this->default, $journal->date, $transaction['foreign_amount']),
];
}
}
@@ -131,11 +184,19 @@ class BillTransformer extends AbstractTransformer
*/
public function transform(Bill $bill): array
{
$paidData = $this->paidDates[(int)$bill->id] ?? [];
$nextExpectedMatch = $this->nextExpectedMatch($bill, $this->paidDates[(int)$bill->id] ?? []);
$payDates = $this->payDates($bill);
$currency = $this->currencies[(int)$bill->transaction_currency_id];
$group = $this->groups[(int)$bill->id] ?? null;
$paidData = $this->paidDates[(int)$bill->id] ?? [];
$nextExpectedMatch = $this->nextExpectedMatch($bill, $this->paidDates[(int)$bill->id] ?? []);
$payDates = $this->payDates($bill);
$currency = $this->currencies[(int)$bill->transaction_currency_id];
$group = $this->groups[(int)$bill->id] ?? null;
// date for currency conversion
/** @var Carbon|null $startParam */
$startParam = $this->parameters->get('start');
/** @var Carbon|null $start */
$date = null === $startParam ? today() : clone $startParam;
$nextExpectedMatchDiff = $this->getNextExpectedMatchDiff($nextExpectedMatch, $payDates);
return [
'id' => (int)$bill->id,
@@ -144,10 +205,18 @@ class BillTransformer extends AbstractTransformer
'name' => $bill->name,
'amount_min' => app('steam')->bcround($bill->amount_min, $currency->decimal_places),
'amount_max' => app('steam')->bcround($bill->amount_max, $currency->decimal_places),
'native_amount_min' => $this->converter->convert($currency, $this->default, $date, $bill->amount_min),
'native_amount_max' => $this->converter->convert($currency, $this->default, $date, $bill->amount_max),
'currency_id' => (string)$bill->transaction_currency_id,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => (int)$currency->decimal_places,
'native_id' => $this->default->id,
'native_code' => $this->default->code,
'native_name' => $this->default->name,
'native_symbol' => $this->default->symbol,
'native_decimal_places' => (int)$this->default->decimal_places,
'date' => $bill->date->toAtomString(),
'end_date' => $bill->end_date?->toAtomString(),
'extension_date' => $bill->extension_date?->toAtomString(),
@@ -185,8 +254,11 @@ class BillTransformer extends AbstractTransformer
// 2023-07-1 sub one day from the start date to fix a possible bug (see #7704)
// 2023-07-18 this particular date is used to search for the last paid date.
// 2023-07-18 the cloned $searchDate is used to grab the correct transactions.
/** @var Carbon $start */
$start = clone $this->parameters->get('start');
/** @var Carbon|null $startParam */
$startParam = $this->parameters->get('start');
/** @var Carbon|null $start */
$start = null === $startParam ? today() : clone $startParam;
$start->subDay();
$lastPaidDate = $this->lastPaidDate($dates, $start);

View File

@@ -302,7 +302,7 @@ class TransactionGroupTransformer extends AbstractTransformer
if (null === $string) {
return null;
}
Log::debug(sprintf('Now in date("%s")', $string));
// Log::debug(sprintf('Now in date("%s")', $string));
if (10 === strlen($string)) {
return Carbon::createFromFormat('Y-m-d', $string, config('app.timezone'));
}

View File

@@ -114,7 +114,11 @@ trait DepositValidation
// source can be any of the following types.
$validTypes = array_keys($this->combinations[$this->transactionType]);
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
if (null === $accountId &&
null === $accountName &&
null === $accountIban &&
null === $accountNumber &&
false === $this->canCreateTypes($validTypes)) {
// if both values are NULL return false,
// because the source of a deposit can't be created.
// (this never happens).
@@ -122,12 +126,12 @@ trait DepositValidation
$result = false;
}
// if there is an iban, it can only be in use by a revenue account or we will fail.
// if there is an iban, it can only be in use by a revenue account, or we will fail.
if (null !== $accountIban && '' !== $accountIban) {
app('log')->debug('Check if there is not already an account with this IBAN');
$existing = $this->findExistingAccount([AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], ['iban' => $accountIban]);
if (null !== $existing) {
$this->destError = (string)trans('validation.deposit_src_iban_exists');
$this->sourceError = (string)trans('validation.deposit_src_iban_exists');
return false;
}
}

View File

@@ -265,6 +265,7 @@ class AccountValidator
if (null !== $accountId && $accountId > 0) {
$first = $this->accountRepository->find($accountId);
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
app('log')->debug(sprintf('ID: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
return $first;
}
}
@@ -273,6 +274,7 @@ class AccountValidator
if (null !== $accountIban && '' !== (string)$accountIban) {
$first = $this->accountRepository->findByIbanNull($accountIban, $validTypes);
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
app('log')->debug(sprintf('Iban: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
return $first;
}
}
@@ -281,14 +283,20 @@ class AccountValidator
if (null !== $accountNumber && '' !== (string)$accountNumber) {
$first = $this->accountRepository->findByAccountNumber($accountNumber, $validTypes);
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
app('log')->debug(sprintf('Number: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
return $first;
}
}
// find by name:
if ('' !== (string)$accountName) {
return $this->accountRepository->findByName($accountName, $validTypes);
$first = $this->accountRepository->findByName($accountName, $validTypes);
if (null !== $first) {
app('log')->debug(sprintf('Name: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
return $first;
}
}
app('log')->debug('Found nothing!');
return null;
}

View File

@@ -103,6 +103,7 @@ trait TransactionValidation
*/
protected function validateSingleAccount(Validator $validator, int $index, string $transactionType, array $transaction): void
{
app('log')->debug(sprintf('Now in validateSingleAccount(%d)', $index));
/** @var AccountValidator $accountValidator */
$accountValidator = app(AccountValidator::class);
@@ -784,8 +785,8 @@ trait TransactionValidation
private function compareAccountData(string $type, array $comparison): bool
{
return match ($type) {
default => $this->compareAccountDataWithdrawal($comparison),
'deposit' => $this->compareAccountDataDeposit($comparison),
default => $this->compareAccountDataWithdrawal($comparison),
'deposit' => $this->compareAccountDataDeposit($comparison),
'transfer' => $this->compareAccountDataTransfer($comparison),
};
}

View File

@@ -3,6 +3,47 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.0.23 - 2023-09-04
### Changed
- New debug information tables are in HTML
### Fixed
- Remove extra slashes from paths, breaking CSS
## 6.0.22 - 2023-09-02
### API
- [Issue 7917](https://github.com/firefly-iii/firefly-iii/issues/7917) Fixed an API issue where submitting an account name would not be accepted.
## 6.0.21 - 2023-09-02
### Added
- Rules will now report failures if a Slack/Discord notification channel is configured
- Notifications can be sent to Discord
- Beta layout `v2`, activate with `FIREFLY_III_LAYOUT=v2`
### Changed
- Audit log settings are changed, refer to the `.env.example`-file.
- Many URLs are new rendered as relative URLs.
### Fixed
- [Issue 7853](https://github.com/firefly-iii/firefly-iii/issues/7853) Left to spend on main page shows incorrect value
- [Issue 7883](https://github.com/firefly-iii/firefly-iii/issues/7883) Missing translation
- [Issue 7910](https://github.com/firefly-iii/firefly-iii/issues/7910) Type format error
- Home page respects account order
- JS errors for users using Firefly III in a subdir.
### API
- Bumped to v2.0.6 but only so the docs match again.
## 6.0.20 - 2023-08-13
### Fixed

View File

@@ -85,7 +85,7 @@
"diglactic/laravel-breadcrumbs": "^8.1",
"doctrine/dbal": "3.*",
"gdbots/query-parser": "^3.0",
"guzzlehttp/guzzle": "^7.7",
"guzzlehttp/guzzle": "^7.8",
"jc5/google2fa-laravel": "^2.0",
"jc5/recovery": "^2",
"laravel/framework": "^10",
@@ -111,7 +111,7 @@
},
"require-dev": {
"barryvdh/laravel-ide-helper": "2.*",
"ergebnis/phpstan-rules": "^2.0",
"ergebnis/phpstan-rules": "^2.1",
"fakerphp/faker": "1.*",
"filp/whoops": "2.*",
"mockery/mockery": "1.*",

670
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -61,6 +61,7 @@ use FireflyIII\Support\Binder\JournalList;
use FireflyIII\Support\Binder\TagList;
use FireflyIII\Support\Binder\TagOrId;
use FireflyIII\Support\Binder\UserGroupAccount;
use FireflyIII\Support\Binder\UserGroupBill;
use FireflyIII\TransactionRules\Actions\AddTag;
use FireflyIII\TransactionRules\Actions\AppendDescription;
use FireflyIII\TransactionRules\Actions\AppendDescriptionToNotes;
@@ -110,8 +111,8 @@ return [
'handle_debts' => true,
// see cer.php for exchange rates feature flag.
],
'version' => '6.0.20',
'api_version' => '2.0.6',
'version' => '6.0.23',
'api_version' => '2.0.7',
'db_version' => 20,
// generic settings
@@ -148,7 +149,7 @@ return [
'update_minimum_age' => 7,
// notifications
'available_notifications' => ['bill_reminder', 'new_access_token', 'transaction_creation', 'user_login'],
'available_notifications' => ['bill_reminder', 'new_access_token', 'transaction_creation', 'user_login', 'rule_action_failures'],
'admin_notifications' => ['admin_new_reg', 'user_new_reg', 'new_version', 'invite_created', 'invite_redeemed'],
// enabled languages
@@ -480,6 +481,7 @@ return [
// V2 API endpoints:
'userGroupAccount' => UserGroupAccount::class,
'userGroupBill' => UserGroupBill::class,
],

View File

@@ -24,6 +24,30 @@ declare(strict_types=1);
use FireflyIII\Support\Logging\AuditLogger;
use Monolog\Handler\SyslogUdpHandler;
// standard config for both log things:
$defaultChannels = ['daily', 'stdout'];
$auditChannels = ['audit_daily', 'audit_stdout'];
// validChannels is missing 'stack' because we already check for that one.
$validChannels = ['single', 'papertrail', 'stdout', 'daily', 'syslog', 'errorlog'];
$validAuditChannels = ['audit_papertrail', 'audit_stdout', 'audit_stdout', 'audit_daily', 'audit_syslog', 'audit_errorlog'];
// which settings did the user set, if any?
$defaultLogChannel = (string)envNonEmpty('LOG_CHANNEL', 'stack');
$auditLogChannel = (string)envNonEmpty('AUDIT_LOG_CHANNEL', '');
if ('stack' === $defaultLogChannel) {
$defaultChannels = ['daily', 'stdout'];
}
if (in_array($defaultLogChannel, $validChannels, true)) {
$defaultChannels = [$defaultLogChannel];
}
if (in_array($auditLogChannel, $validAuditChannels, true)) {
$auditChannels = [$auditLogChannel];
}
return [
/*
|--------------------------------------------------------------------------
@@ -53,16 +77,27 @@ return [
*/
'channels' => [
// default channels for 'stack' and audit logs:
'stack' => [
/*
* 'stack' and 'audit' are the two "generic" channels that
* are valid destinations for logs.
*/
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'stdout'],
'channels' => $defaultChannels,
],
'audit' => [
'audit' => [
'driver' => 'stack',
'channels' => ['audit_daily', 'audit_stdout'],
'channels' => $auditChannels,
],
'papertrail' => [
/*
* There are 6 valid destinations for the normal logs, listed below:
*/
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
'papertrail' => [
'driver' => 'monolog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'handler' => SyslogUdpHandler::class,
@@ -71,55 +106,65 @@ return [
'port' => env('PAPERTRAIL_PORT'),
],
],
// single laravel log file:
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
// stdout, used in stack 'stack' by default:
'stdout' => [
'stdout' => [
'driver' => 'single',
'path' => 'php://stdout',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
// daily, used in stack 'stack' by default:
'daily' => [
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/ff3-' . PHP_SAPI . '.log'),
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'days' => 7,
],
'syslog' => [
'driver' => 'syslog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
'errorlog' => [
'driver' => 'errorlog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
// the audit log destinations:
'audit_daily' => [
/*
* There are 5 valid destinations for the audit logs, listed below.
* The only one missing is "single".
*/
'audit_papertrail' => [
'driver' => 'monolog',
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
'handler' => SyslogUdpHandler::class,
'tap' => [AuditLogger::class],
'handler_with' => [
'host' => env('PAPERTRAIL_HOST'),
'port' => env('PAPERTRAIL_PORT'),
],
],
'audit_stdout' => [
'driver' => 'single',
'path' => 'php://stdout',
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
],
'audit_daily' => [
'driver' => 'daily',
'path' => storage_path('logs/ff3-audit.log'),
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
'days' => 90,
],
'audit_stdout' => [
'driver' => 'single',
'path' => 'php://stdout',
'audit_syslog' => [
'driver' => 'syslog',
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
],
'audit_errorlog' => [
'driver' => 'errorlog',
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
],
// syslog destination
'syslog' => [
'driver' => 'syslog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
// errorlog destination
'errorlog' => [
'driver' => 'errorlog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
],
];

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