Compare commits

...

234 Commits
4.1.6 ... 4.2.0

Author SHA1 Message Date
James Cole
956019ff4a Merge branch 'release/4.2.0' 2016-11-27 16:00:47 +01:00
James Cole
8279cf0e88 New version. 2016-11-27 15:59:13 +01:00
James Cole
43c32abfe8 Various code cleanup. 2016-11-26 13:02:44 +01:00
James Cole
0e66939408 Various code cleanup. 2016-11-26 10:53:20 +01:00
James Cole
22d2a523fb Some minor code fixes. 2016-11-26 10:39:05 +01:00
James Cole
bc825a8603 Remove unused code. 2016-11-26 09:29:41 +01:00
James Cole
c9cfda34a1 Remove duplicate code. 2016-11-26 09:21:49 +01:00
James Cole
e8dfbff73f Various code cleanup. 2016-11-26 09:16:06 +01:00
James Cole
62e41f1997 Remove TODO annotations 2016-11-26 09:07:16 +01:00
James Cole
8c9f90f1b4 Some code cleanup. 2016-11-26 09:01:00 +01:00
James Cole
1453a78e49 Remove todo annotations. 2016-11-26 08:55:26 +01:00
James Cole
7efaf51595 Merge pull request #428 from JC5/l10n_develop
New Crowdin translations
2016-11-26 08:41:35 +01:00
James Cole
6bc6674ab1 Some code simplification. 2016-11-26 08:41:15 +01:00
James Cole
d6c7ff0ccb Chart for budget report will also include split journals. 2016-11-26 07:18:20 +01:00
James Cole
28f655dba1 This code makes sure the budget report also includes split expenses. 2016-11-26 07:09:02 +01:00
James Cole
6a3de12894 Approved. Step name: Proofread 2016-11-25 23:01:03 +01:00
James Cole
c7940333ec Approved. Step name: Proofread 2016-11-25 23:01:01 +01:00
James Cole
8860378757 Fix budget in split journals. 2016-11-25 19:06:06 +01:00
James Cole
728fda0116 This allows the user to set the “default” currency for an asset account (#305). It doesn’t do anything other than this yet. 2016-11-25 18:00:29 +01:00
James Cole
0c72e1831f Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  Approved. Step name: Proofread
  Translated
  New translations
  New translations
2016-11-25 17:43:38 +01:00
James Cole
7da21976ec Merge pull request #425 from JC5/l10n_develop
New Crowdin translations
2016-11-25 17:43:17 +01:00
James Cole
b739859c64 Expand journal meta with soft delete. This pushes Firefly to 4.2.0. 2016-11-25 17:42:45 +01:00
James Cole
d25665f843 New translations 2016-11-25 17:02:24 +01:00
James Cole
1f41f7bd0f New translations 2016-11-25 17:02:20 +01:00
James Cole
dd8638ca98 New translations 2016-11-25 17:02:17 +01:00
James Cole
4ba9ff05b0 New translations 2016-11-25 17:02:12 +01:00
James Cole
618aad5432 New translations 2016-11-25 17:02:08 +01:00
James Cole
e46fc7501e New translations 2016-11-25 17:02:06 +01:00
James Cole
7b91e98d46 New translations 2016-11-25 17:01:58 +01:00
James Cole
85be218f92 New translations 2016-11-25 17:01:55 +01:00
James Cole
71206e395e New translations 2016-11-25 17:01:53 +01:00
James Cole
9b2d2e16b0 New translations 2016-11-25 17:01:48 +01:00
James Cole
5ae01b382e Approved. Step name: Proofread 2016-11-25 17:01:40 +01:00
James Cole
d92a0753a6 Translated 2016-11-25 17:01:35 +01:00
James Cole
f937a74507 New translations 2016-11-25 17:01:27 +01:00
James Cole
c3584ad20c New translations 2016-11-25 17:01:25 +01:00
James Cole
c049d5cfa6 Various small fixes. 2016-11-25 16:55:04 +01:00
James Cole
6c9990e0be Various Javascript related fixes. 2016-11-25 16:54:13 +01:00
James Cole
b34e4cd31b This fixes #422 2016-11-25 16:52:43 +01:00
James Cole
7852b8a785 Make sure ff does not create accounts when balance is said to be 0. 2016-11-25 16:26:03 +01:00
James Cole
6eeb60db5c Multiply sum by -1. 2016-11-24 21:37:09 +01:00
James Cole
d076cfc08f Attempt to fix issue #417 2016-11-24 21:35:23 +01:00
James Cole
68a93ff97c Made fonts local 2016-11-24 19:26:47 +01:00
James Cole
295dcb4f65 Whoops ;) 2016-11-24 19:20:04 +01:00
James Cole
d9849f60c0 Parse error 2016-11-24 19:16:15 +01:00
James Cole
7ebb68e36c This fixes #419 2016-11-24 19:15:16 +01:00
James Cole
f029f7607b Rewrote all email messages. 2016-11-22 21:21:11 +01:00
James Cole
2ba5733ebc Merge pull request #415 from JC5/l10n_develop
New Crowdin translations
2016-11-22 19:31:51 +01:00
James Cole
3fe1d1d368 New translations 2016-11-22 19:12:25 +01:00
James Cole
438c372583 New translations 2016-11-22 19:12:19 +01:00
James Cole
797aa4858e New translations 2016-11-22 19:12:13 +01:00
James Cole
8c858cd066 New translations 2016-11-22 19:12:05 +01:00
James Cole
85aebd39b9 New translations 2016-11-22 19:12:00 +01:00
James Cole
9a5a037424 Approved. Step name: Proofread 2016-11-22 19:11:48 +01:00
James Cole
7d557cbf91 New translations 2016-11-22 19:11:30 +01:00
James Cole
dbbc85a576 Hide some boxes when the user has no bills. 2016-11-22 19:10:38 +01:00
James Cole
eb78cf20c2 This fixes #414 2016-11-22 19:10:17 +01:00
James Cole
4a99399952 Fix chart for account/all overview. 2016-11-21 20:23:25 +01:00
James Cole
6075d75ee2 Fix debug code [skip ci] 2016-11-21 20:15:59 +01:00
James Cole
f4c56fee66 Merge pull request #412 from JC5/l10n_develop
New Crowdin translations
2016-11-20 19:32:54 +01:00
James Cole
04c59304da Add sorting to a report table [skip ci] 2016-11-20 19:11:10 +01:00
James Cole
4b3c31a11a Ignore deleted transactions. [skip ci] 2016-11-20 19:03:08 +01:00
James Cole
14576d2753 Approved. Step name: Proofread 2016-11-20 18:51:13 +01:00
James Cole
72ca1c20c7 Merge pull request #411 from JC5/l10n_develop
New Crowdin translations
2016-11-20 18:45:16 +01:00
James Cole
93645819b8 New translations 2016-11-20 18:41:48 +01:00
James Cole
39468f871b New translations 2016-11-20 18:41:42 +01:00
James Cole
faa47781d2 New translations 2016-11-20 18:41:37 +01:00
James Cole
2c196bab6d New translations 2016-11-20 18:41:30 +01:00
James Cole
9ae71075ef New translations 2016-11-20 18:41:26 +01:00
James Cole
0013cdfa78 Translated 2016-11-20 18:41:14 +01:00
James Cole
52f3f64f7b New translations 2016-11-20 18:41:03 +01:00
James Cole
670fa77dd7 New tests. 2016-11-20 18:34:49 +01:00
James Cole
8baea2feb9 Code for #385 2016-11-20 18:31:29 +01:00
James Cole
c56f937521 Improved sorting in various views. 2016-11-20 17:36:11 +01:00
James Cole
0b613c3b8c Improve sortability in various lists. 2016-11-20 15:30:16 +01:00
James Cole
78f297e18f Fixed some display bugs for split journals. 2016-11-20 14:17:16 +01:00
James Cole
bd8a285d6d Merge pull request #410 from JC5/l10n_develop
New Crowdin translations
2016-11-20 13:06:14 +01:00
James Cole
b44602fd55 New translations 2016-11-20 13:01:38 +01:00
James Cole
41238903e1 New translations 2016-11-20 13:01:33 +01:00
James Cole
a0c88e9b33 New translations 2016-11-20 13:01:30 +01:00
James Cole
5d184aa53e New translations 2016-11-20 13:01:23 +01:00
James Cole
9f9bf86a9f New translations 2016-11-20 13:01:19 +01:00
James Cole
53af9345eb Approved. Step name: Proofread 2016-11-20 13:01:10 +01:00
James Cole
da6bcf04df New translations 2016-11-20 13:00:58 +01:00
James Cole
ec4ec1a147 New (not implemented) tests. 2016-11-20 12:53:04 +01:00
James Cole
350e0b08b1 This implements #377 2016-11-20 12:51:33 +01:00
James Cole
9340ca09e6 Fixed #408 2016-11-20 12:08:43 +01:00
James Cole
a1cef5c339 Found a bug in the import routine where "default accounts" (an account type no longer used by default) is found. 2016-11-20 11:44:27 +01:00
James Cole
94875adb6c Various code cleanup. 2016-11-20 11:43:19 +01:00
James Cole
75a524c656 Added debug code for a possible import issue. 2016-11-20 11:40:05 +01:00
James Cole
e1e94a788c Register and use interface. 2016-11-20 08:57:48 +01:00
James Cole
8417f45d02 Fixed some tests. 2016-11-20 08:54:52 +01:00
James Cole
685310a368 First account controller tests 2016-11-20 08:46:02 +01:00
James Cole
45e7a4576a Extend some test stuff. 2016-11-20 08:30:25 +01:00
James Cole
f8c5c15655 Updated some tests. 2016-11-20 07:24:18 +01:00
James Cole
26190524f4 Skeletons for test 2016-11-19 20:30:30 +01:00
James Cole
5d901a7ecb Remove local development file. [skip ci] 2016-11-19 18:21:48 +01:00
James Cole
929d8b3adc Merge branch 'release/4.1.7' 2016-11-19 16:47:02 +01:00
James Cole
cd6e37b9cb Update composer.lock file. [skip ci] 2016-11-19 16:45:33 +01:00
James Cole
b647386541 New release imminent. [skip ci] 2016-11-19 16:44:17 +01:00
James Cole
174fd88435 Merge pull request #409 from JC5/l10n_develop
New Crowdin translations
2016-11-19 16:43:44 +01:00
James Cole
cc9211b7c2 Translated 2016-11-19 16:40:48 +01:00
James Cole
a9795fb095 Update ignore file [skip ci] 2016-11-19 16:34:52 +01:00
James Cole
8554aae21e Update read me file [skip ci] 2016-11-19 16:31:39 +01:00
James Cole
5a2ef36f2a Fix travis script. 2016-11-19 16:28:04 +01:00
James Cole
01e3f91ece Do not test hhvm. Fix script. 2016-11-19 16:17:04 +01:00
James Cole
7ec9c090cc Fix test script. 2016-11-19 16:13:57 +01:00
James Cole
b057d69f8e Correct travis configuration. 2016-11-19 16:07:02 +01:00
James Cole
ff4e1838bc Update travis configuration. 2016-11-19 16:03:59 +01:00
James Cole
e4ecd0b7ff Empty travis CI file. 2016-11-19 16:01:19 +01:00
James Cole
1ba35f73e1 Fixed a test 2016-11-19 16:00:20 +01:00
James Cole
240f3c126b Restored some tests. 2016-11-19 15:55:49 +01:00
James Cole
23925a0076 Enabled cache [skip ci] 2016-11-19 15:17:00 +01:00
James Cole
50b72cf229 More chart optimisations. 2016-11-19 13:37:44 +01:00
James Cole
ee6b72afa5 Fix some bugs related to cash accounts. 2016-11-19 12:57:35 +01:00
James Cole
781621960d Make sure chart is displayed. 2016-11-19 09:26:32 +01:00
James Cole
e15ea04186 Join two charts, simpler code. 2016-11-19 07:27:54 +01:00
James Cole
73f0cc705b Code cleanup. 2016-11-18 20:06:08 +01:00
James Cole
0c072c7d51 Some code cleanup. 2016-11-18 19:58:06 +01:00
James Cole
884bed85a1 Update composer file. 2016-11-18 19:54:21 +01:00
James Cole
a319264428 fixed #406 2016-11-18 18:58:48 +01:00
James Cole
6506e70a91 Merge pull request #407 from JC5/l10n_develop
New Crowdin translations
2016-11-18 10:03:08 +01:00
James Cole
e6fcb19db7 New translations 2016-11-18 02:01:10 +01:00
James Cole
a3fba53182 Translated 2016-11-18 02:01:03 +01:00
James Cole
f8438dd9d3 New translations 2016-11-18 01:51:04 +01:00
James Cole
028a0dcae1 New translations 2016-11-18 01:41:05 +01:00
James Cole
b4a06b5bbd Translated 2016-11-18 01:41:01 +01:00
James Cole
4fe1a5d527 New translations 2016-11-18 01:30:59 +01:00
James Cole
865930c5b2 New translations 2016-11-18 01:30:58 +01:00
James Cole
96b4e2c196 New translations 2016-11-18 01:20:59 +01:00
James Cole
b7e7c7e9e2 New translations 2016-11-18 01:11:02 +01:00
James Cole
7771669db7 New translations 2016-11-18 01:01:09 +01:00
James Cole
ef59eb6e1f Translated 2016-11-18 01:01:05 +01:00
James Cole
a14b2bc5a7 Translated 2016-11-18 01:01:04 +01:00
James Cole
47349589cb New translations 2016-11-18 00:51:00 +01:00
James Cole
79afe84f30 Merge pull request #405 from JC5/l10n_develop
New Crowdin translations
2016-11-17 20:35:32 +01:00
James Cole
171187b25c New translations 2016-11-17 20:12:21 +01:00
James Cole
7f1b661e61 New translations 2016-11-17 20:12:18 +01:00
James Cole
2c2a3a5475 New translations 2016-11-17 20:12:08 +01:00
James Cole
1677ca9619 New translations 2016-11-17 20:12:00 +01:00
James Cole
204da3e846 New translations 2016-11-17 20:11:55 +01:00
James Cole
f36d423b1e New translations 2016-11-17 20:11:46 +01:00
James Cole
79c7280046 New translations 2016-11-17 20:11:32 +01:00
James Cole
e10fc4a854 Forgotten a translation. 2016-11-17 20:04:53 +01:00
James Cole
5088df103f Finished category report. 2016-11-17 20:02:55 +01:00
James Cole
13b96f6136 New text for translations. 2016-11-16 20:59:32 +01:00
James Cole
757662ca4b Removed duplicate code. 2016-11-16 20:59:21 +01:00
James Cole
4ef324cf24 Optimized chart code. 2016-11-16 20:35:25 +01:00
James Cole
cb02e0ee71 Merge pull request #404 from JC5/l10n_develop
New Crowdin translations
2016-11-16 18:59:17 +01:00
James Cole
ec3a90688e New translations 2016-11-16 15:01:33 +01:00
James Cole
6dcecdcc64 New translations 2016-11-16 06:40:59 +01:00
James Cole
25d917240d Translated 2016-11-16 06:30:58 +01:00
James Cole
0906915a87 New translations 2016-11-16 06:30:57 +01:00
James Cole
9c92a94177 Translated 2016-11-16 06:20:52 +01:00
James Cole
1b125ecd22 New translations 2016-11-16 04:50:52 +01:00
James Cole
25a2bcd76e New translations 2016-11-16 04:20:53 +01:00
James Cole
b2e09f4240 New translations 2016-11-15 23:11:13 +01:00
James Cole
560165850f New translations 2016-11-15 23:01:17 +01:00
James Cole
0bb07e1eeb Small extension of category report. 2016-11-13 20:18:01 +01:00
James Cole
0c0f2109f6 Fix chart size [skip ci] 2016-11-13 11:31:48 +01:00
James Cole
f546670342 Merge pull request #403 from JC5/l10n_develop
New Crowdin translations
2016-11-12 21:58:23 +01:00
James Cole
eecb6c6679 Optimize again for account name [skip ci] 2016-11-12 21:08:14 +01:00
James Cole
750b9d8038 Reduce number of queries. 2016-11-12 21:06:48 +01:00
James Cole
9ce28fdd2e Remove unused route. [skip ci] 2016-11-12 20:54:05 +01:00
James Cole
07af64ada5 New translations 2016-11-12 20:51:40 +01:00
James Cole
a0ab0ec902 New translations 2016-11-12 20:51:33 +01:00
James Cole
752f8582aa New translations 2016-11-12 20:51:27 +01:00
James Cole
4d0eed8c9b New translations 2016-11-12 20:51:23 +01:00
James Cole
0d7a8305f3 New translations 2016-11-12 20:51:17 +01:00
James Cole
2e8c0ec537 Approved. Step name: Proofread 2016-11-12 20:51:12 +01:00
James Cole
3155ec9e2b New translations 2016-11-12 20:51:01 +01:00
James Cole
7bbca7f6a8 Final code check for something with the debug bar [skip ci] 2016-11-12 20:48:29 +01:00
James Cole
f7579db4ad Clean up config [skip ci] 2016-11-12 20:41:15 +01:00
James Cole
2f47c58df5 New string for translation [skip ci] 2016-11-12 20:30:39 +01:00
James Cole
7e7ac264d2 Fixed category chart 2016-11-12 20:29:16 +01:00
James Cole
98d6c90e90 Removed some duplicate code. 2016-11-12 19:22:03 +01:00
James Cole
da49afa37b Removed duplicate code. 2016-11-12 19:12:16 +01:00
James Cole
64364c3e77 Sending of error email message is optional but enabled. 2016-11-12 19:11:59 +01:00
James Cole
b6f0fd1949 Translated 2016-11-12 19:11:04 +01:00
James Cole
0663a18f3a Translated 2016-11-12 19:11:03 +01:00
James Cole
c5928897eb New translations 2016-11-12 19:01:05 +01:00
James Cole
570373e875 Merge pull request #402 from JC5/l10n_develop
New Crowdin translations
2016-11-12 19:00:18 +01:00
James Cole
228afc2eea New translations 2016-11-12 12:31:45 +01:00
James Cole
6b61621d6a New translations 2016-11-12 12:31:42 +01:00
James Cole
424133fa83 New translations 2016-11-12 12:31:35 +01:00
James Cole
02e30c1fcc New translations 2016-11-12 12:31:28 +01:00
James Cole
e17a9d559b New translations 2016-11-12 12:31:25 +01:00
James Cole
36744377f6 New translations 2016-11-12 12:31:07 +01:00
James Cole
7c479f73c0 New translations 2016-11-12 12:31:00 +01:00
James Cole
85b3c4683b Fix redraw bug in category report. 2016-11-12 12:23:55 +01:00
James Cole
c5d2fabfec Optimize some views for category report 2016-11-12 12:12:11 +01:00
James Cole
a294f757ff Fixes all charts in future category report. 2016-11-12 10:14:20 +01:00
James Cole
04515da0bc Fixed the charts 2016-11-12 07:02:32 +01:00
James Cole
6d60d64a82 Some extended code for the category report. 2016-11-12 06:48:38 +01:00
James Cole
32b5a84a0c Fixes #401 2016-11-12 06:34:54 +01:00
James Cole
4b42ef0db8 Fixes #398 2016-11-12 06:27:48 +01:00
James Cole
abc7b9912d Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
2016-11-11 20:53:07 +01:00
James Cole
727717931a Do something fancy with colours. 2016-11-11 20:52:48 +01:00
James Cole
1d66b16468 Merge pull request #397 from JC5/l10n_develop
New Crowdin translations
2016-11-11 11:26:59 +01:00
James Cole
b918429c43 New translations 2016-11-10 06:30:56 +01:00
James Cole
888273d4a0 New translations 2016-11-10 06:30:54 +01:00
James Cole
31b5d5ba72 New translations 2016-11-10 06:30:47 +01:00
James Cole
b148d0868e New translations 2016-11-10 06:30:39 +01:00
James Cole
c1491383a8 New translations 2016-11-10 06:30:36 +01:00
James Cole
5f07918682 New translations 2016-11-10 06:30:17 +01:00
James Cole
8de6bd7ceb New translations 2016-11-10 06:30:10 +01:00
James Cole
5d4f1bc76d First working example of category report. No content, just place holders. #396 2016-11-10 06:23:21 +01:00
James Cole
8583b574ac Multi year was not visible. 2016-11-09 21:46:32 +01:00
James Cole
3600e1b5e7 Extend report capability for issue #396 and related report issues. 2016-11-09 21:36:54 +01:00
James Cole
fe57648349 Allow report options to be pulled using AJAX. Ajax is cool. 2016-11-09 19:25:09 +01:00
James Cole
f0e0cdb49b New website 2016-11-09 11:04:14 +01:00
James Cole
cf69333c6d This fixes #395 2016-11-08 21:34:13 +01:00
James Cole
3f7e16d270 Merge pull request #394 from JC5/l10n_develop
New Crowdin translations
2016-11-08 20:50:57 +01:00
James Cole
a63f1638f4 Upgrade some libraries for #391 2016-11-08 20:50:05 +01:00
James Cole
8ec2a3a391 New translations 2016-11-08 20:41:15 +01:00
James Cole
d875f0e580 New translations 2016-11-08 20:41:12 +01:00
James Cole
729534b4f3 New translations 2016-11-08 20:41:06 +01:00
James Cole
bd6a56a55e New translations 2016-11-08 20:41:04 +01:00
James Cole
96976db350 New translations 2016-11-08 20:40:58 +01:00
James Cole
8735190461 New translations 2016-11-08 20:40:55 +01:00
James Cole
709a14e5c9 New translations 2016-11-08 20:40:53 +01:00
James Cole
c89d2a52b5 New translations 2016-11-08 20:40:48 +01:00
James Cole
6084d16ea8 New translations 2016-11-08 20:40:45 +01:00
James Cole
1688fdb786 Approved. Step name: Proofread 2016-11-08 20:40:38 +01:00
James Cole
6cfb5ee2e9 Approved. Step name: Proofread 2016-11-08 20:40:33 +01:00
James Cole
2db560ed7d New translations 2016-11-08 20:40:27 +01:00
James Cole
45567cdf65 New translations 2016-11-08 20:40:24 +01:00
James Cole
508ad5157b New translations 2016-11-08 20:40:21 +01:00
James Cole
8fc41e0226 Fixes #390 2016-11-08 20:37:53 +01:00
James Cole
a08dfe1e3c Add interface for journal collector. 2016-11-08 20:36:09 +01:00
James Cole
49cc8a97a3 Clean up code, fixes #392 2016-11-08 20:35:30 +01:00
James Cole
5b8583dd2b Make sure scripts don't crash when no database present. 2016-11-07 20:25:09 +01:00
James Cole
f653bc5f6e Expand firefly config. 2016-11-07 18:49:35 +01:00
James Cole
a6a9794fc7 Merge branch 'release/4.1.6' into develop 2016-11-06 16:18:20 +01:00
329 changed files with 15308 additions and 5871 deletions

