Compare commits

...

170 Commits
5.2.7 ... 5.3.3

Author SHA1 Message Date
James Cole
106f097b45 Merge branch 'release/5.3.3' into main 2020-07-17 19:16:36 +02:00
James Cole
fafb7f5e49 Add dependabot 2020-07-17 19:16:05 +02:00
James Cole
e54c08e2d3 Update meta files for new release. 2020-07-17 19:12:29 +02:00
James Cole
03a63d0119 Update translations. 2020-07-17 19:00:38 +02:00
James Cole
e2b698f166 Fix timezone call 2020-07-17 18:52:34 +02:00
James Cole
f14ac5928f Fix some tests. 2020-07-17 18:51:54 +02:00
James Cole
cd65d4d4c5 Call correct timezone. 2020-07-17 18:51:35 +02:00
James Cole
7aa3cd5ba4 Fix #3568 2020-07-17 17:26:36 +02:00
James Cole
0fe17d5458 Fix #3574 2020-07-17 17:24:36 +02:00
James Cole
aaa3cc3d19 Merge pull request #3573 from emansih/develop
Fix url
2020-07-17 13:23:37 +00:00
emansih
c80d1d3402 Fix url 2020-07-17 20:48:18 +10:00
James Cole
254b004587 Fix #3566 2020-07-16 09:55:26 +02:00
James Cole
d813d0b7c2 Merge pull request #3565 from maroux/fix-monthly-date
Fix update monthly_payment_date using the API
2020-07-16 04:14:23 +00:00
Aniruddha Maru
53909fdd8a Fix maybe? 2020-07-15 12:22:09 -07:00
James Cole
0ba03be9a3 Merge branch 'release/5.3.2' into main 2020-07-13 16:03:36 +02:00
James Cole
7aac446cf2 Merge tag '5.3.2' into develop
5.3.2
2020-07-13 16:03:36 +02:00
James Cole
f2dcb108e8 Update meta files for new release. 2020-07-13 15:57:39 +02:00
James Cole
c0e5d6dd68 Update version. 2020-07-13 15:13:57 +02:00
James Cole
0b966ed541 Less verbose error logging for https://github.com/firefly-iii/firefly-iii/issues/3559 2020-07-13 15:03:19 +02:00
James Cole
8219b51e47 Skip error fix https://github.com/firefly-iii/firefly-iii/issues/3559 2020-07-13 14:22:00 +02:00
James Cole
9b102d1cf4 Merge tag '5.3.1' into develop
5.3.1
2020-07-12 20:25:23 +02:00
James Cole
ac607483aa Merge branch 'release/5.3.1' into main 2020-07-12 20:25:20 +02:00
James Cole
3ef46a59e9 Last-minute meta files. 2020-07-12 20:24:30 +02:00
James Cole
5529fa7124 Some last-minute translations, thanks guys! 2020-07-12 20:19:54 +02:00
James Cole
7a30bc4ff6 Updated meta for new release. 2020-07-12 19:48:24 +02:00
James Cole
8a9f6b1896 Double check on integers for https://github.com/firefly-iii/firefly-iii/issues/3554 2020-07-12 17:54:51 +02:00
James Cole
da77bcde04 Update version and API. 2020-07-12 17:34:06 +02:00
James Cole
c4979bdd27 Code for #3554 2020-07-12 17:32:48 +02:00
James Cole
0c9a25a073 Code cleanup and better auth check 2020-07-12 17:31:11 +02:00
James Cole
e24c2491a6 Expand frontend 2020-07-12 17:30:24 +02:00
James Cole
72a2718a93 Fix tests 2020-07-12 17:28:43 +02:00
James Cole
54d92f5b39 Code for #3546 2020-07-11 15:13:15 +02:00
James Cole
86600d4fcf Fix timezone issues. 2020-07-11 08:16:31 +02:00
James Cole
5cfe02edf6 Small textual changes 2020-07-11 07:18:51 +02:00
James Cole
22cb9dfbe0 make sure amount is a string. 2020-07-11 07:18:41 +02:00
James Cole
3782388e45 New security policy + release document. 2020-07-11 07:16:07 +02:00
James Cole
5821256590 Fix https://github.com/firefly-iii/firefly-iii/issues/3545 2020-07-11 07:01:29 +02:00
James Cole
14e06bfedd Experimental signed commit. 2020-07-10 06:48:39 +02:00
James Cole
886475740a Fix issue with profile functions. 2020-07-09 20:03:11 +02:00
James Cole
32e0a5bd80 Fix validation for #3532 2020-07-06 06:55:27 +02:00
James Cole
26d19fab32 Slightly more robust configuration polling. 2020-07-06 06:49:48 +02:00
James Cole
4271dc1638 Better frontend code and charts. 2020-07-05 21:03:35 +02:00
James Cole
a56cefda7d Basic new dashboard. 2020-07-05 18:29:58 +02:00
James Cole
4d4d91bf84 Merge branch 'main' into develop 2020-07-05 09:36:01 +02:00
James Cole
54dfb35007 Fix .env.example 2020-07-05 09:35:44 +02:00
James Cole
3893f59773 Fix some casting 2020-07-05 09:34:56 +02:00
James Cole
7d8786a620 Update changelog 2020-07-05 09:34:48 +02:00
James Cole
f48bc8d299 Budget and category charts in new layout. 2020-07-04 12:08:38 +02:00
James Cole
d33c87c565 Add error log for #3522 2020-07-03 18:41:16 +02:00
James Cole
5a28479788 Merge tag '5.3.0' into develop
5.3.0
2020-07-03 06:24:32 +02:00
James Cole
fabd7b1bfd Merge branch 'release/5.3.0' into main 2020-07-03 06:24:31 +02:00
James Cole
e57cd13145 Fix date in changelog 2020-07-03 06:23:54 +02:00
James Cole
515ee0a503 New version. 2020-07-03 06:22:57 +02:00
James Cole
cacbef4f75 Add PHP7.4 instructions. 2020-07-03 06:19:39 +02:00
James Cole
2772275e62 Update meta files before release. 2020-07-03 05:59:36 +02:00
James Cole
34fd66c835 Remove income from category chart #3517 2020-07-03 05:42:57 +02:00
James Cole
45f5a38512 Update meta files. 2020-07-02 20:23:45 +02:00
James Cole
6874d6469d Fix bill 2020-07-02 20:13:47 +02:00
James Cole
979c1ad305 Merge tag '5.3.0-beta.2' into develop
5.3.0-beta.2
2020-07-01 19:54:32 +02:00
James Cole
80de6c78dc Merge branch 'release/5.3.0-beta.2' into main 2020-07-01 19:54:31 +02:00
James Cole
5009cb5d55 New meta files. 2020-07-01 19:53:48 +02:00
James Cole
0c90171a49 Fix range for #3513 2020-07-01 19:47:32 +02:00
James Cole
d282cb4e34 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2020-07-01 19:47:20 +02:00
James Cole
976f79b5a6 Add account info for #3513 2020-07-01 19:43:53 +02:00
James Cole
6693a5166f Merge pull request #3506 from Johnny-Malizia/searchapi
Added transaction search api support
2020-07-01 19:35:44 +02:00
johnny
7b9906d493 Added transaction search api support
Transaction search api implemented using mostly the same process as the
existing search functionality.
Updated internal_reference and external_id search support to use exact
matches, expecting values in the database to be double quoted. This is
being done to ensure a specific transaction can be looked up at a later
time without prior knowledge of the internal transaction id.
2020-07-01 10:23:44 -07:00
James Cole
f927e61b37 Fix #3509 2020-07-01 15:46:26 +02:00
James Cole
430b498caf Fix meta files. 2020-07-01 15:45:09 +02:00
James Cole
983508e291 Expand API for bills. 2020-07-01 15:33:06 +02:00
James Cole
104a2379f3 Add fancy amounts. 2020-07-01 11:47:16 +02:00
James Cole
e337bcf8bd Can sort and group bills. 2020-07-01 06:33:21 +02:00
James Cole
029774687c Add command to fix inconsistent groups. 2020-07-01 06:02:58 +02:00
James Cole
be58545999 Make sure split transactions are always consistent. 2020-07-01 05:51:15 +02:00
James Cole
d774b4e2e3 Remove locale if using Docker. 2020-06-30 20:43:53 +02:00
James Cole
cb65999124 Expand API, add new migration. 2020-06-30 20:33:08 +02:00
James Cole
77e7af75dc Update translations and routes. 2020-06-30 19:10:17 +02:00
James Cole
714184867c Piggybank thing 2020-06-30 19:06:16 +02:00
James Cole
fad2331d80 Give bills groups too. 2020-06-30 19:06:05 +02:00
James Cole
bb5de8bf7e Update copyrights. 2020-06-30 19:05:35 +02:00
James Cole
6f78c96cee Fix piggy list. 2020-06-30 17:05:25 +02:00
James Cole
883342b2c8 Updated front matter 2020-06-29 08:04:38 +02:00
James Cole
6232858b85 Fix #3469 2020-06-29 06:44:17 +02:00
James Cole
4e4b44d41d Move to main branch. 2020-06-29 06:43:53 +02:00
James Cole
daf74e11ed Bigger max upload. 2020-06-28 15:30:59 +02:00
James Cole
3e2c2e6395 Merge tag '5.3.0-beta.1' into develop
5.3.0-beta.1
2020-06-28 09:25:31 +02:00
James Cole
bc5b2085b9 Merge branch 'release/5.3.0-beta.1' into main 2020-06-28 09:25:30 +02:00
James Cole
ebcd751145 Beta to changelog. 2020-06-28 09:25:19 +02:00
James Cole
cdfde3dca8 Update meta files for new release. 2020-06-28 09:19:18 +02:00
James Cole
4e3378451c Update readme. 2020-06-27 17:54:29 +02:00
James Cole
415fb7294c Fix sonarcloud issues 2020-06-27 17:33:18 +02:00
James Cole
1e35f0e7e3 You can logout other sessions. 2020-06-27 15:42:18 +02:00
James Cole
b83d06294d Drop Sandstorm related code. 2020-06-27 15:19:00 +02:00
James Cole
7945220825 Fix #3493 2020-06-27 13:41:27 +02:00
James Cole
3e8a4d55ef Make list grouped. 2020-06-27 13:32:40 +02:00
James Cole
e1fbdca5c1 Make list grouped. 2020-06-27 13:08:51 +02:00
James Cole
1b31d20877 Skip deleted piggies. 2020-06-27 09:56:38 +02:00
James Cole
aaaab03995 New translations 2020-06-26 08:35:59 +02:00
James Cole
affa8a317a Update packages 2020-06-26 07:30:29 +02:00
James Cole
5a03f3395c Fix #3440 2020-06-26 07:28:25 +02:00
James Cole
8da6ec3f5b Fix #3488 2020-06-26 04:04:54 +02:00
James Cole
a0402ac742 Fix text 2020-06-26 04:04:30 +02:00
James Cole
654e0fc74f Pretty basic fix #3490 2020-06-25 15:49:28 +02:00
James Cole
d3bd1f4124 Escape names to fix #3489 2020-06-25 13:37:44 +02:00
James Cole
2aa6067437 New strings. 2020-06-24 06:45:40 +02:00
James Cole
356c87da49 Fix #3427 2020-06-23 20:45:10 +02:00
James Cole
0d76fcd564 Fix #3398 2020-06-23 20:26:13 +02:00
James Cole
963c3b2a68 Fix #3391 2020-06-23 20:18:59 +02:00
James Cole
7617fe3510 Fix null pointer 2020-06-23 15:30:02 +02:00
James Cole
88724be3aa Fix mailer 2020-06-22 18:31:39 +02:00
James Cole
f845c97955 Merge tag '5.3.0-alpha.1' into develop
5.3.0-alpha.1
2020-06-22 18:18:02 +02:00
James Cole
2c69a2eda2 Merge branch 'release/5.3.0-alpha.1' 2020-06-22 18:17:59 +02:00
James Cole
dcce747e92 Mysql instructions. 2020-06-22 18:17:31 +02:00
James Cole
c251ca5daf Update meta files. 2020-06-22 18:08:23 +02:00
James Cole
bcb9794315 Update meta files. 2020-06-22 18:03:57 +02:00
James Cole
1a043e35c2 New meta files 2020-06-22 18:02:28 +02:00
James Cole
4fef316ddd Fix more date issues 2020-06-21 19:16:21 +02:00
James Cole
7196ac3ec9 Fix date issues. 2020-06-21 19:08:57 +02:00
James Cole
1c74db30ed More layout stuff 2020-06-21 18:29:23 +02:00
James Cole
00440f282b API for piggies + groups. 2020-06-21 18:28:51 +02:00
James Cole
dfe099f9c6 Emergency fix for #3479 2020-06-21 14:16:23 +02:00
James Cole
0dbee47182 Fix issue with null pointer 2020-06-20 22:16:03 +02:00
James Cole
46796a8c10 Logo 2020-06-20 19:14:28 +02:00
James Cole
c381067364 New code. 2020-06-20 19:14:06 +02:00
James Cole
19573de5df Fonts 2020-06-20 19:10:44 +02:00
James Cole
77359fbc55 Code for front page 2020-06-20 19:09:37 +02:00
James Cole
45eb758583 Fix list 2020-06-20 18:57:20 +02:00
James Cole
1a154a8d45 API for object groups. 2020-06-20 16:28:23 +02:00
James Cole
c40f6f2864 Add user id. 2020-06-20 10:23:57 +02:00
James Cole
c036c5a2bc Fix user management for groups. 2020-06-20 10:22:07 +02:00
James Cole
5b29e78c4b Group management code. 2020-06-20 10:10:55 +02:00
James Cole
b54ef9f5e5 Fix bad UUID generation. 2020-06-18 18:14:10 +02:00
James Cole
b7f48a19e8 Merge pull request #3467 from sephrat/days-left-for-bills
Fix #3437
2020-06-17 18:23:13 +00:00
James Cole
c8f1e4bbd7 Expand new layout code. 2020-06-17 07:06:45 +02:00
James Cole
5cc1369191 Expand debug. 2020-06-17 07:05:33 +02:00
Florian Dupret
16a511cf79 move 7c78708 logic from transformer to controller 2020-06-16 18:02:48 +02:00
Florian Dupret
7c78708865 Fix #3437 2020-06-16 14:07:40 +02:00
James Cole
0449430ec3 I messed up 2020-06-14 19:24:23 +02:00
James Cole
dd5a179ba1 More stuff for new layout. 2020-06-14 19:17:45 +02:00
James Cole
7c1139e42b First set of code for new layout based on AdminLTE 3.0 2020-06-14 15:51:20 +02:00
James Cole
caddf3d1c6 Fix #3461 2020-06-13 13:48:52 +02:00
James Cole
54d5778bf3 Add attachment to recurring. 2020-06-12 20:56:58 +02:00
James Cole
b1732d0de8 New possibilities for date range triggers #3403 2020-06-12 07:49:39 +02:00
James Cole
48b5f749a1 Fix logout for #3184 2020-06-11 17:55:38 +02:00
James Cole
a63b8322db Updates for #3184 2020-06-11 06:55:13 +02:00
James Cole
2130eef971 Update .env.example for #3184 2020-06-11 06:54:42 +02:00
James Cole
5b829b514f Include notes in export. 2020-06-09 17:40:09 +02:00
James Cole
1bac3258da Merge pull request #3455 from sephrat/debug_translation
Translate debug page
2020-06-09 15:18:17 +00:00
James Cole
d7181100ee Merge pull request #3454 from sephrat/translations
Translations tweaks
2020-06-09 15:17:30 +00:00
James Cole
ccc82858ad Fix drag/drop + sort 2020-06-09 17:16:21 +02:00
Florian Dupret
5dd6b23b09 Translate debug page 2020-06-09 13:51:01 +02:00
James Cole
6a08f52fa5 Update packages and order. 2020-06-09 11:09:23 +02:00
James Cole
18172b7fdb Fix order, fix bar. 2020-06-09 10:33:55 +02:00
James Cole
3eccc56c7a Better column 2020-06-09 08:48:28 +02:00
Florian Dupret
f7545af17a Missing translation on budgets index page 2020-06-09 08:37:40 +02:00
Florian Dupret
aa4da1e2e1 Telemetry count pluralization 2020-06-09 08:35:10 +02:00
James Cole
b4aafefc2b Fix NULL 2020-06-08 18:22:36 +02:00
James Cole
8304ee21f3 Fix #3450 2020-06-07 21:42:52 +02:00
James Cole
ca6479ede6 Update version. 2020-06-07 16:38:25 +02:00
James Cole
471cdefcff Organise object groups 2020-06-07 16:38:15 +02:00
James Cole
16b0307b0a Piggy bank can now have a group. 2020-06-07 11:31:01 +02:00
James Cole
2f63090e7c Split controller 2020-06-07 11:30:02 +02:00
James Cole
8643034945 Upgrade to 7.4 2020-06-06 22:25:52 +02:00
James Cole
6cc4d14fcb Remove import code. 2020-06-06 21:23:26 +02:00
James Cole
389ffa820f Merge branch 'release/5.2.8' 2020-06-06 12:19:11 +02:00
James Cole
60fa0d7244 Merge tag '5.2.8' into develop
5.2.8
2020-06-06 12:19:11 +02:00
James Cole
28d6885178 Update meta files for 5.2.8 2020-06-06 12:18:42 +02:00
James Cole
00f579909e Update meta files. 2020-06-06 12:16:32 +02:00
James Cole
a35bccb940 Fix #3443 2020-06-06 12:14:55 +02:00
James Cole
1cba62aa42 Cleanup changelog, forgot some unused entries. 2020-06-06 07:26:22 +02:00
James Cole
0974591a8f Merge tag '5.2.7' into develop
5.2.7
2020-06-06 07:18:38 +02:00
920 changed files with 77038 additions and 35192 deletions

View File

