Compare commits

..

120 Commits

Author SHA1 Message Date
James Cole
07aeb6cd20 Merge branch 'release/v6.0.18' 2023-07-18 07:01:55 +02:00
James Cole
d17cc15037 Meta files for new release 2023-07-18 07:00:10 +02:00
James Cole
efade857bb Fix https://github.com/firefly-iii/firefly-iii/issues/7694 2023-07-18 06:44:58 +02:00
James Cole
15ac6a1195 Fix https://github.com/firefly-iii/firefly-iii/issues/7706 2023-07-18 06:38:11 +02:00
James Cole
a45a050e7d Fix https://github.com/firefly-iii/firefly-iii/issues/7749 2023-07-18 06:30:45 +02:00
James Cole
e435ff8b1c Remove debug, add fix for https://github.com/orgs/firefly-iii/discussions/7655#discussioncomment-6468397 2023-07-18 06:18:04 +02:00
James Cole
788dae1477 fix: notifications 2023-07-17 20:33:26 +02:00
James Cole
2bb4cc7954 Fix: add route 2023-07-17 20:26:47 +02:00
James Cole
ecead8a27b Another attempt to fix Slack 2023-07-17 20:15:47 +02:00
James Cole
4a2681df14 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
# Conflicts:
#	composer.json
#	composer.lock
2023-07-17 19:19:34 +02:00
James Cole
5761d82635 Fix: package upgrade for slack messages 2023-07-17 19:18:30 +02:00
James Cole
36fce9db4e Merge pull request #7756 from firefly-iii/dependabot/composer/develop/laravel/slack-notification-channel-3.0.0 2023-07-17 05:41:16 +02:00
dependabot[bot]
1227574bc1 chore(deps): bump laravel/slack-notification-channel from 2.5.0 to 3.0.0
Bumps [laravel/slack-notification-channel](https://github.com/laravel/slack-notification-channel) from 2.5.0 to 3.0.0.
- [Release notes](https://github.com/laravel/slack-notification-channel/releases)
- [Changelog](https://github.com/laravel/slack-notification-channel/blob/3.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/slack-notification-channel/compare/v2.5.0...v3.0.0)

---
updated-dependencies:
- dependency-name: laravel/slack-notification-channel
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 03:15:24 +00:00
James Cole
47c2a66e1d Merge tag 'v6.0.17' into develop
v6.0.17
2023-07-15 16:05:38 +02:00
James Cole
c59e52bbbf Merge branch 'release/v6.0.17' 2023-07-15 16:05:36 +02:00
James Cole
81961fd632 Update packages. 2023-07-15 16:04:52 +02:00
James Cole
b557805eeb Code for new release 2023-07-15 16:02:42 +02:00
James Cole
f43b539470 Fix transfer validation for CAMT imports 2023-07-14 06:07:16 +02:00
James Cole
27037c2fbb Verify all fields are nullable. 2023-07-11 09:14:16 +02:00
James Cole
10646099a0 Add nullable types in validation. 2023-07-11 08:19:47 +02:00
James Cole
43ad63fac6 Merge branch 'main' into develop 2023-07-11 06:58:33 +02:00
James Cole
221c59437c Merge pull request #7745 from firefly-iii/dependabot/npm_and_yarn/semver-6.3.1
chore(deps): bump semver from 6.3.0 to 6.3.1
2023-07-11 06:33:14 +02:00
dependabot[bot]
1a94ec5bbe chore(deps): bump semver from 6.3.0 to 6.3.1
Bumps [semver](https://github.com/npm/node-semver) from 6.3.0 to 6.3.1.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v6.3.1/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v6.3.0...v6.3.1)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-11 04:32:22 +00:00
James Cole
f57c554c56 Merge pull request #7743 from firefly-iii/dependabot/npm_and_yarn/frontend/semver-6.3.1
chore(deps): bump semver from 6.3.0 to 6.3.1 in /frontend
2023-07-11 06:30:48 +02:00
dependabot[bot]
334aec58a4 chore(deps): bump semver from 6.3.0 to 6.3.1 in /frontend
Bumps [semver](https://github.com/npm/node-semver) from 6.3.0 to 6.3.1.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v6.3.1/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v6.3.0...v6.3.1)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-11 01:38:42 +00:00
James Cole
7035da4d75 Merge pull request #7738 from firefly-iii/dependabot/composer/develop/phpstan/phpstan-1.10.25 2023-07-10 07:16:02 +02:00
James Cole
29a40ce8dd Merge pull request #7737 from firefly-iii/dependabot/composer/develop/laravel/passport-11.8.8 2023-07-10 06:47:00 +02:00
dependabot[bot]
c6c98c4df5 chore(deps-dev): bump phpstan/phpstan from 1.10.22 to 1.10.25
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.22 to 1.10.25.
- [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.22...1.10.25)

---
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-07-10 03:55:30 +00:00
dependabot[bot]
277d7e6650 chore(deps): bump laravel/passport from 11.8.7 to 11.8.8
Bumps [laravel/passport](https://github.com/laravel/passport) from 11.8.7 to 11.8.8.
- [Release notes](https://github.com/laravel/passport/releases)
- [Changelog](https://github.com/laravel/passport/blob/11.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/passport/compare/v11.8.7...v11.8.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 03:55:19 +00:00
James Cole
130c539006 Merge pull request #7736 from firefly-iii/dependabot/npm_and_yarn/develop/postcss-8.4.25 2023-07-10 05:51:11 +02:00
dependabot[bot]
2bdb2e12f0 chore(deps-dev): bump postcss from 8.4.24 to 8.4.25
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.24 to 8.4.25.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.24...8.4.25)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 03:11:56 +00:00
James Cole
25779206a1 fix: small issue with cache 2023-07-09 18:45:44 +02:00
James Cole
92f72a5ad7 Merge branch 'main' into develop 2023-07-08 06:40:47 +02:00
James Cole
4e041248ac Merge pull request #7724 from firefly-iii/dependabot/composer/league/oauth2-server-8.5.3 2023-07-07 05:45:52 +02:00
dependabot[bot]
8cc5aa8aba chore(deps): bump league/oauth2-server from 8.5.2 to 8.5.3
Bumps [league/oauth2-server](https://github.com/thephpleague/oauth2-server) from 8.5.2 to 8.5.3.
- [Release notes](https://github.com/thephpleague/oauth2-server/releases)
- [Changelog](https://github.com/thephpleague/oauth2-server/blob/master/CHANGELOG.md)
- [Commits](https://github.com/thephpleague/oauth2-server/compare/8.5.2...8.5.3)

---
updated-dependencies:
- dependency-name: league/oauth2-server
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-06 21:14:52 +00:00
James Cole
4ba712b24e Merge pull request #7717 from tonicospinelli/testing
Cover the startOfPeriod method of Navigation class
2023-07-04 19:32:02 +02:00
Antonio Spinelli
7af9169763 Cover the startOfPeriod method of Navigation class 2023-07-04 14:15:52 -03:00
James Cole
775504acb6 Chore: Add phpdocs 2023-07-04 13:29:19 +02:00
James Cole
7840e37e1a Merge pull request #7685 from tonicospinelli/testing
Create a Calendar Calculator and setup test suite
2023-07-04 13:13:29 +02:00
Antonio Spinelli
720fff4595 Fix typo, remove unused class and change the copyright author 2023-07-04 06:26:05 -03:00
Antonio Spinelli
551c1f4cda Send code coverage to SonarCloud 2023-07-03 15:45:32 -03:00
Antonio Spinelli
7f0db0de04 Organize test suites into unit and integration
This is the goals of project organization composing different
combinations to run any number of tests together.
2023-07-03 13:46:30 -03:00
Antonio Spinelli
6ac3cc384b Add Bimonthly periodicity for Support\Calendar 2023-07-03 13:46:30 -03:00
Antonio Spinelli
563879c218 Fix a bug for monthly calculation periodicity
This change reveals a bug in the Monthly calculation date where the
difference between more than one month was discarded. The new calendar
calculator was prepared to avoid overflow at the end of the month.
2023-07-03 13:46:29 -03:00
Antonio Spinelli
dbb7ed3d5d Add the Calendar Calculator
It encapsulates some date operations like sum. The result will be the
calculated date when calling the nextDateByInterval method, given the
date, periodicity, and skipInterval parameters.

For example, given a date of 2019-12-31, monthly periodicity, and skip
interval 0, the results will be 2020-01-31. Also, if the skip interval
is 1, the result is 2020-02-29. This is because the next date will add
another month to the current range.
2023-07-03 13:46:29 -03:00
Antonio Spinelli
4e3c2ba72c Calculate the next date using periodicity strategies.
All these strategies encapsulate how the Carbon library adds the
interval to the current date.

Monthly, Quarterly, Half-Yearly, and Yearly explicitly use the
overflow control to guarantee the end of the next month or year
adequately.
2023-07-03 13:46:29 -03:00
Antonio Spinelli
8e911491f6 Add tests for Navigation
It is responsible for calculating and manipulating Dates in financial
organization is a very important feature, and requires significant
coverage.

The first test case is just to create an understanding of how it
works.
2023-07-03 13:46:29 -03:00
James Cole
03c5f3cc2d Merge pull request #7711 from firefly-iii/dependabot/composer/develop/spatie/laravel-ignition-2.2.0 2023-07-03 06:01:54 +02:00
dependabot[bot]
484565d600 chore(deps): bump spatie/laravel-ignition from 2.1.3 to 2.2.0
Bumps [spatie/laravel-ignition](https://github.com/spatie/laravel-ignition) from 2.1.3 to 2.2.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.1.3...2.2.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-07-03 04:00:30 +00:00
James Cole
163a979227 Merge pull request #7712 from firefly-iii/dependabot/composer/develop/phpstan/phpstan-1.10.22 2023-07-03 06:00:08 +02:00
James Cole
ed105fee1d Merge pull request #7713 from firefly-iii/dependabot/composer/develop/laravel/framework-10.14.1 2023-07-03 05:59:59 +02:00
James Cole
3b82cfa486 Merge pull request #7714 from firefly-iii/dependabot/composer/develop/nunomaduro/collision-7.7.0 2023-07-03 05:59:28 +02:00
dependabot[bot]
834e52eb2e chore(deps): bump nunomaduro/collision from 7.6.0 to 7.7.0
Bumps [nunomaduro/collision](https://github.com/nunomaduro/collision) from 7.6.0 to 7.7.0.
- [Changelog](https://github.com/nunomaduro/collision/blob/v7.x/CHANGELOG.md)
- [Commits](https://github.com/nunomaduro/collision/compare/v7.6.0...v7.7.0)

---
updated-dependencies:
- dependency-name: nunomaduro/collision
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 03:52:51 +00:00
dependabot[bot]
7eb938fe23 chore(deps): bump laravel/framework from 10.13.5 to 10.14.1
Bumps [laravel/framework](https://github.com/laravel/framework) from 10.13.5 to 10.14.1.
- [Release notes](https://github.com/laravel/framework/releases)
- [Changelog](https://github.com/laravel/framework/blob/10.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/framework/compare/v10.13.5...v10.14.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 03:52:47 +00:00
dependabot[bot]
8db7a4c47d chore(deps-dev): bump phpstan/phpstan from 1.10.21 to 1.10.22
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.21 to 1.10.22.
- [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.21...1.10.22)

---
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-07-03 03:52:37 +00:00
James Cole
b055a5d6af Fix #7704 2023-07-01 12:18:07 +02:00
James Cole
ad0a1b9a24 Add moar debug 2023-06-29 11:47:08 +02:00
James Cole
05d190659a More webhook debug 2023-06-29 11:35:46 +02:00
James Cole
9c6eaffba6 Better debug for webhooks 2023-06-29 11:34:34 +02:00
James Cole
3ee5e9aa04 Remove support for Heroku 2023-06-27 17:15:53 +02:00
James Cole
93a544fe53 Merge tag 'v6.0.16' into develop
v6.0.16
2023-06-27 15:02:07 +02:00
James Cole
3bc98bee20 Merge branch 'release/v6.0.16' 2023-06-27 15:02:06 +02:00
James Cole
f6302bc29b Update meta data for new release. 2023-06-27 15:01:42 +02:00
James Cole
43aa1704a9 Slight refactor in CSS fix. 2023-06-27 14:08:21 +02:00
James Cole
14fe82e361 fix: #7655 2023-06-26 06:10:16 +02:00
James Cole
33317c15a2 Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop 2023-06-26 06:09:10 +02:00
James Cole
ff1b56c5ef chore: update php cs fixer 2023-06-26 06:08:33 +02:00
James Cole
25e2063d70 Merge pull request #7690 from firefly-iii/dependabot/composer/develop/doctrine/dbal-3.6.4
chore(deps): bump doctrine/dbal from 3.6.3 to 3.6.4
2023-06-26 05:59:15 +02:00
James Cole
1d36c74934 Merge pull request #7689 from firefly-iii/dependabot/composer/develop/phpstan/phpstan-1.10.21
chore(deps-dev): bump phpstan/phpstan from 1.10.20 to 1.10.21
2023-06-26 05:59:06 +02:00
James Cole
aebe7908f0 Merge pull request #7687 from MateusBMP/develop
Low contrast ratio on .skin-firefly-iii .btn-info color with dark theme
2023-06-26 05:58:34 +02:00
dependabot[bot]
3e2b881296 chore(deps): bump doctrine/dbal from 3.6.3 to 3.6.4
Bumps [doctrine/dbal](https://github.com/doctrine/dbal) from 3.6.3 to 3.6.4.
- [Release notes](https://github.com/doctrine/dbal/releases)
- [Commits](https://github.com/doctrine/dbal/compare/3.6.3...3.6.4)

---
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-06-26 03:56:30 +00:00
dependabot[bot]
c670a6991d chore(deps-dev): bump phpstan/phpstan from 1.10.20 to 1.10.21
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.20 to 1.10.21.
- [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.20...1.10.21)

---
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-06-26 03:56:24 +00:00
Mateus Pereira
f76a7d59e0 Updating .btn-info color to #fff (issue #7686) 2023-06-25 19:43:09 -03:00
James Cole
3d8bf3ec9b Fix #7683 2023-06-25 06:24:08 +02:00
Sander Dorigo
55e4479454 fix #7683 2023-06-24 21:00:05 +02:00
Sander Dorigo
436fe9fea4 Improve transaction validation 2023-06-24 08:27:28 +02:00
Sander Dorigo
63a7a4b03b New version 2023-06-23 11:07:50 +02:00
Sander Dorigo
0cab974048 Extended IBAN validation 2023-06-23 10:57:26 +02:00
James Cole
5fdcf37d06 Merge tag 'v6.0.15' into develop
v6.0.15
2023-06-21 13:13:11 +02:00
James Cole
4663fb2f12 Merge branch 'release/v6.0.15' 2023-06-21 13:13:10 +02:00
James Cole
e844ab592d Update meta data for release v6.0.15 2023-06-21 13:11:08 +02:00
James Cole
3dcb35710b chore: reformat code. 2023-06-21 12:34:58 +02:00
James Cole
8d87abde64 Fix https://github.com/firefly-iii/firefly-iii/issues/7678 2023-06-21 09:58:37 +02:00
James Cole
2b78485a61 Cleanup edit thing 2023-06-21 07:39:00 +02:00
James Cole
9d057b853f Rule edit form: rule group would always select the top one. 2023-06-21 07:30:08 +02:00
James Cole
bd269eaadf Merge tag 'v6.0.14' into develop
v6.0.14
2023-06-21 07:15:29 +02:00
James Cole
7096c65f50 Merge branch 'release/v6.0.14' 2023-06-21 07:15:28 +02:00
James Cole
1462b0de69 Update meta data for release 6.0.14 2023-06-21 06:40:41 +02:00
James Cole
86a1f170c4 Catch uneven amounts 2023-06-21 06:07:35 +02:00
James Cole
40389fb6d5 fix #7649 2023-06-21 06:06:06 +02:00
James Cole
36021d84cf Fix attachment overview 2023-06-21 06:04:26 +02:00
James Cole
1278f92355 Fix audit log entries and fix #7677 2023-06-21 05:55:57 +02:00
James Cole
573f9adb49 Merge pull request #7676 from timendum/master
Rule ConvertToDeposit: fix missing parameter
2023-06-20 20:03:57 +02:00
Timendum
431c99c27b Rule ConvertToDeposit: fix missing parameter 2023-06-20 15:31:49 +00:00
James Cole
77cc558931 chore: code cleanup 2023-06-20 07:20:26 +02:00
James Cole
42043de34f fix: replace console messages with unified command. 2023-06-20 07:16:56 +02:00
James Cole
f2b2c2109f Merge pull request #7668 from firefly-iii/dependabot/composer/develop/predis/predis-2.2.0 2023-06-19 07:15:54 +02:00
dependabot[bot]
7fe29ad983 Bump predis/predis from 2.1.2 to 2.2.0
Bumps [predis/predis](https://github.com/predis/predis) from 2.1.2 to 2.2.0.
- [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.1.2...v2.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 05:13:33 +00:00
James Cole
07d9bcfb9d Merge pull request #7671 from firefly-iii/dependabot/composer/develop/phpstan/phpstan-1.10.19 2023-06-19 07:07:51 +02:00
James Cole
fdd235e4cb Merge pull request #7670 from firefly-iii/dependabot/composer/develop/nunomaduro/larastan-2.6.3 2023-06-19 07:07:42 +02:00
James Cole
2646acadb8 Merge pull request #7669 from firefly-iii/dependabot/composer/develop/fakerphp/faker-1.23.0 2023-06-19 07:07:33 +02:00
James Cole
5ef646b810 Merge pull request #7667 from firefly-iii/dependabot/composer/develop/nunomaduro/collision-7.6.0 2023-06-19 07:07:15 +02:00
dependabot[bot]
e8bdb5ef38 Bump phpstan/phpstan from 1.10.18 to 1.10.19
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan) from 1.10.18 to 1.10.19.
- [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.18...1.10.19)

---
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-06-19 03:56:57 +00:00
dependabot[bot]
7a851c2cc6 Bump nunomaduro/larastan from 2.6.2 to 2.6.3
Bumps [nunomaduro/larastan](https://github.com/nunomaduro/larastan) from 2.6.2 to 2.6.3.
- [Release notes](https://github.com/nunomaduro/larastan/releases)
- [Changelog](https://github.com/nunomaduro/larastan/blob/master/RELEASE.md)
- [Commits](https://github.com/nunomaduro/larastan/compare/v2.6.2...v2.6.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 03:56:49 +00:00
dependabot[bot]
624784e54e Bump fakerphp/faker from 1.22.0 to 1.23.0
Bumps [fakerphp/faker](https://github.com/FakerPHP/Faker) from 1.22.0 to 1.23.0.
- [Release notes](https://github.com/FakerPHP/Faker/releases)
- [Changelog](https://github.com/FakerPHP/Faker/blob/main/CHANGELOG.md)
- [Commits](https://github.com/FakerPHP/Faker/compare/v1.22.0...v1.23.0)

---
updated-dependencies:
- dependency-name: fakerphp/faker
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 03:56:38 +00:00
dependabot[bot]
20eb2ebe58 Bump nunomaduro/collision from 7.5.2 to 7.6.0
Bumps [nunomaduro/collision](https://github.com/nunomaduro/collision) from 7.5.2 to 7.6.0.
- [Changelog](https://github.com/nunomaduro/collision/blob/v7.x/CHANGELOG.md)
- [Commits](https://github.com/nunomaduro/collision/compare/v7.5.2...v7.6.0)

---
updated-dependencies:
- dependency-name: nunomaduro/collision
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 03:56:28 +00:00
James Cole
25f8acb417 Chore: use of else in accountform 2023-06-18 06:26:50 +02:00
James Cole
f75e6430b1 Fix: nullpointer in billrepository. 2023-06-18 06:26:38 +02:00
James Cole
e72a483c49 Merge pull request #7656 from firefly-iii/fix-7609
Fix #7609
2023-06-14 18:45:14 +02:00
James Cole
68934858ce Fix #7609 2023-06-14 18:44:54 +02:00
James Cole
c08b5177d9 Merge pull request #7647 from firefly-iii/account-validation
Fix account validation
2023-06-12 20:25:08 +02:00
James Cole
fe8635f1ce Fix account validation 2023-06-12 20:24:45 +02:00
James Cole
e96d28b981 Merge pull request #7645 from firefly-iii/fix-7642
Fix #7642
2023-06-12 06:31:25 +02:00
James Cole
784cc3d52d Fix #7642 2023-06-12 06:31:03 +02:00
James Cole
2ab3fb3a71 Merge pull request #7644 from firefly-iii/fix-7630
Fix #7630
2023-06-12 06:24:46 +02:00
James Cole
ff765d4687 Fix #7630 2023-06-12 06:24:30 +02:00
James Cole
f6e778e1d4 Merge branch 'release/v6.0.13' 2023-06-11 18:19:07 +02:00
James Cole
ed36604050 Merge tag 'v6.0.13' into develop
v6.0.13
2023-06-11 18:19:07 +02:00
James Cole
cbf8c6e80d Release v6.0.13 fixes an issue with the authentication controllers. 2023-06-11 18:18:46 +02:00
James Cole
f11db0de61 Merge tag 'v6.0.12' into develop
v6.0.12
2023-06-11 16:34:31 +02:00
1061 changed files with 31544 additions and 22397 deletions

View File

@@ -301,7 +301,6 @@ PUSHER_ID=
DEMO_USERNAME= DEMO_USERNAME=
DEMO_PASSWORD= DEMO_PASSWORD=
USE_ENCRYPTION=false USE_ENCRYPTION=false
IS_HEROKU=false
FIREFLY_III_LAYOUT=v1 FIREFLY_III_LAYOUT=v1
# #

View File

@@ -1 +1,2 @@
vendor vendor
.php-cs-fixer.cache

View File

@@ -379,16 +379,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.17.0", "version": "v3.22.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "3f0ed862f22386c55a767461ef5083bddceeed79" "reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/3f0ed862f22386c55a767461ef5083bddceeed79", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/92b019f6c8d79aa26349d0db7671d37440dc0ff3",
"reference": "3f0ed862f22386c55a767461ef5083bddceeed79", "reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -412,6 +412,7 @@
"symfony/stopwatch": "^5.4 || ^6.0" "symfony/stopwatch": "^5.4 || ^6.0"
}, },
"require-dev": { "require-dev": {
"facile-it/paraunit": "^1.3 || ^2.0",
"justinrainbow/json-schema": "^5.2", "justinrainbow/json-schema": "^5.2",
"keradus/cli-executor": "^2.0", "keradus/cli-executor": "^2.0",
"mikey179/vfsstream": "^1.6.11", "mikey179/vfsstream": "^1.6.11",
@@ -463,7 +464,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.17.0" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.22.0"
}, },
"funding": [ "funding": [
{ {
@@ -471,7 +472,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-05-22T19:59:32+00:00" "time": "2023-07-16T23:08:06+00:00"
}, },
{ {
"name": "psr/cache", "name": "psr/cache",
@@ -1057,16 +1058,16 @@
}, },
{ {
"name": "symfony/filesystem", "name": "symfony/filesystem",
"version": "v6.3.0", "version": "v6.3.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/filesystem.git", "url": "https://github.com/symfony/filesystem.git",
"reference": "97b698e1d77d356304def77a8d0cd73090b359ea" "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/97b698e1d77d356304def77a8d0cd73090b359ea", "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
"reference": "97b698e1d77d356304def77a8d0cd73090b359ea", "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1100,7 +1101,7 @@
"description": "Provides basic utilities for the filesystem", "description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/filesystem/tree/v6.3.0" "source": "https://github.com/symfony/filesystem/tree/v6.3.1"
}, },
"funding": [ "funding": [
{ {
@@ -1116,7 +1117,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-05-30T17:12:32+00:00" "time": "2023-06-01T08:30:39+00:00"
}, },
{ {
"name": "symfony/finder", "name": "symfony/finder",

View File

@@ -30,12 +30,9 @@ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
# clean up php code # clean up php code
cd $SCRIPT_DIR/php-cs-fixer cd $SCRIPT_DIR/php-cs-fixer
composer update composer update --quiet
rm -f .php-cs-fixer.cache rm -f .php-cs-fixer.cache
echo 'Removed cache...'
echo 'Running...'
PHP_CS_FIXER_IGNORE_ENV=true ./vendor/bin/php-cs-fixer fix --config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php --allow-risky=yes PHP_CS_FIXER_IGNORE_ENV=true ./vendor/bin/php-cs-fixer fix --config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php --allow-risky=yes
echo 'Done!'
cd $SCRIPT_DIR/.. cd $SCRIPT_DIR/..
exit 0 exit 0

37
.ci/phpmd.sh Normal file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
#
# phpmd.sh
# 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/>.
#
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
cd $SCRIPT_DIR/phpmd
composer update --quiet
./vendor/bin/phpmd \
$SCRIPT_DIR/../app text phpmd.xml \
--exclude $SCRIPT_DIR/../app/resources/** \
--exclude $SCRIPT_DIR/../app/frontend/** \
--exclude $SCRIPT_DIR/../app/public/** \
--exclude $SCRIPT_DIR/../app/vendor/** \
cd $SCRIPT_DIR/..
exit 0

1
.ci/phpmd/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
vendor

5
.ci/phpmd/composer.json Normal file
View File

@@ -0,0 +1,5 @@
{
"require-dev": {
"phpmd/phpmd": "^2.13"
}
}

1012
.ci/phpmd/composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

72
.ci/phpmd/phpmd.xml Normal file
View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ phpmd.xml
~ 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/>.
-->
<ruleset name="pcsg-generated-ruleset"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>Bla bla</description>
<!--
Commando vanuit firefly directory:
phpmd database,app,tests html /gdrive-all/development/phpmd/phpmd.xml > public/report.html
-->
<!-- Import the entire controversial code rule set -->
<rule ref="rulesets/controversial.xml">
<exclude name="CamelCasePropertyName" />
</rule>
<!-- clean code -->
<rule ref="rulesets/codesize.xml" />
<rule ref="rulesets/design.xml" />
<rule ref="rulesets/naming.xml" />
<rule ref="rulesets/unusedcode.xml" />
<rule ref="rulesets/codesize.xml/CyclomaticComplexity">
<properties>
<property name="reportLevel" value="5"/>
</properties>
</rule>
<rule ref="rulesets/codesize.xml/NPathComplexity">
<properties>
<property name="minimum" value="128"/>
</properties>
</rule>
<rule ref="rulesets/codesize.xml/ExcessiveMethodLength">
<properties>
<property name="minimum" value="40"/>
</properties>
</rule>
<rule ref="rulesets/codesize.xml/ExcessiveParameterList">
<properties>
<property name="minimum" value="5"/>
</properties>
</rule>
<!-- include clean code manually -->
<rule ref="rulesets/cleancode.xml/BooleanArgumentFlag" />
<rule ref="rulesets/cleancode.xml/ElseExpression" />
<!-- no this one -->
<!--<rule ref="rulesets/cleancode.xml/StaticAccess" />-->
</ruleset>

View File

@@ -59,3 +59,5 @@ fi
# restore .env file # restore .env file
mv $SCRIPT_DIR/../.env.backup $SCRIPT_DIR/../.env mv $SCRIPT_DIR/../.env.backup $SCRIPT_DIR/../.env
cd $SCRIPT_DIR/..

View File

@@ -1,321 +0,0 @@
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
APP_ENV=heroku
# Set to true if you want to see debug information in error screens.
APP_DEBUG=false
# This should be your email address.
# If you use Docker or similar, you can set this variable from a file by using SITE_OWNER_FILE
SITE_OWNER=heroku@example.com
# The encryption key for your sessions. Keep this very secure.
# If you generate a new one all existing attachments must be considered LOST.
# Change it to a string of exactly 32 chars or use something like `php artisan key:generate` to generate it.
# If you use Docker or similar, you can set this variable from a file by using APP_KEY_FILE
APP_KEY=7ahyYVPVsmxjdhsweWCauGeJfwc92NP2
#
# Firefly III will launch using this language (for new users and unauthenticated visitors)
# For a list of available languages: https://github.com/firefly-iii/firefly-iii/tree/main/resources/lang
#
# If text is still in English, remember that not everything may have been translated.
DEFAULT_LANGUAGE=en_US
# The locale defines how numbers are formatted.
# by default this value is the same as whatever the language is.
DEFAULT_LOCALE=equal
# Change this value to your preferred time zone.
# Example: Europe/Amsterdam
# For a list of supported time zones, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TZ=UTC
# TRUSTED_PROXIES is a useful variable when using Docker and/or a reverse proxy.
# Set it to ** and reverse proxies work just fine.
TRUSTED_PROXIES=**
# The log channel defines where your log entries go to.
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
# Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself.
# A rotating log option is 'daily', creates 5 files that (surprise) rotate.
# Default setting 'stack' will log to 'daily' and to 'stdout' at the same time.
# - Docker + versions <= 4.8.1.8 and before: use "stdout"
# - Docker + versions > 4.8.1.8 : use "docker_out"
# - Docker + versions >= 5.1.1 : use "stack"
# - For everything else (als not Docker) : use 'stack'
LOG_CHANNEL=stdout
# 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
# nothing will get logged, ever.
APP_LOG_LEVEL=notice
# 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/support/faq
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
# Use "pgsql" for PostgreSQL
# Use "mysql" for MySQL and MariaDB.
# Use "sqlite" for SQLite.
DB_CONNECTION=pgsql
# MySQL supports SSL. You can configure it here.
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
MYSQL_USE_SSL=false
MYSQL_SSL_VERIFY_SERVER_CERT=true
# You need to set at least of these options
MYSQL_SSL_CAPATH=/etc/ssl/certs/
MYSQL_SSL_CA=
MYSQL_SSL_CERT=
MYSQL_SSL_KEY=
MYSQL_SSL_CIPHER=
# PostgreSQL supports SSL. You can configure it here.
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
PGSQL_SSL_MODE=prefer
PGSQL_SSL_ROOT_CERT=null
PGSQL_SSL_CERT=null
PGSQL_SSL_KEY=null
PGSQL_SSL_CRL_FILE=null
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file
SESSION_DRIVER=file
# If you set either of these to 'redis', you might want to update these settings too
# If you use Docker or similar, you can set REDIS_HOST_FILE, REDIS_PASSWORD_FILE or
# REDIS_PORT_FILE to set the value from a file instead of from an environment variable
# can be tcp, unix or http
REDIS_SCHEME=tcp
# use only when using 'unix' for REDIS_SCHEME. Leave empty otherwise.
REDIS_PATH=
# use only when using 'tcp' or 'http' for REDIS_SCHEME. Leave empty otherwise.
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=null
# always use quotes and make sure redis db "0" and "1" exists. Otherwise change accordingly.
REDIS_DB="0"
REDIS_CACHE_DB="1"
# Cookie settings. Should not be necessary to change these.
# If you use Docker or similar, you can set COOKIE_DOMAIN_FILE to set
# the value from a file instead of from an environment variable
COOKIE_PATH="/"
COOKIE_DOMAIN=
COOKIE_SECURE=false
# If you want Firefly III to mail you, update these settings
# For instructions, see: https://docs.firefly-iii.org/advanced-installation/email
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
MAIL_MAILER=log
MAIL_HOST=null
MAIL_PORT=2525
MAIL_FROM=changeme@example.com
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
# Other mail drivers:
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
MAILGUN_DOMAIN=
MAILGUN_SECRET=
# If you are on EU region in mailgun, use api.eu.mailgun.net, otherwise use api.mailgun.net
# If you use Docker or similar, you can set this variable from a file by appending it with _FILE
MAILGUN_ENDPOINT=api.mailgun.net
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
MANDRILL_SECRET=
SPARKPOST_SECRET=
# Firefly III can send you the following messages
SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=true
# These messages contain (sensitive) transaction information:
SEND_REPORT_JOURNALS=true
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
# If you use Docker or similar, you can set this variable from a file by appending it with _FILE
MAPBOX_API_KEY=
# The map will default to this location:
MAP_DEFAULT_LAT=51.983333
MAP_DEFAULT_LONG=5.916667
MAP_DEFAULT_ZOOM=6
# Firefly III currently supports two provider for live Currency Exchange Rates:
# "fixer", and "ratesapi".
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
# built compatible with Fixer.IO, based on data published by European Central Bank, and doesn't require API key.
CER_PROVIDER=ratesapi
# If you have select "fixer" as default currency exchange rates,
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
# the free API up to the point where you might as well offer nothing.
# If you use Docker or similar, you can set this variable from a file by appending it with _FILE
FIXER_API_KEY=
# Firefly III has two options for user authentication. "eloquent" is the default,
# and "ldap" for LDAP servers.
# For full instructions on these settings please visit:
# https://docs.firefly-iii.org/advanced-installation/authentication
# If you use Docker or similar, you can set this variable from a file by appending it with _FILE
LOGIN_PROVIDER=eloquent
#
# It's also possible to change the way users are authenticated. You could use Authelia for example.
# Authentication via the REMOTE_USER header is supported. Change the value below to "remote_user_guard".
#
# If you do this please read the documentation for instructions and warnings:
# https://docs.firefly-iii.org/advanced-installation/authentication
#
# This function is available in Firefly III v5.3.0 and higher.
AUTHENTICATION_GUARD=web
#
# Likewise, it's impossible to log out users who's authentication is handled by an external system.
# Enter a custom URL here that will force a logout (your authentication provider can tell you).
# Setting this variable only works when AUTHENTICATION_GUARD != web
#
CUSTOM_LOGOUT_URL=
# LDAP connection configuration
# OpenLDAP, FreeIPA or ActiveDirectory
# # If you use Docker or similar, you can set this variable from a file by appending it with _FILE
ADLDAP_CONNECTION_SCHEME=OpenLDAP
ADLDAP_AUTO_CONNECT=true
# LDAP connection settings
# You can set the following variables from a file by appending them with _FILE:
# ADLDAP_CONTROLLERS, ADLDAP_PORT, ADLDAP_BASEDN
ADLDAP_CONTROLLERS=
ADLDAP_PORT=389
ADLDAP_TIMEOUT=5
ADLDAP_BASEDN=""
ADLDAP_FOLLOW_REFFERALS=false
# SSL/TLS settings
ADLDAP_USE_SSL=false
ADLDAP_USE_TLS=false
ADLDAP_SSL_CACERTDIR=
ADLDAP_SSL_CACERTFILE=
ADLDAP_SSL_CERTFILE=
ADLDAP_SSL_KEYFILE=
ADLDAP_SSL_CIPHER_SUITE=
ADLDAP_SSL_REQUIRE_CERT=
# You can set the following variables from a file by appending them with _FILE:
ADLDAP_ADMIN_USERNAME=
ADLDAP_ADMIN_PASSWORD=
# You can set the following variables from a file by appending them with _FILE:
ADLDAP_ACCOUNT_PREFIX=
ADLDAP_ACCOUNT_SUFFIX=
# LDAP authentication settings.
ADLDAP_PASSWORD_SYNC=false
ADLDAP_LOGIN_FALLBACK=false
ADLDAP_DISCOVER_FIELD=distinguishedname
ADLDAP_AUTH_FIELD=distinguishedname
# Will allow SSO if your server provides an AUTH_USER field.
# You can set the following variables from a file by appending them with _FILE:
WINDOWS_SSO_ENABLED=false
WINDOWS_SSO_DISCOVER=samaccountname
WINDOWS_SSO_KEY=AUTH_USER
# field to sync as local username.
# You can set the following variable from a file by appending it with _FILE:
ADLDAP_SYNC_FIELD=userprincipalname
# You can disable the X-Frame-Options header if it interferes with tools like
# Organizr. This is at your own risk. Applications running in frames run the risk
# of leaking information to their parent frame.
DISABLE_FRAME_HEADER=false
# You can disable the Content Security Policy header when you're using an ancient browser
# or any version of Microsoft Edge / Internet Explorer (which amounts to the same thing really)
# This leaves you with the risk of not being able to stop XSS bugs should they ever surface.
# This is at your own risk.
DISABLE_CSP_HEADER=false
# If you wish to track your own behavior over Firefly III, set valid analytics tracker information here.
# Nobody uses this except for me on the demo site. But hey, feel free to use this if you want to.
# Do not prepend the TRACKER_URL with http:// or https://
# The only tracker supported is Matomo.
# You can set the following variables from a file by appending them with _FILE:
TRACKER_SITE_ID=
TRACKER_URL=
# You can fine tune the start-up of a Docker container by editing these environment variables.
# Use this at your own risk. Disabling certain checks and features may result in lost of inconsistent data.
# However if you know what you're doing you can significantly speed up container start times.
# Set each value to true to enable, or false to disable.
# Check if the SQLite database exists. Can be skipped if you're not using SQLite.
# Won't significantly speed up things.
DKR_CHECK_SQLITE=true
# Run database creation and migration commands. Disable this only if you're 100% sure the DB exists
# and is up to date.
DKR_RUN_MIGRATION=true
# Run database upgrade commands. Disable this only when you're 100% sure your DB is up-to-date
# with the latest fixes (outside of migrations!)
DKR_RUN_UPGRADE=true
# Verify database integrity. Includes all data checks and verifications.
# Disabling this makes Firefly III assume your DB is intact.
DKR_RUN_VERIFY=true
# Run database reporting commands. When disabled, Firefly III won't go over your data to report current state.
# Disabling this should have no impact on data integrity or safety but it won't warn you of possible issues.
DKR_RUN_REPORT=true
# Generate OAuth2 keys.
# When disabled, Firefly III won't attempt to generate OAuth2 Passport keys. This won't be an issue, IFF (if and only if)
# you had previously generated keys already and they're stored in your database for restoration.
DKR_RUN_PASSPORT_INSTALL=true
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
ADLDAP_CONNECTION=default
BROADCAST_DRIVER=log
QUEUE_DRIVER=sync
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
USE_ENCRYPTION=false
IS_HEROKU=false
FIREFLY_III_LAYOUT=v1
#
# If you have trouble configuring your Firefly III installation, DON'T BOTHER setting this variable.
# It won't work. It doesn't do ANYTHING. Don't believe the lies you read online. I'm not joking.
# This configuration value WILL NOT HELP.
#
# This variable is ONLY used in some of the emails Firefly III sends around. Nowhere else.
# So when configuring anything WEB related this variable doesn't do anything. Nothing
#
# If you're stuck I understand you get desperate but look SOMEWHERE ELSE.
#
APP_URL=http://localhost

View File

@@ -1,31 +0,0 @@
bg_BG
ca_ES
cs_CZ
da_DK
de_DE
el_GR
en_GB
en_US
es_ES
fi_FI
fr_FR
hu_HU
id_ID
it_IT
ja_JP
ko_KR
nb_NO
nl_NL
pl_PL
pt_BR
pt_PT
ro_RO
ru_RU
sk_SK
sl_SI
sv_SE
tr_TR
uk_UA
vi_VN
zh-hans_CN
zh-hant_CN

View File

@@ -143,6 +143,7 @@ MAIL_FROM=changeme@example.com
MAIL_USERNAME=null MAIL_USERNAME=null
MAIL_PASSWORD=null MAIL_PASSWORD=null
MAIL_ENCRYPTION=null MAIL_ENCRYPTION=null
MAIL_SENDMAIL_COMMAND=
# Other mail drivers: # Other mail drivers:
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE # If you use Docker or similar, you can set these variables from a file by appending them with _FILE
@@ -240,7 +241,7 @@ ALLOW_WEBHOOKS=false
# #
# The static cron job token can be useful when you use Docker and wish to manage cron jobs. # The static cron job token can be useful when you use Docker and wish to manage cron jobs.
# 1. Set this token to any 32-character value (this is important!). # 1. Set this token to any 32-character value (this is important!).
# 2. Use this token in the cron URL instead of a user's command line token. # 2. Use this token in the cron URL instead of a user's command line token that you can find in /profile
# #
# For more info: https://docs.firefly-iii.org/firefly-iii/advanced-installation/cron/ # For more info: https://docs.firefly-iii.org/firefly-iii/advanced-installation/cron/
# #
@@ -297,7 +298,6 @@ PUSHER_SECRET=
PUSHER_ID= PUSHER_ID=
DEMO_USERNAME= DEMO_USERNAME=
DEMO_PASSWORD= DEMO_PASSWORD=
IS_HEROKU=false
FIREFLY_III_LAYOUT=v1 FIREFLY_III_LAYOUT=v1
# #

View File

@@ -2,7 +2,10 @@
## Our Pledge ## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making
participation in our project and our community a harassment-free experience for everyone, regardless of age, body size,
disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race,
religion, or sexual identity and orientation.
## Our Standards ## Our Standards
@@ -24,23 +27,35 @@ Examples of unacceptable behavior by participants include:
## Our Responsibilities ## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take
appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits,
issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any
contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope ## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the
project or its community. Examples of representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed representative at an online or offline
event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at james@firefly-iii.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at
james@firefly-iii.org. The project team will review and investigate all complaints, and will respond in a way that it
deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the
reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent
repercussions as determined by other members of the project's leadership.
## Attribution ## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available
at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org [homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/ [version]: http://contributor-covenant.org/version/1/4/

View File

@@ -2,9 +2,11 @@
Sometimes bugs reported to Firefly III are configuration and system problems on the user's side. Sometimes bugs reported to Firefly III are configuration and system problems on the user's side.
If you run into any of the following problems, there's a good chance it's not a Firefly III issue, but a configuration issue. If you run into any of the following problems, there's a good chance it's not a Firefly III issue, but a configuration
issue.
- ⚠️ Firefly III can't connect to the database when starting or the password is wrong, even though you're sure it's correct. - ⚠️ Firefly III can't connect to the database when starting or the password is wrong, even though you're sure it's
correct.
- ⚠️ Errors about a missing `APP_KEY` or other encryption/hash problems - ⚠️ Errors about a missing `APP_KEY` or other encryption/hash problems
- ⚠️ You can't login due to `419` errors (page expired) - ⚠️ You can't login due to `419` errors (page expired)
- ⚠️ Any `500` error when starting Firefly III - ⚠️ Any `500` error when starting Firefly III
@@ -13,4 +15,6 @@ If you run into any of the following problems, there's a good chance it's not a
- ⚠️ Firefly III does not work behind your reverse proxy - ⚠️ Firefly III does not work behind your reverse proxy
- ⚠️ You can't connect to the Data Importer due to 404's or authentication issues. - ⚠️ You can't connect to the Data Importer due to 404's or authentication issues.
If you run into an issue like this, please start a [discussion](https://github.com/firefly-iii/firefly-iii/discussions) or chat on [Gitter.im](https://gitter.im/firefly-iii/firefly-iii). There's a good chance it's not a bug but something we can fix rather quickly :+1: If you run into an issue like this, please start a [discussion](https://github.com/firefly-iii/firefly-iii/discussions)
or chat on [Gitter.im](https://gitter.im/firefly-iii/firefly-iii). There's a good chance it's not a bug but something we
can fix rather quickly :+1:

6
.github/support.md vendored
View File

@@ -9,7 +9,8 @@ First of all: thank you for reporting a bug instead of ditching the tool altoget
1. Open bugs will have open issues, so search for one first. 1. Open bugs will have open issues, so search for one first.
2. If your feature request is already there, vote on it with :+1: or :-1: reactions. 2. If your feature request is already there, vote on it with :+1: or :-1: reactions.
3. Do NOT hijack old issues with the bug you found, open your own issue. 3. Do NOT hijack old issues with the bug you found, open your own issue.
4. If relevant, take the time and see if the [demo site](https://demo.firefly-iii.org/) is also suffering from your issue. 4. If relevant, take the time and see if the [demo site](https://demo.firefly-iii.org/) is also suffering from your
issue.
5. If relevant, read the [documentation](https://docs.firefly-iii.org/). 5. If relevant, read the [documentation](https://docs.firefly-iii.org/).
Please follow these guidelines when opening new issues: Please follow these guidelines when opening new issues:
@@ -25,7 +26,8 @@ Only then [create a new issue](https://github.com/firefly-iii/firefly-iii/issues
## Issue closure and abandonment policy ## Issue closure and abandonment policy
- Issues can be converted into discussions if it's not a bug or feature request. - Issues can be converted into discussions if it's not a bug or feature request.
- Features that won't be implemented will be labelled "wontfix". [This isn't personal](https://docs.firefly-iii.org/firefly-iii/about-firefly-iii/what-its-not/). - Features that won't be implemented will be labelled "
wontfix". [This isn't personal](https://docs.firefly-iii.org/firefly-iii/about-firefly-iii/what-its-not/).
- Issues can be closed if they're duplicates of other issues. - Issues can be closed if they're duplicates of other issues.
- Issues can be closed if the answer is in the FAQ. - Issues can be closed if the answer is in the FAQ.
- Issues will be closed automatically after 14 days. - Issues will be closed automatically after 14 days.

View File

@@ -7,8 +7,7 @@ jobs:
auto_comment: auto_comment:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- - uses: aws-actions/closed-issue-message@v1
uses: aws-actions/closed-issue-message@v1
with: with:
message: | message: |
Hi there! This is an automatic reply. `Share and enjoy` Hi there! This is an automatic reply. `Share and enjoy`

View File

@@ -1,5 +1,6 @@
name: Sonarcloud name: Sonarcloud
on: on:
pull_request:
push: push:
branches: branches:
- main - main
@@ -12,6 +13,33 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup PHP with Xdebug
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
coverage: xdebug
- name: Install Composer dependencies
run: composer install --prefer-dist --no-interaction --no-progress --no-scripts
- name: Copy environment file
run: cp .env.example .env
- name: Generate app key
run: php artisan key:generate
- name: "Run tests with coverage"
run: composer coverage
- name: Fix code coverage paths
run: sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace/@g' coverage.xml
- name: SonarCloud Scan - name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master uses: SonarSource/sonarcloud-github-action@master
env: env:

2
.gitignore vendored
View File

@@ -7,4 +7,4 @@ npm-debug.log
yarn-error.log yarn-error.log
.env .env
/.ci/php-cs-fixer/vendor /.ci/php-cs-fixer/vendor
/.ci/coverage coverage.xml

View File

@@ -1,65 +0,0 @@
{
"name": "Firefly III",
"description": "A free and open source personal finances manager",
"repository": "https://github.com/firefly-iii/firefly-iii",
"website": "https://firefly-iii.org/",
"logo": "https://raw.githubusercontent.com/firefly-iii/firefly-iii/main/public/mstile-150x150.png",
"keywords": [
"finance",
"finances",
"manager",
"management",
"euro",
"dollar",
"laravel",
"money",
"currency",
"financials",
"financial",
"budgets",
"administration",
"tool",
"tooling",
"help",
"helper",
"assistant",
"planning",
"organizing",
"bills",
"personal finance",
"budgets",
"budgeting",
"budgeting tool",
"budgeting application",
"transactions",
"self hosted",
"self-hosted",
"transfers",
"management"
],
"addons": [
{
"plan": "heroku-postgresql",
"options": {
"version": "12"
}
}
],
"scripts": {
"postdeploy": "php artisan migrate:refresh --seed;php artisan firefly:instructions install"
},
"buildpacks": [
{
"url": "heroku/php"
},
{
"url": "https://github.com/heroku/heroku-buildpack-locale"
}
],
"env": {
"APP_KEY": {
"description": "This key is used to create app cookies en secure attachments.",
"value": "base64:If1gJN4pyycXTq+WS5TjneDympKuu+8SKvTl6RZnhJg="
}
}
}

View File

@@ -74,42 +74,6 @@ abstract class Controller extends BaseController
); );
} }
/**
* Method to help build URL's.
*
* @return string
*/
final protected function buildParams(): string
{
$return = '?';
$params = [];
foreach ($this->parameters as $key => $value) {
if ('page' === $key) {
continue;
}
if ($value instanceof Carbon) {
$params[$key] = $value->format('Y-m-d');
continue;
}
$params[$key] = $value;
}
return $return.http_build_query($params);
}
/**
* @return Manager
*/
final protected function getManager(): Manager
{
// create some objects:
$manager = new Manager();
$baseUrl = request()->getSchemeAndHttpHost().'/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
return $manager;
}
/** /**
* Method to grab all parameters from the URL. * Method to grab all parameters from the URL.
* *
@@ -211,4 +175,40 @@ abstract class Controller extends BaseController
return $bag; return $bag;
} }
/**
* Method to help build URL's.
*
* @return string
*/
final protected function buildParams(): string
{
$return = '?';
$params = [];
foreach ($this->parameters as $key => $value) {
if ('page' === $key) {
continue;
}
if ($value instanceof Carbon) {
$params[$key] = $value->format('Y-m-d');
continue;
}
$params[$key] = $value;
}
return $return . http_build_query($params);
}
/**
* @return Manager
*/
final protected function getManager(): Manager
{
// create some objects:
$manager = new Manager();
$baseUrl = request()->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
return $manager;
}
} }

View File

@@ -201,6 +201,94 @@ class DestroyController extends Controller
return response()->json([], 204); return response()->json([], 204);
} }
/**
*
*/
private function destroyBudgets(): void
{
/** @var AvailableBudgetRepositoryInterface $abRepository */
$abRepository = app(AvailableBudgetRepositoryInterface::class);
$abRepository->destroyAll();
/** @var BudgetLimitRepositoryInterface $blRepository */
$blRepository = app(BudgetLimitRepositoryInterface::class);
$blRepository->destroyAll();
/** @var BudgetRepositoryInterface $budgetRepository */
$budgetRepository = app(BudgetRepositoryInterface::class);
$budgetRepository->destroyAll();
}
/**
*
*/
private function destroyBills(): void
{
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$repository->destroyAll();
}
/**
*
*/
private function destroyPiggyBanks(): void
{
/** @var PiggyBankRepositoryInterface $repository */
$repository = app(PiggyBankRepositoryInterface::class);
$repository->destroyAll();
}
/**
*
*/
private function destroyRules(): void
{
/** @var RuleGroupRepositoryInterface $repository */
$repository = app(RuleGroupRepositoryInterface::class);
$repository->destroyAll();
}
/**
*
*/
private function destroyRecurringTransactions(): void
{
/** @var RecurringRepositoryInterface $repository */
$repository = app(RecurringRepositoryInterface::class);
$repository->destroyAll();
}
/**
*
*/
private function destroyCategories(): void
{
/** @var CategoryRepositoryInterface $categoryRepos */
$categoryRepos = app(CategoryRepositoryInterface::class);
$categoryRepos->destroyAll();
}
/**
*
*/
private function destroyTags(): void
{
/** @var TagRepositoryInterface $tagRepository */
$tagRepository = app(TagRepositoryInterface::class);
$tagRepository->destroyAll();
}
/**
* @return void
*/
private function destroyObjectGroups(): void
{
/** @var ObjectGroupRepositoryInterface $repository */
$repository = app(ObjectGroupRepositoryInterface::class);
$repository->deleteAll();
}
/** /**
* @param array $types * @param array $types
*/ */
@@ -226,91 +314,6 @@ class DestroyController extends Controller
} }
} }
/**
*
*/
private function destroyBills(): void
{
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$repository->destroyAll();
}
/**
*
*/
private function destroyBudgets(): void
{
/** @var AvailableBudgetRepositoryInterface $abRepository */
$abRepository = app(AvailableBudgetRepositoryInterface::class);
$abRepository->destroyAll();
/** @var BudgetLimitRepositoryInterface $blRepository */
$blRepository = app(BudgetLimitRepositoryInterface::class);
$blRepository->destroyAll();
/** @var BudgetRepositoryInterface $budgetRepository */
$budgetRepository = app(BudgetRepositoryInterface::class);
$budgetRepository->destroyAll();
}
/**
*
*/
private function destroyCategories(): void
{
/** @var CategoryRepositoryInterface $categoryRepos */
$categoryRepos = app(CategoryRepositoryInterface::class);
$categoryRepos->destroyAll();
}
private function destroyObjectGroups(): void
{
/** @var ObjectGroupRepositoryInterface $repository */
$repository = app(ObjectGroupRepositoryInterface::class);
$repository->deleteAll();
}
/**
*
*/
private function destroyPiggyBanks(): void
{
/** @var PiggyBankRepositoryInterface $repository */
$repository = app(PiggyBankRepositoryInterface::class);
$repository->destroyAll();
}
/**
*
*/
private function destroyRecurringTransactions(): void
{
/** @var RecurringRepositoryInterface $repository */
$repository = app(RecurringRepositoryInterface::class);
$repository->destroyAll();
}
/**
*
*/
private function destroyRules(): void
{
/** @var RuleGroupRepositoryInterface $repository */
$repository = app(RuleGroupRepositoryInterface::class);
$repository->destroyAll();
}
/**
*
*/
private function destroyTags(): void
{
/** @var TagRepositoryInterface $tagRepository */
$tagRepository = app(TagRepositoryInterface::class);
$tagRepository->destroyAll();
}
/** /**
* @param array $types * @param array $types
*/ */

View File

@@ -29,6 +29,8 @@ use FireflyIII\Api\V1\Requests\Data\Export\ExportRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Export\ExportDataGenerator; use FireflyIII\Support\Export\ExportDataGenerator;
use Illuminate\Http\Response as LaravelResponse; use Illuminate\Http\Response as LaravelResponse;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/** /**
* Class ExportController * Class ExportController
@@ -69,6 +71,36 @@ class ExportController extends Controller
return $this->returnExport('accounts'); return $this->returnExport('accounts');
} }
/**
* @param string $key
*
* @return LaravelResponse
* @throws FireflyException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function returnExport(string $key): LaravelResponse
{
$date = date('Y-m-d-H-i-s');
$fileName = sprintf('%s-export-%s.csv', $date, $key);
$data = $this->exporter->export();
/** @var LaravelResponse $response */
$response = response($data[$key]);
$response
->header('Content-Description', 'File Transfer')
->header('Content-Type', 'application/octet-stream')
->header('Content-Disposition', 'attachment; filename=' . $fileName)
->header('Content-Transfer-Encoding', 'binary')
->header('Connection', 'Keep-Alive')
->header('Expires', '0')
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->header('Pragma', 'public')
->header('Content-Length', (string)strlen($data[$key]));
return $response;
}
/** /**
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBills * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/data/exportBills
@@ -200,32 +232,4 @@ class ExportController extends Controller
return $this->returnExport('transactions'); return $this->returnExport('transactions');
} }
/**
* @param string $key
*
* @return LaravelResponse
* @throws FireflyException
*/
private function returnExport(string $key): LaravelResponse
{
$date = date('Y-m-d-H-i-s');
$fileName = sprintf('%s-export-%s.csv', $date, $key);
$data = $this->exporter->export();
/** @var LaravelResponse $response */
$response = response($data[$key]);
$response
->header('Content-Description', 'File Transfer')
->header('Content-Type', 'application/octet-stream')
->header('Content-Disposition', 'attachment; filename='.$fileName)
->header('Content-Transfer-Encoding', 'binary')
->header('Connection', 'Keep-Alive')
->header('Expires', '0')
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->header('Pragma', 'public')
->header('Content-Length', (string)strlen($data[$key]));
return $response;
}
} }

View File

@@ -37,6 +37,9 @@ use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/**
* Class PurgeController
*/
class PurgeController extends Controller class PurgeController extends Controller
{ {
/** /**

View File

@@ -96,6 +96,7 @@ class TriggerController extends Controller
$ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]); $ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]);
} }
// file the rule(s) // file the rule(s)
$transactions = $ruleEngine->find(); $transactions = $ruleEngine->find();
$count = $transactions->count(); $count = $transactions->count();
@@ -149,6 +150,7 @@ class TriggerController extends Controller
$ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]); $ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]);
} }
// fire the rule(s) // fire the rule(s)
$ruleEngine->fire(); $ruleEngine->fire();

View File

@@ -97,6 +97,21 @@ class ShowController extends Controller
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
} }
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/getTransactionByJournal
*
* Show a single transaction, by transaction journal.
*
* @param TransactionJournal $transactionJournal
*
* @return JsonResponse
*/
public function showJournal(TransactionJournal $transactionJournal): JsonResponse
{
return $this->show($transactionJournal->transactionGroup);
}
/** /**
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/getTransaction * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/getTransaction
@@ -133,19 +148,4 @@ class ShowController extends Controller
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
} }
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/transactions/getTransactionByJournal
*
* Show a single transaction, by transaction journal.
*
* @param TransactionJournal $transactionJournal
*
* @return JsonResponse
*/
public function showJournal(TransactionJournal $transactionJournal): JsonResponse
{
return $this->show($transactionJournal->transactionGroup);
}
} }

View File

@@ -97,35 +97,6 @@ class UpdateController extends Controller
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
} }
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/enableCurrency
*
* Enable a currency.
*
* @param TransactionCurrency $currency
*
* @return JsonResponse
* @throws FireflyException
* @throws JsonException
*/
public function enable(TransactionCurrency $currency): JsonResponse
{
$this->repository->enable($currency);
$manager = $this->getManager();
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency);
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($currency, $transformer, 'currencies');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
}
/** /**
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/defaultCurrency * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/defaultCurrency
@@ -157,6 +128,35 @@ class UpdateController extends Controller
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
} }
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/enableCurrency
*
* Enable a currency.
*
* @param TransactionCurrency $currency
*
* @return JsonResponse
* @throws FireflyException
* @throws JsonException
*/
public function enable(TransactionCurrency $currency): JsonResponse
{
$this->repository->enable($currency);
$manager = $this->getManager();
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency);
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($currency, $transformer, 'currencies');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
}
/** /**
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/updateCurrency * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/currencies/updateCurrency

View File

@@ -30,6 +30,9 @@ use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/**
* Class DestroyController
*/
class DestroyController extends Controller class DestroyController extends Controller
{ {
private LinkTypeRepositoryInterface $repository; private LinkTypeRepositoryInterface $repository;

View File

@@ -37,6 +37,9 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
/**
* Class ShowController
*/
class ShowController extends Controller class ShowController extends Controller
{ {
private LinkTypeRepositoryInterface $repository; private LinkTypeRepositoryInterface $repository;

View File

@@ -35,6 +35,9 @@ use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
/**
* Class StoreController
*/
class StoreController extends Controller class StoreController extends Controller
{ {
use TransactionFilter; use TransactionFilter;

View File

@@ -34,6 +34,9 @@ use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
/**
* Class UpdateController
*/
class UpdateController extends Controller class UpdateController extends Controller
{ {
private JournalRepositoryInterface $journalRepository; private JournalRepositoryInterface $journalRepository;

View File

@@ -33,7 +33,6 @@ use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use JsonException;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;

View File

@@ -121,30 +121,6 @@ class BasicController extends Controller
return response()->json($return); return response()->json($return);
} }
/**
* Check if date is outside session range.
*
* @param Carbon $date
*
* @param Carbon $start
* @param Carbon $end
*
* @return bool
*/
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
{
$result = false;
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
$result = true;
}
// start and end in the past? use $end
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
$result = true;
}
return $result;
}
/** /**
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
@@ -422,4 +398,28 @@ class BasicController extends Controller
return $return; return $return;
} }
/**
* Check if date is outside session range.
*
* @param Carbon $date
*
* @param Carbon $start
* @param Carbon $end
*
* @return bool
*/
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
{
$result = false;
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
$result = true;
}
// start and end in the past? use $end
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
$result = true;
}
return $result;
}
} }

View File

@@ -94,6 +94,42 @@ class ConfigurationController extends Controller
return response()->json($return); return response()->json($return);
} }
/**
* Get all config values.
*
* @return array
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function getDynamicConfiguration(): array
{
$isDemoSite = app('fireflyconfig')->get('is_demo_site');
$updateCheck = app('fireflyconfig')->get('permission_update_check');
$lastCheck = app('fireflyconfig')->get('last_update_check');
$singleUser = app('fireflyconfig')->get('single_user_mode');
return [
'is_demo_site' => $isDemoSite?->data,
'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data,
'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data,
'single_user_mode' => $singleUser?->data,
];
}
/**
* @return array
*/
private function getStaticConfiguration(): array
{
$list = EitherConfigKey::$static;
$return = [];
foreach ($list as $key) {
$return[$key] = config($key);
}
return $return;
}
/** /**
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/configuration/getSingleConfiguration * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/configuration/getSingleConfiguration
@@ -102,7 +138,6 @@ class ConfigurationController extends Controller
* *
* @return JsonResponse * @return JsonResponse
* @throws ContainerExceptionInterface * @throws ContainerExceptionInterface
* @throws FireflyException
* @throws NotFoundExceptionInterface * @throws NotFoundExceptionInterface
*/ */
public function show(string $configKey): JsonResponse public function show(string $configKey): JsonResponse
@@ -164,40 +199,4 @@ class ConfigurationController extends Controller
return response()->json(['data' => $data])->header('Content-Type', self::CONTENT_TYPE); return response()->json(['data' => $data])->header('Content-Type', self::CONTENT_TYPE);
} }
/**
* Get all config values.
*
* @return array
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function getDynamicConfiguration(): array
{
$isDemoSite = app('fireflyconfig')->get('is_demo_site');
$updateCheck = app('fireflyconfig')->get('permission_update_check');
$lastCheck = app('fireflyconfig')->get('last_update_check');
$singleUser = app('fireflyconfig')->get('single_user_mode');
return [
'is_demo_site' => $isDemoSite?->data,
'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data,
'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data,
'single_user_mode' => $singleUser?->data,
];
}
/**
* @return array
*/
private function getStaticConfiguration(): array
{
$list = EitherConfigKey::$static;
$return = [];
foreach ($list as $key) {
$return[$key] = config($key);
}
return $return;
}
} }

View File

@@ -28,6 +28,8 @@ use FireflyIII\Api\V1\Requests\System\CronRequest;
use FireflyIII\Support\Http\Controllers\CronRunner; use FireflyIII\Support\Http\Controllers\CronRunner;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/** /**
* Class CronController * Class CronController
@@ -44,6 +46,8 @@ class CronController extends Controller
* @param string $token * @param string $token
* *
* @return JsonResponse * @return JsonResponse
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/ */
public function cron(CronRequest $request, string $token): JsonResponse public function cron(CronRequest $request, string $token): JsonResponse
{ {

View File

@@ -35,6 +35,9 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
/**
* Class MessageController
*/
class MessageController extends Controller class MessageController extends Controller
{ {
public const RESOURCE_KEY = 'webhook_messages'; public const RESOURCE_KEY = 'webhook_messages';

View File

@@ -127,6 +127,7 @@ class ShowController extends Controller
*/ */
public function triggerTransaction(Webhook $webhook, TransactionGroup $group): JsonResponse public function triggerTransaction(Webhook $webhook, TransactionGroup $group): JsonResponse
{ {
app('log')->debug(sprintf('Now in triggerTransaction(%d, %d)', $webhook->id, $group->id));
/** @var MessageGeneratorInterface $engine */ /** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class); $engine = app(MessageGeneratorInterface::class);
$engine->setUser(auth()->user()); $engine->setUser(auth()->user());
@@ -141,6 +142,7 @@ class ShowController extends Controller
$engine->generateMessages(); $engine->generateMessages();
// trigger event to send them: // trigger event to send them:
app('log')->debug('send event RequestedSendWebhookMessages');
event(new RequestedSendWebhookMessages()); event(new RequestedSendWebhookMessages());
return response()->json([], 204); return response()->json([], 204);

View File

@@ -38,6 +38,9 @@ class ExportRequest extends FormRequest
use ChecksLogin; use ChecksLogin;
use ConvertsDataTypes; use ConvertsDataTypes;
/**
* @return array
*/
public function getAll(): array public function getAll(): array
{ {
$result = [ $result = [

View File

@@ -82,6 +82,28 @@ class GenericRequest extends FormRequest
return $return; return $return;
} }
/**
*
*/
private function parseAccounts(): void
{
if (0 !== $this->accounts->count()) {
return;
}
$repository = app(AccountRepositoryInterface::class);
$repository->setUser(auth()->user());
$array = $this->get('accounts');
if (is_array($array)) {
foreach ($array as $accountId) {
$accountId = (int)$accountId;
$account = $repository->find($accountId);
if (null !== $account) {
$this->accounts->push($account);
}
}
}
}
/** /**
* @return Collection * @return Collection
*/ */
@@ -92,6 +114,28 @@ class GenericRequest extends FormRequest
return $this->bills; return $this->bills;
} }
/**
*
*/
private function parseBills(): void
{
if (0 !== $this->bills->count()) {
return;
}
$repository = app(BillRepositoryInterface::class);
$repository->setUser(auth()->user());
$array = $this->get('bills');
if (is_array($array)) {
foreach ($array as $billId) {
$billId = (int)$billId;
$bill = $repository->find($billId);
if (null !== $bill) {
$this->bills->push($bill);
}
}
}
}
/** /**
* @return Collection * @return Collection
*/ */
@@ -102,6 +146,28 @@ class GenericRequest extends FormRequest
return $this->budgets; return $this->budgets;
} }
/**
*
*/
private function parseBudgets(): void
{
if (0 !== $this->budgets->count()) {
return;
}
$repository = app(BudgetRepositoryInterface::class);
$repository->setUser(auth()->user());
$array = $this->get('budgets');
if (is_array($array)) {
foreach ($array as $budgetId) {
$budgetId = (int)$budgetId;
$budget = $repository->find($budgetId);
if (null !== $budget) {
$this->budgets->push($budget);
}
}
}
}
/** /**
* @return Collection * @return Collection
*/ */
@@ -112,6 +178,28 @@ class GenericRequest extends FormRequest
return $this->categories; return $this->categories;
} }
/**
*
*/
private function parseCategories(): void
{
if (0 !== $this->categories->count()) {
return;
}
$repository = app(CategoryRepositoryInterface::class);
$repository->setUser(auth()->user());
$array = $this->get('categories');
if (is_array($array)) {
foreach ($array as $categoryId) {
$categoryId = (int)$categoryId;
$category = $repository->find($categoryId);
if (null !== $category) {
$this->categories->push($category);
}
}
}
}
/** /**
* @return Carbon * @return Carbon
*/ */
@@ -180,114 +268,6 @@ class GenericRequest extends FormRequest
return $this->tags; return $this->tags;
} }
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
// this is cheating, but it works to initialize the collections.
$this->accounts = new Collection();
$this->budgets = new Collection();
$this->categories = new Collection();
$this->bills = new Collection();
$this->tags = new Collection();
return [
'start' => 'required|date',
'end' => 'required|date|after_or_equal:start',
];
}
/**
*
*/
private function parseAccounts(): void
{
if (0 !== $this->accounts->count()) {
return;
}
$repository = app(AccountRepositoryInterface::class);
$repository->setUser(auth()->user());
$array = $this->get('accounts');
if (is_array($array)) {
foreach ($array as $accountId) {
$accountId = (int)$accountId;
$account = $repository->find($accountId);
if (null !== $account) {
$this->accounts->push($account);
}
}
}
}
/**
*
*/
private function parseBills(): void
{
if (0 !== $this->bills->count()) {
return;
}
$repository = app(BillRepositoryInterface::class);
$repository->setUser(auth()->user());
$array = $this->get('bills');
if (is_array($array)) {
foreach ($array as $billId) {
$billId = (int)$billId;
$bill = $repository->find($billId);
if (null !== $bill) {
$this->bills->push($bill);
}
}
}
}
/**
*
*/
private function parseBudgets(): void
{
if (0 !== $this->budgets->count()) {
return;
}
$repository = app(BudgetRepositoryInterface::class);
$repository->setUser(auth()->user());
$array = $this->get('budgets');
if (is_array($array)) {
foreach ($array as $budgetId) {
$budgetId = (int)$budgetId;
$budget = $repository->find($budgetId);
if (null !== $budget) {
$this->budgets->push($budget);
}
}
}
}
/**
*
*/
private function parseCategories(): void
{
if (0 !== $this->categories->count()) {
return;
}
$repository = app(CategoryRepositoryInterface::class);
$repository->setUser(auth()->user());
$array = $this->get('categories');
if (is_array($array)) {
foreach ($array as $categoryId) {
$categoryId = (int)$categoryId;
$category = $repository->find($categoryId);
if (null !== $category) {
$this->categories->push($category);
}
}
}
}
/** /**
* *
*/ */
@@ -309,4 +289,24 @@ class GenericRequest extends FormRequest
} }
} }
} }
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
// this is cheating, but it works to initialize the collections.
$this->accounts = new Collection();
$this->budgets = new Collection();
$this->categories = new Collection();
$this->bills = new Collection();
$this->tags = new Collection();
return [
'start' => 'required|date',
'end' => 'required|date|after_or_equal:start',
];
}
} }

View File

@@ -73,6 +73,65 @@ class StoreRequest extends FormRequest
]; ];
} }
/**
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
*
* @return array
*/
private function getTransactionData(): array
{
$return = [];
// transaction data:
/** @var array|null $transactions */
$transactions = $this->get('transactions');
if (null === $transactions) {
return [];
}
/** @var array $transaction */
foreach ($transactions as $transaction) {
$return[] = $this->getSingleTransactionData($transaction);
}
return $return;
}
/**
* Returns the repetition data as it is found in the submitted data.
*
* @return array
*/
private function getRepetitionData(): array
{
$return = [];
// repetition data:
/** @var array|null $repetitions */
$repetitions = $this->get('repetitions');
if (null === $repetitions) {
return [];
}
/** @var array $repetition */
foreach ($repetitions as $repetition) {
$current = [];
if (array_key_exists('type', $repetition)) {
$current['type'] = $repetition['type'];
}
if (array_key_exists('moment', $repetition)) {
$current['moment'] = $repetition['moment'];
}
if (array_key_exists('skip', $repetition)) {
$current['skip'] = (int)$repetition['skip'];
}
if (array_key_exists('weekend', $repetition)) {
$current['weekend'] = (int)$repetition['weekend'];
}
$return[] = $current;
}
return $return;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
* *
@@ -139,63 +198,4 @@ class StoreRequest extends FormRequest
} }
); );
} }
/**
* Returns the repetition data as it is found in the submitted data.
*
* @return array
*/
private function getRepetitionData(): array
{
$return = [];
// repetition data:
/** @var array|null $repetitions */
$repetitions = $this->get('repetitions');
if (null === $repetitions) {
return [];
}
/** @var array $repetition */
foreach ($repetitions as $repetition) {
$current = [];
if (array_key_exists('type', $repetition)) {
$current['type'] = $repetition['type'];
}
if (array_key_exists('moment', $repetition)) {
$current['moment'] = $repetition['moment'];
}
if (array_key_exists('skip', $repetition)) {
$current['skip'] = (int)$repetition['skip'];
}
if (array_key_exists('weekend', $repetition)) {
$current['weekend'] = (int)$repetition['weekend'];
}
$return[] = $current;
}
return $return;
}
/**
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
*
* @return array
*/
private function getTransactionData(): array
{
$return = [];
// transaction data:
/** @var array|null $transactions */
$transactions = $this->get('transactions');
if (null === $transactions) {
return [];
}
/** @var array $transaction */
foreach ($transactions as $transaction) {
$return[] = $this->getSingleTransactionData($transaction);
}
return $return;
}
} }

