Compare commits

...

444 Commits

Author SHA1 Message Date
James Cole
45b8c36272 Merge branch 'hotfix/4.7.17.1' 2019-07-15 16:50:36 +02:00
James Cole
45ddb64186 Fix #2335 2019-07-15 16:50:18 +02:00
James Cole
76aa8acf0f Merge branch 'release/4.7.17' 2019-03-17 09:40:13 +01:00
James Cole
220add3eda Update lock file 2019-03-17 09:17:55 +01:00
James Cole
541280ee91 Update change logs and config files. 2019-03-17 09:17:07 +01:00
James Cole
01219c951c Enable norsk, update version of DB 2019-03-17 09:14:52 +01:00
James Cole
69fa60cd21 Various language string updates. 2019-03-17 09:14:33 +01:00
James Cole
a11d2df6bb Norwegian strings. 2019-03-17 09:13:23 +01:00
James Cole
07d39a23a8 Improve installer middleware for Sandstorm. 2019-03-17 09:06:45 +01:00
James Cole
ca67d98676 Fix some issues with importer #2166 2019-03-17 08:18:42 +01:00
James Cole
7b60d210ee Other delete thing. 2019-03-17 07:31:12 +01:00
James Cole
78d955ebf6 More debug things. 2019-03-17 07:20:11 +01:00
James Cole
f6f21e02ac Extra debug info for #2159 and some kernel changes. 2019-03-16 21:54:25 +01:00
James Cole
e2ebd01719 Extra debug info for #2159 2019-03-16 20:07:26 +01:00
James Cole
b8ac7c9d89 Fix #2173 2019-03-16 19:39:39 +01:00
James Cole
037d9b7017 Rename class and add copyright statement @wrouesnel #2167 2019-03-16 19:02:13 +01:00
James Cole
6462d2b87a Merge pull request #2167 from wrouesnel/bugfix/ldapauth
Fix LDAP auth configuration paths.
2019-03-16 18:58:21 +01:00
Will Rouesnel
cb4ff35adb Fix LDAP auth configuration paths.
This commit fixes parameters broken in the latest version of `adldap2`.

Specifically:

* `adldap` auth parameters have changed in the latest version.
  * "usernames" has become `identities` and `discover` has changed to
    `discover_users_by`, `auth` has changed to `bind_users_by`
* Add the missing objectguid field to the users table for adldap2.
  * This is added as a nullable (optional) field at the moment to support
    tracking LDAP users as adldap2 wants to.
2019-03-12 09:12:54 +11:00
James Cole
d063f32c1c Fix some cache issues and a version bump. 2019-03-08 17:57:42 +01:00
James Cole
244d8eecab Updated file list. 2019-03-08 07:09:45 +01:00
James Cole
b66f6d7e2e Updated list. 2019-03-08 07:07:43 +01:00
James Cole
aa3a88f537 New file list. 2019-03-08 06:48:04 +01:00
James Cole
e5476e6e7a Merge tag '4.7.16' into develop
4.7.16
2019-03-08 06:22:05 +01:00
James Cole
4d976312e8 Merge branch 'release/4.7.16' 2019-03-08 06:22:04 +01:00
James Cole
2ef5d3d4c6 Update composer file. 2019-03-08 06:11:22 +01:00
James Cole
eb3a89555d Small fix in changelog. 2019-03-08 06:11:13 +01:00
James Cole
0a80b7fca7 New translations. 2019-03-08 06:11:05 +01:00
James Cole
f682981c1e Fix test for PHPUnit8 2019-03-05 20:11:37 +01:00
James Cole
4aac969ae4 Update version and changelog. 2019-03-05 17:52:44 +01:00
James Cole
00651adf5e Fix issue with bill display 2019-03-05 17:52:34 +01:00
James Cole
ed94e71168 Update language strings. 2019-03-05 17:52:26 +01:00
James Cole
6e12f434ad Fix issue #2144 2019-03-05 16:55:03 +01:00
James Cole
b8c4ec1449 Add old string under new key. 2019-03-03 08:17:19 +01:00
James Cole
4ca60ca92a Code for upgrade of various libraries. 2019-03-02 21:18:26 +01:00
James Cole
48219c9af3 Update libraries. 2019-03-02 20:14:45 +01:00
James Cole
9887b9809d Merge branch 'master' into develop
* master:
  Bump version for travis build.
2019-03-02 17:36:44 +01:00
James Cole
a13ad5b417 Bump version for travis build. 2019-03-02 17:36:08 +01:00
James Cole
3f28fd689f Merge tag '4.7.15' into develop
4.7.15
2019-03-02 17:17:22 +01:00
James Cole
959025545e Merge branch 'release/4.7.15' 2019-03-02 17:17:21 +01:00
James Cole
1978463e59 Update language strings for new release. 2019-03-02 15:55:26 +01:00
James Cole
e85ac07c49 Update meta files for new release. 2019-03-02 15:55:01 +01:00
James Cole
b7f0a6fff4 Update migrations so you can always roll back. 2019-03-02 15:54:24 +01:00
James Cole
3a57e09447 Fix for #2128 2019-03-02 14:33:46 +01:00
James Cole
87d5cabe52 Search improvements. 2019-03-02 14:12:09 +01:00
James Cole
af07522f16 Fix for #2135 2019-03-02 10:42:43 +01:00
James Cole
1b8d4e4c63 Make sure bills show attachment notes 2019-03-02 09:18:41 +01:00
James Cole
9f3c114d57 Add JSON decrypt for #2125 2019-03-02 09:18:26 +01:00
James Cole
c51c1b8098 Some basic date tests 2019-03-02 08:05:15 +01:00
James Cole
143fe2a71f Fix #2125 2019-03-01 17:56:52 +01:00
James Cole
3379b723cf Last attempt I hope for #2125 2019-02-28 19:56:41 +01:00
James Cole
39321b320e Improve search speed. 2019-02-27 19:08:09 +01:00
James Cole
fe738fd321 Another fix for #2125 2019-02-27 18:55:56 +01:00
James Cole
95720673d2 Merge pull request #2130 from lastlink/patch-1
add missing rollback for 477 migration
2019-02-26 18:33:20 +01:00
lastlink
963be4a4fa add missing rollback for 477 migration 2019-02-26 00:16:05 -05:00
James Cole
61db419485 Fixes #2125 2019-02-25 20:57:29 +01:00
James Cole
cb17b09b24 Merge pull request #2120 from lastlink/fix/pwaApple
missing meta tag for ios
2019-02-25 07:23:50 +01:00
Mr. Funk
0f1236a597 missing meta tag for ios 2019-02-24 15:12:58 -05:00
James Cole
ac26427a63 Merge branch 'master' into develop
* master:
  Don't change ARM PHP settings.
2019-02-24 10:56:28 +01:00
James Cole
491903778a Don't change ARM PHP settings. 2019-02-24 10:56:08 +01:00
James Cole
405c3e110a Merge tag '4.7.14' into develop
4.7.14
2019-02-24 07:28:00 +01:00
James Cole
5464bfac19 Merge branch 'release/4.7.14' 2019-02-24 07:28:00 +01:00
James Cole
ad31cc1c1f Fixes #2119 and bump version. 2019-02-24 07:27:24 +01:00
James Cole
3aa79fb1e4 Merge tag '4.7.13' into develop
4.7.13
2019-02-23 21:27:05 +01:00
James Cole
e3de03e50d Merge branch 'release/4.7.13' 2019-02-23 21:27:04 +01:00
James Cole
d8b4af34e0 Update files for release 4.7.13 2019-02-23 14:32:24 +01:00
James Cole
222d8e071a Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
* 'develop' of github.com:firefly-iii/firefly-iii:
  New check for journals with 0.00 amount.
2019-02-23 12:36:17 +01:00
James Cole
36c2f27d59 New check for journals with 0.00 amount. 2019-02-23 07:47:41 +01:00
James Cole
5d43faab96 Refactor script locations to clear up root directory. 2019-02-20 07:04:24 +01:00
James Cole
07048cb5fb Fix #2100 2019-02-19 20:19:30 +01:00
James Cole
38c79c3dc4 Upgrade fix for Softaculous. 2019-02-19 19:59:02 +01:00
James Cole
7610d9e0db Update Dockerfiles to increase PHP execution time. 2019-02-19 07:10:13 +01:00
James Cole
e4f3d0a0ea Some new strings. 2019-02-18 19:47:48 +01:00
James Cole
fdf147d9c6 Merge branch 'hotfix/4.7.12.1' 2019-02-16 23:01:29 +01:00
James Cole
f7e418f517 Merge tag '4.7.12.1' into develop
4.7.12.1
2019-02-16 23:01:29 +01:00
James Cole
738f6ed232 Fix #2093 2019-02-16 23:01:14 +01:00
James Cole
12217d9850 Merge tag '4.7.12' into develop
4.7.12
2019-02-16 19:35:01 +01:00
James Cole
87c8b3d7c6 Merge branch 'release/4.7.12' 2019-02-16 19:35:00 +01:00
James Cole
fd571abbb0 Slight change in build script. 2019-02-16 19:33:33 +01:00
James Cole
64ae56757e Updated meta files. 2019-02-16 19:22:16 +01:00
James Cole
7d75f00696 New translations. 2019-02-16 19:22:05 +01:00
James Cole
451c86e431 Final touches for #2085 2019-02-16 19:04:36 +01:00
James Cole
c8c71da903 Extra debug log. #2061 2019-02-16 14:27:54 +01:00
James Cole
423f3a9296 Fix build script. 2019-02-16 14:02:04 +01:00
James Cole
7658b7d9a6 Apply fix for #2070 2019-02-16 11:45:08 +01:00
James Cole
0d0906c5e2 Fix for #2070 2019-02-16 11:43:05 +01:00
James Cole
e0aa7f3ff5 Various code cleanup. 2019-02-16 08:05:48 +01:00
James Cole
59fdf5b77a Updates to the login procedure [skip ci] 2019-02-16 07:20:25 +01:00
James Cole
e28cfade8d Push and build stuff. 2019-02-15 17:15:42 +01:00
James Cole
19710c3eab Update LDAP config for #2085 2019-02-15 07:39:12 +01:00
James Cole
0782354160 Updated LDAP code #2085 2019-02-15 07:22:51 +01:00
James Cole
22bc6d507e Fix issue with upgrade. 2019-02-13 17:54:46 +01:00
James Cole
bf3c74e65f Add required PHP extensions to composer.json. 2019-02-13 17:54:36 +01:00
James Cole
71fb9d8fa5 Code cleaning stuff. 2019-02-13 17:38:41 +01:00
James Cole
9a461fc7b7 Fix for #2082 2019-02-13 17:34:03 +01:00
James Cole
e0d87aa11e First part of a large code cleanup commit. 2019-02-12 21:49:28 +01:00
James Cole
b273af341c Update version. 2019-02-12 21:01:19 +01:00
James Cole
2117fc2a5a Add thing to apply rules to fints #2081 2019-02-12 21:01:12 +01:00
James Cole
58e55dc789 Document things better. 2019-02-12 21:00:53 +01:00
James Cole
ac9b8b8c30 Build and push. 2019-02-12 17:33:34 +01:00
James Cole
4dcc66238e Make directory as well. 2019-02-12 17:31:30 +01:00
James Cole
90c0aa6360 Add touch command. 2019-02-12 17:29:45 +01:00
James Cole
1594335487 Experiment with manifest code. 2019-02-12 17:27:56 +01:00
James Cole
2517674849 Expand experimental things. 2019-02-12 17:21:52 +01:00
James Cole
c00bc2a1f3 Update build script. 2019-02-11 19:01:14 +01:00
James Cole
f999be81c2 Fix upgrade problem in Softaculous. 2019-02-11 17:22:46 +01:00
James Cole
e2530c5486 Refresh journal after a rule is applied, #2070 2019-02-11 17:09:52 +01:00
James Cole
7526f13ca9 Update build script. 2019-02-11 17:07:34 +01:00
James Cole
ed08d299de Fix for #2017 2019-02-11 17:07:07 +01:00
James Cole
b6ea2f1c64 Updated file list for sandstorm 2019-02-10 15:22:18 +01:00
James Cole
9ee14374bb Updated sandstorm file list. 2019-02-10 15:14:52 +01:00
James Cole
925401682b Merge tag '4.7.11' into develop
4.7.11
2019-02-10 14:53:58 +01:00
James Cole
53c71bb7a2 Merge branch 'release/4.7.11' 2019-02-10 14:53:57 +01:00
James Cole
3c036ae021 Fix issue with time stamps. 2019-02-10 14:53:24 +01:00
James Cole
bf89d9956d Update language strings. 2019-02-10 14:33:15 +01:00
James Cole
71104f375c Fix tests 2019-02-10 14:33:03 +01:00
James Cole
4c94820cf4 Update meta files. 2019-02-10 14:32:34 +01:00
James Cole
8998c9a672 Extra code for #1975 2019-02-10 10:54:30 +01:00
James Cole
ccf60f4cdc Back to classic build. 2019-02-10 08:07:25 +01:00
James Cole
c5af1d363c Add audit trail messages to several controllers. 2019-02-10 08:03:09 +01:00
James Cole
c6e3b54705 Make update code slightly more robust. 2019-02-10 07:59:11 +01:00
James Cole
4f274a290e Generic debug info. 2019-02-09 20:44:31 +01:00
James Cole
1774bcbabe Add copyright statements and newlines. 2019-02-09 10:36:59 +01:00
James Cole
492c11784b Update with manifest commands. 2019-02-09 06:40:55 +01:00
James Cole
21ddbd220a Include lib for arm 2019-02-08 17:40:29 +01:00
James Cole
e4a95e55ed Remove copy argument. 2019-02-08 17:34:46 +01:00
James Cole
3a0eb5a428 Do actual build 2019-02-08 17:31:32 +01:00
James Cole
e55fd968cf Rename file 2019-02-08 17:31:25 +01:00
James Cole
d41be08b48 Rename file 2019-02-08 17:30:42 +01:00
James Cole
a18272513d Fix YML 2019-02-08 17:28:04 +01:00
James Cole
e88061199c Trigger a new build. 2019-02-08 17:17:19 +01:00
James Cole
d0d7c437f2 Different build flow. 2019-02-08 17:15:54 +01:00
James Cole
a1ac1a4a29 Remove empty header from changelog [skip ci] 2019-02-08 17:01:49 +01:00
James Cole
41d22876c4 Update changelog and version. 2019-02-08 17:00:46 +01:00
James Cole
e1bb0298cb Fix category use date. 2019-02-08 16:56:07 +01:00
James Cole
1ec2772255 Fix #2052 2019-02-08 16:51:29 +01:00
James Cole
40d77d82cd Bunq import can now handle timestamps. 2019-02-08 16:45:42 +01:00
James Cole
311d51464d Add support for #2006 2019-02-08 07:14:45 +01:00
James Cole
d63c9c9aea Add audit logging. 2019-02-08 07:13:59 +01:00
James Cole
f1e83f240e Build with a new distribution. 2019-02-08 03:21:34 +01:00
James Cole
b0f847959f Merge pull request #2058 from skuzzle/develop
Remove console.log statement
2019-02-05 15:22:12 +01:00
Simon Taddiken
d3d4439b03 Remove console.log statement 2019-02-05 15:02:36 +01:00
James Cole
0e1da3f797 Fix webbased upgrade routine. 2019-02-04 20:35:46 +01:00
James Cole
def95df49e Fix link in changelog [skip ci] 2019-02-03 20:20:24 +01:00
James Cole
73c8c6de37 Disable manifest push [skip ci] 2019-02-03 20:19:59 +01:00
James Cole
25b2b43a38 Fix for #2049 2019-02-03 18:25:58 +01:00
James Cole
1fc2c998de Merge pull request #2048 from nescafe2002/patch-1
Fix delete-account confirmation
2019-02-03 14:52:40 +01:00
Joris
b7a577cc4c Fix delete-account confirmation 2019-02-03 14:44:32 +01:00
James Cole
2cdedd9c29 Merge tag '4.7.10' into develop
4.7.10
2019-02-03 10:10:41 +01:00
James Cole
cf47daa2a0 Merge branch 'release/4.7.10' 2019-02-03 10:10:39 +01:00
James Cole
926e2db1d6 Update meta files for new release. 2019-02-03 10:09:04 +01:00
James Cole
d092034ec5 Updated translations [skip ci] 2019-02-03 09:21:37 +01:00
James Cole
d13b9a0cc3 Mention amd specifically. 2019-02-03 09:05:40 +01:00
James Cole
bbbaf6c868 Small textual changes [skip ci] 2019-02-03 07:51:34 +01:00
James Cole
833add99a0 Reverse build order, add manifest creation. 2019-02-03 07:00:50 +01:00
James Cole
b54e7f0d46 Remove platform argument. 2019-02-03 06:33:18 +01:00
James Cole
a90981481f Edit in build command. 2019-02-02 19:26:02 +01:00
James Cole
edff3d7fb0 Remove target platform reference 2019-02-02 19:24:53 +01:00
James Cole
88d65b4784 Switch args around. 2019-02-02 19:22:03 +01:00
James Cole
6db869de23 Get docker version 2019-02-02 19:16:19 +01:00
James Cole
c645cb18e5 Check experimental flag. 2019-02-02 19:13:10 +01:00
James Cole
298132298b Enable experimental things. 2019-02-02 19:10:46 +01:00
James Cole
400b50a1f0 Enable experimental features. 2019-02-02 19:05:11 +01:00
James Cole
5b597e9b1f Set the platform to ARM. 2019-02-02 18:58:49 +01:00
James Cole
e55de9c1d5 No manifest commands. 2019-02-02 18:07:03 +01:00
James Cole
33ccf7c707 Improve build scripts. 2019-02-02 17:59:18 +01:00
James Cole
6b94210de5 Include adm64 build and manifest stuff. 2019-02-02 17:20:15 +01:00
James Cole
745624f044 Make script executable. 2019-02-02 17:10:47 +01:00
James Cole
68956eabf2 Refer to ARM script. 2019-02-02 17:08:46 +01:00
James Cole
87591f6868 Also expand dockerfile. 2019-02-02 16:10:55 +01:00
James Cole
cc238a639e Expand some commands. 2019-02-02 16:08:15 +01:00
James Cole
de6758a972 Update docker file and entrypoint. 2019-02-02 15:56:36 +01:00
James Cole
49647eedae Fix command again. 2019-02-02 15:49:06 +01:00
James Cole
f24e35236f Fix command. 2019-02-02 15:46:37 +01:00
James Cole
6f200a423e Correct command. 2019-02-02 15:33:33 +01:00
James Cole
ef57c0601a Let's see what happens when we build using another base. 2019-02-02 15:30:23 +01:00
James Cole
9632e99bd3 Fix #2043 2019-02-02 14:33:24 +01:00
James Cole
93e18ea195 Remove unused values from charts. 2019-02-02 12:34:58 +01:00
James Cole
f39676eedd Fix for #2041 2019-02-02 12:28:37 +01:00
James Cole
1d73ff7c13 Fix test coverage. [skip ci] 2019-02-02 09:53:19 +01:00
James Cole
384e64d94d Update docs [skip ci] 2019-02-02 07:04:08 +01:00
James Cole
96bb880d1b #2034 2019-02-02 07:00:09 +01:00
James Cole
cf307a00e1 Code for #2037 2019-02-02 06:10:52 +01:00
James Cole
5a97bcb123 Update docker decrypt location. 2019-01-31 21:00:41 +01:00
James Cole
50f7620d7a See what happens when we try ARM. 2019-01-31 20:40:03 +01:00
James Cole
78ec3ebe59 Remove test routine, push image to test repository. 2019-01-31 20:23:09 +01:00
James Cole
96f0faa546 Change Travis build. 2019-01-31 20:09:30 +01:00
James Cole
4307bf3b83 Remove Firefly III's ability to encrypt data in the database. 2019-01-31 19:56:09 +01:00
James Cole
d56bd85328 New changelog. [skip ci] 2019-01-28 20:26:37 +01:00
James Cole
9f2f258ad9 Added new language, Chinese Traditional. 2019-01-28 20:10:52 +01:00
James Cole
894b48df8e Introduce new model for future remodeling of split transactions. 2019-01-28 20:00:40 +01:00
James Cole
195fba5931 New translations. 2019-01-28 19:37:15 +01:00
James Cole
0556516524 Summarise import options. 2019-01-28 19:36:30 +01:00
James Cole
5cad4e9d82 Fix #1989 2019-01-28 19:36:17 +01:00
James Cole
8607fd4997 Fix #2033 2019-01-28 18:21:22 +01:00
James Cole
d905849b71 Experimental fix for #2031 2019-01-27 21:23:18 +01:00
James Cole
968505ac0e Route can be null. 2019-01-27 18:24:11 +01:00
James Cole
e482b079df Cleanup help file. 2019-01-27 18:23:47 +01:00
James Cole
13cddd8f12 Fix bill test 2019-01-27 18:11:10 +01:00
James Cole
20b458f35d Ignore form action when doing oAuth2. 2019-01-27 17:15:40 +01:00
James Cole
cec8210d8b Fix #2009 2019-01-27 17:09:39 +01:00
James Cole
e6ff895eff Fix #2001 2019-01-27 17:02:45 +01:00
James Cole
74ff17da7e Fix #1990 2019-01-27 16:43:43 +01:00
James Cole
7e1d430003 Fix #1967 2019-01-27 16:42:28 +01:00
James Cole
eeff2895aa Fix #1923 2019-01-27 16:36:08 +01:00
James Cole
f6d88521dd Fix test coverage. 2019-01-27 12:30:52 +01:00
James Cole
c3e6e4f034 Simplify budget repository 2019-01-27 11:05:22 +01:00
James Cole
6c09330e74 Simplify recurring repository. 2019-01-27 11:00:28 +01:00
James Cole
b100c02779 Expand income and expense info without a category. 2019-01-27 10:51:34 +01:00
James Cole
ef6f00fcf5 Make sure "no category" is added to API as well. 2019-01-27 10:51:14 +01:00
James Cole
ad9405887a Remove cash again. 2019-01-27 10:51:00 +01:00
James Cole
01e45de605 Include cash accounts in income and expense charts. 2019-01-27 07:57:10 +01:00
James Cole
aebefe8bcf Fix currency mapper test 2019-01-27 07:48:57 +01:00
James Cole
5e41641d7c Simplify account transformer 2019-01-27 07:48:49 +01:00
James Cole
0f3b3a07d1 Remove debug code. 2019-01-27 07:48:37 +01:00
James Cole
156d2fbaa4 Simplify autocomplete controller 2019-01-27 07:48:29 +01:00
James Cole
4866b9dabb Fix old category chart 2019-01-27 07:48:17 +01:00
James Cole
62ce9660b4 Simplify rule group controller 2019-01-27 07:47:37 +01:00
James Cole
873c2ce10d New routes for new API endpoints. 2019-01-26 20:19:06 +01:00
James Cole
2c2410e710 Category overview chart in API and support methods. 2019-01-26 20:18:42 +01:00
James Cole
624eb0f958 Add new revenue and expense overview chart endpoint to API 2019-01-26 20:10:48 +01:00
James Cole
770e44d1bd Add new tag-cloud API endpoint. 2019-01-26 20:10:35 +01:00
James Cole
7144798377 Add new chart API endpoints. 2019-01-26 12:11:45 +01:00
James Cole
606ff3ed00 Fix problem with rule trigger tester. 2019-01-26 12:11:37 +01:00
James Cole
ca731adce7 Add account name to piggy bank transformer API. 2019-01-26 12:11:23 +01:00
James Cole
ca71f3008c Fix a bug where the importer was unable to create new currencies. 2019-01-26 12:11:10 +01:00
James Cole
7c56c14fa0 Add fallback for empty front page account preferences thing. 2019-01-26 12:10:53 +01:00
James Cole
e3433aa95b Add pie chart for available budget API 2019-01-26 12:10:35 +01:00
James Cole
0df00556db Make sure chart end point returns machine readable dates. 2019-01-26 12:10:21 +01:00
James Cole
a55dd97d51 Add ability to return single preference to the API 2019-01-26 12:10:02 +01:00
James Cole
b52959d41a Add limit parameter to capabilities of API 2019-01-26 12:09:46 +01:00
James Cole
163b0a2105 Add limit parameter to /budgets/x/transactions API endpoint. 2019-01-26 12:09:32 +01:00
James Cole
d33c89a8ee Add start and end data to the available_budget list endpoint API 2019-01-26 12:09:16 +01:00
James Cole
d0d73c5e3e Add limit parameter to account/x/transactions API end point. 2019-01-26 12:08:41 +01:00
James Cole
799331b945 New API endpoints. 2019-01-18 05:38:23 +01:00
James Cole
43073fa1fc Translation updates. 2019-01-12 10:32:59 +01:00
James Cole
7f91ff4b0c Fix #1996 2019-01-11 16:57:40 +01:00
James Cole
ba778d4c60 Update some issue templates. 2019-01-08 17:05:42 +01:00
James Cole
cddb3d82dd Expand changelog. 2019-01-07 18:23:04 +01:00
James Cole
a221f02393 Fix #1983 2019-01-07 18:19:48 +01:00
James Cole
fa4959fc6a Fix #1979 2019-01-07 18:07:59 +01:00
James Cole
bbd68e78c1 Fix #1975 2019-01-07 18:05:43 +01:00
James Cole
3c20938e0f Merge pull request #1977 from BastiaanN/docker-cache
Added memcached support and zend opcache
2019-01-07 15:35:28 +01:00
Bastiaan Nijkamp
94745cb3d5 Added memcached support and zend opcache 2019-01-07 12:14:10 +01:00
James Cole
602b328914 Add tokenizer to composer.lock [skip ci] 2019-01-06 16:55:06 +01:00
James Cole
27802c930f Remove trial code, restore repository. Should not have committed this. Oh well. Let's continue this in a branch like I'm supposed to. 2019-01-06 16:23:37 +01:00
James Cole
3b4622a9ac Add API config. 2019-01-06 16:01:01 +01:00
James Cole
18fb2e91b1 Remove not working command. 2019-01-06 16:00:36 +01:00
James Cole
214cd9c262 Require tokenizer. 2019-01-06 16:00:23 +01:00
James Cole
337c289a74 This combination of JS things works. 2019-01-06 13:37:25 +01:00
James Cole
0d560ec442 Fix for #1968 2019-01-04 17:10:16 +01:00
James Cole
a4d4bf3006 Remove not working parameters, add more debug 2019-01-04 17:03:34 +01:00
James Cole
40028c8be7 These are all the files from the Tabler react repos, made to work with laravel and webpack. 2019-01-04 17:03:16 +01:00
James Cole
961e7e92b3 Some new files for JS rendering of new layout. 2019-01-04 16:37:24 +01:00
James Cole
d3576c4151 Update composer file. 2019-01-04 16:15:13 +01:00
James Cole
1d51a0f396 Update some v2 views. 2019-01-03 19:02:47 +01:00
James Cole
f62b7e4ae9 Update test component. 2019-01-03 19:02:31 +01:00
James Cole
62135bc1cb Updated changelog. 2019-01-03 19:02:18 +01:00
James Cole
b245facdfe Small fixes. 2019-01-03 19:01:32 +01:00
James Cole
577f024310 Make code default for React things. 2019-01-03 19:01:15 +01:00
James Cole
1c2206cb9f Fix issue with recurring transactions and piggy banks. 2019-01-03 18:57:34 +01:00
James Cole
16ba65c424 Merge pull request #1963 from BoGnY/fix/csp-directive
Fixed missing CSP directive and value
2019-01-03 16:13:33 +01:00
Luca Bognolo
59cfaa20ab Fixed wrong CSP value
Signed-off-by: Luca Bognolo <git@bogny.eu>
2019-01-03 16:04:48 +01:00
Luca Bognolo
ab0471c78e Fixed missing CSP directive and value
Signed-off-by: Luca Bognolo <git@bogny.eu>
2019-01-03 00:21:21 +01:00
James Cole
464642250b Fix #1955. 2019-01-01 19:32:37 +01:00
James Cole
d7bf9e234c Fix test coverage. 2019-01-01 18:03:24 +01:00
James Cole
bf257574f9 Update PR template [skip ci] 2019-01-01 17:56:38 +01:00
James Cole
92e42199b5 Wrote the changelog so I don't have to do it later [skip ci] 2019-01-01 17:50:44 +01:00
James Cole
99b0b24a89 Enable currencies if not enabled yet. #1952 2019-01-01 17:17:38 +01:00
James Cole
d86383eba8 Set correct copyright notice @BoGnY #1947 2019-01-01 16:49:41 +01:00
James Cole
4fc12f232a Merge pull request #1951 from BoGnY/feature/cer-provider
Implemented multiple Currency Exchange Rates sites engine
2019-01-01 16:47:01 +01:00
Luca Bognolo
729483102e Implemented multiple Currency Exchange Rates sites engine
Signed-off-by: Luca Bognolo <git@bogny.eu>
2019-01-01 16:32:22 +01:00
James Cole
7ac00935d5 Update old assets. 2019-01-01 15:44:55 +01:00
James Cole
639be5b104 Add newlines to views. 2019-01-01 15:43:03 +01:00
James Cole
17359c5e42 Various experiments with JS and React and what-not. 2019-01-01 15:41:54 +01:00
James Cole
7aa5eee3aa Better fix for date. 2019-01-01 15:29:24 +01:00
James Cole
98d6a7f32e Update some meta files [skip ci] 2019-01-01 14:11:57 +01:00
James Cole
0475b68817 Merge pull request #1948 from BoGnY/feature/croatian-kuna
Add Croatian kuna to currency seeder
2019-01-01 13:15:01 +01:00
Luca Bognolo
2a89d49ba7 Add Croatian kuna to currency seeder
Signed-off-by: Luca Bognolo <git@bogny.eu>
2018-12-31 17:53:13 +01:00
James Cole
aeab5f0333 Fix to make sure that the index respects the fiscal year. 2018-12-31 13:44:24 +01:00
James Cole
f80de95bb0 Refactor code to traits. 2018-12-31 08:11:57 +01:00
James Cole
e7bcc01fe8 Refactor code to traits. 2018-12-31 07:58:13 +01:00
James Cole
9fcbce241e Will speed up Docker start. 2018-12-31 07:57:33 +01:00
James Cole
78a71fb7c6 Add copyright [skip ci] 2018-12-31 07:57:21 +01:00
James Cole
3e25d66902 Add newlines and copyright notices. 2018-12-31 07:48:23 +01:00
James Cole
005e4ba4d1 Improve entrypoint script so Docker will build better. 2018-12-30 15:46:09 +01:00
James Cole
0f83582fed Fix #1944 2018-12-30 15:44:33 +01:00
James Cole
d736ffec8e Fix #1945 2018-12-30 15:31:07 +01:00
James Cole
27d6091e45 Make sure favicon links are relative. #1942 2018-12-30 14:51:50 +01:00
James Cole
290405b987 Some meta improvements [skip ci] 2018-12-30 06:19:40 +01:00
James Cole
7fa2fed147 Fix for #1938 2018-12-30 06:19:16 +01:00
James Cole
fd233510ee Fix https://github.com/firefly-iii/firefly-iii/issues/1933 / https://github.com/Adldap2/Adldap2/issues/629 2018-12-30 06:10:46 +01:00
James Cole
354be8dc88 Fix for #1927 2018-12-29 21:57:24 +01:00
James Cole
9750bcba76 Another fix for https://github.com/firefly-iii/firefly-iii/issues/1939 2018-12-29 21:53:34 +01:00
James Cole
15d08ca17b Fix for https://github.com/firefly-iii/firefly-iii/issues/1939 2018-12-29 21:51:20 +01:00
James Cole
1bc72fa49f Flatten command, different entrypoint command. 2018-12-27 15:21:37 +01:00
James Cole
68061fd292 Rename some translations. 2018-12-27 06:05:01 +01:00
James Cole
2b77e9d96d Update Docker files. 2018-12-27 06:04:49 +01:00
James Cole
5c48d2d56a Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop
* 'develop' of https://github.com/firefly-iii/firefly-iii:
  Fix fullcalendar.mi.js links