View File

@@ -35,8 +35,7 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
SEND_REGISTRATION_MAIL=true
MUST_CONFIRM_ACCOUNT=false
SEND_ERROR_MESSAGE=true
SHOW_INCOMPLETE_TRANSLATIONS=false
ANALYTICS_ID=

45
.env.testing Executable file
View File

@@ -0,0 +1,45 @@
APP_ENV=testing
APP_DEBUG=true
APP_FORCE_SSL=false
APP_FORCE_ROOT=
APP_KEY=TestTestTestTestTestTestTestTest
APP_LOG_LEVEL=debug
APP_URL=http://localhost
DB_CONNECTION=sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=homestead
DB_PASSWORD=secret
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
COOKIE_PATH="/"
COOKIE_DOMAIN=
COOKIE_SECURE=false
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_FROM=changeme@example.com
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=true
SHOW_INCOMPLETE_TRANSLATIONS=false
ANALYTICS_ID=
SITE_OWNER=mail@example.com
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_APP_ID=

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ result.html
test-import.sh
test-import-report.txt
public/google*.html
.env.backup

18
.travis.yml Normal file
View File

@@ -0,0 +1,18 @@
language: php
sudo: false
php:
- '7.0'
install:
- phpenv config-rm xdebug.ini
- composer selfupdate
- rm composer.lock
- composer update --no-scripts
- php artisan clear-compiled
- php artisan optimize
- php artisan env
- ./test.sh -r
- php artisan env
script:
- phpunit

View File

@@ -2,6 +2,61 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [4.2.0] - 2016-11-27
### Added
- Lots of (empty) tests
- Expanded transaction lists (#377)
- New charts at account view
- First code for #305
### Changed
- Updated all email messages.
- Made some fonts local
### Deprecated
- Initial release.
### Removed
- Initial release.
### Fixed
- Issue #408
- Various issues with split journals
- Issue #414, thx @zjean
- Issue #419, thx @schwalberich
- Issue #422, thx @xzaz
- Various import bugs, such as #416 (@zjean)
### Security
- Initial release.
## [4.1.7] - 2016-11-19
### Added
- Check for database table presence in console commands.
- Category report
- Reinstated old test routines.
### Changed
- Confirm account setting is no longer in `.env` file.
- Titles are now in reverse (current page > parent > firefly iii)
- Easier update of language files thanks to Github implementation.
- Uniform colours for charts.
### Fixed
- Made all pages more mobile friendly.
- Fixed #395 found by @marcoveeneman.
- Fixed #398 found by @marcoveeneman.
- Fixed #401 found by @marcoveeneman.
- Many optimizations.
- Updated many libraries.
- Various bugs found by myself.
## [4.1.6] - 2016-11-06
### Added
- New budget table for multi year report.

View File

@@ -1,5 +1,7 @@
# Firefly III [![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master)
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii)
## A personal finances manager
[![Screenshot](https://i.nder.be/hhfv03hp/400)](https://i.nder.be/hhfv03hp) [![Screenshot](https://i.nder.be/hhmwmqw9/400)](https://i.nder.be/hhmwmqw9)
@@ -10,7 +12,7 @@
## Installation
To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://jc5.github.io/firefly-iii/installation-guide/).
To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://firefly-iii.github.io/installation-guide/).
## More about Firefly III
@@ -25,6 +27,6 @@ Firefly works on the principle that if you know where you're money is going, you
- Firefly has lots of features without becoming fancy or bloated.
- If you feel you're missing something you can just ask me and I'll add it!
Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://jc5.github.io/firefly-iii/).
Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/).
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).

View File

@@ -50,9 +50,7 @@ class CreateImport extends Command
}
/**
* Execute the console command.
*
* @return mixed
*
*/
public function handle()
{

View File

@@ -51,9 +51,7 @@ class Import extends Command
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{

View File

@@ -20,6 +20,7 @@ use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
use Illuminate\Database\QueryException;
use Log;
use Schema;
/**
* Class UpgradeDatabase
@@ -65,6 +66,12 @@ class UpgradeDatabase extends Command
*/
private function setTransactionIdentifier()
{
// if table does not exist, return false
if (!Schema::hasTable('transaction_journals')) {
return;
}
$subQuery = TransactionJournal
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('transaction_journals.deleted_at')

View File

@@ -25,7 +25,9 @@ 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 Schema;
use stdClass;
/**
@@ -61,16 +63,21 @@ class VerifyDatabase extends Command
*/
public function handle()
{
// if table does not exist, return false
if (!Schema::hasTable('users')) {
return;
}
$this->reportObject('budget');
$this->reportObject('category');
$this->reportObject('tag');
// accounts with no transactions.
$this->reportAccounts();
// budgets with no limits
$this->reportBudgetLimits();
// budgets with no transactions
$this->reportBudgets();
// categories with no transactions
$this->reportCategories();
// tags with no transactions
$this->reportTags();
// sum of transactions is not zero.
$this->reportSum();
// any deleted transaction journals that have transactions that are NOT deleted:
@@ -136,48 +143,6 @@ class VerifyDatabase extends Command
}
}
/**
* Reports on budgets without any transactions.
*/
private function reportBudgets()
{
$set = Budget
::leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
->distinct()
->whereNull('budget_transaction_journal.budget_id')
->whereNull('budgets.deleted_at')
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']);
/** @var stdClass $entry */
foreach ($set as $entry) {
$line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has budget #' . $entry->id . ' ("' . Crypt::decrypt($entry->name)
. '") which has no transactions.';
$this->line($line);
}
}
/**
* Reports on categories without any transactions.
*/
private function reportCategories()
{
$set = Category
::leftJoin('category_transaction_journal', 'categories.id', '=', 'category_transaction_journal.category_id')
->leftJoin('users', 'categories.user_id', '=', 'users.id')
->distinct()
->whereNull('category_transaction_journal.category_id')
->whereNull('categories.deleted_at')
->get(['categories.id', 'categories.name', 'categories.user_id', 'users.email']);
/** @var stdClass $entry */
foreach ($set as $entry) {
$line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has category #' . $entry->id . ' ("' . Crypt::decrypt($entry->name)
. '") which has no transactions.';
$this->line($line);
}
}
/**
* Reports on deleted accounts that still have not deleted transactions or journals attached to them.
*/
@@ -294,6 +259,39 @@ class VerifyDatabase extends Command
}
/**
* @param string $name
*/
private function reportObject(string $name)
{
$plural = str_plural($name);
$class = sprintf('FireflyIII\Models\%s', ucfirst($name));
$field = $name == 'tag' ? 'tag' : 'name';
$set = $class::leftJoin($name . '_transaction_journal', $plural . '.id', '=', $name . '_transaction_journal.' . $name . '_id')
->leftJoin('users', $plural . '.user_id', '=', 'users.id')
->distinct()
->whereNull($name . '_transaction_journal.' . $name . '_id')
->whereNull($plural . '.deleted_at')
->get([$plural . '.id', $plural . '.' . $field . ' as name', $plural . '.user_id', 'users.email']);
/** @var stdClass $entry */
foreach ($set as $entry) {
$objName = $entry->name;
try {
$objName = Crypt::decrypt($objName);
} catch (DecryptException $e) {
// it probably was not encrypted.
}
$line = sprintf(
'Notice: User #%d (%s) has %s #%d ("%s") which has no transactions.',
$entry->user_id, $entry->email, $name, $entry->id, $objName
);
$this->line($line);
}
}
/**
* Reports for each user when the sum of their transactions is not zero.
*/
@@ -311,34 +309,12 @@ class VerifyDatabase extends Command
}
}
/**
* Reports on tags without any transactions.
*/
private function reportTags()
{
$set = Tag
::leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id')
->leftJoin('users', 'tags.user_id', '=', 'users.id')
->distinct()
->whereNull('tag_transaction_journal.tag_id')
->whereNull('tags.deleted_at')
->get(['tags.id', 'tags.tag', 'tags.user_id', 'users.email']);
/** @var stdClass $entry */
foreach ($set as $entry) {
$line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has tag #' . $entry->id . ' ("' . $entry->tag
. '") which has no transactions.';
$this->line($line);
}
}
/**
* Reports on deleted transactions that are connected to a not deleted journal.
*/
private function reportTransactions()
{
$set = Transaction
::leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
$set = Transaction::leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNotNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at')
->get(

View File

@@ -0,0 +1,46 @@
<?php
/**
* RequestedNewPassword.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Events;
use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
/**
* Class RequestedNewPassword
*
* @package FireflyIII\Events
*/
class RequestedNewPassword extends Event
{
use SerializesModels;
public $ipAddress;
public $token;
public $user;
/**
* Create a new event instance. This event is triggered when a users tries to reset his or her password.
*
* @param User $user
* @param string $token
* @param string $ipAddress
*/
public function __construct(User $user, string $token, string $ipAddress)
{
$this->user = $user;
$this->token = $token;
$this->ipAddress = $ipAddress;
}
}

View File

@@ -77,7 +77,8 @@ class Handler extends ExceptionHandler
*/
public function report(Exception $exception)
{
if ($exception instanceof FireflyException || $exception instanceof ErrorException) {
$doMailError = env('SEND_ERROR_MESSAGE', true);
if (($exception instanceof FireflyException || $exception instanceof ErrorException) && $doMailError) {
$userData = [
'id' => 0,
'email' => 'unknown@example.com',

View File

@@ -298,7 +298,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
'transactions AS opposing', function (JoinClause $join) {
$join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id')
->where('opposing.amount', '=', DB::raw('transactions.amount * -1'))
->where('transactions.identifier', '=', 'opposing.identifier');
->where('transactions.identifier', '=', DB::raw('opposing.identifier'));
}
)
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')

View File

@@ -24,6 +24,15 @@ use Illuminate\Support\Collection;
*/
interface AccountChartGeneratorInterface
{
/**
* @param array $values
* @param array $names
*
* @return array
*/
public function pieChart(array $values, array $names): array;
/**
* @param Collection $accounts
* @param Carbon $start

View File

@@ -14,6 +14,7 @@ namespace FireflyIII\Generator\Chart\Account;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Support\ChartColour;
use Illuminate\Support\Collection;
/**
@@ -83,6 +84,37 @@ class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
return $data;
}
/**
* @param array $values
* @param array $names
*
* @return array
*/
public function pieChart(array $values, array $names): array
{
$data = [
'datasets' => [
0 => [],
],
'labels' => [],
];
$index = 0;
foreach ($values as $categoryId => $value) {
// make larger than 0
if (bccomp($value, '0') === -1) {
$value = bcmul($value, '-1');
}
$data['datasets'][0]['data'][] = round($value, 2);
$data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
$data['labels'][] = $names[$categoryId];
$index++;
}
return $data;
}
/**
* @param Collection $accounts
* @param Carbon $start

View File

@@ -15,6 +15,7 @@ namespace FireflyIII\Generator\Chart\Bill;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Support\ChartColour;
use Illuminate\Support\Collection;
/**
@@ -37,7 +38,7 @@ class ChartJsBillChartGenerator implements BillChartGeneratorInterface
'datasets' => [
[
'data' => [round($unpaid, 2), round(bcmul($paid, '-1'), 2)],
'backgroundColor' => ['rgba(53, 124, 165,0.7)', 'rgba(0, 141, 76, 0.7)',],
'backgroundColor' => [ChartColour::getColour(0), ChartColour::getColour(1)],
],
],

View File

@@ -39,19 +39,9 @@ interface BudgetChartGeneratorInterface
public function frontpage(Collection $entries): array;
/**
* @param Collection $entries
* @param string $viewRange
* @param array $entries
*
* @return array
*/
public function period(Collection $entries, string $viewRange) : array;
/**
* @param Collection $budgets
* @param Collection $entries
*
* @return array
*/
public function year(Collection $budgets, Collection $entries): array;
public function period(array $entries): array;
}

View File

@@ -14,7 +14,6 @@ namespace FireflyIII\Generator\Chart\Budget;
use Illuminate\Support\Collection;
use Navigation;
/**
* Class ChartJsBudgetChartGenerator
@@ -101,15 +100,15 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
}
/**
* @param Collection $entries
* @param string $viewRange
* @param array $entries
*
* @return array
*/
public function period(Collection $entries, string $viewRange) : array
public function period(array $entries) : array
{
$data = [
'labels' => [],
'labels' => array_keys($entries),
'datasets' => [
0 => [
'label' => trans('firefly.budgeted'),
@@ -122,9 +121,8 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
],
'count' => 2,
];
foreach ($entries as $entry) {
$label = Navigation::periodShow($entry['date'], $viewRange);
$data['labels'][] = $label;
foreach ($entries as $label => $entry) {
// data set 0 is budgeted
// data set 1 is spent:
$data['datasets'][0]['data'][] = $entry['budgeted'];
@@ -135,42 +133,4 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
return $data;
}
/**
* @param Collection $budgets
* @param Collection $entries
*
* @return array
*/
public function year(Collection $budgets, Collection $entries): array
{
// language:
$format = (string)trans('config.month');
$data = [
'labels' => [],
'datasets' => [],
];
foreach ($budgets as $budget) {
$data['labels'][] = $budget->name;
}
// also add "no budget"
$data['labels'][] = strval(trans('firefly.no_budget'));
/** @var array $entry */
foreach ($entries as $entry) {
$array = [
'label' => $entry[0]->formatLocalized($format),
'data' => [],
];
array_shift($entry);
$array['data'] = $entry;
$data['datasets'][] = $array;
}
$data['count'] = count($data['datasets']);
return $data;
}
}

View File

@@ -30,14 +30,6 @@ interface CategoryChartGeneratorInterface
*/
public function all(Collection $entries): array;
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInPeriod(Collection $categories, Collection $entries): array;
/**
* @param Collection $entries
*
@@ -45,6 +37,13 @@ interface CategoryChartGeneratorInterface
*/
public function frontpage(Collection $entries): array;
/**
* @param array $entries
*
* @return array
*/
public function mainReportChart(array $entries): array;
/**
* @param Collection $entries
*
@@ -53,10 +52,10 @@ interface CategoryChartGeneratorInterface
public function period(Collection $entries): array;
/**
* @param Collection $categories
* @param Collection $entries
* @param array $data
*
* @return array
*/
public function spentInPeriod(Collection $categories, Collection $entries): array;
public function pieChart(array $data): array;
}

View File

@@ -12,6 +12,7 @@
declare(strict_types = 1);
namespace FireflyIII\Generator\Chart\Category;
use FireflyIII\Support\ChartColour;
use Illuminate\Support\Collection;
@@ -57,39 +58,6 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
return $data;
}
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInPeriod(Collection $categories, Collection $entries): array
{
// language:
$format = (string)trans('config.month');
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
foreach ($categories as $category) {
$data['labels'][] = $category->name;
}
foreach ($entries as $entry) {
$date = $entry[0]->formatLocalized($format);
array_shift($entry);
$data['count']++;
$data['datasets'][] = ['label' => $date, 'data' => $entry];
}
return $data;
}
/**
* @param Collection $entries
*
@@ -117,6 +85,51 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
return $data;
}
/**
* @param array $entries
*
* @return array
*/
public function mainReportChart(array $entries): array
{
$data = [
'count' => 0,
'labels' => array_keys($entries),
'datasets' => [],
];
foreach ($entries as $row) {
foreach ($row['in'] as $categoryId => $amount) {
// get in:
$data['datasets'][$categoryId . 'in']['data'][] = round($amount, 2);
// get out:
$opposite = $row['out'][$categoryId];
$data['datasets'][$categoryId . 'out']['data'][] = round($opposite, 2);
// set name:
$data['datasets'][$categoryId . 'out']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')';
$data['datasets'][$categoryId . 'in']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.income'))) . ')';
}
}
// remove empty rows:
foreach ($data['datasets'] as $key => $content) {
if (array_sum($content['data']) === 0.0) {
unset($data['datasets'][$key]);
}
}
// re-key the datasets array:
$data['datasets'] = array_values($data['datasets']);
$data['count'] = count($data['datasets']);
return $data;
}
/**
*
* @param Collection $entries
@@ -130,35 +143,32 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
}
/**
* @param Collection $categories
* @param Collection $entries
* @param array $entries
*
* @return array
*/
public function spentInPeriod(Collection $categories, Collection $entries): array
public function pieChart(array $entries): array
{
// language:
$format = (string)trans('config.month');
$data = [
'count' => 0,
$data = [
'datasets' => [
0 => [],
],
'labels' => [],
'datasets' => [],
];
foreach ($categories as $category) {
$data['labels'][] = $category->name;
}
$index = 0;
foreach ($entries as $entry) {
$date = $entry[0]->formatLocalized($format);
array_shift($entry);
$data['count']++;
$data['datasets'][] = ['label' => $date, 'data' => $entry];
if (bccomp($entry['amount'], '0') === -1) {
$entry['amount'] = bcmul($entry['amount'], '-1');
}
$data['datasets'][0]['data'][] = round($entry['amount'], 2);
$data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
$data['labels'][] = $entry['name'];
$index++;
}
return $data;
}
}

View File

@@ -0,0 +1,147 @@
<?php
/**
* MonthReportGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Audit;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use Illuminate\Support\Collection;
use Steam;
/**
* Class MonthReportGenerator
*
* @package FireflyIII\Generator\Report\Standard
*/
class MonthReportGenerator implements ReportGeneratorInterface
{
/** @var Collection */
private $accounts;
/** @var Carbon */
private $end;
/** @var Carbon */
private $start;
/**
* @return string
*/
public function generate(): string
{
$auditData = [];
$dayBefore = clone $this->start;
$dayBefore->subDay();
/** @var Account $account */
foreach ($this->accounts as $account) {
// balance the day before:
$id = $account->id;
$dayBeforeBalance = Steam::balance($account, $dayBefore);
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end);
$journals = $collector->getJournals();
$journals = $journals->reverse();
$startBalance = $dayBeforeBalance;
/** @var Transaction $journal */
foreach ($journals as $transaction) {
$transaction->before = $startBalance;
$transactionAmount = $transaction->transaction_amount;
$newBalance = bcadd($startBalance, $transactionAmount);
$transaction->after = $newBalance;
$startBalance = $newBalance;
}
/*
* Reverse set again.
*/
$auditData[$id]['journals'] = $journals->reverse();
$auditData[$id]['exists'] = $journals->count() > 0;
$auditData[$id]['end'] = $this->end->formatLocalized(strval(trans('config.month_and_day')));
$auditData[$id]['endBalance'] = Steam::balance($account, $this->end);
$auditData[$id]['dayBefore'] = $dayBefore->formatLocalized(strval(trans('config.month_and_day')));
$auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
}
$reportType = 'audit';
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
$hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date',
'interest_date', 'book_date', 'process_date',
// three new optional fields.
'due_date', 'payment_date', 'invoice_date',
'from', 'to', 'budget', 'category', 'bill',
// more new optional fields
'internal_reference', 'notes',
'create_date', 'update_date',
];
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
return view('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow'))
->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts)
->render();
}
/**
* @param Collection $accounts
*
* @return ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface
{
$this->accounts = $accounts;
return $this;
}
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setEndDate(Carbon $date): ReportGeneratorInterface
{
$this->end = $date;
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setStartDate(Carbon $date): ReportGeneratorInterface
{
$this->start = $date;
return $this;
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* MultiYearReportGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Audit;
/**
* Class MultiYearReportGenerator
*
* @package FireflyIII\Generator\Report\Audit
*/
class MultiYearReportGenerator extends MonthReportGenerator
{
/**
* Doesn't do anything different.
*/
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* YearReportGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Audit;
/**
* Class YearReportGenerator
*
* @package FireflyIII\Generator\Report\Audit
*/
class YearReportGenerator extends MonthReportGenerator
{
/**
* Doesn't do anything different.
*/
}

View File

@@ -0,0 +1,336 @@
<?php
/**
* MonthReportGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Category;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
use Log;
/**
* Class MonthReportGenerator
*
* @package FireflyIII\Generator\Report\Category
*/
class MonthReportGenerator extends Support implements ReportGeneratorInterface
{
/** @var Collection */
private $accounts;
/** @var Collection */
private $categories;
/** @var Carbon */
private $end;
/** @var Collection */
private $expenses;
/** @var Collection */
private $income;
/** @var Carbon */
private $start;
/**
* MonthReportGenerator constructor.
*/
public function __construct()
{
$this->income = new Collection;
$this->expenses = new Collection;
}
/**
* @return string
*/
public function generate(): string
{
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
$categoryIds = join(',', $this->categories->pluck('id')->toArray());
$reportType = 'category';
$expenses = $this->getExpenses();
$income = $this->getIncome();
$accountSummary = $this->getObjectSummary($this->summarizeByAccount($expenses), $this->summarizeByAccount($income));
$categorySummary = $this->getObjectSummary($this->summarizeByCategory($expenses), $this->summarizeByCategory($income));
$averageExpenses = $this->getAverages($expenses, SORT_ASC);
$averageIncome = $this->getAverages($income, SORT_DESC);
$topExpenses = $this->getTopExpenses();
$topIncome = $this->getTopIncome();
// render!
return view(
'reports.category.month',
compact(
'accountIds', 'categoryIds', 'topIncome', 'reportType', 'accountSummary', 'categorySummary', 'averageExpenses', 'averageIncome', 'topExpenses'
)
)
->with('start', $this->start)->with('end', $this->end)
->with('categories', $this->categories)
->with('accounts', $this->accounts)
->render();
}
/**
* @param Collection $accounts
*
* @return ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface
{
$this->accounts = $accounts;
return $this;
}
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
$this->categories = $categories;
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setEndDate(Carbon $date): ReportGeneratorInterface
{
$this->end = $date;
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setStartDate(Carbon $date): ReportGeneratorInterface
{
$this->start = $date;
return $this;
}
/**
* @param Collection $collection
* @param int $sortFlag
*
* @return array
*/
private function getAverages(Collection $collection, int $sortFlag): array
{
$result = [];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
// opposing name and ID:
$opposingId = $transaction->opposing_account_id;
// is not set?
if (!isset($result[$opposingId])) {
$name = $transaction->opposing_account_name;
$encrypted = intval($transaction->opposing_account_encrypted);
$name = $encrypted === 1 ? Crypt::decrypt($name) : $name;
$result[$opposingId] = [
'name' => $name,
'count' => 1,
'id' => $opposingId,
'average' => $transaction->transaction_amount,
'sum' => $transaction->transaction_amount,
];
continue;
}
$result[$opposingId]['count']++;
$result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount);
$result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count']));
}
// sort result by average:
$average = [];
foreach ($result as $key => $row) {
$average[$key] = floatval($row['average']);
}
array_multisort($average, $sortFlag, $result);
return $result;
}
/**
* @return Collection
*/
private function getExpenses(): Collection
{
if ($this->expenses->count() > 0) {
Log::debug('Return previous set of expenses.');
return $this->expenses;
}
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setCategories($this->categories)->withOpposingAccount()->disableFilter();
$accountIds = $this->accounts->pluck('id')->toArray();
$transactions = $collector->getJournals();
$transactions = self::filterExpenses($transactions, $accountIds);
$this->expenses = $transactions;
return $transactions;
}
/**
* @return Collection
*/
private function getIncome(): Collection
{
if ($this->income->count() > 0) {
return $this->income;
}
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->setCategories($this->categories)->withOpposingAccount();
$accountIds = $this->accounts->pluck('id')->toArray();
$transactions = $collector->getJournals();
$transactions = self::filterIncome($transactions, $accountIds);
$this->income = $transactions;
return $transactions;
}
/**
* @param array $spent
* @param array $earned
*
* @return array
*/
private function getObjectSummary(array $spent, array $earned): array
{
$return = [];
/**
* @var int $accountId
* @var string $entry
*/
foreach ($spent as $objectId => $entry) {
if (!isset($return[$objectId])) {
$return[$objectId] = ['spent' => 0, 'earned' => 0];
}
$return[$objectId]['spent'] = $entry;
}
unset($entry);
/**
* @var int $accountId
* @var string $entry
*/
foreach ($earned as $objectId => $entry) {
if (!isset($return[$objectId])) {
$return[$objectId] = ['spent' => 0, 'earned' => 0];
}
$return[$objectId]['earned'] = $entry;
}
return $return;
}
/**
* @return Collection
*/
private function getTopExpenses(): Collection
{
$transactions = $this->getExpenses()->sortBy('transaction_amount');
$transactions = $transactions->each(
function (Transaction $transaction) {
if (intval($transaction->opposing_account_encrypted) === 1) {
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
}
}
);
return $transactions;
}
/**
* @return Collection
*/
private function getTopIncome(): Collection
{
$transactions = $this->getIncome()->sortByDesc('transaction_amount');
$transactions = $transactions->each(
function (Transaction $transaction) {
if (intval($transaction->opposing_account_encrypted) === 1) {
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
}
}
);
return $transactions;
}
/**
* @param Collection $collection
*
* @return array
*/
private function summarizeByAccount(Collection $collection): array
{
$result = [];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$accountId = $transaction->account_id;
$result[$accountId] = $result[$accountId] ?? '0';
$result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]);
}
return $result;
}
/**
* @param Collection $collection
*
* @return array
*/
private function summarizeByCategory(Collection $collection): array
{
$result = [];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$jrnlCatId = intval($transaction->transaction_journal_category_id);
$transCatId = intval($transaction->transaction_category_id);
$categoryId = max($jrnlCatId, $transCatId);
$result[$categoryId] = $result[$categoryId] ?? '0';
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
}
return $result;
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* MultiYearReportGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Category;
/**
* Class MultiYearReportGenerator
*
* @package FireflyIII\Generator\Report\Audit
*/
class MultiYearReportGenerator extends MonthReportGenerator
{
/**
* Doesn't do anything different.
*/
}