@@ -10,14 +10,14 @@ APP_DEBUG=false
SITE_OWNER=mail@example.com
# The encryption key for your sessions. Keep this very secure.
# If you generate a new one existing data must be considered LOST.
# 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=SomeRandomStringOf32CharsExactly
#
# 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/master/resources/lang
# 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
@@ -57,7 +57,9 @@ 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 "mysql" for MySQL and MariaDB. Use "sqlite" for SQLite.
# Use "pgsql" for PostgreSQL
# Use "mysql" for MySQL and MariaDB.
# Use "sqlite" for SQLite.
DB_CONNECTION=mysql
DB_HOST=fireflyiiidb
DB_PORT=3306
@@ -108,8 +110,8 @@ 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_DRIVER=log
MAIL_HOST=smtp.mailtrap.io
MAIL_MAILER=log
MAIL_HOST=null
MAIL_PORT=2525
MAIL_FROM=changeme@example.com
MAIL_USERNAME=null
@@ -167,6 +169,23 @@ FIXER_API_KEY=
# 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_URI=
# 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
@@ -287,9 +306,8 @@ PUSHER_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
USE_ENCRYPTION=false
IS_SANDSTORM=false
IS_HEROKU=false
BUNQ_USE_SANDBOX=false
FIREFLY_III_LAYOUT=v1
#
# If you have trouble configuring your Firefly III installation, DON'T BOTHER setting this variable.

View File

@@ -8,7 +8,7 @@ I am always interested in expanding Firefly III's many features. Just open a tic
## Pull requests
When contributing to Firefly III, please first discuss the change you wish to make via issue, email, or any other method. I can only accept pull requests against the `develop` branch, never the `master` branch.
When contributing to Firefly III, please first discuss the change you wish to make via issue, email, or any other method. I can only accept pull requests against the `develop` branch, never the `main` branch.
## Translations :us: :fr: :de:

26
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
version: 2
updates:
# composer updates
- package-ecosystem: "composer"
directory: "/" # Location of package manifests
target-branch: develop
labels: bug
schedule:
interval: "daily"
# yarn / JS updates
- package-ecosystem: "npm"
directory: "/"
target-branch: develop
labels: bug
schedule:
interval: "daily"
# yarn / JS updates for new frontend
- package-ecosystem: "npm"
directory: "/frontend"
target-branch: develop
labels: bug
schedule:
interval: "daily"

View File

@@ -1,8 +1,9 @@
<!--
Before you create a new PR, please consider the following two considerations.
Before you create a new PR, please consider:
1) Pull request for the MASTER branch will be closed.
1) Pull requests for the MAIN branch will be closed.
2) We cannot accept pull requests to add new currencies.
3) DO NOT include translations in your PR. Only English US sentences.
Thanks.
-->

50
.github/security.md vendored
View File