2018-12-27 05:24:38 +01:00
James Cole
11a0734009 Merge pull request #1929 from a1ex4/develop
Fix fullcalendar.min.js links
2018-12-27 05:05:16 +01:00
a1ex4
2ccbfe45f2 Fix fullcalendar.mi.js links 2018-12-26 18:43:50 +01:00
James Cole
3793b08d90 Build new packages for Sandstorm. 2018-12-23 19:21:54 +01:00
James Cole
06b63209b1 Show debug info in sandstorm. 2018-12-23 19:21:40 +01:00
James Cole
02e318b3e6 New file list. 2018-12-23 19:21:18 +01:00
James Cole
3b78b26039 Merge tag '4.7.9' into develop
4.7.9
2018-12-23 18:24:34 +01:00
James Cole
f57fc06347 Merge branch 'release/4.7.9' 2018-12-23 18:24:28 +01:00
James Cole
32a14ab8e0 Update composer file. 2018-12-23 18:19:30 +01:00
James Cole
5fafe6e4f4 Update changelog and version references. 2018-12-23 18:19:19 +01:00
James Cole
ee907aba34 New language strings 2018-12-23 18:18:53 +01:00
James Cole
2ae9a6f020 Remove curl build, since bunq seems to work fine without it.
Can I tag people in commit messages? @OGKevin
2018-12-23 10:57:46 +01:00
James Cole
479841a609 Updated favicons. [skip ci] 2018-12-23 07:50:55 +01:00
James Cole
10da45edce Add some JS and CSS resources. 2018-12-23 07:35:15 +01:00
James Cole
36c4a30fe3 Fix currency ID index error. 2018-12-23 07:34:51 +01:00
James Cole
d4542cc3fc Fix currency ID index error. 2018-12-23 07:34:23 +01:00
James Cole
dea02e374f Fix currency ID index error. 2018-12-23 07:34:00 +01:00
James Cole
91deac706c Some new code for the new layout. 2018-12-23 07:21:47 +01:00
James Cole
17e85ca2cf Update docker files. 2018-12-22 06:45:07 +01:00
James Cole
91593335ef Available budget end point also gives info on spent info #1884 2018-12-22 06:40:25 +01:00
James Cole
d27176c0b3 Fix #1828 2018-12-22 06:21:06 +01:00
James Cole
070e1e81ae Fix #1859 2018-12-22 06:07:57 +01:00
James Cole
2c7d94e5e9 Update tests and code. 2018-12-21 16:38:10 +01:00
James Cole
a5520d45e7 Store booleans correctly. 2018-12-21 15:42:57 +01:00
James Cole
922c8703f5 Fix for #1890 2018-12-21 15:42:40 +01:00
James Cole
75cc024e28 Expand account types to fix https://github.com/firefly-iii/api-docs-generator/issues/2 2018-12-21 15:26:48 +01:00
James Cole
75c6da7730 Fix #1909 2018-12-21 10:55:34 +01:00
James Cole
35c8b2fce8 Fix #1901 2018-12-21 10:17:38 +01:00
James Cole
4f5d8f830e Various fixes and checks. 2018-12-21 10:11:18 +01:00
James Cole
78cb6da21a Fix for #1898 2018-12-21 10:11:01 +01:00
James Cole
a7e1a51476 Make sure rules work. 2018-12-21 09:01:21 +01:00
James Cole
a8ff77addd Various improvements to Docker files. 2018-12-21 07:36:53 +01:00
James Cole
3c9c46d574 Expand tests 2018-12-21 07:36:40 +01:00
James Cole
8ede404b8a Refer to correct field name. 2018-12-21 06:51:21 +01:00
James Cole
a7585e3040 Fix for #1985 2018-12-21 06:51:00 +01:00
James Cole
cd47b45fce Add some debug info to bunq and update the composer file [skip ci] 2018-12-20 23:06:47 +01:00
James Cole
b6c23b8eb3 Fix #1893 2018-12-20 22:32:33 +01:00
James Cole
297e12f6e4 New language strings. 2018-12-20 22:23:22 +01:00
James Cole
29b02fcac2 Fix issue with spaces in tag report. 2018-12-20 22:19:07 +01:00
James Cole
0c2218762c Update entry point script. 2018-12-20 22:13:16 +01:00
James Cole
27bc03fc20 Cover all transformers. 2018-12-20 22:03:34 +01:00
James Cole
84b3195e9b Improve transformer coverage. 2018-12-20 20:50:05 +01:00
James Cole
6f54f41946 Add new tests for transformers. 2018-12-20 05:46:05 +01:00
James Cole
c1ae0ab57d Update some tests. 2018-12-19 19:23:52 +01:00
James Cole
446ff81335 Make sure transformers match API definition. 2018-12-19 19:02:16 +01:00
James Cole
03b4a50317 Improve transformers and tests 2018-12-19 06:06:01 +01:00
James Cole
06c3362332 New language strings. 2018-12-18 20:08:38 +01:00
James Cole
5f059d02ad Add a profile warning for access tokens [skip ci] 2018-12-18 20:00:04 +01:00
James Cole
3e71a103a2 Improve test coverage. 2018-12-18 19:57:23 +01:00
James Cole
e46561347d Some rule fixes. 2018-12-18 07:08:46 +01:00
James Cole
a3f33ae888 Go from get to post. 2018-12-18 07:08:35 +01:00
James Cole
f71359e73d Improve test coverage. 2018-12-18 07:08:23 +01:00
James Cole
a0b475d8ef Improve test coverage. 2018-12-18 07:07:51 +01:00
James Cole
f3a597cd12 Improve test coverage. 2018-12-18 06:57:42 +01:00
James Cole
29f2ee93d1 Improve test coverage. 2018-12-18 06:49:26 +01:00
James Cole
454b3ebd97 Improve tests. 2018-12-17 07:09:44 +01:00
James Cole
89942ee49c Improve test coverage for API. 2018-12-16 13:55:19 +01:00
James Cole
746bd2ce92 Do not email about OAuth exceptions. 2018-12-16 13:15:09 +01:00
James Cole
747602a9cb Make a new abstract class for transformers. 2018-12-15 22:03:42 +01:00
James Cole
8ce43a3fe1 Make sure transformers can be mocked. 2018-12-15 22:03:05 +01:00
James Cole
1284f9cecc Refactored API code surrounding accounts to make transformers mockable. 2018-12-15 07:59:49 +01:00
James Cole
c54541b839 Learned that I should not refer to env vars directly so I removed all references. 2018-12-15 07:59:02 +01:00
James Cole
4bd94e5450 Update request data to match API definition. 2018-12-14 18:33:07 +01:00
James Cole
2a30293905 Improve test coverage. 2018-12-12 20:30:25 +01:00
James Cole
159fffef2e Improve test coverage. 2018-12-10 21:45:44 +01:00
James Cole
5313652d7a New translations. 2018-12-09 20:56:04 +01:00
James Cole
ae4612f134 Fix test coverage. 2018-12-09 20:54:11 +01:00
James Cole
97f6e68164 Complete accounts and bills end point. 2018-12-09 13:09:43 +01:00
James Cole
108d43f967 Budgets and budget limits end point. 2018-12-09 08:45:53 +01:00
James Cole
b0e1c85c55 Expand API for categories. 2018-12-09 06:58:03 +01:00
James Cole
82e4055fa6 Expand currency endpoint. 2018-12-09 06:51:53 +01:00
James Cole
2a68ce6c90 Expand config API endpoint. 2018-12-09 06:39:56 +01:00
James Cole
05b0425929 Expand API and routes. 2018-12-08 21:26:20 +01:00
James Cole
3b15415a1b Build import job API endpoint. 2018-12-08 08:22:53 +01:00
James Cole
00fb809ab9 Remove include parameter. 2018-12-08 08:22:34 +01:00
James Cole
494aa15567 Refactored and extended link types. 2018-12-08 07:57:29 +01:00
James Cole
07450f9f23 Update preferences API. 2018-12-07 20:39:36 +01:00
James Cole
f0de469053 Expand API for recurring transactions. 2018-12-07 20:20:54 +01:00
James Cole
8e4092e7d7 Code and routes for rules. 2018-12-07 16:03:05 +01:00
James Cole
9a2e5c36a1 Finish up rule groups API 2018-12-07 15:36:04 +01:00
James Cole
b782316cc0 Update rule group API 2018-12-07 07:49:16 +01:00
James Cole
3e84f9664f Update API for tags. 2018-12-07 07:41:12 +01:00
James Cole
01e789f5ce New translations. 2018-12-05 19:24:42 +01:00
James Cole
b2381f4657 Add tag endpoint. 2018-12-05 19:12:38 +01:00
James Cole
64a3e46cbe New endpoints for transactions. 2018-12-04 19:36:54 +01:00
James Cole
c0d6d0e28e Update API to match new end points. 2018-12-03 15:57:15 +01:00
James Cole
c13d0da9fa Mark new version 2018-12-03 07:18:36 +01:00
James Cole
050334a648 Various API updates. 2018-12-03 07:18:05 +01:00
James Cole
0256337855 Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop
* 'develop' of https://github.com/firefly-iii/firefly-iii:
  Fix typo
2018-12-03 07:17:19 +01:00
James Cole
bb073e4b6e Merge pull request #1891 from emansih/develop
Fix typo
2018-12-02 16:03:06 +01:00
Daniel Quah
40341ac14e Fix typo 2018-12-02 22:14:51 +08:00
James Cole
4420df6e5d New views for password reset, login and other user forms. 2018-11-25 11:31:02 +01:00
James Cole
99cc7a9a60 Upgrade datepicker. 2018-11-25 07:07:44 +01:00
James Cole
7ec1b985d0 Fix #1835 2018-11-25 07:04:35 +01:00
James Cole
9f19d26a23 Working test code. 2018-11-25 06:37:26 +01:00
James Cole
e766614630 Title for login page. 2018-11-25 06:37:14 +01:00
James Cole
ad49553b9c New favicons. 2018-11-25 06:37:03 +01:00
James Cole
1b3b39d2ea Add option to disable the X-Frame header 2018-11-24 07:24:32 +01:00
James Cole
cb68505204 Merge branch 'layout-v2' into develop
* layout-v2:
  First version of login page.
  Make FA and Google Font local
  Basic views.
  All assets for tabler layout.
2018-11-24 07:15:32 +01:00
James Cole
a574dca0cc Make sure pref is always filled in. 2018-11-23 10:39:33 +01:00
James Cole
c3fa1612d6 Fix for #1811 2018-11-23 08:40:08 +01:00
James Cole
7a8f6d8660 Update composer libs. 2018-11-23 08:32:23 +01:00
James Cole
9dda801fae Some new strings. 2018-11-23 08:32:11 +01:00
James Cole
344742c493 Improve bunq import for #1857 2018-11-23 08:31:58 +01:00
James Cole
886d05d436 Fix for #1622 and a special debug view. 2018-11-23 08:31:00 +01:00
James Cole
baac2ad921 Add Swedish to config file [skip ci] 2018-11-21 06:25:56 +01:00
James Cole
c3337c9bac Fix #1878 2018-11-21 06:21:57 +01:00
James Cole
6a6ca22bf0 Merge branch 'develop' into layout-v2
* develop:
  Fix date #1877
  Fix for #1877
  Auth routes now have names.
  First step towards a debug view for transactions.
  Username for FinTS import is not mandatory.
2018-11-21 06:20:23 +01:00
James Cole
f873f5488f Fix date #1877 2018-11-21 06:13:30 +01:00
James Cole
e18f843a57 Fix for #1877 2018-11-21 06:08:14 +01:00
James Cole
56a92505bd Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop
* 'develop' of https://github.com/firefly-iii/firefly-iii:
  Username for FinTS import is not mandatory.
2018-11-21 05:55:37 +01:00
James Cole
a8bc7dd7c1 Auth routes now have names. 2018-11-21 05:55:11 +01:00
James Cole
94614550fc First step towards a debug view for transactions. 2018-11-21 05:54:57 +01:00
James Cole
b2052615e3 First version of login page. 2018-11-21 05:52:57 +01:00
James Cole
f51b9217f5 Make FA and Google Font local 2018-11-21 05:52:34 +01:00
James Cole
d892a87d1d Merge pull request #1876 from bnw/develop
Username for FinTS import is not mandatory.
2018-11-19 10:52:51 +01:00
Ben
b7182f9462 Username for FinTS import is not mandatory. 2018-11-18 22:36:05 +01:00
James Cole
79518753ad Basic views. 2018-11-17 08:30:11 +01:00
James Cole
01634ed5e9 All assets for tabler layout. 2018-11-17 08:29:52 +01:00
James Cole
f7abf8a02d Support multiple layouts. 2018-11-17 08:28:04 +01:00
James Cole
3f7f1a50e7 Fix #1864 2018-11-16 19:23:06 +01:00
James Cole
a5a770e750 Fix for #1766 2018-11-16 15:26:37 +01:00
James Cole
9cd10f58ed Expand API with fields: liability_type, liability_amount, liability_start_date, interest, interest_period. Improve IBAN field. #1866 2018-11-14 19:41:36 +01:00
James Cole
5011531066 Small chart improvements for #1865 2018-11-12 19:31:00 +01:00
James Cole
d1c7a9767a Remove reference to temp_amount 2018-11-12 19:17:17 +01:00
James Cole
d78e11170b Fix string. 2018-11-11 20:09:50 +01:00
James Cole
74f8dceb38 Fix #1865 2018-11-11 20:09:35 +01:00
James Cole
1fbf91c768 Fix tests and improve string. 2018-11-11 08:34:38 +01:00
James Cole
edf764aaf4 Will now warn about non-existent attachments. 2018-11-11 07:03:36 +01:00
James Cole
7380c5096e Fix test coverage. 2018-11-10 15:15:29 +01:00
James Cole
45699f13fc Update composer files. 2018-11-10 10:50:41 +01:00
James Cole
1fd8c9adc2 Fix image link 2018-11-10 10:50:20 +01:00
James Cole
5cc840e390 Verify routine will check currency usage. 2018-11-10 10:50:12 +01:00
James Cole
e491dda229 Currencies can now be enabled and disabled. 2018-11-10 10:04:46 +01:00
James Cole
daa8aa5c9d Fix for #1858 2018-11-09 19:00:24 +01:00
James Cole
0561554726 Bigger sleep cycle, but improve page count to compensate. 2018-11-06 16:51:51 +01:00
James Cole
34abc6490c Fix issue with transaction journal factory. 2018-11-06 05:50:36 +01:00
James Cole
66f309f5ed Clean up debug views. 2018-11-02 21:17:07 +01:00
James Cole
2283f3e786 Fix #1840 2018-11-02 21:16:47 +01:00
James Cole
d261c793ae Add extra cache path. 2018-11-02 20:40:35 +01:00
James Cole
31448a3add Move all layout files to a "v1" directory. 2018-11-02 19:59:29 +01:00
James Cole
d85ccb6ab2 Try to catch a null pointer. 2018-11-02 05:59:22 +01:00
James Cole
bd0be97137 Merge pull request #1848 from kriegalex/feature/swiss-franc
Add swiss franc to currency seeder
2018-11-01 16:06:18 +01:00
Marco Lourenço
81d70a57ca Add swiss franc to currency seeder 2018-11-01 15:15:49 +01:00
James Cole
b17f2f5d1f Improve logging #1607 2018-10-31 16:33:03 +01:00
James Cole
53ed5b2975 Improved logging for bunq routine. #1607 2018-10-31 16:25:21 +01:00
James Cole
5626fee282 Merge tag '4.7.8' into develop
4.7.8
2018-10-28 19:44:37 +01:00
2062 changed files with 157265 additions and 30786 deletions

View File

@@ -45,6 +45,14 @@ DB_DATABASE=${FF_DB_NAME}
DB_USERNAME=${FF_DB_USER}
DB_PASSWORD="${FF_DB_PASSWORD}"
# PostgreSQL supports SSL. You can configure it here.
PGSQL_SSL=${PGSQL_SSL}
PGSQL_SSL_MODE=${PGSQL_SSL_MODE}
PGSQL_SSL_ROOT_CERT=${PGSQL_SSL_ROOT_CERT}
PGSQL_SSL_CERT=${PGSQL_SSL_CERT}
PGSQL_SSL_KEY=${PGSQL_SSL_KEY}
PGSQL_SSL_CRL_FILE=${PGSQL_SSL_CRL_FILE}
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file
SESSION_DRIVER=file
@@ -93,8 +101,14 @@ SEND_REPORT_JOURNALS=${SEND_REPORT_JOURNALS}
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=${MAPBOX_API_KEY}
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this will only work for paid fixer.io accounts because they severly limited
# Firefly III currently supports two provider for live Currency Exchange Rates:
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
CER_PROVIDER=${CER_PROVIDER}
# If you have select "fixer" as default currency exchange rates,
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
# the free API up to the point where you might as well offer nothing.
FIXER_API_KEY=${FIXER_API_KEY}
@@ -106,7 +120,7 @@ ANALYTICS_ID=${ANALYTICS_ID}
USE_ENCRYPTION=true
# Firefly III has two options for user authentication. "eloquent" is the default,
# and "adldap" for LDAP servers.
# and "ldap" for LDAP servers.
# For full instructions on these settings please visit:
# https://firefly-iii.readthedocs.io/en/latest/installation/authentication.html
LOGIN_PROVIDER=${LOGIN_PROVIDER}
@@ -129,8 +143,6 @@ ADLDAP_ADMIN_PASSWORD="${ADLDAP_ADMIN_PASSWORD}"
ADLDAP_ACCOUNT_PREFIX="${ADLDAP_ACCOUNT_PREFIX}"
ADLDAP_ACCOUNT_SUFFIX="${ADLDAP_ACCOUNT_SUFFIX}"
ADLDAP_ADMIN_ACCOUNT_PREFIX="${ADLDAP_ADMIN_ACCOUNT_PREFIX}"
ADLDAP_ADMIN_ACCOUNT_SUFFIX="${ADLDAP_ADMIN_ACCOUNT_SUFFIX}"
# LDAP authentication settings.
ADLDAP_PASSWORD_SYNC=${ADLDAP_PASSWORD_SYNC}
@@ -146,6 +158,10 @@ WINDOWS_SSO_KEY=${WINDOWS_SSO_KEY}
# field to sync as local username.
ADLDAP_SYNC_FIELD=${ADLDAP_SYNC_FIELD}
# You can disable the X-Frame-Options header if it interfears with tools like
# Organizr. This is at your own risk.
DISABLE_FRAME_HEADER=${DISABLE_FRAME_HEADER}
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
@@ -166,3 +182,4 @@ IS_DOCKER=true
IS_SANDSTORM=false
IS_HEROKU=false
BUNQ_USE_SANDBOX=false
FFIII_LAYOUT=v1

View File

@@ -1,73 +1,3 @@
# This is the main Apache server configuration file. It contains the
# configuration directives that give the server its instructions.
# See http://httpd.apache.org/docs/2.4/ for detailed information about
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
# hints.
#
#
# Summary of how the Apache 2 configuration works in Debian:
# The Apache 2 web server configuration in Debian is quite different to
# upstream's suggested way to configure the web server. This is because Debian's
# default Apache2 installation attempts to make adding and removing modules,
# virtual hosts, and extra configuration directives as flexible as possible, in
# order to make automating the changes and administering the server as easy as
# possible.
# It is split into several files forming the configuration hierarchy outlined
# below, all located in the /etc/apache2/ directory:
#
# /etc/apache2/
# |-- apache2.conf
# | `-- ports.conf
# |-- mods-enabled
# | |-- *.load
# | `-- *.conf
# |-- conf-enabled
# | `-- *.conf
# `-- sites-enabled
# `-- *.conf
#
#
# * apache2.conf is the main configuration file (this file). It puts the pieces
# together by including all remaining configuration files when starting up the
# web server.
#
# * ports.conf is always included from the main configuration file. It is
# supposed to determine listening ports for incoming connections which can be
# customized anytime.
#
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
# directories contain particular configuration snippets which manage modules,
# global configuration fragments, or virtual host configurations,
# respectively.
#
# They are activated by symlinking available configuration files from their
# respective *-available/ counterparts. These should be managed by using our
# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
# their respective man pages for detailed information.
#
# * The binary is called apache2. Due to the use of environment variables, in
# the default configuration, apache2 needs to be started/stopped with
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
# work with the default configuration.
# Global configuration
#
#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# NOTE! If you intend to place this on an NFS (or otherwise network)
# mounted filesystem then please read the Mutex documentation (available
# at <URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex>);
# you will save yourself a lot of trouble.
#
# Do NOT add a slash at the end of the directory path.
#
#ServerRoot "/etc/apache2"
#
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
#

22
.deploy/docker/build-amd64.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# build image
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
if [ "$TRAVIS_BRANCH" == "develop" ]; then
echo "Build develop amd64"
docker build -t jc5x/firefly-iii:develop-amd64 -f Dockerfile.amd64 .
docker tag jc5x/firefly-iii:develop-amd64 jc5x/firefly-iii:develop-$VERSION-amd64
docker push jc5x/firefly-iii:develop-amd64
docker push jc5x/firefly-iii:develop-$VERSION-amd64
fi
if [ "$TRAVIS_BRANCH" == "master" ]; then
echo "Build master amd64"
docker build -t jc5x/firefly-iii:latest-amd64 -f Dockerfile.amd64 .
docker tag jc5x/firefly-iii:latest-amd64 jc5x/firefly-iii:release-$VERSION-amd64
docker push jc5x/firefly-iii:latest-amd64
docker push jc5x/firefly-iii:release-$VERSION-amd64
fi

30
.deploy/docker/build-arm.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env bash
docker run --rm --privileged multiarch/qemu-user-static:register --reset
# get qemu-arm-static binary
mkdir tmp
pushd tmp && \
curl -L -o qemu-arm-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/v2.6.0/qemu-arm-static.tar.gz && \
tar xzf qemu-arm-static.tar.gz && \
popd
# build image
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
if [ "$TRAVIS_BRANCH" == "develop" ]; then
echo "Build develop arm"
docker build --tag jc5x/firefly-iii:develop-arm --file Dockerfile.arm .
docker tag jc5x/firefly-iii:develop-arm jc5x/firefly-iii:develop-$VERSION-arm
docker push jc5x/firefly-iii:develop-arm
docker push jc5x/firefly-iii:develop-$VERSION-arm
fi
if [ "$TRAVIS_BRANCH" == "master" ]; then
echo "Build master arm"
docker build --tag jc5x/firefly-iii:latest-arm --file Dockerfile.arm .
docker tag jc5x/firefly-iii:latest-arm jc5x/firefly-iii:release-$VERSION-arm
docker push jc5x/firefly-iii:latest-arm
docker push jc5x/firefly-iii:release-$VERSION-arm
fi

View File

@@ -1,11 +0,0 @@
[program:cron]
command=/usr/sbin/cron -f -L 15
user=root
autostart=true
autorestart=true
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
startsecs=10
startretries=3

View File

@@ -1,6 +0,0 @@
FF_APP_ENV=local
FF_APP_KEY=S0m3R@nd0mString0f32Ch@rsEx@ct1y
FF_DB_HOST=
FF_DB_NAME=
FF_DB_USER=
FF_DB_PASSWORD=

View File

@@ -1,29 +1,64 @@
#!/bin/bash
echo "Now in entrypoint.sh for Firefly III"
lscpu
# make sure the correct directories exists (suggested by @chrif):
mkdir -p $FIREFLY_PATH/storage/app
echo "Making directories..."
mkdir -p $FIREFLY_PATH/storage/app/public
mkdir -p $FIREFLY_PATH/storage/build
mkdir -p $FIREFLY_PATH/storage/database
mkdir -p $FIREFLY_PATH/storage/debugbar
mkdir -p $FIREFLY_PATH/storage/export
mkdir -p $FIREFLY_PATH/storage/framework/cache
mkdir -p $FIREFLY_PATH/storage/framework/cache/data
mkdir -p $FIREFLY_PATH/storage/framework/sessions
mkdir -p $FIREFLY_PATH/storage/framework/testing
mkdir -p $FIREFLY_PATH/storage/framework/views
mkdir -p $FIREFLY_PATH/storage/framework/views/v1
mkdir -p $FIREFLY_PATH/storage/framework/views/v2
mkdir -p $FIREFLY_PATH/storage/logs
mkdir -p $FIREFLY_PATH/storage/upload
echo "Touch DB file (if SQLlite)..."
if [[ $DB_CONNECTION == "sqlite" ]]
then
touch $FIREFLY_PATH/storage/database/database.sqlite
echo "Touched!"
fi
if [[ $FF_DB_CONNECTION == "sqlite" ]]
then
touch $FIREFLY_PATH/storage/database/database.sqlite
echo "Touched!"
fi
# make sure we own the volumes:
echo "Run chown on ${FIREFLY_PATH}/storage..."
chown -R www-data:www-data -R $FIREFLY_PATH/storage
echo "Run chmod on ${FIREFLY_PATH}/storage..."
chmod -R 775 $FIREFLY_PATH/storage
# remove any lingering files that may break upgrades:
echo "Remove log file..."
rm -f $FIREFLY_PATH/storage/logs/laravel.log
cat .env.docker | envsubst > .env
echo "Map environment variables on .env file..."
cat $FIREFLY_PATH/.deploy/docker/.env.docker | envsubst > $FIREFLY_PATH/.env
echo "Dump auto load..."
composer dump-autoload
echo "Discover packages..."
php artisan package:discover
echo "Run various artisan commands..."
php artisan migrate --seed
php artisan firefly:decrypt-all
php artisan firefly:upgrade-database
php artisan firefly:verify
php artisan passport:install
php artisan cache:clear
php artisan firefly:instructions install
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf --nodaemon
echo "Go!"
exec apache2-foreground

View File

@@ -1,6 +0,0 @@
[program:apache2]
command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2 -DFOREGROUND"
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0

35
.deploy/docker/manifest.sh Executable file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
if [ "$TRAVIS_BRANCH" == "develop" ]; then
TARGET=jc5x/firefly-iii:develop
ARM=jc5x/firefly-iii:develop-arm
AMD=jc5x/firefly-iii:develop-amd64
docker manifest create $TARGET $AMD $ARM
docker manifest annotate $TARGET $ARM --arch arm --os linux
docker manifest annotate $TARGET $AMD --arch amd64 --os linux
docker manifest push $TARGET
fi
echo "The version is $VERSION"
if [ "$TRAVIS_BRANCH" == "master" ]; then
TARGET=jc5x/firefly-iii:latest
ARM=jc5x/firefly-iii:latest-arm
AMD=jc5x/firefly-iii:latest-amd64
docker manifest create $TARGET $AMD $ARM
docker manifest annotate $TARGET $ARM --arch arm --os linux
docker manifest annotate $TARGET $AMD --arch amd64 --os linux
docker manifest push $TARGET
# and another one for version specific:
TARGET=jc5x/firefly-iii:release-$VERSION
ARM=jc5x/firefly-iii:release-$VERSION-arm
AMD=jc5x/firefly-iii:release-$VERSION-amd64
docker manifest create $TARGET $AMD $ARM
docker manifest annotate $TARGET $ARM --arch arm --os linux
docker manifest annotate $TARGET $AMD --arch amd64 --os linux
docker manifest push $TARGET
fi

View File