View File

@@ -0,0 +1,91 @@
<?php
/**
* Support.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Category;
use FireflyIII\Models\Transaction;
use Illuminate\Support\Collection;
use Log;
/**
* Class Support
*
* @package FireflyIII\Generator\Report\Category
*/
class Support
{
/**
* @param Collection $collection
* @param array $accounts
*
* @return Collection
*/
public static function filterExpenses(Collection $collection, array $accounts): Collection
{
$result = $collection->filter(
function (Transaction $transaction) use ($accounts) {
$opposing = $transaction->opposing_account_id;
// remove internal transfer
if (in_array($opposing, $accounts)) {
Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id));
return null;
}
// remove positive amount
if (bccomp($transaction->transaction_amount, '0') === 1) {
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
return null;
}
return $transaction;
}
);
return $result;
}
/**
* @param Collection $collection
* @param array $accounts
*
* @return Collection
*/
public static function filterIncome(Collection $collection, array $accounts): Collection
{
$result = $collection->filter(
function (Transaction $transaction) use ($accounts) {
$opposing = $transaction->opposing_account_id;
// remove internal transfer
if (in_array($opposing, $accounts)) {
Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id));
return null;
}
// remove positive amount
if (bccomp($transaction->transaction_amount, '0') === -1) {
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
return null;
}
return $transaction;
}
);
return $result;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* YearReportGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Category;
/**
* Class YearReportGenerator
*
* @package FireflyIII\Generator\Report\Audit
*/
class YearReportGenerator extends MonthReportGenerator
{
/**
* Doesn't do anything different.
*/
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* ReportGeneratorFactory.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
/**
* Class ReportGeneratorFactory
*
* @package FireflyIII\Generator\Report
*/
class ReportGeneratorFactory
{
/**
* @param string $type
* @param Carbon $start
* @param Carbon $end
*
* @return ReportGeneratorInterface
* @throws FireflyException
*/
public static function reportGenerator(string $type, Carbon $start, Carbon $end): ReportGeneratorInterface
{
$period = 'Month';
// more than two months date difference means year report.
if ($start->diffInMonths($end) > 1) {
$period = 'Year';
}
// more than one year date difference means multi year report.
if ($start->diffInMonths($end) > 12) {
$period = 'MultiYear';
}
$class = sprintf('FireflyIII\Generator\Report\%s\%sReportGenerator', $type, $period);
if (class_exists($class)) {
/** @var ReportGeneratorInterface $obj */
$obj = new $class;
$obj->setStartDate($start);
$obj->setEndDate($end);
return $obj;
}
throw new FireflyException(sprintf('Cannot generate report. There is no "%s"-report for period "%s".', $type, $period));
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* ReportGeneratorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Interface ReportGeneratorInterface
*
* @package FireflyIII\Generator\Report
*/
interface ReportGeneratorInterface
{
/**
* @return string
*/
public function generate(): string;
/**
* @param Collection $accounts
*
* @return ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface;
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface;
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setEndDate(Carbon $date): ReportGeneratorInterface;
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setStartDate(Carbon $date): ReportGeneratorInterface;
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* MonthReportGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Standard;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use Illuminate\Support\Collection;
/**
* Class MonthReportGenerator
*
* @package FireflyIII\Generator\Report\Standard
*/
class MonthReportGenerator implements ReportGeneratorInterface
{
/** @var Collection */
private $accounts;
/** @var Carbon */
private $end;
/** @var Carbon */
private $start;
/**
* @return string
*/
public function generate(): string
{
/** @var ReportHelperInterface $helper */
$helper = app(ReportHelperInterface::class);
$bills = $helper->getBillReport($this->start, $this->end, $this->accounts);
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
$reportType = 'default';
// continue!
return view(
'reports.default.month',
compact('bills', 'accountIds', 'reportType')
)->with('start', $this->start)->with('end', $this->end)->render();
}
/**
* @param Collection $accounts
*
* @return ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface
{
$this->accounts = $accounts;
return $this;
}
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setEndDate(Carbon $date): ReportGeneratorInterface
{
$this->end = $date;
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setStartDate(Carbon $date): ReportGeneratorInterface
{
$this->start = $date;
return $this;
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* MultiYearReportGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Standard;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use Illuminate\Support\Collection;
/**
* Class MonthReportGenerator
*
* @package FireflyIII\Generator\Report\Standard
*/
class MultiYearReportGenerator implements ReportGeneratorInterface
{
/** @var Collection */
private $accounts;
/** @var Carbon */
private $end;
/** @var Carbon */
private $start;
/**
* @return string
*/
public function generate(): string
{
// and some id's, joined:
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
$reportType = 'default';
// continue!
return view(
'reports.default.multi-year',
compact('accountIds', 'reportType')
)->with('start', $this->start)->with('end', $this->end)->render();
}
/**
* @param Collection $accounts
*
* @return ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface
{
$this->accounts = $accounts;
return $this;
}
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setEndDate(Carbon $date): ReportGeneratorInterface
{
$this->end = $date;
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setStartDate(Carbon $date): ReportGeneratorInterface
{
$this->start = $date;
return $this;
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* YearReportGenerator.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Standard;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use Illuminate\Support\Collection;
/**
* Class MonthReportGenerator
*
* @package FireflyIII\Generator\Report\Standard
*/
class YearReportGenerator implements ReportGeneratorInterface
{
/** @var Collection */
private $accounts;
/** @var Carbon */
private $end;
/** @var Carbon */
private $start;
/**
* @return string
*/
public function generate(): string
{
// and some id's, joined:
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
$reportType = 'default';
// continue!
return view(
'reports.default.year',
compact('accountIds', 'reportType')
)->with('start', $this->start)->with('end', $this->end)->render();
}
/**
* @param Collection $accounts
*
* @return ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface
{
$this->accounts = $accounts;
return $this;
}
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setEndDate(Carbon $date): ReportGeneratorInterface
{
$this->end = $date;
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setStartDate(Carbon $date): ReportGeneratorInterface
{
$this->start = $date;
return $this;
}
}

View File

@@ -14,8 +14,10 @@ declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use Carbon\Carbon;
use FireflyIII\Events\StoredBudgetLimit;
use FireflyIII\Events\UpdatedBudgetLimit;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Database\QueryException;
use Log;
@@ -38,37 +40,9 @@ class BudgetEventHandler
*/
public function storeRepetition(StoredBudgetLimit $event):bool
{
$budgetLimit = $event->budgetLimit;
$end = $event->end;
$set = $budgetLimit->limitrepetitions()
->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00'))
->where('enddate', $end->format('Y-m-d 00:00:00'))
->get();
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $end;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);
try {
$repetition->save();
} catch (QueryException $e) {
Log::error('Trying to save new LimitRepetition failed: ' . $e->getMessage());
}
}
if ($set->count() == 1) {
$repetition = $set->first();
$repetition->amount = $budgetLimit->amount;
$repetition->save();
}
return true;
return $this->processRepetitionChange($event->budgetLimit, $event->end);
}
/**
* Updates, if present the budget limit repetition part of a budget limit.
*
@@ -78,16 +52,25 @@ class BudgetEventHandler
*/
public function updateRepetition(UpdatedBudgetLimit $event): bool
{
$budgetLimit = $event->budgetLimit;
$end = $event->end;
$set = $budgetLimit->limitrepetitions()
->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00'))
->where('enddate', $end->format('Y-m-d 00:00:00'))
->get();
return $this->processRepetitionChange($event->budgetLimit, $event->end);
}
/**
* @param BudgetLimit $budgetLimit
* @param Carbon $date
*
* @return bool
*/
private function processRepetitionChange(BudgetLimit $budgetLimit, Carbon $date):bool
{
$set = $budgetLimit->limitrepetitions()
->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00'))
->where('enddate', $date->format('Y-m-d 00:00:00'))
->get();
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $end;
$repetition->enddate = $date;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);

View File

@@ -14,10 +14,13 @@ declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use Exception;
use FireflyConfig;
use FireflyIII\Events\ConfirmedUser;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Events\ResentConfirmation;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Mail\Message;
use Log;
use Mail;
@@ -81,36 +84,7 @@ class UserEventHandler
*/
public function sendConfirmationMessage(RegisteredUser $event): bool
{
$user = $event->user;
$ipAddress = $event->ipAddress;
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
if ($confirmAccount === false) {
Preferences::setForUser($user, 'user_confirmed', true);
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
Preferences::mark();
return true;
}
$email = $user->email;
$code = str_random(16);
$route = route('do_confirm_account', [$code]);
Preferences::setForUser($user, 'user_confirmed', false);
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
Preferences::setForUser($user, 'user_confirmed_code', $code);
try {
Mail::send(
['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress],
function (Message $message) use ($email) {
$message->to($email, $email)->subject('Please confirm your Firefly III account');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
} catch (Exception $e) {
Log::error($e->getMessage());
}
return true;
return $this->sendConfirmation($event->user, $event->ipAddress);
}
/**
@@ -124,37 +98,35 @@ class UserEventHandler
*/
function sendConfirmationMessageAgain(ResentConfirmation $event): bool
{
$user = $event->user;
$ipAddress = $event->ipAddress;
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
if ($confirmAccount === false) {
Preferences::setForUser($user, 'user_confirmed', true);
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
Preferences::mark();
return $this->sendConfirmation($event->user, $event->ipAddress);
return true;
}
$email = $user->email;
$code = str_random(16);
$route = route('do_confirm_account', [$code]);
Preferences::setForUser($user, 'user_confirmed', false);
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
Preferences::setForUser($user, 'user_confirmed_code', $code);
}
/**
* @param RequestedNewPassword $event
*
* @return bool
*/
public function sendNewPassword(RequestedNewPassword $event): bool
{
$email = $event->user->email;
$ipAddress = $event->ipAddress;
$token = $event->token;
$url = route('password.reset', [$token]);
// send email.
try {
Mail::send(
['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress],
function (Message $message) use ($email) {
$message->to($email, $email)->subject('Please confirm your Firefly III account');
}
['emails.password-html', 'emails.password-text'], ['url' => $url, 'ip' => $ipAddress], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Your password reset request');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
} catch (Exception $e) {
Log::error($e->getMessage());
}
return true;
}
/**
@@ -179,7 +151,7 @@ class UserEventHandler
// send email.
try {
Mail::send(
['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
['emails.registered-html', 'emails.registered-text'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
}
);
@@ -222,4 +194,43 @@ class UserEventHandler
}
/**
* @param User $user
* @param string $ipAddress
*
* @return bool
*/
private function sendConfirmation(User $user, string $ipAddress): bool
{
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
if ($mustConfirmAccount === false) {
Preferences::setForUser($user, 'user_confirmed', true);
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
Preferences::mark();
return true;
}
$email = $user->email;
$code = str_random(16);
$route = route('do_confirm_account', [$code]);
Preferences::setForUser($user, 'user_confirmed', false);
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
Preferences::setForUser($user, 'user_confirmed_code', $code);
try {
Mail::send(
['emails.confirm-account-html', 'emails.confirm-account-text'], ['route' => $route, 'ip' => $ipAddress],
function (Message $message) use ($email) {
$message->to($email, $email)->subject('Please confirm your Firefly III account');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
} catch (Exception $e) {
Log::error($e->getMessage());
}
return true;
}
}

View File

@@ -13,7 +13,10 @@ declare(strict_types = 1);
namespace FireflyIII\Helpers\Collection;
use Carbon\Carbon;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
/**
* Class Bill
@@ -26,7 +29,11 @@ class Bill
/**
* @var Collection
*/
protected $bills;
private $bills;
/** @var Carbon */
private $endDate;
/** @var Carbon */
private $startDate;
/**
*
@@ -44,6 +51,43 @@ class Bill
$this->bills->push($bill);
}
/**
*
*/
public function filterBills()
{
Log::debug('Now in filterBills()');
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$start = $this->startDate;
$end = $this->endDate;
$lines = $this->bills->filter(
function (BillLine $line) use ($repository, $start, $end) {
// next expected match?
$date = $start;
Log::debug(sprintf('Now at bill line for bill "%s"', $line->getBill()->name));
Log::debug(sprintf('Default date to use is start date: %s', $date->format('Y-m-d')));
if ($line->isHit()) {
$date = $line->getLastHitDate();
Log::debug(sprintf('Line was hit, see date: %s. Always include it.', $date->format('Y-m-d')));
return $line;
}
$expected = $repository->nextExpectedMatch($line->getBill(), $date);
Log::debug(sprintf('Next expected match is %s', $expected->format('Y-m-d')));
if ($expected <= $end && $expected >= $start) {
Log::debug('This date is inside report limits');
return $line;
}
Log::debug('This date is OUTSIDE report limits');
return false;
}
);
$this->bills = $lines;
}
/**
* @return Collection
*/
@@ -62,4 +106,20 @@ class Bill
return $set;
}
/**
* @param Carbon $endDate
*/
public function setEndDate(Carbon $endDate)
{
$this->endDate = $endDate;
}
/**
* @param Carbon $startDate
*/
public function setStartDate(Carbon $startDate)
{
$this->startDate = $startDate;
}
}

View File

@@ -12,6 +12,7 @@
declare(strict_types = 1);
namespace FireflyIII\Helpers\Collection;
use Carbon\Carbon;
use FireflyIII\Models\Bill as BillModel;
/**
@@ -23,8 +24,6 @@ use FireflyIII\Models\Bill as BillModel;
class BillLine
{
/** @var bool */
protected $active;
/** @var string */
protected $amount;
/** @var BillModel */
@@ -35,10 +34,19 @@ class BillLine
protected $max;
/** @var string */
protected $min;
/** @var Carbon */
private $lastHitDate;
/** @var int */
private $transactionJournalId;
/**
* BillLine constructor.
*/
public function __construct()
{
$this->lastHitDate = new Carbon;
}
/**
* @return string
*/
@@ -124,15 +132,7 @@ class BillLine
*/
public function isActive(): bool
{
return $this->active;
}
/**
* @param bool $active
*/
public function setActive(bool $active)
{
$this->active = $active;
return intval($this->bill->active) === 1;
}
/**
@@ -151,4 +151,21 @@ class BillLine
$this->hit = $hit;
}
/**
* @param Carbon $lastHitDate
*/
public function setLastHitDate(Carbon $lastHitDate)
{
$this->lastHitDate = $lastHitDate;
}
/**
* @return Carbon
*/
public function getLastHitDate(): Carbon
{
return $this->lastHitDate;
}
}

View File

@@ -6,6 +6,7 @@ namespace FireflyIII\Helpers\Collector;
use Carbon\Carbon;
use Crypt;
use DB;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
@@ -16,6 +17,7 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
@@ -27,12 +29,13 @@ use Log;
*
* @package FireflyIII\Helpers\Collector
*/
class JournalCollector
class JournalCollector implements JournalCollectorInterface
{
/** @var array */
private $accountIds = [];
/** @var int */
private $count = 0;
/** @var array */
private $fields
= [
@@ -63,6 +66,8 @@ class JournalCollector
/** @var bool */
private $joinedCategory = false;
/** @var bool */
private $joinedOpposing = false;
/** @var bool */
private $joinedTag = false;
/** @var int */
private $limit;
@@ -112,6 +117,16 @@ class JournalCollector
return $this->count;
}
/**
* @return JournalCollectorInterface
*/
public function disableFilter(): JournalCollectorInterface
{
$this->filterTransfers = false;
return $this;
}
/**
* @return Collection
*/
@@ -119,7 +134,9 @@ class JournalCollector
{
$this->run = true;
$set = $this->query->get(array_values($this->fields));
$set = $this->filterTransfers($set);
Log::debug(sprintf('Count of set is %d', $set->count()));
$set = $this->filterTransfers($set);
Log::debug(sprintf('Count of set after filterTransfers() is %d', $set->count()));
// loop for decryption.
$set->each(
@@ -137,7 +154,7 @@ class JournalCollector
* @return LengthAwarePaginator
* @throws FireflyException
*/
public function getPaginatedJournals():LengthAwarePaginator
public function getPaginatedJournals(): LengthAwarePaginator
{
if ($this->run === true) {
throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.');
@@ -152,26 +169,29 @@ class JournalCollector
/**
* @param Collection $accounts
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setAccounts(Collection $accounts): JournalCollector
public function setAccounts(Collection $accounts): JournalCollectorInterface
{
if ($accounts->count() > 0) {
$accountIds = $accounts->pluck('id')->toArray();
$this->query->whereIn('transactions.account_id', $accountIds);
Log::debug(sprintf('setAccounts: %s', join(', ', $accountIds)));
$this->accountIds = $accountIds;
}
if ($accounts->count() > 1) {
$this->filterTransfers = true;
}
return $this;
}
/**
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setAllAssetAccounts(): JournalCollector
public function setAllAssetAccounts(): JournalCollectorInterface
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class, [$this->user]);
@@ -179,6 +199,7 @@ class JournalCollector
if ($accounts->count() > 0) {
$accountIds = $accounts->pluck('id')->toArray();
$this->query->whereIn('transactions.account_id', $accountIds);
$this->accountIds = $accountIds;
}
if ($accounts->count() > 1) {
@@ -191,9 +212,9 @@ class JournalCollector
/**
* @param Collection $bills
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setBills(Collection $bills): JournalCollector
public function setBills(Collection $bills): JournalCollectorInterface
{
if ($bills->count() > 0) {
$billIds = $bills->pluck('id')->toArray();
@@ -207,9 +228,9 @@ class JournalCollector
/**
* @param Budget $budget
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setBudget(Budget $budget): JournalCollector
public function setBudget(Budget $budget): JournalCollectorInterface
{
$this->joinBudgetTables();
@@ -223,12 +244,58 @@ class JournalCollector
return $this;
}
/**
* @param Collection $budgets
*
* @return JournalCollectorInterface
*/
public function setBudgets(Collection $budgets): JournalCollectorInterface
{
$budgetIds = $budgets->pluck('id')->toArray();
if (count($budgetIds) === 0) {
return $this;
}
$this->joinBudgetTables();
$this->query->where(
function (EloquentBuilder $q) use ($budgetIds) {
$q->whereIn('budget_transaction.budget_id', $budgetIds);
$q->orWhereIn('budget_transaction_journal.budget_id', $budgetIds);
}
);
return $this;
}
/**
* @param Collection $categories
*
* @return JournalCollectorInterface
*/
public function setCategories(Collection $categories): JournalCollectorInterface
{
$categoryIds = $categories->pluck('id')->toArray();
if (count($categoryIds) === 0) {
return $this;
}
$this->joinCategoryTables();
$this->query->where(
function (EloquentBuilder $q) use ($categoryIds) {
$q->whereIn('category_transaction.category_id', $categoryIds);
$q->orWhereIn('category_transaction_journal.category_id', $categoryIds);
}
);
return $this;
}
/**
* @param Category $category
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setCategory(Category $category): JournalCollector
public function setCategory(Category $category): JournalCollectorInterface
{
$this->joinCategoryTables();
@@ -245,9 +312,9 @@ class JournalCollector
/**
* @param int $limit
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setLimit(int $limit): JournalCollector
public function setLimit(int $limit): JournalCollectorInterface
{
$this->limit = $limit;
$this->query->limit($limit);
@@ -259,9 +326,9 @@ class JournalCollector
/**
* @param int $offset
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setOffset(int $offset): JournalCollector
public function setOffset(int $offset): JournalCollectorInterface
{
$this->offset = $offset;
@@ -271,9 +338,9 @@ class JournalCollector
/**
* @param int $page
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setPage(int $page): JournalCollector
public function setPage(int $page): JournalCollectorInterface
{
$this->page = $page;
@@ -299,9 +366,9 @@ class JournalCollector
* @param Carbon $start
* @param Carbon $end
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setRange(Carbon $start, Carbon $end): JournalCollector
public function setRange(Carbon $start, Carbon $end): JournalCollectorInterface
{
if ($start <= $end) {
$this->query->where('transaction_journals.date', '>=', $start->format('Y-m-d'));
@@ -314,9 +381,9 @@ class JournalCollector
/**
* @param Tag $tag
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setTag(Tag $tag): JournalCollector
public function setTag(Tag $tag): JournalCollectorInterface
{
$this->joinTagTables();
$this->query->where('tag_transaction_journal.tag_id', $tag->id);
@@ -327,11 +394,12 @@ class JournalCollector
/**
* @param array $types
*
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function setTypes(array $types): JournalCollector
public function setTypes(array $types): JournalCollectorInterface
{
if (count($types) > 0) {
Log::debug('Set query collector types', $types);
$this->query->whereIn('transaction_types.type', $types);
}
@@ -339,9 +407,70 @@ class JournalCollector
}
/**
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function withoutBudget(): JournalCollector
public function withBudgetInformation(): JournalCollectorInterface
{
$this->joinBudgetTables();
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function withCategoryInformation(): JournalCollectorInterface
{
$this->joinCategoryTables();
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function withOpposingAccount(): JournalCollectorInterface
{
$this->joinOpposingTables();
$accountIds = $this->accountIds;
$this->query->where(
function (EloquentBuilder $q1) use ($accountIds) {
// set 1:
// where source is in the set of $accounts
// but destination is not.
$q1->where(
function (EloquentBuilder $q2) use ($accountIds) {
// transactions.account_id in set
$q2->whereIn('transactions.account_id', $accountIds);
// opposing.account_id not in set
$q2->whereNotIn('opposing.account_id', $accountIds);
}
);
// set 1:
// where source is not in the set of $accounts
// but destination is.
$q1->orWhere(
function (EloquentBuilder $q3) use ($accountIds) {
// transactions.account_id not in set
$q3->whereNotIn('transactions.account_id', $accountIds);
// B in set
// opposing.account_id not in set
$q3->whereIn('opposing.account_id', $accountIds);
}
);
}
);
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function withoutBudget(): JournalCollectorInterface
{
$this->joinBudgetTables();
@@ -356,9 +485,9 @@ class JournalCollector
}
/**
* @return JournalCollector
* @return JournalCollectorInterface
*/
public function withoutCategory(): JournalCollector
public function withoutCategory(): JournalCollectorInterface
{
$this->joinCategoryTables();
@@ -389,6 +518,7 @@ class JournalCollector
$set = $set->filter(
function (Transaction $transaction) {
if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) {
Log::debug(
sprintf(
'Included journal #%d (transaction #%d) because its a %s with amount %f',
@@ -430,6 +560,8 @@ class JournalCollector
$this->joinedBudget = true;
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
$this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
$this->fields[] = 'budget_transaction_journal.budget_id as transaction_journal_budget_id';
$this->fields[] = 'budget_transaction.budget_id as transaction_budget_id';
}
}
@@ -443,6 +575,31 @@ class JournalCollector
$this->joinedCategory = true;
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
$this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id');
$this->fields[] = 'category_transaction_journal.category_id as transaction_journal_category_id';
$this->fields[] = 'category_transaction.category_id as transaction_category_id';
}
}
private function joinOpposingTables()
{
if (!$this->joinedOpposing) {
// join opposing transaction (hard):
$this->query->leftJoin(
'transactions as opposing', function (JoinClause $join) {
$join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id')
->where('opposing.identifier', '=', DB::raw('transactions.identifier'))
->where('opposing.amount', '=', DB::raw('transactions.amount * -1'));
}
);
$this->query->leftJoin('accounts as opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id');
$this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id');
$this->query->whereNull('opposing.deleted_at');
$this->fields[] = 'opposing.account_id as opposing_account_id';
$this->fields[] = 'opposing_accounts.name as opposing_account_name';
$this->fields[] = 'opposing_accounts.encrypted as opposing_account_encrypted';
$this->fields[] = 'opposing_account_types.type as opposing_account_type';
}
}
@@ -481,5 +638,4 @@ class JournalCollector
return $query;
}
}

View File

@@ -0,0 +1,166 @@
<?php
/**
* JournalCollectorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Collector;
use Carbon\Carbon;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
/**
* Interface JournalCollectorInterface
*
* @package FireflyIII\Helpers\Collector
*/
interface JournalCollectorInterface
{
/**
* @return int
*/
public function count(): int;
/**
* @return JournalCollectorInterface
*/
public function disableFilter(): JournalCollectorInterface;
/**
* @return Collection
*/
public function getJournals(): Collection;
/**
* @return LengthAwarePaginator
*/
public function getPaginatedJournals():LengthAwarePaginator;
/**
* @param Collection $accounts
*
* @return JournalCollectorInterface
*/
public function setAccounts(Collection $accounts): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function setAllAssetAccounts(): JournalCollectorInterface;
/**
* @param Collection $bills
*
* @return JournalCollectorInterface
*/
public function setBills(Collection $bills): JournalCollectorInterface;
/**
* @param Budget $budget
*
* @return JournalCollectorInterface
*/
public function setBudget(Budget $budget): JournalCollectorInterface;
/**
* @param Collection $budgets
*
* @return JournalCollectorInterface
*/
public function setBudgets(Collection $budgets): JournalCollectorInterface;
/**
* @param Collection $categories
*
* @return JournalCollectorInterface
*/
public function setCategories(Collection $categories): JournalCollectorInterface;
/**
* @param Category $category
*
* @return JournalCollectorInterface
*/
public function setCategory(Category $category): JournalCollectorInterface;
/**
* @param int $limit
*
* @return JournalCollectorInterface
*/
public function setLimit(int $limit): JournalCollectorInterface;
/**
* @param int $offset
*
* @return JournalCollectorInterface
*/
public function setOffset(int $offset): JournalCollectorInterface;
/**
* @param int $page
*
* @return JournalCollectorInterface
*/
public function setPage(int $page): JournalCollectorInterface;
/**
* @param Carbon $start
* @param Carbon $end
*
* @return JournalCollectorInterface
*/
public function setRange(Carbon $start, Carbon $end): JournalCollectorInterface;
/**
* @param Tag $tag
*
* @return JournalCollectorInterface
*/
public function setTag(Tag $tag): JournalCollectorInterface;
/**
* @param array $types
*
* @return JournalCollectorInterface
*/
public function setTypes(array $types): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function withBudgetInformation(): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function withCategoryInformation(): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function withOpposingAccount(): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function withoutBudget(): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function withoutCategory(): JournalCollectorInterface;
}

View File

@@ -15,17 +15,12 @@ namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use DB;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\BudgetLine;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use stdClass;
/**
* Class BudgetReportHelper
@@ -47,60 +42,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface
$this->repository = $repository;
}
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // at 43, its ok.
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Collection
*/
public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-year');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$current = clone $start;
$return = new Collection;
$set = $this->repository->getBudgets();
$budgets = [];
$spent = [];
$headers = $this->createYearHeaders($current, $end);
/** @var Budget $budget */
foreach ($set as $budget) {
$id = $budget->id;
$budgets[$id] = $budget->name;
$current = clone $start;
$budgetData = $this->getBudgetSpentData($current, $end, $budget, $accounts);
$sum = $budgetData['sum'];
$spent[$id] = $budgetData['spent'];
if (bccomp('0', $sum) === 0) {
// not spent anything.
unset($spent[$id]);
unset($budgets[$id]);
}
}
$return->put('headers', $headers);
$return->put('budgets', $budgets);
$return->put('spent', $spent);
$cache->store($return);
return $return;
}
/**
* @param Carbon $start
* @param Carbon $end
@@ -108,55 +49,11 @@ class BudgetReportHelper implements BudgetReportHelperInterface
*
* @return array
*/
public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array
{
$accountIds = $accounts->pluck('id')->toArray();
$query = TransactionJournal
::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->where('transaction_types.type', 'Withdrawal')
->where('transaction_journals.user_id', auth()->user()->id);
if (count($accountIds) > 0) {
$query->whereIn('transactions.account_id', $accountIds);
}
$query->groupBy(['budget_transaction_journal.budget_id', 'the_year']);
$queryResult = $query->get(
[
'budget_transaction_journal.budget_id',
DB::raw('DATE_FORMAT(transaction_journals.date,"%Y") AS the_year'),
DB::raw('SUM(transactions.amount) as sum_of_period'),
]
);
$data = [];
$budgets = $this->repository->getBudgets();
$years = $this->listOfYears($start, $end);
// do budget "zero"
$emptyBudget = new Budget;
$emptyBudget->id = 0;
$emptyBudget->name = strval(trans('firefly.no_budget'));
$budgets->push($emptyBudget);
// get all budgets and years.
foreach ($budgets as $budget) {
$data[$budget->id] = [
'name' => $budget->name,
'entries' => $this->filterAmounts($queryResult, $budget->id, $years),
'sum' => '0',
];
}
// filter out empty ones and fill sum:
$data = $this->getBudgetMultiYearMeta($data);
$report = $this->repository->getBudgetPeriodReport($budgets, $accounts, $start, $end);
$data = $this->filterBudgetPeriodReport($report);
return $data;
}
@@ -246,24 +143,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface
return $set;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function listOfYears(Carbon $start, Carbon $end): array
{
$begin = clone $start;
$years = [];
while ($begin < $end) {
$years[] = $begin->year;
$begin->addYear();
}
return $years;
}
/**
* @param Budget $budget
* @param LimitRepetition $repetition
@@ -285,57 +164,13 @@ class BudgetReportHelper implements BudgetReportHelperInterface
}
/**
* @param Carbon $current
* @param Carbon $end
* Filters empty results from getBudgetPeriodReport
*
* @return array
*/
private function createYearHeaders(Carbon $current, Carbon $end): array
{
$headers = [];
while ($current < $end) {
$short = $current->format('m-Y');
$headers[$short] = $current->formatLocalized((string)trans('config.month'));
$current->addMonth();
}
return $headers;
}
/**
* @param Collection $set
* @param int $budgetId
* @param array $years
*
* @return array
*/
private function filterAmounts(Collection $set, int $budgetId, array $years):array
{
$arr = [];
foreach ($years as $year) {
/** @var stdClass $object */
$result = $set->filter(
function (TransactionJournal $object) use ($budgetId, $year) {
return intval($object->the_year) === $year && $budgetId === intval($object->budget_id);
}
);
$amount = '0';
if (!is_null($result->first())) {
$amount = $result->first()->sum_of_period;
}
$arr[$year] = $amount;
}
return $arr;
}
/**
* @param array $data
*
* @return array
*/
private function getBudgetMultiYearMeta(array $data): array
private function filterBudgetPeriodReport(array $data): array
{
/**
* @var int $budgetId
@@ -354,32 +189,4 @@ class BudgetReportHelper implements BudgetReportHelperInterface
return $data;
}
/**
* @param Carbon $current
* @param Carbon $end
* @param Budget $budget
* @param Collection $accounts
*
* @return array
*/
private function getBudgetSpentData(Carbon $current, Carbon $end, Budget $budget, Collection $accounts): array
{
$sum = '0';
$spent = [];
while ($current < $end) {
$currentEnd = clone $current;
$currentEnd->endOfMonth();
$format = $current->format('m-Y');
$budgetSpent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $current, $currentEnd);
$spent[$format] = $budgetSpent;
$sum = bcadd($sum, $budgetSpent);
$current->addMonth();
}
return [
'spent' => $spent,
'sum' => $sum,
];
}
}

View File

@@ -25,14 +25,6 @@ use Illuminate\Support\Collection;
*/
interface BudgetReportHelperInterface
{
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Collection
*/
public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection;
/**
* @param Carbon $start
@@ -41,7 +33,7 @@ interface BudgetReportHelperInterface
*
* @return array
*/
public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array;
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array;
/**
* @param Carbon $start
@@ -61,12 +53,4 @@ interface BudgetReportHelperInterface
*/
public function getBudgetsWithExpenses(Carbon $start, Carbon $end, Collection $accounts): Collection;
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function listOfYears(Carbon $start, Carbon $end): array;
}

View File

@@ -19,19 +19,15 @@ use FireflyIII\Helpers\Collection\BillLine;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\FiscalHelperInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use stdClass;
@@ -45,20 +41,16 @@ class ReportHelper implements ReportHelperInterface
/** @var BudgetRepositoryInterface */
protected $budgetRepository;
/** @var TagRepositoryInterface */
protected $tagRepository;
/**
* ReportHelper constructor.
*
*
* @param BudgetRepositoryInterface $budgetRepository
* @param TagRepositoryInterface $tagRepository
*/
public function __construct(BudgetRepositoryInterface $budgetRepository, TagRepositoryInterface $tagRepository)
public function __construct(BudgetRepositoryInterface $budgetRepository)
{
$this->budgetRepository = $budgetRepository;
$this->tagRepository = $tagRepository;
}
/**
@@ -78,16 +70,17 @@ class ReportHelper implements ReportHelperInterface
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$bills = $repository->getBillsForAccounts($accounts);
$collector = new JournalCollector(auth()->user());
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($accounts)->setRange($start, $end)->setBills($bills);
$journals = $collector->getJournals();
$collection = new BillCollection;
$collection->setStartDate($start);
$collection->setEndDate($end);
/** @var Bill $bill */
foreach ($bills as $bill) {
$billLine = new BillLine;
$billLine->setBill($bill);
$billLine->setActive(intval($bill->active) === 1);
$billLine->setMin(strval($bill->amount_min));
$billLine->setMax(strval($bill->amount_max));
$billLine->setHit(false);
@@ -102,15 +95,19 @@ class ReportHelper implements ReportHelperInterface
if (!is_null($first)) {
$billLine->setTransactionJournalId($first->id);
$billLine->setAmount($first->transaction_amount);
$billLine->setLastHitDate($first->date);
$billLine->setHit(true);
}
// non active AND non hit? do not add:
// bill is active, or bill is hit:
if ($billLine->isActive() || $billLine->isHit()) {
$collection->addBill($billLine);
}
}
// do some extra filtering.
$collection->filterBills();
return $collection;
}
@@ -234,78 +231,4 @@ class ReportHelper implements ReportHelperInterface
return $months;
}
/**
* Returns an array of tags and their comparitive size with amounts bla bla.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array
{
$ids = $accounts->pluck('id')->toArray();
$set = Tag::
leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id')
->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin(
'transactions AS source', function (JoinClause $join) {
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0');
}
)
->leftJoin(
'transactions AS destination', function (JoinClause $join) {
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', '0');
}
)
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where(
// source.account_id in accountIds XOR destination.account_id in accountIds
function (Builder $query) use ($ids) {
$query->where(
function (Builder $q1) use ($ids) {
$q1->whereIn('source.account_id', $ids)
->whereNotIn('destination.account_id', $ids);
}
)->orWhere(
function (Builder $q2) use ($ids) {
$q2->whereIn('destination.account_id', $ids)
->whereNotIn('source.account_id', $ids);
}
);
}
)
->get(['tags.id', 'tags.tag', 'transaction_journals.id as journal_id', 'destination.amount']);
$collection = [];
if ($set->count() === 0) {
return $collection;
}
/** @var Tag $entry */
foreach ($set as $entry) {
// less than zero? multiply to be above zero.
$amount = $entry->amount;
$id = intval($entry->id);
$previousAmount = $collection[$id]['amount'] ?? '0';
$collection[$id] = [
'id' => $id,
'tag' => $entry->tag,
'amount' => bcadd($previousAmount, $amount),
];
}
// cleanup collection (match "fonts")
$max = strval(max(array_column($collection, 'amount')));
foreach ($collection as $id => $entry) {
$size = bcdiv($entry['amount'], $max, 4);
if (bccomp($size, '0.25') === -1) {
$size = '0.5';
}
$collection[$id]['fontsize'] = $size;
}
return $collection;
}
}

View File

@@ -80,15 +80,4 @@ interface ReportHelperInterface
*/
public function listOfMonths(Carbon $date): array;
/**
* Returns an array of tags and their comparitive size with amounts bla bla.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array;
}

View File

@@ -13,19 +13,24 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Amount;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Input;
use Log;
use Navigation;
use Preferences;
use Session;
@@ -65,9 +70,18 @@ class AccountController extends Controller
*/
public function create(string $what = 'asset')
{
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
$subTitle = trans('firefly.make_new_' . $what . '_account');
Session::flash('preFilled', []);
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$currencies = ExpandedForm::makeSelectList($repository->get());
$defaultCurrency = Amount::getDefaultCurrency();
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
$subTitle = trans('firefly.make_new_' . $what . '_account');
Session::flash(
'preFilled',
[
'currency_id' => $defaultCurrency->id,
]
);
// put previous url in session if not redirect from store (not "create another").
if (session('accounts.create.fromStore') !== true) {
@@ -77,7 +91,7 @@ class AccountController extends Controller
Session::flash('gaEventCategory', 'accounts');
Session::flash('gaEventAction', 'create-' . $what);
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle'));
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencies'));
}
@@ -134,6 +148,9 @@ class AccountController extends Controller
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$currencies = ExpandedForm::makeSelectList($repository->get());
// put previous url in session if not redirect from store (not "return_to_edit").
if (session('accounts.edit.fromUpdate') !== true) {
@@ -157,12 +174,13 @@ class AccountController extends Controller
'openingBalanceDate' => $openingBalanceDate,
'openingBalance' => $openingBalanceAmount,
'virtualBalance' => $account->virtual_balance,
'currency_id' => $account->getMeta('currency_id'),
];
Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'accounts');
Session::flash('gaEventAction', 'edit-' . $what);
return view('accounts.edit', compact('account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what'));
return view('accounts.edit', compact('currencies', 'account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what'));
}
/**
@@ -202,13 +220,12 @@ class AccountController extends Controller
}
/**
* @param AccountTaskerInterface $tasker
* @param ARI $repository
* @param Account $account
* @param JournalCollectorInterface $collector
* @param Account $account
*
* @return View
*/
public function show(AccountTaskerInterface $tasker, ARI $repository, Account $account)
public function show(JournalCollectorInterface $collector, Account $account)
{
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
return $this->redirectToOriginalAccount($account);
@@ -217,61 +234,45 @@ class AccountController extends Controller
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$subTitle = $account->name;
$range = Preferences::get('viewRange', '1M')->data;
/** @var Carbon $start */
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
/** @var Carbon $end */
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// grab those journals:
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id);
// generate entries for each period (and cache those)
$entries = $this->periodEntries($account);
return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end'));
}
/**
* @param ARI $repository
* @param Account $account
*
* @return View
*/
public function showAll(AccountRepositoryInterface $repository, Account $account)
{
$subTitle = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything')));
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// replace with journal collector:
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id);
$journals->setPath('accounts/show/' . $account->id . '/all');
// grouped other months thing:
// oldest transaction in account:
$start = $repository->oldestJournalDate($account);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
// get oldest and newest journal for account:
$start = $repository->oldestJournalDate($account);
$end = $repository->newestJournalDate($account);
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('account-show');
$cache->addProperty($account->id);
if ($cache->has()) {
$entries = $cache->get();
return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle'));
}
// only include asset accounts when this account is an asset:
$assets = new Collection;
if (in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEFAULT])) {
$assets = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
}
while ($end >= $start) {
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
$spent = $tasker->amountOutInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
$earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned]);
$end = Navigation::subtractPeriod($end, $range, 1);
}
$cache->store($entries);
return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle'));
return view('accounts.show_with_date', compact('account', 'journals', 'subTitle', 'start', 'end'));
}
/**
@@ -296,7 +297,7 @@ class AccountController extends Controller
$journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id . '/' . $date);
return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon'));
return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon', 'start', 'end'));
}
/**
@@ -375,6 +376,63 @@ class AccountController extends Controller
return '';
}
/**
* This method returns "period entries", so nov-2015, dec-2015, etc etc (this depends on the users session range)
* and for each period, the amount of money spent and earned. This is a complex operation which is cached for
* performance reasons.
*
* @param Account $account The account involved.
*
* @return Collection
*/
private function periodEntries(Account $account): Collection
{
/** @var ARI $repository */
$repository = app(ARI::class);
/** @var AccountTaskerInterface $tasker */
$tasker = app(AccountTaskerInterface::class);
$start = $repository->oldestJournalDate($account);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('account-show-period-entries');
$cache->addProperty($account->id);
if ($cache->has()) {
Log::debug('Entries are cached, return cache.');
return $cache->get();
}
// only include asset accounts when this account is an asset:
$assets = new Collection;
if (in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEFAULT])) {
$assets = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
}
Log::debug('Going to get period expenses and incomes.');
while ($end >= $start) {
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
$spent = $tasker->amountOutInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
$earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd);
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned]);
$end = Navigation::subtractPeriod($end, $range, 1);
}
$cache->store($entries);
return $entries;
}
/**
* @param Account $account
*

View File

@@ -14,7 +14,6 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Admin;
use Config;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\ConfigurationRequest;
use FireflyIII\Support\Facades\FireflyConfig;
@@ -59,9 +58,11 @@ class ConfigurationController extends Controller
// all available configuration and their default value in case
// they don't exist yet.
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
return view('admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode'));
return view('admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode', 'mustConfirmAccount', 'isDemoSite'));
}
@@ -77,6 +78,8 @@ class ConfigurationController extends Controller
// store config values
FireflyConfig::set('single_user_mode', $data['single_user_mode']);
FireflyConfig::set('must_confirm_account', $data['must_confirm_account']);
FireflyConfig::set('is_demo_site', $data['is_demo_site']);
// flash message
Session::flash('success', strval(trans('firefly.configuration_updated')));

View File

@@ -24,7 +24,7 @@ use FireflyIII\Http\Controllers\Controller;
class HomeController extends Controller
{
/**
* @return mixed
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index()
{

View File

@@ -14,6 +14,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Admin;
use FireflyConfig;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
@@ -46,30 +47,32 @@ class UserController extends Controller
*/
public function index(UserRepositoryInterface $repository)
{
$title = strval(trans('firefly.administration'));
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = strval(trans('firefly.user_administration'));
$subTitleIcon = 'fa-users';
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
$users = $repository->all();
$title = strval(trans('firefly.administration'));
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = strval(trans('firefly.user_administration'));
$subTitleIcon = 'fa-users';
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
$users = $repository->all();
// add meta stuff.
$users->each(
function (User $user) use ($confirmAccount) {
// is user activated?
$isConfirmed = Preferences::getForUser($user, 'user_confirmed', false)->data;
function (User $user) use ($mustConfirmAccount) {
$list = ['user_confirmed', 'twoFactorAuthEnabled', 'twoFactorAuthSecret', 'registration_ip_address', 'confirmation_ip_address'];
$preferences = Preferences::getArrayForUser($user, $list);
$user->activated = true;
if ($isConfirmed === false && $confirmAccount === true) {
if (!($preferences['user_confirmed'] === true) && $mustConfirmAccount === true) {
$user->activated = false;
}
$user->isAdmin = $user->hasRole('owner');
$is2faEnabled = Preferences::getForUser($user, 'twoFactorAuthEnabled', false)->data;
$has2faSecret = !is_null(Preferences::getForUser($user, 'twoFactorAuthSecret'));
$is2faEnabled = $preferences['twoFactorAuthEnabled'] === true;
$has2faSecret = !is_null($preferences['twoFactorAuthSecret']);
$user->has2FA = false;
if ($is2faEnabled && $has2faSecret) {
$user->has2FA = true;
}
$user->prefs = $preferences;
}
);

View File

@@ -57,7 +57,7 @@ class AttachmentController extends Controller
/**
* @param Attachment $attachment
*
* @return View
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
public function delete(Attachment $attachment)
{

View File

@@ -75,7 +75,8 @@ class LoginController extends Controller
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($lockedOut = $this->hasTooManyLoginAttempts($request)) {
$lockedOut = $this->hasTooManyLoginAttempts($request);
if ($lockedOut) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
@@ -188,7 +189,7 @@ class LoginController extends Controller
];
Mail::send(
['emails.blocked-login-html', 'emails.blocked-login'], $fields, function (Message $message) use ($email, $user) {
['emails.blocked-login-html', 'emails.blocked-login-text'], $fields, function (Message $message) use ($email, $user) {
$message->to($email, $email)->subject('Blocked a login attempt from ' . trim($user->email) . '.');
}
);

View File

@@ -123,7 +123,11 @@ class RegisterController extends Controller
*/
public function showRegistrationForm(Request $request)
{
$showDemoWarning = config('firefly.show-demo-warning', false);
// is demo site?
$isDemoSite = FireflyConfig::get('is_demo_site', Config::get('firefly.configuration.is_demo_site'))->data;
// activate account?
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', Config::get('firefly.configuration.must_confirm_account'))->data;
// is allowed to?
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
@@ -136,7 +140,7 @@ class RegisterController extends Controller
$email = $request->old('email');
return view('auth.register', compact('showDemoWarning', 'email'));
return view('auth.register', compact('isDemoSite', 'email', 'mustConfirmAccount'));
}
/**
@@ -217,7 +221,7 @@ class RegisterController extends Controller
];
Mail::send(
['emails.blocked-registration-html', 'emails.blocked-registration'], $fields, function (Message $message) use ($email, $domain) {
['emails.blocked-registration-html', 'emails.blocked-registration-text'], $fields, function (Message $message) use ($email, $domain) {
$message->to($email, $email)->subject('Blocked a registration attempt with domain ' . $domain . '.');
}
);

View File

@@ -326,26 +326,30 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
* @param Budget $budget
* @param LimitRepetition $repetition
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return View
* @throws FireflyException
*/
public function showWithRepetition(
BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget, LimitRepetition $repetition
) {
public function showWithRepetition(Budget $budget, LimitRepetition $repetition)
{
if ($repetition->budgetLimit->budget->id != $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');
}
$start = $repetition->startdate;
$end = $repetition->enddate;
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$start = $repetition->startdate;
$end = $repetition->enddate;
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = trans(
'firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]
);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
// collector:

View File

@@ -184,7 +184,7 @@ class CategoryController extends Controller
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$hideCategory = true; // used in list.
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
@@ -253,7 +253,7 @@ class CategoryController extends Controller
$end = Navigation::endOfPeriod($carbon, $range);
$subTitle = $category->name;
$hideCategory = true; // used in list.
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// new collector:

View File

@@ -17,10 +17,15 @@ use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Log;
@@ -99,6 +104,87 @@ class AccountController extends Controller
return Response::json($data);
}
/**
* @param JournalCollectorInterface $collector
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseByBudget(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expenseByBudget');
if ($cache->has()) {
return Response::json($cache->get());
}
// grab all journals:
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionType::WITHDRAWAL]);
$transactions = $collector->getJournals();
$result = [];
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$jrnlBudgetId = intval($transaction->transaction_journal_budget_id);
$transBudgetId = intval($transaction->transaction_budget_id);
$budgetId = max($jrnlBudgetId, $transBudgetId);
$result[$budgetId] = $result[$budgetId] ?? '0';
$result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]);
}
$names = $this->getBudgetNames(array_keys($result));
$data = $this->generator->pieChart($result, $names);
$cache->store($data);
return Response::json($data);
}
/**
* @param JournalCollectorInterface $collector
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expenseByCategory');
if ($cache->has()) {
return Response::json($cache->get());
}
// grab all journals:
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::WITHDRAWAL]);
$transactions = $collector->getJournals();
$result = [];
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$jrnlCatId = intval($transaction->transaction_journal_category_id);
$transCatId = intval($transaction->transaction_category_id);
$categoryId = max($jrnlCatId, $transCatId);
$result[$categoryId] = $result[$categoryId] ?? '0';
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
}
$names = $this->getCategoryNames(array_keys($result));
$data = $this->generator->pieChart($result, $names);
$cache->store($data);
return Response::json($data);
}
/**
* Shows the balances for all the user's frontpage accounts.
*
@@ -108,41 +194,52 @@ class AccountController extends Controller
*/
public function frontpage(AccountRepositoryInterface $repository)
{
$start = clone session('start', Carbon::now()->startOfMonth());
$end = clone session('end', Carbon::now()->endOfMonth());
$start = clone session('start', Carbon::now()->startOfMonth());
$end = clone session('end', Carbon::now()->endOfMonth());
$frontPage = Preferences::get('frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray());
$accounts = $repository->getAccountsById($frontPage->data);
return Response::json($this->accountBalanceChart($start, $end, $accounts));
}
// chart properties for cache:
/**
* @param JournalCollectorInterface $collector
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse
*/
public function incomeByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('frontpage');
$cache->addProperty('accounts');
$cache->addProperty('incomeByCategory');
if ($cache->has()) {
return Response::json($cache->get());
}
$frontPage = Preferences::get('frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray());
$accounts = $repository->getAccountsById($frontPage->data);
// grab all journals:
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::DEPOSIT]);
$transactions = $collector->getJournals();
$result = [];
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$jrnlCatId = intval($transaction->transaction_journal_category_id);
$transCatId = intval($transaction->transaction_category_id);
$categoryId = max($jrnlCatId, $transCatId);
foreach ($accounts as $account) {
$balances = [];
$current = clone $start;
$range = Steam::balanceInRange($account, $start, clone $end);
$previous = round(array_values($range)[0], 2);
while ($current <= $end) {
$format = $current->format('Y-m-d');
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
$previous = $balance;
$balances[] = $balance;
$current->addDay();
}
$account->balances = $balances;
$result[$categoryId] = $result[$categoryId] ?? '0';
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
}
$data = $this->generator->frontpage($accounts, $start, $end);
$names = $this->getCategoryNames(array_keys($result));
$data = $this->generator->pieChart($result, $names);
$cache->store($data);
return Response::json($data);
}
/**
@@ -156,38 +253,7 @@ class AccountController extends Controller
*/
public function report(Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('all');
$cache->addProperty('accounts');
$cache->addProperty('default');
$cache->addProperty($accounts);
if ($cache->has()) {
return Response::json($cache->get());
}
foreach ($accounts as $account) {
$balances = [];
$current = clone $start;
$range = Steam::balanceInRange($account, $start, clone $end);
$previous = round(array_values($range)[0], 2);
while ($current <= $end) {
$format = $current->format('Y-m-d');
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
$previous = $balance;
$balances[] = $balance;
$current->addDay();
}
$account->balances = $balances;
}
// make chart:
$data = $this->generator->frontpage($accounts, $start, $end);
$cache->store($data);
return Response::json($data);
return Response::json($this->accountBalanceChart($start, $end, $accounts));
}
/**
@@ -287,7 +353,6 @@ class AccountController extends Controller
return Response::json($data);
}
/**
* @param Account $account
* @param string $date
@@ -340,4 +405,90 @@ class AccountController extends Controller
return Response::json($data);
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
private function accountBalanceChart(Carbon $start, Carbon $end, Collection $accounts): array
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('account-balance-chart');
$cache->addProperty($accounts);
if ($cache->has()) {
return $cache->get();
}
foreach ($accounts as $account) {
$balances = [];
$current = clone $start;
$range = Steam::balanceInRange($account, $start, clone $end);
$previous = round(array_values($range)[0], 2);
while ($current <= $end) {
$format = $current->format('Y-m-d');
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
$previous = $balance;
$balances[] = $balance;
$current->addDay();
}
$account->balances = $balances;
}
$data = $this->generator->frontpage($accounts, $start, $end);
$cache->store($data);
return $data;
}
/**
* @param array $budgetIds
*
* @return array
*/
private function getBudgetNames(array $budgetIds): array
{
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$budgets = $repository->getBudgets();
$grouped = $budgets->groupBy('id')->toArray();
$return = [];
foreach ($budgetIds as $budgetId) {
if (isset($grouped[$budgetId])) {
$return[$budgetId] = $grouped[$budgetId][0]['name'];
}
}
$return[0] = trans('firefly.no_budget');
return $return;
}
/**
* Small helper function for some of the charts.
*
* @param array $categoryIds
*
* @return array
*/
private function getCategoryNames(array $categoryIds): array
{
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$grouped = $categories->groupBy('id')->toArray();
$return = [];
foreach ($categoryIds as $categoryId) {
if (isset($grouped[$categoryId])) {
$return[$categoryId] = $grouped[$categoryId][0]['name'];
}
}
$return[0] = trans('firefly.noCategory');
return $return;
}
}

View File

@@ -24,7 +24,6 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Log;
use Navigation;
use Preferences;
use Response;
@@ -147,7 +146,6 @@ class BudgetController extends Controller
*/
public function frontpage(BudgetRepositoryInterface $repository)
{
Log::debug('Hello');
$start = session('start', Carbon::now()->startOfMonth());
$end = session('end', Carbon::now()->endOfMonth());
// chart properties for cache:
@@ -185,6 +183,7 @@ class BudgetController extends Controller
return Response::json($data);
}
/**
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
@@ -205,40 +204,49 @@ class BudgetController extends Controller
$cache->addProperty('budget');
$cache->addProperty('period');
if ($cache->has()) {
return Response::json($cache->get());
return Response::json($cache->get());
}
// loop over period, add by users range:
$current = clone $start;
$viewRange = Preferences::get('viewRange', '1M')->data;
$set = new Collection;
// the expenses:
$periods = Navigation::listOfPeriods($start, $end);
$entries = $repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end);
$budgeted = [];
$key = Navigation::preferredCarbonFormat($start, $end);
$range = Navigation::preferredRangeFormat($start, $end);
// get budgeted:
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$current = clone $start;
while ($current < $end) {
$currentStart = clone $current;
$currentEnd = Navigation::endOfPeriod($currentStart, $viewRange);
$reps = $repetitions->filter(
function (LimitRepetition $repetition) use ($budget, $currentStart) {
if ($repetition->budget_id === $budget->id && $repetition->startdate == $currentStart) {
$currentStart = Navigation::startOfPeriod($current, $range);
$currentEnd = Navigation::endOfPeriod($current, $range);
$reps = $repetitions->filter(
function (LimitRepetition $repetition) use ($budget, $currentStart, $currentEnd) {
if ($repetition->budget_id === $budget->id && $repetition->startdate >= $currentStart && $repetition->enddate <= $currentEnd) {
return true;
}
return false;
}
);
$budgeted = $reps->sum('amount');
$spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $currentStart, $currentEnd);
$entry = [
'date' => clone $currentStart,
'budgeted' => $budgeted,
'spent' => $spent,
];
$set->push($entry);
$index = $currentStart->format($key);
$budgeted[$index] = $reps->sum('amount');
$currentEnd->addDay();
$current = clone $currentEnd;
}
$data = $this->generator->period($set, $viewRange);
// join them:
$result = [];
foreach (array_keys($periods) as $period) {
$nice = $periods[$period];
$result[$nice] = [
'spent' => isset($entries[$budget->id]['entries'][$period]) ? $entries[$budget->id]['entries'][$period] : '0',
'budgeted' => isset($entries[$period]) ? $budgeted[$period] : 0,
];
}
$data = $this->generator->period($result);
$cache->store($data);
return Response::json($data);
@@ -326,11 +334,11 @@ class BudgetController extends Controller
*
* @return array
*/
private function spentInPeriodWithout(Carbon $start, Carbon $end):array
private function spentInPeriodWithout(Carbon $start, Carbon $end): array
{
// collector
$collector = new JournalCollector(auth()->user());
$types = [TransactionType::WITHDRAWAL];
$types = [TransactionType::WITHDRAWAL];
$collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget();
$journals = $collector->getJournals();
$sum = '0';

View File

@@ -48,7 +48,6 @@ class CategoryController extends Controller
$this->generator = app(CategoryChartGeneratorInterface::class);
}
/**
* Show an overview for a category for all time, per month/week/year.
*
@@ -172,6 +171,7 @@ class CategoryController extends Controller
return Response::json($data);
}
/**
* @param CRI $repository
* @param Category $category

View File

@@ -0,0 +1,397 @@
<?php
/**
* CategoryReportController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface;
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
use Response;
/**
* Separate controller because many helper functions are shared.
*
* Class CategoryReportController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class CategoryReportController extends Controller
{
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var CategoryRepositoryInterface */
private $categoryRepository;
/** @var CategoryChartGeneratorInterface */
private $generator;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->generator = app(CategoryChartGeneratorInterface::class);
$this->categoryRepository = app(CategoryRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
* @param string $others
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$names = [];
// collect journals (just like the category report does):
$set = $this->getExpenses($accounts, $categories, $start, $end);
$grouped = $this->groupByOpposingAccount($set);
// show the grouped results:
$result = [];
$total = '0';
foreach ($grouped as $accountId => $amount) {
if (!isset($names[$accountId])) {
$account = $this->accountRepository->find(intval($accountId));
$names[$accountId] = $account->name;
}
$amount = bcmul($amount, '-1');
$total = bcadd($total, $amount);
$result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount];
}
// also collect all transactions NOT in these categories.
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcmul($sum, '-1');
Log::debug(sprintf('Sum of others in accountExpense is %f', $sum));
$sum = bcsub($sum, $total);
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
}
$data = $this->generator->pieChart($result);
return Response::json($data);
}
/**
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
* @param string $others
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$names = [];
// collect journals (just like the category report does):
$set = $this->getIncome($accounts, $categories, $start, $end);
$grouped = $this->groupByOpposingAccount($set);
// loop and show the grouped results:
$result = [];
$total = '0';
foreach ($grouped as $accountId => $amount) {
if (!isset($names[$accountId])) {
$account = $this->accountRepository->find(intval($accountId));
$names[$accountId] = $account->name;
}
$total = bcadd($total, $amount);
$result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount];
}
// also collect others?
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
Log::debug(sprintf('Sum of others in accountIncome is %f', $sum));
$sum = bcsub($sum, $total);
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
}
$data = $this->generator->pieChart($result);
return Response::json($data);
}
/**
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
* @param string $others
*
* @return \Illuminate\Http\JsonResponse
*/
public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$names = [];
// collect journals (just like the category report does):
$set = $this->getExpenses($accounts, $categories, $start, $end);
$grouped = $this->groupByCategory($set);
// show the grouped results:
$result = [];
$total = '0';
foreach ($grouped as $categoryId => $amount) {
if (!isset($names[$categoryId])) {
$category = $this->categoryRepository->find(intval($categoryId));
$names[$categoryId] = $category->name;
}
$amount = bcmul($amount, '-1');
$total = bcadd($total, $amount);
$result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount];
}
// also collect all transactions NOT in these categories.
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcmul($sum, '-1');
Log::debug(sprintf('Sum of others in categoryExpense is %f', $sum));
$sum = bcsub($sum, $total);
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
}
$data = $this->generator->pieChart($result);
return Response::json($data);
}
/**
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
* @param string $others
*
* @return \Illuminate\Http\JsonResponse
*/
public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$names = [];
// collect journals (just like the category report does):
$set = $this->getIncome($accounts, $categories, $start, $end);
$grouped = $this->groupByCategory($set);
// loop and show the grouped results:
$result = [];
$total = '0';
foreach ($grouped as $categoryId => $amount) {
if (!isset($names[$categoryId])) {
$category = $this->categoryRepository->find(intval($categoryId));
$names[$categoryId] = $category->name;
}
$total = bcadd($total, $amount);
$result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount];
}
// also collect others?
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
Log::debug(sprintf('Sum of others in categoryIncome is %f', $sum));
$sum = bcsub($sum, $total);
$result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum];
}
$data = $this->generator->pieChart($result);
return Response::json($data);
}
/**
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse
*/
public function mainChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
{
// determin optimal period:
$period = '1D';
$format = 'month_and_day';
$function = 'endOfDay';
if ($start->diffInMonths($end) > 1) {
$period = '1M';
$format = 'month';
$function = 'endOfMonth';
}
if ($start->diffInMonths($end) > 13) {
$period = '1Y';
$format = 'year';
$function = 'endOfYear';
}
Log::debug(sprintf('Period is %s', $period));
$data = [];
$currentStart = clone $start;
while ($currentStart < $end) {
$currentEnd = clone $currentStart;
Log::debug(sprintf('Function is %s', $function));
$currentEnd = $currentEnd->$function();
$expenses = $this->groupByCategory($this->getExpenses($accounts, $categories, $currentStart, $currentEnd));
$income = $this->groupByCategory($this->getIncome($accounts, $categories, $currentStart, $currentEnd));
$label = $currentStart->formatLocalized(strval(trans('config.' . $format)));
Log::debug(sprintf('Now grabbing CMC expenses between %s and %s', $currentStart->format('Y-m-d'), $currentEnd->format('Y-m-d')));
$data[$label] = [
'in' => [],
'out' => [],
];
/** @var Category $category */
foreach ($categories as $category) {
// get sum, and get label:
$categoryId = $category->id;
$data[$label]['name'][$categoryId] = $category->name;
$data[$label]['in'][$categoryId] = $income[$categoryId] ?? '0';
$data[$label]['out'][$categoryId] = $expenses[$categoryId] ?? '0';
}
$currentStart = clone $currentEnd;
$currentStart->addDay();
}
$data = $this->generator->mainReportChart($data);
return Response::json($data);
}
/**
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
private function getExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
{
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setCategories($categories)->withOpposingAccount()->disableFilter();
$accountIds = $accounts->pluck('id')->toArray();
$transactions = $collector->getJournals();
$set = MonthReportGenerator::filterExpenses($transactions, $accountIds);
return $set;
}
/**
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
private function getIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
{
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->setCategories($categories)->withOpposingAccount();
$accountIds = $accounts->pluck('id')->toArray();
$transactions = $collector->getJournals();
$set = MonthReportGenerator::filterIncome($transactions, $accountIds);
return $set;
}
/**
* @param Collection $set
*
* @return array
*/
private function groupByCategory(Collection $set): array
{
// group by category ID:
$grouped = [];
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$jrnlCatId = intval($transaction->transaction_journal_category_id);
$transCatId = intval($transaction->transaction_category_id);
$categoryId = max($jrnlCatId, $transCatId);
$grouped[$categoryId] = $grouped[$categoryId] ?? '0';
$grouped[$categoryId] = bcadd($transaction->transaction_amount, $grouped[$categoryId]);
}
return $grouped;
}
/**
* @param Collection $set
*
* @return array
*/
private function groupByOpposingAccount(Collection $set): array
{
$grouped = [];
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$accountId = $transaction->opposing_account_id;
$grouped[$accountId] = $grouped[$accountId] ?? '0';
$grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]);
}
return $grouped;
}
}