View File

@@ -80,6 +80,70 @@ class UpdateRequest extends FormRequest
return $return; return $return;
} }
/**
* Returns the repetition data as it is found in the submitted data.
*
* @return array|null
*/
private function getRepetitionData(): ?array
{
$return = [];
// repetition data:
/** @var array|null $repetitions */
$repetitions = $this->get('repetitions');
if (null === $repetitions) {
return null;
}
/** @var array $repetition */
foreach ($repetitions as $repetition) {
$current = [];
if (array_key_exists('type', $repetition)) {
$current['type'] = $repetition['type'];
}
if (array_key_exists('moment', $repetition)) {
$current['moment'] = (string)$repetition['moment'];
}
if (array_key_exists('skip', $repetition)) {
$current['skip'] = (int)$repetition['skip'];
}
if (array_key_exists('weekend', $repetition)) {
$current['weekend'] = (int)$repetition['weekend'];
}
$return[] = $current;
}
if (0 === count($return)) {
return null;
}
return $return;
}
/**
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
*
* @return array|null
*/
private function getTransactionData(): ?array
{
$return = [];
// transaction data:
/** @var array|null $transactions */
$transactions = $this->get('transactions');
if (null === $transactions) {
return null;
}
/** @var array $transaction */
foreach ($transactions as $transaction) {
$return[] = $this->getSingleTransactionData($transaction);
}
return $return;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
* *
@@ -154,68 +218,4 @@ class UpdateRequest extends FormRequest
); );
} }
/**
* Returns the repetition data as it is found in the submitted data.
*
* @return array|null
*/
private function getRepetitionData(): ?array
{
$return = [];
// repetition data:
/** @var array|null $repetitions */
$repetitions = $this->get('repetitions');
if (null === $repetitions) {
return null;
}
/** @var array $repetition */
foreach ($repetitions as $repetition) {
$current = [];
if (array_key_exists('type', $repetition)) {
$current['type'] = $repetition['type'];
}
if (array_key_exists('moment', $repetition)) {
$current['moment'] = (string)$repetition['moment'];
}
if (array_key_exists('skip', $repetition)) {
$current['skip'] = (int)$repetition['skip'];
}
if (array_key_exists('weekend', $repetition)) {
$current['weekend'] = (int)$repetition['weekend'];
}
$return[] = $current;
}
if (0 === count($return)) {
return null;
}
return $return;
}
/**
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
*
* @return array|null
*/
private function getTransactionData(): ?array
{
$return = [];
// transaction data:
/** @var array|null $transactions */
$transactions = $this->get('transactions');
if (null === $transactions) {
return null;
}
/** @var array $transaction */
foreach ($transactions as $transaction) {
$return[] = $this->getSingleTransactionData($transaction);
}
return $return;
}
} }