@@ -1,30 +0,0 @@
# supervisor config file
# Adapted from the config file distributed with the supervisor package on Debian
# Jessie
# Enable supervisord in non-daemon mode. Disable the logfile as we receive
# log messages via stdout/err. Set up the child process log directory in case
# the user doesn't set logging to stdout/err.
[supervisord]
nodaemon = true
logfile = NONE
pidfile = /var/run/supervisord.pid
childlogdir = /var/log/supervisor
loglevel=debug
# Enable supervisorctl via RPC interface over Unix socket
[unix_http_server]
file = /var/run/supervisor.sock
chmod = 0700
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl = unix:///var/run/supervisor.sock
# Include conf files for child processes
# Debian/Ubuntu packages use /etc/supervisor/conf.d
[include]
files = /etc/supervisor/conf.d/*.conf

View File

@@ -42,7 +42,12 @@ DB_CONNECTION=pgsql
# PostgreSQL supports SSL. You can configure it here.
PGSQL_SSL_MODE=prefer
PGSQL_SSL_ROOT_CERT=null
PGSQL_SSL_CERT=null
PGSQL_SSL_KEY=null
PGSQL_SSL_CRL_FILE=null
# If you're looking for performance improvements, you could install memcached.
@@ -93,8 +98,14 @@ SEND_REPORT_JOURNALS=true
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this will only work for paid fixer.io accounts because they severly limited
# Firefly III currently supports two provider for live Currency Exchange Rates:
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
CER_PROVIDER=fixer
# If you have select "fixer" as default currency exchange rates,
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
# the free API up to the point where you might as well offer nothing.
FIXER_API_KEY=
@@ -106,7 +117,7 @@ ANALYTICS_ID=
USE_ENCRYPTION=true
# Firefly III has two options for user authentication. "eloquent" is the default,
# and "adldap" for LDAP servers.
# and "ldap" for LDAP servers.
# For full instructions on these settings please visit:
# https://firefly-iii.readthedocs.io/en/latest/installation/authentication.html
LOGIN_PROVIDER=eloquent
@@ -130,8 +141,6 @@ ADLDAP_ADMIN_PASSWORD=
ADLDAP_ACCOUNT_PREFIX=
ADLDAP_ACCOUNT_SUFFIX=
ADLDAP_ADMIN_ACCOUNT_PREFIX=
ADLDAP_ADMIN_ACCOUNT_SUFFIX=
# LDAP authentication settings.
ADLDAP_PASSWORD_SYNC=false
@@ -147,6 +156,10 @@ WINDOWS_SSO_KEY=AUTH_USER
# field to sync as local username.
ADLDAP_SYNC_FIELD=userprincipalname
# You can disable the X-Frame-Options header if it interfears with tools like
# Organizr. This is at your own risk.
DISABLE_FRAME_HEADER=false
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
@@ -167,3 +180,4 @@ IS_DOCKER=false
IS_SANDSTORM=false
IS_HEROKU=true
BUNQ_USE_SANDBOX=false
FFIII_LAYOUT=v1

View File

@@ -1,4 +1,5 @@
en_US
es_ES
de_DE
fr_FR
it_IT
@@ -6,4 +7,4 @@ nl_NL
pl_PL
pt_BR
ru_RU
tr_TR
nb_NO

View File

@@ -3,7 +3,7 @@
APP_ENV=local
# Set to true if you want to see debug information in error screens.
APP_DEBUG=false
APP_DEBUG=true
# This should be your email address
SITE_OWNER=sandstorm@example.com
@@ -93,8 +93,14 @@ SEND_REPORT_JOURNALS=true
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this will only work for paid fixer.io accounts because they severly limited
# Firefly III currently supports two provider for live Currency Exchange Rates:
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
CER_PROVIDER=fixer
# If you have select "fixer" as default currency exchange rates,
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
# the free API up to the point where you might as well offer nothing.
FIXER_API_KEY=
@@ -106,7 +112,7 @@ ANALYTICS_ID=
USE_ENCRYPTION=true
# Firefly III has two options for user authentication. "eloquent" is the default,
# and "adldap" for LDAP servers.
# and "ldap" for LDAP servers.
# For full instructions on these settings please visit:
# https://firefly-iii.readthedocs.io/en/latest/installation/authentication.html
LOGIN_PROVIDER=eloquent
@@ -130,8 +136,6 @@ ADLDAP_ADMIN_PASSWORD=
ADLDAP_ACCOUNT_PREFIX=
ADLDAP_ACCOUNT_SUFFIX=
ADLDAP_ADMIN_ACCOUNT_PREFIX=
ADLDAP_ADMIN_ACCOUNT_SUFFIX=
# LDAP authentication settings.
ADLDAP_PASSWORD_SYNC=false
@@ -147,6 +151,10 @@ WINDOWS_SSO_KEY=AUTH_USER
# field to sync as local username.
ADLDAP_SYNC_FIELD=userprincipalname
# You can disable the X-Frame-Options header if it interfears with tools like
# Organizr. This is at your own risk.
DISABLE_FRAME_HEADER=true
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
@@ -167,3 +175,4 @@ IS_DOCKER=false
IS_SANDSTORM=true
IS_HEROKU=false
BUNQ_USE_SANDBOX=false
FFIII_LAYOUT=v1

View File

@@ -45,6 +45,13 @@ DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
# PostgreSQL supports SSL. You can configure it here.
PGSQL_SSL_MODE=prefer
PGSQL_SSL_ROOT_CERT=null
PGSQL_SSL_CERT=null
PGSQL_SSL_KEY=null
PGSQL_SSL_CRL_FILE=null
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file
SESSION_DRIVER=file
@@ -93,8 +100,14 @@ SEND_REPORT_JOURNALS=true
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this will only work for paid fixer.io accounts because they severly limited
# Firefly III currently supports two provider for live Currency Exchange Rates:
# "fixer" is the default (for backward compatibility), and "ratesapi" is the new one.
# RatesApi.IO (see https://ratesapi.io) is a FREE and OPEN SOURCE live currency exchange rates,
# built compatible with Fixer.IO, based on data published by European Central Bank, and don't require API key.
CER_PROVIDER=fixer
# If you have select "fixer" as default currency exchange rates,
# set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this WILL ONLY WORK FOR PAID fixer.io accounts because they severely limited
# the free API up to the point where you might as well offer nothing.
FIXER_API_KEY=
@@ -106,7 +119,7 @@ ANALYTICS_ID=
USE_ENCRYPTION=true
# Firefly III has two options for user authentication. "eloquent" is the default,
# and "adldap" for LDAP servers.
# and "ldap" for LDAP servers.
# For full instructions on these settings please visit:
# https://firefly-iii.readthedocs.io/en/latest/installation/authentication.html
LOGIN_PROVIDER=eloquent
@@ -130,8 +143,6 @@ ADLDAP_ADMIN_PASSWORD=
ADLDAP_ACCOUNT_PREFIX=
ADLDAP_ACCOUNT_SUFFIX=
ADLDAP_ADMIN_ACCOUNT_PREFIX=
ADLDAP_ADMIN_ACCOUNT_SUFFIX=
# LDAP authentication settings.
ADLDAP_PASSWORD_SYNC=false
@@ -147,6 +158,10 @@ WINDOWS_SSO_KEY=AUTH_USER
# field to sync as local username.
ADLDAP_SYNC_FIELD=userprincipalname
# You can disable the X-Frame-Options header if it interfears with tools like
# Organizr. This is at your own risk.
DISABLE_FRAME_HEADER=false
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
@@ -167,3 +182,4 @@ IS_DOCKER=false
IS_SANDSTORM=false
IS_HEROKU=false
BUNQ_USE_SANDBOX=false
FFIII_LAYOUT=v1

View File

@@ -1,169 +0,0 @@
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
APP_ENV=testing
# Set to true if you want to see debug information in error screens.
APP_DEBUG=true
# This should be your email address
SITE_OWNER=thegrumpydictator+testing@gmail.com
# The encryption key for your database and sessions. Keep this very secure.
# If you generate a new one all existing data must be considered LOST.
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
APP_KEY=TestTestTestTestTestTestTestTest
# Change this value to your preferred time zone.
# Example: Europe/Amsterdam
TZ=Europe/Amsterdam
# This variable must match your installation's external address but keep in mind that
# it's only used on the command line as a fallback value.
APP_URL=http://localhost
# TRUSTED_PROXIES is a useful variable when using Docker and/or a reverse proxy.
TRUSTED_PROXIES=
# The log channel defines where your log entries go to.
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
# Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself.
LOG_CHANNEL=dailytest
# Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
# nothing will get logged, ever.
APP_LOG_LEVEL=info
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
# For other database types, please see the FAQ: http://firefly-iii.readthedocs.io/en/latest/support/faq.html
DB_CONNECTION=sqlite
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file
SESSION_DRIVER=file
# You can configure another file storage backend if you cannot use the local storage option.
# To set this up, fill in the following variables. The upload path is used to store uploaded
# files and the export path is to store exported data (before download).
SFTP_HOST=
SFTP_PORT=
SFTP_UPLOAD_PATH=
SFTP_EXPORT_PATH=
# SFTP uses either the username/password combination or the private key to authenticate.
SFTP_USERNAME=
SFTP_PASSWORD=
SFTP_PRIV_KEY=
# Cookie settings. Should not be necessary to change these.
COOKIE_PATH="/"
COOKIE_DOMAIN=
COOKIE_SECURE=false
# If you want Firefly III to mail you, update these settings
# For instructions, see: https://firefly-iii.readthedocs.io/en/latest/installation/mail.html
MAIL_DRIVER=log
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_FROM=changeme@example.com
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
# Other mail drivers:
MAILGUN_DOMAIN=
MAILGUN_SECRET=
MANDRILL_SECRET=
SPARKPOST_SECRET=
# Firefly III can send you the following messages
SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=false
# These messages contain (sensitive) transaction information:
SEND_REPORT_JOURNALS=true
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=
# Set a Fixer IO API key here (see https://fixer.io) to enable live currency exchange rates.
# Please note that this will only work for paid fixer.io accounts because they severly limited
# the free API up to the point where you might as well offer nothing.
FIXER_API_KEY=
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
ANALYTICS_ID=
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
USE_ENCRYPTION=false
# Firefly III has two options for user authentication. "eloquent" is the default,
# and "adldap" for LDAP servers.
# For full instructions on these settings please visit:
# https://firefly-iii.readthedocs.io/en/latest/installation/authentication.html
LOGIN_PROVIDER=eloquent
# LDAP connection configuration
# or FreeIPA or ActiveDirectory
ADLDAP_CONNECTION_SCHEME=OpenLDAP
ADLDAP_AUTO_CONNECT=true
# LDAP connection settings
ADLDAP_CONTROLLERS=
ADLDAP_PORT=389
ADLDAP_TIMEOUT=5
ADLDAP_BASEDN=""
ADLDAP_FOLLOW_REFFERALS=false
ADLDAP_USE_SSL=false
ADLDAP_USE_TLS=false
ADLDAP_ADMIN_USERNAME=
ADLDAP_ADMIN_PASSWORD=
ADLDAP_ACCOUNT_PREFIX=
ADLDAP_ACCOUNT_SUFFIX=
ADLDAP_ADMIN_ACCOUNT_PREFIX=
ADLDAP_ADMIN_ACCOUNT_SUFFIX=
# LDAP authentication settings.
ADLDAP_PASSWORD_SYNC=false
ADLDAP_LOGIN_FALLBACK=false
ADLDAP_DISCOVER_FIELD=distinguishedname
ADLDAP_AUTH_FIELD=distinguishedname
# Will allow SSO if your server provides an AUTH_USER field.
WINDOWS_SSO_DISCOVER=samaccountname
WINDOWS_SSO_KEY=AUTH_USER
# field to sync as local username.
ADLDAP_SYNC_FIELD=userprincipalname
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
ADLDAP_CONNECTION=default
BROADCAST_DRIVER=log
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_PREFIX=firefly_tst
SEARCH_RESULT_LIMIT=50
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=false
IS_SANDSTORM=false
IS_HEROKU=false
BUNQ_USE_SANDBOX=true

View File

@@ -5,21 +5,23 @@ about: Create a report to help Firefly III improve
---
**Bug description**
I am running Firefly III version x.x.x
I am running Firefly III version x.x.x, and my problem is:
(please give a clear and concise description of what the bug is)
<!-- Replace the version and describe your problem or your issue will be closed. -->
**Steps to reproduce**
What do you need to do to trigger this bug?
<!-- What do you need to do to trigger this bug? -->
**Expected behavior**
What do you expect to see after those steps?
<!-- What do you expect to see after those steps? -->
**Extra info**
Please add extra info here, such as OS, browser, and the output from the /debug page of your Firefly III installation (click the version at the bottom).
<!-- Please add extra info here, such as OS, browser, and the output from the /debug page of your Firefly III installation (click the version at the bottom). -->
**Bonus points**
Earn bonus points by:
<!-- Earn bonus points by:
- Post a stacktrace from your log files
- Add a screenshot
- Remember the human
-->

View File

@@ -7,21 +7,19 @@ about: Ask away!
I am running Firefly III version x.x.x
**Description**
**Steps to reproduce**
(if relevant of course)
<!-- (if relevant of course) -->
**Extra info**
Please add extra info here, such as OS, browser, and the output from the `/debug`-page of your Firefly III installation (click the version at the bottom).
<!-- Please add extra info here, such as OS, browser, and the output from the `/debug`-page of your Firefly III installation (click the version at the bottom). -->
**Bonus points**
Earn bonus points by:
<!-- Earn bonus points by:
- Add a screenshot
- Replicate the problem on the demo site https://demo.firefly-iii.org/
- Make a drawing
- Donate money (just kidding ;)
- Replicate the problem on the demo site https://demo.firefly-iii.org/
- Remember the human
-->

View File

@@ -5,17 +5,28 @@ about: Suggest an idea or feature for Firefly III
---
**Description**
<!--
Please describe your feature request:
- I would like Firefly III to do X.
- What if you would add feature Y?
- Firefly III doesn't do Z.
- I would like Firefly III to do ABC.
- What if you would add feature XYZ?
- Firefly III doesn't do DEF.
-->
**Solution**
Describe what your feature would add to Firefly III.
<!-- Describe what your feature would add to Firefly III. -->
**What are alternatives?**
Please describe what alternatives currently exist.
<!-- Please describe what alternatives currently exist. -->
**Additional context**
Add any other context or screenshots about the feature request here.
<!-- Add any other context or screenshots about the feature request here. -->
**Bonus points**
<!-- Earn bonus points by:
- Make a drawing
- Donate money (just kidding ;)
- Remember the human
-->

View File

@@ -1,4 +1,13 @@
Fixes # (if relevant)
<!--
Before you create a new PR, please consider the following two considerations.
1) Pull request for the MASTER branch will be closed.
2) We cannot accept pull requests to add new currencies.
Thanks.
-->
Fixes issue # (if relevant)
Changes in this pull request:

6
.github/stale.yml vendored
View File

@@ -1,7 +1,7 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 14
daysUntilStale: 7
# Number of days of inactivity before a stale Issue or Pull Request is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
@@ -13,6 +13,8 @@ exemptLabels:
- enhancement
- feature
- bug
- possible-bug
- announcement
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
@@ -53,4 +55,4 @@ limitPerRun: 30
# issues:
# exemptLabels:
# - confirmed
# - confirmed

View File

@@ -6,7 +6,7 @@ set -euo pipefail
echo "In build.sh"
cd /opt/app
cp .env.sandstorm .env
cp .deploy/sandstorm/.env.sandstorm .env
if [ -f /opt/app/composer.json ] ; then
if [ ! -f composer.phar ] ; then

View File

@@ -1,4 +1,147 @@
$ 4.7.8
# 4.7.17.1 (API 0.9.2)
- XSS bug in budget title.
# 4.7.17 (API 0.9.2)
- Support for Norwegian!
- Clear cache during install routine.
- Add Firefly III version number to install routine.
- Initial release.
- [Issue 2159](https://github.com/firefly-iii/firefly-iii/issues/2159) Bad redirect due to Laravel upgrade.
- [Issue 2166](https://github.com/firefly-iii/firefly-iii/issues/2166) Importer had some issues with distinguishing double transfers.
- [Issue 2167](https://github.com/firefly-iii/firefly-iii/issues/2167) New LDAP package gave some configuration changes.
- [Issue 2173](https://github.com/firefly-iii/firefly-iii/issues/2173) Missing class when generating 2FA codes.
# 4.7.16 (API 0.9.2)
- 4.7.16 was released to fix a persistent issue with broken user preferences.
- Firefly III now uses Laravel 5.8
# 4.7.15 (API 0.9.2)
- 4.7.15 was released to fix some issues upgrading from older versions.
- [Issue 2128](https://github.com/firefly-iii/firefly-iii/issues/2128) Support for Postgres SSL
- [Issue 2120](https://github.com/firefly-iii/firefly-iii/issues/2120) Add a missing meta tag, thanks to @lastlink
- Search is a lot faster now.
- [Issue 2125](https://github.com/firefly-iii/firefly-iii/issues/2125) Decryption issues during upgrade
- [Issue 2130](https://github.com/firefly-iii/firefly-iii/issues/2130) Fixed database migrations and rollbacks.
- [Issue 2135](https://github.com/firefly-iii/firefly-iii/issues/2135) Date fixes in transaction overview
# 4.7.14 (API 0.9.2)
- 4.7.14 was released to fix an issue with the Composer installation script.
# 4.7.13 (API 0.9.2)
- 4.7.13 was released to fix an issue that affected the Softaculous build.
- A routine has been added that warns about transactions with a 0.00 amount.
- PHP maximum execution time is now 600 seconds in the Docker image.
- Moved several files outside of the root of Firefly III
- Fix issue where missing preference breaks the database upgrade.
- [Issue 2100](https://github.com/firefly-iii/firefly-iii/issues/2100) Mass edit transactions results in a reset of the date.
# 4.7.12
- 4.7.12 was released to fix several shortcomings in v4.7.11's Docker image. Those in turn were caused by me. My apologies.
- [Issue 2085](https://github.com/firefly-iii/firefly-iii/issues/2085) Upgraded the LDAP code. To keep using LDAP, set the `LOGIN_PROVIDER` to `ldap`.
- [Issue 2061](https://github.com/firefly-iii/firefly-iii/issues/2061) Some users reported empty update popups.
- [Issue 2070](https://github.com/firefly-iii/firefly-iii/issues/2070) A cache issue prevented rules from being applied correctly.
- [Issue 2071](https://github.com/firefly-iii/firefly-iii/issues/2071) Several issues with Postgres and date values with time zone information in them.
- [Issue 2081](https://github.com/firefly-iii/firefly-iii/issues/2081) Rules were not being applied when importing using FinTS.
- [Issue 2082](https://github.com/firefly-iii/firefly-iii/issues/2082) The mass-editor changed all dates to today.
# 4.7.11
- Experimental audit logging channel to track important events (separate from debug logging).
- [Issue 2003](https://github.com/firefly-iii/firefly-iii/issues/2003), [issue 2006](https://github.com/firefly-iii/firefly-iii/issues/2006) Transactions can be stored with a timestamp. The user-interface does not support this yet. But the API does.
- Docker image tags a new manifest for arm and amd64.
- [skuzzle](https://github.com/skuzzle) removed an annoying console.log statement.
- [Issue 2048](https://github.com/firefly-iii/firefly-iii/issues/2048) Fix "Are you sure?" popup, thanks to @nescafe2002!
- [Issue 2049](https://github.com/firefly-iii/firefly-iii/issues/2049) Empty preferences would crash Firefly III.
- [Issue 2052](https://github.com/firefly-iii/firefly-iii/issues/2052) Rules could not auto-covert to liabilities.
- Webbased upgrade routine will also decrypt the database.
- Last use date for categories was off.
- The `date`-field in any transaction object now returns a ISO 8601 timestamp instead of a date.
# 4.7.10
- [Issue 2037](https://github.com/firefly-iii/firefly-iii/issues/2037) Added some new magic keywords to reports.
- Added a new currency exchange rate service, [ratesapi.io](https://ratesapi.io/), that does not require expensive API keys. Built by [@BoGnY](https://github.com/BoGnY).
- Added Chinese Traditional translations. Thanks!
- [Issue 1977](https://github.com/firefly-iii/firefly-iii/issues/1977) Docker image now includes memcached support
- [Issue 2031](https://github.com/firefly-iii/firefly-iii/issues/2031) A new generic debit/credit indicator for imports.
- The new Docker image no longer has the capability to run cron jobs, and will no longer generate your recurring transactions for you. This has been done to simplify the build and make sure your Docker container runs one service, as it should. To set up a cron job for your new Docker container, [check out the documentation](https://docs.firefly-iii.org/en/latest/installation/cronjob.html).
- Due to a change in the database structure, this upgrade will reset your preferences. Sorry about that.
- I will no longer accept PR's that introduce new currencies.
- Firefly III no longer encrypts the database and will [decrypt the database]() on its first run.
- [Issue 1923](https://github.com/firefly-iii/firefly-iii/issues/1923) Broken window position for date picker.
- [Issue 1967](https://github.com/firefly-iii/firefly-iii/issues/1967) Attachments were hidden in bill view.
- [Issue 1927](https://github.com/firefly-iii/firefly-iii/issues/1927) It was impossible to make recurring transactions skip.
- [Issue 1929](https://github.com/firefly-iii/firefly-iii/issues/1929) Fix the recurring transactions calendar overview.
- [Issue 1933](https://github.com/firefly-iii/firefly-iii/issues/1933) Fixed a bug that made it impossible to authenticate to FreeIPA servers.
- [Issue 1938](https://github.com/firefly-iii/firefly-iii/issues/1938) The importer can now handle the insane way Postbank (DE) formats its numbers.
- [Issue 1942](https://github.com/firefly-iii/firefly-iii/issues/1942) Favicons are relative so Scriptaculous installations work better.
- [Issue 1944](https://github.com/firefly-iii/firefly-iii/issues/1944) Make sure that the search allows you to mass-select transactions.
- [Issue 1945](https://github.com/firefly-iii/firefly-iii/issues/1945) Slight UI change so the drop-down menu renders better.
- [Issue 1955](https://github.com/firefly-iii/firefly-iii/issues/1955) Fixed a bug in the category report.
- [Issue 1968](https://github.com/firefly-iii/firefly-iii/issues/1968) The yearly range would jump to 1-Jan / 1-Jan instead of 1-Jan / 31-Dec
- [Issue 1975](https://github.com/firefly-iii/firefly-iii/issues/1975) Fixed explanation for missing credit card liabilities.
- [Issue 1979](https://github.com/firefly-iii/firefly-iii/issues/1979) Make sure tags are trimmed.
- [Issue 1983](https://github.com/firefly-iii/firefly-iii/issues/1983) Could not use your favorite decimal separator.
- [Issue 1989](https://github.com/firefly-iii/firefly-iii/issues/1989) Bug in YNAB importer forced you to select all accounts.
- [Issue 1990](https://github.com/firefly-iii/firefly-iii/issues/1990) Rule description was invisible in edit screen.
- [Issue 1996](https://github.com/firefly-iii/firefly-iii/issues/1996) Deleted budget would inadvertently also hide transactions.
- [Issue 2001](https://github.com/firefly-iii/firefly-iii/issues/2001) Various issues with tag chart view.
- [Issue 2009](https://github.com/firefly-iii/firefly-iii/issues/2009) Could not change recurrence back to "forever".
- [Issue 2033](https://github.com/firefly-iii/firefly-iii/issues/2033) Longitude can go from -180 to 180.
- [Issue 2034](https://github.com/firefly-iii/firefly-iii/issues/2034) Rules were not being triggered in mass-edit.
- #2043 In rare instances the repetition of a recurring transaction was displayed incorrectly.
- Fixed broken translations in the recurring transactions overview.
- When you create a recurring transfer you make make it fill (or empty) a piggy bank. This was not working, despite a fix in 4.7.8.
- Fixed a bug where the importer would not be capable of creating new currencies.
- Rule trigger tester would skip the amount.
- OAuth2 form can now submit back to original requester.
- Submitting transactions with a disabled currency will auto-enable the currency.
- The documentation now states that "Deposit" is a possible return when you get a transaction.
- "savingAsset" was incorrectly documented as "savingsAsset".
- Account endpoint can now return type "reconciliation" and "initial-balance" correctly.
- New API endpoint under `/summary/basic` that gives you a basic overview of the user's finances.
- New API endpoints under `/chart/*` to allow you to render charts.
- `/accounts/x/transactions` now supports the limit query parameter.
- `/budgets/x/transactions` now supports the limit query parameter.
- `/available_budgets` now supports custom start and end date parameters.
- New endpoint `/preferences/prefName` to retrieve a single preference.
- Added field `account_name` to all piggy banks.
- New tag cloud in API.
# 4.7.9
- [Issue 1622](https://github.com/firefly-iii/firefly-iii/issues/1622) Can now unlink a transaction from a bill.
- [Issue 1848](https://github.com/firefly-iii/firefly-iii/issues/1848) Added support for the Swiss Franc.
- [Issue 1828](https://github.com/firefly-iii/firefly-iii/issues/1828) Focus on fields for easy access.
- [Issue 1859](https://github.com/firefly-iii/firefly-iii/issues/1859) Warning when seeding database.
- Completely rewritten API. Check out the documentation [here](https://api-docs.firefly-iii.org/).
- Currencies can now be enabled and disabled, making for cleaner views.
- You can disable the `X-Frame-Options` header if this is necessary.
- New fancy favicons.
- Updated and improved docker build.
- Docker build no longer builds its own cURL.
- [Issue 1607](https://github.com/firefly-iii/firefly-iii/issues/1607) [issue 1857](https://github.com/firefly-iii/firefly-iii/issues/1857) [issue 1895](https://github.com/firefly-iii/firefly-iii/issues/1895) Improved bunq import and added support for auto-savings.
- [Issue 1766](https://github.com/firefly-iii/firefly-iii/issues/1766) Extra commands so cache dir is owned by www user.
- [Issue 1811](https://github.com/firefly-iii/firefly-iii/issues/1811) 404 when generating report without options.
- [Issue 1835](https://github.com/firefly-iii/firefly-iii/issues/1835) Strange debug popup removed.
- [Issue 1840](https://github.com/firefly-iii/firefly-iii/issues/1840) Error when exporting data.
- [Issue 1857](https://github.com/firefly-iii/firefly-iii/issues/1857) Bunq import words again (see above).
- [Issue 1858](https://github.com/firefly-iii/firefly-iii/issues/1858) SQL errors when importing CSV.
- [Issue 1861](https://github.com/firefly-iii/firefly-iii/issues/1861) Period navigator was broken.
- [Issue 1864](https://github.com/firefly-iii/firefly-iii/issues/1864) First description was empty on split transactions.
- [Issue 1865](https://github.com/firefly-iii/firefly-iii/issues/1865) Bad math when showing categories.
- [Issue 1868](https://github.com/firefly-iii/firefly-iii/issues/1868) Fixes to FinTS import.
- [Issue 1872](https://github.com/firefly-iii/firefly-iii/issues/1872) Some images had 404's.
- [Issue 1877](https://github.com/firefly-iii/firefly-iii/issues/1877) Several encryption / decryption issues.
- [Issue 1878](https://github.com/firefly-iii/firefly-iii/issues/1878) Wrong nav links
- [Issue 1884](https://github.com/firefly-iii/firefly-iii/issues/1884) Budget API improvements (see above)
- [Issue 1888](https://github.com/firefly-iii/firefly-iii/issues/1888) Transaction API improvements (see above)
- [Issue 1890](https://github.com/firefly-iii/firefly-iii/issues/1890) Fixes in Bills API
- [Issue 1891](https://github.com/firefly-iii/firefly-iii/issues/1891) Typo fixed.
- [Issue 1893](https://github.com/firefly-iii/firefly-iii/issues/1893) Update piggies from recurring transactions.
- [Issue 1898](https://github.com/firefly-iii/firefly-iii/issues/1898) Bug in tag report.
- [Issue 1901](https://github.com/firefly-iii/firefly-iii/issues/1901) Redirect when cloning transactions.
- [Issue 1909](https://github.com/firefly-iii/firefly-iii/issues/1909) Date range fixes.
- [Issue 1916](https://github.com/firefly-iii/firefly-iii/issues/1916) Date range fixes.
# 4.7.8
- [Issue 1005](https://github.com/firefly-iii/firefly-iii/issues/1005) You can now configure Firefly III to use LDAP.
- [Issue 1071](https://github.com/firefly-iii/firefly-iii/issues/1071) You can execute transaction rules using the command line (so you can cronjob it)

View File

@@ -25,9 +25,9 @@ mkdir -p /var/storage/build
mkdir -p /var/storage/database
mkdir -p /var/storage/debugbar
mkdir -p /var/storage/export
mkdir -p /var/storage/framework/cache
mkdir -p /var/storage/framework/cache/v1
mkdir -p /var/storage/framework/sessions
mkdir -p /var/storage/framework/views
mkdir -p /var/storage/framework/views/v1
mkdir -p /var/storage/logs
mkdir -p /var/storage/upload
@@ -58,9 +58,5 @@ echo "Migrating..."
php /opt/app/artisan migrate --seed --force
echo "Done!"
echo "Clear cache.."
php /opt/app/artisan cache:clear
echo "Done"
# Start nginx.
/usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;"

File diff suppressed because it is too large Load Diff

View File

@@ -15,8 +15,8 @@ const pkgdef :Spk.PackageDefinition = (
manifest = (
appTitle = (defaultText = "Firefly III"),
appVersion = 18,
appMarketingVersion = (defaultText = "4.7.8"),
appVersion = 28,
appMarketingVersion = (defaultText = "4.7.17.1"),
actions = [
# Define your "new document" handlers here.
@@ -103,7 +103,7 @@ const pkgdef :Spk.PackageDefinition = (
# not have been detected as a dependency during `spk dev`. If you list
# a directory here, its entire contents will be included recursively.
#bridgeConfig = (
bridgeConfig = (
# # Used for integrating permissions and roles into the Sandstorm shell
# # and for sandstorm-http-bridge to pass to your app.
# # Uncomment this block and adjust the permissions and roles to make
@@ -165,12 +165,12 @@ const pkgdef :Spk.PackageDefinition = (
# ),
# ],
# ),
# #apiPath = "/api",
apiPath = "/api/v1/",
# # Apps can export an API to the world. The API is to be used primarily by Javascript
# # code and native apps, so it can't serve out regular HTML to browsers. If a request
# # comes in to your app's API, sandstorm-http-bridge will prefix the request's path with
# # this string, if specified.
#),
),
);
const myCommand :Spk.Manifest.Command = (

View File

@@ -13,6 +13,8 @@ apt-get update
apt-get install -y python-software-properties software-properties-common
# install all languages
#en_US
sed -i 's/# es_ES.UTF-8 UTF-8/es_ES.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# it_IT.UTF-8 UTF-8/it_IT.UTF-8 UTF-8/g' /etc/locale.gen
@@ -20,7 +22,10 @@ sed -i 's/# nl_NL.UTF-8 UTF-8/nl_NL.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# pl_PL.UTF-8 UTF-8/pl_PL.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# pt_BR.UTF-8 UTF-8/pt_BR.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# ru_RU.UTF-8 UTF-8/ru_RU.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# tr_TR.UTF-8 UTF-8/tr_TR.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# zh_TW.UTF-8 UTF-8/zh_TW.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# nb_NO.UTF-8 UTF-8/nb_NO.UTF-8 UTF-8/g' /etc/locale.gen
dpkg-reconfigure --frontend=noninteractive locales
@@ -38,7 +43,7 @@ echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sou
# install packages.
apt-get update
apt-get install -y nginx php7.2-fpm php7.2-mysql php7.2-gd php7.2-cli php7.2-curl git php7.2-dev php7.2-zip php7.2-intl php7.2-dom php7.2-mbstring php7.2-bcmath mysql-server
apt-get install -y nginx php7.2-fpm php7.2-mysql php7.2-gd php7.2-cli php7.2-curl php7.2-ldap git php7.2-dev php7.2-zip php7.2-intl php7.2-dom php7.2-mbstring php7.2-bcmath mysql-server
service nginx stop
service php7.2-fpm stop
service mysql stop

View File

@@ -1,29 +1,29 @@
language: php
php:
- 7.2
sudo: required
language: bash
env:
- VERSION=4.7.17.1
cache:
directories:
- vendor
- $HOME/.composer/cache
install:
- rm composer.lock
- composer update --no-scripts
- cp .env.testing .env
- php artisan clear-compiled
- php artisan env
- wget -q https://github.com/firefly-iii/test-data/raw/master/storage/database.sqlite -O storage/database/database.sqlite
- mkdir -p build/logs
script:
- ./vendor/bin/phpunit -c phpunit.coverage.xml
after_success:
- travis_retry php vendor/bin/php-coveralls -x storage/build/clover-all.xml
dist: xenial
# safelist
branches:
only:
- develop
- master
services:
- docker
script:
# enable experimental features.
- echo '{"experimental":true}' | sudo tee /etc/docker/daemon.json
- mkdir $HOME/.docker
- touch $HOME/.docker/config.json
- echo '{"experimental":"enabled"}' | sudo tee $HOME/.docker/config.json
- sudo service docker restart
- docker version -f '{{.Server.Experimental}}'
- docker version
# build everything
- .deploy/docker/build-amd64.sh
- .deploy/docker/build-arm.sh
- .deploy/docker/manifest.sh

View File

@@ -1,116 +1,53 @@
FROM php:7.2-apache
# If building on a RPi, use --build-arg cores=3 to use all cores when compiling
# to speed up the image build
ARG CORES
ENV CORES ${CORES:-1}
ENV FIREFLY_PATH /var/www/firefly-iii/
ENV CURL_VERSION 7.60.0
ENV OPENSSL_VERSION 1.1.1-pre6
ENV COMPOSER_ALLOW_SUPERUSER 1
LABEL version="1.1" maintainer="thegrumpydictator@gmail.com"
# install packages
RUN apt-get update -y && \
apt-get install -y --no-install-recommends libcurl4-openssl-dev \
zlib1g-dev \
libjpeg62-turbo-dev \
wget \
libpng-dev \
libicu-dev \
libldap2-dev \
libedit-dev \
libtidy-dev \
libxml2-dev \
unzip \
libsqlite3-dev \
nano \
libpq-dev \
libbz2-dev \
gettext-base \
cron \
rsyslog \
supervisor \
locales && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# LDAP install
RUN docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu/ && docker-php-ext-install ldap
# Install latest curl
RUN cd /tmp && \
wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz && \
tar -xvf openssl-${OPENSSL_VERSION}.tar.gz && \
cd openssl-${OPENSSL_VERSION} && \
./config && \
make -j${CORES} && \
make install
RUN cd /tmp && \
wget https://curl.haxx.se/download/curl-${CURL_VERSION}.tar.gz && \
tar -xvf curl-${CURL_VERSION}.tar.gz && \
cd curl-${CURL_VERSION} && \
./configure --with-ssl --host=$(gcc -dumpmachine) && \
make -j${CORES} && \
make install
# Make sure that libcurl is using the newer curl libaries
RUN echo "/usr/local/lib" >> /etc/ld.so.conf.d/00-curl.conf && ldconfig
# Mimic the Debian/Ubuntu config file structure for supervisor
COPY .deploy/docker/supervisord.conf /etc/supervisor/supervisord.conf
RUN mkdir -p /etc/supervisor/conf.d /var/log/supervisor
# copy Firefly III supervisor conf file.
COPY ./.deploy/docker/firefly-iii.conf /etc/supervisor/conf.d/firefly-iii.conf
# copy cron job supervisor conf file.
COPY ./.deploy/docker/cronjob.conf /etc/supervisor/conf.d/cronjob.conf
# copy ca certs to correct location
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# test crons added via crontab
RUN echo "0 3 * * * /usr/local/bin/php /var/www/firefly-iii/artisan firefly:cron" | crontab -
#RUN (crontab -l ; echo "*/1 * * * * free >> /var/www/firefly-iii/public/cron.html") 2>&1 | crontab -
# Install PHP exentions.
RUN docker-php-ext-install -j$(nproc) gd intl tidy zip bcmath pdo_mysql bz2 pdo_pgsql
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Generate locales supported by Firefly III
RUN echo "en_US.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\n\n" > /etc/locale.gen && locale-gen
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable apache mod rewrite..
RUN a2enmod rewrite
# Enable apache mod ssl..
RUN a2enmod ssl
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.4" maintainer="thegrumpydictator@gmail.com"
# Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Install some stuff
RUN apt-get update && apt-get install -y libpng-dev \
libicu-dev \
unzip \
gettext-base \
libldap2-dev \
libpq-dev \
locales \
libmemcached-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Make sure we own Firefly III directory
RUN chown -R www-data:www-data /var/www && chmod -R 775 $FIREFLY_PATH/storage
# Copy in Firefly Source
# Copy in Firefly III source
WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH
# Fix the link to curl:
RUN rm -rf /usr/local/lib/libcurl.so.4 && ln -s /usr/lib/x86_64-linux-gnu/libcurl.so.4.4.0 /usr/local/lib/libcurl.so.4
# copy ca certs to correct location
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# Run composer
RUN composer install --prefer-dist --no-dev --no-scripts --no-suggest
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Run a lot of installation commands:
RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \
a2enmod rewrite && a2enmod ssl && \
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
pecl install memcached-3.1.3 && \
docker-php-ext-enable memcached && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
locale-gen && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest
# configure PHP
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini && \
sed -i 's/max_execution_time = 30/max_execution_time = 600/' /usr/local/etc/php/php.ini && \
sed -i 's/memory_limit = 128M/memory_limit = 512M/' /usr/local/etc/php/php.ini
# Expose port 80
EXPOSE 80