View File

@@ -49,20 +49,18 @@ class ReportController extends Controller
* This chart, by default, is shown on the multi-year and year report pages,
* which means that giving it a 2 week "period" should be enough granularity.
*
* @param string $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function netWorth(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
public function netWorth(Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('netWorth');
$cache->addProperty($start);
$cache->addProperty($reportType);
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
@@ -92,52 +90,36 @@ class ReportController extends Controller
/**
* @param AccountTaskerInterface $accountTasker
* @param string $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
* @internal param AccountRepositoryInterface $repository
*/
public function yearInOut(AccountTaskerInterface $accountTasker, string $reportType, Carbon $start, Carbon $end, Collection $accounts)
public function yearInOut(Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('yearInOut');
$cache->addProperty($start);
$cache->addProperty($reportType);
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
// always per month.
$currentStart = clone $start;
$spentArray = [];
$earnedArray = [];
while ($currentStart <= $end) {
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
$date = $currentStart->format('Y-m');
$spent = $accountTasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd);
$earned = $accountTasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd);
$spentArray[$date] = bcmul($spent, '-1');
$earnedArray[$date] = $earned;
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
}
$chartSource = $this->getYearData($accounts, $start, $end);
if ($start->diffInMonths($end) > 12) {
// data = method X
$data = $this->multiYearInOut($earnedArray, $spentArray, $start, $end);
$data = $this->multiYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data);
return Response::json($data);
}
// data = method Y
$data = $this->singleYearInOut($earnedArray, $spentArray, $start, $end);
$data = $this->singleYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data);
return Response::json($data);
@@ -146,16 +128,14 @@ class ReportController extends Controller
}
/**
* @param AccountTaskerInterface $accountTasker
* @param string $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
* @internal param AccountRepositoryInterface $repository
*/
public function yearInOutSummarized(AccountTaskerInterface $accountTasker, string $reportType, Carbon $start, Carbon $end, Collection $accounts)
public function yearInOutSummarized(Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
@@ -163,35 +143,21 @@ class ReportController extends Controller
$cache->addProperty('yearInOutSummarized');
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($reportType);
$cache->addProperty($accounts);
if ($cache->has()) {
return Response::json($cache->get());
}
// always per month.
$currentStart = clone $start;
$spentArray = [];
$earnedArray = [];
while ($currentStart <= $end) {
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
$date = $currentStart->format('Y-m');
$spent = $accountTasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd);
$earned = $accountTasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd);
$spentArray[$date] = bcmul($spent, '-1');
$earnedArray[$date] = $earned;
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
}
$chartSource = $this->getYearData($accounts, $start, $end);
if ($start->diffInMonths($end) > 12) {
// per year
$data = $this->multiYearInOutSummarized($earnedArray, $spentArray, $start, $end);
$data = $this->multiYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data);
return Response::json($data);
}
// per month!
$data = $this->singleYearInOutSummarized($earnedArray, $spentArray, $start, $end);
$data = $this->singleYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data);
return Response::json($data);
@@ -342,4 +308,33 @@ class ReportController extends Controller
return $sum;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getYearData(Collection $accounts, Carbon $start, Carbon $end): array
{
$tasker = app(AccountTaskerInterface::class);
$currentStart = clone $start;
$spentArray = [];
$earnedArray = [];
while ($currentStart <= $end) {
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
$date = $currentStart->format('Y-m');
$spent = $tasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd);
$earned = $tasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd);
$spentArray[$date] = bcmul($spent, '-1');
$earnedArray[$date] = $earned;
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
}
return [
'spent' => $spentArray,
'earned' => $earnedArray,
];
}
}