View File

@@ -67,6 +67,48 @@ class StoreRequest extends FormRequest
return $data; return $data;
} }
/**
* @return array
*/
private function getRuleTriggers(): array
{
$triggers = $this->get('triggers');
$return = [];
if (is_array($triggers)) {
foreach ($triggers as $trigger) {
$return[] = [
'type' => $trigger['type'],
'value' => $trigger['value'],
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')),
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
];
}
}
return $return;
}
/**
* @return array
*/
private function getRuleActions(): array
{
$actions = $this->get('actions');
$return = [];
if (is_array($actions)) {
foreach ($actions as $action) {
$return[] = [
'type' => $action['type'],
'value' => $action['value'],
'active' => $this->convertBoolean((string)($action['active'] ?? 'true')),
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
];
}
}
return $return;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
* *
@@ -120,6 +162,21 @@ class StoreRequest extends FormRequest
); );
} }
/**
* Adds an error to the validator when there are no triggers in the array of data.
*
* @param Validator $validator
*/
protected function atLeastOneTrigger(Validator $validator): void
{
$data = $validator->getData();
$triggers = $data['triggers'] ?? [];
// need at least one trigger
if (!is_countable($triggers) || 0 === count($triggers)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_trigger'));
}
}
/** /**
* Adds an error to the validator when there are no repetitions in the array of data. * Adds an error to the validator when there are no repetitions in the array of data.
* *
@@ -135,35 +192,6 @@ class StoreRequest extends FormRequest
} }
} }
/**
* Adds an error to the validator when there are no ACTIVE actions in the array of data.
*
* @param Validator $validator
*/
protected function atLeastOneActiveAction(Validator $validator): void
{
$data = $validator->getData();
$actions = $data['actions'] ?? [];
// need at least one trigger
if (!is_countable($actions) || 0 === count($actions)) {
return;
}
$allInactive = true;
$inactiveIndex = 0;
foreach ($actions as $index => $action) {
$active = array_key_exists('active', $action) ? $action['active'] : true; // assume true
if (true === $active) {
$allInactive = false;
}
if (false === $active) {
$inactiveIndex = $index;
}
}
if (true === $allInactive) {
$validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
}
}
/** /**
* Adds an error to the validator when there are no ACTIVE triggers in the array of data. * Adds an error to the validator when there are no ACTIVE triggers in the array of data.
* *
@@ -194,59 +222,31 @@ class StoreRequest extends FormRequest
} }
/** /**
* Adds an error to the validator when there are no triggers in the array of data. * Adds an error to the validator when there are no ACTIVE actions in the array of data.
* *
* @param Validator $validator * @param Validator $validator
*/ */
protected function atLeastOneTrigger(Validator $validator): void protected function atLeastOneActiveAction(Validator $validator): void
{ {
$data = $validator->getData(); $data = $validator->getData();
$triggers = $data['triggers'] ?? []; $actions = $data['actions'] ?? [];
// need at least one trigger // need at least one trigger
if (!is_countable($triggers) || 0 === count($triggers)) { if (!is_countable($actions) || 0 === count($actions)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_trigger')); return;
}
$allInactive = true;
$inactiveIndex = 0;
foreach ($actions as $index => $action) {
$active = array_key_exists('active', $action) ? $action['active'] : true; // assume true
if (true === $active) {
$allInactive = false;
}
if (false === $active) {
$inactiveIndex = $index;
} }
} }
if (true === $allInactive) {
/** $validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
* @return array
*/
private function getRuleActions(): array
{
$actions = $this->get('actions');
$return = [];
if (is_array($actions)) {
foreach ($actions as $action) {
$return[] = [
'type' => $action['type'],
'value' => $action['value'],
'active' => $this->convertBoolean((string)($action['active'] ?? 'true')),
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
];
} }
} }
return $return;
}
/**
* @return array
*/
private function getRuleTriggers(): array
{
$triggers = $this->get('triggers');
$return = [];
if (is_array($triggers)) {
foreach ($triggers as $trigger) {
$return[] = [
'type' => $trigger['type'],
'value' => $trigger['value'],
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')),
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
];
}
}
return $return;
}
} }

View File

@@ -52,24 +52,11 @@ class TestRequest extends FormRequest
} }
/** /**
* @return array * @return int
*/ */
public function rules(): array private function getPage(): int
{ {
return [ return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page');
'start' => 'date',
'end' => 'date|after_or_equal:start',
'accounts' => '',
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
];
}
/**
* @return array
*/
private function getAccounts(): array
{
return $this->get('accounts');
} }
/** /**
@@ -83,10 +70,23 @@ class TestRequest extends FormRequest
} }
/** /**
* @return int * @return array
*/ */
private function getPage(): int private function getAccounts(): array
{ {
return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page'); return $this->get('accounts');
}
/**
* @return array
*/
public function rules(): array
{
return [
'start' => 'date',
'end' => 'date|after_or_equal:start',
'accounts' => '',
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
];
} }
} }

View File