57
Dockerfile.amd64 Normal file
View File

@@ -0,0 +1,57 @@
FROM php:7.2-apache
ARG ARCH
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.4" maintainer="thegrumpydictator@gmail.com"
# Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install some stuff
RUN apt-get update && apt-get install -y libpng-dev \
libicu-dev \
unzip \
gettext-base \
libldap2-dev \
libpq-dev \
locales \
libmemcached-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Copy in Firefly III source
WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH
# copy ca certs to correct location
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Run a lot of installation commands:
RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \
a2enmod rewrite && a2enmod ssl && \
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
pecl install memcached-3.1.3 && \
docker-php-ext-enable memcached && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
locale-gen && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest
# configure PHP
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini && \
sed -i 's/max_execution_time = 30/max_execution_time = 600/' /usr/local/etc/php/php.ini && \
sed -i 's/memory_limit = 128M/memory_limit = 512M/' /usr/local/etc/php/php.ini
# Expose port 80
EXPOSE 80
# Run entrypoint thing
ENTRYPOINT [".deploy/docker/entrypoint.sh"]

50
Dockerfile.arm Normal file
View File

@@ -0,0 +1,50 @@
FROM arm32v7/php:7.2.8-apache-stretch
ARG ARCH
COPY tmp/qemu-arm-static /usr/bin/qemu-arm-static
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.4" maintainer="thegrumpydictator@gmail.com"
# Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install some stuff
RUN apt-get update && apt-get install -y libpng-dev \
libicu-dev \
unzip \
gettext-base \
libldap2-dev \
libpq-dev \
locales \
libmemcached-dev
# Copy in Firefly III source
WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH
# copy ca certs to correct location
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Run a lot of installation commands:
RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \
a2enmod rewrite && a2enmod ssl && \
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
pecl install memcached-3.1.3 && \
docker-php-ext-enable memcached && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
locale-gen && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest
# Expose port 80
EXPOSE 80
# Run entrypoint thing
ENTRYPOINT [".deploy/docker/entrypoint.sh"]

50
Dockerfile.arm64 Normal file
View File

@@ -0,0 +1,50 @@
FROM arm32v7/php:7.2.8-apache-stretch
ARG ARCH
COPY tmp/qemu-arm-static /usr/bin/qemu-arm-static
ENV FIREFLY_PATH=/var/www/firefly-iii COMPOSER_ALLOW_SUPERUSER=1
LABEL version="1.4" maintainer="thegrumpydictator@gmail.com"
# Create volumes
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Install some stuff
RUN apt-get update && apt-get install -y libpng-dev \
libicu-dev \
unzip \
gettext-base \
libldap2-dev \
libpq-dev \
locales \
libmemcached-dev
# Copy in Firefly III source
WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH
# copy ca certs to correct location
COPY ./.deploy/docker/cacert.pem /usr/local/ssl/cert.pem
# copy Apache config to correct spot.
COPY ./.deploy/docker/apache2.conf /etc/apache2/apache2.conf
# Enable default site (Firefly III)
COPY ./.deploy/docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
# Run a lot of installation commands:
RUN chown -R www-data:www-data /var/www && \
chmod -R 775 $FIREFLY_PATH/storage && \
a2enmod rewrite && a2enmod ssl && \
docker-php-ext-configure ldap --with-libdir=lib/$(gcc -dumpmachine)/ && \
docker-php-ext-install -j$(nproc) zip bcmath ldap gd pdo_pgsql pdo_mysql intl opcache && \
pecl install memcached-3.1.3 && \
docker-php-ext-enable memcached && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nes_ES.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nit_IT.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8\nru_RU.UTF-8 UTF-8\ntr_TR.UTF-8 UTF-8\nzh_TW.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8\n\n" > /etc/locale.gen && \
locale-gen && \
composer install --prefer-dist --no-dev --no-scripts --no-suggest
# Expose port 80
EXPOSE 80
# Run entrypoint thing
ENTRYPOINT [".deploy/docker/entrypoint.sh"]

View File

@@ -60,7 +60,7 @@ class AboutController extends Controller
'driver' => $currentDriver,
];
return response()->json(['data' => $data], 200)->header('Content-Type', 'application/vnd.api+json');
return response()->json(['data' => $data])->header('Content-Type', 'application/vnd.api+json');
}
/**
@@ -75,7 +75,12 @@ class AboutController extends Controller
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item(auth()->user(), new UserTransformer($this->parameters), 'users');
/** @var UserTransformer $transformer */
$transformer = app(UserTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item(auth()->user(), $transformer, 'users');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}

View File