View File

@@ -13,10 +13,15 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Session;
use View;
/**
@@ -60,4 +65,37 @@ class Controller extends BaseController
}
/**
* @param TransactionJournal $journal
*
* @return bool
*/
protected function isOpeningBalance(TransactionJournal $journal): bool
{
return TransactionJournal::transactionTypeStr($journal) === TransactionType::OPENING_BALANCE;
}
/**
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
protected function redirectToAccount(TransactionJournal $journal)
{
$valid = [AccountType::DEFAULT, AccountType::ASSET];
$transactions = $journal->transactions;
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$account = $transaction->account;
if (in_array($account->accountType->type, $valid)) {
return redirect(route('accounts.show', [$account->id]));
}
}
Session::flash('error', strval(trans('firefly.cannot_redirect_to_account')));
return redirect(route('index'));
}
}

View File

@@ -56,7 +56,7 @@ class ExportController extends Controller
/**
* @param ExportJob $job
*
* @return mixed
* @return \Symfony\Component\HttpFoundation\Response|\Illuminate\Contracts\Routing\ResponseFactory
* @throws FireflyException
*/
public function download(ExportJob $job)

View File

@@ -15,10 +15,11 @@ namespace FireflyIII\Http\Controllers;
use Artisan;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
@@ -121,7 +122,6 @@ class HomeController extends Controller
*/
public function index(ARI $repository)
{
$types = config('firefly.accountTypesByIdentifier.asset');
$count = $repository->count($types);
@@ -142,18 +142,20 @@ class HomeController extends Controller
$accounts = $repository->getAccountsById($frontPage->data);
$showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data;
foreach ($accounts as $account) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit(10)->setPage(1);
$set = $collector->getJournals();
// zero bills? Hide some elements from view.
/** @var BillRepositoryInterface $billRepository */
$billRepository = app(BillRepositoryInterface::class);
$billCount = $billRepository->getBills()->count();
if (count($set) > 0) {
$transactions[] = [$set, $account];
}
foreach ($accounts as $account) {
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit(10)->setPage(1);
$set = $collector->getJournals();
$transactions[] = [$set, $account];
}
return view(
'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage')
'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage', 'billCount')
);
}
@@ -169,8 +171,7 @@ class HomeController extends Controller
'admin.users.domains.block-', 'import.json', 'help.',
];
$routes = Route::getRoutes();
echo '<pre>';
$return = '<pre>';
/** @var \Illuminate\Routing\Route $route */
foreach ($routes as $route) {
@@ -178,15 +179,13 @@ class HomeController extends Controller
$methods = $route->getMethods();
if (!is_null($name) && strlen($name) > 0 && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) {
echo sprintf('touch %s.md', $name) . "\n";
$return .= sprintf('touch %s.md', $name) . "\n";
}
}
echo '</pre>';
$return .= '</pre><hr />';
echo '<hr />';
return '&nbsp;';
return $return;
}
/**
@@ -208,10 +207,7 @@ class HomeController extends Controller
*
* @return bool
*/
private
function startsWithAny(
array $array, string $needle
): bool
private function startsWithAny(array $array, string $needle): bool
{
foreach ($array as $entry) {
if ((substr($needle, 0, strlen($entry)) === $entry)) {

View File

@@ -78,14 +78,16 @@ class NewUserController extends Controller
$this->createAssetAccount($request, $repository);
// create savings account
if (strlen($request->get('savings_balance')) > 0) {
$savingBalance = strval($request->get('savings_balance')) === '' ? '0' : strval($request->get('savings_balance'));
if (bccomp($savingBalance, '0') !== 0) {
$this->createSavingsAccount($request, $repository);
$count++;
}
// create credit card.
if (strlen($request->get('credit_card_limit')) > 0) {
$limit = strval($request->get('credit_card_limit')) === '' ? '0' : strval($request->get('credit_card_limit'));
if (bccomp($limit, '0') !== 0) {
$this->storeCreditCard($request, $repository);
$count++;
}

View File

@@ -19,6 +19,7 @@ use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Navigation;
/**
* Class BudgetController
@@ -29,29 +30,28 @@ class BudgetController extends Controller
{
/**
*
* @param BudgetReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return mixed|string
* @return string
*/
public function budgetMultiYear(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
public function budgetPeriodReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-mult-year-report');
$cache->addProperty('budget-period-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
return $cache->get();
}
$years = $helper->listOfYears($start, $end);
$budgetMultiYear = $helper->getBudgetMultiYear($start, $end, $accounts);
$result = view('reports.partials.budget-multi-year', compact('budgetMultiYear', 'years'))->render();
$periods = Navigation::listOfPeriods($start, $end);
$budgets = $helper->getBudgetPeriodReport($start, $end, $accounts);
$result = view('reports.partials.budget-period', compact('budgets', 'periods'))->render();
$cache->store($result);
return $result;
@@ -86,35 +86,4 @@ class BudgetController extends Controller
return $result;
}
/**
* @param BudgetReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function budgetYearOverview(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-year-overview');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$budgets = $helper->budgetYearOverview($start, $end, $accounts);
$result = view('reports.partials.budget-year-overview', compact('budgets'))->render();
$cache->store($result);
return $result;
}
}

View File

@@ -15,16 +15,17 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Generator\Report\ReportGeneratorFactory;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Models\Account;
use FireflyIII\Http\Requests\ReportFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection;
use Preferences;
use Response;
use Session;
use Steam;
use View;
/**
@@ -49,6 +50,7 @@ class ReportController extends Controller
function ($request, $next) {
View::share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart');
View::share('subTitleIcon', 'fa-calendar');
$this->helper = app(ReportHelperInterface::class);
@@ -58,6 +60,115 @@ class ReportController extends Controller
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
* @throws FireflyException
*/
public function auditReport(Carbon $start, Carbon $end, Collection $accounts)
{
if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date'));
}
if ($start < session('first')) {
$start = session('first');
}
View::share(
'subTitle', trans(
'firefly.report_audit',
[
'start' => $start->formatLocalized($this->monthFormat),
'end' => $end->formatLocalized($this->monthFormat),
]
)
);
$generator = ReportGeneratorFactory::reportGenerator('Audit', $start, $end);
$generator->setAccounts($accounts);
$result = $generator->generate();
return $result;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @param Collection $categories
*
* @return string
*/
public function categoryReport(Carbon $start, Carbon $end, Collection $accounts, Collection $categories)
{
if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date'));
}
if ($start < session('first')) {
$start = session('first');
}
View::share(
'subTitle', trans(
'firefly.report_category',
[
'start' => $start->formatLocalized($this->monthFormat),
'end' => $end->formatLocalized($this->monthFormat),
]
)
);
$generator = ReportGeneratorFactory::reportGenerator('Category', $start, $end);
$generator->setAccounts($accounts);
$generator->setCategories($categories);
$result = $generator->generate();
return $result;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
* @throws FireflyException
*/
public function defaultReport(Carbon $start, Carbon $end, Collection $accounts)
{
if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date'));
}
if ($start < session('first')) {
$start = session('first');
}
View::share(
'subTitle', trans(
'firefly.report_default',
[
'start' => $start->formatLocalized($this->monthFormat),
'end' => $end->formatLocalized($this->monthFormat),
]
)
);
$generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end);
$generator->setAccounts($accounts);
$result = $generator->generate();
return $result;
}
/**
* @param AccountRepositoryInterface $repository
*
@@ -65,40 +176,59 @@ class ReportController extends Controller
*/
public function index(AccountRepositoryInterface $repository)
{
/** @var Carbon $start */
$start = clone session('first');
$months = $this->helper->listOfMonths($start);
$customFiscalYear = Preferences::get('customFiscalYear', 0)->data;
// does the user have shared accounts?
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
// get id's for quick links:
$accountIds = [];
/** @var Account $account */
foreach ($accounts as $account) {
$accountIds [] = $account->id;
}
$accountList = join(',', $accountIds);
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$accountList = join(',', $accounts->pluck('id')->toArray());
return view('reports.index', compact('months', 'accounts', 'start', 'accountList', 'customFiscalYear'));
}
/**
* @param string $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param string $reportType
*
* @return View
* @return mixed
*/
public function options(string $reportType)
{
switch ($reportType) {
default:
$result = $this->noReportOptions();
break;
case 'category':
$result = $this->categoryReportOptions();
break;
}
return Response::json(['html' => $result]);
}
/**
* @param ReportFormRequest $request
*
* @return RedirectResponse
* @throws FireflyException
*/
public function report(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
public function postIndex(ReportFormRequest $request): RedirectResponse
{
// throw an error if necessary.
// report type:
$reportType = $request->get('report_type');
$start = $request->getStartDate()->format('Ymd');
$end = $request->getEndDate()->format('Ymd');
$accounts = join(',', $request->getAccountList()->pluck('id')->toArray());
$categories = join(',', $request->getCategoryList()->pluck('id')->toArray());
if ($request->getAccountList()->count() === 0) {
Session::flash('error', trans('firefly.select_more_than_one_account'));
return redirect(route('reports.index'));
}
if ($end < $start) {
throw new FireflyException('End date cannot be before start date, silly!');
return view('error')->with('message', trans('firefly.end_after_start_date'));
}
// lower threshold
@@ -106,190 +236,42 @@ class ReportController extends Controller
$start = session('first');
}
View::share(
'subTitle', trans(
'firefly.report_' . $reportType,
[
'start' => $start->formatLocalized($this->monthFormat),
'end' => $end->formatLocalized($this->monthFormat),
]
)
);
View::share('subTitleIcon', 'fa-calendar');
switch ($reportType) {
default:
throw new FireflyException('Unfortunately, reports of the type "' . e($reportType) . '" are not available at this time.');
throw new FireflyException(sprintf('Firefly does not support the "%s"-report yet.', $reportType));
case 'category':
$uri = route('reports.report.category', [$start, $end, $accounts, $categories]);
break;
case 'default':
// more than one year date difference means year report.
if ($start->diffInMonths($end) > 12) {
return $this->defaultMultiYear($reportType, $start, $end, $accounts);
}
// more than two months date difference means year report.
if ($start->diffInMonths($end) > 1) {
return $this->defaultYear($reportType, $start, $end, $accounts);
}
// otherwise default
return $this->defaultMonth($reportType, $start, $end, $accounts);
$uri = route('reports.report.default', [$start, $end, $accounts]);
break;
case 'audit':
// always default
return $this->auditReport($start, $end, $accounts);
$uri = route('reports.report.audit', [$start, $end, $accounts]);
break;
}
return redirect($uri);
}
/**
* @return string
*/
private function categoryReportOptions(): string
{
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$result = view('reports.options.category', compact('categories'))->render();
return $result;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return View
* @return string
*/
private function auditReport(Carbon $start, Carbon $end, Collection $accounts)
private function noReportOptions(): string
{
$auditData = [];
$dayBefore = clone $start;
$dayBefore->subDay();
/** @var Account $account */
foreach ($accounts as $account) {
// balance the day before:
$id = $account->id;
$dayBeforeBalance = Steam::balance($account, $dayBefore);
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($start, $end);
$journals = $collector->getJournals();
$journals = $journals->reverse();
$startBalance = $dayBeforeBalance;
/** @var Transaction $journal */
foreach ($journals as $transaction) {
$transaction->before = $startBalance;
$transactionAmount = $transaction->transaction_amount;
$newBalance = bcadd($startBalance, $transactionAmount);
$transaction->after = $newBalance;
$startBalance = $newBalance;
}
/*
* Reverse set again.
*/
$auditData[$id]['journals'] = $journals->reverse();
$auditData[$id]['exists'] = $journals->count() > 0;
$auditData[$id]['end'] = $end->formatLocalized(strval(trans('config.month_and_day')));
$auditData[$id]['endBalance'] = Steam::balance($account, $end);
$auditData[$id]['dayBefore'] = $dayBefore->formatLocalized(strval(trans('config.month_and_day')));
$auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
}
$reportType = 'audit';
$accountIds = join(',', $accounts->pluck('id')->toArray());
$hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date',
'interest_date', 'book_date', 'process_date',
// three new optional fields.
'due_date', 'payment_date', 'invoice_date',
'from', 'to', 'budget', 'category', 'bill',
// more new optional fields
'internal_reference', 'notes',
'create_date', 'update_date',
];
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
return view('reports.audit.report', compact('start', 'end', 'reportType', 'accountIds', 'accounts', 'auditData', 'hideable', 'defaultShow'));
}
/**
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return View
*/
private function defaultMonth(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$bills = $this->helper->getBillReport($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
// and some id's, joined:
$accountIds = join(',', $accounts->pluck('id')->toArray());
// continue!
return view(
'reports.default.month',
compact(
'start', 'end',
'tags',
'bills',
'accountIds',
'reportType'
)
);
}
/**
* @param $reportType
* @param $start
* @param $end
* @param $accounts
*
* @return View
*/
private function defaultMultiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
// need all budgets
// need all years.
// and some id's, joined:
$accountIds = [];
/** @var Account $account */
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$accountIds = join(',', $accountIds);
return view(
'reports.default.multi-year',
compact(
'accounts', 'start', 'end', 'accountIds', 'reportType'
)
);
}
/**
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return View
*/
private function defaultYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
Session::flash('gaEventCategory', 'report');
Session::flash('gaEventAction', 'year');
Session::flash('gaEventLabel', $start->format('Y'));
// and some id's, joined:
$accountIds = [];
/** @var Account $account */
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$accountIds = join(',', $accountIds);
return view(
'reports.default.year',
compact(
'start', 'reportType',
'accountIds', 'end'
)
);
return view('reports.options.no-options')->render();
}
}

View File

@@ -35,6 +35,7 @@ class SearchController extends Controller
/**
* Results always come in the form of an array [results, count, fullCount]
*
* @param Request $request
* @param SearchInterface $searcher
*
* @return $this

View File

@@ -64,6 +64,10 @@ class ConvertController extends Controller
*/
public function convert(TransactionType $destinationType, TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
$positiveAmount = TransactionJournal::amountPositive($journal);
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$sourceType = $journal->transactionType;
@@ -113,6 +117,10 @@ class ConvertController extends Controller
*/
public function submit(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
$data = $request->all();
// cannot convert to its own type.
@@ -160,7 +168,6 @@ class ConvertController extends Controller
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
$sourceType = $journal->transactionType;
$destination = null;
$joined = $sourceType->type . '-' . $destinationType->type;
switch ($joined) {
default:

View File

@@ -196,35 +196,33 @@ class MassController extends Controller
$journal = $repository->find(intval($journalId));
if ($journal) {
// get optional fields:
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
$sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0;
$sourceAccountName = $request->get('source_account_name')[$journal->id] ?? '';
$destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0;
$destAccountName = $request->get('destination_account_name')[$journal->id] ?? '';
$budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0;
$category = $request->get('category')[$journal->id];
$tags = $journal->tags->pluck('tag')->toArray();
$budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0;
$category = $request->get('category')[$journal->id];
$tags = $journal->tags->pluck('tag')->toArray();
// build data array
$data = [
'id' => $journal->id,
'what' => $what,
'description' => $request->get('description')[$journal->id],
'source_account_id' => intval($sourceAccountId),
'source_account_name' => $sourceAccountName,
'destination_account_id' => intval($destAccountId),
'destination_account_name' => $destAccountName,
'amount' => round($request->get('amount')[$journal->id], 4),
'amount_currency_id_amount' => intval($request->get('amount_currency_id_amount_' . $journal->id)),
'date' => new Carbon($request->get('date')[$journal->id]),
'interest_date' => $journal->interest_date,
'book_date' => $journal->book_date,
'process_date' => $journal->process_date,
'budget_id' => $budgetId,
'category' => $category,
'tags' => $tags,
'id' => $journal->id,
'what' => $what,
'description' => $request->get('description')[$journal->id],
'source_account_id' => intval($sourceAccountId),
'source_account_name' => $sourceAccountName,
'destination_account_id' => intval($destAccountId),
'destination_account_name' => $destAccountName,
'amount' => round($request->get('amount')[$journal->id], 4),
'currency_id' => intval($request->get('amount_currency_id_amount_' . $journal->id)),
'date' => new Carbon($request->get('date')[$journal->id]),
'interest_date' => $journal->interest_date,
'book_date' => $journal->book_date,
'process_date' => $journal->process_date,
'budget_id' => $budgetId,
'category' => $category,
'tags' => $tags,
];
// call repository update function.

View File

@@ -125,6 +125,10 @@ class SingleController extends Controller
*/
public function delete(TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$subTitle = trans('firefly.delete_' . $what, ['description' => $journal->description]);
@@ -146,8 +150,12 @@ class SingleController extends Controller
*/
public function destroy(JournalRepositoryInterface $repository, TransactionJournal $transactionJournal)
{
if ($this->isOpeningBalance($transactionJournal)) {
return $this->redirectToAccount($transactionJournal);
}
$type = TransactionJournal::transactionTypeStr($transactionJournal);
Session::flash('success', strval(trans('firefly.deleted_' . $type, ['description' => e($transactionJournal->description)])));
Session::flash('success', strval(trans('firefly.deleted_' . strtolower($type), ['description' => e($transactionJournal->description)])));
$repository->delete($transactionJournal);
@@ -164,17 +172,23 @@ class SingleController extends Controller
*/
public function edit(TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
$count = $journal->transactions()->count();
if ($count > 2) {
return redirect(route('transactions.edit-split', [$journal->id]));
}
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
// view related code
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
// journal related code
$sourceAccounts = TransactionJournal::sourceAccountList($journal);
@@ -189,9 +203,9 @@ class SingleController extends Controller
'budget_id' => TransactionJournal::budgetId($journal),
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
'source_account_id' => $sourceAccounts->first()->id,
'source_account_name' => $sourceAccounts->first()->name,
'source_account_name' => $sourceAccounts->first()->edit_name,
'destination_account_id' => $destinationAccounts->first()->id,
'destination_account_name' => $destinationAccounts->first()->name,
'destination_account_name' => $destinationAccounts->first()->edit_name,
'amount' => TransactionJournal::amountPositive($journal),
// new custom fields:
@@ -290,6 +304,10 @@ class SingleController extends Controller
*/
public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
$data = $request->getJournalData();
$journal = $repository->update($journal, $data);
$this->attachments->saveAttachmentsForModel($journal);
@@ -324,6 +342,4 @@ class SingleController extends Controller
return redirect(session('transactions.edit.url'));
}
}

View File

@@ -89,6 +89,10 @@ class SplitController extends Controller
*/
public function edit(Request $request, TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
$currencies = ExpandedForm::makeSelectList($this->currencies->get());
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
@@ -127,6 +131,10 @@ class SplitController extends Controller
*/
public function update(Request $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
$data = $this->arrayFromInput($request);
$journal = $repository->updateSplitJournal($journal, $data);
@@ -187,6 +195,7 @@ class SplitController extends Controller
'transactions' => $this->getTransactionDataFromRequest($request),
];
return $array;
}
@@ -283,4 +292,5 @@ class SplitController extends Controller
return $return;
}
}

View File

@@ -14,11 +14,14 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Log;
use Navigation;
use Preferences;
use Response;
use View;
@@ -50,25 +53,127 @@ class TransactionController extends Controller
}
/**
* @param Request $request
* @param string $what
* @param Request $request
* @param JournalRepositoryInterface $repository
* @param string $what
*
* @return View
*/
public function index(Request $request, string $what)
public function index(Request $request, JournalRepositoryInterface $repository, string $what)
{
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
$types = config('firefly.transactionTypesByWhat.' . $what);
$subTitle = trans('firefly.title_' . $what);
$range = Preferences::get('viewRange', '1M')->data;
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$collector = new JournalCollector(auth()->user());
// to make sure we only grab a subset, based on the current date (in session):
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
$collector->setRange($start, $end);
// do not filter transfers if $what = transfer.
if (!in_array($what, ['transfer', 'transfers'])) {
Log::debug('Also get opposing account info.');
$collector->withOpposingAccount();
}
$journals = $collector->getPaginatedJournals();
$journals->setPath('transactions/' . $what);
return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals'));
unset($start, $end);
// then also show a list of periods where the user can click on, based on the
// user's range and the oldest journal the user has:
$first = $repository->first();
$blockStart = is_null($first->id) ? new Carbon : $first->date;
$blockStart = Navigation::startOfPeriod($blockStart, $range);
$blockEnd = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
while ($blockEnd >= $blockStart) {
Log::debug(sprintf('Now at blockEnd: %s', $blockEnd->format('Y-m-d')));
$blockEnd = Navigation::startOfPeriod($blockEnd, $range);
$dateStr = $blockEnd->format('Y-m-d');
$dateName = Navigation::periodShow($blockEnd, $range);
$entries->push([$dateStr, $dateName]);
$blockEnd = Navigation::subtractPeriod($blockEnd, $range, 1);
}
return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'entries'));
}
/**
* @param Request $request
* @param string $what
*
* @return View
*/
public function indexAll(Request $request, string $what)
{
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
$types = config('firefly.transactionTypesByWhat.' . $what);
$subTitle = sprintf('%s (%s)', trans('firefly.title_' . $what), strtolower(trans('firefly.everything')));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
// do not filter transfers if $what = transfer.
if (!in_array($what, ['transfer', 'transfers'])) {
Log::debug('Also get opposing account info.');
$collector->withOpposingAccount();
}
$journals = $collector->getPaginatedJournals();
$journals->setPath('transactions/' . $what . '/all');
return view('transactions.index-all', compact('subTitle', 'what', 'subTitleIcon', 'journals'));
}
/**
* @param Request $request
* @param string $what
*
* @param string $date
*
* @return View
*/
public function indexDate(Request $request, string $what, string $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
$types = config('firefly.transactionTypesByWhat.' . $what);
$subTitle = trans('firefly.title_' . $what) . ' (' . Navigation::periodShow($carbon, $range) . ')';
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
Log::debug(sprintf('Transaction index by date will show between %s and %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
$collector->setRange($start, $end);
// do not filter transfers if $what = transfer.
if (!in_array($what, ['transfer', 'transfers'])) {
Log::debug('Also get opposing account info.');
$collector->withOpposingAccount();
}
$journals = $collector->getPaginatedJournals();
$journals->setPath('transactions/' . $what . '/' . $date);
return view('transactions.index-date', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'carbon'));
}
@@ -108,6 +213,10 @@ class TransactionController extends Controller
*/
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
$events = $tasker->getPiggyBankEvents($journal);
$transactions = $tasker->getTransactionsOverview($journal);
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
@@ -117,4 +226,5 @@ class TransactionController extends Controller
}
}