@@ -50,16 +50,13 @@ class TriggerRequest extends FormRequest
} }
/** /**
* @return array * @param string $field
*
* @return Carbon|null
*/ */
public function rules(): array private function getDate(string $field): ?Carbon
{ {
return [ return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($this->query($field), 0, 10));
'start' => 'date',
'end' => 'date|after_or_equal:start',
'accounts' => '',
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
];
} }
/** /**
@@ -71,12 +68,15 @@ class TriggerRequest extends FormRequest
} }
/** /**
* @param string $field * @return array
*
* @return Carbon|null
*/ */
private function getDate(string $field): ?Carbon public function rules(): array
{ {
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', substr($this->query($field), 0, 10)); return [
'start' => 'date',
'end' => 'date|after_or_equal:start',
'accounts' => '',
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
];
} }
} }

View File

@@ -73,6 +73,56 @@ class UpdateRequest extends FormRequest
return $return; return $return;
} }
/**
* @return array|null
*/
private function getRuleTriggers(): ?array
{
if (!$this->has('triggers')) {
return null;
}
$triggers = $this->get('triggers');
$return = [];
if (is_array($triggers)) {
foreach ($triggers as $trigger) {
$active = array_key_exists('active', $trigger) ? $trigger['active'] : true;
$stopProcessing = array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false;
$return[] = [
'type' => $trigger['type'],
'value' => $trigger['value'],
'active' => $active,
'stop_processing' => $stopProcessing,
];
}
}
return $return;
}
/**
* @return array|null
*/
private function getRuleActions(): ?array
{
if (!$this->has('actions')) {
return null;
}
$actions = $this->get('actions');
$return = [];
if (is_array($actions)) {
foreach ($actions as $action) {
$return[] = [
'type' => $action['type'],
'value' => $action['value'],
'active' => $this->convertBoolean((string)($action['active'] ?? 'false')),
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
];
}
}
return $return;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
* *
@@ -130,21 +180,6 @@ class UpdateRequest extends FormRequest
); );
} }
/**
* Adds an error to the validator when there are no repetitions in the array of data.
*
* @param Validator $validator
*/
protected function atLeastOneAction(Validator $validator): void
{
$data = $validator->getData();
$actions = $data['actions'] ?? null;
// need at least one action
if (is_array($actions) && 0 === count($actions)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_action'));
}
}
/** /**
* Adds an error to the validator when there are no repetitions in the array of data. * Adds an error to the validator when there are no repetitions in the array of data.
* *
@@ -160,36 +195,6 @@ class UpdateRequest extends FormRequest
} }
} }
/**
* Adds an error to the validator when there are no repetitions in the array of data.
*
* @param Validator $validator
*/
protected function atLeastOneValidAction(Validator $validator): void
{
$data = $validator->getData();
$actions = $data['actions'] ?? [];
$allInactive = true;
$inactiveIndex = 0;
// need at least one action
if (is_array($actions) && 0 === count($actions)) {
return;
}
foreach ($actions as $index => $action) {
$active = array_key_exists('active', $action) ? $action['active'] : true; // assume true
if (true === $active) {
$allInactive = false;
}
if (false === $active) {
$inactiveIndex = $index;
}
}
if (true === $allInactive) {
$validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
}
}
/** /**
* Adds an error to the validator when there are no repetitions in the array of data. * Adds an error to the validator when there are no repetitions in the array of data.
* *
@@ -220,52 +225,47 @@ class UpdateRequest extends FormRequest
} }
/** /**
* @return array|null * Adds an error to the validator when there are no repetitions in the array of data.
*
* @param Validator $validator
*/ */
private function getRuleActions(): ?array protected function atLeastOneAction(Validator $validator): void
{ {
if (!$this->has('actions')) { $data = $validator->getData();
return null; $actions = $data['actions'] ?? null;
// need at least one action
if (is_array($actions) && 0 === count($actions)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_action'));
} }
$actions = $this->get('actions');
$return = [];
if (is_array($actions)) {
foreach ($actions as $action) {
$return[] = [
'type' => $action['type'],
'value' => $action['value'],
'active' => $this->convertBoolean((string)($action['active'] ?? 'false')),
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
];
}
}
return $return;
} }
/** /**
* @return array|null * Adds an error to the validator when there are no repetitions in the array of data.
*
* @param Validator $validator
*/ */
private function getRuleTriggers(): ?array protected function atLeastOneValidAction(Validator $validator): void
{ {
if (!$this->has('triggers')) { $data = $validator->getData();
return null; $actions = $data['actions'] ?? [];
} $allInactive = true;
$triggers = $this->get('triggers'); $inactiveIndex = 0;
$return = []; // need at least one action
if (is_array($triggers)) { if (is_array($actions) && 0 === count($actions)) {
foreach ($triggers as $trigger) { return;
$active = array_key_exists('active', $trigger) ? $trigger['active'] : true;
$stopProcessing = array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false;
$return[] = [
'type' => $trigger['type'],
'value' => $trigger['value'],
'active' => $active,
'stop_processing' => $stopProcessing,
];
}
} }
return $return; foreach ($actions as $index => $action) {
$active = array_key_exists('active', $action) ? $action['active'] : true; // assume true
if (true === $active) {
$allInactive = false;
}
if (false === $active) {
$inactiveIndex = $index;
}
}
if (true === $allInactive) {
$validator->errors()->add(sprintf('actions.%d.active', $inactiveIndex), (string)trans('validation.at_least_one_active_action'));
}
} }
} }

View File

@@ -50,16 +50,13 @@ class TestRequest extends FormRequest
} }
/** /**
* @return array * @param string $field
*
* @return Carbon|null
*/ */
public function rules(): array private function getDate(string $field): ?Carbon
{ {
return [ return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
'start' => 'date',
'end' => 'date|after_or_equal:start',
'accounts' => '',
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
];
} }
/** /**
@@ -71,12 +68,15 @@ class TestRequest extends FormRequest
} }
/** /**
* @param string $field * @return array
*
* @return Carbon|null
*/ */
private function getDate(string $field): ?Carbon public function rules(): array
{ {
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field)); return [
'start' => 'date',
'end' => 'date|after_or_equal:start',
'accounts' => '',
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
];
} }
} }

View File

@@ -50,14 +50,13 @@ class TriggerRequest extends FormRequest
} }
/** /**
* @return array * @param string $field
*
* @return Carbon|null
*/ */
public function rules(): array private function getDate(string $field): ?Carbon
{ {
return [ return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
'start' => 'date',
'end' => 'date|after_or_equal:start',
];
} }
/** /**
@@ -69,12 +68,13 @@ class TriggerRequest extends FormRequest
} }
/** /**
* @param string $field * @return array
*
* @return Carbon|null
*/ */
private function getDate(string $field): ?Carbon public function rules(): array
{ {
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field)); return [
'start' => 'date',
'end' => 'date|after_or_equal:start',
];
} }
} }

View File

@@ -69,6 +69,103 @@ class StoreRequest extends FormRequest
// TODO include location and ability to process it. // TODO include location and ability to process it.
} }
/**
* Get transaction data.
*
* @return array
*/
private function getTransactionData(): array
{
$return = [];
/**
* @var array $transaction
*/
foreach ($this->get('transactions') as $transaction) {
$object = new NullArrayObject($transaction);
$return[] = [
'type' => $this->clearString($object['type'], false),
'date' => $this->dateFromValue($object['date']),
'order' => $this->integerFromValue((string)$object['order']),
'currency_id' => $this->integerFromValue((string)$object['currency_id']),
'currency_code' => $this->clearString((string)$object['currency_code'], false),
// foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code'], false),
// amount and foreign amount. Cannot be 0.
'amount' => $this->clearString((string)$object['amount'], false),
'foreign_amount' => $this->clearString((string)$object['foreign_amount'], false),
// description.
'description' => $this->clearString($object['description'], false),
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string)$object['source_id']),
'source_name' => $this->clearString((string)$object['source_name'], false),
'source_iban' => $this->clearString((string)$object['source_iban'], false),
'source_number' => $this->clearString((string)$object['source_number'], false),
'source_bic' => $this->clearString((string)$object['source_bic'], false),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
'destination_name' => $this->clearString((string)$object['destination_name'], false),
'destination_iban' => $this->clearString((string)$object['destination_iban'], false),
'destination_number' => $this->clearString((string)$object['destination_number'], false),
'destination_bic' => $this->clearString((string)$object['destination_bic'], false),
// budget info
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
'budget_name' => $this->clearString((string)$object['budget_name'], false),
// category info
'category_id' => $this->integerFromValue((string)$object['category_id']),
'category_name' => $this->clearString((string)$object['category_name'], false),
// journal bill reference. Optional. Will only work for withdrawals
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
'bill_name' => $this->clearString((string)$object['bill_name'], false),
// piggy bank reference. Optional. Will only work for transfers
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name'], false),
// some other interesting properties
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
'notes' => $this->clearString((string)$object['notes']),
'tags' => $this->arrayFromValue($object['tags']),
// all custom fields:
'internal_reference' => $this->clearString((string)$object['internal_reference'], false),
'external_id' => $this->clearString((string)$object['external_id'], false),
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id'], false),
'external_url' => $this->clearString((string)$object['external_url'], false),
'sepa_cc' => $this->clearString((string)$object['sepa_cc'], false),
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op'], false),
'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id'], false),
'sepa_db' => $this->clearString((string)$object['sepa_db'], false),
'sepa_country' => $this->clearString((string)$object['sepa_country'], false),
'sepa_ep' => $this->clearString((string)$object['sepa_ep'], false),
'sepa_ci' => $this->clearString((string)$object['sepa_ci'], false),
'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id'], false),
// custom date fields. Must be Carbon objects. Presence is optional.
'interest_date' => $this->dateFromValue($object['interest_date']),
'book_date' => $this->dateFromValue($object['book_date']),
'process_date' => $this->dateFromValue($object['process_date']),
'due_date' => $this->dateFromValue($object['due_date']),
'payment_date' => $this->dateFromValue($object['payment_date']),
'invoice_date' => $this->dateFromValue($object['invoice_date']),
];
}
return $return;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
* *
@@ -197,101 +294,4 @@ class StoreRequest extends FormRequest
} }
); );
} }
/**
* Get transaction data.
*
* @return array
*/
private function getTransactionData(): array
{
$return = [];
/**
* @var array $transaction
*/
foreach ($this->get('transactions') as $transaction) {
$object = new NullArrayObject($transaction);
$return[] = [
'type' => $this->clearString($object['type'], false),
'date' => $this->dateFromValue($object['date']),
'order' => $this->integerFromValue((string)$object['order']),
'currency_id' => $this->integerFromValue((string)$object['currency_id']),
'currency_code' => $this->clearString((string)$object['currency_code'], false),
// foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code'], false),
// amount and foreign amount. Cannot be 0.
'amount' => $this->clearString((string)$object['amount'], false),
'foreign_amount' => $this->clearString((string)$object['foreign_amount'], false),
// description.
'description' => $this->clearString($object['description'], false),
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string)$object['source_id']),
'source_name' => $this->clearString((string)$object['source_name'], false),
'source_iban' => $this->clearString((string)$object['source_iban'], false),
'source_number' => $this->clearString((string)$object['source_number'], false),
'source_bic' => $this->clearString((string)$object['source_bic'], false),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
'destination_name' => $this->clearString((string)$object['destination_name'], false),
'destination_iban' => $this->clearString((string)$object['destination_iban'], false),
'destination_number' => $this->clearString((string)$object['destination_number'], false),
'destination_bic' => $this->clearString((string)$object['destination_bic'], false),
// budget info
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
'budget_name' => $this->clearString((string)$object['budget_name'], false),
// category info
'category_id' => $this->integerFromValue((string)$object['category_id']),
'category_name' => $this->clearString((string)$object['category_name'], false),
// journal bill reference. Optional. Will only work for withdrawals
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
'bill_name' => $this->clearString((string)$object['bill_name'], false),
// piggy bank reference. Optional. Will only work for transfers
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name'], false),
// some other interesting properties
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
'notes' => $this->clearString((string)$object['notes']),
'tags' => $this->arrayFromValue($object['tags']),
// all custom fields:
'internal_reference' => $this->clearString((string)$object['internal_reference'], false),
'external_id' => $this->clearString((string)$object['external_id'], false),
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id'], false),
'external_url' => $this->clearString((string)$object['external_url'], false),
'sepa_cc' => $this->clearString((string)$object['sepa_cc'], false),
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op'], false),
'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id'], false),
'sepa_db' => $this->clearString((string)$object['sepa_db'], false),
'sepa_country' => $this->clearString((string)$object['sepa_country'], false),
'sepa_ep' => $this->clearString((string)$object['sepa_ep'], false),
'sepa_ci' => $this->clearString((string)$object['sepa_ci'], false),
'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id'], false),
// custom date fields. Must be Carbon objects. Presence is optional.
'interest_date' => $this->dateFromValue($object['interest_date']),
'book_date' => $this->dateFromValue($object['book_date']),
'process_date' => $this->dateFromValue($object['process_date']),
'due_date' => $this->dateFromValue($object['due_date']),
'payment_date' => $this->dateFromValue($object['payment_date']),
'invoice_date' => $this->dateFromValue($object['invoice_date']),
];
}
return $return;
}
} }

View File

@@ -59,6 +59,7 @@ class UpdateRequest extends FormRequest
* Get all data. Is pretty complex because of all the ??-statements. * Get all data. Is pretty complex because of all the ??-statements.
* *
* @return array * @return array
* @throws FireflyException
*/ */
public function getAll(): array public function getAll(): array
{ {
@@ -152,6 +153,7 @@ class UpdateRequest extends FormRequest
* Get transaction data. * Get transaction data.
* *
* @return array * @return array
* @throws FireflyException
*/ */
private function getTransactionData(): array private function getTransactionData(): array
{ {
@@ -322,7 +324,7 @@ class UpdateRequest extends FormRequest
return [ return [
// basic fields for group: // basic fields for group:
'group_title' => 'between:1,1000', 'group_title' => 'between:1,1000|nullable',
'apply_rules' => [new IsBoolean()], 'apply_rules' => [new IsBoolean()],
// transaction rules (in array for splits): // transaction rules (in array for splits):
@@ -335,8 +337,8 @@ class UpdateRequest extends FormRequest
// currency info // currency info
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id', 'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id', 'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code', 'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
@@ -356,9 +358,9 @@ class UpdateRequest extends FormRequest
'transactions.*.destination_name' => 'between:1,255|nullable', 'transactions.*.destination_name' => 'between:1,255|nullable',
// budget, category, bill and piggy // budget, category, bill and piggy
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser()], 'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser(), 'nullable'],
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser()], 'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser()],
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser()], 'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'],
'transactions.*.category_name' => 'between:1,255|nullable', 'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()], 'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()],
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser()], 'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser()],
@@ -366,7 +368,7 @@ class UpdateRequest extends FormRequest
// other interesting fields // other interesting fields
'transactions.*.reconciled' => [new IsBoolean()], 'transactions.*.reconciled' => [new IsBoolean()],
'transactions.*.notes' => 'min:1|max:50000|nullable', 'transactions.*.notes' => 'min:1|max:50000|nullable',
'transactions.*.tags' => 'between:0,255', 'transactions.*.tags' => 'between:0,255|nullable',
// meta info fields // meta info fields
'transactions.*.internal_reference' => 'min:1|max:255|nullable', 'transactions.*.internal_reference' => 'min:1|max:255|nullable',

View File

@@ -27,6 +27,9 @@ use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
/**
* Class PreferenceStoreRequest
*/
class PreferenceStoreRequest extends FormRequest class PreferenceStoreRequest extends FormRequest
{ {
use ChecksLogin; use ChecksLogin;

View File

@@ -28,6 +28,9 @@ use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
/**
* Class PreferenceUpdateRequest
*/
class PreferenceUpdateRequest extends FormRequest class PreferenceUpdateRequest extends FormRequest
{ {
use ChecksLogin; use ChecksLogin;

View File

@@ -32,7 +32,6 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use JsonException; use JsonException;

View File

@@ -64,54 +64,6 @@ class Controller extends BaseController
} }
} }
/**
* @param string $key
* @param LengthAwarePaginator $paginator
* @param AbstractTransformer $transformer
*
* @return array
*/
final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array
{
$manager = new Manager();
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$objects = $paginator->getCollection();
// the transformer, at this point, needs to collect information that ALL items in the collection
// require, like meta data and stuff like that, and save it for later.
$transformer->collectMetaData($objects);
$resource = new FractalCollection($objects, $transformer, $key);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return $manager->createData($resource)->toArray();
}
/**
* Returns a JSON API object and returns it.
*
* @param string $key
* @param Model $object
* @param AbstractTransformer $transformer
*
* @return array
*/
final protected function jsonApiObject(string $key, Model $object, AbstractTransformer $transformer): array
{
// create some objects:
$manager = new Manager();
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$transformer->collectMetaData(new Collection([$object]));
$resource = new Item($object, $transformer, $key);
return $manager->createData($resource)->toArray();
}
/** /**
* TODO duplicate from V1 controller * TODO duplicate from V1 controller
* Method to grab all parameters from the URL. * Method to grab all parameters from the URL.
@@ -179,4 +131,52 @@ class Controller extends BaseController
return $bag; return $bag;
} }
/**
* @param string $key
* @param LengthAwarePaginator $paginator
* @param AbstractTransformer $transformer
*
* @return array
*/
final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array
{
$manager = new Manager();
$baseUrl = request()->getSchemeAndHttpHost() . '/api/v2';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$objects = $paginator->getCollection();
// the transformer, at this point, needs to collect information that ALL items in the collection
// require, like meta data and stuff like that, and save it for later.
$transformer->collectMetaData($objects);
$resource = new FractalCollection($objects, $transformer, $key);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return $manager->createData($resource)->toArray();
}
/**
* Returns a JSON API object and returns it.
*
* @param string $key
* @param Model $object
* @param AbstractTransformer $transformer
*
* @return array
*/
final protected function jsonApiObject(string $key, Model $object, AbstractTransformer $transformer): array
{
// create some objects:
$manager = new Manager();
$baseUrl = request()->getSchemeAndHttpHost() . '/api/v2';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$transformer->collectMetaData(new Collection([$object]));
$resource = new Item($object, $transformer, $key);
return $manager->createData($resource)->toArray();
}
} }

View File