@@ -1,12 +1,54 @@
# Security Policy
Firefly III is an application to manage your personal finances. As such, the developer has adopted this security disclosure and response policy to ensure that critical issues are responsibly handled.
## Supported Versions
Only the latest Firefly III release is maintained. Applicable fixes, including security fixes, will not backported to older release branches. Please refer to [releases.md](https://github.com/firefly-iii/firefly-iii/blob/main/releases.md) for details.
Only the latest version of Firefly III is supported. If you're not running the latest version of Firefly III, please upgrade at your earliest convenience.
## Reporting a Vulnerability - Private Disclosure Process
Security is of the highest importance and all security vulnerabilities or suspected security vulnerabilities should be reported to Firefly III privately, to minimize attacks against current users of Firefly III before they are fixed. Vulnerabilities will be investigated and patched on the next patch (or minor) release as soon as possible. This information could be kept entirely internal to the project.
## Reporting a Vulnerability
If you know of a publicly disclosed security vulnerability for Firefly III, please **IMMEDIATELY** contact james@firefly-iii.org to inform the Firefly III developer. You can use my [GPG key](https://keybase.io/jc5) for extra security.
If you find something that compromises the security of Firefly III, you should [send me a message](mailto:james@firefly-iii.org) as soon as possible. These issues will be fixed immediately. You can also open an issue, but if you feel the issue is sensitive, please drop me a message instead.
**IMPORTANT: Do not file public issues on GitHub for security vulnerabilities**
You can use my [GPG key](https://keybase.io/jc5) for extra security. My [GitHub commits](https://github.com/firefly-iii/firefly-iii/commits/master) are almost always signed with this key.
To report a vulnerability or a security-related issue, please email the private address james@firefly-iii.org with the details of the vulnerability. The email will be received by the developer of Firefly III. Emails will be addressed within 3 business days, including a detailed plan to investigate the issue and any potential workarounds to perform in the meantime. Do not report non-security-impacting bugs through this channel. Use [GitHub issues](https://github.com/firefly-iii/firefly-iii/issues/new/choose) instead.
### Proposed Email Content
Provide a descriptive subject line and in the body of the email include the following information:
* Basic identity information, such as your name and your affiliation or company.
* Detailed steps to reproduce the vulnerability (POC scripts, screenshots, and compressed packet captures are all helpful to us).
* Description of the effects of the vulnerability on Firefly III and the related hardware and software configurations, so that the developer can reproduce it.
* How the vulnerability affects Firefly III usage and an estimation of the attack surface, if there is one.
* List other projects or dependencies that were used in conjunction with Firefly III to produce the vulnerability.
## When to report a vulnerability
* When you think Firefly III has a potential security vulnerability.
* When you suspect a potential vulnerability but you are unsure that it impacts Firefly III.
* When you know of or suspect a potential vulnerability on another project that is used by Firefly III. For example Firefly III has a dependency on Docker, MySQL, etc.
## Patch, Release, and Disclosure
The Firefly III developer will respond to vulnerability reports as follows:
1. The developer will investigate the vulnerability and determine its effects and criticality.
2. If the issue is not deemed to be a vulnerability, the developer will follow up with a detailed reason for rejection.
3. The developer will initiate a conversation with the reporter within 3 business days.
4. If a vulnerability is acknowledged and the timeline for a fix is determined, the developer will work on a plan to communicate with the appropriate community, including identifying mitigating steps that affected users can take to protect themselves until the fix is rolled out.
5. The developer will also create a [CVSS](https://www.first.org/cvss/specification-document) using the [CVSS Calculator](https://www.first.org/cvss/calculator/3.0). The developer makes the final call on the calculated CVSS; it is better to move quickly than making the CVSS perfect. Issues may also be reported to [Mitre](https://cve.mitre.org/) using this [scoring calculator](https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator). The CVE will initially be set to private.
6. The developer will work on fixing the vulnerability and perform internal testing before preparing to roll out the fix.
7. A public disclosure date is negotiated by the Firefly III developer and the bug submitter. We prefer to fully disclose the bug as soon as possible once a user mitigation or patch is available. It is reasonable to delay disclosure when the bug or the fix is not yet fully understood, the solution is not well-tested, or for distributor coordination. The timeframe for disclosure is from immediate (especially if its already publicly known) to a few weeks. For a critical vulnerability with a straightforward mitigation, we expect report date to public disclosure date to be on the order of 14 business days. The Firefly III developer holds the final say when setting a public disclosure date.
9. Once the fix is confirmed, the developer will patch the vulnerability in the next patch or minor release. Upon release of the patched version of Firefly III, we will follow the **Public Disclosure Process**.
### Public Disclosure Process
The developer publishes a public [advisory](https://github.com/firefly-iii/firefly-iii/security/advisories) to the Firefly III community via GitHub. In most cases, additional communication via Twitter, reddit and other channels will assist in educating Firefly III users and rolling out the patched release to affected users.
The develop will also publish any mitigating steps users can take until the fix can be applied to their Firefly III instances.
## Confidentiality, integrity and availability
We consider vulnerabilities leading to the compromise of data confidentiality, elevation of privilege, or integrity to be our highest priority concerns. Availability, in particular in areas relating to DoS and resource exhaustion, is also a serious security concern. The Firefly III developer takes all vulnerabilities, potential vulnerabilities, and suspected vulnerabilities seriously and will investigate them in an urgent and expeditious manner.
Note that we do not currently consider the default settings for Firefly III to be secure-by-default. It is necessary for operators to explicitly configure settings, role based access control, and other resource related features in Firefly III to provide a hardened Firefly III environment. We will not act on any security disclosure that relates to a lack of safe defaults. Over time, we will work towards improved safe-by-default configuration, taking into account backwards compatibility.
## Credits
This security policy is based on [Harbor](https://github.com/goharbor/harbor)'s security policy.

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
/node_modules
/frontend/node_modules
/frontend/fonts
/public/hot
/public/storage
/storage/*.key

View File

@@ -3,7 +3,7 @@
"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/master/public/mstile-150x150.png",
"logo": "https://raw.githubusercontent.com/firefly-iii/firefly-iii/main/public/mstile-150x150.png",
"keywords": [
"finance",
"finances",
@@ -41,7 +41,7 @@
{
"plan": "heroku-postgresql",
"options": {
"version": "9.5"
"version": "12"
}
}
],
@@ -58,8 +58,8 @@
],
"env": {
"APP_KEY": {
"description": "This key is used to encrypt your data.",
"description": "This key is used to create app cookies en secure attachments.",
"value": "base64:If1gJN4pyycXTq+WS5TjneDympKuu+8SKvTl6RZnhJg="
}
}
}
}

View File

@@ -50,8 +50,9 @@ use League\Fractal\Resource\Item;
class AccountController extends Controller
{
use AccountFilter, TransactionFilter;
/** @var AccountRepositoryInterface The account repository */
private $repository;
private AccountRepositoryInterface $repository;
public const RESOURCE_KEY = 'accounts';
/**
* AccountController constructor.
@@ -150,7 +151,7 @@ class AccountController extends Controller
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($accounts, $transformer, 'accounts');
$resource = new FractalCollection($accounts, $transformer, self::RESOURCE_KEY);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -208,7 +209,7 @@ class AccountController extends Controller
/** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($account, $transformer, 'accounts');
$resource = new Item($account, $transformer, self::RESOURCE_KEY);
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -230,7 +231,7 @@ class AccountController extends Controller
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($account, $transformer, 'accounts');
$resource = new Item($account, $transformer, self::RESOURCE_KEY);
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -304,7 +305,7 @@ class AccountController extends Controller
/** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($account, $transformer, 'accounts');
$resource = new Item($account, $transformer, self::RESOURCE_KEY);
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}

View File

@@ -42,10 +42,8 @@ use Illuminate\Http\JsonResponse;
class AccountController extends Controller
{
use ApiSupport;
/** @var CurrencyRepositoryInterface */
private $currencyRepository;
/** @var AccountRepositoryInterface */
private $repository;
private CurrencyRepositoryInterface $currencyRepository;
private AccountRepositoryInterface $repository;
/**
* AccountController constructor.
@@ -196,6 +194,8 @@ class AccountController extends Controller
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'start_date' => $start->format('Y-m-d'),
'end_date' => $end->format('Y-m-d'),
'type' => 'line', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [],

View File

@@ -0,0 +1,298 @@
<?php
/**
* BudgetController.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\DateRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
/**
* Class BudgetController
*/
class BudgetController extends Controller
{
private BudgetLimitRepositoryInterface $blRepository;
private OperationsRepositoryInterface $opsRepository;
private BudgetRepositoryInterface $repository;
/**
* BudgetController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
//$this->generator = app(GeneratorInterface::class);
$this->repository = app(BudgetRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
//$this->nbRepository = app(NoBudgetRepositoryInterface::class);
return $next($request);
}
);
}
/**
* [
* 'label' => 'label for entire set'
* 'currency_id' => 0,
* 'currency_code' => 'EUR',
* 'currency_symbol' => '$',
* 'currency_decimal_places' => 2,
* 'type' => 'bar', // line, area or bar
* 'yAxisID' => 0, // 0, 1, 2
* 'entries' => ['a' => 1, 'b' => 4],
* ],
*
* @param DateRequest $request
*
* @return JsonResponse
*/
public function overview(DateRequest $request): JsonResponse
{
$dates = $request->getAll();
$budgets = $this->repository->getActiveBudgets();
$budgetNames = [];
$currencyNames = [];
$sets = [];
/** @var Budget $budget */
foreach ($budgets as $budget) {
$expenses = $this->getExpenses($budget, $dates['start'], $dates['end']);
$expenses = $this->filterNulls($expenses);
foreach ($expenses as $set) {
$budgetNames[] = $set['budget_name'];
$currencyNames[] = $set['currency_name'];
$sets[] = $set;
}
}
$budgetNames = array_unique($budgetNames);
$currencyNames = array_unique($currencyNames);
$basic = $this->createSets($budgetNames, $currencyNames);
$filled = $this->fillSets($basic, $sets);
$keys = array_values($filled);
return response()->json($keys);
}
/**
* @param Collection $limits
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function getExpenses(Budget $budget, Carbon $start, Carbon $end): array
{
$limits = $this->blRepository->getBudgetLimits($budget, $start, $end);
if (0 === $limits->count()) {
return $this->getExpenseInRange($budget, $start, $end);
}
$arr = [];
/** @var BudgetLimit $limit */
foreach ($limits as $limit) {
$arr[] = $this->getExpensesForLimit($limit);
}
return $arr;
}
/**
* @param array $budgetNames
* @param array $currencyNames
*
* @return array
*/
private function createSets(array $budgetNames, array $currencyNames): array
{
$return = [];
foreach ($currencyNames as $currencyName) {
$entries = [];
foreach ($budgetNames as $budgetName) {
$label = sprintf('%s (%s)', $budgetName, $currencyName);
$entries[$label] = '0';
}
// left
$return['left'] = [
'label' => sprintf('%s (%s)', trans('firefly.left'), $currencyName),
'data_type' => 'left',
'currency_name' => $currencyName,
'type' => 'bar',
'yAxisID' => 0, // 0, 1, 2
'entries' => $entries,
];
// spent_capped
$return['spent_capped'] = [
'label' => sprintf('%s (%s)', trans('firefly.spent'), $currencyName),
'data_type' => 'spent_capped',
'currency_name' => $currencyName,
'type' => 'bar',
'yAxisID' => 0, // 0, 1, 2
'entries' => $entries,
];
// overspent
$return['overspent'] = [
'label' => sprintf('%s (%s)', trans('firefly.overspent'), $currencyName),
'data_type' => 'overspent',
'currency_name' => $currencyName,
'type' => 'bar',
'yAxisID' => 0, // 0, 1, 2
'entries' => $entries,
];
}
return $return;
}
/**
* @param array $basic
* @param array $sets
*
* @return array
*/
private function fillSets(array $basic, array $sets): array
{
foreach ($sets as $set) {
$label = $set['label'];
//$basic['spent']['entries'][$label] = $set['entries']['spent'];
$basic['spent_capped']['entries'][$label] = $set['entries']['spent_capped'];
$basic['left']['entries'][$label] = $set['entries']['left'];
$basic['overspent']['entries'][$label] = $set['entries']['overspent'];
}
return $basic;
}
/**
* @param array $expenses
*
* @return array
*/
private function filterNulls(array $expenses): array
{
$return = [];
/** @var array|null $arr */
foreach ($expenses as $arr) {
if (null !== $arr) {
$return[] = $arr;
}
}
return $return;
}
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getExpenseInRange(Budget $budget, Carbon $start, Carbon $end): array
{
$spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget]), null);
$return = [];
/** @var array $set */
foreach ($spent as $set) {
$current = [
'label' => sprintf('%s (%s)', $budget->name, $set['currency_name']),
'budget_name' => $budget->name,
'start_date' => $start->format('Y-m-d'),
'end_date' => $end->format('Y-m-d'),
'currency_id' => (int) $set['currency_id'],
'currency_code' => $set['currency_code'],
'currency_name' => $set['currency_name'],
'currency_symbol' => $set['currency_symbol'],
'currency_decimal_places' => (int) $set['currency_decimal_places'],
'type' => 'bar', // line, area or bar,
'entries' => [],
];
$sumSpent = bcmul($set['sum'], '-1'); // spent
$current['entries']['spent'] = $sumSpent;
$current['entries']['amount'] = '0';
$current['entries']['spent_capped'] = $sumSpent;
$current['entries']['left'] = '0';
$current['entries']['overspent'] = '0';
$return[] = $current;
}
return $return;
}
/**
* @param BudgetLimit $limit
*
* @return array|null
*/
private function getExpensesForLimit(BudgetLimit $limit): ?array
{
$budget = $limit->budget;
$spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $limit->transactionCurrency);
$currency = $limit->transactionCurrency;
// when limited to a currency, the count is always one. Or it's empty.
$set = array_shift($spent);
if (null === $set) {
return null;
}
$return = [
'label' => sprintf('%s (%s)', $budget->name, $set['currency_name']),
'budget_name' => $budget->name,
'start_date' => $limit->start_date->format('Y-m-d'),
'end_date' => $limit->end_date->format('Y-m-d'),
'currency_id' => (int) $currency->id,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => (int) $currency->decimal_places,
'type' => 'bar', // line, area or bar,
'entries' => [],
];
$sumSpent = bcmul($set['sum'], '-1'); // spent
$return['entries']['spent'] = $sumSpent;
$return['entries']['amount'] = $limit->amount;
$return['entries']['spent_capped'] = 1 === bccomp($sumSpent, $limit->amount) ? $limit->amount : $sumSpent;
$return['entries']['left'] = 1 === bccomp($limit->amount, $sumSpent) ? bcadd($set['sum'], $limit->amount) : '0'; // left
$return['entries']['overspent'] = 1 === bccomp($limit->amount, $sumSpent) ? '0' : bcmul(bcadd($set['sum'], $limit->amount), '-1'); // overspent
return $return;
}
}

View File

@@ -74,8 +74,6 @@ class CategoryController extends Controller
* @param DateRequest $request
*
* @return JsonResponse
*
* TODO after 4.8,0, simplify
*/
public function overview(DateRequest $request): JsonResponse
{
@@ -89,32 +87,15 @@ class CategoryController extends Controller
$tempData = [];
$spentWith = $this->opsRepository->listExpenses($start, $end);
$earnedWith = $this->opsRepository->listIncome($start, $end);
$spentWithout = $this->noCatRepository->listExpenses($start, $end);
$earnedWithout = $this->noCatRepository->listIncome($start, $end);
$categories = [];
foreach ([$spentWith, $earnedWith, $spentWithout, $earnedWithout] as $set) {
foreach ([$spentWith, $spentWithout,] as $set) {
foreach ($set as $currency) {
foreach ($currency['categories'] as $category) {
$categories[] = $category['name'];
$inKey = sprintf('%d-i', $currency['currency_id']);
$outKey = sprintf('%d-e', $currency['currency_id']);
// make data arrays if not yet present.
$tempData[$inKey] = $tempData[$inKey] ?? [
'currency_id' => $currency['currency_id'],
'label' => (string) trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
'currency_code' => $currency['currency_code'],
'currency_symbol' => $currency['currency_symbol'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [
// per category:
// "category" => 5,
],
];
$tempData[$outKey] = $tempData[$outKey] ?? [
'currency_id' => $currency['currency_id'],
'label' => (string) trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
@@ -123,16 +104,12 @@ class CategoryController extends Controller
'currency_decimal_places' => $currency['currency_decimal_places'],
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [
// per category:
// "category" => 5,
],
'entries' => [],
];
foreach ($category['transaction_journals'] as $journal) {
// is it expense or income?
$letter = -1 === bccomp($journal['amount'], '0') ? 'e' : 'i';
$currentKey = sprintf('%d-%s', $currency['currency_id'], $letter);
$currentKey = sprintf('%d-%s', $currency['currency_id'], 'e');
$name = $category['name'];
$tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0';
$tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']);
@@ -141,49 +118,6 @@ class CategoryController extends Controller
}
}
// foreach ([] as $set) {
// foreach ($set as $currency) {
// $inKey = sprintf('%d-i', $currency['currency_id']);
// $outKey = sprintf('%d-e', $currency['currency_id']);
// $categories[] = (string)trans('firefly.no_category');
// // make data arrays if not yet present.
// $tempData[$inKey] = $tempData[$inKey] ?? [
// 'currency_id' => $currency['currency_id'],
// 'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $currency['currency_name']]),
// 'currency_code' => $currency['currency_code'],
// 'currency_symbol' => $currency['currency_symbol'],
// 'currency_decimal_places' => $currency['currency_decimal_places'],
// 'type' => 'bar', // line, area or bar
// 'yAxisID' => 0, // 0, 1, 2
// 'entries' => [
// // per category:
// // "category" => 5,
// ],
// ];
// $tempData[$outKey] = $tempData[$outKey] ?? [
// 'currency_id' => $currency['currency_id'],
// 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]),
// 'currency_code' => $currency['currency_code'],
// 'currency_symbol' => $currency['currency_symbol'],
// 'currency_decimal_places' => $currency['currency_decimal_places'],
// 'type' => 'bar', // line, area or bar
// 'yAxisID' => 0, // 0, 1, 2
// 'entries' => [
// // per category:
// // "category" => 5,
// ],
// ];
// foreach ($currency['transaction_journals'] as $journal) {
// // is it expense or income?
// $letter = -1 === bccomp($journal['amount'], '0') ? 'e' : 'i';
// $currentKey = sprintf('%d-%s', $currency['currency_id'], $letter);
// $name = (string)trans('firefly.no_category');
// $tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0';
// $tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']);
// }
// }
// }
// re-sort every spent array and add 0 for missing entries.
foreach ($tempData as $index => $set) {
$oldSet = $set['entries'];

View File

@@ -577,7 +577,8 @@ class CurrencyController extends Controller
{
$manager = $this->getManager();
$currency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $currency);
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -0,0 +1,278 @@
<?php
/**
* DestroyController.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Data;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\DataDestroyRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
use FireflyIII\Services\Internal\Destroy\JournalDestroyService;
use Illuminate\Http\JsonResponse;
/**
* Class DestroyController
*/
class DestroyController extends Controller
{
/**
* @return JsonResponse
*/
public function destroy(DataDestroyRequest $request): JsonResponse
{
$objects = $request->getObjects();
switch ($objects) {
default:
throw new FireflyException(sprintf('This endpoint can\'t handle object "%s"', $objects));
case 'budgets':
$this->destroyBudgets();
break;
case 'bills':
$this->destroyBills();
break;
case 'piggy_banks':
$this->destroyPiggyBanks();
break;
case 'rules':
$this->destroyRules();
break;
case 'recurring':
$this->destroyRecurringTransactions();
break;
case 'categories':
$this->destroyCategories();
break;
case 'tags':
$this->destroyTags();
break;
case 'object_groups':
$this->destroyObjectGroups();
break;
case 'accounts':
$this->destroyAccounts(
[
AccountType::ASSET, AccountType::DEFAULT,
AccountType::BENEFICIARY, AccountType::EXPENSE,
AccountType::REVENUE, AccountType::INITIAL_BALANCE,
AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD,
]
);
break;
case 'asset_accounts':
$this->destroyAccounts(
[
AccountType::ASSET, AccountType::DEFAULT,
]
);
break;
case 'expense_accounts':
$this->destroyAccounts(
[
AccountType::BENEFICIARY, AccountType::EXPENSE,
]
);
break;
case 'revenue_accounts':
$this->destroyAccounts(
[
AccountType::REVENUE,
]
);
break;
case 'liabilities':
$this->destroyAccounts(
[
AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD,
]
);
break;
case 'transactions':
$this->destroyTransactions(
[
TransactionType::WITHDRAWAL,
TransactionType::DEPOSIT,
TransactionType::TRANSFER,
TransactionType::RECONCILIATION,
TransactionType::OPENING_BALANCE,
]
);
break;
case 'withdrawals':
$this->destroyTransactions(
[
TransactionType::WITHDRAWAL,
]
);
break;
case 'deposits':
$this->destroyTransactions(
[
TransactionType::DEPOSIT,
]
);
break;
case 'transfers':
$this->destroyTransactions(
[
TransactionType::TRANSFER,
]
);
break;
}
return response()->json([], 204);
}
/**
* @param array $types
*/
private function destroyAccounts(array $types): void
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$collection = $repository->getAccountsByType($types);
$service = app(AccountDestroyService::class);
/** @var Account $account */
foreach ($collection as $account) {
$service->destroy($account, null);
}
}
/**
*
*/
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
*/
private function destroyTransactions(array $types): void
{
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$journals = $repository->findByType($types);
$service = app(JournalDestroyService::class);
/** @var TransactionJournal $journal */
foreach($journals as $journal) {
$service->destroy($journal);
}
}
}

View File

@@ -1,181 +0,0 @@
<?php
/**
* ImportController.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\ImportJobTransformer;
use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
/**
* Class ImportController
*
* @deprecated
* @codeCoverageIgnore
*/
class ImportController extends Controller
{
use TransactionFilter;
/** @var ImportJobRepositoryInterface Import job repository. */
private $repository;
/**
* ImportController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(ImportJobRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
}
);
}
/**
* @return JsonResponse
* @codeCoverageIgnore
*/
public function listAll(): JsonResponse
{
// create some objects:
$manager = $this->getManager();
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of accounts. Count it and split it.
$collection = $this->repository->get();
$count = $collection->count();
$importJobs = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($importJobs, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.import.list') . $this->buildParams());
/** @var ImportJobTransformer $transformer */
$transformer = app(ImportJobTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($importJobs, $transformer, 'import_jobs');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param ImportJob $importJob
*
* @return JsonResponse
* @codeCoverageIgnore
*/
public function show(ImportJob $importJob): JsonResponse
{
$manager = $this->getManager();
/** @var ImportJobTransformer $transformer */
$transformer = app(ImportJobTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($importJob, $transformer, 'import_jobs');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show all transactions
*
* @param Request $request
* @param ImportJob $importJob
*
* @return JsonResponse
* @codeCoverageIgnore
*/
public function transactions(Request $request, ImportJob $importJob): JsonResponse
{
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$type = $request->get('type') ?? 'default';
$this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = $this->getManager();
$tag = $importJob->tag;
$transactions = new Collection();
$paginator = new LengthAwarePaginator($transactions, 0, $pageSize);
$paginator->setPath(route('api.v1.import.transactions', [$importJob->key]) . $this->buildParams());
if (null !== $tag) {
/** @var User $admin */
$admin = auth()->user();
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// filter on tag.
->setTag($tag)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes($types);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$paginator = $collector->getPaginatedGroups();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection();
}
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
}

View File

@@ -0,0 +1,196 @@
<?php
/**
* GroupController.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\ObjectGroupUpdateRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\ObjectGroup;
use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
use FireflyIII\Transformers\ObjectGroupTransformer;
use FireflyIII\Transformers\PiggyBankTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
/**
* Class GroupController.
*
*/
class ObjectGroupController extends Controller
{
private ObjectGroupRepositoryInterface $repository;
/**
* ObjectGroupController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(ObjectGroupRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
}
);
}
/**
* Remove the specified resource from storage.
*
* @param ObjectGroup $objectGroup
*
* @codeCoverageIgnore
* @return JsonResponse
*/
public function delete(ObjectGroup $objectGroup): JsonResponse
{
$this->repository->destroy($objectGroup);
return response()->json([], 204);
}
/**
* Display a listing of the resource.
*
* @param Request $request
*
* @codeCoverageIgnore
* @return JsonResponse
*/
public function index(Request $request): JsonResponse
{
$manager = $this->getManager();
// types to get, page size:
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of accounts. Count it and split it.
$collection = $this->repository->get();
$count = $collection->count();
$objectGroups = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($objectGroups, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.object-groups.index') . $this->buildParams());
/** @var ObjectGroupTransformer $transformer */
$transformer = app(ObjectGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($objectGroups, $transformer, 'object_groups');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List all piggies under the object group.
*
* @param ObjectGroup $objectGroup
*
* @return JsonResponse
* @codeCoverageIgnore
*
*/
public function piggyBanks(ObjectGroup $objectGroup): JsonResponse
{
// create some objects:
$manager = $this->getManager();
// types to get, page size:
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of piggy banks. Count it and split it.
$collection = $this->repository->getPiggyBanks($objectGroup);
$count = $collection->count();
$piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.object-groups.piggy_banks', [$objectGroup->id]) . $this->buildParams());
/** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($piggyBanks, $transformer, 'piggy_banks');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show single instance.
*
* @param ObjectGroup $objectGroup
*
* @return JsonResponse
*/
public function show(ObjectGroup $objectGroup): JsonResponse
{
$manager = $this->getManager();
/** @var ObjectGroupTransformer $transformer */
$transformer = app(ObjectGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($objectGroup, $transformer, 'object_groups');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Update object.
*
* @param ObjectGroupUpdateRequest $request
* @param Account $account
*
* @return JsonResponse
*/
public function update(ObjectGroupUpdateRequest $request, ObjectGroup $objectGroup): JsonResponse
{
$data = $request->getUpdateData();
$this->repository->update($objectGroup, $data);
$this->repository->sort();
$manager = $this->getManager();
/** @var ObjectGroupTransformer $transformer */
$transformer = app(ObjectGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($objectGroup, $transformer, 'object_groups');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\PiggyBankRequest;
use FireflyIII\Api\V1\Requests\PiggyBankStoreRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
@@ -204,12 +205,12 @@ class PiggyBankController extends Controller
/**
* Store new object.
*
* @param PiggyBankRequest $request
* @param PiggyBankStoreRequest $request
*
* @throws FireflyException
* @return JsonResponse
*/
public function store(PiggyBankRequest $request): JsonResponse
public function store(PiggyBankStoreRequest $request): JsonResponse
{
$piggyBank = $this->repository->store($request->getAll());
$manager = $this->getManager();
@@ -240,7 +241,6 @@ class PiggyBankController extends Controller
$this->repository->setCurrentAmount($piggyBank, $data['current_amount']);
}
$manager = $this->getManager();
/** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class);

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* AccountController.php
* Copyright (c) 2019 james@firefly-iii.org
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Search;
use FireflyIII\Api\V1\Controllers\Controller;
@@ -41,8 +43,7 @@ class AccountController extends Controller
{
use AccountFilter;
/** @var array */
private $validFields;
private array $validFields;
public function __construct()
{

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* TransactionController.php
* Copyright (c) 2019 james@firefly-iii.org
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,45 +20,51 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Search;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Support\Search\SearchInterface;
use FireflyIII\Transformers\TransactionGroupTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection;
/**
* Class TransactionController
*/
class TransactionController extends Controller
{
/** @var string */
public const SEARCH_ALL = 'all';
/** @var string */
public const SEARCH_DESCRIPTION = 'description';
/** @var string */
public const SEARCH_NOTES = 'notes';
/** @var string */
public const SEARCH_ACCOUNTS = 'accounts';
/** @var array */
private $validFields;
public function __construct()
{
parent::__construct();
$this->validFields = [
self::SEARCH_ALL,
self::SEARCH_DESCRIPTION,
self::SEARCH_NOTES,
self::SEARCH_ACCOUNTS,
];
}
/**
* @param Request $request
* @param Request $request
* @param SearchInterface $searcher
*
* @return void
* @return JsonResponse
*/
public function search(Request $request): void
public function search(Request $request, SearchInterface $searcher): JsonResponse
{
die('the route is present but nobody\'s home.');
$manager = $this->getManager();
$fullQuery = (string) $request->get('query');
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
$searcher->parseQuery($fullQuery);
$searcher->setPage($page);
$searcher->setLimit((int) config('firefly.search_result_limit'));
$groups = $searcher->searchTransactions();
$parameters = ['search' => $fullQuery];
$url = route('api.v1.search.transactions') . '?' . http_build_query($parameters);
$groups->setPath($url);
$transactions = $groups->getCollection();
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Collection($transactions, $transformer, 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($groups));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
}

View File

@@ -1,113 +0,0 @@
<?php
declare(strict_types=1);
/**
* TransferController.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V1\Controllers\Search;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Search\TransferRequest;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Support\Search\TransferSearch;
use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
/**
* Class TransferController
*/
class TransferController extends Controller
{
/**
* @param TransferRequest $request
*
* @return JsonResponse|Response
*/
public function search(TransferRequest $request)
{
// configure transfer search to search for a > b
$search = app(TransferSearch::class);
$search->setSource($request->get('source'));
$search->setDestination($request->get('destination'));
$search->setAmount($request->get('amount'));
$search->setDescription($request->get('description'));
$search->setDate($request->get('date'));
$left = $search->search();
// configure transfer search to search for b > a
$search->setSource($request->get('destination'));
$search->setDestination($request->get('source'));
$search->setAmount($request->get('amount'));
$search->setDescription($request->get('description'));
$search->setDate($request->get('date'));
$right = $search->search();
// add parameters to URL:
$this->parameters->set('source', $request->get('source'));
$this->parameters->set('destination', $request->get('destination'));
$this->parameters->set('amount', $request->get('amount'));
$this->parameters->set('description', $request->get('description'));
$this->parameters->set('date', $request->get('date'));
// get all journal ID's.
$total = $left->merge($right)->unique('id')->pluck('id')->toArray();
if (0 === count($total)) {
// forces search to be empty.
$total = [-1];
}
// collector to return results.
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$manager = $this->getManager();
/** @var User $admin */
$admin = auth()->user();
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setUser($admin)
// all info needed for the API:
->withAPIInformation()
// set page size:
->setLimit($pageSize)
// set page to retrieve
->setPage(1)
->setJournalIds($total);
$paginator = $collector->getPaginatedGroups();
$paginator->setPath(route('api.v1.search.transfers') . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
}

View File

@@ -75,7 +75,7 @@ class AccountStoreRequest extends Request
'opening_balance' => $this->string('opening_balance'),
'opening_balance_date' => $this->date('opening_balance_date'),
'cc_type' => $this->string('credit_card_type'),
'cc_Monthly_payment_date' => $this->string('monthly_payment_date'),
'cc_monthly_payment_date' => $this->string('monthly_payment_date'),
'notes' => $this->nlString('notes'),
'interest' => $this->string('interest'),
'interest_period' => $this->string('interest_period'),

View File

@@ -75,7 +75,7 @@ class AccountUpdateRequest extends Request
'opening_balance' => $this->nullableString('opening_balance'),
'opening_balance_date' => $this->date('opening_balance_date'),
'cc_type' => $this->nullableString('credit_card_type'),
'cc_Monthly_payment_date' => $this->nullableString('monthly_payment_date'),
'cc_monthly_payment_date' => $this->nullableString('monthly_payment_date'),
'notes' => $this->nullableNlString('notes'),
'interest' => $this->nullableString('interest'),
'interest_period' => $this->nullableString('interest_period'),

View File

@@ -67,7 +67,7 @@ class AvailableBudgetRequest extends Request
return [
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'amount' => 'required|numeric|more:0',
'amount' => 'required|numeric|gt:0',
'start' => 'required|date|before:end',
'end' => 'required|date|after:start',
];

View File

@@ -70,6 +70,7 @@ class BillRequest extends Request
'repeat_freq' => $this->string('repeat_freq'),
'skip' => $this->integer('skip'),
'active' => $active,
'order' => $this->integer('order'),
'notes' => $this->nlString('notes'),
];
}
@@ -83,13 +84,13 @@ class BillRequest extends Request
public function rules(): array
{
$rules = [
'name' => 'required|between:1,255|uniqueObjectForUser:bills,name',
'amount_min' => 'required|numeric|more:0',
'amount_max' => 'required|numeric|more:0',
'name' => 'between:1,255|uniqueObjectForUser:bills,name',
'amount_min' => 'numeric|gt:0',
'amount_max' => 'numeric|gt:0',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'date' => 'required|date',
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
'date' => 'date',
'repeat_freq' => 'in:weekly,monthly,quarterly,half-year,yearly',
'skip' => 'between:0,31',
'active' => [new IsBoolean],
'notes' => 'between:1,65536',

View File

@@ -70,7 +70,7 @@ class BudgetLimitRequest extends Request
'budget_id' => 'required|exists:budgets,id|belongsToUser:budgets,id',
'start' => 'required|before:end|date',
'end' => 'required|after:start|date',
'amount' => 'required|more:0',
'amount' => 'required|gt:0',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
];

View File

@@ -1,7 +1,7 @@
<?php
/**
* SnsDescription.php
* Copyright (c) 2019 hugovanduijn@gmail.com.
* DataDestroyRequest.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -18,56 +18,49 @@
* 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\Import\Specifics;
namespace FireflyIII\Api\V1\Requests;
/**
* Class SnsDescription.
*
* @codeCoverageIgnore
* @deprecated
* Class DataDestroyRequest
*/
class SnsDescription implements SpecificInterface
class DataDestroyRequest extends Request
{
/**
* Get description of specific.
* Authorize logged in users.
*
* @return string
* @codeCoverageIgnore
* @return bool
*/
public static function getDescription(): string
public function authorize(): bool
{
return 'import.specific_sns_descr';
// Only allow authenticated users
return auth()->check() && !auth()->user()->hasRole('demo');
}
/**
* Get name of specific.
* Get all data from the request.
*
* @return string
* @codeCoverageIgnore
*/
public static function getName(): string
public function getObjects(): string
{
return 'import.specific_sns_name';
return $this->get('objects') ?? '';
}
/**
* Run specific.
*
* @param array $row
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function run(array $row): array
public function rules(): array
{
$row = array_values($row);
if (!isset($row[17])) {
return $row;
}
$row[17] = ltrim($row[17], "'");
$row[17] = rtrim($row[17], "'");
$valid = 'budgets,bills,piggy_banks,rules,recurring,categories,tags,object_groups' .
',accounts,asset_accounts,expense_accounts,revenue_accounts,liabilities,transactions,withdrawals,deposits,transfers';
return $row;
return [
'objects' => sprintf('min:1|string|in:%s', $valid),
];
}
}

View File

@@ -1,6 +1,7 @@
<?php
/**
* RabobankDescription.php
* ObjectGroupUpdateRequest.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
@@ -18,52 +19,53 @@
* 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\Import\Specifics;
namespace FireflyIII\Api\V1\Requests;
/**
* Class RabobankDescription.
* Class AccountObjectGroupUpdateRequestUpdateRequest
*
* @codeCoverageIgnore
* @deprecated
*/
class RabobankDescription implements SpecificInterface
class ObjectGroupUpdateRequest extends Request
{
/**
* Description of this specific.
* Authorize logged in users.
*
* @return string
* @codeCoverageIgnore
* @return bool
*/
public static function getDescription(): string
public function authorize(): bool
{
return 'import.specific_rabo_descr';
// Only allow authenticated users
return auth()->check();
}
/**
* Name of this specific.
*
* @return string
* @codeCoverageIgnore
* @return array
*/
public static function getName(): string
public function getUpdateData(): array
{
return 'import.specific_rabo_name';
return [
'title' => $this->string('title'),
'order' => $this->integer('order'),
];
}
/**
* Run the specific.
*
* @param array $row
* The rules that the incoming request must be matched against.
*
* @return array
*
*/
public function run(array $row): array
public function rules(): array
{
$row = array_values($row);
$objectGroup = $this->route()->parameter('objectGroup');
return $row;
return [
'title' => sprintf('min:1|uniqueObjectGroup:%d', $objectGroup->id),
'order' => 'numeric',
];
}
}

View File

@@ -63,6 +63,7 @@ class PiggyBankRequest extends Request
'startdate' => $this->date('start_date'),
'targetdate' => $this->date('target_date'),
'notes' => $this->nlString('notes'),
'order' => $this->integer('order'),
];
}
@@ -90,7 +91,7 @@ class PiggyBankRequest extends Request
$piggyBank = $this->route()->parameter('piggyBank');
$rules['name'] = 'between:1,255|uniquePiggyBankForUser:' . $piggyBank->id;
$rules['account_id'] = ['belongsToUser:accounts', new IsAssetAccountId];
$rules['target_amount'] = 'numeric|more:0';
$rules['target_amount'] = 'numeric|gt:0';
$rules['current_amount'] = ['numeric', new ZeroOrMore, new LessThanPiggyTarget];
break;
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* PiggyBankStoreRequest.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\ZeroOrMore;
/**
*
* Class PiggyBankStoreRequest
*
* @codeCoverageIgnore
*/
class PiggyBankStoreRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
return [
'name' => $this->string('name'),
'account_id' => $this->integer('account_id'),
'targetamount' => $this->string('target_amount'),
'current_amount' => $this->string('current_amount'),
'startdate' => $this->date('start_date'),
'targetdate' => $this->date('target_date'),
'notes' => $this->nlString('notes'),
'object_group_id' => $this->integer('object_group_id'),
'object_group' => $this->string('object_group_name'),
];
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'required|between:1,255|uniquePiggyBankForUser',
'current_amount' => ['numeric', new ZeroOrMore, 'lte:target_amount'],
'account_id' => 'required|numeric|belongsToUser:accounts,id',
'object_group_id' => 'numeric|belongsToUser:object_groups,id',
'target_amount' => ['numeric', new ZeroOrMore, 'lte:target_amount', 'required'],
'start_date' => 'date|nullable',
'target_date' => 'date|nullable|after:start_date',
'notes' => 'max:65000',
];
}
}

View File

@@ -104,8 +104,8 @@ class RecurrenceStoreRequest extends Request
'repetitions.*.skip' => 'required|numeric|between:0,31',
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
'transactions.*.description' => 'required|between:1,255',
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.foreign_amount' => 'numeric|more:0',
'transactions.*.amount' => 'required|numeric|gt:0',
'transactions.*.foreign_amount' => 'numeric|gt:0',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',

View File

@@ -107,8 +107,8 @@ class RecurrenceUpdateRequest extends Request
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
'transactions.*.description' => 'required|between:1,255',
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.foreign_amount' => 'numeric|more:0',
'transactions.*.amount' => 'required|numeric|gt:0',
'transactions.*.foreign_amount' => 'numeric|gt:0',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* TransferRequest.php
* Copyright (c) 2019 james@firefly-iii.org
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Search;
use FireflyIII\Api\V1\Requests\Request;
@@ -49,7 +51,7 @@ class TransferRequest extends Request
return [
'source' => ['required', new IsTransferAccount],
'destination' => ['required', new IsTransferAccount],
'amount' => 'required|numeric|more:0',
'amount' => 'required|numeric|gt:0',
'description' => 'required|min:1',
'date' => 'required|date',
];

View File

@@ -98,7 +98,7 @@ class TransactionStoreRequest extends Request
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code|nullable',
// amount
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.amount' => 'required|numeric|gt:0',
'transactions.*.foreign_amount' => 'numeric',
// description
@@ -221,7 +221,7 @@ class TransactionStoreRequest extends Request
// foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string) $object['foreign_currency_id']),
'foreign_currency_code' => $this->stringFromValue($object['foreign_currency_code']),
'foreign_currency_code' => $this->stringFromValue((string) $object['foreign_currency_code']),
// amount and foreign amount. Cannot be 0.
'amount' => $this->stringFromValue((string) $object['amount']),
@@ -232,37 +232,37 @@ class TransactionStoreRequest extends Request
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string) $object['source_id']),
'source_name' => $this->stringFromValue($object['source_name']),
'source_iban' => $this->stringFromValue($object['source_iban']),
'source_number' => $this->stringFromValue($object['source_number']),
'source_bic' => $this->stringFromValue($object['source_bic']),
'source_name' => $this->stringFromValue((string) $object['source_name']),
'source_iban' => $this->stringFromValue((string) $object['source_iban']),
'source_number' => $this->stringFromValue((string) $object['source_number']),
'source_bic' => $this->stringFromValue((string) $object['source_bic']),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string) $object['destination_id']),
'destination_name' => $this->stringFromValue($object['destination_name']),
'destination_iban' => $this->stringFromValue($object['destination_iban']),
'destination_number' => $this->stringFromValue($object['destination_number']),
'destination_bic' => $this->stringFromValue($object['destination_bic']),
'destination_name' => $this->stringFromValue((string) $object['destination_name']),
'destination_iban' => $this->stringFromValue((string) $object['destination_iban']),
'destination_number' => $this->stringFromValue((string) $object['destination_number']),
'destination_bic' => $this->stringFromValue((string) $object['destination_bic']),
// budget info
'budget_id' => $this->integerFromValue((string) $object['budget_id']),
'budget_name' => $this->stringFromValue($object['budget_name']),
'budget_name' => $this->stringFromValue((string) $object['budget_name']),
// category info
'category_id' => $this->integerFromValue((string) $object['category_id']),
'category_name' => $this->stringFromValue($object['category_name']),
'category_name' => $this->stringFromValue((string) $object['category_name']),
// journal bill reference. Optional. Will only work for withdrawals
'bill_id' => $this->integerFromValue((string) $object['bill_id']),
'bill_name' => $this->stringFromValue($object['bill_name']),
'bill_name' => $this->stringFromValue((string) $object['bill_name']),
// piggy bank reference. Optional. Will only work for transfers
'piggy_bank_id' => $this->integerFromValue((string) $object['piggy_bank_id']),
'piggy_bank_name' => $this->stringFromValue($object['piggy_bank_name']),
'piggy_bank_name' => $this->stringFromValue((string) $object['piggy_bank_name']),
// some other interesting properties
'reconciled' => $this->convertBoolean((string) $object['reconciled']),
'notes' => $this->nlStringFromValue($object['notes']),
'notes' => $this->nlStringFromValue((string) $object['notes']),
'tags' => $this->arrayFromValue($object['tags']),
// all custom fields:

View File

@@ -172,7 +172,7 @@ class TransactionUpdateRequest extends Request
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
// amount
'transactions.*.amount' => 'numeric|more:0|max:100000000000',
'transactions.*.amount' => 'numeric|gt:0|max:100000000000',
'transactions.*.foreign_amount' => 'numeric|gte:0',
// description
@@ -263,17 +263,13 @@ class TransactionUpdateRequest extends Request
// TODO if the transaction_journal_id is empty, some fields are mandatory, like the amount!
// all journals must have a description
//$this->validateDescriptions($validator);
// // validate foreign currency info
// $this->validateForeignCurrencyInformation($validator);
//
//
// // make sure all splits have valid source + dest info
// $this->validateSplitAccounts($validator);
// the group must have a description if > 1 journal.
// $this->validateGroupDescription($validator);
}
);
}

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* CorrectOpeningBalanceCurrencies.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\Account;

View File

@@ -0,0 +1,81 @@
<?php
/**
* FixGroupAccounts.php
* Copyright (c) 2020 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\Correction;
use DB;
use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Handlers\Events\UpdatedGroupEventHandler;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
/**
* Class FixGroupAccounts
*/
class FixGroupAccounts extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Unify the source / destination accounts of split groups.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:unify-group-accounts';
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
// select transaction_group_id, count(transaction_group_id) as the_count from transaction_journals group by transaction_group_id having the_count > 1
$groups = [];
$res = TransactionJournal
::groupBy('transaction_group_id')
->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
foreach ($res as $journal) {
if ((int) $journal->the_count > 1) {
$groups[] = (int) $journal->transaction_group_id;
}
}
$handler = new UpdatedGroupEventHandler;
foreach($groups as $groupId) {
$group = TransactionGroup::find($groupId);
$event = new UpdatedTransactionGroup($group);
$handler->unifyAccounts($event);
}
$this->line('Updated inconsistent transaction groups.');
return 0;
}
}

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* FixLongDescriptions.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\TransactionGroup;

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* FixRecurringTransactions.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Models\Recurrence;
@@ -55,9 +57,9 @@ class FixRecurringTransactions extends Command
/**
* Execute the console command.
*
* @return mixed
* @return int
*/
public function handle()
public function handle(): int
{
$start = microtime(true);
$this->stupidLaravel();

View File

@@ -1,6 +1,4 @@
<?php
declare(strict_types=1);
/**
* CreateDatabase.php
@@ -22,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Illuminate\Console\Command;
@@ -50,9 +50,9 @@ class CreateDatabase extends Command
/**
* Execute the console command.
*
* @return mixed
* @return int
*/
public function handle()
public function handle(): int
{
if ('mysql' !== env('DB_CONNECTION')) {
$this->info(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));

View File

@@ -159,7 +159,6 @@ class DecryptDatabase extends Command
if ('The MAC is invalid.' === $e->getMessage()) {
throw new FireflyException($e->getMessage()); // @codeCoverageIgnore
}
Log::debug(sprintf('Could not decrypt. %s', $e->getMessage()));
}
return $value;

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* ExportData.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Export;
use Carbon\Carbon;

View File

@@ -1,76 +0,0 @@
<?php
/**
* CreateCSVImport.php
* Copyright (c) 2020 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/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Import;
use Exception;
use FireflyIII\Console\Commands\VerifiesAccessToken;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Import\Storage\ImportArrayStorage;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Console\Command;
use Log;
/**
* Class CreateCSVImport.
*
* @deprecated
* @codeCoverageIgnore
*/
class CreateCSVImport extends Command
{
use VerifiesAccessToken;
/**
* The console command description.
*
* @var string
*/
protected $description = 'Use this command to create a new CSV file import.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature
= 'firefly-iii:csv-import
{file? : The CSV file to import.}
{configuration? : The configuration file to use for the import.}
{--user=1 : The user ID that the import should import for.}
{--token= : The user\'s access token.}';
/**
* Run the command.
*/
public function handle(): int
{
$this->error('This command is disabled.');
return 1;
}
}

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* RestoreOAuthKeys.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Support\System\OAuthKeys;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
/**
* SetLatestVersion.php
@@ -21,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Illuminate\Console\Command;

View File

@@ -60,7 +60,7 @@ class ApplyRules extends Command
*/
protected $signature
= 'firefly-iii:apply-rules
{--user=1 : The user ID that the import should import for.}
{--user=1 : The user ID.}
{--token= : The user\'s access token.}
{--accounts= : A comma-separated list of asset accounts or liabilities to apply your rules to.}
{--rule_groups= : A comma-separated list of rule groups to apply. Take the ID\'s of these rule groups from the Firefly III interface.}

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* MigrateRecurrenceMeta.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Upgrade;
use FireflyIII\Models\RecurrenceMeta;

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* MigrateTagLocations.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Upgrade;
use FireflyIII\Models\Location;

View File

@@ -211,7 +211,6 @@ class MigrateToRules extends Command
$lang = app('preferences')->getForUser($user, 'language', 'en_US');
$groupTitle = (string) trans('firefly.rulegroup_for_bills_title', [], $lang->data);
$ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle);
//$currency = $this->getCurrency($user);
if (null === $ruleGroup) {
$ruleGroup = $this->ruleGroupRepository->store(

View File

@@ -341,19 +341,17 @@ class TransferCurrenciesCorrections extends Command
if (isset($this->accountCurrencies[$accountId]) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) {
return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore
}
// TODO we can use getAccountCurrency() instead
$currencyId = (int) $this->accountRepos->getMetaValue($account, 'currency_id');
$result = $this->currencyRepos->findNull($currencyId);
if (null === $result) {
$currency = $this->accountRepos->getAccountCurrency($account);
if (null === $currency) {
// @codeCoverageIgnoreStart
$this->accountCurrencies[$accountId] = 0;
return null;
// @codeCoverageIgnoreEnd
}
$this->accountCurrencies[$accountId] = $result;
$this->accountCurrencies[$accountId] = $currency;
return $result;
return $currency;
}
/**

View File

@@ -76,7 +76,7 @@ class UpgradeDatabase extends Command
'firefly-iii:migrate-recurrence-meta',
'firefly-iii:migrate-tag-locations',
// there are 16 verify commands.
// there are 15 verify commands.
'firefly-iii:fix-piggies',
'firefly-iii:create-link-types',
'firefly-iii:create-access-tokens',
@@ -93,6 +93,7 @@ class UpgradeDatabase extends Command
'firefly-iii:fix-ob-currencies',
'firefly-iii:fix-long-descriptions',
'firefly-iii:fix-recurring-transactions',
'firefly-iii:unify-group-accounts',
// two report commands
'firefly-iii:report-empty-objects',

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* DuplicateTransactionException.php
* Copyright (c) 2019 james@firefly-iii.org
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Exceptions;
use Exception;

View File

@@ -38,6 +38,7 @@ use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Log;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
/**
* Class GracefulNotFoundHandler
@@ -53,7 +54,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
* @throws Exception
* @return mixed
*/
public function render($request, Exception $exception)
public function render($request, Throwable $exception)
{
$route = $request->route();
if (null === $route) {
@@ -136,12 +137,12 @@ class GracefulNotFoundHandler extends ExceptionHandler
/**
* @param Request $request
* @param Exception $exception
* @param Throwable $exception
*
* @throws Exception
* @return Redirector|Response
*/
private function handleAccount(Request $request, Exception $exception)
private function handleAccount(Request $request, Throwable $exception)
{
Log::debug('404 page is probably a deleted account. Redirect to overview of account types.');
/** @var User $user */
@@ -164,12 +165,12 @@ class GracefulNotFoundHandler extends ExceptionHandler
/**
* @param Request $request
* @param Exception $exception
* @param Throwable $exception
*
* @throws Exception
* @return RedirectResponse|Redirector|Response
*/
private function handleAttachment(Request $request, Exception $exception)
private function handleAttachment(Request $request, Throwable $exception)
{
Log::debug('404 page is probably a deleted attachment. Redirect to parent object.');
/** @var User $user */
@@ -208,13 +209,13 @@ class GracefulNotFoundHandler extends ExceptionHandler
}
/**
* @param Request $request
* @param Throwable $request
* @param Exception $exception
*
* @throws Exception
* @return RedirectResponse|\Illuminate\Http\Response|Redirector|Response
*/
private function handleGroup(Request $request, Exception $exception)
private function handleGroup(Request $request, Throwable $exception)
{
Log::debug('404 page is probably a deleted group. Redirect to overview of group types.');
/** @var User $user */

View File

@@ -33,9 +33,9 @@ use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException as LaravelValidationException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Http\Request;
use Throwable;
/**
* Class Handler
*
@@ -51,7 +51,7 @@ class Handler extends ExceptionHandler
*
* @return mixed
*/
public function render($request, Exception $exception)
public function render($request, Throwable $exception)
{
if ($exception instanceof LaravelValidationException && $request->expectsJson()) {
// ignore it: controller will handle it.
@@ -119,7 +119,7 @@ class Handler extends ExceptionHandler
*
* @return void
*/
public function report(Exception $exception)
public function report(Throwable $exception)
{
$doMailError = config('firefly.send_error_message');
// if the user wants us to mail:
@@ -143,13 +143,13 @@ class Handler extends ExceptionHandler
'line' => $exception->getLine(),
'code' => $exception->getCode(),
'version' => config('firefly.version'),
'url' => Request::fullUrl(),
'userAgent' => Request::userAgent(),
'json' => Request::acceptsJson(),
'url' => request()->fullUrl(),
'userAgent' => request()->userAgent(),
'json' => request()->acceptsJson(),
];
// create job that will mail.
$ipAddress = Request::ip() ?? '0.0.0.0';
$ipAddress = request()->ip() ?? '0.0.0.0';
$job = new MailError($userData, (string) config('firefly.site_owner'), $ipAddress, $data);
dispatch($job);
}

View File

@@ -27,6 +27,7 @@ namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
use FireflyIII\Services\Internal\Support\BillServiceTrait;
use FireflyIII\User;
use Illuminate\Database\QueryException;
@@ -37,7 +38,7 @@ use Log;
*/
class BillFactory
{
use BillServiceTrait;
use BillServiceTrait, CreatesObjectGroups;
/** @var User */
private $user;
@@ -97,6 +98,24 @@ class BillFactory
$this->updateNote($bill, $data['notes']);
}
$objectGroupTitle = $data['object_group'] ?? '';
if ('' !== $objectGroupTitle) {
$objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle);
if (null !== $objectGroup) {
$bill->objectGroups()->sync([$objectGroup->id]);
$bill->save();
}
}
// try also with ID:
$objectGroupId = (int) ($data['object_group_id'] ?? 0);
if (0 !== $objectGroupId) {
$objectGroup = $this->findObjectGroupById($objectGroupId);
if (null !== $objectGroup) {
$bill->objectGroups()->sync([$objectGroup->id]);
$bill->save();
}
}
return $bill;
}

View File

@@ -367,19 +367,6 @@ class TransactionJournalFactory
// verify that journal has two transactions. Otherwise, delete and cancel.
// TODO this can't be faked so it can't be tested.
// $count = $journal->transactions()->count();
// if (2 !== $count) {
// // @codeCoverageIgnoreStart
// Log::error(sprintf('The journal unexpectedly has %d transaction(s). This is not OK. Cancel operation.', $count));
// try {
// $journal->delete();
// } catch (Exception $e) {
// Log::debug(sprintf('Dont care: %s.', $e->getMessage()));
// }
//
// return null;
// // @codeCoverageIgnoreEnd
// }
$journal->completed = true;
$journal->save();

View File

@@ -23,14 +23,58 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\TransactionRules\Engine\RuleEngine;
use Log;
/**
* Class UpdatedGroupEventHandler
*/
class UpdatedGroupEventHandler
{
/**
* This method will make sure all source / destination accounts are the same.
*
* @param UpdatedTransactionGroup $updatedGroupEvent
*/
public function unifyAccounts(UpdatedTransactionGroup $updatedGroupEvent): void
{
$group = $updatedGroupEvent->transactionGroup;
if (1 === $group->transactionJournals->count()) {
return;
}
Log::debug(sprintf('Correct inconsistent accounts in group #%d', $group->id));
// first journal:
/** @var TransactionJournal $first */
$first = $group->transactionJournals()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->first();
$all = $group->transactionJournals()->get()->pluck('id')->toArray();
/** @var Account $sourceAccount */
$sourceAccount = $first->transactions()->where('amount', '<', '0')->first()->account;
/** @var Account $destAccount */
$destAccount = $first->transactions()->where('amount', '>', '0')->first()->account;
$type = $first->transactionType->type;
if (TransactionType::TRANSFER === $type || TransactionType::WITHDRAWAL === $type) {
// set all source transactions to source account:
Transaction::whereIn('transaction_journal_id', $all)
->where('amount', '<', 0)->update(['account_id' => $sourceAccount->id]);
}
if (TransactionType::TRANSFER === $type || TransactionType::DEPOSIT === $type) {
// set all destination transactions to destination account:
Transaction::whereIn('transaction_journal_id', $all)
->where('amount', '>', 0)->update(['account_id' => $destAccount->id]);
}
}
/**
* This method will check all the rules when a journal is updated.
*

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* AccountCollection.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Helpers\Collector\Extensions;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* AmountCollection.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Helpers\Collector\Extensions;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* CollectorProperties.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Helpers\Collector\Extensions;
use FireflyIII\User;
@@ -36,6 +38,8 @@ trait CollectorProperties
private $hasAccountInfo;
/** @var bool Will be true if query result includes bill information. */
private $hasBillInformation;
/** @var bool */
private $hasNotesInformation;
/** @var bool Will be true if query result contains budget info. */
private $hasBudgetInformation;
/** @var bool Will be true if query result contains category info. */
@@ -56,4 +60,6 @@ trait CollectorProperties
private $total;
/** @var User The user object. */
private $user;
private bool $hasJoinedMetaTables;
}

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* MetaCollection.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Helpers\Collector\Extensions;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
@@ -28,6 +30,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
@@ -36,6 +39,27 @@ use Illuminate\Support\Collection;
trait MetaCollection
{
/**
* @inheritDoc
*/
public function withNotes(): GroupCollectorInterface
{
if (false === $this->hasNotesInformation) {
// join bill table
$this->query->leftJoin(
'notes',
static function (JoinClause $join) {
$join->on('notes.noteable_id', '=', 'transaction_journals.id');
$join->where('notes.noteable_type', '=', 'FireflyIII\Models\TransactionJournal');
}
);
// add fields
$this->fields[] = 'notes.text as notes';
$this->hasNotesInformation = true;
}
return $this;
}
/**
* Limit the search to a specific bill.
@@ -288,4 +312,36 @@ trait MetaCollection
}
/**
* @inheritDoc
*/
public function setExternalId(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'external_id');
$this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $externalId));
return $this;
}
/**
* @inheritDoc
*/
public function setInternalReference(string $internalReference): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'internal_reference');
$this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $internalReference));
return $this;
}
}

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
/**
* TimeCollection.php
* Copyright (c) 2020 james@firefly-iii.org
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Helpers\Collector\Extensions;
use Carbon\Carbon;

View File

@@ -62,8 +62,10 @@ class GroupCollector implements GroupCollectorInterface
$this->hasCatInformation = false;
$this->hasBudgetInformation = false;
$this->hasBillInformation = false;
$this->hasNotesInformation = false;
$this->hasJoinedTagTables = false;
$this->hasJoinedAttTables = false;
$this->hasJoinedMetaTables = false;
$this->integerFields = [
'transaction_group_id',
'user_id',
@@ -166,13 +168,8 @@ class GroupCollector implements GroupCollectorInterface
*/
public function getGroups(): Collection
{
//$start = microtime(true);
/** @var Collection $result */
$result = $this->query->get($this->fields);
//$end = round(microtime(true) - $start, 5);
// log info about query time.
//Log::info(sprintf('Query took Firefly III %s seconds', $end));
//Log::info($this->query->toSql(), $this->query->getBindings());
// now to parse this into an array.
$collection = $this->parseArray($result);
@@ -311,7 +308,7 @@ class GroupCollector implements GroupCollectorInterface
$this->query->where(
static function (EloquentBuilder $q) use ($array) {
$q->where(
function (EloquentBuilder $q1) use ($array) {
static function (EloquentBuilder $q1) use ($array) {
foreach ($array as $word) {
$keyword = sprintf('%%%s%%', $word);
$q1->where('transaction_journals.description', 'LIKE', $keyword);
@@ -552,9 +549,14 @@ class GroupCollector implements GroupCollectorInterface
$result['tags'] = [];
$result['attachments'] = [];
try {
$result['date'] = new Carbon($result['date']);
$result['created_at'] = new Carbon($result['created_at']);
$result['updated_at'] = new Carbon($result['updated_at']);
$result['date'] = new Carbon($result['date'], 'UTC');
$result['created_at'] = new Carbon($result['created_at'], 'UTC');
$result['updated_at'] = new Carbon($result['updated_at'], 'UTC');
// this is going to happen a lot:
$result['date']->setTimezone(config('app.timezone'));
$result['created_at']->setTimezone(config('app.timezone'));
$result['updated_at']->setTimezone(config('app.timezone'));
} catch (Exception $e) {
Log::error($e->getMessage());
}

View File

@@ -398,6 +398,13 @@ interface GroupCollectorInterface
*/
public function withCategoryInformation(): GroupCollectorInterface;
/**
* Will include notes.
*
* @return GroupCollectorInterface
*/
public function withNotes(): GroupCollectorInterface;
/**
* Add tag info.
*
@@ -419,4 +426,22 @@ interface GroupCollectorInterface
*/
public function withoutCategory(): GroupCollectorInterface;
/**
* Look for specific external ID's.
*
* @param string $externalId
*
* @return GroupCollectorInterface
*/
public function setExternalId(string $externalId): GroupCollectorInterface;
/**
* Look for specific external ID's.
*
* @param string $externalId
*
* @return GroupCollectorInterface
*/
public function setInternalReference(string $externalId): GroupCollectorInterface;
}

View File

@@ -77,7 +77,7 @@ class Help implements HelpInterface
*/
public function getFromGitHub(string $route, string $language): string
{
$uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route);
$uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/main/%s/%s.md', $language, $route);
Log::debug(sprintf('Trying to get %s...', $uri));
$opt = ['headers' => ['User-Agent' => $this->userAgent]];
$content = '';

View File

@@ -43,8 +43,9 @@ trait UpdateTrait
{
Log::debug('Now in getLatestRelease()');
/** @var UpdateRequestInterface $checker */
$checker = app(UpdateRequestInterface::class);
$channel = app('fireflyconfig')->get('update_channel', 'stable')->data;
$checker = app(UpdateRequestInterface::class);
$channelConfig = app('fireflyconfig')->get('update_channel', 'stable');
$channel = $channelConfig ? $channelConfig->data : 'stable';
return $checker->getUpdateInformation($channel);
}

View File

@@ -49,8 +49,7 @@ class EditController extends Controller
/** @var AccountRepositoryInterface The account repository */
private $repository;
/** @var AttachmentHelperInterface Helper for attachments. */
private $attachments;
private AttachmentHelperInterface $attachments;
/**
* EditController constructor.
@@ -67,7 +66,7 @@ class EditController extends Controller
$this->repository = app(AccountRepositoryInterface::class);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
return $next($request);
}

View File

@@ -101,7 +101,7 @@ class IndexController extends Controller
$account->startBalance = $this->isInArray($startBalances, $account->id);
$account->endBalance = $this->isInArray($endBalances, $account->id);
$account->difference = bcsub($account->endBalance, $account->startBalance);
$account->interest = round($this->repository->getMetaValue($account, 'interest'), 6);
$account->interest = number_format((float) $this->repository->getMetaValue($account, 'interest'), 6, '.', '');
$account->interestPeriod = (string) trans(sprintf('firefly.interest_calc_%s', $this->repository->getMetaValue($account, 'interest_period')));
$account->accountTypeString = (string) trans(sprintf('firefly.account_type_%s', $account->accountType->type));
}
@@ -115,6 +115,14 @@ class IndexController extends Controller
}
/**
* @return \Illuminate\Contracts\Foundation\Application|Factory|View
*/
public function emptyIndex(?string $objectType = null)
{
return view('accounts.empty-index', compact('objectType'));
}
/**
* Show list of accounts.
*
@@ -125,6 +133,11 @@ class IndexController extends Controller
*/
public function index(Request $request, string $objectType)
{
// temp catch for layout.
if ('v2' === config('firefly.layout')) {
return $this->emptyIndex($objectType);
}
$objectType = $objectType ?? 'asset';
$subTitle = (string) trans(sprintf('firefly.%s_accounts', $objectType));
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
@@ -155,7 +168,7 @@ class IndexController extends Controller
$account->startBalance = $this->isInArray($startBalances, $account->id);
$account->endBalance = $this->isInArray($endBalances, $account->id);
$account->difference = bcsub($account->endBalance, $account->startBalance);
$account->interest = round($this->repository->getMetaValue($account, 'interest'), 6);
$account->interest = number_format((float) $this->repository->getMetaValue($account, 'interest'), 6, '.', '');
$account->interestPeriod = (string) trans(sprintf('firefly.interest_calc_%s', $this->repository->getMetaValue($account, 'interest_period')));
$account->accountTypeString = (string) trans(sprintf('firefly.account_type_%s', $account->accountType->type));
$account->location = $this->repository->getLocation($account);

View File

@@ -129,9 +129,8 @@ class ReconcileController extends Controller
$startDate = clone $start;
$startDate->subDay();
$startBalance = round(app('steam')->balance($account, $startDate), $currency->decimal_places);
$endBalance = round(app('steam')->balance($account, $end), $currency->decimal_places);
$startBalance = number_format((float) app('steam')->balance($account, $startDate), $currency->decimal_places, '.', '');
$endBalance = number_format((float) app('steam')->balance($account, $end), $currency->decimal_places, '.', '');
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
$subTitle = (string) trans('firefly.reconcile_account', ['account' => $account->name]);

View File

@@ -46,10 +46,8 @@ class ShowController extends Controller
{
use UserNavigation, PeriodOverview;
/** @var CurrencyRepositoryInterface The currency repository */
private $currencyRepos;
/** @var AccountRepositoryInterface The account repository */
private $repository;
private CurrencyRepositoryInterface $currencyRepos;
private AccountRepositoryInterface $repository;
/**
* ShowController constructor.
@@ -77,6 +75,7 @@ class ShowController extends Controller
}
/**
* Show an account.
*
@@ -91,6 +90,13 @@ class ShowController extends Controller
*/
public function show(Request $request, Account $account, Carbon $start = null, Carbon $end = null)
{
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
// temp catch for layout.
if ('v2' === config('firefly.layout')) {
return view('accounts.empty-index', compact('objectType'));
}
if (!$this->isEditableAccount($account)) {
return $this->redirectAccountToAccount($account); // @codeCoverageIgnore
}
@@ -105,7 +111,6 @@ class ShowController extends Controller
}
$location = $this->repository->getLocation($account);
$attachments = $this->repository->getAttachments($account);
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
$today = new Carbon;
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
$page = (int) $request->get('page');

View File

@@ -25,7 +25,6 @@ namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use FireflyIII\Http\Requests\ConfigurationRequest;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
@@ -55,7 +54,6 @@ class ConfigurationController extends Controller
}
);
$this->middleware(IsDemoUser::class)->except(['index']);
$this->middleware(IsSandStormUser::class);
}
/**

View File

@@ -25,7 +25,6 @@ namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Events\AdminRequestedTestMessage;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
@@ -48,7 +47,6 @@ class HomeController extends Controller
{
parent::__construct();
$this->middleware(IsDemoUser::class)->except(['index']);
$this->middleware(IsSandStormUser::class)->except(['index']);
}
/**
@@ -61,9 +59,8 @@ class HomeController extends Controller
Log::channel('audit')->info('User visits admin index.');
$title = (string) trans('firefly.administration');
$mainTitleIcon = 'fa-hand-spock-o';
$sandstorm = 1 === (int) getenv('SANDSTORM');
return view('admin.index', compact('title', 'mainTitleIcon', 'sandstorm'));
return view('admin.index', compact('title', 'mainTitleIcon'));
}
/**

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* TelemetryController.php
* Copyright (c) 2020 thegrumpydictator@gmail.com
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Admin;
use Carbon\Carbon;

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Helpers\Update\UpdateTrait;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -57,7 +56,6 @@ class UpdateController extends Controller
}
);
$this->middleware(IsDemoUser::class)->except(['index']);
$this->middleware(IsSandStormUser::class)->except(['index']);
}
/**

View File

@@ -24,7 +24,6 @@ namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use FireflyIII\Http\Requests\UserFormRequest;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
@@ -57,7 +56,6 @@ class UserController extends Controller
}
);
$this->middleware(IsDemoUser::class)->except(['index', 'show']);
$this->middleware(IsSandStormUser::class);
}
/**

View File

@@ -1,6 +1,4 @@
<?php
declare(strict_types=1);
/**
* ConfirmPasswordController.php
@@ -22,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller;

View File

@@ -176,4 +176,35 @@ class LoginController extends Controller
throw $exception;
}
/**
* Log the user out of the application.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$authGuard = config('firefly.authentication_guard');
$logoutUri = config('firefly.custom_logout_uri');
if ('remote_user_guard' === $authGuard && '' !== $logoutUri) {
return redirect($logoutUri);
}
$this->guard()->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
if ($response = $this->loggedOut($request)) {
return $response;
}
return $request->wantsJson()
? new \Illuminate\Http\Response('', 204)
: redirect('/');
}
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* CreateController.php
* Copyright (c) 2020 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\Http\Controllers\Bill;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\BillStoreRequest;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
/**
* Class CreateController
*/
class CreateController extends Controller
{
private AttachmentHelperInterface $attachments;
private BillRepositoryInterface $repository;
/**
* BillController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.bills'));
app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->attachments = app(AttachmentHelperInterface::class);
$this->repository = app(BillRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Create a new bill.
*
* @param Request $request
*
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function create(Request $request)
{
$periods = [];
/** @var array $billPeriods */
$billPeriods = config('firefly.bill_periods');
foreach ($billPeriods as $current) {
$periods[$current] = strtolower((string) trans('firefly.repeat_freq_' . $current));
}
$subTitle = (string) trans('firefly.create_new_bill');
$defaultCurrency = app('amount')->getDefaultCurrency();
// put previous url in session if not redirect from store (not "create another").
if (true !== session('bills.create.fromStore')) {
$this->rememberPreviousUri('bills.create.uri');
}
$request->session()->forget('bills.create.fromStore');
return view('bills.create', compact('periods', 'subTitle', 'defaultCurrency'));
}
/**
* Store a new bill.
*
* @param BillStoreRequest $request
*
* @return RedirectResponse
*
*/
public function store(BillStoreRequest $request): RedirectResponse
{
$billData = $request->getBillData();
$billData['active'] = true;
try {
$bill = $this->repository->store($billData);
} catch (FireflyException $e) {
Log::error($e->getMessage());
$request->session()->flash('error', (string) trans('firefly.bill_store_error'));
return redirect(route('bills.create'))->withInput();
}
$request->session()->flash('success', (string) trans('firefly.stored_new_bill', ['name' => $bill->name]));
app('preferences')->mark();
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($bill, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
}
return redirect(route('rules.create-from-bill', [$bill->id]));
}
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* DeleteController.php
* Copyright (c) 2020 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\Http\Controllers\Bill;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;
/**
* Class DeleteController
*/
class DeleteController extends Controller
{
private BillRepositoryInterface $repository;
/**
* BillController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
app('view')->share('showBudget', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.bills'));
app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->repository = app(BillRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Delete a bill.
*
* @param Bill $bill
*
* @return Factory|View
*/
public function delete(Bill $bill)
{
// put previous url in session
$this->rememberPreviousUri('bills.delete.uri');
$subTitle = (string) trans('firefly.delete_bill', ['name' => $bill->name]);
return view('bills.delete', compact('bill', 'subTitle'));
}
/**
* Destroy a bill.
*
* @param Request $request
* @param Bill $bill
*
* @return RedirectResponse|Redirector
*/
public function destroy(Request $request, Bill $bill)
{
$name = $bill->name;
$this->repository->destroy($bill);
$request->session()->flash('success', (string) trans('firefly.deleted_bill', ['name' => $name]));
app('preferences')->mark();
return redirect($this->getPreviousUri('bills.delete.uri'));
}
}

View File

@@ -0,0 +1,155 @@
<?php
/**
* EditController.php
* Copyright (c) 2020 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\Http\Controllers\Bill;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\BillUpdateRequest;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
/**
* Class EditController
*/
class EditController extends Controller
{
private AttachmentHelperInterface $attachments;
private BillRepositoryInterface $repository;
/**
* BillController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.bills'));
app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->attachments = app(AttachmentHelperInterface::class);
$this->repository = app(BillRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Edit a bill.
*
* @param Request $request
* @param Bill $bill
*
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function edit(Request $request, Bill $bill)
{
$periods = [];
/** @var array $billPeriods */
$billPeriods = config('firefly.bill_periods');
foreach ($billPeriods as $current) {
$periods[$current] = (string) trans('firefly.' . $current);
}
$subTitle = (string) trans('firefly.edit_bill', ['name' => $bill->name]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('bills.edit.fromUpdate')) {
$this->rememberPreviousUri('bills.edit.uri');
}
$currency = app('amount')->getDefaultCurrency();
$bill->amount_min = round((float) $bill->amount_min, $currency->decimal_places);
$bill->amount_max = round((float) $bill->amount_max, $currency->decimal_places);
$rules = $this->repository->getRulesForBill($bill);
$defaultCurrency = app('amount')->getDefaultCurrency();
// code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token');
$preFilled = [
'notes' => $this->repository->getNoteText($bill),
'transaction_currency_id' => $bill->transaction_currency_id,
'active' => $hasOldInput ? (bool) $request->old('active') : $bill->active,
'object_group' => $bill->objectGroups->first() ? $bill->objectGroups->first()->title : '',
];
$request->session()->flash('preFilled', $preFilled);
$request->session()->forget('bills.edit.fromUpdate');
return view('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'defaultCurrency', 'preFilled'));
}
/**
* Update a bill.
*
* @param BillUpdateRequest $request
* @param Bill $bill
*
* @return RedirectResponse
*/
public function update(BillUpdateRequest $request, Bill $bill): RedirectResponse
{
$billData = $request->getBillData();
$bill = $this->repository->update($bill, $billData);
$request->session()->flash('success', (string) trans('firefly.updated_bill', ['name' => $bill->name]));
app('preferences')->mark();
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($bill, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
// flash messages
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
}
$redirect = redirect($this->getPreviousUri('bills.edit.uri'));
if (1 === (int) $request->get('return_to_edit')) {
// @codeCoverageIgnoreStart
$request->session()->put('bills.edit.fromUpdate', true);
$redirect = redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
}
return $redirect;
}
}

View File

@@ -0,0 +1,253 @@
<?php
/**
* IndexController.php
* Copyright (c) 2020 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\Http\Controllers\Bill;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\ObjectGroup\OrganisesObjectGroups;
use FireflyIII\Transformers\BillTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Log;
use Symfony\Component\HttpFoundation\ParameterBag;
/**
* Class IndexController
*/
class IndexController extends Controller
{
use OrganisesObjectGroups;
private BillRepositoryInterface $repository;
/**
* BillController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.bills'));
app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->repository = app(BillRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Show all bills.
*/
public function index()
{
$this->cleanupObjectGroups();
$this->repository->correctOrder();
$start = session('start');
$end = session('end');
$collection = $this->repository->getBills();
$total = $collection->count();
$defaultCurrency = app('amount')->getDefaultCurrency();
$parameters = new ParameterBag;
$parameters->set('start', $start);
$parameters->set('end', $end);
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);
$transformer->setParameters($parameters);
// loop all bills, convert to array and add rules and stuff.
$rules = $this->repository->getRulesForBills($collection);
// make bill groups:
$bills = [
0 => [ // the index is the order, not the ID.
'object_group_id' => 0,
'object_group_title' => (string) trans('firefly.default_group_title_name'),
'bills' => [],
],
];
/** @var Bill $bill */
foreach ($collection as $bill) {
$array = $transformer->transform($bill);
$groupOrder = (int) $array['object_group_order'];
// make group array if necessary:
$bills[$groupOrder] = $bills[$groupOrder] ?? [
'object_group_id' => $array['object_group_id'],
'object_group_title' => $array['object_group_title'],
'bills' => [],
];
// expected today? default:
$array['next_expected_match_diff'] = trans('firefly.not_expected_period');
$nextExpectedMatch = new Carbon($array['next_expected_match']);
if ($nextExpectedMatch->isToday()) {
$array['next_expected_match_diff'] = trans('firefly.today');
}
$current = $array['pay_dates'][0] ?? null;
if (null !== $current && !$nextExpectedMatch->isToday()) {
$currentExpectedMatch = Carbon::createFromFormat('!Y-m-d', $current);
$array['next_expected_match_diff'] = $currentExpectedMatch->diffForHumans(today(), Carbon::DIFF_RELATIVE_TO_NOW);
}
$currency = $bill->transactionCurrency ?? $defaultCurrency;
$array['currency_id'] = $currency->id;
$array['currency_name'] = $currency->name;
$array['currency_symbol'] = $currency->symbol;
$array['currency_code'] = $currency->code;
$array['currency_decimal_places'] = $currency->decimal_places;
$array['attachments'] = $this->repository->getAttachments($bill);
$array['rules'] = $rules[$bill['id']] ?? [];
$bills[$groupOrder]['bills'][] = $array;
}
// order by key
ksort($bills);
// summarise per currency / per group.
$sums = $this->getSums($bills);
return view('bills.index', compact('bills', 'sums', 'total'));
}
/**
* @param array $bills
*
* @return array
*/
private function getSums(array $bills): array
{
$sums = [];
$range = app('preferences')->get('viewRange', '1M')->data;
/** @var array $group */
foreach ($bills as $groupOrder => $group) {
/** @var array $bill */
foreach ($group['bills'] as $bill) {
if (false === $bill['active']) {
continue;
}
/** @var TransactionCurrency $currency */
$currencyId = $bill['currency_id'];
$sums[$groupOrder][$currencyId] = $sums[$groupOrder][$currencyId] ?? [
'currency_id' => $currencyId,
'currency_code' => $bill['currency_code'],
'currency_name' => $bill['currency_name'],
'currency_symbol' => $bill['currency_symbol'],
'currency_decimal_places' => $bill['currency_decimal_places'],
'avg' => '0',
'period' => $range,
'per_period' => '0',
];
// only fill in avg when bill is active.
if (count($bill['pay_dates']) > 0) {
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
$avg = bcmul($avg, (string) count($bill['pay_dates']));
$sums[$groupOrder][$currencyId]['avg'] = bcadd($sums[$groupOrder][$currencyId]['avg'], $avg);
}
// fill in per period regardless:
$sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $this->amountPerPeriod($bill, $range));
}
}
return $sums;
}
/**
* @param array $bill
* @param string $range
*
* @return string
*/
private function amountPerPeriod(array $bill, string $range): string
{
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
Log::debug(sprintf('Amount per period for bill #%d "%s"', $bill['id'], $bill['name']));
Log::debug(sprintf(sprintf('Average is %s', $avg)));
// calculate amount per year:
$multiplies = [
'yearly' => '1',
'half-year' => '2',
'quarterly' => '4',
'monthly' => '12',
'weekly' => '52.17',
];
$yearAmount = bcmul($avg, $multiplies[$bill['repeat_freq']]);
Log::debug(sprintf('Amount per year is %s (%s * %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']]));
// per period:
$division = [
'1Y' => '1',
'6M' => '2',
'3M' => '4',
'1M' => '12',
'1W' => '52.16',
'1D' => '365.24',
];
$perPeriod = bcdiv($yearAmount, $division[$range]);
Log::debug(sprintf('Amount per %s is %s (%s / %s)', $range, $perPeriod, $yearAmount, $division[$range]));
return $perPeriod;
}
/**
* Set the order of a bill.
*
* @param Request $request
* @param Bill $bill
*
* @return JsonResponse
*/
public function setOrder(Request $request, Bill $bill): JsonResponse
{
$objectGroupTitle = (string)$request->get('objectGroupTitle');
$newOrder = (int) $request->get('order');
$this->repository->setOrder($bill, $newOrder);
if ('' !== $objectGroupTitle) {
$this->repository->setObjectGroup($bill, $objectGroupTitle);
}
if ('' === $objectGroupTitle) {
$this->repository->removeObjectGroup($bill);
}
return response()->json(['data' => 'OK']);
}
}

View File

@@ -0,0 +1,195 @@
<?php
/**
* ShowController.php
* Copyright (c) 2020 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\Http\Controllers\Bill;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\Transformers\BillTransformer;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Collection;
use Illuminate\View\View;
use League\Fractal\Manager;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\DataArraySerializer;
use Symfony\Component\HttpFoundation\ParameterBag;
/**
* Class ShowController
*/
class ShowController extends Controller
{
private BillRepositoryInterface $repository;
/**
* BillController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
app('view')->share('showBudget', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.bills'));
app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->repository = app(BillRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Rescan bills for transactions.
*
* @param Request $request
* @param Bill $bill
*
* @throws FireflyException
* @return RedirectResponse|Redirector
*/
public function rescan(Request $request, Bill $bill)
{
$total = 0;
if (false === $bill->active) {
$request->session()->flash('warning', (string) trans('firefly.cannot_scan_inactive_bill'));
return redirect(route('bills.show', [$bill->id]));
}
$set = new Collection;
if (true === $bill->active) {
$set = $this->repository->getRulesForBill($bill);
$total = 0;
}
if (0 === $set->count()) {
$request->session()->flash('error', (string) trans('firefly.no_rules_for_bill'));
return redirect(route('bills.show', [$bill->id]));
}
// unlink all journals:
$this->repository->unlinkAll($bill);
foreach ($set as $rule) {
// simply fire off all rules?
/** @var TransactionMatcher $matcher */
$matcher = app(TransactionMatcher::class);
$matcher->setSearchLimit(100000); // large upper limit
$matcher->setTriggeredLimit(100000); // large upper limit
$matcher->setRule($rule);
$matchingTransactions = $matcher->findTransactionsByRule();
$total += count($matchingTransactions);
$this->repository->linkCollectionToBill($bill, $matchingTransactions);
}
$request->session()->flash('success', (string) trans_choice('firefly.rescanned_bill', $total));
app('preferences')->mark();
return redirect(route('bills.show', [$bill->id]));
}
/**
* Show a bill.
*
* @param Request $request
* @param Bill $bill
*
* @return Factory|View
*/
public function show(Request $request, Bill $bill)
{
// add info about rules:
$rules = $this->repository->getRulesForBill($bill);
$subTitle = $bill->name;
/** @var Carbon $start */
$start = session('start');
/** @var Carbon $end */
$end = session('end');
$year = $start->year;
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$yearAverage = $this->repository->getYearAverage($bill, $start);
$overallAverage = $this->repository->getOverallAverage($bill);
$manager = new Manager();
$manager->setSerializer(new DataArraySerializer());
$manager->parseIncludes(['attachments', 'notes']);
// Make a resource out of the data and
$parameters = new ParameterBag();
$parameters->set('start', $start);
$parameters->set('end', $end);
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);
$transformer->setParameters($parameters);
$resource = new Item($bill, $transformer, 'bill');
$object = $manager->createData($resource)->toArray();
$object['data']['currency'] = $bill->transactionCurrency;
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setBill($bill)->setLimit($pageSize)->setPage($page)->withBudgetInformation()
->withCategoryInformation()->withAccountInformation();
$groups = $collector->getPaginatedGroups();
$groups->setPath(route('bills.show', [$bill->id]));
// transform any attachments as well.
$collection = $this->repository->getAttachments($bill);
$attachments = new Collection;
// @codeCoverageIgnoreStart
if ($collection->count() > 0) {
/** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class);
$attachments = $collection->each(
static function (Attachment $attachment) use ($transformer) {
return $transformer->transform($attachment);
}
);
}
// @codeCoverageIgnoreEnd
return view('bills.show', compact('attachments', 'groups', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle'));
}
}

View File

@@ -1,477 +0,0 @@
<?php
/**
* BillController.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\Transformers\BillTransformer;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Collection;
use Illuminate\View\View;
use League\Fractal\Manager;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\DataArraySerializer;
use Log;
use Symfony\Component\HttpFoundation\ParameterBag;
/**
* Class BillController.
*
*/
class BillController extends Controller
{
/** @var AttachmentHelperInterface Helper for attachments. */
private $attachments;
/** @var BillRepositoryInterface Bill repository */
private $billRepository;
/**
* BillController constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
app('view')->share('showBudget', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.bills'));
app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->attachments = app(AttachmentHelperInterface::class);
$this->billRepository = app(BillRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Create a new bill.
*
* @param Request $request
*
* @return Factory|View
*/
public function create(Request $request)
{
$periods = [];
/** @var array $billPeriods */
$billPeriods = config('firefly.bill_periods');
foreach ($billPeriods as $current) {
$periods[$current] = strtolower((string) trans('firefly.repeat_freq_' . $current));
}
$subTitle = (string) trans('firefly.create_new_bill');
$defaultCurrency = app('amount')->getDefaultCurrency();
// put previous url in session if not redirect from store (not "create another").
if (true !== session('bills.create.fromStore')) {
$this->rememberPreviousUri('bills.create.uri');
}
$request->session()->forget('bills.create.fromStore');
return view('bills.create', compact('periods', 'subTitle', 'defaultCurrency'));
}
/**
* Delete a bill.
*
* @param Bill $bill
*
* @return Factory|View
*/
public function delete(Bill $bill)
{
// put previous url in session
$this->rememberPreviousUri('bills.delete.uri');
$subTitle = (string) trans('firefly.delete_bill', ['name' => $bill->name]);
return view('bills.delete', compact('bill', 'subTitle'));
}
/**
* Destroy a bill.
*
* @param Request $request
* @param Bill $bill
*
* @return RedirectResponse|Redirector
*/
public function destroy(Request $request, Bill $bill)
{
$name = $bill->name;
$this->billRepository->destroy($bill);
$request->session()->flash('success', (string) trans('firefly.deleted_bill', ['name' => $name]));
app('preferences')->mark();
return redirect($this->getPreviousUri('bills.delete.uri'));
}
/**
* Edit a bill.
*
* @param Request $request
* @param Bill $bill
*
* @return Factory|View
*/
public function edit(Request $request, Bill $bill)
{
$periods = [];
/** @var array $billPeriods */
$billPeriods = config('firefly.bill_periods');
foreach ($billPeriods as $current) {
$periods[$current] = (string) trans('firefly.' . $current);
}
$subTitle = (string) trans('firefly.edit_bill', ['name' => $bill->name]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('bills.edit.fromUpdate')) {
$this->rememberPreviousUri('bills.edit.uri');
}
$currency = app('amount')->getDefaultCurrency();
$bill->amount_min = round((float) $bill->amount_min, $currency->decimal_places);
$bill->amount_max = round((float) $bill->amount_max, $currency->decimal_places);
$rules = $this->billRepository->getRulesForBill($bill);
$defaultCurrency = app('amount')->getDefaultCurrency();
// code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token');
$preFilled = [
'notes' => $this->billRepository->getNoteText($bill),
'transaction_currency_id' => $bill->transaction_currency_id,
'active' => $hasOldInput ? (bool) $request->old('active') : $bill->active,
];
$request->session()->flash('preFilled', $preFilled);
$request->session()->forget('bills.edit.fromUpdate');
return view('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'defaultCurrency', 'preFilled'));
}
/**
* Show all bills.
*
* @return Factory|View
*/
public function index()
{
$start = session('start');
$end = session('end');
$unfiltered = $this->billRepository->getBills();
$defaultCurrency = app('amount')->getDefaultCurrency();
$parameters = new ParameterBag();
$parameters->set('start', $start);
$parameters->set('end', $end);
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);
$transformer->setParameters($parameters);
/** @var Collection $bills */
$bills = $unfiltered->map(
function (Bill $bill) use ($transformer, $defaultCurrency) {
$return = $transformer->transform($bill);
$currency = $bill->transactionCurrency ?? $defaultCurrency;
$return['currency_id'] = $currency->id;
$return['currency_name'] = $currency->name;
$return['currency_symbol'] = $currency->symbol;
$return['currency_code'] = $currency->code;
$return['currency_decimal_places'] = $currency->decimal_places;
$return['attachments'] = $this->billRepository->getAttachments($bill);
return $return;
}
);
// add info about rules:
$rules = $this->billRepository->getRulesForBills($unfiltered);
$bills = $bills->map(
static function (array $bill) use ($rules) {
$bill['rules'] = $rules[$bill['id']] ?? [];
return $bill;
}
);
// summarise per currency:
$sums = $this->getSums($bills);
return view('bills.index', compact('bills', 'sums'));
}
/**
* Rescan bills for transactions.
*
* @param Request $request
* @param Bill $bill
*
* @throws FireflyException
* @return RedirectResponse|Redirector
*/
public function rescan(Request $request, Bill $bill)
{
$total = 0;
if (false === $bill->active) {
$request->session()->flash('warning', (string) trans('firefly.cannot_scan_inactive_bill'));
return redirect(route('bills.show', [$bill->id]));
}
$set = new Collection;
if (true === $bill->active) {
$set = $this->billRepository->getRulesForBill($bill);
$total = 0;
}
if (0 === $set->count()) {
$request->session()->flash('error', (string) trans('firefly.no_rules_for_bill'));
return redirect(route('bills.show', [$bill->id]));
}
// unlink all journals:
$this->billRepository->unlinkAll($bill);
foreach ($set as $rule) {
// simply fire off all rules?
/** @var TransactionMatcher $matcher */
$matcher = app(TransactionMatcher::class);
$matcher->setSearchLimit(100000); // large upper limit
$matcher->setTriggeredLimit(100000); // large upper limit
$matcher->setRule($rule);
$matchingTransactions = $matcher->findTransactionsByRule();
$total += count($matchingTransactions);
$this->billRepository->linkCollectionToBill($bill, $matchingTransactions);
}
$request->session()->flash('success', (string) trans_choice('firefly.rescanned_bill', $total));
app('preferences')->mark();
return redirect(route('bills.show', [$bill->id]));
}
/**
* Show a bill.
*
* @param Request $request
* @param Bill $bill
*
* @return Factory|View
*/
public function show(Request $request, Bill $bill)
{
// add info about rules:
$rules = $this->billRepository->getRulesForBill($bill);
$subTitle = $bill->name;
/** @var Carbon $start */
$start = session('start');
/** @var Carbon $end */
$end = session('end');
$year = $start->year;
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$yearAverage = $this->billRepository->getYearAverage($bill, $start);
$overallAverage = $this->billRepository->getOverallAverage($bill);
$manager = new Manager();
$manager->setSerializer(new DataArraySerializer());
$manager->parseIncludes(['attachments', 'notes']);
// Make a resource out of the data and
$parameters = new ParameterBag();
$parameters->set('start', $start);
$parameters->set('end', $end);
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);
$transformer->setParameters($parameters);
$resource = new Item($bill, $transformer, 'bill');
$object = $manager->createData($resource)->toArray();
$object['data']['currency'] = $bill->transactionCurrency;
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setBill($bill)->setLimit($pageSize)->setPage($page)->withBudgetInformation()
->withCategoryInformation()->withAccountInformation();
$groups = $collector->getPaginatedGroups();
$groups->setPath(route('bills.show', [$bill->id]));
// transform any attachments as well.
$collection = $this->billRepository->getAttachments($bill);
$attachments = new Collection;
// @codeCoverageIgnoreStart
if ($collection->count() > 0) {
/** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class);
$attachments = $collection->each(
static function (Attachment $attachment) use ($transformer) {
return $transformer->transform($attachment);
}
);
}
// @codeCoverageIgnoreEnd
return view('bills.show', compact('attachments', 'groups', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle'));
}
/**
* Store a new bill.
*
* @param BillFormRequest $request
*
* @return RedirectResponse
*
*/
public function store(BillFormRequest $request): RedirectResponse
{
$billData = $request->getBillData();
$billData['active'] = true;
try {
$bill = $this->billRepository->store($billData);
} catch (FireflyException $e) {
Log::error($e->getMessage());
$request->session()->flash('error', (string) trans('firefly.bill_store_error'));
return redirect(route('bills.create'))->withInput();
}
$request->session()->flash('success', (string) trans('firefly.stored_new_bill', ['name' => $bill->name]));
app('preferences')->mark();
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($bill, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
}
return redirect(route('rules.create-from-bill', [$bill->id]));
}
/**
* Update a bill.
*
* @param BillFormRequest $request
* @param Bill $bill
*
* @return RedirectResponse
*/
public function update(BillFormRequest $request, Bill $bill): RedirectResponse
{
$billData = $request->getBillData();
$bill = $this->billRepository->update($bill, $billData);
$request->session()->flash('success', (string) trans('firefly.updated_bill', ['name' => $bill->name]));
app('preferences')->mark();
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($bill, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
// flash messages
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
}
$redirect = redirect($this->getPreviousUri('bills.edit.uri'));
if (1 === (int) $request->get('return_to_edit')) {
// @codeCoverageIgnoreStart
$request->session()->put('bills.edit.fromUpdate', true);
$redirect = redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
}
return $redirect;
}
/**
* @param Collection $bills
*
* @return array
*/
private function getSums(Collection $bills): array
{
$sums = [];
/** @var array $bill */
foreach ($bills as $bill) {
if (false === $bill['active']) {
continue;
}
if (0 === count($bill['pay_dates'])) {
continue;
}
/** @var TransactionCurrency $currency */
$currencyId = $bill['currency_id'];
$sums[$currencyId] = $sums[$currencyId] ?? [
'currency_id' => $currencyId,
'currency_code' => $bill['currency_code'],
'currency_name' => $bill['currency_name'],
'currency_symbol' => $bill['currency_symbol'],
'currency_decimal_places' => $bill['currency_decimal_places'],
'avg' => '0',
];
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
$avg = bcmul($avg, (string) count($bill['pay_dates']));
$sums[$currencyId]['avg'] = bcadd($sums[$currencyId]['avg'], $avg);
}
return $sums;
}
}

View File

@@ -157,7 +157,7 @@ class AvailableBudgetController extends Controller
*/
public function edit(AvailableBudget $availableBudget, Carbon $start, Carbon $end)
{
$availableBudget->amount = round($availableBudget->amount, $availableBudget->transactionCurrency->decimal_places);
$availableBudget->amount = number_format((float) $availableBudget->amount, $availableBudget->transactionCurrency->decimal_places, '.', '');
return view('budgets.available-budgets.edit', compact('availableBudget', 'start', 'end'));
}

View File

@@ -213,7 +213,7 @@ class BudgetLimitController extends Controller
$array['left_per_day'] = bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']);
// left per day formatted.
$array['amount'] = round($limit['amount'], $limit->transactionCurrency->decimal_places);
$array['amount'] = number_format((float) $limit['amount'], $limit->transactionCurrency->decimal_places, '.', '');
$array['left_per_day_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $array['left_per_day']);
return response()->json($array);

View File

@@ -174,7 +174,7 @@ class IndexController extends Controller
$currency = $limit->transactionCurrency ?? $defaultCurrency;
$array['budgeted'][] = [
'id' => $limit->id,
'amount' => round($limit->amount, $currency->decimal_places),
'amount' => number_format((float) $limit->amount, $currency->decimal_places, '.', ''),
'start_date' => $limit->start_date->formatLocalized($this->monthAndDayFormat),
'end_date' => $limit->end_date->formatLocalized($this->monthAndDayFormat),
'in_range' => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end),

View File

@@ -84,8 +84,6 @@ class ShowController extends Controller
*/
public function noBudget(Request $request, Carbon $start = null, Carbon $end = null)
{
/** @var Carbon $start */
$start = $start ?? session('start');
/** @var Carbon $end */
@@ -211,7 +209,7 @@ class ShowController extends Controller
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->withAccountInformation()
->setBudget($budget)->setLimit($pageSize)->setPage($page)->withBudgetInformation()->withCategoryInformation();
$groups = $collector->getPaginatedGroups();
$groups->setPath(route('budgets.show', [$budget->id, $budgetLimit->id]));

View File

@@ -79,7 +79,6 @@ class ShowController extends Controller
*/
public function show(Request $request, Category $category, Carbon $start = null, Carbon $end = null)
{
//Log::debug('Now in show()');
/** @var Carbon $start */
$start = $start ?? session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
@@ -106,8 +105,6 @@ class ShowController extends Controller
$groups = $collector->getPaginatedGroups();
$groups->setPath($path);
//Log::debug('End of show()');
return view('categories.show', compact('category','attachments', 'groups', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end'));
}

View File

@@ -126,8 +126,8 @@ class BillController extends Controller
);
$chartData = [
['type' => 'line', 'label' => (string) trans('firefly.max-amount'), 'currency_symbol' => $bill->transactionCurrency->symbol, 'entries' => []],
['type' => 'line', 'label' => (string) trans('firefly.min-amount'), 'currency_symbol' => $bill->transactionCurrency->symbol, 'entries' => []],
['type' => 'line', 'label' => (string) trans('firefly.max-amount'), 'currency_symbol' => $bill->transactionCurrency->symbol, 'entries' => []],
['type' => 'bar', 'label' => (string) trans('firefly.journal-amount'), 'currency_symbol' => $bill->transactionCurrency->symbol, 'entries' => []],
];

View File

@@ -111,7 +111,6 @@ class BudgetController extends Controller
$loopStart = app('navigation')->startOfPeriod($loopStart, $step);
$currencies = [];
$defaultEntries = [];
// echo '<hr>';
while ($end >= $loopStart) {
/** @var Carbon $currentEnd */
$loopEnd = app('navigation')->endOfPeriod($loopStart, $step);
@@ -217,7 +216,6 @@ class BudgetController extends Controller
$cache->addProperty($budget->id);
$cache->addProperty($budgetLimitId);
$cache->addProperty('chart.budget.expense-asset');
$collector->setRange(session()->get('start'), session()->get('end'));
$start = session()->get('start');
$end = session()->get('end');
if (null !== $budgetLimit) {
@@ -231,6 +229,7 @@ class BudgetController extends Controller
if ($cache->has()) {
return response()->json($cache->get()); // @codeCoverageIgnore
}
$collector->setRange($start, $end);
$collector->setBudget($budget);
$journals = $collector->getExtractedJournals();
$result = [];
@@ -283,13 +282,12 @@ class BudgetController extends Controller
$cache->addProperty($budget->id);
$cache->addProperty($budgetLimitId);
$cache->addProperty('chart.budget.expense-category');
$collector->setRange(session()->get('start'), session()->get('end'));
$start = session()->get('start');
$end = session()->get('end');
if (null !== $budgetLimit) {
$start = $budgetLimit->start_date;
$end = $budgetLimit->end_date;
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->setCurrency($budgetLimit->transactionCurrency);
$collector->setCurrency($budgetLimit->transactionCurrency);
}
$cache->addProperty($start);
$cache->addProperty($end);
@@ -297,6 +295,7 @@ class BudgetController extends Controller
if ($cache->has()) {
return response()->json($cache->get()); // @codeCoverageIgnore
}
$collector->setRange($start, $end);
$collector->setBudget($budget)->withCategoryInformation();
$journals = $collector->getExtractedJournals();
$result = [];
@@ -346,7 +345,6 @@ class BudgetController extends Controller
$cache->addProperty($budget->id);
$cache->addProperty($budgetLimitId);
$cache->addProperty('chart.budget.expense-expense');
$collector->setRange(session()->get('start'), session()->get('end'));
$start = session()->get('start');
$end = session()->get('end');
if (null !== $budgetLimit) {
@@ -360,7 +358,7 @@ class BudgetController extends Controller
if ($cache->has()) {
return response()->json($cache->get()); // @codeCoverageIgnore
}
$collector->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget)->withAccountInformation();
$journals = $collector->getExtractedJournals();
$result = [];
@@ -414,7 +412,7 @@ class BudgetController extends Controller
$cache->addProperty($end);
$cache->addProperty('chart.budget.frontpage');
if ($cache->has()) {
// return response()->json($cache->get()); // @codeCoverageIgnore
return response()->json($cache->get()); // @codeCoverageIgnore
}
$budgets = $this->repository->getActiveBudgets();
$chartData = [

View File

@@ -85,8 +85,6 @@ class CategoryController extends Controller
$start = app('navigation')->startOfPeriod($start, $range);
$end = $this->getDate();
//Log::debug(sprintf('Full range is %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
/** @var WholePeriodChartGenerator $generator */
$generator = app(WholePeriodChartGenerator::class);
$chartData = $generator->generate($category, $start, $end);
@@ -113,7 +111,7 @@ class CategoryController extends Controller
$cache->addProperty($end);
$cache->addProperty('chart.category.frontpage');
if ($cache->has()) {
// return response()->json($cache->get()); // @codeCoverageIgnore
return response()->json($cache->get()); // @codeCoverageIgnore
}
$frontPageGenerator = new FrontpageChartGenerator($start, $end);

View File

@@ -64,61 +64,6 @@ class CategoryReportController extends Controller
);
}
//
// /**
// * Chart for expenses grouped by expense account.
// *
// * TODO this chart is not multi-currency aware.
// *
// * @param Collection $accounts
// * @param Collection $categories
// * @param Carbon $start
// * @param Carbon $end
// * @param string $others
// *
// * @return JsonResponse
// */
// public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others): JsonResponse
// {
// /** @var MetaPieChartInterface $helper */
// $helper = app(MetaPieChartInterface::class);
// $helper->setAccounts($accounts)->setCategories($categories)->setStart($start)->setEnd($end)->setCollectOtherObjects(1 === (int)$others);
//
// $chartData = $helper->generate('expense', 'account');
// $data = $this->generator->pieChart($chartData);
//
// return response()->json($data);
// }
//
// /**
// * Chart for income grouped by revenue account.
// *
// * TODO this chart is not multi-currency aware.
// *
// * @param Collection $accounts
// * @param Collection $categories
// * @param Carbon $start
// * @param Carbon $end
// * @param string $others
// *
// * @return JsonResponse
// */
// public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others): JsonResponse
// {
// /** @var MetaPieChartInterface $helper */
// $helper = app(MetaPieChartInterface::class);
// $helper->setAccounts($accounts);
// $helper->setCategories($categories);
// $helper->setStart($start);
// $helper->setEnd($end);
// $helper->setCollectOtherObjects(1 === (int)$others);
// $chartData = $helper->generate('income', 'account');
// $data = $this->generator->pieChart($chartData);
//
// return response()->json($data);
// }
/**
* @param Collection $accounts
* @param Collection $categories

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* TransactionController.php
* Copyright (c) 2020 thegrumpydictator@gmail.com
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;

View File

@@ -39,13 +39,13 @@ class Controller extends BaseController
use AuthorizesRequests, DispatchesJobs, ValidatesRequests, UserNavigation, RequestInformation;
/** @var string Format for date and time. */
protected $dateTimeFormat;
protected string $dateTimeFormat;
/** @var string Format for "23 Feb, 2016". */
protected $monthAndDayFormat;
protected string $monthAndDayFormat;
/** @var string Format for "March 2018" */
protected $monthFormat;
protected string $monthFormat;
/** @var string Redirect user */
protected $redirectUri = '/';
protected string $redirectUri = '/';
/**
* Controller constructor.
@@ -55,7 +55,8 @@ class Controller extends BaseController
public function __construct()
{
// is site a demo site?
$isDemoSite = app('fireflyconfig')->get('is_demo_site', config('firefly.configuration.is_demo_site',),)->data;
$isDemoSiteConfig = app('fireflyconfig')->get('is_demo_site', config('firefly.configuration.is_demo_site', false,),);
$isDemoSite = $isDemoSiteConfig ? $isDemoSiteConfig->data : false;
app('view')->share('IS_DEMO_SITE', $isDemoSite,);
app('view')->share('DEMO_USERNAME', config('firefly.demo_username'));
app('view')->share('DEMO_PASSWORD', config('firefly.demo_password'));

View File

@@ -121,23 +121,26 @@ class DebugController extends Controller
$search = ['~', '#'];
$replace = ['\~', '# '];
$now = Carbon::now()->format('Y-m-d H:i:s e');
$installationId = app('fireflyconfig')->get('installation_id', '')->data;
$phpVersion = str_replace($search, $replace, PHP_VERSION);
$phpOs = str_replace($search, $replace, PHP_OS);
$interface = PHP_SAPI;
$drivers = implode(', ', DB::availableDrivers());
$currentDriver = DB::getDriverName();
$userAgent = $request->header('user-agent');
$trustedProxies = config('firefly.trusted_proxies');
$displayErrors = ini_get('display_errors');
$errorReporting = $this->errorReporting((int) ini_get('error_reporting'));
$appEnv = config('app.env');
$appDebug = var_export(config('app.debug'), true);
$logChannel = config('logging.default');
$appLogLevel = config('logging.level');
$cacheDriver = config('cache.default');
$loginProvider = config('auth.providers.users.driver');
$now = Carbon::now()->format('Y-m-d H:i:s e');
$installationIdConfig = app('fireflyconfig')->get('installation_id', '');
$installationId = $installationIdConfig ? $installationIdConfig->data : '';
$phpVersion = str_replace($search, $replace, PHP_VERSION);
$phpOs = str_replace($search, $replace, PHP_OS);
$interface = PHP_SAPI;
$drivers = implode(', ', DB::availableDrivers());
$currentDriver = DB::getDriverName();
$userAgent = $request->header('user-agent');
$trustedProxies = config('firefly.trusted_proxies');
$displayErrors = ini_get('display_errors');
$errorReporting = $this->errorReporting((int) ini_get('error_reporting'));
$appEnv = config('app.env');
$appDebug = var_export(config('app.debug'), true);
$logChannel = config('logging.default');
$appLogLevel = config('logging.level');
$cacheDriver = config('cache.default');
$loginProvider = config('auth.providers.users.driver');
$bcscale = bcscale();
$layout = env('FIREFLY_III_LAYOUT');
// some new vars.
$telemetry = true === config('firefly.send_telemetry') && true === config('firefly.feature_flags.telemetry');
@@ -195,6 +198,8 @@ class DebugController extends Controller
'drivers',
'currentDriver',
'loginProvider',
'bcscale',
'layout',
'userAgent',
'displayErrors',
'installationId',
@@ -224,15 +229,14 @@ class DebugController extends Controller
{
$set = RouteFacade::getRoutes();
$ignore = ['chart.', 'javascript.', 'json.', 'report-data.', 'popup.', 'debugbar.', 'attachments.download', 'attachments.preview',
'bills.rescan', 'budgets.income', 'currencies.def', 'error', 'flush', 'help.show', 'import.file',
'bills.rescan', 'budgets.income', 'currencies.def', 'error', 'flush', 'help.show',
'login', 'logout', 'password.reset', 'profile.confirm-email-change', 'profile.undo-email-change',
'register', 'report.options', 'routes', 'rule-groups.down', 'rule-groups.up', 'rules.up', 'rules.down',
'rules.select', 'search.search', 'test-flash', 'transactions.link.delete', 'transactions.link.switch',
'two-factor.lost', 'reports.options', 'debug', 'import.create-job', 'import.download', 'import.start', 'import.status.json',
'two-factor.lost', 'reports.options', 'debug',
'preferences.delete-code', 'rules.test-triggers', 'piggy-banks.remove-money', 'piggy-banks.add-money',
'accounts.reconcile.transactions', 'accounts.reconcile.overview',
'transactions.clone', 'two-factor.index', 'api.v1', 'installer.', 'attachments.view', 'import.create',
'import.job.download', 'import.job.start', 'import.job.status.json', 'import.job.store', 'recurring.events',
'transactions.clone', 'two-factor.index', 'api.v1', 'installer.', 'attachments.view', 'recurring.events',
'recurring.suggest',
];
$return = '&nbsp;';

View File

@@ -1,8 +1,8 @@
<?php
declare(strict_types=1);
/**
* IndexController.php
* Copyright (c) 2019 james@firefly-iii.org
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@@ -20,6 +20,8 @@ declare(strict_types=1);
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Export;

View File

@@ -1,82 +0,0 @@
<?php
/**
* CallbackController.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;
use Log;
/**
* Class CallbackController
*
* @deprecated
* @codeCoverageIgnore
*/
class CallbackController extends Controller
{
/**
* Callback specifically for YNAB logins.
*
* @param Request $request
*
* @param ImportJobRepositoryInterface $repository
*
* @return Factory|RedirectResponse|Redirector|View
*/
public function ynab(Request $request, ImportJobRepositoryInterface $repository)
{
$code = (string) $request->get('code');
$jobKey = (string) $request->get('state');
if ('' === $code) {
return view('error')->with('message', 'You Need A Budget did not reply with a valid authorization code. Firefly III cannot continue.');
}
$importJob = $repository->findByKey($jobKey);
if ('' === $jobKey || null === $importJob) {
return view('error')->with('message', 'You Need A Budget did not reply with the correct state identifier. Firefly III cannot continue.');
}
Log::debug(sprintf('Got a code from YNAB: %s', $code));
// we have a code. Make the job ready for the next step, and then redirect the user.
$configuration = $repository->getConfiguration($importJob);
$configuration['auth_code'] = $code;
$repository->setConfiguration($importJob, $configuration);
// set stage to make the import routine take the correct action:
$repository->setStatus($importJob, 'ready_to_run');
$repository->setStage($importJob, 'get_access_token');
return redirect(route('import.job.status.index', [$importJob->key]));
}
}

View File

@@ -1,198 +0,0 @@
<?php
/**
* IndexController.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Import\Prerequisites\PrerequisitesInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Binder\ImportProvider;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Response as LaravelResponse;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;
use Log;
/**
*
* Class IndexController
*
* @deprecated
* @codeCoverageIgnore
*/
class IndexController extends Controller
{
/** @var array All available providers */
public $providers;
/** @var ImportJobRepositoryInterface The import job repository */
public $repository;
/** @var UserRepositoryInterface The user repository */
public $userRepository;
/**
* IndexController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', (string) trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
$this->providers = ImportProvider::getProviders();
return $next($request);
}
);
}
/**
* Creates a new import job for $importProvider.
*
* @param string $importProvider
*
* @return RedirectResponse|Redirector
*
*/
public function create(string $importProvider)
{
$hasPreReq = (bool) config(sprintf('import.has_prereq.%s', $importProvider));
$hasConfig = (bool) config(sprintf('import.has_job_config.%s', $importProvider));
$allowedForDemo = (bool) config(sprintf('import.allowed_for_demo.%s', $importProvider));
$isDemoUser = $this->userRepository->hasRole(auth()->user(), 'demo');
Log::debug(sprintf('Will create job for provider "%s"', $importProvider));
Log::debug(sprintf('Is demo user? %s', var_export($isDemoUser, true)));
Log::debug(sprintf('Is allowed for user? %s', var_export($allowedForDemo, true)));
Log::debug(sprintf('Has prerequisites? %s', var_export($hasPreReq, true)));
Log::debug(sprintf('Has config? %s', var_export($hasConfig, true)));
// @codeCoverageIgnoreStart
if ($isDemoUser && !$allowedForDemo) {
Log::debug('User is demo and this provider doesnt work for demo users.');
return redirect(route('import.index'));
}
// @codeCoverageIgnoreEnd
$importJob = $this->repository->create($importProvider);
Log::debug(sprintf('Created job #%d for provider %s', $importJob->id, $importProvider));
// no prerequisites but job has config:
if (false === $hasPreReq && false !== $hasConfig) {
Log::debug('Provider has no prerequisites. Continue.');
$this->repository->setStatus($importJob, 'has_prereq');
Log::debug('Redirect to configuration.');
return redirect(route('import.job.configuration.index', [$importJob->key]));
}
// job has prerequisites:
Log::debug('Job provider has prerequisites.');
/** @var PrerequisitesInterface $providerPre */
$providerPre = app((string) config(sprintf('import.prerequisites.%s', $importProvider)));
$providerPre->setUser($importJob->user);
// and are not filled in:
if (!$providerPre->isComplete()) {
Log::debug('Job provider prerequisites are not yet filled in. Redirect to prerequisites-page.');
// redirect to global prerequisites
return redirect(route('import.prerequisites.index', [$importProvider, $importJob->key]));
}
Log::debug('Prerequisites are complete.');
// but are filled in:
$this->repository->setStatus($importJob, 'has_prereq');
// and has no config:
if (false === $hasConfig) {
// @codeCoverageIgnoreStart
Log::debug('Provider has no configuration. Job is ready to start.');
$this->repository->setStatus($importJob, 'ready_to_run');
Log::debug('Redirect to status-page.');
return redirect(route('import.job.status.index', [$importJob->key]));
// @codeCoverageIgnoreEnd
}
// but also needs config:
Log::debug('Job has configuration. Redirect to job-config.');
// Otherwise just redirect to job configuration.
return redirect(route('import.job.configuration.index', [$importJob->key]));
}
/**
* Generate a JSON file of the job's configuration and send it to the user.
*
* @param ImportJob $job
*
* @return LaravelResponse
*/
public function download(ImportJob $job): LaravelResponse
{
Log::debug('Now in download()', ['job' => $job->key]);
$config = $this->repository->getConfiguration($job);
// This is CSV import specific:
$config['delimiter'] = $config['delimiter'] ?? ',';
$config['delimiter'] = "\t" === $config['delimiter'] ? 'tab' : $config['delimiter'];
$result = json_encode($config, JSON_PRETTY_PRINT);
$name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
/** @var LaravelResponse $response */
$response = response($result);
$response->header('Content-disposition', 'attachment; filename=' . $name)
->header('Content-Type', 'application/json')
->header('Content-Description', 'File Transfer')
->header('Connection', 'Keep-Alive')
->header('Expires', '0')
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->header('Pragma', 'public')
->header('Content-Length', strlen($result));
return $response;
}
/**
* General import index.
*
* @return Factory|View
*/
public function index()
{
$providers = $this->providers;
$subTitle = (string) trans('import.index_breadcrumb');
$subTitleIcon = 'fa-home';
$isDemoUser = $this->userRepository->hasRole(auth()->user(), 'demo');
return view('import.index', compact('subTitle', 'subTitleIcon', 'providers', 'isDemoUser'));
}
}

View File

@@ -1,169 +0,0 @@
<?php
/**
* JobConfigurationController.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Support\Http\Controllers\CreateStuff;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Routing\Redirector;
use Illuminate\Support\MessageBag;
use Illuminate\View\View;
use Log;
/**
* Class JobConfigurationController
*
* @deprecated
* @codeCoverageIgnore
*/
class JobConfigurationController extends Controller
{
use CreateStuff;
/** @var ImportJobRepositoryInterface The import job repository */
public $repository;
/**
* JobConfigurationController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', (string) trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Configure the job. This method is returned to until job is deemed "configured".
*
* @param ImportJob $importJob
*
* @throws FireflyException
*
* @return Factory|RedirectResponse|Redirector|View
*
*/
public function index(ImportJob $importJob)
{
Log::debug('Now in JobConfigurationController::index()');
$allowed = ['has_prereq', 'need_job_config'];
if (null !== $importJob && !in_array($importJob->status, $allowed, true)) {
Log::error(sprintf('Job has state "%s", but we only accept %s', $importJob->status, json_encode($allowed)));
session()->flash('error', (string) trans('import.bad_job_status', ['status' => e($importJob->status)]));
return redirect(route('import.index'));
}
Log::debug(sprintf('Now in JobConfigurationController::index() with job "%s" and status "%s"', $importJob->key, $importJob->status));
// if provider has no config, just push it through:
$importProvider = $importJob->provider;
if (!(bool) config(sprintf('import.has_job_config.%s', $importProvider))) {
// @codeCoverageIgnoreStart
Log::debug('Job needs no config, is ready to run!');
$this->repository->setStatus($importJob, 'ready_to_run');
return redirect(route('import.job.status.index', [$importJob->key]));
// @codeCoverageIgnoreEnd
}
$configurator = $this->makeConfigurator($importJob);
if ($configurator->configurationComplete()) {
Log::debug('Config is complete, set status to ready_to_run.');
$this->repository->setStatus($importJob, 'ready_to_run');
return redirect(route('import.job.status.index', [$importJob->key]));
}
$view = $configurator->getNextView();
$data = $configurator->getNextData();
$subTitle = (string) trans('import.job_configuration_breadcrumb', ['key' => $importJob->key]);
$subTitleIcon = 'fa-wrench';
return view($view, compact('data', 'importJob', 'subTitle', 'subTitleIcon'));
}
/**
* Store the configuration. Returns to "configure" method until job is configured.
*
* @param Request $request
* @param ImportJob $importJob
*
* @throws FireflyException
* @return RedirectResponse|Redirector
*
*/
public function post(Request $request, ImportJob $importJob)
{
// catch impossible status:
$allowed = ['has_prereq', 'need_job_config'];
if (null !== $importJob && !in_array($importJob->status, $allowed, true)) {
session()->flash('error', (string) trans('import.bad_job_status', ['status' => e($importJob->status)]));
return redirect(route('import.index'));
}
Log::debug('Now in postConfigure()', ['job' => $importJob->key]);
$configurator = $this->makeConfigurator($importJob);
// is the job already configured?
if ($configurator->configurationComplete()) {
$this->repository->setStatus($importJob, 'ready_to_run');
return redirect(route('import.job.status.index', [$importJob->key]));
}
// uploaded files are attached to the job.
// the configurator can then handle them.
$result = new MessageBag;
/** @var UploadedFile $upload */
foreach ($request->allFiles() as $name => $upload) {
$result = $this->repository->storeFileUpload($importJob, $name, $upload);
}
$data = $request->all();
$messages = $configurator->configureJob($data);
$result->merge($messages);
if ($messages->count() > 0) {
$request->session()->flash('warning', $messages->first());
}
// return to configure
return redirect(route('import.job.configuration.index', [$importJob->key]));
}
}

View File

@@ -1,240 +0,0 @@
<?php
/**
* JobStatusController.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Support\Http\Controllers\CreateStuff;
use Illuminate\Http\JsonResponse;
use Log;
/**
* Class JobStatusController
*
* @deprecated
* @codeCoverageIgnore
*/
class JobStatusController extends Controller
{
use CreateStuff;
/** @var ImportJobRepositoryInterface The import job repository */
private $repository;
/**
* JobStatusController constructor.
*/
public function __construct()
{
parent::__construct();
// set time limit to zero to prevent timeouts.
set_time_limit(0);
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', (string) trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Index for job status.
*
* @param ImportJob $importJob
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index(ImportJob $importJob)
{
$subTitleIcon = 'fa-gear';
$subTitle = (string) trans('import.job_status_breadcrumb', ['key' => $importJob->key]);
return view('import.status', compact('importJob', 'subTitle', 'subTitleIcon'));
}
/**
* JSON overview of job status.
*
* @param ImportJob $importJob
*
* @return JsonResponse
*/
public function json(ImportJob $importJob): JsonResponse
{
$count = $this->repository->countTransactions($importJob);
$json = [
'status' => $importJob->status,
'errors' => $importJob->errors,
'count' => $count,
'tag_id' => $importJob->tag_id,
'tag_name' => null === $importJob->tag_id ? null : $importJob->tag->tag,
'report_txt' => (string) trans('import.unknown_import_result'),
'download_config' => false,
'download_config_text' => '',
];
if ('file' === $importJob->provider) {
$json['download_config'] = true;
$json['download_config_text']
= trans('import.should_download_config', ['route' => route('import.job.download', [$importJob->key])]) . ' '
. trans('import.share_config_file');
}
// if count is zero:
if (null !== $importJob->tag_id) {
$count = $this->repository->countByTag($importJob);
}
if (0 === $count) {
$json['report_txt'] = (string) trans('import.result_no_transactions');
}
if (1 === $count && null !== $importJob->tag_id) {
$json['report_txt'] = trans(
'import.result_one_transaction',
['route' => route('tags.show', [$importJob->tag_id, 'all']), 'tag' => $importJob->tag->tag]
);
}
if ($count > 1 && null !== $importJob->tag_id) {
$json['report_txt'] = trans(
'import.result_many_transactions',
['count' => $count, 'route' => route('tags.show', [$importJob->tag_id, 'all']), 'tag' => $importJob->tag->tag]
);
}
return response()->json($json);
}
/**
* Calls to start the job.
*
* @param ImportJob $importJob
*
* @return JsonResponse
*/
public function start(ImportJob $importJob): JsonResponse
{
Log::info('Now in JobStatusController::start');
// catch impossible status:
$allowed = ['ready_to_run', 'need_job_config'];
if (null !== $importJob && !in_array($importJob->status, $allowed, true)) {
Log::error(sprintf('Job is not ready. Status should be in array, but is %s', $importJob->status), $allowed);
$this->repository->setStatus($importJob, 'error');
return response()->json(
['status' => 'NOK', 'message' => sprintf('JobStatusController::start expects status "ready_to_run" instead of "%s".', $importJob->status)]
);
}
$importProvider = $importJob->provider;
$key = sprintf('import.routine.%s', $importProvider);
$className = config($key);
if (null === $className || !class_exists($className)) {
// @codeCoverageIgnoreStart
$message = sprintf('Cannot find import routine class for job of type "%s".', $importProvider);
Log::error($message);
return response()->json(
['status' => 'NOK', 'message' => $message]
);
// @codeCoverageIgnoreEnd
}
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setImportJob($importJob);
Log::debug(sprintf('Created class of type %s', $className));
try {
Log::debug(sprintf('Try to call %s:run()', $className));
$routine->run();
} catch (FireflyException|Exception $e) {
$message = 'The import routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$this->repository->setStatus($importJob, 'error');
return response()->json(['status' => 'NOK', 'message' => $message]);
}
// expect nothing from routine, just return OK to user.
Log::info('Now finished with JobStatusController::start');
return response()->json(['status' => 'OK', 'message' => 'stage_finished']);
}
/**
* Store does three things:
*
* - Store the transactions.
* - Add them to a tag.
*
* @param ImportJob $importJob
*
* @return JsonResponse
*/
public function store(ImportJob $importJob): JsonResponse
{
Log::info('Now in JobStatusController::store');
// catch impossible status:
$allowed = ['provider_finished', 'storing_data'];
if (null !== $importJob && !in_array($importJob->status, $allowed, true)) {
Log::error(sprintf('Job is not ready. Status should be in array, but is %s', $importJob->status), $allowed);
return response()->json(
['status' => 'NOK', 'message' => sprintf('JobStatusController::start expects status "provider_finished" instead of "%s".', $importJob->status)]
);
}
// set job to be storing data:
$this->repository->setStatus($importJob, 'storing_data');
try {
$this->storeTransactions($importJob);
} catch (FireflyException $e) {
$message = 'The import storage routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$this->repository->setStatus($importJob, 'error');
return response()->json(['status' => 'NOK', 'message' => $message]);
}
// set storage to be finished:
$this->repository->setStatus($importJob, 'storage_finished');
Log::info('Now finished with JobStatusController::start');
// expect nothing from routine, just return OK to user.
return response()->json(['status' => 'OK', 'message' => 'storage_finished']);
}
}

View File

@@ -1,177 +0,0 @@
<?php
/**
* PrerequisitesController.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Import\Prerequisites\PrerequisitesInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;
use Log;
/**
* Class PrerequisitesController
*
* @deprecated
* @codeCoverageIgnore
*/
class PrerequisitesController extends Controller
{
/** @var ImportJobRepositoryInterface The import job repository */
private $repository;
/**
* PrerequisitesController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', (string) trans('firefly.import_index_title'));
app('view')->share('subTitleIcon', 'fa-check');
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
}
/**
* This method will process and store import provider global prerequisites
* such as API keys.
*
* @param string $importProvider
* @param ImportJob $importJob
*
* @return Factory|RedirectResponse|Redirector|View
*/
public function index(string $importProvider, ImportJob $importJob = null)
{
// catch impossible status:
$allowed = ['new'];
if (null !== $importJob && !in_array($importJob->status, $allowed, true)) {
Log::error(sprintf('Job has state "%s" but this Prerequisites::index() only accepts %s', $importJob->status, json_encode($allowed)));
session()->flash('error', (string) trans('import.bad_job_status', ['status' => e($importJob->status)]));
return redirect(route('import.index'));
}
app('view')->share('subTitle', (string) trans('import.prerequisites_breadcrumb_' . $importProvider));
$class = (string) config(sprintf('import.prerequisites.%s', $importProvider));
/** @var User $user */
$user = auth()->user();
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser($user);
if (null !== $importJob && $object->isComplete()) {
// update job:
$this->repository->setStatus($importJob, 'has_prereq');
// redirect to job config:
return redirect(route('import.job.configuration.index', [$importJob->key]));
}
$view = $object->getView();
$parameters = ['title' => (string) trans('firefly.import_index_title'), 'mainTitleIcon' => 'fa-archive', 'importJob' => $importJob];
$parameters = array_merge($object->getViewParameters(), $parameters);
return view($view, $parameters);
}
/**
* This method processes the prerequisites the user has entered in the previous step.
*
* Whatever storePrerequisites does, it should make sure that the system is ready to continue immediately. So
* no extra calls or stuff, except maybe to open a session
*
* @param Request $request
* @param string $importProvider
* @param ImportJob $importJob
*
* @return RedirectResponse|Redirector
* @see PrerequisitesInterface::storePrerequisites
*
*/
public function post(Request $request, string $importProvider, ImportJob $importJob = null)
{
Log::debug(sprintf('Now in postPrerequisites for %s', $importProvider));
// catch impossible status:
$allowed = ['new'];
if (null !== $importJob && !in_array($importJob->status, $allowed, true)) {
Log::error(sprintf('Job has state "%s" but this Prerequisites::post() only accepts %s', $importJob->status, json_encode($allowed)));
session()->flash('error', (string) trans('import.bad_job_status', ['status' => e($importJob->status)]));
return redirect(route('import.index'));
}
$class = (string) config(sprintf('import.prerequisites.%s', $importProvider));
/** @var User $user */
$user = auth()->user();
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser($user);
Log::debug('Going to store entered prerequisites.');
// store post data
$data = $request->all();
$result = $object->storePrerequisites($data);
Log::debug(sprintf('Result of storePrerequisites has message count: %d', $result->count()));
if ($result->count() > 0) {
$request->session()->flash('error', e($result->first()));
// redirect back to job, if has job:
return redirect(route('import.prerequisites.index', [$importProvider, $importJob->key ?? '']))->withInput();
}
// session flash!
$request->session()->flash('success', (string) trans('import.prerequisites_saved_for_' . $importProvider));
// if has job, redirect to global config for provider
// if no job, back to index!
if (null === $importJob) {
return redirect(route('import.index'));
}
// update job:
$this->repository->setStatus($importJob, 'has_prereq');
// redirect to job config:
return redirect(route('import.job.configuration.index', [$importJob->key]));
}
}

View File

@@ -22,6 +22,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionCurrency;
@@ -95,6 +96,30 @@ class JavascriptController extends Controller
->header('Content-Type', 'text/javascript');
}
/**
* Bit of a hack but OK.
*
* @param Request $request
*
* @return Response
*/
public function variablesV2(Request $request): Response
{
/** @var Carbon $start */
$start = clone session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
$end = clone session('end', Carbon::now()->endOfMonth());
$data = [
'start' => $start->format('Y-m-d'),
'end' => $end->format('Y-m-d'),
];
return response()
->view('javascript.variables', $data)
->header('Content-Type', 'text/javascript');
}
/**
* Show some common variables to be used in scripts.
*

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