View File

@@ -14,6 +14,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Middleware;
use Closure;
use FireflyConfig;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Preferences;
@@ -45,11 +46,11 @@ class IsConfirmed
return redirect()->guest('login');
}
// must the user be confirmed in the first place?
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
// user must be logged in, then continue:
$isConfirmed = Preferences::get('user_confirmed', false)->data;
if ($isConfirmed === false && $confirmAccount === true) {
if ($isConfirmed === false && $mustConfirmAccount === true) {
// user account is not confirmed, redirect to
// confirmation page:

View File

@@ -14,6 +14,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Middleware;
use Closure;
use FireflyConfig;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Preferences;
@@ -45,10 +46,10 @@ class IsNotConfirmed
return redirect()->guest('login');
}
// must the user be confirmed in the first place?
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
// user must be logged in, then continue:
$isConfirmed = Preferences::get('user_confirmed', false)->data;
if ($isConfirmed || $confirmAccount === false) {
if ($isConfirmed || $mustConfirmAccount === false) {
// user account is confirmed, simply send them home.
return redirect(route('home'));
}

View File

@@ -57,13 +57,13 @@ class Range
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param Closure $theNext
* @param Closure $next
* @param string|null $guard
*
* @return mixed
* @internal param Closure $next
*/
public function handle(Request $request, Closure $theNext, $guard = null)
public function handle(Request $request, Closure $next, $guard = null)
{
if (!Auth::guard($guard)->guest()) {
@@ -80,7 +80,7 @@ class Range
$this->configureList();
}
return $theNext($request);
return $next($request);
}
@@ -130,13 +130,30 @@ class Range
*/
private function datePicker()
{
$viewRange = Preferences::get('viewRange', '1M')->data;
$start = Session::get('start');
$end = Session::get('end');
$prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period
$prevEnd = Navigation::endOfPeriod($prevStart, $viewRange);
$nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period
$nextEnd = Navigation::endOfPeriod($nextStart, $viewRange);
$viewRange = Preferences::get('viewRange', '1M')->data;
/** @var Carbon $start */
$start = Session::get('start');
/** @var Carbon $end */
$end = Session::get('end');
$prevStart = clone $start;
$prevEnd = clone $start;
$nextStart = clone $end;
$nextEnd = clone $end;
if ($viewRange === 'custom') {
$days = $start->diffInDays($end);
$prevStart->subDays($days);
$nextEnd->addDays($days);
unset($days);
}
if ($viewRange !== 'custom') {
$prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period
$prevEnd = Navigation::endOfPeriod($prevStart, $viewRange);
$nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period
$nextEnd = Navigation::endOfPeriod($nextStart, $viewRange);
}
$ranges = [];
$ranges['current'] = [$start->format('Y-m-d'), $end->format('Y-m-d')];
$ranges['previous'] = [$prevStart->format('Y-m-d'), $prevEnd->format('Y-m-d')];
@@ -146,6 +163,7 @@ class Range
default:
throw new FireflyException('The date picker does not yet support "' . $viewRange . '".');
case '1D':
case 'custom':
$format = (string)trans('config.month_and_day');
break;
case '3M':

View File

@@ -42,6 +42,7 @@ class AccountFormRequest extends Request
'name' => trim($this->input('name')),
'active' => intval($this->input('active')) === 1,
'accountType' => $this->input('what'),
'currency_id' => intval($this->input('currency_id')),
'virtualBalance' => round($this->input('virtualBalance'), 2),
'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')),
'iban' => trim($this->input('iban')),
@@ -80,6 +81,7 @@ class AccountFormRequest extends Request
'iban' => 'iban',
'virtualBalance' => 'numeric',
'openingBalanceDate' => 'date',
'currency_id' => 'exists:transaction_currencies,id',
'accountNumber' => 'between:1,255|uniqueAccountNumberForUser',
'accountRole' => 'in:' . $accountRoles,
'active' => 'boolean',

View File

@@ -36,7 +36,9 @@ class ConfigurationRequest extends Request
public function getConfigurationData(): array
{
return [
'single_user_mode' => intval($this->get('single_user_mode')) === 1,
'single_user_mode' => intval($this->get('single_user_mode')) === 1,
'must_confirm_account' => intval($this->get('must_confirm_account')) === 1,
'is_demo_site' => intval($this->get('is_demo_site')) === 1,
];
}
@@ -46,7 +48,9 @@ class ConfigurationRequest extends Request
public function rules()
{
$rules = [
'single_user_mode' => 'between:0,1|numeric',
'single_user_mode' => 'between:0,1|numeric',
'must_confirm_account' => 'between:0,1|numeric',
'is_demo_site' => 'between:0,1|numeric',
];
return $rules;

View File

@@ -160,6 +160,7 @@ class JournalFormRequest extends Request
*/
private function getFieldOrEmptyString(string $field): string
{
return $this->get($field) ?? '';
$string = $this->get($field) ?? '';
return trim($string);
}
}

View File

@@ -0,0 +1,132 @@
<?php
/**
* ReportFormRequest.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Support\Collection;
/**
* Class CategoryFormRequest
*
*
* @package FireflyIII\Http\Requests
*/
class ReportFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return auth()->check();
}
/**
* @return Collection
*/
public function getAccountList():Collection
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$set = $this->get('accounts');
$collection = new Collection;
if (is_array($set)) {
foreach ($set as $accountId) {
$account = $repository->find(intval($accountId));
if (!is_null($account->id)) {
$collection->push($account);
}
}
}
return $collection;
}
/**
* @return Collection
*/
public function getCategoryList(): Collection
{
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$set = $this->get('category');
$collection = new Collection;
if (is_array($set)) {
foreach ($set as $categoryId) {
$category = $repository->find(intval($categoryId));
if (!is_null($category->id)) {
$collection->push($category);
}
}
}
return $collection;
}
/**
* @return Carbon
* @throws FireflyException
*/
public function getEndDate(): Carbon
{
$date = new Carbon;
$range = $this->get('daterange');
$parts = explode(' - ', strval($range));
if (count($parts) === 2) {
try {
$date = new Carbon($parts[1]);
} catch (Exception $e) {
throw new FireflyException(sprintf('"%s" is not a valid date range.', $range));
}
}
return $date;
}
/**
* @return Carbon
* @throws FireflyException
*/
public function getStartDate(): Carbon
{
$date = new Carbon;
$range = $this->get('daterange');
$parts = explode(' - ', strval($range));
if (count($parts) === 2) {
try {
$date = new Carbon($parts[0]);
} catch (Exception $e) {
throw new FireflyException(sprintf('"%s" is not a valid date range.', $range));
}
}
return $date;
}
/**
* @return array
*/
public function rules(): array
{
return [
'report_type' => 'in:audit,default,category',
];
}
}