@@ -31,6 +31,9 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
/**
* Class ListController
*/
class ListController extends Controller class ListController extends Controller
{ {
private BudgetRepositoryInterface $repository; private BudgetRepositoryInterface $repository;

View File

@@ -1,6 +1,6 @@
<?php <?php
declare(strict_types=1);
/* /*
* ShowController.php * ShowController.php
* Copyright (c) 2023 james@firefly-iii.org * Copyright (c) 2023 james@firefly-iii.org
@@ -21,6 +21,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\Model\Budget; namespace FireflyIII\Api\V2\Controllers\Model\Budget;
use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Controllers\Controller;

View File

@@ -32,6 +32,9 @@ use FireflyIII\Transformers\V2\BudgetLimitTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
/**
* Class ListController
*/
class ListController extends Controller class ListController extends Controller
{ {
private BudgetLimitRepositoryInterface $repository; private BudgetLimitRepositoryInterface $repository;

View File

@@ -24,6 +24,9 @@ declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\System; namespace FireflyIII\Api\V2\Controllers\System;
/**
* Class ConfigurationController
*/
class ConfigurationController class ConfigurationController
{ {
} }

View File

@@ -26,6 +26,9 @@ namespace FireflyIII\Api\V2\Controllers\System;
use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Controllers\Controller;
/**
* Class DebugController
*/
class DebugController extends Controller class DebugController extends Controller
{ {
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\AutoBudget; use FireflyIII\Models\AutoBudget;
use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
@@ -39,6 +40,8 @@ use Illuminate\Console\Command;
*/ */
class CorrectAmounts extends Command class CorrectAmounts extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'This command makes sure positive and negative amounts are recorded correctly.'; protected $description = 'This command makes sure positive and negative amounts are recorded correctly.';
protected $signature = 'firefly-iii:fix-amount-pos-neg'; protected $signature = 'firefly-iii:fix-amount-pos-neg';
@@ -78,7 +81,7 @@ class CorrectAmounts extends Command
$set = AutoBudget::where('amount', '<', 0)->get(); $set = AutoBudget::where('amount', '<', 0)->get();
$count = $set->count(); $count = $set->count();
if (0 === $count) { if (0 === $count) {
$this->info('Correct: All auto budget amounts are positive.'); $this->friendlyPositive('All auto budget amounts are positive.');
return; return;
} }
@@ -87,7 +90,7 @@ class CorrectAmounts extends Command
$item->amount = app('steam')->positive((string)$item->amount); $item->amount = app('steam')->positive((string)$item->amount);
$item->save(); $item->save();
} }
$this->line(sprintf('Corrected %d auto budget amount(s).', $count)); $this->friendlyInfo(sprintf('Corrected %d auto budget amount(s).', $count));
} }
/** /**
@@ -98,7 +101,7 @@ class CorrectAmounts extends Command
$set = AvailableBudget::where('amount', '<', 0)->get(); $set = AvailableBudget::where('amount', '<', 0)->get();
$count = $set->count(); $count = $set->count();
if (0 === $count) { if (0 === $count) {
$this->info('Correct: All available budget amounts are positive.'); $this->friendlyPositive('All available budget amounts are positive.');
return; return;
} }
@@ -107,7 +110,7 @@ class CorrectAmounts extends Command
$item->amount = app('steam')->positive((string)$item->amount); $item->amount = app('steam')->positive((string)$item->amount);
$item->save(); $item->save();
} }
$this->line(sprintf('Corrected %d available budget amount(s).', $count)); $this->friendlyInfo(sprintf('Corrected %d available budget amount(s).', $count));
} }
/** /**
@@ -118,7 +121,7 @@ class CorrectAmounts extends Command
$set = Bill::where('amount_min', '<', 0)->orWhere('amount_max', '<', 0)->get(); $set = Bill::where('amount_min', '<', 0)->orWhere('amount_max', '<', 0)->get();
$count = $set->count(); $count = $set->count();
if (0 === $count) { if (0 === $count) {
$this->info('Correct: All bill amounts are positive.'); $this->friendlyPositive('All bill amounts are positive.');
return; return;
} }
@@ -128,6 +131,7 @@ class CorrectAmounts extends Command
$item->amount_max = app('steam')->positive((string)$item->amount_max); $item->amount_max = app('steam')->positive((string)$item->amount_max);
$item->save(); $item->save();
} }
$this->friendlyInfo(sprintf('Corrected %d bill amount(s).', $count));
} }
/** /**
@@ -138,7 +142,7 @@ class CorrectAmounts extends Command
$set = BudgetLimit::where('amount', '<', 0)->get(); $set = BudgetLimit::where('amount', '<', 0)->get();
$count = $set->count(); $count = $set->count();
if (0 === $count) { if (0 === $count) {
$this->info('Correct: All budget limit amounts are positive.'); $this->friendlyPositive('All budget limit amounts are positive.');
return; return;
} }
@@ -147,7 +151,7 @@ class CorrectAmounts extends Command
$item->amount = app('steam')->positive((string)$item->amount); $item->amount = app('steam')->positive((string)$item->amount);
$item->save(); $item->save();
} }
$this->line(sprintf('Corrected %d budget limit amount(s).', $count)); $this->friendlyInfo(sprintf('Corrected %d budget limit amount(s).', $count));
} }
/** /**
@@ -158,7 +162,7 @@ class CorrectAmounts extends Command
$set = CurrencyExchangeRate::where('rate', '<', 0)->get(); $set = CurrencyExchangeRate::where('rate', '<', 0)->get();
$count = $set->count(); $count = $set->count();
if (0 === $count) { if (0 === $count) {
$this->info('Correct: All currency exchange rates are positive.'); $this->friendlyPositive('All currency exchange rates are positive.');
return; return;
} }
@@ -167,7 +171,27 @@ class CorrectAmounts extends Command
$item->rate = app('steam')->positive((string)$item->rate); $item->rate = app('steam')->positive((string)$item->rate);
$item->save(); $item->save();
} }
$this->line(sprintf('Corrected %d currency exchange rate(s).', $count)); $this->friendlyInfo(sprintf('Corrected %d currency exchange rate(s).', $count));
}
/**
* @return void
*/
private function fixRepetitions(): void
{
$set = PiggyBankRepetition::where('currentamount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->friendlyPositive('All piggy bank repetition amounts are positive.');
return;
}
/** @var PiggyBankRepetition $item */
foreach ($set as $item) {
$item->currentamount = app('steam')->positive((string)$item->currentamount);
$item->save();
}
$this->friendlyInfo(sprintf('Corrected %d piggy bank repetition amount(s).', $count));
} }
/** /**
@@ -178,7 +202,7 @@ class CorrectAmounts extends Command
$set = PiggyBank::where('targetamount', '<', 0)->get(); $set = PiggyBank::where('targetamount', '<', 0)->get();
$count = $set->count(); $count = $set->count();
if (0 === $count) { if (0 === $count) {
$this->info('Correct: All piggy bank amounts are positive.'); $this->friendlyPositive('All piggy bank amounts are positive.');
return; return;
} }
@@ -187,7 +211,7 @@ class CorrectAmounts extends Command
$item->targetamount = app('steam')->positive((string)$item->targetamount); $item->targetamount = app('steam')->positive((string)$item->targetamount);
$item->save(); $item->save();
} }
$this->line(sprintf('Corrected %d piggy bank amount(s).', $count)); $this->friendlyInfo(sprintf('Corrected %d piggy bank amount(s).', $count));
} }
/** /**
@@ -200,7 +224,7 @@ class CorrectAmounts extends Command
->get(); ->get();
$count = $set->count(); $count = $set->count();
if (0 === $count) { if (0 === $count) {
$this->info('Correct: All recurring transaction amounts are positive.'); $this->friendlyPositive('All recurring transaction amounts are positive.');
return; return;
} }
@@ -210,27 +234,7 @@ class CorrectAmounts extends Command
$item->foreign_amount = app('steam')->positive((string)$item->foreign_amount); $item->foreign_amount = app('steam')->positive((string)$item->foreign_amount);
$item->save(); $item->save();
} }
$this->line(sprintf('Corrected %d recurring transaction amount(s).', $count)); $this->friendlyInfo(sprintf('Corrected %d recurring transaction amount(s).', $count));
}
/**
* @return void
*/
private function fixRepetitions(): void
{
$set = PiggyBankRepetition::where('currentamount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->info('Correct: All piggy bank repetition amounts are positive.');
return;
}
/** @var PiggyBankRepetition $item */
foreach ($set as $item) {
$item->currentamount = app('steam')->positive((string)$item->currentamount);
$item->save();
}
$this->line(sprintf('Corrected %d piggy bank repetition amount(s).', $count));
} }
/** /**
@@ -250,11 +254,11 @@ class CorrectAmounts extends Command
} }
} }
if (0 === $fixed) { if (0 === $fixed) {
$this->info('Correct: All rule trigger amounts are positive.'); $this->friendlyPositive('All rule trigger amounts are positive.');
return; return;
} }
$this->line(sprintf('Corrected %d rule trigger amount(s).', $fixed)); $this->friendlyInfo(sprintf('Corrected %d rule trigger amount(s).', $fixed));
} }
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use Artisan; use Artisan;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Schema; use Schema;
@@ -34,6 +35,8 @@ use Schema;
*/ */
class CorrectDatabase extends Command class CorrectDatabase extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Will correct the integrity of your database, if necessary.'; protected $description = 'Will correct the integrity of your database, if necessary.';
protected $signature = 'firefly-iii:correct-database'; protected $signature = 'firefly-iii:correct-database';
@@ -44,7 +47,7 @@ class CorrectDatabase extends Command
{ {
// if table does not exist, return false // if table does not exist, return false
if (!Schema::hasTable('users')) { if (!Schema::hasTable('users')) {
$this->error('No "users"-table, will not continue.'); $this->friendlyError('No "users"-table, will not continue.');
return 1; return 1;
} }
@@ -76,7 +79,7 @@ class CorrectDatabase extends Command
'firefly-iii:trigger-credit-recalculation', 'firefly-iii:trigger-credit-recalculation',
]; ];
foreach ($commands as $command) { foreach ($commands as $command) {
$this->line(sprintf('Now executing command "%s"', $command)); $this->friendlyLine(sprintf('Now executing command "%s"', $command));
$this->call($command); $this->call($command);
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
@@ -39,6 +40,8 @@ use Illuminate\Support\Collection;
*/ */
class CorrectOpeningBalanceCurrencies extends Command class CorrectOpeningBalanceCurrencies extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Will make sure that opening balance transaction currencies match the account they\'re for.'; protected $description = 'Will make sure that opening balance transaction currencies match the account they\'re for.';
protected $signature = 'firefly-iii:fix-ob-currencies'; protected $signature = 'firefly-iii:fix-ob-currencies';
@@ -58,16 +61,27 @@ class CorrectOpeningBalanceCurrencies extends Command
if ($count > 0) { if ($count > 0) {
$message = sprintf('Corrected %d opening balance transaction(s).', $count); $message = sprintf('Corrected %d opening balance transaction(s).', $count);
$this->line($message); $this->friendlyInfo($message);
} }
if (0 === $count) { if (0 === $count) {
$message = 'Correct: There was nothing to fix in the opening balance transactions.'; $message = 'There was nothing to fix in the opening balance transactions.';
$this->info($message); $this->friendlyPositive($message);
} }
return 0; return 0;
} }
/**
* @return Collection
*/
private function getJournals(): Collection
{
/** @var Collection */
return TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->whereNull('transaction_journals.deleted_at')
->where('transaction_types.type', TransactionType::OPENING_BALANCE)->get(['transaction_journals.*']);
}
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* *
@@ -80,7 +94,7 @@ class CorrectOpeningBalanceCurrencies extends Command
if (null === $account) { if (null === $account) {
$message = sprintf('Transaction journal #%d has no valid account. Can\'t fix this line.', $journal->id); $message = sprintf('Transaction journal #%d has no valid account. Can\'t fix this line.', $journal->id);
app('log')->warning($message); app('log')->warning($message);
$this->warn($message); $this->friendlyError($message);
return 0; return 0;
} }
@@ -109,34 +123,10 @@ class CorrectOpeningBalanceCurrencies extends Command
return null; return null;
} }
/**
* @param Account $account
*
* @return TransactionCurrency
*/
private function getCurrency(Account $account): TransactionCurrency
{
/** @var AccountRepositoryInterface $repos */
$repos = app(AccountRepositoryInterface::class);
$repos->setUser($account->user);
return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user);
}
/**
* @return Collection
*/
private function getJournals(): Collection
{
/** @var Collection */
return TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->whereNull('transaction_journals.deleted_at')
->where('transaction_types.type', TransactionType::OPENING_BALANCE)->get(['transaction_journals.*']);
}
/** /**
* @param Account $account * @param Account $account
* @param TransactionJournal $journal * @param TransactionJournal $journal
*
* @return int * @return int
*/ */
private function setCorrectCurrency(Account $account, TransactionJournal $journal): int private function setCorrectCurrency(Account $account, TransactionJournal $journal): int
@@ -160,4 +150,18 @@ class CorrectOpeningBalanceCurrencies extends Command
return $count; return $count;
} }
/**
* @param Account $account
*
* @return TransactionCurrency
*/
private function getCurrency(Account $account): TransactionCurrency
{
/** @var AccountRepositoryInterface $repos */
$repos = app(AccountRepositoryInterface::class);
$repos->setUser($account->user);
return $repos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUser($account->user);
}
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use Exception; use Exception;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -33,6 +34,8 @@ use Illuminate\Console\Command;
*/ */
class CreateAccessTokens extends Command class CreateAccessTokens extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -66,12 +69,12 @@ class CreateAccessTokens extends Command
if (null === $pref) { if (null === $pref) {
$token = $user->generateAccessToken(); $token = $user->generateAccessToken();
app('preferences')->setForUser($user, 'access_token', $token); app('preferences')->setForUser($user, 'access_token', $token);
$this->line(sprintf('Generated access token for user %s', $user->email)); $this->friendlyInfo(sprintf('Generated access token for user %s', $user->email));
++$count; ++$count;
} }
} }
if (0 === $count) { if (0 === $count) {
$this->info('Correct: Verified access tokens.'); $this->friendlyPositive('Verified access tokens.');
} }
return 0; return 0;

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\LinkType; use FireflyIII\Models\LinkType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -31,6 +32,8 @@ use Illuminate\Console\Command;
*/ */
class CreateLinkTypes extends Command class CreateLinkTypes extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -67,13 +70,13 @@ class CreateLinkTypes extends Command
$link->inward = $values[1]; $link->inward = $values[1];
$link->outward = $values[0]; $link->outward = $values[0];
++$count; ++$count;
$this->line(sprintf('Created missing link type "%s"', $name)); $this->friendlyInfo(sprintf('Created missing link type "%s"', $name));
} }
$link->editable = false; $link->editable = false;
$link->save(); $link->save();
} }
if (0 === $count) { if (0 === $count) {
$this->info('Correct: all link types are OK'); $this->friendlyPositive('All link types are OK');
} }
return 0; return 0;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use Exception; use Exception;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
*/ */
class DeleteEmptyGroups extends Command class DeleteEmptyGroups extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Delete empty transaction groups.'; protected $description = 'Delete empty transaction groups.';
protected $signature = 'firefly-iii:delete-empty-groups'; protected $signature = 'firefly-iii:delete-empty-groups';
@@ -50,7 +53,7 @@ class DeleteEmptyGroups extends Command
$total = count($groupIds); $total = count($groupIds);
if ($total > 0) { if ($total > 0) {
$this->info(sprintf('Deleted %d empty transaction group(s).', $total)); $this->friendlyInfo(sprintf('Deleted %d empty transaction group(s).', $total));
// again, chunks for SQLite. // again, chunks for SQLite.
$chunks = array_chunk($groupIds, 500); $chunks = array_chunk($groupIds, 500);
@@ -59,7 +62,7 @@ class DeleteEmptyGroups extends Command
} }
} }
if (0 === $total) { if (0 === $total) {
$this->info('Correct: verified empty groups.'); $this->friendlyInfo('Verified there are no empty groups.');
} }
return 0; return 0;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use DB; use DB;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -35,6 +36,8 @@ use Illuminate\Support\Facades\Log;
*/ */
class DeleteEmptyJournals extends Command class DeleteEmptyJournals extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -61,30 +64,6 @@ class DeleteEmptyJournals extends Command
return 0; return 0;
} }
private function deleteEmptyJournals(): void
{
$count = 0;
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->groupBy('transaction_journals.id')
->whereNull('transactions.transaction_journal_id')
->get(['transaction_journals.id']);
foreach ($set as $entry) {
try {
TransactionJournal::find($entry->id)->delete();
} catch (QueryException $e) {
Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
}
$this->info(sprintf('Deleted empty transaction journal #%d', $entry->id));
++$count;
}
if (0 === $count) {
$this->info('Correct: no empty transaction journals.');
}
}
/** /**
* Delete transactions and their journals if they have an uneven number of transactions. * Delete transactions and their journals if they have an uneven number of transactions.
*/ */
@@ -107,12 +86,41 @@ class DeleteEmptyJournals extends Command
Transaction::where('transaction_journal_id', (int)$row->transaction_journal_id)->delete(); Transaction::where('transaction_journal_id', (int)$row->transaction_journal_id)->delete();
$this->info(sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id)); $this->friendlyWarning(
sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id)
);
$total++; $total++;
} }
} }
if (0 === $total) { if (0 === $total) {
$this->info('Correct: no uneven transaction journals.'); $this->friendlyPositive('No uneven transaction journals.');
}
}
/**
* @return void
*/
private function deleteEmptyJournals(): void
{
$count = 0;
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->groupBy('transaction_journals.id')
->whereNull('transactions.transaction_journal_id')
->get(['transaction_journals.id']);
foreach ($set as $entry) {
try {
TransactionJournal::find($entry->id)->delete();
} catch (QueryException $e) {
Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
}
$this->friendlyInfo(sprintf('Deleted empty transaction journal #%d', $entry->id));
++$count;
}
if (0 === $count) {
$this->friendlyPositive('No empty transaction journals.');
} }
} }
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use Exception; use Exception;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -34,6 +35,8 @@ use stdClass;
*/ */
class DeleteOrphanedTransactions extends Command class DeleteOrphanedTransactions extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -63,37 +66,8 @@ class DeleteOrphanedTransactions extends Command
} }
/** /**
* * @return void
*/ */
private function deleteFromOrphanedAccounts(): void
{
$set
= Transaction::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->whereNotNull('accounts.deleted_at')
->get(['transactions.*']);
$count = 0;
/** @var Transaction $transaction */
foreach ($set as $transaction) {
// delete journals
$journal = TransactionJournal::find((int)$transaction->transaction_journal_id);
if ($journal) {
$journal->delete();
}
Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete();
$this->line(
sprintf(
'Deleted transaction journal #%d because account #%d was already deleted.',
$transaction->transaction_journal_id,
$transaction->account_id
)
);
$count++;
}
if (0 === $count) {
$this->info('Correct: no orphaned accounts.');
}
}
private function deleteOrphanedJournals(): void private function deleteOrphanedJournals(): void
{ {
$set = TransactionJournal::leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id') $set = TransactionJournal::leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
@@ -102,15 +76,15 @@ class DeleteOrphanedTransactions extends Command
->get(['transaction_journals.id', 'transaction_journals.transaction_group_id']); ->get(['transaction_journals.id', 'transaction_journals.transaction_group_id']);
$count = $set->count(); $count = $set->count();
if (0 === $count) { if (0 === $count) {
$this->info('Correct: no orphaned journals.'); $this->friendlyPositive('No orphaned journals.');
return;
} }
if ($count > 0) { $this->friendlyInfo(sprintf('Found %d orphaned journal(s).', $count));
$this->info(sprintf('Found %d orphaned journal(s).', $count));
foreach ($set as $entry) { foreach ($set as $entry) {
$journal = TransactionJournal::withTrashed()->find((int)$entry->id); $journal = TransactionJournal::withTrashed()->find((int)$entry->id);
if (null !== $journal) { if (null !== $journal) {
$journal->delete(); $journal->delete();
$this->info( $this->friendlyWarning(
sprintf( sprintf(
'Journal #%d (part of deleted transaction group #%d) has been deleted as well.', 'Journal #%d (part of deleted transaction group #%d) has been deleted as well.',
$entry->id, $entry->id,
@@ -120,7 +94,6 @@ class DeleteOrphanedTransactions extends Command
} }
} }
} }
}
/** /**
* @throws Exception * @throws Exception
@@ -143,7 +116,7 @@ class DeleteOrphanedTransactions extends Command
$transaction = Transaction::find((int)$entry->transaction_id); $transaction = Transaction::find((int)$entry->transaction_id);
if (null !== $transaction) { if (null !== $transaction) {
$transaction->delete(); $transaction->delete();
$this->info( $this->friendlyWarning(
sprintf( sprintf(
'Transaction #%d (part of deleted transaction journal #%d) has been deleted as well.', 'Transaction #%d (part of deleted transaction journal #%d) has been deleted as well.',
$entry->transaction_id, $entry->transaction_id,
@@ -154,7 +127,39 @@ class DeleteOrphanedTransactions extends Command
} }
} }
if (0 === $count) { if (0 === $count) {
$this->info('Correct: no orphaned transactions.'); $this->friendlyPositive('No orphaned transactions.');
}
}
/**
*
*/
private function deleteFromOrphanedAccounts(): void
{
$set
= Transaction::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->whereNotNull('accounts.deleted_at')
->get(['transactions.*']);
$count = 0;
/** @var Transaction $transaction */
foreach ($set as $transaction) {
// delete journals
$journal = TransactionJournal::find((int)$transaction->transaction_journal_id);
if ($journal) {
$journal->delete();
}
Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete();
$this->friendlyWarning(
sprintf(
'Deleted transaction journal #%d because account #%d was already deleted.',
$transaction->transaction_journal_id,
$transaction->account_id
)
);
$count++;
}
if (0 === $count) {
$this->friendlyPositive('No orphaned accounts.');
} }
} }
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
*/ */
class DeleteZeroAmount extends Command class DeleteZeroAmount extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -57,13 +60,13 @@ class DeleteZeroAmount extends Command
$journals = TransactionJournal::whereIn('id', $set)->get(); $journals = TransactionJournal::whereIn('id', $set)->get();
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$this->info(sprintf('Deleted transaction journal #%d because the amount is zero (0.00).', $journal->id)); $this->friendlyWarning(sprintf('Deleted transaction journal #%d because the amount is zero (0.00).', $journal->id));
$journal->delete(); $journal->delete();
Transaction::where('transaction_journal_id', $journal->id)->delete(); Transaction::where('transaction_journal_id', $journal->id)->delete();
} }
if (0 === $journals->count()) { if (0 === $journals->count()) {
$this->info('Correct: no zero-amount transaction journals.'); $this->friendlyPositive('No zero-amount transaction journals.');
} }
return 0; return 0;

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
@@ -30,13 +31,14 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/** /**
* Class EnableCurrencies * Class EnableCurrencies
*/ */
class EnableCurrencies extends Command class EnableCurrencies extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Enables all currencies in use.'; protected $description = 'Enables all currencies in use.';
protected $signature = 'firefly-iii:enable-currencies'; protected $signature = 'firefly-iii:enable-currencies';
@@ -85,10 +87,10 @@ class EnableCurrencies extends Command
); );
$disabled = TransactionCurrency::whereIn('id', $found)->where('enabled', false)->count(); $disabled = TransactionCurrency::whereIn('id', $found)->where('enabled', false)->count();
if ($disabled > 0) { if ($disabled > 0) {
$this->info(sprintf('%d were (was) still disabled. This has been corrected.', $disabled)); $this->friendlyInfo(sprintf('%d currencies were (was) disabled while in use by transactions. This has been corrected.', $disabled));
} }
if (0 === $disabled) { if (0 === $disabled) {
$this->info('Correct: All currencies are correctly enabled or disabled.'); $this->friendlyPositive('All currencies are correctly enabled or disabled.');
} }
TransactionCurrency::whereIn('id', $found)->update(['enabled' => true]); TransactionCurrency::whereIn('id', $found)->update(['enabled' => true]);

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
*/ */
class FixAccountOrder extends Command class FixAccountOrder extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Make sure account order is correct.'; protected $description = 'Make sure account order is correct.';
protected $signature = 'firefly-iii:fix-account-order'; protected $signature = 'firefly-iii:fix-account-order';
@@ -52,7 +55,7 @@ class FixAccountOrder extends Command
$this->repository->resetAccountOrder(); $this->repository->resetAccountOrder();
} }
$this->info('Correct: All accounts are ordered correctly'); $this->friendlyPositive('All accounts are ordered correctly');
return 0; return 0;
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory; use FireflyIII\Factory\AccountFactory;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
@@ -30,14 +31,16 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use JsonException;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use JsonException;
/** /**
* Class FixAccountTypes * Class FixAccountTypes
*/ */
class FixAccountTypes extends Command class FixAccountTypes extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Make sure all journals have the correct from/to account types.'; protected $description = 'Make sure all journals have the correct from/to account types.';
protected $signature = 'firefly-iii:fix-account-types'; protected $signature = 'firefly-iii:fix-account-types';
private int $count; private int $count;
@@ -60,16 +63,90 @@ class FixAccountTypes extends Command
$this->inspectJournal($journal); $this->inspectJournal($journal);
} }
if (0 === $this->count) { if (0 === $this->count) {
$this->info('Correct: all account types are OK'); $this->friendlyPositive('All account types are OK');
} }
if (0 !== $this->count) { if (0 !== $this->count) {
Log::debug(sprintf('%d journals had to be fixed.', $this->count)); Log::debug(sprintf('%d journals had to be fixed.', $this->count));
$this->info(sprintf('Acted on %d transaction(s)!', $this->count)); $this->friendlyInfo(sprintf('Acted on %d transaction(s)', $this->count));
} }
return 0; return 0;
} }
/**
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
* be called from the handle method instead of using the constructor to initialize the command.
*
*/
private function stupidLaravel(): void
{
$this->count = 0;
}
/**
* @param TransactionJournal $journal
*
* @throws FireflyException
* @throws JsonException
*/
private function inspectJournal(TransactionJournal $journal): void
{
$transactions = $journal->transactions()->count();
if (2 !== $transactions) {
Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions));
$this->friendlyError(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions));
return;
}
$type = $journal->transactionType->type;
$sourceTransaction = $this->getSourceTransaction($journal);
$destTransaction = $this->getDestinationTransaction($journal);
$sourceAccount = $sourceTransaction->account;
$sourceAccountType = $sourceAccount->accountType->type;
$destAccount = $destTransaction->account;
$destAccountType = $destAccount->accountType->type;
if (!array_key_exists($type, $this->expected)) {
Log::info(sprintf('No source/destination info for transaction type %s.', $type));
$this->friendlyError(sprintf('No source/destination info for transaction type %s.', $type));
return;
}
if (!array_key_exists($sourceAccountType, $this->expected[$type])) {
Log::debug(sprintf('Going to fix journal #%d', $journal->id));
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
return;
}
$expectedTypes = $this->expected[$type][$sourceAccountType];
if (!in_array($destAccountType, $expectedTypes, true)) {
Log::debug(sprintf('Going to fix journal #%d', $journal->id));
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
}
}
/**
* @param TransactionJournal $journal
*
* @return Transaction
*/
private function getSourceTransaction(TransactionJournal $journal): Transaction
{
return $journal->transactions->firstWhere('amount', '<', 0);
}
/**
* @param TransactionJournal $journal
*
* @return Transaction
*/
private function getDestinationTransaction(TransactionJournal $journal): Transaction
{
return $journal->transactions->firstWhere('amount', '>', 0);
}
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param string $type * @param string $type
@@ -94,7 +171,7 @@ class FixAccountTypes extends Command
$journal->transactionType()->associate($withdrawal); $journal->transactionType()->associate($withdrawal);
$journal->save(); $journal->save();
$message = sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id); $message = sprintf('Converted transaction #%d from a transfer to a withdrawal.', $journal->id);
$this->info($message); $this->friendlyInfo($message);
Log::debug($message); Log::debug($message);
// check it again: // check it again:
$this->inspectJournal($journal); $this->inspectJournal($journal);
@@ -107,7 +184,7 @@ class FixAccountTypes extends Command
$journal->transactionType()->associate($deposit); $journal->transactionType()->associate($deposit);
$journal->save(); $journal->save();
$message = sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id); $message = sprintf('Converted transaction #%d from a transfer to a deposit.', $journal->id);
$this->info($message); $this->friendlyInfo($message);
Log::debug($message); Log::debug($message);
// check it again: // check it again:
$this->inspectJournal($journal); $this->inspectJournal($journal);
@@ -128,7 +205,7 @@ class FixAccountTypes extends Command
$result->id, $result->id,
$result->name $result->name
); );
$this->info($message); $this->friendlyWarning($message);
Log::debug($message); Log::debug($message);
$this->inspectJournal($journal); $this->inspectJournal($journal);
break; break;
@@ -148,93 +225,20 @@ class FixAccountTypes extends Command
$result->id, $result->id,
$result->name $result->name
); );
$this->info($message); $this->friendlyWarning($message);
Log::debug($message); Log::debug($message);
$this->inspectJournal($journal); $this->inspectJournal($journal);
break; break;
default: default:
$message = sprintf('The source account of %s #%d cannot be of type "%s".', $type, $journal->id, $source->account->accountType->type); $message = sprintf('The source account of %s #%d cannot be of type "%s".', $type, $journal->id, $source->account->accountType->type);
$this->info($message); $this->friendlyError($message);
Log::debug($message); Log::debug($message);
$message = sprintf('The destination account of %s #%d cannot be of type "%s".', $type, $journal->id, $dest->account->accountType->type); $message = sprintf('The destination account of %s #%d cannot be of type "%s".', $type, $journal->id, $dest->account->accountType->type);
$this->info($message); $this->friendlyError($message);
Log::debug($message); Log::debug($message);
break; break;
} }
} }
/**
* @param TransactionJournal $journal
*
* @return Transaction
*/
private function getDestinationTransaction(TransactionJournal $journal): Transaction
{
return $journal->transactions->firstWhere('amount', '>', 0);
}
/**
* @param TransactionJournal $journal
*
* @return Transaction
*/
private function getSourceTransaction(TransactionJournal $journal): Transaction
{
return $journal->transactions->firstWhere('amount', '<', 0);
}
/**
* @param TransactionJournal $journal
*
* @throws FireflyException
*/
private function inspectJournal(TransactionJournal $journal): void
{
$transactions = $journal->transactions()->count();
if (2 !== $transactions) {
Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions));
$this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions));
return;
}
$type = $journal->transactionType->type;
$sourceTransaction = $this->getSourceTransaction($journal);
$destTransaction = $this->getDestinationTransaction($journal);
$sourceAccount = $sourceTransaction->account;
$sourceAccountType = $sourceAccount->accountType->type;
$destAccount = $destTransaction->account;
$destAccountType = $destAccount->accountType->type;
if (!array_key_exists($type, $this->expected)) {
Log::info(sprintf('No source/destination info for transaction type %s.', $type));
$this->info(sprintf('No source/destination info for transaction type %s.', $type));
return;
}
if (!array_key_exists($sourceAccountType, $this->expected[$type])) {
Log::debug(sprintf('Going to fix journal #%d', $journal->id));
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
return;
}
$expectedTypes = $this->expected[$type][$sourceAccountType];
if (!in_array($destAccountType, $expectedTypes, true)) {
Log::debug(sprintf('Going to fix journal #%d', $journal->id));
$this->fixJournal($journal, $type, $sourceTransaction, $destTransaction);
}
}
/**
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
* be called from the handle method instead of using the constructor to initialize the command.
*
*/
private function stupidLaravel(): void
{
$this->count = 0;
}
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -36,6 +37,8 @@ use Illuminate\Console\Command;
*/ */
class FixFrontpageAccounts extends Command class FixFrontpageAccounts extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Fixes a preference that may include deleted accounts or accounts of another type.'; protected $description = 'Fixes a preference that may include deleted accounts or accounts of another type.';
protected $signature = 'firefly-iii:fix-frontpage-accounts'; protected $signature = 'firefly-iii:fix-frontpage-accounts';
@@ -54,7 +57,7 @@ class FixFrontpageAccounts extends Command
$this->fixPreference($preference); $this->fixPreference($preference);
} }
} }
$this->info('Correct: account preferences are OK'); $this->friendlyPositive('Account preferences are OK');
return 0; return 0;
} }

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use DB; use DB;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Handlers\Events\UpdatedGroupEventHandler; use FireflyIII\Handlers\Events\UpdatedGroupEventHandler;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
@@ -36,6 +37,8 @@ use Illuminate\Console\Command;
*/ */
class FixGroupAccounts extends Command class FixGroupAccounts extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Unify the source / destination accounts of split groups.'; protected $description = 'Unify the source / destination accounts of split groups.';
protected $signature = 'firefly-iii:unify-group-accounts'; protected $signature = 'firefly-iii:unify-group-accounts';
@@ -62,7 +65,7 @@ class FixGroupAccounts extends Command
$handler->unifyAccounts($event); $handler->unifyAccounts($event);
} }
$this->info('Correct: updated possible inconsistent transaction groups.'); $this->friendlyPositive('Updated possible inconsistent transaction groups.');
return 0; return 0;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -34,6 +35,8 @@ use Illuminate\Support\Collection;
*/ */
class FixIbans extends Command class FixIbans extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Removes spaces from IBANs'; protected $description = 'Removes spaces from IBANs';
protected $signature = 'firefly-iii:fix-ibans'; protected $signature = 'firefly-iii:fix-ibans';
private int $count = 0; private int $count = 0;
@@ -49,12 +52,34 @@ class FixIbans extends Command
$this->filterIbans($accounts); $this->filterIbans($accounts);
$this->countAndCorrectIbans($accounts); $this->countAndCorrectIbans($accounts);
if (0 === $this->count) { if (0 === $this->count) {
$this->info('Correct: All IBANs are valid.'); $this->friendlyPositive('All IBANs are valid.');
} }
return 0; return 0;
} }
/**
* @param Collection $accounts
*
* @return void
*/
private function filterIbans(Collection $accounts): void
{
/** @var Account $account */
foreach ($accounts as $account) {
$iban = $account->iban;
if (str_contains($iban, ' ')) {
$iban = app('steam')->filterSpaces((string)$account->iban);
if ('' !== $iban) {
$account->iban = $iban;
$account->save();
$this->friendlyInfo(sprintf('Removed spaces from IBAN of account #%d', $account->id));
$this->count++;
}
}
}
}
/** /**
* @param Collection $accounts * @param Collection $accounts
* *
@@ -82,7 +107,7 @@ class FixIbans extends Command
&& // allowed combination && // allowed combination
!(AccountType::REVENUE === $set[$userId][$iban] && AccountType::EXPENSE === $type) // also allowed combination. !(AccountType::REVENUE === $set[$userId][$iban] && AccountType::EXPENSE === $type) // also allowed combination.
) { ) {
$this->line( $this->friendlyWarning(
sprintf( sprintf(
'IBAN "%s" is used more than once and will be removed from %s #%d ("%s")', 'IBAN "%s" is used more than once and will be removed from %s #%d ("%s")',
$iban, $iban,
@@ -102,26 +127,4 @@ class FixIbans extends Command
} }
} }
} }
/**
* @param Collection $accounts
*
* @return void
*/
private function filterIbans(Collection $accounts): void
{
/** @var Account $account */
foreach ($accounts as $account) {
$iban = $account->iban;
if (str_contains($iban, ' ')) {
$iban = app('steam')->filterSpaces((string)$account->iban);
if ('' !== $iban) {
$account->iban = $iban;
$account->save();
$this->line(sprintf('Removed spaces from IBAN of account #%d', $account->id));
$this->count++;
}
}
}
}
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -33,6 +34,8 @@ use Illuminate\Console\Command;
*/ */
class FixLongDescriptions extends Command class FixLongDescriptions extends Command
{ {
use ShowsFriendlyMessages;
private const MAX_LENGTH = 1000; private const MAX_LENGTH = 1000;
protected $description = 'Fixes long descriptions in journals and groups.'; protected $description = 'Fixes long descriptions in journals and groups.';
protected $signature = 'firefly-iii:fix-long-descriptions'; protected $signature = 'firefly-iii:fix-long-descriptions';
@@ -51,7 +54,7 @@ class FixLongDescriptions extends Command
if (strlen($journal->description) > self::MAX_LENGTH) { if (strlen($journal->description) > self::MAX_LENGTH) {
$journal->description = substr($journal->description, 0, self::MAX_LENGTH); $journal->description = substr($journal->description, 0, self::MAX_LENGTH);
$journal->save(); $journal->save();
$this->line(sprintf('Truncated description of transaction journal #%d', $journal->id)); $this->friendlyWarning(sprintf('Truncated description of transaction journal #%d', $journal->id));
$count++; $count++;
} }
} }
@@ -62,12 +65,12 @@ class FixLongDescriptions extends Command
if (strlen((string)$group->title) > self::MAX_LENGTH) { if (strlen((string)$group->title) > self::MAX_LENGTH) {
$group->title = substr($group->title, 0, self::MAX_LENGTH); $group->title = substr($group->title, 0, self::MAX_LENGTH);
$group->save(); $group->save();
$this->line(sprintf('Truncated description of transaction group #%d', $group->id)); $this->friendlyWarning(sprintf('Truncated description of transaction group #%d', $group->id));
$count++; $count++;
} }
} }
if (0 === $count) { if (0 === $count) {
$this->info('Correct: all transaction group and journal title lengths are within bounds.'); $this->friendlyPositive('All transaction group and journal title lengths are within bounds.');
} }
return 0; return 0;

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -34,6 +35,8 @@ use Illuminate\Console\Command;
*/ */
class FixPiggies extends Command class FixPiggies extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Fixes common issues with piggy banks.'; protected $description = 'Fixes common issues with piggy banks.';
protected $signature = 'firefly-iii:fix-piggies'; protected $signature = 'firefly-iii:fix-piggies';
@@ -63,10 +66,10 @@ class FixPiggies extends Command
} }
} }
if (0 === $count) { if (0 === $count) {
$this->info('Correct: all piggy bank events are OK.'); $this->friendlyPositive('All piggy bank events are OK.');
} }
if (0 !== $count) { if (0 !== $count) {
$this->line(sprintf('Fixed %d piggy bank event(s).', $count)); $this->friendlyInfo(sprintf('Fixed %d piggy bank event(s).', $count));
} }
return 0; return 0;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Recurrence; use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
@@ -37,6 +38,8 @@ use Illuminate\Console\Command;
*/ */
class FixRecurringTransactions extends Command class FixRecurringTransactions extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Fixes recurring transactions with the wrong transaction type.'; protected $description = 'Fixes recurring transactions with the wrong transaction type.';
protected $signature = 'firefly-iii:fix-recurring-transactions'; protected $signature = 'firefly-iii:fix-recurring-transactions';
private int $count = 0; private int $count = 0;
@@ -53,12 +56,25 @@ class FixRecurringTransactions extends Command
$this->stupidLaravel(); $this->stupidLaravel();
$this->correctTransactions(); $this->correctTransactions();
if (0 === $this->count) { if (0 === $this->count) {
$this->info('Correct: all recurring transactions are OK.'); $this->friendlyPositive('All recurring transactions are OK.');
} }
return 0; return 0;
} }
/**
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
* be called from the handle method instead of using the constructor to initialize the command.
*
*/
private function stupidLaravel(): void
{
$this->recurringRepos = app(RecurringRepositoryInterface::class);
$this->userRepos = app(UserRepositoryInterface::class);
}
/** /**
* *
*/ */
@@ -71,6 +87,19 @@ class FixRecurringTransactions extends Command
} }
} }
/**
* @param User $user
*/
private function processUser(User $user): void
{
$this->recurringRepos->setUser($user);
$recurrences = $this->recurringRepos->get();
/** @var Recurrence $recurrence */
foreach ($recurrences as $recurrence) {
$this->processRecurrence($recurrence);
}
}
/** /**
* @param Recurrence $recurrence * @param Recurrence $recurrence
*/ */
@@ -93,7 +122,7 @@ class FixRecurringTransactions extends Command
$type = $recurrence->transactionType; $type = $recurrence->transactionType;
$link = config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type)); $link = config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type));
if (null !== $link && strtolower($type->type) !== strtolower($link)) { if (null !== $link && strtolower($type->type) !== strtolower($link)) {
$this->warn( $this->friendlyWarning(
sprintf('Recurring transaction #%d should be a "%s" but is a "%s" and will be corrected.', $recurrence->id, $link, $type->type) sprintf('Recurring transaction #%d should be a "%s" but is a "%s" and will be corrected.', $recurrence->id, $link, $type->type)
); );
$transactionType = TransactionType::whereType($link)->first(); $transactionType = TransactionType::whereType($link)->first();
@@ -104,30 +133,4 @@ class FixRecurringTransactions extends Command
} }
} }
} }
/**
* @param User $user
*/
private function processUser(User $user): void
{
$this->recurringRepos->setUser($user);
$recurrences = $this->recurringRepos->get();
/** @var Recurrence $recurrence */
foreach ($recurrences as $recurrence) {
$this->processRecurrence($recurrence);
}
}
/**
* Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is
* executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should
* be called from the handle method instead of using the constructor to initialize the command.
*
*/
private function stupidLaravel(): void
{
$this->recurringRepos = app(RecurringRepositoryInterface::class);
$this->userRepos = app(UserRepositoryInterface::class);
}
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
@@ -37,6 +38,8 @@ use Illuminate\Support\Collection;
*/ */
class FixTransactionTypes extends Command class FixTransactionTypes extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Make sure all transactions are of the correct type, based on source + dest.'; protected $description = 'Make sure all transactions are of the correct type, based on source + dest.';
protected $signature = 'firefly-iii:fix-transaction-types'; protected $signature = 'firefly-iii:fix-transaction-types';
@@ -57,28 +60,15 @@ class FixTransactionTypes extends Command
} }
} }
if ($count > 0) { if ($count > 0) {
$this->info('Corrected transaction type of %d transaction journals.', $count); $this->friendlyInfo('Corrected transaction type of %d transaction journals.', $count);
return 0; return 0;
} }
$this->info('Correct: all transaction journals are of the correct transaction type'); $this->friendlyPositive('All transaction journals are of the correct transaction type');
return 0; return 0;
} }
/**
* @param TransactionJournal $journal
* @param string $expectedType
*/
private function changeJournal(TransactionJournal $journal, string $expectedType): void
{
$type = TransactionType::whereType($expectedType)->first();
if (null !== $type) {
$journal->transaction_type_id = $type->id;
$journal->save();
}
}
/** /**
* Collect all transaction journals. * Collect all transaction journals.
* *
@@ -102,13 +92,13 @@ class FixTransactionTypes extends Command
$source = $this->getSourceAccount($journal); $source = $this->getSourceAccount($journal);
$destination = $this->getDestinationAccount($journal); $destination = $this->getDestinationAccount($journal);
} catch (FireflyException $e) { } catch (FireflyException $e) {
$this->error($e->getMessage()); $this->friendlyError($e->getMessage());
return false; return false;
} }
$expectedType = (string)config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type)); $expectedType = (string)config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type));
if ($expectedType !== $type) { if ($expectedType !== $type) {
$this->line( $this->friendlyWarning(
sprintf( sprintf(
'Transaction journal #%d was of type "%s" but is corrected to "%s" (%s -> %s)', 'Transaction journal #%d was of type "%s" but is corrected to "%s" (%s -> %s)',
$journal->id, $journal->id,
@@ -126,6 +116,36 @@ class FixTransactionTypes extends Command
return false; return false;
} }
/**
* @param TransactionJournal $journal
*
* @return Account
* @throws FireflyException
*/
private function getSourceAccount(TransactionJournal $journal): Account
{
$collection = $journal->transactions->filter(
static function (Transaction $transaction) {
return $transaction->amount < 0;
}
);
if (0 === $collection->count()) {
throw new FireflyException(sprintf('300001: Journal #%d has no source transaction.', $journal->id));
}
if (1 !== $collection->count()) {
throw new FireflyException(sprintf('300002: Journal #%d has multiple source transactions.', $journal->id));
}
/** @var Transaction $transaction */
$transaction = $collection->first();
/** @var Account|null $account */
$account = $transaction->account;
if (null === $account) {
throw new FireflyException(sprintf('300003: Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id));
}
return $account;
}
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* *
@@ -158,31 +178,14 @@ class FixTransactionTypes extends Command
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* * @param string $expectedType
* @return Account
* @throws FireflyException
*/ */
private function getSourceAccount(TransactionJournal $journal): Account private function changeJournal(TransactionJournal $journal, string $expectedType): void
{ {
$collection = $journal->transactions->filter( $type = TransactionType::whereType($expectedType)->first();
static function (Transaction $transaction) { if (null !== $type) {
return $transaction->amount < 0; $journal->transaction_type_id = $type->id;
} $journal->save();
); }
if (0 === $collection->count()) {
throw new FireflyException(sprintf('300001: Journal #%d has no source transaction.', $journal->id));
}
if (1 !== $collection->count()) {
throw new FireflyException(sprintf('300002: Journal #%d has multiple source transactions.', $journal->id));
}
/** @var Transaction $transaction */
$transaction = $collection->first();
/** @var Account|null $account */
$account = $transaction->account;
if (null === $account) {
throw new FireflyException(sprintf('300003: Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id));
}
return $account;
} }
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use DB; use DB;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -34,6 +35,8 @@ use stdClass;
*/ */
class FixUnevenAmount extends Command class FixUnevenAmount extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Fix journals with uneven amounts.'; protected $description = 'Fix journals with uneven amounts.';
protected $signature = 'firefly-iii:fix-uneven-amount'; protected $signature = 'firefly-iii:fix-uneven-amount';
@@ -51,16 +54,32 @@ class FixUnevenAmount extends Command
->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]); ->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]);
/** @var stdClass $entry */ /** @var stdClass $entry */
foreach ($journals as $entry) { foreach ($journals as $entry) {
$sum = (string)$entry->the_sum;
if (!is_numeric($sum) || '' === $sum || str_contains($sum, 'e') || str_contains($sum, ',')) {
$message = sprintf(
'Journal #%d has an invalid sum ("%s"). No sure what to do.',
$entry->transaction_journal_id,
$entry->the_sum
);
$this->friendlyWarning($message);
app('log')->warning($message);
$count++;
continue;
}
if (0 !== bccomp((string)$entry->the_sum, '0')) { if (0 !== bccomp((string)$entry->the_sum, '0')) {
$message = sprintf('Sum of journal #%d is %s instead of zero.', $entry->transaction_journal_id, $entry->the_sum); $message = sprintf(
$this->warn($message); 'Sum of journal #%d is %s instead of zero.',
$entry->transaction_journal_id,
$entry->the_sum
);
$this->friendlyWarning($message);
app('log')->warning($message); app('log')->warning($message);
$this->fixJournal((int)$entry->transaction_journal_id); $this->fixJournal((int)$entry->transaction_journal_id);
$count++; $count++;
} }
} }
if (0 === $count) { if (0 === $count) {
$this->info('Correct: Database amount integrity is OK'); $this->friendlyPositive('Database amount integrity is OK');
} }
return 0; return 0;
@@ -80,7 +99,7 @@ class FixUnevenAmount extends Command
$source = $journal->transactions()->where('amount', '<', 0)->first(); $source = $journal->transactions()->where('amount', '<', 0)->first();
if (null === $source) { if (null === $source) {
$this->error( $this->friendlyError(
sprintf( sprintf(
'Journal #%d ("%s") has no source transaction. It will be deleted to maintain database consistency.', 'Journal #%d ("%s") has no source transaction. It will be deleted to maintain database consistency.',
$journal->id ?? 0, $journal->id ?? 0,
@@ -100,7 +119,7 @@ class FixUnevenAmount extends Command
$destination = $journal->transactions()->where('amount', '>', 0)->first(); $destination = $journal->transactions()->where('amount', '>', 0)->first();
if (null === $destination) { if (null === $destination) {
$this->error( $this->friendlyError(
sprintf( sprintf(
'Journal #%d ("%s") has no destination transaction. It will be deleted to maintain database consistency.', 'Journal #%d ("%s") has no destination transaction. It will be deleted to maintain database consistency.',
$journal->id ?? 0, $journal->id ?? 0,
@@ -118,6 +137,6 @@ class FixUnevenAmount extends Command
$destination->save(); $destination->save();
$message = sprintf('Corrected amount in transaction journal #%d', $param); $message = sprintf('Corrected amount in transaction journal #%d', $param);
$this->line($message); $this->friendlyInfo($message);
} }
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
*/ */
class RemoveBills extends Command class RemoveBills extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Remove bills from transactions that shouldn\'t have one.'; protected $description = 'Remove bills from transactions that shouldn\'t have one.';
protected $signature = 'firefly-iii:remove-bills'; protected $signature = 'firefly-iii:remove-bills';
@@ -50,14 +53,14 @@ class RemoveBills extends Command
$journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get(); $journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get();
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$this->line(sprintf('Transaction journal #%d should not be linked to bill #%d.', $journal->id, $journal->bill_id)); $this->friendlyWarning(sprintf('Transaction journal #%d will be unlinked from bill #%d.', $journal->id, $journal->bill_id));
$journal->bill_id = null; $journal->bill_id = null;
$journal->save(); $journal->save();
} }
if ($journals->count() > 0) { if ($journals->count() > 0) {
$this->info('Fixed all transaction journals so they have correct bill information.'); $this->friendlyInfo('Fixed all transaction journals so they have correct bill information.');
} }
$this->info('Correct: verified bills / journals in %s seconds'); $this->friendlyPositive('All bills and journals are OK');
return 0; return 0;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use DB; use DB;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** /**
@@ -31,6 +32,8 @@ use Illuminate\Console\Command;
*/ */
class RenameMetaFields extends Command class RenameMetaFields extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Rename changed meta fields.'; protected $description = 'Rename changed meta fields.';
protected $signature = 'firefly-iii:rename-meta-fields'; protected $signature = 'firefly-iii:rename-meta-fields';
@@ -63,10 +66,10 @@ class RenameMetaFields extends Command
$this->rename($original, $update); $this->rename($original, $update);
} }
if (0 === $this->count) { if (0 === $this->count) {
$this->info('Correct: all meta fields are correct.'); $this->friendlyPositive('All meta fields are correct.');
} }
if (0 !== $this->count) { if (0 !== $this->count) {
$this->info(sprintf('Renamed %d meta field(s).', $this->count)); $this->friendlyInfo(sprintf('Renamed %d meta field(s).', $this->count));
} }
return 0; return 0;
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -33,6 +34,8 @@ use Illuminate\Support\Facades\Log;
*/ */
class TransferBudgets extends Command class TransferBudgets extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Removes budgets from transfers.'; protected $description = 'Removes budgets from transfers.';
protected $signature = 'firefly-iii:fix-transfer-budgets'; protected $signature = 'firefly-iii:fix-transfer-budgets';
@@ -52,19 +55,19 @@ class TransferBudgets extends Command
/** @var TransactionJournal $entry */ /** @var TransactionJournal $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
$message = sprintf('Transaction journal #%d is a %s, so has no longer a budget.', $entry->id, $entry->transactionType->type); $message = sprintf('Transaction journal #%d is a %s, so has no longer a budget.', $entry->id, $entry->transactionType->type);
$this->info($message); $this->friendlyInfo($message);
Log::debug($message); Log::debug($message);
$entry->budgets()->sync([]); $entry->budgets()->sync([]);
$count++; $count++;
} }
if (0 === $count) { if (0 === $count) {
$message = 'Correct: no invalid budget/journal entries.'; $message = 'No invalid budget/journal entries.';
$this->info($message); $this->friendlyPositive($message);
} }
if (0 !== $count) { if (0 !== $count) {
$message = sprintf('Corrected %d invalid budget/journal entries (entry).', $count); $message = sprintf('Corrected %d invalid budget/journal entries (entry).', $count);
Log::debug($message); Log::debug($message);
$this->line($message); $this->friendlyInfo($message);
} }
return 0; return 0;
} }

View File

@@ -1,5 +1,26 @@
<?php <?php
/*
* TriggerCreditCalculation.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/>.
*/
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
@@ -7,7 +28,6 @@ namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\Services\Internal\Support\CreditRecalculateService;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
/** /**
* Class CorrectionSkeleton * Class CorrectionSkeleton
@@ -30,6 +50,19 @@ class TriggerCreditCalculation extends Command
return 0; return 0;
} }
/**
* @return void
*/
private function processAccounts(): void
{
$accounts = Account::leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
->whereIn('account_types.type', config('firefly.valid_liabilities'))
->get(['accounts.*']);
foreach ($accounts as $account) {
$this->processAccount($account);
}
}
/** /**
* @param Account $account * @param Account $account
* *
@@ -42,14 +75,4 @@ class TriggerCreditCalculation extends Command
$object->setAccount($account); $object->setAccount($account);
$object->recalculate(); $object->recalculate();
} }
private function processAccounts(): void
{
$accounts = Account::leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
->whereIn('account_types.type', config('firefly.valid_liabilities'))
->get(['accounts.*']);
foreach ($accounts as $account) {
$this->processAccount($account);
}
}
} }

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Console\Commands\Export;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Console\Commands\VerifiesAccessToken; use FireflyIII\Console\Commands\VerifiesAccessToken;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
@@ -35,14 +36,17 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\Export\ExportDataGenerator; use FireflyIII\Support\Export\ExportDataGenerator;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use InvalidArgumentException;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use InvalidArgumentException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/** /**
* Class ExportData * Class ExportData
*/ */
class ExportData extends Command class ExportData extends Command
{ {
use ShowsFriendlyMessages;
use VerifiesAccessToken; use VerifiesAccessToken;
/** /**
@@ -81,12 +85,14 @@ class ExportData extends Command
* *
* @return int * @return int
* @throws FireflyException * @throws FireflyException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/ */
public function handle(): int public function handle(): int
{ {
// verify access token // verify access token
if (!$this->verifyAccessToken()) { if (!$this->verifyAccessToken()) {
$this->error('Invalid access token. Check /profile.'); $this->friendlyError('Invalid access token. Check /profile.');
return 1; return 1;
} }
@@ -99,7 +105,7 @@ class ExportData extends Command
try { try {
$options = $this->parseOptions(); $options = $this->parseOptions();
} catch (FireflyException $e) { } catch (FireflyException $e) {
$this->error(sprintf('Could not work with your options: %s', $e)); $this->friendlyError(sprintf('Could not work with your options: %s', $e));
return 1; return 1;
} }
@@ -122,14 +128,14 @@ class ExportData extends Command
$exporter->setExportPiggies($options['export']['piggies']); $exporter->setExportPiggies($options['export']['piggies']);
$data = $exporter->export(); $data = $exporter->export();
if (0 === count($data)) { if (0 === count($data)) {
$this->error('You must export *something*. Use --export-transactions or another option. See docs.firefly-iii.org'); $this->friendlyError('You must export *something*. Use --export-transactions or another option. See docs.firefly-iii.org');
} }
$returnCode = 0; $returnCode = 0;
if (0 !== count($data)) { if (0 !== count($data)) {
try { try {
$this->exportData($options, $data); $this->exportData($options, $data);
} catch (FireflyException $e) { } catch (FireflyException $e) {
$this->error(sprintf('Could not store data: %s', $e->getMessage())); $this->friendlyError(sprintf('Could not store data: %s', $e->getMessage()));
$returnCode = 1; $returnCode = 1;
} }
@@ -198,7 +204,7 @@ class ExportData extends Command
$date = Carbon::createFromFormat('!Y-m-d', $this->option($field)); $date = Carbon::createFromFormat('!Y-m-d', $this->option($field));
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
$this->error(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start'))); $this->friendlyError(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start')));
$error = true; $error = true;
} }
} }
@@ -288,11 +294,11 @@ class ExportData extends Command
throw new FireflyException(sprintf('File "%s" exists already. Use --force to overwrite.', $file)); throw new FireflyException(sprintf('File "%s" exists already. Use --force to overwrite.', $file));
} }
if (true === $options['force'] && file_exists($file)) { if (true === $options['force'] && file_exists($file)) {
$this->warn(sprintf('File "%s" exists already but will be replaced.', $file)); $this->friendlyWarning(sprintf('File "%s" exists already but will be replaced.', $file));
} }
// continue to write to file. // continue to write to file.
file_put_contents($file, $content); file_put_contents($file, $content);
$this->info(sprintf('Wrote %s-export to file "%s".', $key, $file)); $this->friendlyPositive(sprintf('Wrote %s-export to file "%s".', $key, $file));
} }
} }
} }