@@ -24,15 +24,21 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\AccountRequest;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\PiggyBankTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
@@ -46,8 +52,7 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class AccountController extends Controller
{
/** @var CurrencyRepositoryInterface The currency repository */
private $currencyRepository;
use AccountFilter, TransactionFilter;
/** @var AccountRepositoryInterface The account repository */
private $repository;
@@ -65,9 +70,6 @@ class AccountController extends Controller
$this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUser($user);
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
$this->currencyRepository->setUser($user);
return $next($request);
}
);
@@ -105,7 +107,7 @@ class AccountController extends Controller
$this->parameters->set('type', $type);
// types to get, page size:
$types = $this->mapTypes($this->parameters->get('type'));
$types = $this->mapAccountTypes($this->parameters->get('type'));
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of accounts. Count it and split it.
@@ -119,12 +121,58 @@ class AccountController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($accounts, new AccountTransformer($this->parameters), 'accounts');
/** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($accounts, $transformer, 'accounts');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List all of them.
*
* @param Request $request
* @param Account $account
*
* @return JsonResponse]
*/
public function piggyBanks(Request $request, Account $account): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of budgets. Count it and split it.
$collection = $this->repository->getPiggyBanks($account);
$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.accounts.piggy_banks', [$account->id]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @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.
*
@@ -136,14 +184,13 @@ class AccountController extends Controller
public function show(Request $request, Account $account): JsonResponse
{
$manager = new Manager;
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($account, new AccountTransformer($this->parameters), 'accounts');
/** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($account, $transformer, 'accounts');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -157,18 +204,80 @@ class AccountController extends Controller
*/
public function store(AccountRequest $request): JsonResponse
{
$data = $request->getAll();
// if currency ID is 0, find the currency by the code:
if (0 === $data['currency_id']) {
$currency = $this->currencyRepository->findByCodeNull($data['currency_code']);
$data['currency_id'] = null === $currency ? 0 : $currency->id;
}
$data = $request->getAll();
$account = $this->repository->store($data);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($account, new AccountTransformer($this->parameters), 'accounts');
/** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($account, $transformer, 'accounts');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show all transactions.
*
* @param Request $request
* @param Account $account
*
* @return JsonResponse
*/
public function transactions(Request $request, Account $account): JsonResponse
{
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$type = $request->get('type') ?? 'default';
$this->parameters->set('type', $type);
// user can overrule page size with limit parameter.
$limit = $this->parameters->get('limit');
if (null !== $limit && $limit > 0) {
$pageSize = $limit;
}
$types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
if ($this->repository->isAsset($account)) {
$collector->setAccounts(new Collection([$account]));
}
if (!$this->repository->isAsset($account)) {
$collector->setOpposingAccounts(new Collection([$account]));
}
if (\in_array(TransactionType::TRANSFER, $types, true)) {
$collector->removeFilter(InternalTransferFilter::class);
}
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.accounts.transactions', [$account->id]) . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
@@ -183,68 +292,18 @@ class AccountController extends Controller
*/
public function update(AccountRequest $request, Account $account): JsonResponse
{
$data = $request->getAll();
// if currency ID is 0, find the currency by the code:
if (0 === $data['currency_id']) {
$currency = $this->currencyRepository->findByCodeNull($data['currency_code']);
$data['currency_id'] = null === $currency ? 0 : $currency->id;
}
// set correct type:
$data = $request->getAll();
$data['type'] = config('firefly.shortNamesByFullName.' . $account->accountType->type);
$this->repository->update($account, $data);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($account, new AccountTransformer($this->parameters), 'accounts');
/** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($account, $transformer, 'accounts');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* All the available types.
*
* @param string $type
*
* @return array
*/
private function mapTypes(string $type): array
{
$types = [
'all' => [AccountType::DEFAULT, AccountType::CASH, AccountType::ASSET, AccountType::EXPENSE, AccountType::REVENUE,
AccountType::INITIAL_BALANCE, AccountType::BENEFICIARY, AccountType::IMPORT, AccountType::RECONCILIATION,
AccountType::LOAN,AccountType::DEBT, AccountType::MORTGAGE],
'asset' => [AccountType::DEFAULT, AccountType::ASSET,],
'cash' => [AccountType::CASH,],
'expense' => [AccountType::EXPENSE, AccountType::BENEFICIARY,],
'revenue' => [AccountType::REVENUE,],
'special' => [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION,],
'hidden' => [AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION],
'liability' => [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD],
'liabilities' => [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD],
'cc' => [AccountType::CREDITCARD],
'creditcard' => [AccountType::CREDITCARD],
'credit_card' => [AccountType::CREDITCARD],
AccountType::DEFAULT => [AccountType::DEFAULT],
AccountType::CASH => [AccountType::CASH],
AccountType::ASSET => [AccountType::ASSET],
AccountType::EXPENSE => [AccountType::EXPENSE],
AccountType::REVENUE => [AccountType::REVENUE],
AccountType::INITIAL_BALANCE => [AccountType::INITIAL_BALANCE],
AccountType::BENEFICIARY => [AccountType::BENEFICIARY],
AccountType::IMPORT => [AccountType::IMPORT],
AccountType::RECONCILIATION => [AccountType::RECONCILIATION],
AccountType::LOAN => [AccountType::LOAN],
AccountType::MORTGAGE => [AccountType::MORTGAGE],
AccountType::DEBT => [AccountType::DEBT],
AccountType::CREDITCARD => [AccountType::CREDITCARD],
];
$return = $types['all'];
if (isset($types[$type])) {
$return = $types[$type];
}
return $return; // @codeCoverageIgnore
}
}

View File

@@ -100,7 +100,7 @@ class AttachmentController extends Controller
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
/** @var LaravelResponse $response */
$response = response($content, 200);
$response = response($content);
$response
->header('Content-Description', 'File Transfer')
->header('Content-Type', 'application/octet-stream')
@@ -144,7 +144,12 @@ class AttachmentController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($attachments, new AttachmentTransformer($this->parameters), 'attachments');
/** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($attachments, $transformer, 'attachments');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -161,14 +166,14 @@ class AttachmentController extends Controller
public function show(Request $request, Attachment $attachment): JsonResponse
{
$manager = new Manager;
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($attachment, new AttachmentTransformer($this->parameters), 'attachments');
/** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($attachment, $transformer, 'attachments');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -188,7 +193,12 @@ class AttachmentController extends Controller
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($attachment, new AttachmentTransformer($this->parameters), 'attachments');
/** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($attachment, $transformer, 'attachments');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -209,7 +219,11 @@ class AttachmentController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($attachment, new AttachmentTransformer($this->parameters), 'attachments');
/** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($attachment, $transformer, 'attachments');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}

View File

@@ -24,10 +24,10 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\AvailableBudgetRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Transformers\AvailableBudgetTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@@ -46,8 +46,6 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class AvailableBudgetController extends Controller
{
/** @var CurrencyRepositoryInterface The currency repository */
private $currencyRepository;
/** @var BudgetRepositoryInterface The budget repository */
private $repository;
@@ -60,9 +58,8 @@ class AvailableBudgetController extends Controller
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(BudgetRepositoryInterface::class);
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
$user = auth()->user();
$this->repository = app(BudgetRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
@@ -101,7 +98,20 @@ class AvailableBudgetController extends Controller
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of available budgets. Count it and split it.
$collection = $this->repository->getAvailableBudgets();
$collection = $this->repository->getAvailableBudgets();
// filter list on start and end date, if present.
// TODO: put this in the query.
$start = $this->parameters->get('start');
$end = $this->parameters->get('end');
if (null !== $start && null !== $end) {
$collection = $collection->filter(
function (AvailableBudget $availableBudget) use ($start, $end) {
return $availableBudget->start_date->gte($start) && $availableBudget->end_date->lte($end);
}
);
}
$count = $collection->count();
$availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
@@ -111,7 +121,12 @@ class AvailableBudgetController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($availableBudgets, new AvailableBudgetTransformer($this->parameters), 'available_budgets');
/** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($availableBudgets, $transformer, 'available_budgets');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -127,16 +142,15 @@ class AvailableBudgetController extends Controller
*/
public function show(Request $request, AvailableBudget $availableBudget): JsonResponse
{
$manager = new Manager;
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($availableBudget, new AvailableBudgetTransformer($this->parameters), 'available_budgets');
/** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($availableBudget, $transformer, 'available_budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -147,30 +161,32 @@ class AvailableBudgetController extends Controller
* @param AvailableBudgetRequest $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function store(AvailableBudgetRequest $request): JsonResponse
{
$data = $request->getAll();
$currency = $this->currencyRepository->findNull($data['currency_id']);
$data = $request->getAll();
/** @var TransactionCurrencyFactory $factory */
$factory = app(TransactionCurrencyFactory::class);
$currency = $factory->find($data['currency_id'], $data['currency_code']);
if (null === $currency) {
$currency = $this->currencyRepository->findByCodeNull($data['currency_code']);
$currency = app('amount')->getDefaultCurrency();
}
if (null === $currency) {
throw new FireflyException('Could not find the indicated currency.');
}
$availableBudget = $this->repository->setAvailableBudget($currency, $data['start_date'], $data['end_date'], $data['amount']);
$availableBudget = $this->repository->setAvailableBudget($currency, $data['start'], $data['end'], $data['amount']);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($availableBudget, new AvailableBudgetTransformer($this->parameters), 'available_budgets');
/** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($availableBudget, $transformer, 'available_budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Update the specified resource in storage.
*
@@ -182,12 +198,32 @@ class AvailableBudgetController extends Controller
public function update(AvailableBudgetRequest $request, AvailableBudget $availableBudget): JsonResponse
{
$data = $request->getAll();
/** @var TransactionCurrencyFactory $factory */
$factory = app(TransactionCurrencyFactory::class);
/** @var TransactionCurrency $currency */
$currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null);
if (null === $currency) {
// use default currency:
$currency = app('amount')->getDefaultCurrency();
}
$currency->enabled = true;
$currency->save();
unset($data['currency_code']);
$data['currency_id'] = $currency->id;
$this->repository->updateAvailableBudget($availableBudget, $data);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($availableBudget, new AvailableBudgetTransformer($this->parameters), 'available_budgets');
/** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($availableBudget, $transformer, 'available_budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -26,12 +26,18 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\BillRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\Transformers\BillTransformer;
use FireflyIII\Transformers\RuleTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
@@ -44,6 +50,7 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class BillController extends Controller
{
use TransactionFilter;
/** @var BillRepositoryInterface The bill repository */
private $repository;
@@ -67,6 +74,43 @@ class BillController extends Controller
);
}
/**
* Display a listing of the resource.
*
* @param Request $request
* @param Bill $bill
*
* @return JsonResponse
*/
public function attachments(Request $request, Bill $bill): JsonResponse
{
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$collection = $this->repository->getAttachments($bill);
$count = $collection->count();
$attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.bills.attachments', [$bill->id]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($attachments, $transformer, 'attachments');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Remove the specified resource from storage.
*
@@ -99,12 +143,56 @@ class BillController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($bills, new BillTransformer($this->parameters), 'bills');
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($bills, $transformer, 'bills');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List all of them.
*
* @param Request $request
* @param Bill $bill
*
* @return JsonResponse
*/
public function rules(Request $request, Bill $bill): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of budgets. Count it and split it.
$collection = $this->repository->getRulesForBill($bill);
$count = $collection->count();
$rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.bills.rules', [$bill->id]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($rules, $transformer, 'rules');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show the specified bill.
@@ -117,14 +205,14 @@ class BillController extends Controller
public function show(Request $request, Bill $bill): JsonResponse
{
$manager = new Manager();
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($bill, new BillTransformer($this->parameters), 'bills');
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($bill, $transformer, 'bills');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -145,7 +233,11 @@ class BillController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($bill, new BillTransformer($this->parameters), 'bills');
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($bill, $transformer, 'bills');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -153,6 +245,53 @@ class BillController extends Controller
}
/**
* Show all transactions.
*
* @param Request $request
*
* @param Bill $bill
*
* @return JsonResponse
*/
public function transactions(Request $request, Bill $bill): 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 = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setBills(new Collection([$bill]));
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.bills.transactions', [$bill->id]) . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
/**
* Update a bill.
@@ -170,7 +309,11 @@ class BillController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($bill, new BillTransformer($this->parameters), 'bills');
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($bill, $transformer, 'bills');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -23,11 +23,16 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\BudgetLimitRequest;
use FireflyIII\Api\V1\Requests\BudgetRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\Transformers\BudgetTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -45,6 +50,7 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class BudgetController extends Controller
{
use TransactionFilter;
/** @var BudgetRepositoryInterface The budget repository */
private $repository;
@@ -68,6 +74,39 @@ class BudgetController extends Controller
);
}
/**
* Display a listing of the resource.
*
* @param Request $request
* @param Budget $budget
*
* @return JsonResponse
*/
public function budgetLimits(Request $request, Budget $budget): JsonResponse
{
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$this->parameters->set('budget_id', $budget->id);
$collection = $this->repository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end'));
$count = $collection->count();
$budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budgets.budget_limits', [$budget->id]) . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($budgetLimits, $transformer, 'budget_limits');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Remove the specified resource from storage.
*
@@ -109,13 +148,17 @@ class BudgetController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($budgets, new BudgetTransformer($this->parameters), 'budgets');
/** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($budgets, $transformer, 'budgets');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show a budget.
*
@@ -127,14 +170,14 @@ class BudgetController extends Controller
public function show(Request $request, Budget $budget): JsonResponse
{
$manager = new Manager();
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($budget, new BudgetTransformer($this->parameters), 'budgets');
/** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($budget, $transformer, 'budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -155,13 +198,99 @@ class BudgetController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($budget, new BudgetTransformer($this->parameters), 'budgets');
/** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($budget, $transformer, 'budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
throw new FireflyException('Could not store new budget.'); // @codeCoverageIgnore
}
/**
* Store a newly created resource in storage.
*
* @param BudgetLimitRequest $request
* @param Budget $budget
*
* @return JsonResponse
*/
public function storeBudgetLimit(BudgetLimitRequest $request, Budget $budget): JsonResponse
{
$data = $request->getAll();
$data['budget'] = $budget;
$budgetLimit = $this->repository->storeBudgetLimit($data);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($budgetLimit, $transformer, 'budget_limits');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show all transactions.
*
* @param Request $request
*
* @param Budget $budget
*
* @return JsonResponse
*/
public function transactions(Request $request, Budget $budget): JsonResponse
{
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// user can overrule page size with limit parameter.
$limit = $this->parameters->get('limit');
if (null !== $limit && $limit > 0) {
$pageSize = $limit;
}
$type = $request->get('type') ?? 'default';
$this->parameters->set('type', $type);
$types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setBudget($budget);
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.budgets.transactions', [$budget->id]) . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
/**
* Update a budget.
@@ -179,7 +308,11 @@ class BudgetController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($budget, new BudgetTransformer($this->parameters), 'budgets');
/** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($budget, $transformer, 'budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -26,9 +26,12 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\BudgetLimitRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -48,6 +51,7 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class BudgetLimitController extends Controller
{
use TransactionFilter;
/** @var BudgetRepositoryInterface The budget repository */
private $repository;
@@ -113,7 +117,12 @@ class BudgetLimitController extends Controller
$paginator->setPath(route('api.v1.budget_limits.index') . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($budgetLimits, new BudgetLimitTransformer($this->parameters), 'budget_limits');
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($budgetLimits, $transformer, 'budget_limits');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -130,14 +139,14 @@ class BudgetLimitController extends Controller
public function show(Request $request, BudgetLimit $budgetLimit): JsonResponse
{
$manager = new Manager;
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($budgetLimit, new BudgetLimitTransformer($this->parameters), 'budget_limits');
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($budgetLimit, $transformer, 'budget_limits');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -163,7 +172,55 @@ class BudgetLimitController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($budgetLimit, new BudgetLimitTransformer($this->parameters), 'budget_limits');
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($budgetLimit, $transformer, 'budget_limits');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show all transactions.
*
* @param Request $request
* @param BudgetLimit $budgetLimit
*
* @return JsonResponse
*/
public function transactions(Request $request, BudgetLimit $budgetLimit): 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 = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setBudget($budgetLimit->budget);
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date);
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.budget_limits.transactions', [$budgetLimit->id]) . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
@@ -178,18 +235,18 @@ class BudgetLimitController extends Controller
*/
public function update(BudgetLimitRequest $request, BudgetLimit $budgetLimit): JsonResponse
{
$data = $request->getAll();
$budget = $this->repository->findNull($data['budget_id']);
if (null === $budget) {
$budget = $budgetLimit->budget;
}
$data['budget'] = $budget;
$data = $request->getAll();
$data['budget'] = $budgetLimit->budget;
$budgetLimit = $this->repository->updateBudgetLimit($budgetLimit, $data);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($budgetLimit, new BudgetLimitTransformer($this->parameters), 'budget_limits');
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($budgetLimit, $transformer, 'budget_limits');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -25,9 +25,14 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\CategoryRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\CategoryTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -45,6 +50,7 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class CategoryController extends Controller
{
use TransactionFilter;
/** @var CategoryRepositoryInterface The category repository */
private $repository;
@@ -109,7 +115,13 @@ class CategoryController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($categories, new CategoryTransformer($this->parameters), 'categories');
/** @var CategoryTransformer $transformer */
$transformer = app(CategoryTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($categories, $transformer, 'categories');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -127,14 +139,14 @@ class CategoryController extends Controller
public function show(Request $request, Category $category): JsonResponse
{
$manager = new Manager();
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($category, new CategoryTransformer($this->parameters), 'categories');
/** @var CategoryTransformer $transformer */
$transformer = app(CategoryTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($category, $transformer, 'categories');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -155,13 +167,68 @@ class CategoryController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($category, new CategoryTransformer($this->parameters), 'categories');
/** @var CategoryTransformer $transformer */
$transformer = app(CategoryTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($category, $transformer, 'categories');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
throw new FireflyException('Could not store new category.'); // @codeCoverageIgnore
}
/**
* Show all transactions.
*
* @param Request $request
*
* @param Category $category
*
* @return JsonResponse
*/
public function transactions(Request $request, Category $category): 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 = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setCategory($category);
if (\in_array(TransactionType::TRANSFER, $types, true)) {
$collector->removeFilter(InternalTransferFilter::class);
}
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.categories.transactions', [$category->id]) . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
/**
* Update the category.
@@ -179,7 +246,11 @@ class CategoryController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($category, new CategoryTransformer($this->parameters), 'categories');
/** @var CategoryTransformer $transformer */
$transformer = app(CategoryTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($category, $transformer, 'categories');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -0,0 +1,348 @@
<?php
/**
* AccountController.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://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\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
/**
* Class AccountController
*/
class AccountController extends Controller
{
/** @var CurrencyRepositoryInterface */
private $currencyRepository;
/** @var AccountRepositoryInterface */
private $repository;
/**
* AccountController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUser($user);
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
$this->currencyRepository->setUser($user);
return $next($request);
}
);
}
/**
* @param Request $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function expenseOverview(Request $request): JsonResponse
{
// parameters for chart:
$start = (string)$request->get('start');
$end = (string)$request->get('end');
if ('' === $start || '' === $end) {
throw new FireflyException('Start and end are mandatory parameters.');
}
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
$start->subDay();
// prep some vars:
$currencies = [];
$chartData = [];
$tempData = [];
// grab all accounts and names
$accounts = $this->repository->getAccountsByType([AccountType::EXPENSE]);
$accountNames = $this->extractNames($accounts);
$startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start);
$endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end);
// loop the end balances. This is an array for each account ($expenses)
foreach ($endBalances as $accountId => $expenses) {
$accountId = (int)$accountId;
// loop each expense entry (each entry can be a different currency).
foreach ($expenses as $currencyId => $endAmount) {
$currencyId = (int)$currencyId;
// see if there is an accompanying start amount.
// grab the difference and find the currency.
$startAmount = $startBalances[$accountId][$currencyId] ?? '0';
$diff = bcsub($endAmount, $startAmount);
$currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->findNull($currencyId);
if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array.
$tempData[] = [
'name' => $accountNames[$accountId],
'difference' => $diff,
'diff_float' => (float)$diff,
'currency_id' => $currencyId,
];
}
}
}
// sort temp array by amount.
$amounts = array_column($tempData, 'diff_float');
array_multisort($amounts, SORT_DESC, $tempData);
// loop all found currencies and build the data array for the chart.
/**
* @var int $currencyId
* @var TransactionCurrency $currency
*/
foreach ($currencies as $currencyId => $currency) {
$currentSet = [
'label' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => $this->expandNames($tempData),
];
$chartData[$currencyId] = $currentSet;
}
// loop temp data and place data in correct array:
foreach ($tempData as $entry) {
$currencyId = $entry['currency_id'];
$name = $entry['name'];
$chartData[$currencyId]['entries'][$name] = round($entry['difference'], $chartData[$currencyId]['currency_decimal_places']);
}
$chartData = array_values($chartData);
return response()->json($chartData);
}
/**
* @param Request $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function overview(Request $request): JsonResponse
{
// parameters for chart:
$start = (string)$request->get('start');
$end = (string)$request->get('end');
if ('' === $start || '' === $end) {
throw new FireflyException('Start and end are mandatory parameters.');
}
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
// user's preferences
$defaultSet = $this->repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray();
$frontPage = app('preferences')->get('frontPageAccounts', $defaultSet);
$default = app('amount')->getDefaultCurrency();
if (0 === \count($frontPage->data)) {
$frontPage->data = $defaultSet;
$frontPage->save();
}
// get accounts:
$accounts = $this->repository->getAccountsById($frontPage->data);
$chartData = [];
/** @var Account $account */
foreach ($accounts as $account) {
$currency = $this->repository->getAccountCurrency($account);
if (null === $currency) {
$currency = $default;
}
$currentSet = [
'label' => $account->name,
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'type' => 'line', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [],
];
$currentStart = clone $start;
$range = app('steam')->balanceInRange($account, $start, clone $end);
$previous = round(array_values($range)[0], 12);
while ($currentStart <= $end) {
$format = $currentStart->format('Y-m-d');
$label = $currentStart->format('Y-m-d');
$balance = isset($range[$format]) ? round($range[$format], 12) : $previous;
$previous = $balance;
$currentStart->addDay();
$currentSet['entries'][$label] = $balance;
}
$chartData[] = $currentSet;
}
return response()->json($chartData);
}
/**
* @param Request $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function revenueOverview(Request $request): JsonResponse
{
// parameters for chart:
$start = (string)$request->get('start');
$end = (string)$request->get('end');
if ('' === $start || '' === $end) {
throw new FireflyException('Start and end are mandatory parameters.');
}
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
$start->subDay();
// prep some vars:
$currencies = [];
$chartData = [];
$tempData = [];
// grab all accounts and names
$accounts = $this->repository->getAccountsByType([AccountType::REVENUE]);
$accountNames = $this->extractNames($accounts);
$startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start);
$endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end);
// loop the end balances. This is an array for each account ($expenses)
foreach ($endBalances as $accountId => $expenses) {
$accountId = (int)$accountId;
// loop each expense entry (each entry can be a different currency).
foreach ($expenses as $currencyId => $endAmount) {
$currencyId = (int)$currencyId;
// see if there is an accompanying start amount.
// grab the difference and find the currency.
$startAmount = $startBalances[$accountId][$currencyId] ?? '0';
$diff = bcsub($endAmount, $startAmount);
$currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->findNull($currencyId);
if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array.
$tempData[] = [
'name' => $accountNames[$accountId],
'difference' => bcmul($diff, '-1'),
'diff_float' => (float)$diff * -1,
'currency_id' => $currencyId,
];
}
}
}
// sort temp array by amount.
$amounts = array_column($tempData, 'diff_float');
array_multisort($amounts, SORT_DESC, $tempData);
// loop all found currencies and build the data array for the chart.
/**
* @var int $currencyId
* @var TransactionCurrency $currency
*/
foreach ($currencies as $currencyId => $currency) {
$currentSet = [
'label' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => $this->expandNames($tempData),
];
$chartData[$currencyId] = $currentSet;
}
// loop temp data and place data in correct array:
foreach ($tempData as $entry) {
$currencyId = $entry['currency_id'];
$name = $entry['name'];
$chartData[$currencyId]['entries'][$name] = round($entry['difference'], $chartData[$currencyId]['currency_decimal_places']);
}
$chartData = array_values($chartData);
return response()->json($chartData);
}
/**
* Small helper function for the revenue and expense account charts.
* TODO should include Trait instead of doing this.
*
* @param array $names
*
* @return array
*/
protected function expandNames(array $names): array
{
$result = [];
foreach ($names as $entry) {
$result[$entry['name']] = 0;
}
return $result;
}
/**
* Small helper function for the revenue and expense account charts.
* TODO should include Trait instead of doing this.
*
* @param Collection $accounts
*
* @return array
*/
protected function extractNames(Collection $accounts): array
{
$return = [];
/** @var Account $account */
foreach ($accounts as $account) {
$return[$account->id] = $account->name;
}
return $return;
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* AvailableBudgetController.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
/**
* Class AvailableBudgetController
*/
class AvailableBudgetController extends Controller
{
/** @var BudgetRepositoryInterface */
private $repository;
/**
* AvailableBudgetController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(BudgetRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
}
);
}
/**
* @param AvailableBudget $availableBudget
*
* @return JsonResponse
*/
public function overview(AvailableBudget $availableBudget): JsonResponse
{
$currency = $availableBudget->transactionCurrency;
$budgets = $this->repository->getActiveBudgets();
$budgetInformation = $this->repository->spentInPeriodMc($budgets, new Collection, $availableBudget->start_date, $availableBudget->end_date);
$spent = 0.0;
// get for current currency
foreach ($budgetInformation as $spentInfo) {
if ($spentInfo['currency_id'] === $availableBudget->transaction_currency_id) {
$spent = $spentInfo['amount'];
}
}
$left = bcadd($availableBudget->amount, (string)$spent);
// left less than zero? Set to zero.
if (bccomp($left, '0') === -1) {
$left = '0';
}
$chartData = [
[
'label' => trans('firefly.spent'),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'type' => 'pie',
'yAxisID' => 0, // 0, 1, 2
'entries' => [$spent * -1],
],
[
'label' => trans('firefly.left'),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'type' => 'line', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [round($left, $currency->decimal_places)],
],
];
return response()->json($chartData);
}
}

View File

@@ -0,0 +1,209 @@
<?php
/**
* CategoryController.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://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\Exceptions\FireflyException;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
/**
* Class CategoryController
*/
class CategoryController extends Controller
{
/** @var CategoryRepositoryInterface */
private $categoryRepository;
/**
* AccountController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->categoryRepository = app(CategoryRepositoryInterface::class);
$this->categoryRepository->setUser($user);
return $next($request);
}
);
}
/**
* @param Request $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function overview(Request $request): JsonResponse
{
// parameters for chart:
$start = (string)$request->get('start');
$end = (string)$request->get('end');
if ('' === $start || '' === $end) {
throw new FireflyException('Start and end are mandatory parameters.');
}
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
$tempData = [];
$spent = $this->categoryRepository->spentInPeriodPerCurrency(new Collection, new Collection, $start, $end);
$earned = $this->categoryRepository->earnedInPeriodPerCurrency(new Collection, new Collection, $start, $end);
$categories = [];
// earned:
foreach ($earned as $categoryId => $row) {
$categoryName = $row['name'];
foreach ($row['earned'] as $currencyId => $income) {
// find or make set for currency:
$key = sprintf('%s-e', $currencyId);
$decimalPlaces = $income['currency_decimal_places'];
if (!isset($tempData[$key])) {
$tempData[$key] = [
'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $income['currency_symbol']]),
'currency_id' => $income['currency_id'],
'currency_code' => $income['currency_code'],
'currency_symbol' => $income['currency_symbol'],
'currency_decimal_places' => $decimalPlaces,
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [],
];
}
$amount = round($income['earned'], $decimalPlaces);
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
$tempData[$key]['entries'][$categoryName]
= $amount;
}
}
// earned with no category:
$noCategory = $this->categoryRepository->earnedInPeriodPcWoCategory(new Collection, $start, $end);
foreach ($noCategory as $currencyId => $income) {
$categoryName = (string)trans('firefly.no_category');
// find or make set for currency:
$key = sprintf('%s-e', $currencyId);
$decimalPlaces = $income['currency_decimal_places'];
if (!isset($tempData[$key])) {
$tempData[$key] = [
'label' => (string)trans('firefly.box_earned_in_currency', ['currency' => $income['currency_symbol']]),
'currency_id' => $income['currency_id'],
'currency_code' => $income['currency_code'],
'currency_symbol' => $income['currency_symbol'],
'currency_decimal_places' => $decimalPlaces,
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [],
];
}
$amount = round($income['spent'], $decimalPlaces);
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
$tempData[$key]['entries'][$categoryName]
= $amount;
}
// spent
foreach ($spent as $categoryId => $row) {
$categoryName = $row['name'];
// create a new set if necessary, "spent (EUR)":
foreach ($row['spent'] as $currencyId => $expense) {
// find or make set for currency:
$key = sprintf('%s-s', $currencyId);
$decimalPlaces = $expense['currency_decimal_places'];
if (!isset($tempData[$key])) {
$tempData[$key] = [
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $expense['currency_symbol']]),
'currency_id' => $expense['currency_id'],
'currency_code' => $expense['currency_code'],
'currency_symbol' => $expense['currency_symbol'],
'currency_decimal_places' => $decimalPlaces,
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [],
];
}
$amount = round($expense['spent'], $decimalPlaces);
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
$tempData[$key]['entries'][$categoryName]
= $amount;
}
}
// spent with no category
$noCategory = $this->categoryRepository->spentInPeriodPcWoCategory(new Collection, $start, $end);
foreach ($noCategory as $currencyId => $expense) {
$categoryName = (string)trans('firefly.no_category');
// find or make set for currency:
$key = sprintf('%s-s', $currencyId);
$decimalPlaces = $expense['currency_decimal_places'];
if (!isset($tempData[$key])) {
$tempData[$key] = [
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $expense['currency_symbol']]),
'currency_id' => $expense['currency_id'],
'currency_code' => $expense['currency_code'],
'currency_symbol' => $expense['currency_symbol'],
'currency_decimal_places' => $decimalPlaces,
'type' => 'bar', // line, area or bar
'yAxisID' => 0, // 0, 1, 2
'entries' => [],
];
}
$amount = round($expense['spent'], $decimalPlaces);
$categories[$categoryName] = isset($categories[$categoryName]) ? $categories[$categoryName] + $amount : $amount;
$tempData[$key]['entries'][$categoryName]
= $amount;
}
asort($categories);
$keys = array_keys($categories);
// re-sort every spent array and add 0 for missing entries.
foreach ($tempData as $index => $set) {
$oldSet = $set['entries'];
$newSet = [];
foreach ($keys as $key) {
$value = $oldSet[$key] ?? 0;
$value = $value < 0 ? $value * -1 : $value;
$newSet[$key] = $value;
}
$tempData[$index]['entries'] = $newSet;
}
$chartData = array_values($tempData);
return response()->json($chartData);
}
}

View File

@@ -23,12 +23,12 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\ConfigurationRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Configuration;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* Class ConfigurationController.
@@ -54,7 +54,6 @@ class ConfigurationController extends Controller
$admin = auth()->user();
if (!$this->repository->hasRole($admin, 'owner')) {
/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
throw new FireflyException('No access to method.'); // @codeCoverageIgnore
}
@@ -72,40 +71,25 @@ class ConfigurationController extends Controller
{
$configData = $this->getConfigData();
return response()->json(['data' => $configData], 200)->header('Content-Type', 'application/vnd.api+json');
return response()->json(['data' => $configData])->header('Content-Type', 'application/vnd.api+json');
}
/**
* Update the configuration.
*
* @param Request $request
* @param ConfigurationRequest $request
* @param string $name
*
* @return JsonResponse
* @throws FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function update(Request $request): JsonResponse
public function update(ConfigurationRequest $request, string $name): JsonResponse
{
$name = $request->get('name');
$value = $request->get('value');
$valid = ['is_demo_site', 'permission_update_check', 'single_user_mode'];
if (!\in_array($name, $valid, true)) {
throw new FireflyException('You cannot edit this configuration value.');
}
$configValue = '';
switch ($name) {
case 'is_demo_site':
case 'single_user_mode':
$configValue = 'true' === $value;
break;
case 'permission_update_check':
$configValue = (int)$value >= -1 && (int)$value <= 1 ? (int)$value : -1;
break;
}
app('fireflyconfig')->set($name, $configValue);
$data = $request->getAll();
app('fireflyconfig')->set($name, $data['value']);
$configData = $this->getConfigData();
return response()->json(['data' => $configData], 200)->header('Content-Type', 'application/vnd.api+json');
return response()->json(['data' => $configData])->header('Content-Type', 'application/vnd.api+json');
}
/**

View File

@@ -100,11 +100,11 @@ class Controller extends BaseController
// some date fields:
$dates = ['start', 'end', 'date'];
foreach ($dates as $field) {
$date = request()->get($field);
$date = request()->query->get($field);
$obj = null;
if (null !== $date) {
try {
$obj = new Carbon($date);
$obj = Carbon::parse($date);
} catch (InvalidDateException $e) {
// don't care
Log::error(sprintf('Invalid date exception in API controller: %s', $e->getMessage()));
@@ -113,6 +113,15 @@ class Controller extends BaseController
$bag->set($field, $obj);
}
// integer fields:
$integers = ['limit'];
foreach ($integers as $integer) {
$value = request()->query->get($integer);
if (null !== $value) {
$bag->set($integer, (int)$value);
}
}
return $bag;
}

View File

@@ -26,14 +26,41 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\CurrencyRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Account;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleTrigger;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\AvailableBudgetTransformer;
use FireflyIII\Transformers\BillTransformer;
use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\Transformers\CurrencyExchangeRateTransformer;
use FireflyIII\Transformers\CurrencyTransformer;
use FireflyIII\Transformers\RecurrenceTransformer;
use FireflyIII\Transformers\RuleTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
@@ -47,6 +74,7 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class CurrencyController extends Controller
{
use AccountFilter, TransactionFilter;
/** @var CurrencyRepositoryInterface The currency repository */
private $repository;
/** @var UserRepositoryInterface The user repository */
@@ -73,6 +101,243 @@ class CurrencyController extends Controller
);
}
/**
* Display a list of accounts.
*
* @param Request $request
* @param TransactionCurrency $currency
*
* @return JsonResponse
*/
public function accounts(Request $request, TransactionCurrency $currency): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// read type from URI
$type = $request->get('type') ?? 'all';
$this->parameters->set('type', $type);
// types to get, page size:
$types = $this->mapAccountTypes($this->parameters->get('type'));
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of accounts. Count it and split it.
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$unfiltered = $accountRepository->getAccountsByType($types);
// filter list on currency preference:
$collection = $unfiltered->filter(
function (Account $account) use ($currency, $accountRepository) {
$currencyId = (int)$accountRepository->getMetaValue($account, 'currency_id');
return $currencyId === $currency->id;
}
);
$count = $collection->count();
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.accounts', [$currency->code]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($accounts, $transformer, 'accounts');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Display a listing of the resource.
*
* @param Request $request
*
* @param TransactionCurrency $currency
*
* @return JsonResponse
*/
public function availableBudgets(Request $request, TransactionCurrency $currency): JsonResponse
{
/** @var User $admin */
$admin = auth()->user();
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of available budgets. Count it and split it.
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$repository->setUser($admin);
$unfiltered = $repository->getAvailableBudgets();
// filter list.
$collection = $unfiltered->filter(
function (AvailableBudget $availableBudget) use ($currency) {
return $availableBudget->transaction_currency_id === $currency->id;
}
);
$count = $collection->count();
$availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.available_budgets', [$currency->code]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($availableBudgets, $transformer, 'available_budgets');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List all bills
*
* @param Request $request
* @param TransactionCurrency $currency
*
* @return JsonResponse
*/
public function bills(Request $request, TransactionCurrency $currency): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$paginator = $repository->getPaginator($pageSize);
/** @var Collection $bills */
$unfiltered = $paginator->getCollection();
// filter and paginate list:
$collection = $unfiltered->filter(
function (Bill $bill) use ($currency) {
return $bill->transaction_currency_id === $currency->id;
}
);
$count = $collection->count();
$bills = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.bills', [$currency->code]) . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($bills, $transformer, 'bills');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List all budget limits
*
* @param Request $request
*
* @param TransactionCurrency $currency
*
* @return JsonResponse
*/
public function budgetLimits(Request $request, TransactionCurrency $currency): JsonResponse
{
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$unfiltered = $repository->getAllBudgetLimits($this->parameters->get('start'), $this->parameters->get('end'));
// TODO replace this
// filter budget limits on currency ID
$collection = $unfiltered->filter(
function (BudgetLimit $budgetLimit) use ($currency) {
return $budgetLimit->transaction_currency_id === $currency->id;
}
);
$count = $collection->count();
$budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.budget_limits', [$currency->code]) . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($budgetLimits, $transformer, 'budget_limits');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show a list of known exchange rates
*
* @param Request $request
* @param TransactionCurrency $currency
*
* @return JsonResponse
*/
public function cer(Request $request, TransactionCurrency $currency): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$collection = $this->repository->getExchangeRates($currency);
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$count = $collection->count();
$exchangeRates = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($exchangeRates, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.cer', [$currency->code]) . $this->buildParams());
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var CurrencyExchangeRateTransformer $transformer */
$transformer = app(CurrencyExchangeRateTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($exchangeRates, $transformer, 'currency_exchange_rates');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Remove the specified resource from storage.
*
@@ -90,7 +355,7 @@ class CurrencyController extends Controller
// access denied:
throw new FireflyException('No access to method, user is not owner.'); // @codeCoverageIgnore
}
if (!$this->repository->canDeleteCurrency($currency)) {
if ($this->repository->currencyInUse($currency)) {
throw new FireflyException('No access to method, currency is in use.'); // @codeCoverageIgnore
}
$this->repository->destroy($currency);
@@ -98,6 +363,66 @@ class CurrencyController extends Controller
return response()->json([], 204);
}
/**
* Disable a currency.
*
* @param Request $request
* @param TransactionCurrency $currency
*
* @return JsonResponse
*/
public function disable(Request $request, TransactionCurrency $currency): JsonResponse
{
// must be unused.
if ($this->repository->currencyInUse($currency)) {
return response()->json([], 409);
}
$this->repository->disable($currency);
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency);
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($currency, $transformer, 'currencies');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Enable a currency.
*
* @param Request $request
* @param TransactionCurrency $currency
*
* @return JsonResponse
*/
public function enable(Request $request, TransactionCurrency $currency): JsonResponse
{
$this->repository->enable($currency);
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency);
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($currency, $transformer, 'currencies');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Display a listing of the resource.
*
@@ -108,7 +433,7 @@ class CurrencyController extends Controller
public function index(Request $request): JsonResponse
{
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$collection = $this->repository->get();
$collection = $this->repository->getAll();
$count = $collection->count();
// slice them:
$currencies = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
@@ -122,12 +447,158 @@ class CurrencyController extends Controller
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency);
$resource = new FractalCollection($currencies, new CurrencyTransformer($this->parameters), 'currencies');
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($currencies, $transformer, 'currencies');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Make the currency a default currency.
*
* @param Request $request
* @param TransactionCurrency $currency
*
* @return JsonResponse
*/
public function makeDefault(Request $request, TransactionCurrency $currency): JsonResponse
{
$this->repository->enable($currency);
app('preferences')->set('currencyPreference', $currency->code);
app('preferences')->mark();
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$this->parameters->set('defaultCurrency', $currency);
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($currency, $transformer, 'currencies');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List all recurring transactions.
*
* @param Request $request
*
* @param TransactionCurrency $currency
*
* @return JsonResponse]
*/
public function recurrences(Request $request, TransactionCurrency $currency): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of budgets. Count it and split it.
/** @var RecurringRepositoryInterface $repository */
$repository = app(RecurringRepositoryInterface::class);
$unfiltered = $repository->getAll();
// filter selection
$collection = $unfiltered->filter(
function (Recurrence $recurrence) use ($currency) {
/** @var RecurrenceTransaction $transaction */
foreach ($recurrence->recurrenceTransactions as $transaction) {
if ($transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id) {
return $recurrence;
}
}
return null;
}
);
$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.currencies.recurrences', [$currency->code]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($piggyBanks, $transformer, 'recurrences');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List all of them.
*
* @param Request $request
* @param TransactionCurrency $currency
*
* @return JsonResponse]
*/
public function rules(Request $request, TransactionCurrency $currency): JsonResponse
{
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of budgets. Count it and split it.
/** @var RuleRepositoryInterface $repository */
$repository = app(RuleRepositoryInterface::class);
$unfiltered = $repository->getAll();
$collection = $unfiltered->filter(
function (Rule $rule) use ($currency) {
/** @var RuleTrigger $trigger */
foreach ($rule->ruleTriggers as $trigger) {
if ('currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value) {
return $rule;
}
}
return null;
}
);
$count = $collection->count();
$rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.rules.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($rules, $transformer, 'rules');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show a currency.
@@ -140,16 +611,16 @@ class CurrencyController extends Controller
public function show(Request $request, TransactionCurrency $currency): JsonResponse
{
$manager = new Manager();
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency);
$resource = new Item($currency, new CurrencyTransformer($this->parameters), 'currencies');
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($currency, $transformer, 'currencies');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -177,7 +648,11 @@ class CurrencyController extends Controller
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency);
$resource = new Item($currency, new CurrencyTransformer($this->parameters), 'currencies');
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($currency, $transformer, 'currencies');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -185,6 +660,57 @@ class CurrencyController extends Controller
}
/**
* Show all transactions.
*
* @param Request $request
*
* @param TransactionCurrency $currency
*
* @return JsonResponse
*/
public function transactions(Request $request, TransactionCurrency $currency): 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 = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setCurrency($currency);
if (\in_array(TransactionType::TRANSFER, $types, true)) {
$collector->removeFilter(InternalTransferFilter::class);
}
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.currencies.transactions', [$currency->code]) . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
/**
* Update a currency.
@@ -211,7 +737,11 @@ class CurrencyController extends Controller
$defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user());
$this->parameters->set('defaultCurrency', $defaultCurrency);
$resource = new Item($currency, new CurrencyTransformer($this->parameters), 'currencies');
/** @var CurrencyTransformer $transformer */
$transformer = app(CurrencyTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($currency, $transformer, 'currencies');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -92,6 +92,7 @@ class CurrencyExchangeRateController extends Controller
$this->parameters->set('from', $fromCurrency->code);
$this->parameters->set('to', $toCurrency->code);
$this->parameters->set('date', $dateObj->format('Y-m-d'));
$this->parameters->set('amount', $request->get('amount'));
$rate = $this->repository->getExchangeRate($fromCurrency, $toCurrency, $dateObj);
if (null === $rate) {
@@ -103,8 +104,10 @@ class CurrencyExchangeRateController extends Controller
$service->setUser($admin);
$rate = $service->getRate($fromCurrency, $toCurrency, $dateObj);
}
$resource = new Item($rate, new CurrencyExchangeRateTransformer($this->parameters), 'currency_exchange_rates');
/** @var CurrencyExchangeRateTransformer $transformer */
$transformer = app(CurrencyExchangeRateTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($rate, $transformer, 'currency_exchange_rates');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}

View File

@@ -0,0 +1,185 @@
<?php
/**
* ImportController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\ImportJobTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class ImportController
*/
class ImportController extends Controller
{
use TransactionFilter;
/** @var ImportJobRepositoryInterface Import job repository. */
private $repository;
/**
* LinkTypeController constructor.
*/
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);
}
);
}
/**
* @param Request $request
*
* @return JsonResponse
*/
public function listAll(Request $request): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$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());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @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 Request $request
* @param ImportJob $importJob
*
* @return JsonResponse
*/
public function show(Request $request, ImportJob $importJob): JsonResponse
{
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @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
*/
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 = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$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();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setTag($tag);
if (\in_array(TransactionType::TRANSFER, $types, true)) {
$collector->removeFilter(InternalTransferFilter::class);
}
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection();
}
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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

@@ -25,10 +25,15 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\LinkTypeRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\LinkType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -46,6 +51,7 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class LinkTypeController extends Controller
{
use TransactionFilter;
/** @var LinkTypeRepositoryInterface The link type repository */
private $repository;
@@ -84,7 +90,7 @@ class LinkTypeController extends Controller
if (false === $linkType->editable) {
throw new FireflyException(sprintf('You cannot delete this link type (#%d, "%s")', $linkType->id, $linkType->name));
}
$this->repository->destroy($linkType, null);
$this->repository->destroy($linkType);
return response()->json([], 204);
}
@@ -114,7 +120,12 @@ class LinkTypeController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($linkTypes, new LinkTypeTransformer($this->parameters), 'link_types');
/** @var LinkTypeTransformer $transformer */
$transformer = app(LinkTypeTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($linkTypes, $transformer, 'link_types');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -132,14 +143,13 @@ class LinkTypeController extends Controller
public function show(Request $request, LinkType $linkType): JsonResponse
{
$manager = new Manager;
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($linkType, new LinkTypeTransformer($this->parameters), 'link_types');
/** @var LinkTypeTransformer $transformer */
$transformer = app(LinkTypeTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($linkType, $transformer, 'link_types');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -168,12 +178,70 @@ class LinkTypeController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($linkType, new LinkTypeTransformer($this->parameters), 'link_types');
/** @var LinkTypeTransformer $transformer */
$transformer = app(LinkTypeTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($linkType, $transformer, 'link_types');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Delete the resource.
*
* @param Request $request
* @param LinkType $linkType
*
* @return JsonResponse
*/
public function transactions(Request $request, LinkType $linkType): 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 = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// whatever is returned by the query, it must be part of these journals:
$journalIds = $this->repository->getJournalIds($linkType);
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setJournalIds($journalIds);
if (\in_array(TransactionType::TRANSFER, $types, true)) {
$collector->removeFilter(InternalTransferFilter::class);
}
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
/**
* Update object.
*
@@ -202,7 +270,11 @@ class LinkTypeController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($linkType, new LinkTypeTransformer($this->parameters), 'link_types');
/** @var LinkTypeTransformer $transformer */
$transformer = app(LinkTypeTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($linkType, $transformer, 'link_types');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -27,6 +27,7 @@ use FireflyIII\Api\V1\Requests\PiggyBankRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Transformers\PiggyBankEventTransformer;
use FireflyIII\Transformers\PiggyBankTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@@ -110,7 +111,47 @@ class PiggyBankController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($piggyBanks, new PiggyBankTransformer($this->parameters), 'piggy_banks');
/** @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');
}
/**
* List single resource.
*
* @param Request $request
* @param PiggyBank $piggyBank
*
* @return JsonResponse
*/
public function piggyBankEvents(Request $request, PiggyBank $piggyBank): JsonResponse
{
// types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$collection = $this->repository->getEvents($piggyBank);
$count = $collection->count();
$events = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($events, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.piggy_banks.events', [$piggyBank->id]) . $this->buildParams());
/** @var PiggyBankEventTransformer $transformer */
$transformer = app(PiggyBankEventTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($events, $transformer, 'piggy_bank_events');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -128,14 +169,14 @@ class PiggyBankController extends Controller
public function show(Request $request, PiggyBank $piggyBank): JsonResponse
{
$manager = new Manager();
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($piggyBank, new PiggyBankTransformer($this->parameters), 'piggy_banks');
/** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($piggyBank, $transformer, 'piggy_banks');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -157,7 +198,11 @@ class PiggyBankController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($piggyBank, new PiggyBankTransformer($this->parameters), 'piggy_banks');
/** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($piggyBank, $transformer, 'piggy_banks');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -180,7 +225,11 @@ class PiggyBankController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($piggyBank, new PiggyBankTransformer($this->parameters), 'piggy_banks');
/** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($piggyBank, $transformer, 'piggy_banks');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -24,7 +24,9 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\PreferenceRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Transformers\PreferenceTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@@ -41,6 +43,34 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class PreferenceController extends Controller
{
/**
* LinkTypeController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($user);
// an important fallback is that the frontPageAccount array gets refilled automatically
// when it turns up empty.
$frontPageAccounts = app('preferences')->getForUser($user, 'frontPageAccounts', [])->data;
if (0 === \count($frontPageAccounts)) {
/** @var Collection $accounts */
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$accountIds = $accounts->pluck('id')->toArray();
app('preferences')->setForUser($user, 'frontPageAccounts', $accountIds);
}
return $next($request);
}
);
}
/**
* List all of them.
*
@@ -51,12 +81,13 @@ class PreferenceController extends Controller
public function index(Request $request): JsonResponse
{
/** @var User $user */
$user = auth()->user();
$available = [
$user = auth()->user();
$available = [
'language', 'customFiscalYear', 'fiscalYearStart', 'currencyPreference',
'transaction_journal_optional_fields', 'frontPageAccounts', 'viewRange',
'listPageSize, twoFactorAuthEnabled',
];
$preferences = new Collection;
foreach ($available as $name) {
$pref = app('preferences')->getForUser($user, $name);
@@ -71,7 +102,12 @@ class PreferenceController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($preferences, new PreferenceTransformer($this->parameters), 'preferences');
/** @var PreferenceTransformer $transformer */
$transformer = app(PreferenceTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($preferences, $transformer, 'preferences');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -79,12 +115,13 @@ class PreferenceController extends Controller
}
/**
* List single resource.
* Return a single preference by name.
*
* @param Request $request
* @param Preference $preference
*
* @return JsonResponse
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function show(Request $request, Preference $preference): JsonResponse
{
@@ -94,10 +131,13 @@ class PreferenceController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($preference, new PreferenceTransformer($this->parameters), 'preferences');
/** @var PreferenceTransformer $transformer */
$transformer = app(PreferenceTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($preference, $transformer, 'preferences');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
@@ -137,7 +177,11 @@ class PreferenceController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($result, new PreferenceTransformer($this->parameters), 'preferences');
/** @var PreferenceTransformer $transformer */
$transformer = app(PreferenceTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($result, $transformer, 'preferences');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -24,9 +24,16 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\RecurrenceRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Support\Cronjobs\RecurringCronjob;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\RecurrenceTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -36,12 +43,14 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Log;
/**
* Class RecurrenceController
*/
class RecurrenceController extends Controller
{
use TransactionFilter;
/** @var RecurringRepositoryInterface The recurring transaction repository */
private $repository;
@@ -106,7 +115,12 @@ class RecurrenceController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($piggyBanks, new RecurrenceTransformer($this->parameters), 'recurrences');
/** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($piggyBanks, $transformer, 'recurrences');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -124,14 +138,14 @@ class RecurrenceController extends Controller
public function show(Request $request, Recurrence $recurrence): JsonResponse
{
$manager = new Manager();
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($recurrence, new RecurrenceTransformer($this->parameters), 'recurrences');
/** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($recurrence, $transformer, 'recurrences');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -151,11 +165,94 @@ class RecurrenceController extends Controller
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($recurrence, new RecurrenceTransformer($this->parameters), 'recurrences');
/** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($recurrence, $transformer, 'recurrences');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show transactions for this recurrence.
*
* @param Request $request
* @param Recurrence $recurrence
*
* @return JsonResponse
*/
public function transactions(Request $request, Recurrence $recurrence): 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 = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// whatever is returned by the query, it must be part of these journals:
$journalIds = $this->repository->getJournalIds($recurrence);
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setJournalIds($journalIds);
if (\in_array(TransactionType::TRANSFER, $types, true)) {
$collector->removeFilter(InternalTransferFilter::class);
}
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
/**
* @return JsonResponse
* @throws FireflyException
*/
public function trigger(): JsonResponse
{
/** @var RecurringCronjob $recurring */
$recurring = app(RecurringCronjob::class);
try {
$result = $recurring->fire();
} catch (FireflyException $e) {
Log::error($e->getMessage());
throw new FireflyException('Could not fire recurring cron job.');
}
if (false === $result) {
return response()->json([], 204);
}
if (true === $result) {
return response()->json();
}
return response()->json([], 418); // @codeCoverageIgnore
}
/**
* Update single recurrence.
*
@@ -171,8 +268,11 @@ class RecurrenceController extends Controller
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RecurrenceTransformer $transformer */
$transformer = app(RecurrenceTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($category, new RecurrenceTransformer($this->parameters), 'recurrences');
$resource = new Item($category, $transformer, 'recurrences');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -23,25 +23,36 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon;
use FireflyIII\Api\V1\Requests\RuleRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Rule;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\Transformers\RuleTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Log;
/**
* Class RuleController
*/
class RuleController extends Controller
{
/** @var AccountRepositoryInterface Account repository */
private $accountRepository;
/** @var RuleRepositoryInterface The rule repository */
private $ruleRepository;
@@ -59,6 +70,9 @@ class RuleController extends Controller
$this->ruleRepository = app(RuleRepositoryInterface::class);
$this->ruleRepository->setUser($user);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->accountRepository->setUser($user);
return $next($request);
}
);
@@ -105,7 +119,12 @@ class RuleController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($rules, new RuleTransformer($this->parameters), 'rules');
/** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($rules, $transformer, 'rules');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -123,14 +142,14 @@ class RuleController extends Controller
public function show(Request $request, Rule $rule): JsonResponse
{
$manager = new Manager();
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($rule, new RuleTransformer($this->parameters), 'rules');
/** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($rule, $transformer, 'rules');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -150,11 +169,127 @@ class RuleController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($rule, new RuleTransformer($this->parameters), 'rules');
/** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($rule, $transformer, 'rules');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param Request $request
* @param Rule $rule
*
* @return JsonResponse
* @throws FireflyException
*/
public function testRule(Request $request, Rule $rule): JsonResponse
{
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$page = 0 === (int)$request->query('page') ? 1 : (int)$request->query('page');
$startDate = null === $request->query('start_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('start_date'));
$endDate = null === $request->query('end_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('end_date'));
$searchLimit = 0 === (int)$request->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$request->query('search_limit');
$triggerLimit = 0 === (int)$request->query('triggered_limit') ? (int)config('firefly.test-triggers.range') : (int)$request->query('triggered_limit');
$accountList = '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts'));
$accounts = new Collection;
foreach ($accountList as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $this->accountRepository->findNull((int)$accountId);
if (null !== $account && AccountType::ASSET === $account->accountType->type) {
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$accounts->push($account);
}
if (null === $account) {
Log::debug(sprintf('No asset account with id "%s"', $accountId));
}
}
/** @var Rule $rule */
Log::debug(sprintf('Now testing rule #%d, "%s"', $rule->id, $rule->title));
/** @var TransactionMatcher $matcher */
$matcher = app(TransactionMatcher::class);
// set all parameters:
$matcher->setRule($rule);
$matcher->setStartDate($startDate);
$matcher->setEndDate($endDate);
$matcher->setSearchLimit($searchLimit);
$matcher->setTriggeredLimit($triggerLimit);
$matcher->setAccounts($accounts);
$matchingTransactions = $matcher->findTransactionsByRule();
$matchingTransactions = $matchingTransactions->unique('id');
// make paginator out of results.
$count = $matchingTransactions->count();
$transactions = $matchingTransactions->slice(($page - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($transactions, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.rules.test', [$rule->id]) . $this->buildParams());
// resulting list is presented as JSON thing.
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($matchingTransactions, $transformer, 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Execute the given rule group on a set of existing transactions.
*
* @param Request $request
* @param Rule $rule
*
* @return JsonResponse
*/
public function triggerRule(Request $request, Rule $rule): JsonResponse
{
// Get parameters specified by the user
/** @var User $user */
$user = auth()->user();
$startDate = new Carbon($request->get('start_date'));
$endDate = new Carbon($request->get('end_date'));
$accountList = '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts'));
$accounts = new Collection;
foreach ($accountList as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $this->accountRepository->findNull((int)$accountId);
if (null !== $account && $this->accountRepository->isAsset($account)) {
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$accounts->push($account);
}
if (null === $account) {
Log::debug(sprintf('No asset account with id "%s"', $accountId));
}
}
// Create a job to do the work asynchronously
$job = new ExecuteRuleOnExistingTransactions($rule);
// Apply parameters to the job
$job->setUser($user);
$job->setAccounts($accounts);
$job->setStartDate($startDate);
$job->setEndDate($endDate);
// Dispatch a new job to execute it in a queue
$this->dispatch($job);
return response()->json([], 204);
}
/**
* Update a rule.
*
@@ -170,7 +305,11 @@ class RuleController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($rule, new RuleTransformer($this->parameters), 'rules');
/** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($rule, $transformer, 'rules');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -23,19 +23,30 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon;
use FireflyIII\Api\V1\Requests\RuleGroupRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\Transformers\RuleGroupTransformer;
use FireflyIII\Transformers\RuleTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Log;
/**
@@ -43,6 +54,8 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class RuleGroupController extends Controller
{
/** @var AccountRepositoryInterface Account repository */
private $accountRepository;
/** @var RuleGroupRepositoryInterface The rule group repository */
private $ruleGroupRepository;
@@ -60,6 +73,9 @@ class RuleGroupController extends Controller
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
$this->ruleGroupRepository->setUser($user);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->accountRepository->setUser($user);
return $next($request);
}
);
@@ -106,12 +122,55 @@ class RuleGroupController extends Controller
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($ruleGroups, new RuleGroupTransformer($this->parameters), 'rule_groups');
/** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($ruleGroups, $transformer, 'rule_groups');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param Request $request
* @param RuleGroup $group
*
* @return JsonResponse
*/
public function rules(Request $request, RuleGroup $group): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of budgets. Count it and split it.
$collection = $this->ruleGroupRepository->getRules($group);
$count = $collection->count();
$rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.rule_groups.rules', [$group->id]) . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var RuleTransformer $transformer */
$transformer = app(RuleTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($rules, $transformer, 'rules');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List single resource.
*
@@ -123,14 +182,14 @@ class RuleGroupController extends Controller
public function show(Request $request, RuleGroup $ruleGroup): JsonResponse
{
$manager = new Manager();
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($ruleGroup, new RuleGroupTransformer($this->parameters), 'rule_groups');
/** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($ruleGroup, $transformer, 'rule_groups');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -150,14 +209,128 @@ class RuleGroupController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($ruleGroup, new RuleGroupTransformer($this->parameters), 'rule_groups');
/** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($ruleGroup, $transformer, 'rule_groups');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param Request $request
* @param RuleGroup $group
*
* @return JsonResponse
* @throws FireflyException
*/
public function testGroup(Request $request, RuleGroup $group): JsonResponse
{
Log::debug('Now in testGroup()');
/** @var Collection $rules */
$rules = $this->ruleGroupRepository->getActiveRules($group);
if (0 === $rules->count()) {
throw new FireflyException('No rules in this rule group.');
}
$parameters = $this->getTestParameters($request);
$accounts = $this->getAccountParameter($parameters['account_list']);
$matchingTransactions = new Collection;
Log::debug(sprintf('Going to test %d rules', $rules->count()));
/** @var Rule $rule */
foreach ($rules as $rule) {
Log::debug(sprintf('Now testing rule #%d, "%s"', $rule->id, $rule->title));
/** @var TransactionMatcher $matcher */
$matcher = app(TransactionMatcher::class);
// set all parameters:
$matcher->setRule($rule);
$matcher->setStartDate($parameters['start_date']);
$matcher->setEndDate($parameters['end_date']);
$matcher->setSearchLimit($parameters['search_limit']);
$matcher->setTriggeredLimit($parameters['trigger_limit']);
$matcher->setAccounts($accounts);
$result = $matcher->findTransactionsByRule();
$matchingTransactions = $result->merge($matchingTransactions);
}
$matchingTransactions = $matchingTransactions->unique('id');
// make paginator out of results.
$count = $matchingTransactions->count();
$transactions = $matchingTransactions->slice(($parameters['page'] - 1) * $parameters['page_size'], $parameters['page_size']);
// make paginator:
$paginator = new LengthAwarePaginator($transactions, $count, $parameters['page_size'], $parameters['page']);
$paginator->setPath(route('api.v1.rule_groups.test', [$group->id]) . $this->buildParams());
// resulting list is presented as JSON thing.
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($matchingTransactions, $transformer, 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Execute the given rule group on a set of existing transactions.
*
* @param Request $request
* @param RuleGroup $group
*
* @return JsonResponse
*/
public function triggerGroup(Request $request, RuleGroup $group): JsonResponse
{
// Get parameters specified by the user
/** @var User $user */
$user = auth()->user();
$startDate = new Carbon($request->get('start_date'));
$endDate = new Carbon($request->get('end_date'));
$accountList = '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts'));
$accounts = new Collection;
foreach ($accountList as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $this->accountRepository->findNull((int)$accountId);
if (null !== $account && AccountType::ASSET === $account->accountType->type) {
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$accounts->push($account);
}
if (null === $account) {
Log::debug(sprintf('No asset account with id "%s"', $accountId));
}
}
/** @var Collection $rules */
$rules = $this->ruleGroupRepository->getActiveRules($group);
foreach ($rules as $rule) {
// Create a job to do the work asynchronously
$job = new ExecuteRuleOnExistingTransactions($rule);
// Apply parameters to the job
$job->setUser($user);
$job->setAccounts($accounts);
$job->setStartDate($startDate);
$job->setEndDate($endDate);
// Dispatch a new job to execute it in a queue
$this->dispatch($job);
}
return response()->json([], 204);
}
/**
* Update a rule group.
* TODO update order of rule group
*
* @param RuleGroupRequest $request
* @param RuleGroup $ruleGroup
@@ -171,9 +344,58 @@ class RuleGroupController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($ruleGroup, new RuleGroupTransformer($this->parameters), 'rule_groups');
/** @var RuleGroupTransformer $transformer */
$transformer = app(RuleGroupTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($ruleGroup, $transformer, 'rule_groups');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param array $accounts
*
* @return Collection
*/
private function getAccountParameter(array $accounts): Collection
{
$return = new Collection;
foreach ($accounts as $accountId) {
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
$account = $this->accountRepository->findNull((int)$accountId);
if (null !== $account && AccountType::ASSET === $account->accountType->type) {
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
$return->push($account);
}
if (null === $account) {
Log::debug(sprintf('No asset account with id "%s"', $accountId));
}
}
return $return;
}
/**
* @param Request $request
*
* @return array
*/
private function getTestParameters(Request $request): array
{
return [
'page_size' => (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data,
'page' => 0 === (int)$request->query('page') ? 1 : (int)$request->query('page'),
'start_date' => null === $request->query('start_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('start_date')),
'end_date' => null === $request->query('end_date') ? null : Carbon::createFromFormat('Y-m-d', $request->query('end_date')),
'search_limit' => 0 === (int)$request->query('search_limit') ? (int)config('firefly.test-triggers.limit') : (int)$request->query('search_limit'),
'trigger_limit' => 0 === (int)$request->query('triggered_limit')
? (int)config('firefly.test-triggers.range')
: (int)$request->query(
'triggered_limit'
),
'account_list' => '' === (string)$request->query('accounts') ? [] : explode(',', $request->query('accounts')),
];
}
}

View File

@@ -0,0 +1,417 @@
<?php
/**
* SummaryController.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
/**
* Class SummaryController
*/
class SummaryController extends Controller
{
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var BillRepositoryInterface */
private $billRepository;
/** @var BudgetRepositoryInterface */
private $budgetRepository;
/** @var CurrencyRepositoryInterface */
private $currencyRepos;
/**
* AccountController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->billRepository = app(BillRepositoryInterface::class);
$this->budgetRepository = app(BudgetRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->billRepository->setUser($user);
$this->currencyRepos->setUser($user);
$this->budgetRepository->setUser($user);
$this->accountRepository->setUser($user);
return $next($request);
}
);
}
/**
* @param Request $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function basic(Request $request): JsonResponse
{
// parameters for boxes:
$start = (string)$request->get('start');
$end = (string)$request->get('end');
if ('' === $start || '' === $end) {
throw new FireflyException('Start and end are mandatory parameters.');
}
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
// balance information:
$balanceData = $this->getBalanceInformation($start, $end);
$billData = $this->getBillInformation($start, $end);
$spentData = $this->getLeftToSpendInfo($start, $end);
$networthData = $this->getNetWorthInfo($start, $end);
$total = array_merge($balanceData, $billData, $spentData, $networthData);
// TODO: liabilities with icon line-chart
return response()->json($total);
}
/**
* Check if date is outside session range.
*
* @param Carbon $date
*
* @param Carbon $start
* @param Carbon $end
*
* @return bool
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
{
$result = false;
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
$result = true;
}
// start and end in the past? use $end
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
$result = true;
}
return $result;
}
/**
* This method will scroll through the results of the spentInPeriodMc() array and return the correct info.
*
* @param array $spentInfo
* @param TransactionCurrency $currency
*
* @return float
*/
private function findInSpentArray(array $spentInfo, TransactionCurrency $currency): float
{
foreach ($spentInfo as $array) {
if ($array['currency_id'] === $currency->id) {
return $array['amount'];
}
}
return 0.0;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getBalanceInformation(Carbon $start, Carbon $end): array
{
// prep some arrays:
$incomes = [];
$expenses = [];
$sums = [];
$return = [];
// collect income of user:
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)
->setTypes([TransactionType::DEPOSIT])
->withOpposingAccount();
$set = $collector->getTransactions();
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$currencyId = (int)$transaction->transaction_currency_id;
$incomes[$currencyId] = $incomes[$currencyId] ?? '0';
$incomes[$currencyId] = bcadd($incomes[$currencyId], $transaction->transaction_amount);
$sums[$currencyId] = $sums[$currencyId] ?? '0';
$sums[$currencyId] = bcadd($sums[$currencyId], $transaction->transaction_amount);
}
// collect expenses:
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)
->setTypes([TransactionType::WITHDRAWAL])
->withOpposingAccount();
$set = $collector->getTransactions();
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$currencyId = (int)$transaction->transaction_currency_id;
$expenses[$currencyId] = $expenses[$currencyId] ?? '0';
$expenses[$currencyId] = bcadd($expenses[$currencyId], $transaction->transaction_amount);
$sums[$currencyId] = $sums[$currencyId] ?? '0';
$sums[$currencyId] = bcadd($sums[$currencyId], $transaction->transaction_amount);
}
// format amounts:
$keys = array_keys($sums);
foreach ($keys as $currencyId) {
$currency = $this->currencyRepos->findNull($currencyId);
if (null === $currency) {
continue;
}
// create objects for big array.
$return[] = [
'key' => sprintf('balance-in-%s', $currency->code),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => round($sums[$currencyId] ?? 0, $currency->decimal_places),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false),
'local_icon' => 'balance-scale',
'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false) .
' + ' . app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => round($expenses[$currencyId] ?? 0, $currency->decimal_places),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false),
'local_icon' => 'balance-scale',
'sub_title' => '',
];
$return[] = [
'key' => sprintf('earned-in-%s', $currency->code),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => round($incomes[$currencyId] ?? 0, $currency->decimal_places),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
'local_icon' => 'balance-scale',
'sub_title' => '',
];
}
return $return;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getBillInformation(Carbon $start, Carbon $end): array
{
/*
* Since both this method and the chart use the exact same data, we can suffice
* with calling the one method in the bill repository that will get this amount.
*/
$paidAmount = $this->billRepository->getBillsPaidInRangePerCurrency($start, $end);
$unpaidAmount = $this->billRepository->getBillsUnpaidInRangePerCurrency($start, $end);
$return = [];
foreach ($paidAmount as $currencyId => $amount) {
$amount = bcmul($amount, '-1');
$currency = $this->currencyRepos->findNull((int)$currencyId);
if (null === $currency) {
continue;
}
$return[] = [
'key' => sprintf('bills-paid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => round($amount, $currency->decimal_places),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $amount, false),
'local_icon' => 'check',
'sub_title' => '',
];
}
foreach ($unpaidAmount as $currencyId => $amount) {
$amount = bcmul($amount, '-1');
$currency = $this->currencyRepos->findNull((int)$currencyId);
if (null === $currency) {
continue;
}
$return[] = [
'key' => sprintf('bills-unpaid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => round($amount, $currency->decimal_places),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $amount, false),
'local_icon' => 'calendar-o',
'sub_title' => '',
];
}
return $return;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
{
$return = [];
$today = new Carbon;
$available = $this->budgetRepository->getAvailableBudgetWithCurrency($start, $end);
$budgets = $this->budgetRepository->getActiveBudgets();
$spentInfo = $this->budgetRepository->spentInPeriodMc($budgets, new Collection, $start, $end);
foreach ($available as $currencyId => $amount) {
$currency = $this->currencyRepos->findNull($currencyId);
if (null === $currency) {
continue;
}
$spentInCurrency = (string)$this->findInSpentArray($spentInfo, $currency);
$leftToSpend = bcadd($amount, $spentInCurrency);
$days = $today->diffInDays($end) + 1;
$perDay = '0';
if (0 !== $days && bccomp($leftToSpend, '0') > -1) {
$perDay = bcdiv($leftToSpend, (string)$days);
}
$return[] = [
'key' => sprintf('left-to-spend-in-%s', $currency->code),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => round($leftToSpend, $currency->decimal_places),
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $leftToSpend, false),
'local_icon' => 'money',
'sub_title' => (string)trans('firefly.box_spend_per_day', ['amount' => app('amount')->formatAnything($currency, $perDay, false)]),
];
}
return $return;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getNetWorthInfo(Carbon $start, Carbon $end): array
{
/** @var User $user */
$user = auth()->user();
$date = Carbon::now()->startOfDay();
// start and end in the future? use $end
if ($this->notInDateRange($date, $start, $end)) {
/** @var Carbon $date */
$date = session('end', Carbon::now()->endOfMonth());
}
/** @var NetWorthInterface $netWorthHelper */
$netWorthHelper = app(NetWorthInterface::class);
$netWorthHelper->setUser($user);
$allAccounts = $this->accountRepository->getActiveAccountsByType([AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE]);
// filter list on preference of being included.
$filtered = $allAccounts->filter(
function (Account $account) {
$includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth');
return null === $includeNetWorth ? true : '1' === $includeNetWorth;
}
);
$netWorthSet = $netWorthHelper->getNetWorthByCurrency($filtered, $date);
$return = [];
foreach ($netWorthSet as $index => $data) {
/** @var TransactionCurrency $currency */
$currency = $data['currency'];
$amount = round($data['balance'], $currency->decimal_places);
if (0.0 === $amount) {
continue;
}
// return stuff
$return[] = [
'key' => sprintf('net-worth-in-%s', $currency->code),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $amount,
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $data['balance'], false),
'local_icon' => 'line-chart',
'sub_title' => '',
];
}
return $return;
}
}

View File

@@ -0,0 +1,298 @@
<?php
/**
* TagController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon;
use FireflyIII\Api\V1\Requests\TagRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\TagTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class TagController
*/
class TagController extends Controller
{
use TransactionFilter;
/** @var TagRepositoryInterface The tag repository */
private $repository;
/**
* RuleController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(TagRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
}
);
}
/**
* @param Request $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function cloud(Request $request): JsonResponse
{
// parameters for cloud:
$start = (string)$request->get('start');
$end = (string)$request->get('end');
if ('' === $start || '' === $end) {
throw new FireflyException('Start and end are mandatory parameters.');
}
$start = Carbon::createFromFormat('Y-m-d', $start);
$end = Carbon::createFromFormat('Y-m-d', $end);
// get all tags:
$tags = $this->repository->get();
$min = null;
$max = 0;
$return = [
'tags' => [],
];
/** @var Tag $tag */
foreach ($tags as $tag) {
$earned = (float)$this->repository->earnedInPeriod($tag, $start, $end);
$spent = (float)$this->repository->spentInPeriod($tag, $start, $end);
$size = ($spent * -1) + $earned;
$min = $min ?? $size;
if ($size > 0) {
$max = $size > $max ? $size : $max;
$return['tags'][] = [
'tag' => $tag->tag,
'id' => $tag->id,
'size' => $size,
];
}
}
foreach ($return['tags'] as $index => $info) {
$return['tags'][$index]['relative'] = $return['tags'][$index]['size'] / $max;
}
$return['min'] = $min;
$return['max'] = $max;
return response()->json($return);
}
/**
* Delete the resource.
*
* @param Tag $tag
*
* @return JsonResponse
*/
public function delete(Tag $tag): JsonResponse
{
$this->repository->destroy($tag);
return response()->json([], 204);
}
/**
* List all of them.
*
* @param Request $request
*
* @return JsonResponse]
*/
public function index(Request $request): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of budgets. Count it and split it.
$collection = $this->repository->get();
$count = $collection->count();
$rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.tags.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TagTransformer $transformer */
$transformer = app(TagTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($rules, $transformer, 'tags');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* List single resource.
*
* @param Request $request
* @param Tag $tag
*
* @return JsonResponse
*/
public function show(Request $request, Tag $tag): JsonResponse
{
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TagTransformer $transformer */
$transformer = app(TagTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($tag, $transformer, 'tags');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Store new object.
*
* @param TagRequest $request
*
* @return JsonResponse
*/
public function store(TagRequest $request): JsonResponse
{
$rule = $this->repository->store($request->getAll());
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TagTransformer $transformer */
$transformer = app(TagTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($rule, $transformer, 'tags');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show all transactions.
*
* @param Request $request
* @param Tag $tag
*
* @return JsonResponse
*/
public function transactions(Request $request, Tag $tag): 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 = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setTag($tag);
if (\in_array(TransactionType::TRANSFER, $types, true)) {
$collector->removeFilter(InternalTransferFilter::class);
}
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection();
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
/**
* Update a rule.
*
* @param TagRequest $request
* @param Tag $tag
*
* @return JsonResponse
*/
public function update(TagRequest $request, Tag $tag): JsonResponse
{
$rule = $this->repository->update($tag, $request->getAll());
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
/** @var TagTransformer $transformer */
$transformer = app(TagTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($rule, $transformer, 'tags');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
}

View File

@@ -35,6 +35,9 @@ use FireflyIII\Helpers\Filter\PositiveAmountFilter;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\Transformers\PiggyBankEventTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@@ -51,6 +54,7 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class TransactionController extends Controller
{
use TransactionFilter;
/** @var JournalRepositoryInterface The journal repository */
private $repository;
@@ -75,6 +79,30 @@ class TransactionController extends Controller
);
}
/**
* @param Request $request
* @param Transaction $transaction
*
* @return JsonResponse
*/
public function attachments(Request $request, Transaction $transaction): JsonResponse
{
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$attachments = $this->repository->getAttachmentsByTr($transaction);
/** @var AttachmentTransformer $transformer */
$transformer = app(AttachmentTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($attachments, $transformer, 'attachments');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Remove the specified resource from storage.
*
@@ -103,7 +131,7 @@ class TransactionController extends Controller
$type = $request->get('type') ?? 'default';
$this->parameters->set('type', $type);
$types = $this->mapTypes($this->parameters->get('type'));
$types = $this->mapTransactionTypes($this->parameters->get('type'));
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
@@ -129,33 +157,54 @@ class TransactionController extends Controller
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection();
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions');
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::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');
}
/**
* @param Request $request
* @param Transaction $transaction
*
* @return JsonResponse
*/
public function piggyBankEvents(Request $request, Transaction $transaction): JsonResponse
{
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$events = $this->repository->getPiggyBankEventsbyTr($transaction);
/** @var PiggyBankEventTransformer $transformer */
$transformer = app(PiggyBankEventTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($events, $transformer, 'piggy_bank_events');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show a single transaction.
*
* @param Request $request
* @param Transaction $transaction
* @param string $include
*
* @return JsonResponse
*/
public function show(Request $request, Transaction $transaction, string $include = null): JsonResponse
public function show(Request $request, Transaction $transaction): JsonResponse
{
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// add include parameter:
$include = $include ?? '';
$include = $request->get('include') ?? $include;
$manager->parseIncludes($include);
// collect transactions using the journal collector
$collector = app(TransactionCollectorInterface::class);
$collector->setUser(auth()->user());
@@ -169,11 +218,14 @@ class TransactionController extends Controller
$collector->addFilter(PositiveAmountFilter::class);
}
if (!($transactionType === TransactionType::WITHDRAWAL)) {
$collector->addFilter(NegativeAmountFilter::class);
$collector->addFilter(NegativeAmountFilter::class); // @codeCoverageIgnore
}
$transactions = $collector->getTransactions();
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions');
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -194,16 +246,12 @@ class TransactionController extends Controller
$data['user'] = auth()->user()->id;
$journal = $repository->store($data);
event(new StoredTransactionJournal($journal, 0));
event(new StoredTransactionJournal($journal));
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
// collect transactions using the journal collector
$collector = app(TransactionCollectorInterface::class);
$collector->setUser(auth()->user());
@@ -221,7 +269,12 @@ class TransactionController extends Controller
}
$transactions = $collector->getTransactions();
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions');
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -247,10 +300,6 @@ class TransactionController extends Controller
event(new UpdatedTransactionJournal($journal));
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
// needs a lot of extra data to match the journal collector. Or just expand that one.
// collect transactions using the journal collector
$collector = app(TransactionCollectorInterface::class);
@@ -269,46 +318,14 @@ class TransactionController extends Controller
}
$transactions = $collector->getTransactions();
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions');
/** @var TransactionTransformer $transformer */
$transformer = app(TransactionTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($transactions, $transformer, 'transactions');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* All the types you can request.
*
* @param string $type
*
* @return array
*/
private function mapTypes(string $type): array
{
$types = [
'all' => [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE,
TransactionType::RECONCILIATION,],
'withdrawal' => [TransactionType::WITHDRAWAL,],
'withdrawals' => [TransactionType::WITHDRAWAL,],
'expense' => [TransactionType::WITHDRAWAL,],
'expenses' => [TransactionType::WITHDRAWAL,],
'income' => [TransactionType::DEPOSIT,],
'deposit' => [TransactionType::DEPOSIT,],
'deposits' => [TransactionType::DEPOSIT,],
'transfer' => [TransactionType::TRANSFER,],
'transfers' => [TransactionType::TRANSFER,],
'opening_balance' => [TransactionType::OPENING_BALANCE,],
'reconciliation' => [TransactionType::RECONCILIATION,],
'reconciliations' => [TransactionType::RECONCILIATION,],
'special' => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,],
'specials' => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,],
'default' => [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER,],
];
$return = $types['default'];
if (isset($types[$type])) {
$return = $types[$type];
}
return $return;
}
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* JournalLinkController.php
* TransactionLinkController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
@@ -23,12 +23,13 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\JournalLinkRequest;
use FireflyIII\Api\V1\Requests\TransactionLinkRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionJournalLink;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Transformers\JournalLinkTransformer;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\TransactionLinkTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -40,12 +41,12 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class JournalLinkController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* Class TransactionLinkController
*/
class JournalLinkController extends Controller
class TransactionLinkController extends Controller
{
use TransactionFilter;
/** @var JournalRepositoryInterface The journal repository */
private $journalRepository;
/** @var LinkTypeRepositoryInterface The link type repository */
@@ -105,21 +106,25 @@ class JournalLinkController extends Controller
// types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$linkType = $this->repository->findByName($name);
// get list of accounts. Count it and split it.
// get list of transaction links. Count it and split it.
$collection = $this->repository->getJournalLinks($linkType);
$count = $collection->count();
$journalLinks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($journalLinks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.journal_links.index') . $this->buildParams());
$paginator->setPath(route('api.v1.transaction_links.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($journalLinks, new JournalLinkTransformer($this->parameters), 'journal_links');
/** @var TransactionLinkTransformer $transformer */
$transformer = app(TransactionLinkTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($journalLinks, $transformer, 'transaction_links');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -137,14 +142,14 @@ class JournalLinkController extends Controller
public function show(Request $request, TransactionJournalLink $journalLink): JsonResponse
{
$manager = new Manager;
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'journal_links');
/** @var TransactionLinkTransformer $transformer */
$transformer = app(TransactionLinkTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($journalLink, $transformer, 'transaction_links');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -153,19 +158,14 @@ class JournalLinkController extends Controller
/**
* Store new object.
*
* @param JournalLinkRequest $request
* @param TransactionLinkRequest $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function store(JournalLinkRequest $request): JsonResponse
public function store(TransactionLinkRequest $request): JsonResponse
{
$manager = new Manager;
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$data = $request->getAll();
$inward = $this->journalRepository->findNull($data['inward_id'] ?? 0);
$outward = $this->journalRepository->findNull($data['outward_id'] ?? 0);
@@ -175,7 +175,12 @@ class JournalLinkController extends Controller
$data['direction'] = 'inward';
$journalLink = $this->repository->storeLink($data, $inward, $outward);
$resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'journal_links');
/** @var TransactionLinkTransformer $transformer */
$transformer = app(TransactionLinkTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($journalLink, $transformer, 'transaction_links');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -184,21 +189,15 @@ class JournalLinkController extends Controller
/**
* Update object.
*
* @param JournalLinkRequest $request
* @param TransactionLinkRequest $request
* @param TransactionJournalLink $journalLink
*
* @return JsonResponse
* @throws FireflyException
*/
public function update(JournalLinkRequest $request, TransactionJournalLink $journalLink): JsonResponse
public function update(TransactionLinkRequest $request, TransactionJournalLink $journalLink): JsonResponse
{
$manager = new Manager;
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$manager = new Manager;
$data = $request->getAll();
$data['inward'] = $this->journalRepository->findNull($data['inward_id'] ?? 0);
$data['outward'] = $this->journalRepository->findNull($data['outward_id'] ?? 0);
@@ -208,7 +207,11 @@ class JournalLinkController extends Controller
$data['direction'] = 'inward';
$journalLink = $this->repository->updateLink($journalLink, $data);
$resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'journal_links');
/** @var TransactionLinkTransformer $transformer */
$transformer = app(TransactionLinkTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($journalLink, $transformer, 'transaction_links');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -78,7 +78,7 @@ class UserController extends Controller
{
/** @var User $admin */
$admin = auth()->user();
if ($this->repository->hasRole($admin, 'owner')) {
if ($admin->id !== $user->id && $this->repository->hasRole($admin, 'owner')) {
$this->repository->destroy($user);
return response()->json([], 204);
@@ -113,7 +113,11 @@ class UserController extends Controller
$paginator->setPath(route('api.v1.users.index') . $this->buildParams());
// make resource
$resource = new FractalCollection($users, new UserTransformer($this->parameters), 'users');
/** @var UserTransformer $transformer */
$transformer = app(UserTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($users, $transformer, 'users');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
@@ -134,12 +138,12 @@ class UserController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
// make resource
$resource = new Item($user, new UserTransformer($this->parameters), 'users');
/** @var UserTransformer $transformer */
$transformer = app(UserTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($user, $transformer, 'users');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -161,12 +165,13 @@ class UserController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
// make resource
$resource = new Item($user, new UserTransformer($this->parameters), 'users');
/** @var UserTransformer $transformer */
$transformer = app(UserTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($user, $transformer, 'users');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
@@ -189,12 +194,12 @@ class UserController extends Controller
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
// make resource
$resource = new Item($user, new UserTransformer($this->parameters), 'users');
/** @var UserTransformer $transformer */
$transformer = app(UserTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($user, $transformer, 'users');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');

View File

@@ -23,6 +23,9 @@
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean;
/**
* Class AccountRequest
*/
@@ -47,10 +50,19 @@ class AccountRequest extends Request
*/
public function getAll(): array
{
$active = true;
$includeNetWorth = true;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
if (null !== $this->get('include_net_worth')) {
$includeNetWorth = $this->boolean('include_net_worth');
}
$data = [
'name' => $this->string('name'),
'active' => $this->boolean('active'),
'include_net_worth' => $this->boolean('include_net_worth'),
'active' => $active,
'include_net_worth' => $includeNetWorth,
'accountType' => $this->string('type'),
'account_type_id' => null,
'currency_id' => $this->integer('currency_id'),
@@ -62,18 +74,13 @@ class AccountRequest extends Request
'accountRole' => $this->string('account_role'),
'openingBalance' => $this->string('opening_balance'),
'openingBalanceDate' => $this->date('opening_balance_date'),
'ccType' => $this->string('cc_type'),
'ccMonthlyPaymentDate' => $this->string('cc_monthly_payment_date'),
'ccType' => $this->string('credit_card_type'),
'ccMonthlyPaymentDate' => $this->string('monthly_payment_date'),
'notes' => $this->string('notes'),
'interest' => $this->string('interest'),
'interest_period' => $this->string('interest_period'),
];
// new fields for liabilities
// 'liability_type' => $this->string('liability_type'),
// 'liability_start_date' => $this->date('liability_start_date'),
//];
if ('liability' === $data['accountType']) {
$data['openingBalance'] = bcmul($this->string('liability_amount'), '-1');
$data['openingBalanceDate'] = $this->date('liability_start_date');
@@ -95,29 +102,27 @@ class AccountRequest extends Request
$types = implode(',', array_keys(config('firefly.subTitlesByIdentifier')));
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
$rules = [
'name' => 'required|min:1|uniqueAccountForUser',
'opening_balance' => 'numeric|required_with:opening_balance_date|nullable',
'opening_balance_date' => 'date|required_with:opening_balance|nullable',
'iban' => 'iban|nullable',
'bic' => 'bic|nullable',
'virtual_balance' => 'numeric|nullable',
'currency_id' => 'numeric|exists:transaction_currencies,id|required_without:currency_code',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:currency_id',
'account_number' => 'between:1,255|nullable|uniqueAccountNumberForUser',
'account_role' => 'in:' . $accountRoles . '|required_if:type,asset',
'active' => 'required|boolean',
'include_net_worth' => 'required|boolean',
'cc_type' => 'in:' . $ccPaymentTypes . '|required_if:account_role,ccAsset',
'cc_monthly_payment_date' => 'date' . '|required_if:account_role,ccAsset|required_if:cc_type,monthlyFull',
'type' => 'required|in:' . $types,
'notes' => 'min:0|max:65536',
// required fields for liabilities:
'liability_type' => 'required_if:type,liability|in:loan,debt,mortgage,credit card',
'liability_amount' => 'required_if:type,liability|min:0|numeric',
'liability_start_date' => 'required_if:type,liability|date',
'interest' => 'required_if:type,liability|between:0,100|numeric',
'interest_period' => 'required_if:type,liability|in:daily,monthly,yearly',
'name' => 'required|min:1|uniqueAccountForUser',
'type' => 'required|in:' . $types,
'iban' => 'iban|nullable',
'bic' => 'bic|nullable',
'account_number' => 'between:1,255|nullable|uniqueAccountNumberForUser',
'opening_balance' => 'numeric|required_with:opening_balance_date|nullable',
'opening_balance_date' => 'date|required_with:opening_balance|nullable',
'virtual_balance' => 'numeric|nullable',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'active' => [new IsBoolean],
'include_net_worth' => [new IsBoolean],
'account_role' => 'in:' . $accountRoles . '|required_if:type,asset',
'credit_card_type' => 'in:' . $ccPaymentTypes . '|required_if:account_role,ccAsset',
'monthly_payment_date' => 'date' . '|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull',
'liability_type' => 'required_if:type,liability|in:loan,debt,mortgage',
'liability_amount' => 'required_if:type,liability|min:0|numeric',
'liability_start_date' => 'required_if:type,liability|date',
'interest' => 'required_if:type,liability|between:0,100|numeric',
'interest_period' => 'required_if:type,liability|in:daily,monthly,yearly',
'notes' => 'min:0|max:65536',
];
switch ($this->method()) {
default:

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\Bill;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Rules\IsValidAttachmentModel;
@@ -69,9 +70,10 @@ class AttachmentRequest extends Request
{
$models = implode(
',', [
Bill::class,
ImportJob::class,
TransactionJournal::class,
str_replace('FireflyIII\\Models\\', '', Bill::class),
str_replace('FireflyIII\\Models\\', '', ImportJob::class),
str_replace('FireflyIII\\Models\\', '', TransactionJournal::class),
str_replace('FireflyIII\\Models\\', '', Transaction::class),
]
);
$model = $this->string('model');

View File

@@ -50,8 +50,8 @@ class AvailableBudgetRequest extends Request
'currency_id' => $this->integer('currency_id'),
'currency_code' => $this->string('currency_code'),
'amount' => $this->string('amount'),
'start_date' => $this->date('start_date'),
'end_date' => $this->date('end_date'),
'start' => $this->date('start'),
'end' => $this->date('end'),
];
}
@@ -63,11 +63,11 @@ class AvailableBudgetRequest extends Request
public function rules(): array
{
$rules = [
'currency_id' => 'numeric|exists:transaction_currencies,id|required_without:currency_code',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:currency_id',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'amount' => 'required|numeric|more:0',
'start_date' => 'required|date|before:end_date',
'end_date' => 'required|date|after:start_date',
'start' => 'required|date|before:end',
'end' => 'required|date|after:start',
];
return $rules;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean;
use Illuminate\Validation\Validator;
/**
@@ -50,6 +51,11 @@ class BillRequest extends Request
*/
public function getAll(): array
{
$active = true;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
$data = [
'name' => $this->string('name'),
'amount_min' => $this->string('amount_min'),
@@ -59,8 +65,7 @@ class BillRequest extends Request
'date' => $this->date('date'),
'repeat_freq' => $this->string('repeat_freq'),
'skip' => $this->integer('skip'),
'automatch' => $this->boolean('automatch'),
'active' => $this->boolean('active'),
'active' => $active,
'notes' => $this->string('notes'),
];
@@ -78,13 +83,13 @@ class BillRequest extends Request
'name' => 'required|between:1,255|uniqueObjectForUser:bills,name',
'amount_min' => 'required|numeric|more:0',
'amount_max' => 'required|numeric|more:0',
'currency_id' => 'numeric|exists:transaction_currencies,id|required_without:currency_code',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:currency_id',
'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',
'skip' => 'required|between:0,31',
'automatch' => 'required|boolean',
'active' => 'required|boolean',
'skip' => 'between:0,31',
'automatch' => [new IsBoolean],
'active' => [new IsBoolean],
'notes' => 'between:1,65536',
];
switch ($this->method()) {

View File

@@ -48,10 +48,12 @@ class BudgetLimitRequest extends Request
public function getAll(): array
{
return [
'budget_id' => $this->integer('budget_id'),
'start_date' => $this->date('start_date'),
'end_date' => $this->date('end_date'),
'amount' => $this->string('amount'),
'budget_id' => $this->integer('budget_id'),
'start' => $this->date('start'),
'end' => $this->date('end'),
'amount' => $this->string('amount'),
'currency_id' => $this->integer('currency_id'),
'currency_code' => $this->string('currency_code'),
];
}
@@ -63,10 +65,12 @@ class BudgetLimitRequest extends Request
public function rules(): array
{
$rules = [
'budget_id' => 'required|exists:budgets,id|belongsToUser:budgets,id',
'start_date' => 'required|before:end_date|date',
'end_date' => 'required|after:start_date|date',
'amount' => 'required|more:0',
'budget_id' => 'required|exists:budgets,id|belongsToUser:budgets,id',
'start' => 'required|before:end|date',
'end' => 'required|after:start|date',
'amount' => 'required|more:0',
'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
];
switch ($this->method()) {
default:
@@ -76,6 +80,12 @@ class BudgetLimitRequest extends Request
$rules['budget_id'] = 'required|exists:budgets,id|belongsToUser:budgets,id';
break;
}
// if request has a budget already, drop the rule.
$budget = $this->route()->parameter('budget');
if (null !== $budget) {
unset($rules['budget_id']);
}
return $rules;
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\Budget;
use FireflyIII\Rules\IsBoolean;
/**
* Class BudgetRequest
@@ -48,9 +49,14 @@ class BudgetRequest extends Request
*/
public function getAll(): array
{
$active = true;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
return [
'name' => $this->string('name'),
'active' => $this->boolean('active'),
'active' => $active,
'order' => 0,
];
}
@@ -64,7 +70,7 @@ class BudgetRequest extends Request
{
$rules = [
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
'active' => 'required|boolean',
'active' => [new IsBoolean],
];
switch ($this->method()) {
default:

View File

@@ -49,8 +49,7 @@ class CategoryRequest extends Request
public function getAll(): array
{
return [
'name' => $this->string('name'),
'active' => $this->boolean('active'),
'name' => $this->string('name'),
];
}
@@ -62,8 +61,7 @@ class CategoryRequest extends Request
public function rules(): array
{
$rules = [
'name' => 'required|between:1,100|uniqueObjectForUser:categories,name',
'active' => 'required|boolean',
'name' => 'required|between:1,100|uniqueObjectForUser:categories,name',
];
switch ($this->method()) {
default:

View File

@@ -0,0 +1,83 @@
<?php
/**
* ConfigurationRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean;
/**
* Class ConfigurationRequest
*/
class ConfigurationRequest 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
{
$name = $this->route()->parameter('configName');
switch ($name) {
case 'is_demo_site':
case 'single_user_mode':
return ['value' => $this->boolean('value')];
case 'permission_update_check':
return ['value' => $this->integer('value')];
}
return ['value' => $this->string('value')]; // @codeCoverageIgnore
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$name = $this->route()->parameter('configName');
switch ($name) {
case 'is_demo_site':
case 'single_user_mode':
return ['value' => ['required', new IsBoolean]];
case 'permission_update_check':
return ['value' => 'required|numeric|between:-1,1'];
}
return ['value' => 'required']; // @codeCoverageIgnore
}
}

View File

@@ -23,6 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean;
/**
* Class CurrencyRequest
@@ -47,12 +49,22 @@ class CurrencyRequest extends Request
*/
public function getAll(): array
{
$enabled = true;
$default = false;
if (null !== $this->get('enabled')) {
$enabled = $this->boolean('enabled');
}
if (null !== $this->get('default')) {
$default = $this->boolean('default');
}
return [
'name' => $this->string('name'),
'code' => $this->string('code'),
'symbol' => $this->string('symbol'),
'decimal_places' => $this->integer('decimal_places'),
'default' => $this->boolean('default'),
'default' => $default,
'enabled' => $enabled,
];
}
@@ -67,8 +79,10 @@ class CurrencyRequest extends Request
'name' => 'required|between:1,255|unique:transaction_currencies,name',
'code' => 'required|between:3,3|unique:transaction_currencies,code',
'symbol' => 'required|between:1,5|unique:transaction_currencies,symbol',
'decimal_places' => 'required|between:0,20|numeric|min:0|max:20',
'default' => 'boolean',
'decimal_places' => 'between:0,20|numeric|min:0|max:20',
'enabled' => [new IsBoolean()],
'default' => [new IsBoolean()],
];
switch ($this->method()) {
@@ -76,7 +90,7 @@ class CurrencyRequest extends Request
break;
case 'PUT':
case 'PATCH':
$currency = $this->route()->parameter('currency');
$currency = $this->route()->parameter('currency_code');
$rules['name'] = 'required|between:1,255|unique:transaction_currencies,name,' . $currency->id;
$rules['code'] = 'required|between:1,255|unique:transaction_currencies,code,' . $currency->id;
$rules['symbol'] = 'required|between:1,255|unique:transaction_currencies,symbol,' . $currency->id;

View File

@@ -58,8 +58,8 @@ class PiggyBankRequest extends Request
'account_id' => $this->integer('account_id'),
'targetamount' => $this->string('target_amount'),
'current_amount' => $current,
'startdate' => $this->date('start_date'),
'targetdate' => $this->date('target_date'),
'startdate' => $this->date('start_date'),
'targetdate' => $this->date('target_date'),
'notes' => $this->string('notes'),
];
}

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Validation\RecurrenceValidation;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
@@ -54,6 +55,14 @@ class RecurrenceRequest extends Request
*/
public function getAll(): array
{
$active = true;
$applyRules = true;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
if (null !== $this->get('apply_rules')) {
$applyRules = $this->boolean('apply_rules');
}
$return = [
'recurrence' => [
'type' => $this->string('type'),
@@ -62,8 +71,8 @@ class RecurrenceRequest extends Request
'first_date' => $this->date('first_date'),
'repeat_until' => $this->date('repeat_until'),
'repetitions' => $this->integer('nr_of_repetitions'),
'apply_rules' => $this->boolean('apply_rules'),
'active' => $this->boolean('active'),
'apply_rules' => $applyRules,
'active' => $active,
],
'meta' => [
'piggy_bank_id' => $this->integer('piggy_bank_id'),
@@ -91,19 +100,21 @@ class RecurrenceRequest extends Request
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
'description' => 'between:1,65000',
'first_date' => sprintf('required|date|after:%s', $today->format('Y-m-d')),
'apply_rules' => [new IsBoolean],
'active' => [new IsBoolean],
'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')),
'nr_of_repetitions' => 'numeric|between:1,31',
'apply_rules' => 'required|boolean',
'active' => 'required|boolean',
'tags' => 'between:1,64000',
'piggy_bank_id' => 'numeric',
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',
'repetitions.*.moment' => 'between:0,10',
'repetitions.*.skip' => 'required|numeric|between:0,31',
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|required_without:transactions.*.currency_code',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:transactions.*.currency_id',
'transactions.*.description' => 'required|between:1,255',
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.foreign_amount' => 'numeric|more: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',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
@@ -112,8 +123,8 @@ class RecurrenceRequest extends Request
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.destination_name' => 'between:1,255|nullable',
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.description' => 'required|between:1,255',
];
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Rules\IsBoolean;
/**
@@ -50,10 +51,16 @@ class RuleGroupRequest extends Request
*/
public function getAll(): array
{
$active = true;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
return [
'title' => $this->string('title'),
'description' => $this->string('description'),
'active' => $this->boolean('active'),
'active' => $active,
];
}
@@ -67,7 +74,7 @@ class RuleGroupRequest extends Request
$rules = [
'title' => 'required|between:1,100|uniqueObjectForUser:rule_groups,title',
'description' => 'between:1,5000|nullable',
'active' => 'required|boolean',
'active' => [new IsBoolean],
];
switch ($this->method()) {
default:

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean;
use Illuminate\Validation\Validator;
@@ -49,17 +50,30 @@ class RuleRequest extends Request
*/
public function getAll(): array
{
$strict = true;
$active = true;
$stopProcessing = false;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
if (null !== $this->get('strict')) {
$strict = $this->boolean('strict');
}
if (null !== $this->get('stop_processing')) {
$stopProcessing = $this->boolean('stop_processing');
}
$data = [
'title' => $this->string('title'),
'description' => $this->string('description'),
'rule_group_id' => $this->integer('rule_group_id'),
'rule_group_title' => $this->string('rule_group_title'),
'trigger' => $this->string('trigger'),
'strict' => $this->boolean('strict'),
'stop_processing' => $this->boolean('stop_processing'),
'active' => $this->boolean('active'),
'rule_triggers' => $this->getRuleTriggers(),
'rule_actions' => $this->getRuleActions(),
'strict' => $strict,
'stop_processing' => $stopProcessing,
'active' => $active,
'triggers' => $this->getRuleTriggers(),
'actions' => $this->getRuleActions(),
];
return $data;
@@ -78,23 +92,23 @@ class RuleRequest extends Request
// some triggers and actions require text:
$contextTriggers = implode(',', config('firefly.context-rule-triggers'));
$contextActions = implode(',', config('firefly.context-rule-actions'));
$rules = [
'title' => 'required|between:1,100|uniqueObjectForUser:rules,title',
'description' => 'between:1,5000|nullable',
'rule_group_id' => 'required|belongsToUser:rule_groups|required_without:rule_group_title',
'rule_group_title' => 'nullable|between:1,255|required_without:rule_group_id|belongsToUser:rule_groups,title',
'trigger' => 'required|in:store-journal,update-journal',
'rule_triggers.*.name' => 'required|in:' . implode(',', $validTriggers),
'rule_triggers.*.stop_processing' => 'boolean',
'rule_triggers.*.value' => 'required_if:rule_actions.*.type,' . $contextTriggers . '|min:1|ruleTriggerValue',
'rule_actions.*.name' => 'required|in:' . implode(',', $validActions),
'rule_actions.*.value' => 'required_if:rule_actions.*.type,' . $contextActions . '|ruleActionValue',
'rule_actions.*.stop_processing' => 'boolean',
'strict' => 'required|boolean',
'stop_processing' => 'required|boolean',
'active' => 'required|boolean',
$rules = [
'title' => 'required|between:1,100|uniqueObjectForUser:rules,title',
'description' => 'between:1,5000|nullable',
'rule_group_id' => 'required|belongsToUser:rule_groups|required_without:rule_group_title',
'rule_group_title' => 'nullable|between:1,255|required_without:rule_group_id|belongsToUser:rule_groups,title',
'trigger' => 'required|in:store-journal,update-journal',
'triggers.*.type' => 'required|in:' . implode(',', $validTriggers),
'triggers.*.value' => 'required_if:actions.*.type,' . $contextTriggers . '|min:1|ruleTriggerValue',
'triggers.*.stop_processing' => [new IsBoolean],
'triggers.*.active' => [new IsBoolean],
'actions.*.type' => 'required|in:' . implode(',', $validActions),
'actions.*.value' => 'required_if:actions.*.type,' . $contextActions . '|ruleActionValue',
'actions.*.stop_processing' => [new IsBoolean],
'actions.*.active' => [new IsBoolean],
'strict' => [new IsBoolean],
'stop_processing' => [new IsBoolean],
'active' => [new IsBoolean],
];
return $rules;
@@ -124,10 +138,10 @@ class RuleRequest extends Request
*/
protected function atLeastOneAction(Validator $validator): void
{
$data = $validator->getData();
$repetitions = $data['rule_actions'] ?? [];
// need at least one transaction
if (0 === \count($repetitions)) {
$data = $validator->getData();
$actions = $data['actions'] ?? [];
// need at least one trigger
if (0 === \count($actions)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_action'));
}
}
@@ -139,10 +153,10 @@ class RuleRequest extends Request
*/
protected function atLeastOneTrigger(Validator $validator): void
{
$data = $validator->getData();
$repetitions = $data['rule_triggers'] ?? [];
// need at least one transaction
if (0 === \count($repetitions)) {
$data = $validator->getData();
$triggers = $data['triggers'] ?? [];
// need at least one trugger
if (0 === \count($triggers)) {
$validator->errors()->add('title', (string)trans('validation.at_least_one_trigger'));
}
}
@@ -152,14 +166,15 @@ class RuleRequest extends Request
*/
private function getRuleActions(): array
{
$actions = $this->get('rule_actions');
$actions = $this->get('actions');
$return = [];
if (\is_array($actions)) {
foreach ($actions as $action) {
$return[] = [
'name' => $action['name'],
'type' => $action['type'],
'value' => $action['value'],
'stop_processing' => 1 === (int)($action['stop-processing'] ?? '0'),
'active' => $this->convertBoolean((string)($action['active'] ?? 'false')),
'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')),
];
}
}
@@ -172,14 +187,15 @@ class RuleRequest extends Request
*/
private function getRuleTriggers(): array
{
$triggers = $this->get('rule_triggers');
$triggers = $this->get('triggers');
$return = [];
if (\is_array($triggers)) {
foreach ($triggers as $trigger) {
$return[] = [
'name' => $trigger['name'],
'type' => $trigger['type'],
'value' => $trigger['value'],
'stop_processing' => 1 === (int)($trigger['stop-processing'] ?? '0'),
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'false')),
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
];
}
}

View File

@@ -0,0 +1,93 @@
<?php
/**
* TagRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\Tag;
/**
* Class TagRequest
*/
class TagRequest 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
{
$data = [
'tag' => $this->string('tag'),
'date' => $this->date('date'),
'description' => $this->string('description'),
'latitude' => '' === $this->string('latitude') ? null : $this->string('latitude'),
'longitude' => '' === $this->string('longitude') ? null : $this->string('longitude'),
'zoom_level' => $this->integer('zoom_level'),
];
return $data;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
$rules = [
'tag' => 'required|min:1|uniqueObjectForUser:tags,tag',
'description' => 'min:1|nullable',
'date' => 'date|nullable',
'latitude' => 'numeric|min:-90|max:90|nullable|required_with:longitude',
'longitude' => 'numeric|min:-180|max:180|nullable|required_with:latitude',
'zoom_level' => 'numeric|min:0|max:80|nullable',
];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
/** @var Tag $tag */
$tag = $this->route()->parameter('tagOrId');
$rules['tag'] = 'required|min:1|uniqueObjectForUser:tags,tag,' . $tag->id;
break;
}
return $rules;
}
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* JournalLinkRequest.php
* TransactionLinkRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
@@ -25,13 +25,14 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\User;
use Illuminate\Validation\Validator;
/**
*
* Class JournalLinkRequest
* Class TransactionLinkRequest
*/
class JournalLinkRequest extends Request
class TransactionLinkRequest extends Request
{
/**
* Authorize logged in users.
@@ -71,8 +72,8 @@ class JournalLinkRequest extends Request
return [
'link_type_id' => 'exists:link_types,id|required_without:link_type_name',
'link_type_name' => 'exists:link_types,name|required_without:link_type_id',
'inward_id' => 'required|belongsToUser:transaction_journals,id',
'outward_id' => 'required|belongsToUser:transaction_journals,id',
'inward_id' => 'required|belongsToUser:transaction_journals,id|different:outward_id',
'outward_id' => 'required|belongsToUser:transaction_journals,id|different:inward_id',
'notes' => 'between:0,65000',
];
}
@@ -98,13 +99,15 @@ class JournalLinkRequest extends Request
*/
private function validateExistingLink(Validator $validator): void
{
/** @var User $user */
$user = auth()->user();
/** @var LinkTypeRepositoryInterface $repository */
$repository = app(LinkTypeRepositoryInterface::class);
$repository->setUser(auth()->user());
$repository->setUser($user);
/** @var JournalRepositoryInterface $journalRepos */
$journalRepos = app(JournalRepositoryInterface::class);
$journalRepos->setUser(auth()->user());
$journalRepos->setUser($user);
$data = $validator->getData();
$inwardId = (int)($data['inward_id'] ?? 0);
@@ -124,8 +127,12 @@ class JournalLinkRequest extends Request
}
if ($repository->findLink($inward, $outward)) {
$validator->errors()->add('outward_id', 'Already have a link between inward and outward.');
$validator->errors()->add('inward_id', 'Already have a link between inward and outward.');
// only if not updating:
$link = $this->route()->parameter('journalLink');
if (null === $link) {
$validator->errors()->add('outward_id', 'Already have a link between inward and outward.');
$validator->errors()->add('inward_id', 'Already have a link between inward and outward.');
}
}
}
}

View File

@@ -25,6 +25,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Rules\IsDateOrTime;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
@@ -58,13 +60,22 @@ class TransactionRequest extends Request
{
$data = [
'type' => $this->string('type'),
'date' => $this->date('date'),
'date' => $this->dateTime('date'),
'description' => $this->string('description'),
'piggy_bank_id' => $this->integer('piggy_bank_id'),
'piggy_bank_name' => $this->string('piggy_bank_name'),
'bill_id' => $this->integer('bill_id'),
'bill_name' => $this->string('bill_name'),
'tags' => explode(',', $this->string('tags')),
'notes' => $this->string('notes'),
'sepa-cc' => $this->string('sepa_cc'),
'sepa-ct-op' => $this->string('sepa_ct_op'),
'sepa-ct-id' => $this->string('sepa_ct_id'),
'sepa-db' => $this->string('sepa_db'),
'sepa-country' => $this->string('sepa_country'),
'sepa-ep' => $this->string('sepa_ep'),
'sepa-ci' => $this->string('sepa_ci'),
'sepa-batch-id' => $this->string('sepa_batch_id'),
'interest_date' => $this->date('interest_date'),
'book_date' => $this->date('book_date'),
'process_date' => $this->date('process_date'),
@@ -72,8 +83,9 @@ class TransactionRequest extends Request
'payment_date' => $this->date('payment_date'),
'invoice_date' => $this->date('invoice_date'),
'internal_reference' => $this->string('internal_reference'),
'notes' => $this->string('notes'),
'original-source' => sprintf('api-v%s', config('firefly.api_version')),
'bunq_payment_id' => $this->string('bunq_payment_id'),
'external_id' => $this->string('external_id'),
'original-source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'transactions' => $this->getTransactionData(),
];
@@ -90,9 +102,9 @@ class TransactionRequest extends Request
{
$rules = [
// basic fields for journal:
'type' => 'required|in:withdrawal,deposit,transfer',
'date' => 'required|date',
'type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
'description' => 'between:1,255',
'date' => ['required', new IsDateOrTime],
'piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUser],
'piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser],
'bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
@@ -100,6 +112,19 @@ class TransactionRequest extends Request
'tags' => 'between:1,255',
// then, custom fields for journal
'notes' => 'min:1,max:50000|nullable',
// SEPA fields:
'sepa_cc' => 'min:1,max:255|nullable',
'sepa_ct_op' => 'min:1,max:255|nullable',
'sepa_ct_id' => 'min:1,max:255|nullable',
'sepa_db' => 'min:1,max:255|nullable',
'sepa_country' => 'min:1,max:255|nullable',
'sepa_ep' => 'min:1,max:255|nullable',
'sepa_ci' => 'min:1,max:255|nullable',
'sepa_batch_id' => 'min:1,max:255|nullable',
// dates
'interest_date' => 'date|nullable',
'book_date' => 'date|nullable',
'process_date' => 'date|nullable',
@@ -107,13 +132,14 @@ class TransactionRequest extends Request
'payment_date' => 'date|nullable',
'invoice_date' => 'date|nullable',
'internal_reference' => 'min:1,max:255|nullable',
'notes' => 'min:1,max:50000|nullable',
'bunq_payment_id' => 'min:1,max:255|nullable',
'external_id' => 'min:1,max:255|nullable',
// transaction rules (in array for splits):
'transactions.*.description' => 'nullable|between:1,255',
'transactions.*.amount' => 'required|numeric|more:0',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|required_without:transactions.*.currency_code',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:transactions.*.currency_id',
'transactions.*.description' => 'nullable|between:1,255',
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
'transactions.*.foreign_amount' => 'numeric|more:0',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
@@ -121,8 +147,7 @@ class TransactionRequest extends Request
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
'transactions.*.category_name' => 'between:1,255|nullable',
'transactions.*.reconciled' => 'boolean|nullable',
// basic rules will be expanded later.
'transactions.*.reconciled' => [new IsBoolean],
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
@@ -172,8 +197,8 @@ class TransactionRequest extends Request
$return = [];
foreach ($this->get('transactions') as $index => $transaction) {
$return[] = [
'description' => $transaction['description'] ?? null,
'amount' => $transaction['amount'],
'description' => $transaction['description'] ?? null,
'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
'currency_code' => $transaction['currency_code'] ?? null,
'foreign_amount' => $transaction['foreign_amount'] ?? null,
@@ -187,7 +212,7 @@ class TransactionRequest extends Request
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
'reconciled' => $transaction['reconciled'] ?? false,
'reconciled' => $this->convertBoolean((string)($transaction['reconciled'] ?? 'false')),
'identifier' => $index,
];
}

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\User;
@@ -64,10 +65,15 @@ class UserRequest extends Request
*/
public function getAll(): array
{
$blocked = false;
if (null === $this->get('blocked')) {
$blocked = $this->boolean('blocked');
}
$data = [
'email' => $this->string('email'),
'blocked' => $this->boolean('blocked'),
'blocked' => $blocked,
'blocked_code' => $this->string('blocked_code'),
'role' => $this->string('role'),
];
return $data;
@@ -82,8 +88,9 @@ class UserRequest extends Request
{
$rules = [
'email' => 'required|email|unique:users,email,',
'blocked' => 'required|boolean',
'blocked' => [new IsBoolean],
'blocked_code' => 'in:email_changed',
'role' => 'in:owner,demo',
];
switch ($this->method()) {
default:

View File

@@ -1,5 +1,27 @@
<?php
/**
* ApplyRules.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Carbon\Carbon;
@@ -19,6 +41,8 @@ use Illuminate\Support\Collection;
/**
*
* Class ApplyRules
*
* @codeCoverageIgnore
*/
class ApplyRules extends Command
{
@@ -161,6 +185,8 @@ class ApplyRules extends Command
private function applyRuleSelection(Collection $rules, Collection $transactions, bool $breakProcessing): void
{
$bar = $this->output->createProgressBar($rules->count() * $transactions->count());
/** @var Rule $rule */
foreach ($rules as $rule) {
/** @var Processor $processor */
$processor = app(Processor::class);
@@ -168,7 +194,7 @@ class ApplyRules extends Command
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
/** @var Rule $rule */
/** @noinspection DisconnectedForeachInstructionInspection */
$bar->advance();
$result = $processor->handleTransaction($transaction);
if (true === $result) {
@@ -187,6 +213,7 @@ class ApplyRules extends Command
/**
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function grabAllRules(): void
{
@@ -203,11 +230,12 @@ class ApplyRules extends Command
/**
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function parseDates(): void
{
// parse start date.
$startDate = Carbon::create()->startOfMonth();
$startDate = Carbon::now()->startOfMonth();
$startString = $this->option('start_date');
if (null === $startString) {
/** @var JournalRepositoryInterface $repository */

View File

@@ -134,8 +134,8 @@ class CreateExport extends Command
}
$processor->createZipFile();
$disk = Storage::disk('export');
$fileName = sprintf('export-%s.zip', date('Y-m-d_H-i-s'));
$disk = Storage::disk('export');
$fileName = sprintf('export-%s.zip', date('Y-m-d_H-i-s'));
$localPath = storage_path('export') . '/' . $job->key . '.zip';
// "move" from local to export disk

View File

@@ -97,7 +97,7 @@ class CreateImport extends Command
return 1;
}
if (\strlen($configuration) > 0) {
if ('' !== $configuration) {
$configurationData = json_decode(file_get_contents($configuration), true);
if (null === $configurationData) {
$this->errorLine(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
@@ -136,7 +136,7 @@ class CreateImport extends Command
}
// store file as attachment.
if (\strlen($file) > 0) {
if ('' !== $file) {
$messages = $jobRepository->storeCLIUpload($importJob, 'import_file', $file);
if ($messages->count() > 0) {
$this->errorLine($messages->first());

View File

@@ -1,5 +1,27 @@
<?php
/**
* Cron.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use FireflyIII\Exceptions\FireflyException;
@@ -26,16 +48,6 @@ class Cron extends Command
*/
protected $signature = 'firefly:cron';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*

View File

@@ -0,0 +1,159 @@
<?php
/**
* DecryptDatabase.php
* Copyright (c) 2019 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Crypt;
use DB;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Preference;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException;
use Log;
/**
*
* Class DecryptDatabase
*/
class DecryptDatabase extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Decrypts the database.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:decrypt-all';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->line('Going to decrypt the database.');
$tables = [
'accounts' => ['name', 'iban'],
'attachments' => ['filename', 'mime', 'title', 'description'],
'bills' => ['name', 'match'],
'budgets' => ['name'],
'categories' => ['name'],
'piggy_banks' => ['name'],
'preferences' => ['data'],
'tags' => ['tag', 'description'],
'transaction_journals' => ['description'],
'transactions' => ['description'],
'journal_links' => ['comment'],
];
foreach ($tables as $table => $fields) {
if ($this->isDecrypted($table)) {
$this->info(sprintf('No decryption required for table "%s".', $table));
continue;
}
foreach ($fields as $field) {
$rows = DB::table($table)->get(['id', $field]);
foreach ($rows as $row) {
$original = $row->$field;
if (null === $original) {
continue;
}
$id = $row->id;
$value = $this->tryDecrypt($original);
// A separate routine for preferences:
if ('preferences' === $table) {
// try to json_decrypt the value.
$value = json_decode($value, true) ?? $value;
Log::debug(sprintf('Decrypted field "%s" "%s" to "%s" in table "%s" (row #%d)', $field, $original, print_r($value, true), $table, $id));
/** @var Preference $object */
$object = Preference::find((int)$id);
if (null !== $object) {
$object->data = $value;
$object->save();
}
continue;
}
if ($value !== $original) {
Log::debug(sprintf('Decrypted field "%s" "%s" to "%s" in table "%s" (row #%d)', $field, $original, $value, $table, $id));
DB::table($table)->where('id', $id)->update([$field => $value]);
}
}
}
$this->line(sprintf('Decrypted the data in table "%s".', $table));
// mark as decrypted:
$configName = sprintf('is_decrypted_%s', $table);
FireflyConfig::set($configName, true);
}
$this->info('Done!');
return 0;
}
/**
* @param string $table
*
* @return bool
*/
private function isDecrypted(string $table): bool
{
$configName = sprintf('is_decrypted_%s', $table);
$configVar = FireflyConfig::get($configName, false);
if (null !== $configVar) {
return (bool)$configVar->data;
}
return false;
}
/**
* @param $value
*
* @return mixed
*/
private function tryDecrypt($value)
{
try {
$value = Crypt::decrypt($value);
} catch (DecryptException $e) {
if ('The MAC is invalid.' === $e->getMessage()) {
throw new FireflyException($e->getMessage());
}
Log::debug(sprintf('Could not decrypt. %s', $e->getMessage()));
}
return $value;
}
}

View File

@@ -28,6 +28,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Crypt;
use DB;
use Exception;
use FireflyIII\Models\Account;
@@ -35,6 +36,7 @@ use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Note;
use FireflyIII\Models\Preference;
@@ -52,6 +54,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\User;
use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Database\QueryException;
use Illuminate\Support\Collection;
use Log;
@@ -105,6 +108,22 @@ class UpgradeDatabase extends Command
return 0;
}
/**
* @param string $value
*
* @return string
*/
private function tryDecrypt(string $value): string
{
try {
$value = Crypt::decrypt($value);
} catch (DecryptException $e) {
Log::debug(sprintf('Could not decrypt. %s', $e->getMessage()));
}
return $value;
}
/**
* Since it is one routine these warnings make sense and should be supressed.
*
@@ -126,11 +145,17 @@ class UpgradeDatabase extends Command
return;
}
$currencyCode = $this->tryDecrypt($currencyPreference->data);
$currency = TransactionCurrency::where('code', $currencyPreference->data)->first();
// try json decrypt just in case.
if (\strlen($currencyCode) > 3) {
$currencyCode = json_decode($currencyCode) ?? 'EUR';
}
$currency = TransactionCurrency::where('code', $currencyCode)->first();
if (null === $currency) {
$this->line('Fall back to default currency in migrateBillsToRules().');
$currency = app('amount')->getDefaultCurrency();
$currency = app('amount')->getDefaultCurrencyByUser($user);
}
if (null === $ruleGroup) {
@@ -267,11 +292,12 @@ class UpgradeDatabase extends Command
if (!Schema::hasTable('transaction_journals')) {
return;
}
$subQuery = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->groupBy(['transaction_journals.id'])
->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]);
$subQuery = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->groupBy(['transaction_journals.id'])
->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]);
/** @noinspection PhpStrictTypeCheckingInspection */
$result = DB::table(DB::raw('(' . $subQuery->toSql() . ') AS derived'))
->mergeBindings($subQuery->getQuery())
->where('t_count', '>', 2)
@@ -292,23 +318,38 @@ class UpgradeDatabase extends Command
*/
public function updateAccountCurrencies(): void
{
Log::debug('Now in updateAccountCurrencies()');
$defaultConfig = (string)config('firefly.default_currency', 'EUR');
Log::debug(sprintf('System default currency is "%s"', $defaultConfig));
$accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']);
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$accounts->each(
function (Account $account) use ($repository) {
function (Account $account) use ($repository, $defaultConfig) {
$repository->setUser($account->user);
// get users preference, fall back to system pref.
$defaultCurrencyCode = app('preferences')->getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
$accountCurrency = (int)$repository->getMetaValue($account, 'currency_id');
$openingBalance = $account->getOpeningBalance();
$obCurrency = (int)$openingBalance->transaction_currency_id;
// expand and debug routine.
$defaultCurrencyCode = app('preferences')->getForUser($account->user, 'currencyPreference', $defaultConfig)->data;
Log::debug(sprintf('Default currency code is "%s"', var_export($defaultCurrencyCode, true)));
if (!is_string($defaultCurrencyCode)) {
$defaultCurrencyCode = $defaultConfig;
Log::debug(sprintf('Default currency code is not a string, now set to "%s"', $defaultCurrencyCode));
}
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
$accountCurrency = (int)$repository->getMetaValue($account, 'currency_id');
$openingBalance = $account->getOpeningBalance();
$obCurrency = (int)$openingBalance->transaction_currency_id;
if (null === $defaultCurrency) {
throw new UnexpectedValueException('The default currency is NULL, and this is more or less impossible.');
throw new UnexpectedValueException(sprintf('User has a preference for "%s", but this currency does not exist.', $defaultCurrencyCode));
}
Log::debug(
sprintf('Found default currency #%d (%s) while searching for "%s"', $defaultCurrency->id, $defaultCurrency->code, $defaultCurrencyCode)
);
// both 0? set to default currency:
if (0 === $accountCurrency && 0 === $obCurrency) {
@@ -448,11 +489,12 @@ class UpgradeDatabase extends Command
/** @var BudgetLimit $budgetLimit */
foreach ($budgetLimits as $budgetLimit) {
if (null === $budgetLimit->transaction_currency_id) {
/** @var Budget $budget */
$budget = $budgetLimit->budget;
if (null !== $budget) {
$user = $budget->user;
if (null !== $user) {
$currency = \Amount::getDefaultCurrencyByUser($user);
$currency = app('amount')->getDefaultCurrencyByUser($user);
$budgetLimit->transaction_currency_id = $currency->id;
$budgetLimit->save();
$this->line(
@@ -464,6 +506,9 @@ class UpgradeDatabase extends Command
}
}
/**
*
*/
private function createNewTypes(): void
{
// create transaction type "Reconciliation".
@@ -490,7 +535,7 @@ class UpgradeDatabase extends Command
// move description:
$description = (string)$att->description;
if (\strlen($description) > 0) {
if ('' !== $description) {
// find or create note:
$note = $att->notes()->first();
if (null === $note) {
@@ -541,19 +586,19 @@ class UpgradeDatabase extends Command
*/
private function removeCCLiabilities(): void
{
$ccType = AccountType::where('type', AccountType::CREDITCARD)->first();
$debtType =AccountType::where('type', AccountType::DEBT)->first();
if(null === $ccType || null === $debtType) {
$ccType = AccountType::where('type', AccountType::CREDITCARD)->first();
$debtType = AccountType::where('type', AccountType::DEBT)->first();
if (null === $ccType || null === $debtType) {
return;
}
/** @var Collection $accounts */
$accounts = Account::where('account_type_id', $ccType->id)->get();
foreach($accounts as $account) {
foreach ($accounts as $account) {
$account->account_type_id = $debtType->id;
$account->save();
$this->line(sprintf('Converted credit card liability account "%s" (#%d) to generic debt liability.', $account->name, $account->id));
}
if($accounts->count() > 0) {
if ($accounts->count() > 0) {
$this->info('Credit card liability types are no longer supported and have been converted to generic debts. See: http://bit.ly/FF3-credit-cards');
}
}

View File

@@ -1,94 +0,0 @@
<?php
/**
* UseEncryption.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
/**
* Class UseEncryption.
* @codeCoverageIgnore
*/
class UseEncryption extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command will make sure that entries in the database will be encrypted (or not) according to the settings in .env';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:use-encryption';
/**
* Execute the console command.
*/
public function handle(): int
{
if (true === config('firefly.encryption')) {
$this->info('Firefly III configuration calls for encrypted data.');
}
if (false === config('firefly.encryption')) {
$this->info('Firefly III configuration calls for unencrypted data.');
}
$this->handleObjects('Account', 'name', 'encrypted');
$this->handleObjects('Bill', 'name', 'name_encrypted');
$this->handleObjects('Bill', 'match', 'match_encrypted');
$this->handleObjects('Budget', 'name', 'encrypted');
$this->handleObjects('Category', 'name', 'encrypted');
$this->handleObjects('PiggyBank', 'name', 'encrypted');
$this->handleObjects('TransactionJournal', 'description', 'encrypted');
return 0;
}
/**
* Run each object and encrypt them (or not).
*
* @param string $class
* @param string $field
* @param string $indicator
*/
public function handleObjects(string $class, string $field, string $indicator): void
{
$fqn = sprintf('FireflyIII\Models\%s', $class);
$encrypt = true === config('firefly.encryption') ? 0 : 1;
/** @noinspection PhpUndefinedMethodInspection */
$set = $fqn::where($indicator, $encrypt)->withTrashed()->get();
foreach ($set as $entry) {
$newName = $entry->$field;
$entry->$field = $newName;
/** @noinspection PhpUndefinedMethodInspection */
$entry->save();
}
/** @noinspection PhpUndefinedMethodInspection */
$this->line(sprintf('Updated %d %s.', $set->count(), strtolower(Str::plural($class))));
}
}

View File

@@ -25,22 +25,25 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Crypt;
use DB;
use Exception;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Category;
use FireflyIII\Models\LinkType;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Log;
use Schema;
use stdClass;
@@ -95,6 +98,8 @@ class VerifyDatabase extends Command
$this->fixDoubleAmounts();
$this->fixBadMeta();
$this->removeBills();
$this->enableCurrencies();
$this->reportZeroAmount();
return 0;
}
@@ -150,6 +155,45 @@ class VerifyDatabase extends Command
}
}
/**
* Will make sure that all currencies in use are actually enabled.
*/
private function enableCurrencies(): void
{
$found = [];
// get all meta entries
/** @var Collection $meta */
$meta = AccountMeta::where('name', 'currency_id')->groupBy('data')->get(['data']);
foreach ($meta as $entry) {
$found[] = (int)$entry->data;
}
// get all from journals:
/** @var Collection $journals */
$journals = TransactionJournal::groupBy('transaction_currency_id')->get(['transaction_currency_id']);
foreach ($journals as $entry) {
$found[] = (int)$entry->transaction_currency_id;
}
// get all from transactions
/** @var Collection $transactions */
$transactions = Transaction::groupBy('transaction_currency_id')->get(['transaction_currency_id']);
foreach ($transactions as $entry) {
$found[] = (int)$entry->transaction_currency_id;
}
// get all from budget limits
/** @var Collection $limits */
$limits = BudgetLimit::groupBy('transaction_currency_id')->get(['transaction_currency_id']);
foreach ($limits as $entry) {
$found[] = (int)$entry->transaction_currency_id;
}
$found = array_unique($found);
TransactionCurrency::whereIn('id', $found)->update(['enabled' => true]);
}
/**
* Fix the situation where the matching transactions of a journal somehow have non-matching categories or budgets.
*
@@ -406,12 +450,6 @@ class VerifyDatabase extends Command
/** @var stdClass $entry */
foreach ($set as $entry) {
$objName = $entry->name;
try {
$objName = Crypt::decrypt($objName);
} catch (DecryptException $e) {
// it probably was not encrypted.
Log::debug(sprintf('Not a problem: %s', $e->getMessage()));
}
// also count the transactions:
$countTransactions = DB::table('budget_transaction')->where('budget_id', $entry->id)->count();
@@ -444,12 +482,6 @@ class VerifyDatabase extends Command
/** @var stdClass $entry */
foreach ($set as $entry) {
$objName = $entry->name;
try {
$objName = Crypt::decrypt($objName);
} catch (DecryptException $e) {
// it probably was not encrypted.
Log::debug(sprintf('Not a problem: %s', $e->getMessage()));
}
// also count the transactions:
$countTransactions = DB::table('category_transaction')->where('category_id', $entry->id)->count();
@@ -583,12 +615,6 @@ class VerifyDatabase extends Command
/** @var stdClass $entry */
foreach ($set as $entry) {
$objName = $entry->name;
try {
$objName = Crypt::decrypt($objName);
} catch (DecryptException $e) {
// it probably was not encrypted.
Log::debug(sprintf('Not a problem: %s', $e->getMessage()));
}
$line = sprintf(
'User #%d (%s) has %s #%d ("%s") which has no transactions.',
@@ -665,4 +691,24 @@ class VerifyDatabase extends Command
);
}
}
/**
* Collect all journals with empty amount.
*/
private function reportZeroAmount(): void
{
$set = Transaction::where('amount', 0)->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
$set = array_unique($set);
/** @var Collection $journals */
$journals = TransactionJournal::whereIn('id', $set)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$message = sprintf(
'Transaction "%s" (#%d), owned by user %s, has amount zero (0.00). It should be deleted.', $journal->description,
$journal->id, $journal->user->email
);
$this->error($message);
}
}
}

View File

@@ -30,6 +30,7 @@ use Log;
/**
* File to make sure commands work.
*
* @codeCoverageIgnore
*/
class Kernel extends ConsoleKernel
@@ -54,7 +55,9 @@ class Kernel extends ConsoleKernel
{
$schedule->call(
function () {
Log::error('Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions at https://firefly-iii.readthedocs.io/en/latest/');
Log::error(
'Firefly III no longer users the Laravel scheduler to do cron jobs! Please read the instructions at https://firefly-iii.readthedocs.io/en/latest/'
);
echo "\n";
echo '------------';
echo "\n";

View File

@@ -30,6 +30,7 @@ use Log;
/**
* Class AdminRequestedTestMessage.
*
* @codeCoverageIgnore
*/
class AdminRequestedTestMessage extends Event

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Events;
/**
* Class Event.
*
* @codeCoverageIgnore
*/
abstract class Event

View File

@@ -29,6 +29,7 @@ use Illuminate\Queue\SerializesModels;
/**
* Class RegisteredUser.
*
* @codeCoverageIgnore
*/
class RegisteredUser extends Event

View File

@@ -29,6 +29,7 @@ use Illuminate\Queue\SerializesModels;
/**
* Class RequestedNewPassword.
*
* @codeCoverageIgnore
*/
class RequestedNewPassword extends Event

View File

@@ -1,4 +1,25 @@
<?php
/**
* RequestedReportOnJournals.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
/**
* RequestedReportOnJournals.php

View File

@@ -38,18 +38,14 @@ class StoredTransactionJournal extends Event
/** @var TransactionJournal The journal that was stored. */
public $journal;
/** @var int The piggy bank ID. */
public $piggyBankId;
/**
* Create a new event instance.
*
* @param TransactionJournal $journal
* @param int $piggyBankId
*/
public function __construct(TransactionJournal $journal, int $piggyBankId)
public function __construct(TransactionJournal $journal)
{
$this->journal = $journal;
$this->piggyBankId = $piggyBankId;
$this->journal = $journal;
}
}

View File

@@ -38,6 +38,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class Handler
*
* @codeCoverageIgnore
*/
class Handler extends ExceptionHandler
@@ -92,7 +93,7 @@ class Handler extends ExceptionHandler
}
if ($exception instanceof FireflyException || $exception instanceof ErrorException || $exception instanceof OAuthServerException) {
$isDebug = env('APP_DEBUG', false);
$isDebug = config('app.debug');
return response()->view('errors.FireflyException', ['exception' => $exception, 'debug' => $isDebug], 500);
}
@@ -116,11 +117,11 @@ class Handler extends ExceptionHandler
public function report(Exception $exception)
{
$doMailError = env('SEND_ERROR_MESSAGE', true);
$doMailError = config('firefly.send_error_message');
// if the user wants us to mail:
if (true === $doMailError
// and if is one of these error instances
&& ($exception instanceof FireflyException || $exception instanceof ErrorException || $exception instanceof OAuthServerException)) {
&& ($exception instanceof FireflyException || $exception instanceof ErrorException)) {
$userData = [
'id' => 0,
'email' => 'unknown@example.com',
@@ -145,7 +146,7 @@ class Handler extends ExceptionHandler
// create job that will mail.
$ipAddress = Request::ip() ?? '0.0.0.0';
$job = new MailError($userData, env('SITE_OWNER', ''), $ipAddress, $data);
$job = new MailError($userData, (string)config('firefly.site_owner'), $ipAddress, $data);
dispatch($job);
}

View File

@@ -28,6 +28,7 @@ use Crypt;
use FireflyIII\Models\Attachment;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Support\Collection;
use Log;
use Storage;
@@ -102,6 +103,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
* @param Attachment $attachment
*
* @return bool
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
private function exportAttachment(Attachment $attachment): bool
{
@@ -110,7 +112,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
if ($this->uploadDisk->exists($file)) {
try {
$decrypted = Crypt::decrypt($this->uploadDisk->get($file));
} catch (DecryptException $e) {
} catch (DecryptException|FileNotFoundException $e) {
Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage());
return false;

View File

@@ -110,7 +110,7 @@ class UploadCollector extends BasicCollector implements CollectorInterface
Log::error(sprintf('Could not decrypt old import file "%s". Skipped because: %s', $key, $e->getMessage()));
}
if (\strlen($content) > 0) {
if ('' !== $content) {
// add to export disk.
$date = $job->created_at->format('Y-m-d');
$file = sprintf('%s-Old %s import dated %s.%s', $this->job->key, strtoupper($job->file_type), $date, $job->file_type);

View File

@@ -49,48 +49,24 @@ use FireflyIII\Models\Transaction;
*/
final class Entry
{
/** @var int ID of the journal */
public $journal_id;
/** @var int ID of the transaction */
public $transaction_id = 0;
/** @var string The date. */
public $date;
/** @var string The description */
public $description;
/** @var string The currency code. */
public $currency_code;
/** @var string The amount. */
public $amount;
/** @var string The foreign currency code */
public $foreign_currency_code = '';
/** @var string Foreign amount */
public $foreign_amount = '0';
/** @var string Transaction type */
public $transaction_type;
/** @var string Asset account BIC */
public $asset_account_bic;
/** @var string Asset account IBAN */
public $asset_account_iban;
/** @var string Asset account ID */
public $asset_account_id;
/** @var string Asset account name */
public $asset_account_name;
/** @var string Asset account IBAN */
public $asset_account_iban;
/** @var string Asset account BIC */
public $asset_account_bic;
/** @var string Asset account number */
public $asset_account_number;
/** @var string Asset account currency code */
public $asset_currency_code;
/** @var string Opposing account ID */
public $opposing_account_id;
/** @var string Opposing account name */
public $opposing_account_name;
/** @var string Opposing account IBAN */
public $opposing_account_iban;
/** @var string Opposing account BIC */
public $opposing_account_bic;
/** @var string Opposing account number */
public $opposing_account_number;
/** @var string Opposing account code */
public $opposing_currency_code;
/** @var string Bill ID */
public $bill_id;
/** @var string Bill name */
public $bill_name;
/** @var string Budget ID */
public $budget_id;
/** @var string Budget name */
@@ -99,14 +75,38 @@ final class Entry
public $category_id;
/** @var string Category name */
public $category_name;
/** @var string Bill ID */
public $bill_id;
/** @var string Bill name */
public $bill_name;
/** @var string The currency code. */
public $currency_code;
/** @var string The date. */
public $date;
/** @var string The description */
public $description;
/** @var string Foreign amount */
public $foreign_amount = '0';
/** @var string The foreign currency code */
public $foreign_currency_code = '';
/** @var int ID of the journal */
public $journal_id;
/** @var string Notes */
public $notes;
/** @var string Opposing account BIC */
public $opposing_account_bic;
/** @var string Opposing account IBAN */
public $opposing_account_iban;
/** @var string Opposing account ID */
public $opposing_account_id;
/** @var string Opposing account name */
public $opposing_account_name;
/** @var string Opposing account number */
public $opposing_account_number;
/** @var string Opposing account code */
public $opposing_currency_code;
/** @var string Tags */
public $tags;
/** @var int ID of the transaction */
public $transaction_id = 0;
/** @var string Transaction type */
public $transaction_type;
/**
* Entry constructor.
@@ -132,7 +132,7 @@ final class Entry
$entry->transaction_id = $transaction->id;
$entry->date = $transaction->date->format('Ymd');
$entry->description = $transaction->description;
if (\strlen((string)$transaction->transaction_description) > 0) {
if ('' !== (string)$transaction->transaction_description) {
$entry->description = $transaction->transaction_description . '(' . $transaction->description . ')';
}
$entry->currency_code = $transaction->transactionCurrency->code;
@@ -148,14 +148,14 @@ final class Entry
$entry->transaction_type = $transaction->transaction_type_type;
$entry->asset_account_id = (string)$transaction->account_id;
$entry->asset_account_name = app('steam')->tryDecrypt($transaction->account_name);
$entry->asset_account_name = $transaction->account_name;
$entry->asset_account_iban = $transaction->account_iban;
$entry->asset_account_number = $transaction->account_number;
$entry->asset_account_bic = $transaction->account_bic;
$entry->asset_currency_code = $transaction->account_currency_code;
$entry->opposing_account_id = (string)$transaction->opposing_account_id;
$entry->opposing_account_name = app('steam')->tryDecrypt($transaction->opposing_account_name);
$entry->opposing_account_name = $transaction->opposing_account_name;
$entry->opposing_account_iban = $transaction->opposing_account_iban;
$entry->opposing_account_number = $transaction->opposing_account_number;
$entry->opposing_account_bic = $transaction->opposing_account_bic;
@@ -163,23 +163,23 @@ final class Entry
// budget
$entry->budget_id = (string)$transaction->transaction_budget_id;
$entry->budget_name = app('steam')->tryDecrypt($transaction->transaction_budget_name);
$entry->budget_name = $transaction->transaction_budget_name;
if (null === $transaction->transaction_budget_id) {
$entry->budget_id = $transaction->transaction_journal_budget_id;
$entry->budget_name = app('steam')->tryDecrypt($transaction->transaction_journal_budget_name);
$entry->budget_name = $transaction->transaction_journal_budget_name;
}
// category
$entry->category_id = (string)$transaction->transaction_category_id;
$entry->category_name = app('steam')->tryDecrypt($transaction->transaction_category_name);
$entry->category_name = $transaction->transaction_category_name;
if (null === $transaction->transaction_category_id) {
$entry->category_id = $transaction->transaction_journal_category_id;
$entry->category_name = app('steam')->tryDecrypt($transaction->transaction_journal_category_name);
$entry->category_name = $transaction->transaction_journal_category_name;
}
// budget
$entry->bill_id = (string)$transaction->bill_id;
$entry->bill_name = app('steam')->tryDecrypt($transaction->bill_name);
$entry->bill_name = $transaction->bill_name;
$entry->tags = $transaction->tags;
$entry->notes = $transaction->notes;

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Export;
use Crypt;
use DB;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Export\Collector\AttachmentCollector;
@@ -41,8 +40,8 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
use Illuminate\Support\Facades\Storage;
use Log;
use ZipArchive;
/**
@@ -193,8 +192,8 @@ class ExpandedProcessor implements ProcessorInterface
*/
public function createZipFile(): bool
{
$zip = new ZipArchive;
$file = $this->job->key . '.zip';
$zip = new ZipArchive;
$file = $this->job->key . '.zip';
$localPath = storage_path('export') . '/' . $file;
if (true !== $zip->open($localPath, ZipArchive::CREATE)) {
@@ -339,7 +338,7 @@ class ExpandedProcessor implements ProcessorInterface
$return = [];
/** @var Note $note */
foreach ($notes as $note) {
if (\strlen(trim((string)$note->text)) > 0) {
if ('' !== trim((string)$note->text)) {
$id = (int)$note->noteable_id;
$return[$id] = $note->text;
}
@@ -368,7 +367,7 @@ class ExpandedProcessor implements ProcessorInterface
foreach ($set as $entry) {
$id = (int)$entry->transaction_journal_id;
$result[$id] = $result[$id] ?? [];
$result[$id][] = Crypt::decrypt($entry->tag);
$result[$id][] = $entry->tag;
}
return $result;

View File

@@ -25,8 +25,8 @@ declare(strict_types=1);
namespace FireflyIII\Export\Exporter;
use FireflyIII\Export\Entry\Entry;
use League\Csv\Writer;
use Illuminate\Support\Facades\Storage;
use League\Csv\Writer;
/**
* Class CsvExporter.

View File

@@ -30,6 +30,7 @@ namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Services\Internal\Support\AccountServiceTrait;
use FireflyIII\User;
use Log;
@@ -47,11 +48,13 @@ class AccountFactory
use AccountServiceTrait;
/**
* Constructor.
* AccountFactory constructor.
*
* @codeCoverageIgnore
*/
public function __construct()
{
if ('testing' === env('APP_ENV')) {
if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
}
}
@@ -79,7 +82,6 @@ class AccountFactory
// account may exist already:
$return = $this->find($data['name'], $type->type);
if (null === $return) {
// create it:
$databaseData
@@ -92,6 +94,21 @@ class AccountFactory
'iban' => $data['iban'],
];
// find currency, or use default currency instead.
/** @var TransactionCurrencyFactory $factory */
$factory = app(TransactionCurrencyFactory::class);
/** @var TransactionCurrency $currency */
$currency = $factory->find((int)($data['currency_id'] ?? null), (string)($data['currency_code'] ?? null));
if (null === $currency) {
// use default currency:
$currency = app('amount')->getDefaultCurrencyByUser($this->user);
}
$currency->enabled = true;
$currency->save();
unset($data['currency_code']);
$data['currency_id'] = $currency->id;
// remove virtual balance when not an asset account or a liability
$canHaveVirtual = [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD];
if (!\in_array($type->type, $canHaveVirtual, true)) {

View File

@@ -39,10 +39,11 @@ class AccountMetaFactory
*/
public function __construct()
{
if ('testing' === env('APP_ENV')) {
if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
}
}
/**
* @param array $data
*
@@ -73,6 +74,7 @@ class AccountMetaFactory
// if $data has field and $entry is null, create new one:
if (null === $entry) {
Log::debug(sprintf('Created meta-field "%s":"%s" for account #%d ("%s") ', $field, $value, $account->id, $account->name));
return $this->create(['account_id' => $account->id, 'name' => $field, 'data' => $value]);
}
@@ -89,6 +91,7 @@ class AccountMetaFactory
} catch (Exception $e) { // @codeCoverageIgnore
Log::debug(sprintf('Could not delete entry: %s', $e->getMessage())); // @codeCoverageIgnore
}
return null;
}

View File

@@ -23,8 +23,11 @@ declare(strict_types=1);
namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Note;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\User;
use Log;
@@ -33,31 +36,46 @@ use Log;
*/
class AttachmentFactory
{
/** @var User */
private $user;
/**
* Constructor.
*/
public function __construct()
{
if ('testing' === env('APP_ENV')) {
if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
}
}
/** @var User */
private $user;
/**
* @param array $data
*
* @return Attachment|null
* @throws FireflyException
*/
public function create(array $data): ?Attachment
{
// append if necessary.
$model = false === strpos('FireflyIII', $data['model']) ? 'FireflyIII\\Models\\' . $data['model'] : $data['model'];
if (Transaction::class === $model) {
/** @var Transaction $transaction */
$transaction = $this->user->transactions()->find((int)$data['model_id']);
if (null === $transaction) {
throw new FireflyException('Unexpectedly could not find transaction');
}
$data['model_id'] = $transaction->transaction_journal_id;
$model = TransactionJournal::class;
}
// create attachment:
$attachment = Attachment::create(
[
'user_id' => $this->user->id,
'attachable_id' => $data['model_id'],
'attachable_type' => $data['model'],
'attachable_type' => $model,
'md5' => '',
'filename' => $data['filename'],
'title' => '' === $data['title'] ? null : $data['title'],

View File

@@ -36,20 +36,21 @@ use Log;
*/
class BillFactory
{
use BillServiceTrait;
/** @var User */
private $user;
/**
* Constructor.
*/
public function __construct()
{
if ('testing' === env('APP_ENV')) {
if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
}
}
use BillServiceTrait;
/** @var User */
private $user;
/**
* @param array $data
*
@@ -60,7 +61,13 @@ class BillFactory
/** @var TransactionCurrencyFactory $factory */
$factory = app(TransactionCurrencyFactory::class);
/** @var TransactionCurrency $currency */
$currency = $factory->find((int)$data['currency_id'], (string)$data['currency_code']);
$currency = $factory->find((int)($data['currency_id'] ?? null), (string)($data['currency_code'] ?? null));
if (null === $currency) {
// use default currency:
$currency = app('amount')->getDefaultCurrencyByUser($this->user);
}
/** @var Bill $bill */
$bill = Bill::create(
[
@@ -73,7 +80,7 @@ class BillFactory
'date' => $data['date'],
'repeat_freq' => $data['repeat_freq'],
'skip' => $data['skip'],
'automatch' => $data['automatch'] ?? true,
'automatch' => true,
'active' => $data['active'] ?? true,
]
);
@@ -104,7 +111,7 @@ class BillFactory
}
// then find by name:
if (null === $bill && \strlen($billName) > 0) {
if (null === $bill && '' !== $billName) {
$bill = $this->findByName($billName);
}

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