View File

@@ -90,11 +90,12 @@ class SplitJournalFormRequest extends Request
$return = [];
// description is leading because it is one of the mandatory fields.
foreach ($this->get('description') as $index => $description) {
$category = $this->get('category')[$index] ?? '';
$transaction = [
'description' => $description,
'amount' => round($this->get('amount')[$index], 2),
'budget_id' => $this->get('budget_id')[$index] ? intval($this->get('budget_id')[$index]) : 0,
'category' => $this->get('category')[$index] ?? '',
'category' => trim($category),
'source_account_id' => isset($this->get('source_account_id')[$index])
? intval($this->get('source_account_id')[$index])
: intval(

View File

@@ -83,6 +83,17 @@ Breadcrumbs::register(
}
);
Breadcrumbs::register(
'accounts.show.all', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account);
$title = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything')));
$breadcrumbs->push($title, route('accounts.show.all', [$account->id]));
}
);
Breadcrumbs::register(
'accounts.delete', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account);
@@ -573,6 +584,28 @@ Breadcrumbs::register(
$breadcrumbs->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', [$what]));
}
);
Breadcrumbs::register(
'transactions.index.all', function (BreadCrumbGenerator $breadcrumbs, string $what) {
$breadcrumbs->parent('transactions.index', $what);
$title = sprintf('%s (%s)', trans('breadcrumbs.' . $what . '_list'), strtolower(trans('firefly.everything')));
$breadcrumbs->push($title, route('transactions.index.all', [$what]));
}
);
Breadcrumbs::register(
'transactions.index.date', function (BreadCrumbGenerator $breadcrumbs, string $what, Carbon $date) {
$breadcrumbs->parent('transactions.index', $what);
$range = Preferences::get('viewRange', '1M')->data;
$title = trans('breadcrumbs.' . $what . '_list') . ' (' . Navigation::periodShow($date, $range) . ')';
$breadcrumbs->push($title, route('transactions.index.date', [$what, $date->format('Y-m-d')]));
}
);
Breadcrumbs::register(
'transactions.create', function (BreadCrumbGenerator $breadcrumbs, string $what) {
$breadcrumbs->parent('transactions.index', $what);

View File

@@ -0,0 +1,85 @@
<?php
/**
* TagSplit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Log;
/**
* Class TagSplit
*
* @package FireflyIII\Import\Converter
*/
class TagSplit
{
/**
* @param User $user
* @param array $mapping
* @param array $parts
*
* @return Collection
*/
public static function createSetFromSplits(User $user, array $mapping, array $parts): Collection
{
$set = new Collection;
Log::debug('Exploded parts.', $parts);
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class, [$user]);
/** @var string $part */
foreach ($parts as $part) {
if (isset($mapping[$part])) {
Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $mapping[$part]]);
$tag = $repository->find(intval($mapping[$part]));
if (!is_null($tag->id)) {
Log::debug('Found tag by ID', ['id' => $tag->id]);
$set->push($tag);
continue;
}
}
// not mapped? Still try to find it first:
$tag = $repository->findByTag($part);
if (!is_null($tag->id)) {
Log::debug('Found tag by name ', ['id' => $tag->id]);
$set->push($tag);
}
if (is_null($tag->id)) {
// create new tag
$tag = $repository->store(
[
'tag' => $part,
'date' => null,
'description' => $part,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
]
);
Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]);
$set->push($tag);
}
}
return $set;
}
}