View File

@@ -24,23 +24,52 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\GroupMembership; use FireflyIII\Models\GroupMembership;
use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserGroup;
use FireflyIII\Models\UserRole; use FireflyIII\Models\UserRole;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
/** /**
* Class CreateGroupMemberships * Class CreateGroupMemberships
*/ */
class CreateGroupMemberships extends Command class CreateGroupMemberships extends Command
{ {
use ShowsFriendlyMessages;
public const CONFIG_NAME = '560_create_group_memberships'; public const CONFIG_NAME = '560_create_group_memberships';
protected $description = 'Update group memberships'; protected $description = 'Update group memberships';
protected $signature = 'firefly-iii:create-group-memberships'; protected $signature = 'firefly-iii:create-group-memberships';
/**
* Execute the console command.
*
* @return int
* @throws FireflyException
*/
public function handle(): int
{
$this->createGroupMemberships();
$this->friendlyPositive('Validated group memberships');
return 0;
}
/**
*
* @throws FireflyException
*/
private function createGroupMemberships(): void
{
$users = User::get();
/** @var User $user */
foreach ($users as $user) {
self::createGroupMembership($user);
}
}
/** /**
* TODO move to helper. * TODO move to helper.
* *
@@ -78,31 +107,4 @@ class CreateGroupMemberships extends Command
$user->save(); $user->save();
} }
} }
/**
* Execute the console command.
*
* @return int
* @throws FireflyException
*/
public function handle(): int
{
$this->createGroupMemberships();
$this->info('Correct: validated group memberships');
return 0;
}
/**
*
* @throws FireflyException
*/
private function createGroupMemberships(): void
{
$users = User::get();
/** @var User $user */
foreach ($users as $user) {
self::createGroupMembership($user);
}
}
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
@@ -35,6 +36,8 @@ use stdClass;
*/ */
class ReportEmptyObjects extends Command class ReportEmptyObjects extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -64,51 +67,6 @@ class ReportEmptyObjects extends Command
return 0; return 0;
} }
/**
* Reports on accounts with no transactions.
*/
private function reportAccounts(): void
{
$set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('users', 'accounts.user_id', '=', 'users.id')
->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'])
->whereNull('transactions.account_id')
->get(
['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']
);
/** @var stdClass $entry */
foreach ($set as $entry) {
$line = 'User #%d (%s) has account #%d ("%s") which has no transactions.';
$line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name);
$this->line($line);
}
}
/**
* Reports on budgets with no budget limits (which makes them pointless).
*/
private function reportBudgetLimits(): void
{
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email'])
->whereNull('budget_limits.id')
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']);
/** @var Budget $entry */
foreach ($set as $entry) {
$line = sprintf(
'User #%d (%s) has budget #%d ("%s") which has no budget limits.',
$entry->user_id,
$entry->email,
$entry->id,
$entry->name
);
$this->line($line);
}
}
/** /**
* Report on budgets with no transactions or journals. * Report on budgets with no transactions or journals.
*/ */
@@ -130,7 +88,7 @@ class ReportEmptyObjects extends Command
$entry->id, $entry->id,
$entry->name $entry->name
); );
$this->line($line); $this->friendlyWarning($line);
} }
} }
@@ -155,7 +113,7 @@ class ReportEmptyObjects extends Command
$entry->id, $entry->id,
$entry->name $entry->name
); );
$this->line($line); $this->friendlyWarning($line);
} }
} }
@@ -180,7 +138,52 @@ class ReportEmptyObjects extends Command
$entry->id, $entry->id,
$entry->tag $entry->tag
); );
$this->line($line); $this->friendlyWarning($line);
}
}
/**
* Reports on accounts with no transactions.
*/
private function reportAccounts(): void
{
$set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('users', 'accounts.user_id', '=', 'users.id')
->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'])
->whereNull('transactions.account_id')
->get(
['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']
);
/** @var stdClass $entry */
foreach ($set as $entry) {
$line = 'User #%d (%s) has account #%d ("%s") which has no transactions.';
$line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name);
$this->friendlyWarning($line);
}
}
/**
* Reports on budgets with no budget limits (which makes them pointless).
*/
private function reportBudgetLimits(): void
{
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email'])
->whereNull('budget_limits.id')
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']);
/** @var Budget $entry */
foreach ($set as $entry) {
$line = sprintf(
'User #%d (%s) has budget #%d ("%s") which has no budget limits.',
$entry->user_id,
$entry->email,
$entry->id,
$entry->name
);
$this->friendlyWarning($line);
} }
} }
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Integrity;
use Artisan; use Artisan;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Schema; use Schema;
@@ -34,6 +35,8 @@ use Schema;
*/ */
class ReportIntegrity extends Command class ReportIntegrity extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -60,11 +63,10 @@ class ReportIntegrity extends Command
'firefly-iii:create-group-memberships', 'firefly-iii:create-group-memberships',
'firefly-iii:report-empty-objects', 'firefly-iii:report-empty-objects',
'firefly-iii:report-sum', 'firefly-iii:report-sum',
'firefly-iii:restore-oauth-keys',
'firefly-iii:upgrade-group-information', 'firefly-iii:upgrade-group-information',
]; ];
foreach ($commands as $command) { foreach ($commands as $command) {
$this->line(sprintf('Now executing %s', $command)); $this->friendlyLine(sprintf('Now executing %s', $command));
$this->call($command); $this->call($command);
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
*/ */
class ReportSum extends Command class ReportSum extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Report on the total sum of transactions. Must be 0.'; protected $description = 'Report on the total sum of transactions. Must be 0.';
protected $signature = 'firefly-iii:report-sum'; protected $signature = 'firefly-iii:report-sum';
@@ -58,12 +61,17 @@ class ReportSum extends Command
/** @var User $user */ /** @var User $user */
foreach ($userRepository->all() as $user) { foreach ($userRepository->all() as $user) {
$sum = (string)$user->transactions()->sum('amount'); $sum = (string)$user->transactions()->sum('amount');
if (!is_numeric($sum)) {
$message = sprintf('Error: Transactions for user #%d (%s) have an invalid sum ("%s").', $user->id, $user->email, $sum);
$this->friendlyError($message);
continue;
}
if (0 !== bccomp($sum, '0')) { if (0 !== bccomp($sum, '0')) {
$message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum); $message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum);
$this->error($message); $this->friendlyError($message);
} }
if (0 === bccomp($sum, '0')) { if (0 === bccomp($sum, '0')) {
$this->info(sprintf('Correct: Amount integrity OK for user #%d', $user->id)); $this->friendlyPositive(sprintf('Amount integrity OK for user #%d', $user->id));
} }
} }
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Support\System\OAuthKeys; use FireflyIII\Support\System\OAuthKeys;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -32,6 +33,8 @@ use Illuminate\Console\Command;
*/ */
class RestoreOAuthKeys extends Command class RestoreOAuthKeys extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Will restore the OAuth keys generated for the system.'; protected $description = 'Will restore the OAuth keys generated for the system.';
protected $signature = 'firefly-iii:restore-oauth-keys'; protected $signature = 'firefly-iii:restore-oauth-keys';
@@ -50,9 +53,35 @@ class RestoreOAuthKeys extends Command
/** /**
* *
*/ */
private function generateKeys(): void private function restoreOAuthKeys(): void
{ {
OAuthKeys::generateKeys(); if (!$this->keysInDatabase() && !$this->keysOnDrive()) {
$this->generateKeys();
$this->storeKeysInDB();
$this->friendlyInfo('Generated and stored new keys.');
return;
}
if ($this->keysInDatabase() && !$this->keysOnDrive()) {
$result = $this->restoreKeysFromDB();
if (true === $result) {
$this->friendlyInfo('Restored OAuth keys from database.');
return;
}
$this->generateKeys();
$this->storeKeysInDB();
$this->friendlyInfo('Generated and stored new keys.');
return;
}
if (!$this->keysInDatabase() && $this->keysOnDrive()) {
$this->storeKeysInDB();
$this->friendlyInfo('Stored OAuth keys in database.');
return;
}
$this->friendlyPositive('OAuth keys are OK');
} }
/** /**
@@ -74,43 +103,9 @@ class RestoreOAuthKeys extends Command
/** /**
* *
*/ */
private function restoreKeysFromDB(): bool private function generateKeys(): void
{ {
return OAuthKeys::restoreKeysFromDB(); OAuthKeys::generateKeys();
}
/**
*
*/
private function restoreOAuthKeys(): void
{
if (!$this->keysInDatabase() && !$this->keysOnDrive()) {
$this->generateKeys();
$this->storeKeysInDB();
$this->line('Correct: generated and stored new keys.');
return;
}
if ($this->keysInDatabase() && !$this->keysOnDrive()) {
$result = $this->restoreKeysFromDB();
if (true === $result) {
$this->line('Correct: restored OAuth keys from database.');
return;
}
$this->generateKeys();
$this->storeKeysInDB();
$this->line('Correct: generated and stored new keys.');
return;
}
if (!$this->keysInDatabase() && $this->keysOnDrive()) {
$this->storeKeysInDB();
$this->line('Correct: stored OAuth keys in database.');
return;
}
$this->line('Correct: OAuth keys are OK');
} }
/** /**
@@ -120,4 +115,12 @@ class RestoreOAuthKeys extends Command
{ {
OAuthKeys::storeKeysInDB(); OAuthKeys::storeKeysInDB();
} }
/**
*
*/
private function restoreKeysFromDB(): bool
{
return OAuthKeys::restoreKeysFromDB();
}
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Attachment; use FireflyIII\Models\Attachment;
use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\AvailableBudget;
@@ -48,6 +49,8 @@ use Illuminate\Database\QueryException;
*/ */
class UpdateGroupInformation extends Command class UpdateGroupInformation extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'Makes sure that every object is linked to a group'; protected $description = 'Makes sure that every object is linked to a group';
protected $signature = 'firefly-iii:upgrade-group-information'; protected $signature = 'firefly-iii:upgrade-group-information';
@@ -70,11 +73,16 @@ class UpdateGroupInformation extends Command
return 0; return 0;
} }
/**
* @param User $user
*
* @return void
*/
private function updateGroupInfo(User $user): void private function updateGroupInfo(User $user): void
{ {
$group = $user->userGroup; $group = $user->userGroup;
if (null === $group) { if (null === $group) {
$this->warn(sprintf('User "%s" has no group.', $user->email)); $this->friendlyWarning(sprintf('User "%s" has no group.', $user->email));
return; return;
} }
@@ -111,12 +119,12 @@ class UpdateGroupInformation extends Command
try { try {
$result = $className::where('user_id', $user->id)->where('user_group_id', null)->update(['user_group_id' => $group->id]); $result = $className::where('user_id', $user->id)->where('user_group_id', null)->update(['user_group_id' => $group->id]);
} catch (QueryException $e) { } catch (QueryException $e) {
$this->error(sprintf('Could not update group information for "%s" because of error "%s"', $className, $e->getMessage())); $this->friendlyError(sprintf('Could not update group information for "%s" because of error "%s"', $className, $e->getMessage()));
return; return;
} }
if (0 !== $result) { if (0 !== $result) {
$this->info(sprintf('Correct: Moved %d %s objects to the correct group.', $result, str_replace('FireflyIII\\Models\\', '', $className))); $this->friendlyPositive(sprintf('Moved %d %s objects to the correct group.', $result, str_replace('FireflyIII\\Models\\', '', $className)));
} }
} }
} }