View File

@@ -13,7 +13,6 @@ declare(strict_types = 1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
@@ -41,49 +40,7 @@ class TagsComma extends BasicConverter implements ConverterInterface
return new Collection;
}
$parts = array_unique(explode(',', $value));
$set = new Collection;
Log::debug('Exploded parts.', $parts);
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class, [$this->user]);
/** @var string $part */
foreach ($parts as $part) {
if (isset($this->mapping[$part])) {
Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $this->mapping[$part]]);
$tag = $repository->find(intval($this->mapping[$part]));
if (!is_null($tag->id)) {
Log::debug('Found tag by ID', ['id' => $tag->id]);
$set->push($tag);
continue;
}
}
// not mapped? Still try to find it first:
$tag = $repository->findByTag($part);
if (!is_null($tag->id)) {
Log::debug('Found tag by name ', ['id' => $tag->id]);
$set->push($tag);
}
if (is_null($tag->id)) {
// create new tag
$tag = $repository->store(
[
'tag' => $part,
'date' => null,
'description' => $part,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
]
);
Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]);
$set->push($tag);
}
}
$set = TagSplit::createSetFromSplits($this->user, $this->mapping, $parts);
$this->setCertainty(100);
return $set;

View File

@@ -13,7 +13,6 @@ declare(strict_types = 1);
namespace FireflyIII\Import\Converter;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
@@ -41,49 +40,7 @@ class TagsSpace extends BasicConverter implements ConverterInterface
return new Collection;
}
$parts = array_unique(explode(' ', $value));
$set = new Collection;
Log::debug('Exploded parts.', $parts);
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class, [$this->user]);
/** @var string $part */
foreach ($parts as $part) {
if (isset($this->mapping[$part])) {
Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $this->mapping[$part]]);
$tag = $repository->find(intval($this->mapping[$part]));
if (!is_null($tag->id)) {
Log::debug('Found tag by ID', ['id' => $tag->id]);
$set->push($tag);
continue;
}
}
// not mapped? Still try to find it first:
$tag = $repository->findByTag($part);
if (!is_null($tag->id)) {
Log::debug('Found tag by name ', ['id' => $tag->id]);
$set->push($tag);
}
if (is_null($tag->id)) {
// create new tag
$tag = $repository->store(
[
'tag' => $part,
'date' => null,
'description' => $part,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
]
);
Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]);
$set->push($tag);
}
}
$set = TagSplit::createSetFromSplits($this->user, $this->mapping, $parts);
$this->setCertainty(100);
return $set;

View File

@@ -416,6 +416,7 @@ class ImportValidator
Log::debug('Transaction type is now deposit.');
return $entry;
case AccountType::DEFAULT:
case AccountType::ASSET:
$entry->fields['transaction-type'] = TransactionType::whereType(TransactionType::TRANSFER)->first();
Log::debug('Transaction type is now transfer.');

View File

@@ -112,14 +112,16 @@ class CsvSetup implements SetupInterface
*/
public function getDataForSettings(): array
{
Log::debug('Now in getDataForSettings()');
if ($this->doColumnRoles()) {
Log::debug('doColumnRoles() is true.');
$data = $this->getDataForColumnRoles();
return $data;
}
if ($this->doColumnMapping()) {
Log::debug('doColumnMapping() is true.');
$data = $this->getDataForColumnMapping();
return $data;
@@ -427,10 +429,13 @@ class CsvSetup implements SetupInterface
}
/**
* This method collects the data that will enable a user to choose column content.
*
* @return array
*/
private function getDataForColumnRoles():array
{
Log::debug('Now in getDataForColumnRoles()');
$config = $this->job->configuration;
$data = [
'columns' => [],
@@ -447,15 +452,16 @@ class CsvSetup implements SetupInterface
$end = $start + config('csv.example_rows');
// collect example data in $data['columns']
Log::debug(sprintf('While %s is smaller than %d', $start, $end));
while ($start < $end) {
$row = $reader->fetchOne($start);
Log::debug(sprintf('Row %d has %d columns', $start, count($row)));
// run specifics here:
// and this is the point where the specifix go to work.
foreach ($config['specifics'] as $name => $enabled) {
/** @var SpecificInterface $specific */
$specific = app('FireflyIII\Import\Specifics\\' . $name);
Log::debug(sprintf('Will now apply specific "%s" to row %d.', $name, $start));
// it returns the row, possibly modified:
$row = $specific->run($row);
}

View File

@@ -45,9 +45,11 @@ class RabobankDescription implements SpecificInterface
*/
public function run(array $row): array
{
$oppositeAccount = trim($row[5]);
$oppositeName = trim($row[6]);
$alternateName = trim($row[10]);
Log::debug(sprintf('Now in RabobankSpecific::run(). Row has %d columns', count($row)));
$oppositeAccount = isset($row[5]) ? trim($row[5]) : '';
$oppositeName = isset($row[6]) ? trim($row[6]) : '';
$alternateName = isset($row[10]) ? trim($row[10]) : '';
if (strlen($oppositeAccount) < 1 && strlen($oppositeName) < 1) {
Log::debug(
sprintf(

View File

@@ -78,7 +78,7 @@ class MailError extends Job implements ShouldQueue
$args['ip'] = $this->ipAddress;
Mail::send(
['emails.error-html', 'emails.error'], $args,
['emails.error-html', 'emails.error-text'], $args,
function (Message $message) use ($email) {
if ($email != 'mail@example.com') {
$message->to($email, $email)->subject('Caught an error in Firely III.');

View File

@@ -26,46 +26,11 @@ use Illuminate\Database\Query\JoinClause;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
/**
* FireflyIII\Models\Account
* Class Account
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property \Carbon\Carbon $deleted_at
* @property integer $user_id
* @property integer $account_type_id
* @property string $name
* @property boolean $active
* @property boolean $encrypted
* @property float $virtual_balance
* @property string $iban
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\AccountMeta[] $accountMeta
* @property-read \FireflyIII\Models\AccountType $accountType
* @property-read mixed $name_for_editform
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\PiggyBank[] $piggyBanks
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions
* @property-read \FireflyIII\User $user
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account accountTypeIn($types)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account hasMetaValue($name, $value)
* @property string $startBalance
* @property string $endBalance
* @property float $difference
* @property \Carbon\Carbon $lastActivityDate
* @property float $piggyBalance
* @property float $percentage
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereDeletedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereAccountTypeId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereName($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereActive($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereEncrypted($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereVirtualBalance($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereIban($value)
* @mixin \Eloquent
* @package FireflyIII\Models
*/
class Account extends Model
{
@@ -154,6 +119,20 @@ class Account extends Model
return $this->belongsTo('FireflyIII\Models\AccountType');
}
/**
* @return string
*/
public function getEditNameAttribute(): string
{
$name = $this->name;
if ($this->accountType->type === AccountType::CASH) {
return '';
}
return $name;
}
/**
* FIxxME can return null
*
@@ -189,7 +168,7 @@ class Account extends Model
{
foreach ($this->accountMeta as $meta) {
if ($meta->name == $fieldName) {
return $meta->data;
return strval($meta->data);
}
}

View File

@@ -17,22 +17,9 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* FireflyIII\Models\AccountMeta
* Class AccountMeta
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $account_id
* @property string $name
* @property string $data
* @property-read Account $account
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereAccountId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereName($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereData($value)
* @mixin \Eloquent
* @package FireflyIII\Models
*/
class AccountMeta extends Model
{

View File

@@ -17,18 +17,9 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* FireflyIII\Models\AccountType
* Class AccountType
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $type
* @property-read \Illuminate\Database\Eloquent\Collection|Account[] $accounts
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereType($value)
* @mixin \Eloquent
* @package FireflyIII\Models
*/
class AccountType extends Model
{

View File

@@ -21,41 +21,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* FireflyIII\Models\Attachment
* Class Attachment
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $deleted_at
* @property integer $attachable_id
* @property string $attachable_type
* @property integer $user_id
* @property string $md5
* @property string $filename
* @property string $title
* @property string $description
* @property string $notes
* @property string $mime
* @property integer $size
* @property boolean $uploaded
* @property-read Attachment $attachable
* @property-read \FireflyIII\User $user
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereDeletedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereAttachableId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereAttachableType($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereMd5($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereFilename($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereTitle($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereDescription($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereNotes($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereMime($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereSize($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereUploaded($value)
* @mixin \Eloquent
* @package FireflyIII\Models
*/
class Attachment extends Model
{

View File

@@ -21,48 +21,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
/**
* FireflyIII\Models\Bill
* Class Bill
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $user_id
* @property string $name
* @property string $match
* @property float $amount_min
* @property float $amount_max
* @property \Carbon\Carbon $date
* @property boolean $active
* @property boolean $automatch
* @property string $repeat_freq
* @property integer $skip
* @property boolean $name_encrypted
* @property boolean $match_encrypted
* @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionjournals
* @property-read \FireflyIII\User $user
* @property \Carbon\Carbon $nextExpectedMatch
* @property \Carbon\Carbon $lastFoundMatch
* @property bool $paidInPeriod
* @property string $lastPaidAmount
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereName($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereMatch($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereAmountMin($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereAmountMax($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereDate($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereActive($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereAutomatch($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereRepeatFreq($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereSkip($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereNameEncrypted($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereMatchEncrypted($value)
* @mixin \Eloquent
* @property string $deleted_at
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereDeletedAt($value)
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionJournals
* @package FireflyIII\Models
*/
class Bill extends Model
{

View File

@@ -21,35 +21,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
/**
* FireflyIII\Models\Budget
* Class Budget
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property \Carbon\Carbon $deleted_at
* @property string $name
* @property integer $user_id
* @property boolean $active
* @property boolean $encrypted
* @property-read \Illuminate\Database\Eloquent\Collection|BudgetLimit[] $budgetlimits
* @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionjournals
* @property-read \FireflyIII\User $user
* @property string $dateFormatted
* @property string $budgeted
* @property float $amount
* @property \Carbon\Carbon $date
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereDeletedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereName($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereActive($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereEncrypted($value)
* @mixin \Eloquent
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\LimitRepetition[] $limitrepetitions
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionJournals
* @package FireflyIII\Models
*/
class Budget extends Model
{

View File

@@ -16,28 +16,9 @@ namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* FireflyIII\Models\BudgetLimit
* Class BudgetLimit
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $budget_id
* @property \Carbon\Carbon $startdate
* @property float $amount
* @property boolean $repeats
* @property string $repeat_freq
* @property-read Budget $budget
* @property int $component_id
* @property-read \Illuminate\Database\Eloquent\Collection|LimitRepetition[] $limitrepetitions
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereBudgetId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereAmount($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereRepeats($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereRepeatFreq($value)
* @mixin \Eloquent
* @package FireflyIII\Models
*/
class BudgetLimit extends Model
{

View File

@@ -21,31 +21,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
/**
* FireflyIII\Models\Category
* Class Category
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property \Carbon\Carbon $deleted_at
* @property string $name
* @property integer $user_id
* @property boolean $encrypted
* @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionjournals
* @property-read \FireflyIII\User $user
* @property string $dateFormatted
* @property string $spent
* @property \Carbon\Carbon $lastActivity
* @property string $type
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereDeletedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereName($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereEncrypted($value)
* @mixin \Eloquent
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionJournals
* @package FireflyIII\Models
*/
class Category extends Model
{

View File

@@ -17,21 +17,9 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* FireflyIII\Models\Configuration
* Class Configuration
*
* @mixin \Eloquent
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property \Carbon\Carbon $deleted_at
* @property string $name
* @property string $data
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereDeletedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereName($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereData($value)
* @package FireflyIII\Models
*/
class Configuration extends Model
{

View File

@@ -19,21 +19,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class ExportJob
*
* @package FireflyIII
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $user_id
* @property string $key
* @property string $status
* @property-read \FireflyIII\User $user
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereKey($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereStatus($value)
* @mixin \Eloquent
* @package FireflyIII\Models
*/
class ExportJob extends Model
{

View File

@@ -15,33 +15,14 @@ namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model;
use Log;
use Storage;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* FireflyIII\Models\ImportJob
* Class ImportJob
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $user_id
* @property string $key
* @property string $file_type
* @property string $status
* @property array $configuration
* @property-read \FireflyIII\User $user
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereKey($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereFileType($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereStatus($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereConfiguration($value)
* @mixin \Eloquent
* @property string $extended_status
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereExtendedStatus($value)
* @package FireflyIII\Models
*/
class ImportJob extends Model
{
@@ -171,6 +152,7 @@ class ImportJob extends Model
$disk = Storage::disk('upload');
$encryptedContent = $disk->get($fileName);
$content = Crypt::decrypt($encryptedContent);
Log::debug(sprintf('Content size is %d bytes.', $content));
return $content;
}

View File

@@ -19,28 +19,9 @@ use Illuminate\Database\Eloquent\Model;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* FireflyIII\Models\LimitRepetition
* Class LimitRepetition
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $budget_limit_id
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $enddate
* @property float $amount
* @property-read BudgetLimit $budgetLimit
* @property int $budget_id
* @property string $spent
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereBudgetLimitId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereEnddate($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereAmount($value)
* @mixin \Eloquent
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition after($date)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition before($date)
* @package FireflyIII\Models
*/
class LimitRepetition extends Model
{

View File

@@ -16,28 +16,10 @@ namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use League\CommonMark\CommonMarkConverter;
/**
* FireflyIII\Models\Note
* Class Note
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property string $deleted_at
* @property integer $noteable_id
* @property string $noteable_type
* @property string $title
* @property string $text
* @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $noteable
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereDeletedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereNoteableId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereNoteableType($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereTitle($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereText($value)
* @mixin \Eloquent
* @package FireflyIII\Models
*/
class Note extends Model
{

View File

@@ -21,42 +21,9 @@ use Steam;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* FireflyIII\Models\PiggyBank
* Class PiggyBank
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property \Carbon\Carbon $deleted_at
* @property integer $account_id
* @property string $name
* @property float $targetamount
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $targetdate
* @property integer $order
* @property boolean $encrypted
* @property-read Account $account
* @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankRepetition[] $piggyBankRepetitions
* @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankEvent[] $piggyBankEvents
* @property string $reminder
* @property PiggyBankRepetition $currentRep
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereAccountId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereName($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereTargetamount($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereTargetdate($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereReminder($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereReminderSkip($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereRemindMe($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereOrder($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereDeletedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereEncrypted($value)
* @mixin \Eloquent
* @property boolean $active
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereActive($value)
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Note[] $notes
* @package FireflyIII\Models
*/
class PiggyBank extends Model
{

View File

@@ -16,25 +16,9 @@ namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* FireflyIII\Models\PiggyBankEvent
* Class PiggyBankEvent
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $piggy_bank_id
* @property integer $transaction_journal_id
* @property \Carbon\Carbon $date
* @property float $amount
* @property PiggyBank $piggyBank
* @property-read TransactionJournal $transactionJournal
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent wherePiggyBankId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereTransactionJournalId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereDate($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereAmount($value)
* @mixin \Eloquent
* @package FireflyIII\Models
*/
class PiggyBankEvent extends Model
{

View File

@@ -18,26 +18,9 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
/**
* FireflyIII\Models\PiggyBankRepetition
* Class PiggyBankRepetition
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $piggy_bank_id
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $targetdate
* @property float $currentamount
* @property-read PiggyBank $piggyBank
* @method static \Illuminate\Database\Query\Builder|PiggyBankRepetition onDates($start, $target)
* @method static \Illuminate\Database\Query\Builder|PiggyBankRepetition relevantOnDate($date)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition wherePiggyBankId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereStartdate($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereTargetdate($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereCurrentamount($value)
* @mixin \Eloquent
* @package FireflyIII\Models
*/
class PiggyBankRepetition extends Model
{

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