View File

@@ -0,0 +1,93 @@
<?php
/*
* ShowsFriendlyMessages.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/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
/**
* Trait ShowsFriendlyMessages
*/
trait ShowsFriendlyMessages
{
/**
* @param string $message
*
* @return void
*/
public function friendlyError(string $message): void
{
$this->error(sprintf(' [x] %s', trim($message)));
}
/**
* @param string $message
*
* @return void
*/
public function friendlyInfo(string $message): void
{
$this->friendlyNeutral($message);
}
/**
* @param string $message
*
* @return void
*/
public function friendlyNeutral(string $message): void
{
$this->line(sprintf(' [i] %s', trim($message)));
}
/**
* @param string $message
*
* @return void
*/
public function friendlyLine(string $message): void
{
$this->line(sprintf(' %s', trim($message)));
}
/**
* @param string $message
*
* @return void
*/
public function friendlyPositive(string $message): void
{
$this->info(sprintf(' [✓] %s', trim($message)));
}
/**
* @param string $message
*
* @return void
*/
public function friendlyWarning(string $message): void
{
$this->warn(sprintf(' [!] %s', trim($message)));
}
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System; namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use PDO; use PDO;
use PDOException; use PDOException;
@@ -33,6 +34,8 @@ use PDOException;
*/ */
class CreateDatabase extends Command class CreateDatabase extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -54,7 +57,7 @@ class CreateDatabase extends Command
public function handle(): int public function handle(): int
{ {
if ('mysql' !== env('DB_CONNECTION', 'mysql')) { if ('mysql' !== env('DB_CONNECTION', 'mysql')) {
$this->info(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION'))); $this->friendlyInfo(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));
return 0; return 0;
} }
@@ -67,7 +70,7 @@ class CreateDatabase extends Command
if ('' !== env('DB_SOCKET', '')) { if ('' !== env('DB_SOCKET', '')) {
$dsn = sprintf('mysql:unix_socket=%s;charset=utf8mb4', env('DB_SOCKET', '')); $dsn = sprintf('mysql:unix_socket=%s;charset=utf8mb4', env('DB_SOCKET', ''));
} }
$this->info(sprintf('DSN is %s', $dsn)); $this->friendlyLine(sprintf('DSN is %s', $dsn));
$options = [ $options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
@@ -79,7 +82,7 @@ class CreateDatabase extends Command
try { try {
$pdo = new PDO($dsn, env('DB_USERNAME'), env('DB_PASSWORD'), $options); $pdo = new PDO($dsn, env('DB_USERNAME'), env('DB_PASSWORD'), $options);
} catch (PDOException $e) { } catch (PDOException $e) {
$this->error(sprintf('Error when connecting to DB: %s', $e->getMessage())); $this->friendlyError(sprintf('Error when connecting to DB: %s', $e->getMessage()));
} }
// only continue when no error. // only continue when no error.
@@ -96,14 +99,14 @@ class CreateDatabase extends Command
} }
} }
if (false === $exists && true === $checked) { if (false === $exists && true === $checked) {
$this->error(sprintf('Database "%s" does not exist.', env('DB_DATABASE'))); $this->friendlyError(sprintf('Database "%s" does not exist.', env('DB_DATABASE')));
// try to create it. // try to create it.
$pdo->exec(sprintf('CREATE DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;', env('DB_DATABASE'))); $pdo->exec(sprintf('CREATE DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;', env('DB_DATABASE')));
$this->info(sprintf('Created database "%s"', env('DB_DATABASE'))); $this->friendlyInfo(sprintf('Created database "%s"', env('DB_DATABASE')));
} }
if (true === $exists && true === $checked) { if (true === $exists && true === $checked) {
$this->info(sprintf('Database "%s" exists.', env('DB_DATABASE'))); $this->friendlyInfo(sprintf('Database "%s" exists.', env('DB_DATABASE')));
} }
return 0; return 0;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System; namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
@@ -36,6 +37,8 @@ use Str;
*/ */
class CreateFirstUser extends Command class CreateFirstUser extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -58,14 +61,14 @@ class CreateFirstUser extends Command
public function handle(): int public function handle(): int
{ {
if ('testing' !== env('APP_ENV', 'local')) { if ('testing' !== env('APP_ENV', 'local')) {
$this->error('This command only works in the testing environment.'); $this->friendlyError('This command only works in the testing environment.');
return 1; return 1;
} }
$this->stupidLaravel(); $this->stupidLaravel();
$count = $this->repository->count(); $count = $this->repository->count();
if ($count > 0) { if ($count > 0) {
$this->error('Already have more than zero users in DB.'); $this->friendlyError('Already have more than zero users in DB.');
return 1; return 1;
} }
@@ -81,8 +84,8 @@ class CreateFirstUser extends Command
$user->save(); $user->save();
$user->setRememberToken(Str::random(60)); $user->setRememberToken(Str::random(60));
$this->info(sprintf('Created new admin user (ID #%d) with email address "%s" and password "%s".', $user->id, $user->email, $password)); $this->friendlyInfo(sprintf('Created new admin user (ID #%d) with email address "%s" and password "%s".', $user->id, $user->email, $password));
$this->error('Change this password.'); $this->friendlyWarning('Change this password.');
return 0; return 0;
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System; namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AutoBudget; use FireflyIII\Models\AutoBudget;
@@ -51,6 +52,8 @@ use Illuminate\Support\Facades\Log;
*/ */
class ForceDecimalSize extends Command class ForceDecimalSize extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'This command resizes DECIMAL columns in MySQL or PostgreSQL and correct amounts (only MySQL).'; protected $description = 'This command resizes DECIMAL columns in MySQL or PostgreSQL and correct amounts (only MySQL).';
protected $signature = 'firefly-iii:force-decimal-size'; protected $signature = 'firefly-iii:force-decimal-size';
private string $cast; private string $cast;
@@ -89,15 +92,14 @@ class ForceDecimalSize extends Command
/** /**
* Execute the console command. * Execute the console command.
* *
* @throws FireflyException
*/ */
public function handle(): int public function handle(): int
{ {
Log::debug('Now in ForceDecimalSize::handle()'); Log::debug('Now in ForceDecimalSize::handle()');
$this->determineDatabaseType(); $this->determineDatabaseType();
$this->error('Running this command is dangerous and can cause data loss.'); $this->friendlyError('Running this command is dangerous and can cause data loss.');
$this->error('Please do not continue.'); $this->friendlyError('Please do not continue.');
$question = $this->confirm('Do you want to continue?'); $question = $this->confirm('Do you want to continue?');
if (true === $question) { if (true === $question) {
$this->correctAmounts(); $this->correctAmounts();
@@ -107,6 +109,116 @@ class ForceDecimalSize extends Command
return 0; return 0;
} }
/**
* @return void
*/
private function determineDatabaseType(): void
{
// switch stuff based on database connection:
$this->operator = 'REGEXP';
$this->regularExpression = '\'\\\\.[\\\\d]{%d}[1-9]+\'';
$this->cast = 'CHAR';
if ('pgsql' === config('database.default')) {
$this->operator = 'SIMILAR TO';
$this->regularExpression = '\'%%\.[\d]{%d}[1-9]+%%\'';
$this->cast = 'TEXT';
}
if ('sqlite' === config('database.default')) {
$this->regularExpression = '"\\.[\d]{%d}[1-9]+"';
}
}
/**
* This method checks if a basic check can be done or if it needs to be complicated.
*
* @return void
*/
private function correctAmounts(): void
{
// if sqlite, add function?
if ('sqlite' === (string)config('database.default')) {
DB::connection()->getPdo()->sqliteCreateFunction('REGEXP', function ($pattern, $value) {
mb_regex_encoding('UTF-8');
$pattern = trim($pattern, '"');
return (false !== mb_ereg($pattern, (string)$value)) ? 1 : 0;
});
}
if (!in_array((string)config('database.default'), ['mysql', 'pgsql', 'sqlite'], true)) {
$this->friendlyWarning(sprintf('Skip correcting amounts, does not support "%s"...', (string)config('database.default')));
return;
}
$this->correctAmountsByCurrency();
}
/**
* This method loops all enabled currencies and then calls the method that will fix all objects in this currency.
*
* @return void
* @throws FireflyException
*/
private function correctAmountsByCurrency(): void
{
/** @var Collection $enabled */
$enabled = TransactionCurrency::whereEnabled(1)->get();
/** @var TransactionCurrency $currency */
foreach ($enabled as $currency) {
$this->correctByCurrency($currency);
}
}
/**
* This method loops the available tables that may need fixing, and calls for the right method that can fix them.
*
* @param TransactionCurrency $currency
*
* @return void
* @throws FireflyException
*/
private function correctByCurrency(TransactionCurrency $currency): void
{
/**
* @var string $name
* @var array $fields
*/
foreach ($this->tables as $name => $fields) {
switch ($name) {
default:
$message = sprintf('Cannot handle table "%s"', $name);
$this->friendlyError($message);
throw new FireflyException($message);
case 'accounts':
$this->correctAccountAmounts($currency, $fields);
break;
case 'auto_budgets':
case 'available_budgets':
case 'bills':
case 'budget_limits':
case 'recurrences_transactions':
$this->correctGeneric($currency, $name);
break;
case 'currency_exchange_rates':
case 'limit_repetitions':
// do nothing
break;
case 'piggy_bank_events':
$this->correctPiggyEventAmounts($currency, $fields);
break;
case 'piggy_bank_repetitions':
$this->correctPiggyRepetitionAmounts($currency, $fields);
break;
case 'piggy_banks':
$this->correctPiggyAmounts($currency, $fields);
break;
case 'transactions':
$this->correctTransactionAmounts($currency);
break;
}
}
}
/** /**
* This method loops over all accounts and validates the amounts. * This method loops over all accounts and validates the amounts.
* *
@@ -136,7 +248,7 @@ class ForceDecimalSize extends Command
}); });
$result = $query->get(['accounts.*']); $result = $query->get(['accounts.*']);
if (0 === $result->count()) { if (0 === $result->count()) {
$this->line(sprintf('Correct: All accounts in %s', $currency->code)); $this->friendlyPositive(sprintf('All accounts in %s are OK', $currency->code));
return; return;
} }
@@ -150,104 +262,12 @@ class ForceDecimalSize extends Command
// fix $field by rounding it down correctly. // fix $field by rounding it down correctly.
$pow = pow(10, (int)$currency->decimal_places); $pow = pow(10, (int)$currency->decimal_places);
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
$this->line(sprintf('Account #%d has %s with value "%s", this has been corrected to "%s".', $account->id, $field, $value, $correct)); $this->friendlyInfo(sprintf('Account #%d has %s with value "%s", this has been corrected to "%s".', $account->id, $field, $value, $correct));
Account::find($account->id)->update([$field => $correct]); Account::find($account->id)->update([$field => $correct]);
} }
} }
} }
/**
* This method checks if a basic check can be done or if it needs to be complicated.
*
* @return void
*/
private function correctAmounts(): void
{
// if sqlite, add function?
if ('sqlite' === (string)config('database.default')) {
DB::connection()->getPdo()->sqliteCreateFunction('REGEXP', function ($pattern, $value) {
mb_regex_encoding('UTF-8');
$pattern = trim($pattern, '"');
return (false !== mb_ereg($pattern, (string)$value)) ? 1 : 0;
});
}
if (!in_array((string)config('database.default'), ['mysql', 'pgsql', 'sqlite'], true)) {
$this->line(sprintf('Skip correcting amounts, does not support "%s"...', (string)config('database.default')));
return;
}
$this->correctAmountsByCurrency();
}
/**
* This method loops all enabled currencies and then calls the method that will fix all objects in this currency.
*
* @return void
*/
private function correctAmountsByCurrency(): void
{
$this->line('Going to correct amounts.');
/** @var Collection $enabled */
$enabled = TransactionCurrency::whereEnabled(1)->get();
/** @var TransactionCurrency $currency */
foreach ($enabled as $currency) {
$this->correctByCurrency($currency);
}
}
/**
* This method loops the available tables that may need fixing, and calls for the right method that can fix them.
*
* @param TransactionCurrency $currency
*
* @return void
* @throws FireflyException
*/
private function correctByCurrency(TransactionCurrency $currency): void
{
$this->line(sprintf('Going to correct amounts in currency %s ("%s").', $currency->code, $currency->name));
/**
* @var string $name
* @var array $fields
*/
foreach ($this->tables as $name => $fields) {
switch ($name) {
default:
$message = sprintf('Cannot handle table "%s"', $name);
$this->line($message);
throw new FireflyException($message);
case 'accounts':
$this->correctAccountAmounts($currency, $fields);
break;
case 'auto_budgets':
case 'available_budgets':
case 'bills':
case 'budget_limits':
case 'recurrences_transactions':
$this->correctGeneric($currency, $name);
break;
case 'currency_exchange_rates':
case 'limit_repetitions':
// do nothing
break;
case 'piggy_bank_events':
$this->correctPiggyEventAmounts($currency, $fields);
break;
case 'piggy_bank_repetitions':
$this->correctPiggyRepetitionAmounts($currency, $fields);
break;
case 'piggy_banks':
$this->correctPiggyAmounts($currency, $fields);
break;
case 'transactions':
$this->correctTransactionAmounts($currency);
break;
}
}
}
/** /**
* This method fixes all auto budgets in currency $currency. * This method fixes all auto budgets in currency $currency.
* *
@@ -279,7 +299,7 @@ class ForceDecimalSize extends Command
$result = $query->get(['*']); $result = $query->get(['*']);
if (0 === $result->count()) { if (0 === $result->count()) {
$this->line(sprintf('Correct: All %s in %s', $table, $currency->code)); $this->friendlyPositive(sprintf('All %s in %s are OK', $table, $currency->code));
return; return;
} }
@@ -293,63 +313,12 @@ class ForceDecimalSize extends Command
// fix $field by rounding it down correctly. // fix $field by rounding it down correctly.
$pow = pow(10, (int)$currency->decimal_places); $pow = pow(10, (int)$currency->decimal_places);
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
$this->line(sprintf('%s #%d has %s with value "%s", this has been corrected to "%s".', $table, $item->id, $field, $value, $correct)); $this->friendlyWarning(sprintf('%s #%d has %s with value "%s", this has been corrected to "%s".', $table, $item->id, $field, $value, $correct));
$class::find($item->id)->update([$field => $correct]); $class::find($item->id)->update([$field => $correct]);
} }
} }
} }
/**
* This method fixes all piggy banks in currency $currency.
*
* @param TransactionCurrency $currency
* @param array $fields
*
* @return void
*/
private function correctPiggyAmounts(TransactionCurrency $currency, array $fields): void
{
$operator = $this->operator;
$cast = $this->cast;
$regularExpression = $this->regularExpression;
/** @var Builder $query */
$query = PiggyBank::leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id')
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
->where('account_meta.name', 'currency_id')
->where('account_meta.data', json_encode((string)$currency->id))
->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) {
foreach ($fields as $field) {
$q->orWhere(
DB::raw(sprintf('CAST(piggy_banks.%s AS %s)', $field, $cast)),
$operator,
DB::raw(sprintf($regularExpression, $currency->decimal_places))
);
}
});
$result = $query->get(['piggy_banks.*']);
if (0 === $result->count()) {
$this->line(sprintf('Correct: All piggy banks in %s', $currency->code));
return;
}
/** @var PiggyBank $item */
foreach ($result as $item) {
foreach ($fields as $field) {
$value = $item->$field;
if (null === $value) {
continue;
}
// fix $field by rounding it down correctly.
$pow = pow(10, (int)$currency->decimal_places);
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
$this->line(sprintf('Piggy bank #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
PiggyBank::find($item->id)->update([$field => $correct]);
}
}
}
/** /**
* This method fixes all piggy bank events in currency $currency. * This method fixes all piggy bank events in currency $currency.
* *
@@ -382,7 +351,7 @@ class ForceDecimalSize extends Command
$result = $query->get(['piggy_bank_events.*']); $result = $query->get(['piggy_bank_events.*']);
if (0 === $result->count()) { if (0 === $result->count()) {
$this->line(sprintf('Correct: All piggy bank events in %s', $currency->code)); $this->friendlyPositive(sprintf('All piggy bank events in %s are OK', $currency->code));
return; return;
} }
@@ -396,7 +365,9 @@ class ForceDecimalSize extends Command
// fix $field by rounding it down correctly. // fix $field by rounding it down correctly.
$pow = pow(10, (int)$currency->decimal_places); $pow = pow(10, (int)$currency->decimal_places);
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
$this->line(sprintf('Piggy bank event #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct)); $this->friendlyWarning(
sprintf('Piggy bank event #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct)
);
PiggyBankEvent::find($item->id)->update([$field => $correct]); PiggyBankEvent::find($item->id)->update([$field => $correct]);
} }
} }
@@ -434,7 +405,7 @@ class ForceDecimalSize extends Command
$result = $query->get(['piggy_bank_repetitions.*']); $result = $query->get(['piggy_bank_repetitions.*']);
if (0 === $result->count()) { if (0 === $result->count()) {
$this->line(sprintf('Correct: All piggy bank repetitions in %s', $currency->code)); $this->friendlyPositive(sprintf('All piggy bank repetitions in %s', $currency->code));
return; return;
} }
@@ -448,12 +419,65 @@ class ForceDecimalSize extends Command
// fix $field by rounding it down correctly. // fix $field by rounding it down correctly.
$pow = pow(10, (int)$currency->decimal_places); $pow = pow(10, (int)$currency->decimal_places);
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
$this->line(sprintf('Piggy bank repetition #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct)); $this->friendlyWarning(
sprintf('Piggy bank repetition #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct)
);
PiggyBankRepetition::find($item->id)->update([$field => $correct]); PiggyBankRepetition::find($item->id)->update([$field => $correct]);
} }
} }
} }
/**
* This method fixes all piggy banks in currency $currency.
*
* @param TransactionCurrency $currency
* @param array $fields
*
* @return void
*/
private function correctPiggyAmounts(TransactionCurrency $currency, array $fields): void
{
$operator = $this->operator;
$cast = $this->cast;
$regularExpression = $this->regularExpression;
/** @var Builder $query */
$query = PiggyBank::leftJoin('accounts', 'piggy_banks.account_id', '=', 'accounts.id')
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
->where('account_meta.name', 'currency_id')
->where('account_meta.data', json_encode((string)$currency->id))
->where(static function (Builder $q) use ($fields, $currency, $operator, $cast, $regularExpression) {
foreach ($fields as $field) {
$q->orWhere(
DB::raw(sprintf('CAST(piggy_banks.%s AS %s)', $field, $cast)),
$operator,
DB::raw(sprintf($regularExpression, $currency->decimal_places))
);
}
});
$result = $query->get(['piggy_banks.*']);
if (0 === $result->count()) {
$this->friendlyPositive(sprintf('All piggy banks in %s are OK', $currency->code));
return;
}
/** @var PiggyBank $item */
foreach ($result as $item) {
foreach ($fields as $field) {
$value = $item->$field;
if (null === $value) {
continue;
}
// fix $field by rounding it down correctly.
$pow = pow(10, (int)$currency->decimal_places);
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
$this->friendlyWarning(sprintf('Piggy bank #%d has %s with value "%s", this has been corrected to "%s".', $item->id, $field, $value, $correct));
PiggyBank::find($item->id)->update([$field => $correct]);
}
}
}
/** /**
* This method fixes all transactions in currency $currency. * This method fixes all transactions in currency $currency.
* *
@@ -473,7 +497,7 @@ class ForceDecimalSize extends Command
$result = $query->get(['transactions.*']); $result = $query->get(['transactions.*']);
if (0 === $result->count()) { if (0 === $result->count()) {
$this->line(sprintf('Correct: All transactions in %s', $currency->code)); $this->friendlyPositive(sprintf('All transactions in %s are OK', $currency->code));
} }
/** @var Transaction $item */ /** @var Transaction $item */
@@ -485,7 +509,7 @@ class ForceDecimalSize extends Command
// fix $field by rounding it down correctly. // fix $field by rounding it down correctly.
$pow = pow(10, (int)$currency->decimal_places); $pow = pow(10, (int)$currency->decimal_places);
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
$this->line(sprintf('Transaction #%d has amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct)); $this->friendlyWarning(sprintf('Transaction #%d has amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct));
Transaction::find($item->id)->update(['amount' => $correct]); Transaction::find($item->id)->update(['amount' => $correct]);
} }
@@ -499,7 +523,7 @@ class ForceDecimalSize extends Command
$result = $query->get(['*']); $result = $query->get(['*']);
if (0 === $result->count()) { if (0 === $result->count()) {
$this->line(sprintf('Correct: All transactions in foreign currency %s', $currency->code)); $this->friendlyPositive(sprintf('All transactions in foreign currency %s are OK', $currency->code));
return; return;
} }
@@ -512,33 +536,19 @@ class ForceDecimalSize extends Command
// fix $field by rounding it down correctly. // fix $field by rounding it down correctly.
$pow = pow(10, (int)$currency->decimal_places); $pow = pow(10, (int)$currency->decimal_places);
$correct = bcdiv((string)round($value * $pow), (string)$pow, 12); $correct = bcdiv((string)round($value * $pow), (string)$pow, 12);
$this->line(sprintf('Transaction #%d has foreign amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct)); $this->friendlyWarning(
sprintf('Transaction #%d has foreign amount with value "%s", this has been corrected to "%s".', $item->id, $value, $correct)
);
Transaction::find($item->id)->update(['foreign_amount' => $correct]); Transaction::find($item->id)->update(['foreign_amount' => $correct]);
} }
} }
private function determineDatabaseType(): void
{
// switch stuff based on database connection:
$this->operator = 'REGEXP';
$this->regularExpression = '\'\\\\.[\\\\d]{%d}[1-9]+\'';
$this->cast = 'CHAR';
if ('pgsql' === config('database.default')) {
$this->operator = 'SIMILAR TO';
$this->regularExpression = '\'%%\.[\d]{%d}[1-9]+%%\'';
$this->cast = 'TEXT';
}
if ('sqlite' === config('database.default')) {
$this->regularExpression = '"\\.[\d]{%d}[1-9]+"';
}
}
/** /**
* @return void * @return void
*/ */
private function updateDecimals(): void private function updateDecimals(): void
{ {
$this->info('Going to force the size of DECIMAL columns. Please hold.'); $this->friendlyInfo('Going to force the size of DECIMAL columns. Please hold.');
$type = (string)config('database.default'); $type = (string)config('database.default');
/** /**
@@ -548,11 +558,11 @@ class ForceDecimalSize extends Command
foreach ($this->tables as $name => $fields) { foreach ($this->tables as $name => $fields) {
/** @var string $field */ /** @var string $field */
foreach ($fields as $field) { foreach ($fields as $field) {
$this->line(sprintf('Updating table "%s", field "%s"...', $name, $field)); $this->friendlyLine(sprintf('Updating table "%s", field "%s"...', $name, $field));
switch ($type) { switch ($type) {
default: default:
$this->error(sprintf('Cannot handle database type "%s".', $type)); $this->friendlyError(sprintf('Cannot handle database type "%s".', $type));
return; return;
case 'pgsql': case 'pgsql':

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System; namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Console\Commands\VerifiesAccessToken; use FireflyIII\Console\Commands\VerifiesAccessToken;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -31,8 +32,12 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
/**
* Class ForceMigration
*/
class ForceMigration extends Command class ForceMigration extends Command
{ {
use ShowsFriendlyMessages;
use VerifiesAccessToken; use VerifiesAccessToken;
/** /**
@@ -58,13 +63,13 @@ class ForceMigration extends Command
public function handle(): int public function handle(): int
{ {
if (!$this->verifyAccessToken()) { if (!$this->verifyAccessToken()) {
$this->error('Invalid access token.'); $this->friendlyError('Invalid access token.');
return 1; return 1;
} }
$this->error('Running this command is dangerous and can cause data loss.'); $this->friendlyError('Running this command is dangerous and can cause data loss.');
$this->error('Please do not continue.'); $this->friendlyError('Please do not continue.');
$question = $this->confirm('Do you want to continue?'); $question = $this->confirm('Do you want to continue?');
if (true === $question) { if (true === $question) {
$user = $this->getUser(); $user = $this->getUser();
@@ -77,19 +82,22 @@ class ForceMigration extends Command
return 0; return 0;
} }
/**
* @return void
*/
private function forceMigration(): void private function forceMigration(): void
{ {
DB::commit(); DB::commit();
$this->line('Dropping "migrations" table...'); $this->friendlyLine('Dropping "migrations" table...');
sleep(2); sleep(2);
Schema::dropIfExists('migrations'); Schema::dropIfExists('migrations');
$this->line('Re-run all migrations...'); $this->friendlyLine('Re-run all migrations...');
Artisan::call('migrate', ['--seed' => true]); Artisan::call('migrate', ['--seed' => true]);
sleep(2); sleep(2);
$this->line(''); $this->friendlyLine('');
$this->line('There is a good chance you just saw a lot of error messages.'); $this->friendlyWarning('There is a good chance you just saw a lot of error messages.');
$this->line('No need to panic yet. First try to access Firefly III (again).'); $this->friendlyWarning('No need to panic yet. First try to access Firefly III (again).');
$this->line('The issue, whatever it was, may have been solved now.'); $this->friendlyWarning('The issue, whatever it was, may have been solved now.');
$this->line(''); $this->friendlyLine('');
} }
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System; namespace FireflyIII\Console\Commands\System;
use Crypt; use Crypt;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Attachment; use FireflyIII\Models\Attachment;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\DecryptException;
@@ -37,6 +38,8 @@ use Storage;
*/ */
class ScanAttachments extends Command class ScanAttachments extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -79,7 +82,7 @@ class ScanAttachments extends Command
$attachment->md5 = $md5; $attachment->md5 = $md5;
$attachment->mime = $mime; $attachment->mime = $mime;
$attachment->save(); $attachment->save();
$this->line(sprintf('Fixed attachment #%d', $attachment->id)); $this->friendlyInfo(sprintf('Fixed attachment #%d', $attachment->id));
} }
return 0; return 0;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System; namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** /**
@@ -31,6 +32,8 @@ use Illuminate\Console\Command;
*/ */
class SetLatestVersion extends Command class SetLatestVersion extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -52,13 +55,13 @@ class SetLatestVersion extends Command
public function handle(): int public function handle(): int
{ {
if (!$this->option('james-is-cool')) { if (!$this->option('james-is-cool')) {
$this->error('Am too!'); $this->friendlyError('Am too!');
return 0; return 0;
} }
app('fireflyconfig')->set('db_version', config('firefly.db_version')); app('fireflyconfig')->set('db_version', config('firefly.db_version'));
app('fireflyconfig')->set('ff3_version', config('firefly.version')); app('fireflyconfig')->set('ff3_version', config('firefly.version'));
$this->line('Updated version.'); $this->friendlyInfo('Updated version.');
return 0; return 0;
} }

View File

@@ -66,6 +66,93 @@ class UpgradeFireflyInstructions extends Command
return 0; return 0;
} }
/**
* Render upgrade instructions.
*/
private function updateInstructions(): void
{
/** @var string $version */
$version = config('firefly.version');
$config = config('upgrade.text.upgrade');
$text = '';
foreach (array_keys($config) as $compare) {
// if string starts with:
if (\str_starts_with($version, $compare)) {
$text = $config[$compare];
}
}
$this->newLine();
$this->showLogo();
$this->newLine();
$this->showLine();
$this->boxed('');
if (null === $text || '' === $text) {
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version));
$this->boxedInfo('There are no extra upgrade instructions.');
$this->boxed('Firefly III should be ready for use.');
$this->boxed('');
$this->showLine();
return;
}
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s!', $version));
$this->boxedInfo($text);
$this->boxed('');
$this->showLine();
}
/**
* The logo takes up 8 lines of code. So 8 colors can be used.
*
* @return void
*/
private function showLogo(): void
{
$today = date('m-d');
$month = date('m');
// variation in colors and effects just because I can!
// default is Ukraine flag:
$colors = ['blue', 'blue', 'blue', 'yellow', 'yellow', 'yellow', 'default', 'default'];
// 5th of May is Dutch liberation day and 29th of April is Dutch King's Day and September 17 is my birthday.
if ('05-01' === $today || '04-29' === $today || '09-17' === $today) {
$colors = ['red', 'red', 'red', 'white', 'white', 'blue', 'blue', 'blue'];
}
// National Coming Out Day, International Day Against Homophobia, Biphobia and Transphobia and Pride Month
if ('10-11' === $today || '05-17' === $today || '06' === $month) {
$colors = ['red', 'bright-red', 'yellow', 'green', 'blue', 'magenta', 'default', 'default'];
}
// International Transgender Day of Visibility
if ('03-31' === $today) {
$colors = ['bright-blue', 'bright-red', 'white', 'white', 'bright-red', 'bright-blue', 'default', 'default'];
}
$this->line(sprintf('<fg=%s> ______ _ __ _ _____ _____ _____ </>', $colors[0]));
$this->line(sprintf('<fg=%s> | ____(_) / _| | |_ _|_ _|_ _| </>', $colors[1]));
$this->line(sprintf('<fg=%s> | |__ _ _ __ ___| |_| |_ _ | | | | | | </>', $colors[2]));
$this->line(sprintf('<fg=%s> | __| | | \'__/ _ \ _| | | | | | | | | | | </>', $colors[3]));
$this->line(sprintf('<fg=%s> | | | | | | __/ | | | |_| | _| |_ _| |_ _| |_ </>', $colors[4]));
$this->line(sprintf('<fg=%s> |_| |_|_| \___|_| |_|\__, | |_____|_____|_____| </>', $colors[5]));
$this->line(sprintf('<fg=%s> __/ | </>', $colors[6]));
$this->line(sprintf('<fg=%s> |___/ </>', $colors[7]));
}
/**
* Show a line.
*/
private function showLine(): void
{
$line = '+';
$line .= str_repeat('-', 78);
$line .= '+';
$this->line($line);
}
/** /**
* Show a nice box. * Show a nice box.
* *
@@ -127,90 +214,4 @@ class UpgradeFireflyInstructions extends Command
$this->boxed(''); $this->boxed('');
$this->showLine(); $this->showLine();
} }
/**
* Show a line.
*/
private function showLine(): void
{
$line = '+';
$line .= str_repeat('-', 78);
$line .= '+';
$this->line($line);
}
/**
* The logo takes up 8 lines of code. So 8 colors can be used.
* @return void
*/
private function showLogo(): void
{
$today = date('m-d');
$month = date('m');
// variation in colors and effects just because I can!
// default is Ukraine flag:
$colors = ['blue', 'blue', 'blue', 'yellow', 'yellow', 'yellow', 'default', 'default'];
// 5th of May is Dutch liberation day and 29th of April is Dutch King's Day and September 17 is my birthday.
if ('05-01' === $today || '04-29' === $today || '09-17' === $today) {
$colors = ['red', 'red', 'red', 'white', 'white', 'blue', 'blue', 'blue'];
}
// National Coming Out Day, International Day Against Homophobia, Biphobia and Transphobia and Pride Month
if ('10-11' === $today || '05-17' === $today || '06' === $month) {
$colors = ['red', 'bright-red', 'yellow', 'green', 'blue', 'magenta', 'default', 'default'];
}
// International Transgender Day of Visibility
if ('03-31' === $today) {
$colors = ['bright-blue', 'bright-red', 'white', 'white', 'bright-red', 'bright-blue', 'default', 'default'];
}
$this->line(sprintf('<fg=%s> ______ _ __ _ _____ _____ _____ </>', $colors[0]));
$this->line(sprintf('<fg=%s> | ____(_) / _| | |_ _|_ _|_ _| </>', $colors[1]));
$this->line(sprintf('<fg=%s> | |__ _ _ __ ___| |_| |_ _ | | | | | | </>', $colors[2]));
$this->line(sprintf('<fg=%s> | __| | | \'__/ _ \ _| | | | | | | | | | | </>', $colors[3]));
$this->line(sprintf('<fg=%s> | | | | | | __/ | | | |_| | _| |_ _| |_ _| |_ </>', $colors[4]));
$this->line(sprintf('<fg=%s> |_| |_|_| \___|_| |_|\__, | |_____|_____|_____| </>', $colors[5]));
$this->line(sprintf('<fg=%s> __/ | </>', $colors[6]));
$this->line(sprintf('<fg=%s> |___/ </>', $colors[7]));
}
/**
* Render upgrade instructions.
*/
private function updateInstructions(): void
{
/** @var string $version */
$version = config('firefly.version');
$config = config('upgrade.text.upgrade');
$text = '';
foreach (array_keys($config) as $compare) {
// if string starts with:
if (\str_starts_with($version, $compare)) {
$text = $config[$compare];
}
}
$this->newLine();
$this->showLogo();
$this->newLine();
$this->showLine();
$this->boxed('');
if (null === $text || '' === $text) {
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version));
$this->boxedInfo('There are no extra upgrade instructions.');
$this->boxed('Firefly III should be ready for use.');
$this->boxed('');
$this->showLine();
return;
}
$this->boxed(sprintf('Thank you for updating to Firefly III, v%s!', $version));
$this->boxedInfo($text);
$this->boxed('');
$this->showLine();
}
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\System; namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -35,6 +36,8 @@ use Storage;
*/ */
class VerifySecurityAlerts extends Command class VerifySecurityAlerts extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -80,23 +83,23 @@ class VerifySecurityAlerts extends Command
// depends on level // depends on level
if ('info' === $array['level']) { if ('info' === $array['level']) {
Log::debug('INFO level alert'); Log::debug('INFO level alert');
$this->info($array['message']); $this->friendlyInfo($array['message']);
return 0; return 0;
} }
if ('warning' === $array['level']) { if ('warning' === $array['level']) {
Log::debug('WARNING level alert'); Log::debug('WARNING level alert');
$this->warn('------------------------ :o'); $this->friendlyWarning('------------------------ :o');
$this->warn($array['message']); $this->friendlyWarning($array['message']);
$this->warn('------------------------ :o'); $this->friendlyWarning('------------------------ :o');
return 0; return 0;
} }
if ('danger' === $array['level']) { if ('danger' === $array['level']) {
Log::debug('DANGER level alert'); Log::debug('DANGER level alert');
$this->error('------------------------ :-('); $this->friendlyError('------------------------ :-(');
$this->error($array['message']); $this->friendlyError($array['message']);
$this->error('------------------------ :-('); $this->friendlyError('------------------------ :-(');
return 0; return 0;
} }
@@ -104,8 +107,8 @@ class VerifySecurityAlerts extends Command
return 0; return 0;
} }
} }
Log::debug('This version is not mentioned.'); Log::debug(sprintf('No security alerts for version %s', $version));
$this->friendlyPositive(sprintf('No security alerts for version %s', $version));
return 0; return 0;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Tools; namespace FireflyIII\Console\Commands\Tools;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Console\Commands\VerifiesAccessToken; use FireflyIII\Console\Commands\VerifiesAccessToken;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
@@ -43,6 +44,7 @@ use Illuminate\Support\Facades\Log;
*/ */
class ApplyRules extends Command class ApplyRules extends Command
{ {
use ShowsFriendlyMessages;
use VerifiesAccessToken; use VerifiesAccessToken;
/** /**
@@ -88,7 +90,7 @@ class ApplyRules extends Command
$start = microtime(true); $start = microtime(true);
$this->stupidLaravel(); $this->stupidLaravel();
if (!$this->verifyAccessToken()) { if (!$this->verifyAccessToken()) {
$this->error('Invalid access token.'); $this->friendlyError('Invalid access token.');
return 1; return 1;
} }
@@ -111,11 +113,11 @@ class ApplyRules extends Command
$rulesToApply = $this->getRulesToApply(); $rulesToApply = $this->getRulesToApply();
$count = $rulesToApply->count(); $count = $rulesToApply->count();
if (0 === $count) { if (0 === $count) {
$this->error('No rules or rule groups have been included.'); $this->friendlyError('No rules or rule groups have been included.');
$this->warn('Make a selection using:'); $this->friendlyWarning('Make a selection using:');
$this->warn(' --rules=1,2,...'); $this->friendlyWarning(' --rules=1,2,...');
$this->warn(' --rule_groups=1,2,...'); $this->friendlyWarning(' --rule_groups=1,2,...');
$this->warn(' --all_rules'); $this->friendlyWarning(' --all_rules');
return 1; return 1;
} }
@@ -139,14 +141,14 @@ class ApplyRules extends Command
$ruleEngine->addOperator(['type' => 'date_before', 'value' => $this->endDate->format('Y-m-d')]); $ruleEngine->addOperator(['type' => 'date_before', 'value' => $this->endDate->format('Y-m-d')]);
// start running rules. // start running rules.
$this->line(sprintf('Will apply %d rule(s) to your transaction(s).', $count)); $this->friendlyLine(sprintf('Will apply %d rule(s) to your transaction(s).', $count));
// file the rule(s) // file the rule(s)
$ruleEngine->fire(); $ruleEngine->fire();
$this->line(''); $this->friendlyLine('');
$end = round(microtime(true) - $start, 2); $end = round(microtime(true) - $start, 2);
$this->line(sprintf('Done in %s seconds!', $end)); $this->friendlyPositive(sprintf('Done in %s seconds!', $end));
return 0; return 0;
} }
@@ -201,7 +203,7 @@ class ApplyRules extends Command
{ {
$accountString = $this->option('accounts'); $accountString = $this->option('accounts');
if (null === $accountString || '' === $accountString) { if (null === $accountString || '' === $accountString) {
$this->error('Please use the --accounts option to indicate the accounts to apply rules to.'); $this->friendlyError('Please use the --accounts option to indicate the accounts to apply rules to.');
return false; return false;
} }
@@ -220,7 +222,7 @@ class ApplyRules extends Command
} }
if (0 === $finalList->count()) { if (0 === $finalList->count()) {
$this->error('Please make sure all accounts in --accounts are asset accounts or liabilities.'); $this->friendlyError('Please make sure all accounts in --accounts are asset accounts or liabilities.');
return false; return false;
} }
@@ -247,7 +249,7 @@ class ApplyRules extends Command
$this->ruleGroupSelection[] = $ruleGroup->id; $this->ruleGroupSelection[] = $ruleGroup->id;
} }
if (false === $ruleGroup->active) { if (false === $ruleGroup->active) {
$this->warn(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title)); $this->friendlyWarning(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title));
} }
} }

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Tools; namespace FireflyIII\Console\Commands\Tools;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Cronjobs\AutoBudgetCronjob; use FireflyIII\Support\Cronjobs\AutoBudgetCronjob;
use FireflyIII\Support\Cronjobs\BillWarningCronjob; use FireflyIII\Support\Cronjobs\BillWarningCronjob;
@@ -43,6 +44,8 @@ use Psr\Container\NotFoundExceptionInterface;
*/ */
class Cron extends Command class Cron extends Command
{ {
use ShowsFriendlyMessages;
/** /**
* The console command description. * The console command description.
* *
@@ -61,6 +64,8 @@ class Cron extends Command
/** /**
* @return int * @return int
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/ */
public function handle(): int public function handle(): int
{ {
@@ -68,7 +73,7 @@ class Cron extends Command
try { try {
$date = new Carbon($this->option('date')); $date = new Carbon($this->option('date'));
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {
$this->error(sprintf('"%s" is not a valid date', $this->option('date'))); $this->friendlyError(sprintf('"%s" is not a valid date', $this->option('date')));
} }
$force = (bool)$this->option('force'); $force = (bool)$this->option('force');
@@ -81,7 +86,7 @@ class Cron extends Command
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
$this->error($e->getMessage()); $this->friendlyError($e->getMessage());
} }
} }
@@ -93,7 +98,7 @@ class Cron extends Command
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
$this->error($e->getMessage()); $this->friendlyError($e->getMessage());
} }
/* /*
@@ -104,7 +109,7 @@ class Cron extends Command
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
$this->error($e->getMessage()); $this->friendlyError($e->getMessage());
} }
/* /*
@@ -115,71 +120,14 @@ class Cron extends Command
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
$this->error($e->getMessage()); $this->friendlyError($e->getMessage());
} }
$this->info('More feedback on the cron jobs can be found in the log files.'); $this->friendlyInfo('More feedback on the cron jobs can be found in the log files.');
return 0; return 0;
} }
/**
* @param bool $force
* @param Carbon|null $date
*
*/
private function autoBudgetCronJob(bool $force, ?Carbon $date): void
{
$autoBudget = new AutoBudgetCronjob();
$autoBudget->setForce($force);
// set date in cron job:
if (null !== $date) {
$autoBudget->setDate($date);
}
$autoBudget->fire();
if ($autoBudget->jobErrored) {
$this->error(sprintf('Error in "create auto budgets" cron: %s', $autoBudget->message));
}
if ($autoBudget->jobFired) {
$this->line(sprintf('"Create auto budgets" cron fired: %s', $autoBudget->message));
}
if ($autoBudget->jobSucceeded) {
$this->info(sprintf('"Create auto budgets" cron ran with success: %s', $autoBudget->message));
}
}
/**
* @param bool $force
* @param Carbon|null $date
*
* @throws FireflyException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function billWarningCronJob(bool $force, ?Carbon $date): void
{
$autoBudget = new BillWarningCronjob();
$autoBudget->setForce($force);
// set date in cron job:
if (null !== $date) {
$autoBudget->setDate($date);
}
$autoBudget->fire();
if ($autoBudget->jobErrored) {
$this->error(sprintf('Error in "bill warnings" cron: %s', $autoBudget->message));
}
if ($autoBudget->jobFired) {
$this->line(sprintf('"Send bill warnings" cron fired: %s', $autoBudget->message));
}
if ($autoBudget->jobSucceeded) {
$this->info(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message));
}
}
/** /**
* @param bool $force * @param bool $force
* @param Carbon|null $date * @param Carbon|null $date
@@ -196,13 +144,13 @@ class Cron extends Command
$exchangeRates->fire(); $exchangeRates->fire();
if ($exchangeRates->jobErrored) { if ($exchangeRates->jobErrored) {
$this->error(sprintf('Error in "exchange rates" cron: %s', $exchangeRates->message)); $this->friendlyError(sprintf('Error in "exchange rates" cron: %s', $exchangeRates->message));
} }
if ($exchangeRates->jobFired) { if ($exchangeRates->jobFired) {
$this->line(sprintf('"Exchange rates" cron fired: %s', $exchangeRates->message)); $this->friendlyInfo(sprintf('"Exchange rates" cron fired: %s', $exchangeRates->message));
} }
if ($exchangeRates->jobSucceeded) { if ($exchangeRates->jobSucceeded) {
$this->info(sprintf('"Exchange rates" cron ran with success: %s', $exchangeRates->message)); $this->friendlyPositive(sprintf('"Exchange rates" cron ran with success: %s', $exchangeRates->message));
} }
} }
@@ -226,13 +174,70 @@ class Cron extends Command
$recurring->fire(); $recurring->fire();
if ($recurring->jobErrored) { if ($recurring->jobErrored) {
$this->error(sprintf('Error in "create recurring transactions" cron: %s', $recurring->message)); $this->friendlyError(sprintf('Error in "create recurring transactions" cron: %s', $recurring->message));
} }
if ($recurring->jobFired) { if ($recurring->jobFired) {
$this->line(sprintf('"Create recurring transactions" cron fired: %s', $recurring->message)); $this->friendlyInfo(sprintf('"Create recurring transactions" cron fired: %s', $recurring->message));
} }
if ($recurring->jobSucceeded) { if ($recurring->jobSucceeded) {
$this->info(sprintf('"Create recurring transactions" cron ran with success: %s', $recurring->message)); $this->friendlyPositive(sprintf('"Create recurring transactions" cron ran with success: %s', $recurring->message));
}
}
/**
* @param bool $force
* @param Carbon|null $date
*
*/
private function autoBudgetCronJob(bool $force, ?Carbon $date): void
{
$autoBudget = new AutoBudgetCronjob();
$autoBudget->setForce($force);
// set date in cron job:
if (null !== $date) {
$autoBudget->setDate($date);
}
$autoBudget->fire();
if ($autoBudget->jobErrored) {
$this->friendlyError(sprintf('Error in "create auto budgets" cron: %s', $autoBudget->message));
}
if ($autoBudget->jobFired) {
$this->friendlyInfo(sprintf('"Create auto budgets" cron fired: %s', $autoBudget->message));
}
if ($autoBudget->jobSucceeded) {
$this->friendlyPositive(sprintf('"Create auto budgets" cron ran with success: %s', $autoBudget->message));
}
}
/**
* @param bool $force
* @param Carbon|null $date
*
* @throws FireflyException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function billWarningCronJob(bool $force, ?Carbon $date): void
{
$autoBudget = new BillWarningCronjob();
$autoBudget->setForce($force);
// set date in cron job:
if (null !== $date) {
$autoBudget->setDate($date);
}
$autoBudget->fire();
if ($autoBudget->jobErrored) {
$this->friendlyError(sprintf('Error in "bill warnings" cron: %s', $autoBudget->message));
}
if ($autoBudget->jobFired) {
$this->friendlyInfo(sprintf('"Send bill warnings" cron fired: %s', $autoBudget->message));
}
if ($autoBudget->jobSucceeded) {
$this->friendlyPositive(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message));
} }
} }
} }

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