Compare commits

..

367 Commits
4.2.0 ... 4.2.2

Author SHA1 Message Date
James Cole
8121a384ef Merge branch 'release/4.2.2' 2016-12-18 10:54:46 +01:00
James Cole
8666197e05 Changelog and version bump. 2016-12-18 10:48:05 +01:00
James Cole
1ea2b8bbcb Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Approved. Step name: Proofread
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
2016-12-18 10:41:49 +01:00
James Cole
a71cedd8a9 Merge pull request #474 from JC5/l10n_develop
New Crowdin translations
2016-12-18 10:41:39 +01:00
James Cole
04c5f583f6 Approved. Step name: Proofread 2016-12-18 10:41:08 +01:00
James Cole
7716ff4e8c Update various tests and the composer lock file. 2016-12-18 10:37:59 +01:00
James Cole
6b51a116d1 New translations 2016-12-18 09:41:52 +01:00
James Cole
b2f14dc177 New translations 2016-12-18 09:41:50 +01:00
James Cole
da1d3b82f9 New translations 2016-12-18 09:41:45 +01:00
James Cole
6282d8c828 New translations 2016-12-18 09:41:40 +01:00
James Cole
73129b0ce5 New translations 2016-12-18 09:41:35 +01:00
James Cole
f71e7a2f28 New translations 2016-12-18 09:41:31 +01:00
James Cole
341da327e3 New translations 2016-12-18 09:41:12 +01:00
James Cole
3d8adfa7e4 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
  Approved. Step name: Proofread
2016-12-18 09:27:42 +01:00
James Cole
279d7769f5 This fixes #470 2016-12-18 09:27:27 +01:00
James Cole
b7d3b40353 Merge pull request #471 from JC5/l10n_develop
New Crowdin translations
2016-12-18 09:16:27 +01:00
James Cole
7ecd691ee2 New tests. 2016-12-17 19:19:49 +01:00
James Cole
f3398c7dec This fixes #472 2016-12-17 17:09:46 +01:00
James Cole
90644e662d New translations 2016-12-17 08:41:52 +01:00
James Cole
f5c5cb7fb9 New translations 2016-12-17 08:41:47 +01:00
James Cole
312e79921a New translations 2016-12-17 08:41:43 +01:00
James Cole
b83d346a86 New translations 2016-12-17 08:41:37 +01:00
James Cole
3eed67f108 New translations 2016-12-17 08:41:33 +01:00
James Cole
15f0bc63b2 New translations 2016-12-17 08:41:27 +01:00
James Cole
0a4b0ec929 Approved. Step name: Proofread 2016-12-17 08:41:17 +01:00
James Cole
560f6cbf24 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii: (23 commits)
  New translations
  Approved. Step name: Proofread
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  Approved. Step name: Proofread
  New translations
  Translated
  ...
2016-12-17 08:35:19 +01:00
James Cole
9165e0238f Import related tests. 2016-12-17 08:35:03 +01:00
James Cole
97d6be6809 Merge pull request #469 from JC5/l10n_develop
New Crowdin translations
2016-12-16 08:26:48 +01:00
James Cole
4de14eba0c Fix some routes for the budget report. 2016-12-16 08:07:31 +01:00
James Cole
6c64023bf7 New translations 2016-12-16 07:31:43 +01:00
James Cole
a923c288e6 Approved. Step name: Proofread 2016-12-16 07:31:41 +01:00
James Cole
4c1d8e8e85 New translations 2016-12-15 23:02:39 +01:00
James Cole
02f2def88b New translations 2016-12-15 23:02:38 +01:00
James Cole
4bcacc5d68 New translations 2016-12-15 23:02:35 +01:00
James Cole
913dbe6b1a New translations 2016-12-15 23:02:31 +01:00
James Cole
ce8164dd87 New translations 2016-12-15 23:02:28 +01:00
James Cole
a5b412f546 New translations 2016-12-15 23:02:26 +01:00
James Cole
82bb352624 New translations 2016-12-15 23:02:22 +01:00
James Cole
ebbf2659b1 New translations 2016-12-15 23:02:17 +01:00
James Cole
d0084becea New translations 2016-12-15 23:02:16 +01:00
James Cole
6af2b37ac2 New translations 2016-12-15 23:02:13 +01:00
James Cole
814fc6eabd New translations 2016-12-15 23:02:11 +01:00
James Cole
50278a679a New translations 2016-12-15 23:02:05 +01:00
James Cole
d42e9c75ef New translations 2016-12-15 23:02:03 +01:00
James Cole
00b3dced2c New translations 2016-12-15 23:02:02 +01:00
James Cole
5c0c00188f New translations 2016-12-15 23:01:59 +01:00
James Cole
2ec56626f3 Approved. Step name: Proofread 2016-12-15 23:01:56 +01:00
James Cole
e87456b2f8 New translations 2016-12-15 23:01:52 +01:00
James Cole
3609b515e5 Translated 2016-12-15 23:01:50 +01:00
James Cole
a1609542c3 Translated 2016-12-15 23:01:48 +01:00
James Cole
4c4625583a Approved. Step name: Proofread 2016-12-15 23:01:46 +01:00
James Cole
7b479316ea Approved. Step name: Proofread 2016-12-15 23:01:38 +01:00
James Cole
b021c7690f Basic edit user routine. 2016-12-15 22:56:31 +01:00
James Cole
2be060796e Merge pull request #468 from JC5/l10n_develop
New Crowdin translations
2016-12-15 21:56:24 +01:00
James Cole
1b4d55cca4 Fix various code style issues. 2016-12-15 21:35:33 +01:00
James Cole
a8cea4119d Approved. Step name: Proofread 2016-12-15 17:21:27 +01:00
James Cole
e247aace8d Various code cleanup. 2016-12-15 17:16:46 +01:00
James Cole
41553e9b86 New translations 2016-12-15 14:43:23 +01:00
James Cole
e875587260 New translations 2016-12-15 14:43:18 +01:00
James Cole
5377483345 New translations 2016-12-15 14:43:08 +01:00
James Cole
4112acfb8d New translations 2016-12-15 14:42:56 +01:00
James Cole
f3bc02e11c New translations 2016-12-15 14:42:46 +01:00
James Cole
8e411a898b New translations 2016-12-15 14:42:40 +01:00
James Cole
915edbecc9 New translations 2016-12-15 14:42:28 +01:00
James Cole
975a6c34bf Finished #452 2016-12-15 14:38:05 +01:00
James Cole
cdd988b4de Piggy banks and #452 2016-12-15 14:05:50 +01:00
James Cole
b58bc97422 Code for #452 2016-12-15 13:47:28 +01:00
James Cole
482688ac3c Merge pull request #467 from JC5/l10n_develop
New Crowdin translations
2016-12-15 11:20:24 +01:00
James Cole
aea31b5e28 Budget charts #452 2016-12-15 10:44:06 +01:00
James Cole
d7cbc53b4b Multiply by -1. 2016-12-15 10:41:56 +01:00
James Cole
f74c6c2d19 Updated budget charts [skip ci] 2016-12-15 10:41:10 +01:00
James Cole
3080d2ddc4 New translations 2016-12-15 10:03:53 +01:00
James Cole
4ad5881760 New translations 2016-12-15 10:03:43 +01:00
James Cole
7e55d1a4fd New translations 2016-12-15 10:03:36 +01:00
James Cole
7ef5eed6e2 New translations 2016-12-15 10:03:22 +01:00
James Cole
10aa41a7ea New translations 2016-12-15 10:03:15 +01:00
James Cole
1f9b362b6f New translations 2016-12-15 10:03:01 +01:00
James Cole
4bf9bfb521 Approved. Step name: Proofread 2016-12-15 10:02:26 +01:00
James Cole
1d7119114d New translations [skip ci] 2016-12-15 09:55:10 +01:00
James Cole
e1b6df6fb1 Include budgeted info as well. [skip ci] 2016-12-15 09:54:10 +01:00
James Cole
7cf38bb01e Include budgeted info as well. [skip ci] 2016-12-15 09:52:58 +01:00
James Cole
e34ec22845 Forgot to do * -1. [skip ci] 2016-12-15 09:51:22 +01:00
James Cole
46506abeb8 Forgot to do * -1. [skip ci] 2016-12-15 09:50:22 +01:00
James Cole
95654cc4d4 New budget chart generator 2016-12-15 09:49:35 +01:00
James Cole
47aded820d New test. 2016-12-15 08:53:10 +01:00
James Cole
24444ebf08 Merge pull request #466 from JC5/l10n_develop
New Crowdin translations
2016-12-15 08:41:53 +01:00
James Cole
bdc0df8350 Approved. Step name: Proofread 2016-12-15 08:31:26 +01:00
James Cole
b2c9a2973c Merge pull request #465 from JC5/l10n_develop
New Crowdin translations
2016-12-15 08:27:54 +01:00
James Cole
da2a347511 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
  Approved. Step name: Proofread
  Approved. Step name: Proofread
  Approved. Step name: Proofread
2016-12-15 08:17:17 +01:00
James Cole
6fbc3ba060 New screenshots [skip ci] 2016-12-15 08:16:34 +01:00
James Cole
02eff06cd3 New translations 2016-12-15 08:11:28 +01:00
James Cole
7586d4b494 New translations 2016-12-15 08:01:18 +01:00
James Cole
9059f0fee6 Translated 2016-12-15 07:51:13 +01:00
James Cole
c2af9e3d20 Merge pull request #464 from JC5/l10n_develop
New Crowdin translations
2016-12-14 22:53:58 +01:00
James Cole
0b51366526 New translations 2016-12-14 19:13:32 +01:00
James Cole
e40260bd9c New translations 2016-12-14 19:13:25 +01:00
James Cole
cf2842840d New translations 2016-12-14 19:13:18 +01:00
James Cole
17fa8fcb2c New translations 2016-12-14 19:13:06 +01:00
James Cole
0d2f9864e2 New translations 2016-12-14 19:12:59 +01:00
James Cole
89cbd91204 New translations 2016-12-14 19:12:46 +01:00
James Cole
f4d9b57887 Approved. Step name: Proofread 2016-12-14 19:12:39 +01:00
James Cole
4b2e4afca5 Approved. Step name: Proofread 2016-12-14 19:12:27 +01:00
James Cole
dd1ba30c48 Approved. Step name: Proofread 2016-12-14 19:12:18 +01:00
James Cole
3ba4570691 Merge pull request #463 from JC5/l10n_develop
New Crowdin translations
2016-12-14 18:59:39 +01:00
James Cole
848cfabcba Rearrange code [skip ci] 2016-12-14 18:59:12 +01:00
James Cole
1bbd10b909 New translations 2016-12-14 18:53:48 +01:00
James Cole
a16a4f813d New translations 2016-12-14 18:53:44 +01:00
James Cole
91cfa963b2 New translations 2016-12-14 18:53:42 +01:00
James Cole
a35557eb62 New translations 2016-12-14 18:53:34 +01:00
James Cole
aad4e47b6a New translations 2016-12-14 18:53:32 +01:00
James Cole
1b177723ae New translations 2016-12-14 18:53:25 +01:00
James Cole
99dba92bd3 New translations 2016-12-14 18:53:17 +01:00
James Cole
e13ccff056 New translations 2016-12-14 18:53:14 +01:00
James Cole
46528dd29d New translations 2016-12-14 18:53:08 +01:00
James Cole
4f611ad810 New translations 2016-12-14 18:52:57 +01:00
James Cole
af41985a64 New translations 2016-12-14 18:52:55 +01:00
James Cole
d0864e06b5 Translated 2016-12-14 18:52:41 +01:00
James Cole
6f0366e146 Translated 2016-12-14 18:52:38 +01:00
James Cole
e0cdbcb28c Approved. Step name: Proofread 2016-12-14 18:52:35 +01:00
James Cole
f19b99194c Wording 2016-12-14 18:47:32 +01:00
James Cole
43a55e2e35 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
  Translated
  Translated
  Approved. Step name: Proofread
  Approved. Step name: Proofread
2016-12-14 18:45:27 +01:00
James Cole
b2743825ca Sort account list by name [skip ci] 2016-12-14 18:44:56 +01:00
James Cole
d4f6cce56e New translations 2016-12-14 17:32:30 +01:00
James Cole
6092d206b6 New translations 2016-12-14 17:22:30 +01:00
James Cole
c8ad83cc91 New translations 2016-12-14 13:21:56 +01:00
James Cole
7d31071ff8 New translations 2016-12-14 13:12:12 +01:00
James Cole
c975ef15f1 Translated 2016-12-14 13:12:03 +01:00
James Cole
f855011d34 Translated 2016-12-14 13:12:00 +01:00
James Cole
fbcf0929d8 New translations 2016-12-14 13:01:30 +01:00
James Cole
d89e75cbe8 New translations 2016-12-14 12:51:23 +01:00
James Cole
ccaa42ad74 New translations 2016-12-14 12:41:14 +01:00
James Cole
56d8dce622 New translations 2016-12-14 12:31:07 +01:00
James Cole
c79baf98cf New translations 2016-12-14 12:21:22 +01:00
James Cole
d1cab9f68c Merge pull request #462 from JC5/l10n_develop
New Crowdin translations
2016-12-14 10:37:35 +01:00
James Cole
69c5c93353 This fixes the tests. 2016-12-13 21:09:04 +01:00
James Cole
28ebd683e4 New translations 2016-12-13 21:03:02 +01:00
James Cole
d752edd625 New translations 2016-12-13 21:02:58 +01:00
James Cole
1dab45d493 New translations 2016-12-13 21:02:54 +01:00
James Cole
b99982d02b New translations 2016-12-13 21:02:48 +01:00
James Cole
fff17ac6c1 New translations 2016-12-13 21:02:44 +01:00
James Cole
4086257983 New translations 2016-12-13 21:02:37 +01:00
James Cole
bd9e0ac281 New translations 2016-12-13 21:02:34 +01:00
James Cole
b075d6db5e New translations 2016-12-13 21:02:32 +01:00
James Cole
befd79cf14 New translations 2016-12-13 21:02:26 +01:00
James Cole
07f68d2b14 New translations 2016-12-13 21:02:22 +01:00
James Cole
d14889bd27 Translated 2016-12-13 21:02:11 +01:00
James Cole
91e40c14f9 Translated 2016-12-13 21:02:09 +01:00
James Cole
b7b2206262 Approved. Step name: Proofread 2016-12-13 21:02:01 +01:00
James Cole
f344d0319c Approved. Step name: Proofread 2016-12-13 21:01:50 +01:00
James Cole
0c8a1682b6 Wrong reference #461 [skip ci] 2016-12-13 20:58:51 +01:00
James Cole
39866be3f1 Translations. #461 2016-12-13 20:57:10 +01:00
James Cole
947e82fa0f Fixed final mails for #461 2016-12-13 20:51:10 +01:00
James Cole
0335a64a21 Code for #461 2016-12-13 20:37:38 +01:00
James Cole
a9e57e1c34 First set of code for #461 2016-12-13 17:21:28 +01:00
James Cole
8a8279f97a Merge pull request #459 from JC5/l10n_develop
New Crowdin translations
2016-12-12 20:22:49 +01:00
James Cole
b968889552 Approved. Step name: Proofread 2016-12-12 20:21:52 +01:00
James Cole
4068df5e50 Approved. Step name: Proofread 2016-12-12 20:21:33 +01:00
James Cole
dc42370322 Merge pull request #458 from JC5/l10n_develop
New Crowdin translations
2016-12-12 20:13:58 +01:00
James Cole
8c24f14ee5 New translations 2016-12-12 20:12:47 +01:00
James Cole
494d1743a2 New translations 2016-12-12 20:12:43 +01:00
James Cole
4a30d9f6bb New translations 2016-12-12 20:12:37 +01:00
James Cole
ed6d25067c New translations 2016-12-12 20:12:32 +01:00
James Cole
445ae7e10e New translations 2016-12-12 20:12:25 +01:00
James Cole
6f45609161 New translations 2016-12-12 20:12:20 +01:00
James Cole
f1230e47f7 New translations 2016-12-12 20:11:52 +01:00
James Cole
7e0ef6d43e Better view for accounts and I fixed a html error. 2016-12-12 20:02:33 +01:00
James Cole
14f9da544a This fixes #454 2016-12-12 19:39:54 +01:00
James Cole
5a84036e16 Merge pull request #457 from JC5/l10n_develop
New Crowdin translations
2016-12-12 17:42:05 +01:00
James Cole
4dccf7b7b5 Properly check hashes, issue #456 2016-12-12 17:17:36 +01:00
James Cole
66060dbed4 New translations 2016-12-12 15:32:47 +01:00
James Cole
cfb824588f New translations 2016-12-12 15:32:36 +01:00
James Cole
d2b4316d7a New translations 2016-12-12 15:32:24 +01:00
James Cole
3af69b433d New translations 2016-12-12 15:32:13 +01:00
James Cole
a6733fa255 Translated 2016-12-12 15:32:04 +01:00
James Cole
4277c54009 Approved. Step name: Proofread 2016-12-12 15:32:00 +01:00
James Cole
66baa7554a New translations 2016-12-12 15:31:48 +01:00
James Cole
ffca4b0543 More code for #456 2016-12-12 15:27:56 +01:00
James Cole
3e3c48314f Code for #456 2016-12-12 15:24:47 +01:00
James Cole
06ff450d31 Fixed sort 2016-12-12 08:14:38 +01:00
James Cole
07c57cc640 Merge pull request #453 from JC5/l10n_develop
New Crowdin translations
2016-12-12 07:45:59 +01:00
James Cole
a67f10c99e Wrote export tests. 2016-12-11 18:34:18 +01:00
James Cole
2882bcbf7b New translations 2016-12-11 17:51:47 +01:00
James Cole
67cc5b0280 New translations 2016-12-11 17:51:45 +01:00
James Cole
b42b178b71 New translations 2016-12-11 17:51:38 +01:00
James Cole
7de05cd173 New translations 2016-12-11 17:51:34 +01:00
James Cole
3db43743d9 New translations 2016-12-11 17:51:28 +01:00
James Cole
14638e4ed8 New translations 2016-12-11 17:51:25 +01:00
James Cole
e756b93810 New translations 2016-12-11 17:51:18 +01:00
James Cole
358d83dcfc Changed language strings [skip ci] 2016-12-11 17:49:02 +01:00
James Cole
331c231a94 Small bug fix in bill chart [skip ci] 2016-12-11 17:47:47 +01:00
James Cole
4403b65bae Experimental bill chart [skip ci] 2016-12-11 17:46:30 +01:00
James Cole
a27d80d765 Fix sort [skip ci] 2016-12-11 17:32:48 +01:00
James Cole
04272fff81 Fixed a small bug in the account frontpage chart. 2016-12-11 17:30:55 +01:00
James Cole
e963708c54 Remove from provider as well (#452) 2016-12-11 17:06:23 +01:00
James Cole
08c4542847 Clean up chart code. 2016-12-11 17:05:48 +01:00
James Cole
553e9270e5 More code for #452 2016-12-11 16:38:21 +01:00
James Cole
8a7297e131 Code for currency controller tests. 2016-12-11 16:25:46 +01:00
James Cole
0f260da8e6 More code for issue #452 2016-12-11 16:25:25 +01:00
James Cole
77560ab3a8 Wrote more tests. 2016-12-11 16:02:15 +01:00
James Cole
e3b2f2d9a8 Experimental code for issue #452 2016-12-11 16:02:04 +01:00
James Cole
74e01a52b9 More tests 2016-12-11 14:03:30 +01:00
James Cole
dc28ba42ef More tests 2016-12-11 13:28:13 +01:00
James Cole
406150620a Fixed more tests. 2016-12-11 13:16:56 +01:00
James Cole
43f59a1135 Fixed missing chart. 2016-12-11 11:15:19 +01:00
James Cole
5c02eaa66c Split controller tests. 2016-12-11 11:04:53 +01:00
James Cole
b4eac84097 Update tests, fixes some bugs. 2016-12-11 10:38:06 +01:00
James Cole
ec3b356f86 Fix mass edit and mass delete routes. [skip ci] 2016-12-10 17:55:47 +01:00
James Cole
bf99d5c299 Fixed the account view, changed routes. 2016-12-10 17:54:35 +01:00
James Cole
a297131440 Finished even more tests 2016-12-10 17:46:19 +01:00
James Cole
bae2161ee3 Expand tests. 2016-12-10 16:32:52 +01:00
James Cole
0fe0de1a7f New tests 2016-12-10 07:29:36 +01:00
James Cole
e7845115f6 New tests 2016-12-10 06:54:50 +01:00
James Cole
bc11c3fab2 Working but fairly useless budget report 2016-12-10 06:50:13 +01:00
James Cole
1b7546f3f9 Expand tests. 2016-12-09 18:53:13 +01:00
James Cole
663be30117 Fixed the account overview chart 2016-12-09 18:52:27 +01:00
James Cole
cf34713518 Fix some tests. 2016-12-09 16:30:33 +01:00
James Cole
3f56a8ec53 Expand test routines 2016-12-09 15:17:57 +01:00
James Cole
35d105588b Fix tag assignment for multiple deposits [skip ci] 2016-12-09 14:50:28 +01:00
James Cole
122d988ed2 Add some debug. [skip ci] 2016-12-09 14:42:14 +01:00
James Cole
9fcc5e7a67 Fix decryption bug. 2016-12-09 14:21:26 +01:00
James Cole
9a492c3731 Merge pull request #450 from JC5/l10n_develop
New Crowdin translations
2016-12-09 14:18:18 +01:00
James Cole
4f752031f3 New translations 2016-12-09 07:42:04 +01:00
James Cole
19be8bb891 New translations 2016-12-09 07:42:01 +01:00
James Cole
693e1b08c7 New translations 2016-12-09 07:41:55 +01:00
James Cole
9aad380518 New translations 2016-12-09 07:41:51 +01:00
James Cole
8c518c8d58 New translations 2016-12-09 07:41:44 +01:00
James Cole
9af89a19db New translations 2016-12-09 07:41:40 +01:00
James Cole
939b18b86c New translations 2016-12-09 07:41:31 +01:00
James Cole
108e775a15 New routes 2016-12-09 07:40:00 +01:00
James Cole
653692ade0 Try to test for confirmation errors. 2016-12-09 07:20:48 +01:00
James Cole
72c6bfee7e New bread crumb for user edit 2016-12-09 07:08:43 +01:00
James Cole
ac92939429 Test to see if bread crumb present. 2016-12-09 07:08:31 +01:00
James Cole
052957bbd0 New view for edit user 2016-12-09 07:08:20 +01:00
James Cole
97e6afe3dc New text to be translated. 2016-12-09 07:08:09 +01:00
James Cole
1fd028dfb8 First code for #426 2016-12-09 07:07:53 +01:00
James Cole
c73866f47c Fixed date [skip ci] 2016-12-09 06:28:51 +01:00
James Cole
b0e120abee New translations 2016-12-08 21:52:28 +01:00
James Cole
b2da38d401 New translations 2016-12-08 21:52:22 +01:00
James Cole
cabe2579fa New translations 2016-12-08 21:52:16 +01:00
James Cole
18a845ac55 New translations 2016-12-08 21:52:06 +01:00
James Cole
a4d14f8259 New translations 2016-12-08 21:52:01 +01:00
James Cole
9d084e62f7 New translations 2016-12-08 21:51:53 +01:00
James Cole
0393fcd704 Approved. Step name: Proofread 2016-12-08 21:51:30 +01:00
James Cole
edb5b2ed5e Initial code for new budget report #426 2016-12-08 21:50:20 +01:00
James Cole
529bab1112 Merge branch 'release/4.2.1' 2016-12-08 21:23:19 +01:00
James Cole
ab9212a4c9 last code for 4.2.1 2016-12-08 21:22:42 +01:00
James Cole
b2cbba0f3b Merge pull request #449 from JC5/l10n_develop
New Crowdin translations
2016-12-08 21:04:16 +01:00
James Cole
ca73ef8531 Approved. Step name: Proofread 2016-12-08 20:41:30 +01:00
James Cole
d13490cb6e Approved. Step name: Proofread 2016-12-08 20:41:27 +01:00
James Cole
73566e11c0 Removed some JS code that was not necessary. [skip ci] 2016-12-08 20:33:41 +01:00
James Cole
36ebd0f0ee Expand view. 2016-12-07 21:38:35 +01:00
James Cole
efe290d96c This fixes the tests. 2016-12-07 20:45:26 +01:00
James Cole
da3988cc63 New tests 2016-12-07 20:06:06 +01:00
James Cole
df6f4aecf8 Update tests. 2016-12-07 19:53:41 +01:00
Sander
db1a60b6df Fix display bug 2016-12-07 07:58:55 +00:00
James Cole
d79866f115 Fix route [skip ci] 2016-12-07 06:27:27 +01:00
James Cole
cdd18b229e Refactor some duplicated code 2016-12-06 16:58:39 +01:00
James Cole
cca2de9f1b This should remove some issues from scrutinizer. 2016-12-06 16:55:13 +01:00
James Cole
6a58dbb207 Generic code cleanup. 2016-12-06 16:44:58 +01:00
James Cole
779f461491 Merge pull request #447 from JC5/l10n_develop
New Crowdin translations
2016-12-06 16:44:39 +01:00
James Cole
085eca6c02 New translations 2016-12-06 14:42:07 +01:00
James Cole
25db11a8c7 Translated 2016-12-06 14:22:19 +01:00
James Cole
76bcc68ab9 Merge pull request #446 from JC5/l10n_develop
New Crowdin translations
2016-12-06 10:52:05 +01:00
James Cole
9daefaaca4 Fix sort [skip ci] 2016-12-06 10:42:49 +01:00
James Cole
fbbbcc4e74 Fix category report. 2016-12-06 10:42:13 +01:00
James Cole
2e1f31a7f8 Fix sort. 2016-12-06 10:19:43 +01:00
James Cole
8a0ac81fd0 Fix decrypt exception. 2016-12-06 10:13:48 +01:00
James Cole
cdd50dfdd2 Fix decrypt exception. 2016-12-06 10:12:08 +01:00
James Cole
a05c8ca351 New test files. 2016-12-06 09:16:36 +01:00
James Cole
2ca584f097 Fix test. 2016-12-06 09:12:04 +01:00
James Cole
687da83feb Remove unused collections [skip ci] 2016-12-06 09:09:05 +01:00
James Cole
c799fc655d Final set of route changes. 2016-12-06 09:07:50 +01:00
James Cole
27848f55ce New translations 2016-12-06 09:02:19 +01:00
James Cole
001a6e310e New translations 2016-12-06 09:02:13 +01:00
James Cole
ad00bc2806 New translations 2016-12-06 09:02:08 +01:00
James Cole
d8e3365345 New translations 2016-12-06 09:01:58 +01:00
James Cole
5849fe2c30 New translations 2016-12-06 09:01:55 +01:00
James Cole
690b498197 New translations 2016-12-06 09:01:47 +01:00
James Cole
d5ddd447bc Approved. Step name: Proofread 2016-12-06 09:01:26 +01:00
James Cole
628c7cd055 Many more route fixes. 2016-12-06 08:59:08 +01:00
James Cole
f4887bbbf7 More routes and pages fixed. 2016-12-06 08:15:53 +01:00
James Cole
d8f291be6e Clean up code after changing routes. 2016-12-06 07:48:41 +01:00
James Cole
02257e3887 More routes fixed. 2016-12-06 07:06:20 +01:00
James Cole
bebfbf0b90 Fixing routes 2016-12-06 06:52:17 +01:00
James Cole
9cb3bfaa57 Clean up routes 2016-12-06 06:15:42 +01:00
James Cole
8e2c035536 Cleaned up a lot of routes [skip ci] 2016-12-05 22:19:24 +01:00
James Cole
6b56c2bf7c Many new renamed routes that will break half of Firefly. 2016-12-05 21:58:23 +01:00
James Cole
d91b9e71d5 Remove unused code. 2016-12-05 20:44:20 +01:00
James Cole
344916d57e Catch encrypted opposing accounts. [skip ci] 2016-12-05 20:39:17 +01:00
James Cole
b1ef225bd0 Also fix income/expenses sum 2016-12-05 20:35:13 +01:00
James Cole
b713eae009 Fixed some issues with expense/income overview. 2016-12-05 20:19:26 +01:00
James Cole
098cc88d5f Fix various routes. 2016-12-05 20:01:01 +01:00
James Cole
2476dd38b3 Route clean up 2016-12-04 19:55:15 +01:00
James Cole
8fec569dbb Merge pull request #443 from JC5/l10n_develop
New Crowdin translations
2016-12-04 19:05:34 +01:00
James Cole
ba92aa207c New translations 2016-12-04 18:11:51 +01:00
James Cole
f7abf132e2 New translations 2016-12-04 18:11:44 +01:00
James Cole
38919ae300 New translations 2016-12-04 18:11:34 +01:00
James Cole
bba15cef24 New translations 2016-12-04 18:11:28 +01:00
James Cole
e8792fa218 Translated 2016-12-04 18:11:20 +01:00
James Cole
c5f81d4a94 Approved. Step name: Proofread 2016-12-04 18:11:19 +01:00
James Cole
a7b8c9d94d Translated 2016-12-04 18:11:16 +01:00
James Cole
f4b9b7ae84 Various report updates and code cleanup. 2016-12-04 18:02:19 +01:00
James Cole
905a2432c6 Remove todo from code, as we are supposed to. 2016-12-04 17:17:25 +01:00
James Cole
89e4c3de25 A fairly primitive data validation routine for split transactions. 2016-12-04 17:13:37 +01:00
James Cole
86ea9db37e Extend currency data for issue #439 2016-12-04 09:33:41 +01:00
James Cole
62a9fda1c2 New tests. 2016-12-04 08:11:29 +01:00
James Cole
49f7c1bbc1 Updated composer file [skip ci] 2016-12-03 21:53:08 +01:00
James Cole
9dc6f41c18 Include chart with report 2016-12-03 21:48:40 +01:00
James Cole
0a844e4313 Use format amount routine [skip ci] 2016-12-03 21:26:34 +01:00
James Cole
53daa89fcb Display optimisations. [skip ci] 2016-12-03 21:24:55 +01:00
James Cole
c5d31bccc5 Small table optimisations [skip ci] 2016-12-03 21:12:02 +01:00
James Cole
b032825342 Building report from issue #386 2016-12-03 21:03:20 +01:00
James Cole
8377a2a0de Building report from issue #386 2016-12-03 20:38:13 +01:00
James Cole
57e49c225b Merge pull request #438 from JC5/l10n_develop
New Crowdin translations
2016-12-01 08:05:21 +01:00
James Cole
6638f6fb5c Approved. Step name: Proofread 2016-12-01 08:01:07 +01:00
James Cole
71e1b58f1d Experimental code for issue #435. Let’s try this a few times, see how it works. 2016-11-29 19:34:54 +01:00
James Cole
a87cb0fc0b 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
  Translated
  Translated
  Translated
  New translations
  Translated
  Translated
  Translated
  New translations
  New translations
2016-11-29 19:34:11 +01:00
James Cole
2e65f63e4a Merge pull request #436 from JC5/l10n_develop
New Crowdin translations
2016-11-29 19:33:51 +01:00
James Cole
5fb2db4e28 New translations 2016-11-29 17:41:26 +01:00
James Cole
238ae125b5 New translations 2016-11-29 17:41:20 +01:00
James Cole
110d7f691c New translations 2016-11-29 17:31:42 +01:00
James Cole
9fb9c7e3ee New translations 2016-11-29 17:31:36 +01:00
James Cole
a95b1857fe New translations 2016-11-29 17:21:56 +01:00
James Cole
ea97b817fc New translations 2016-11-29 17:21:49 +01:00
James Cole
0eea85a884 Translated 2016-11-29 17:21:45 +01:00
James Cole
eae4e988be Translated 2016-11-29 17:21:43 +01:00
James Cole
bdf752bf7e Translated 2016-11-29 17:21:42 +01:00
James Cole
a19fed5959 New translations 2016-11-29 17:12:07 +01:00
James Cole
7474553832 Translated 2016-11-29 17:12:04 +01:00
James Cole
52567116c2 Translated 2016-11-29 17:12:02 +01:00
James Cole
a70b369aaf Translated 2016-11-29 17:12:00 +01:00
James Cole
33a9e80d9d New translations 2016-11-29 17:11:58 +01:00
James Cole
96ef409f75 Merge pull request #434 from JC5/l10n_develop
New Crowdin translations
2016-11-29 13:23:05 +01:00
James Cole
8f5152e185 Small code cleanup [skip ci] 2016-11-28 20:52:56 +01:00
James Cole
f5f17d1f40 New translations 2016-11-28 20:41:22 +01:00
James Cole
b960f50f38 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Approved. Step name: Proofread
  New translations
  New translations
  New translations
  New translations
  New translations
  Approved. Step name: Proofread
  New translations
2016-11-28 20:41:04 +01:00
James Cole
72e357b673 Merge pull request #433 from JC5/l10n_develop
New Crowdin translations
2016-11-28 20:40:39 +01:00
James Cole
b33aa733c7 Fix some minor scrutinizer issues. 2016-11-28 20:38:03 +01:00
James Cole
a6a2c0c182 Remove unused language files. 2016-11-28 20:35:12 +01:00
James Cole
3097ab84fa Approved. Step name: Proofread 2016-11-28 20:31:24 +01:00
James Cole
dd9ce3e06d New translations 2016-11-28 19:52:36 +01:00
James Cole
560fc8b01c New translations 2016-11-28 19:52:30 +01:00
James Cole
f72aba6939 New translations 2016-11-28 19:52:24 +01:00
James Cole
03bc74cae9 New translations 2016-11-28 19:52:16 +01:00
James Cole
7eaf8e3eeb New translations 2016-11-28 19:52:11 +01:00
James Cole
b2b4732657 Approved. Step name: Proofread 2016-11-28 19:51:57 +01:00
James Cole
70473b7635 New translations 2016-11-28 19:51:40 +01:00
James Cole
e4a9e23dfb Merge pull request #432 from JC5/l10n_develop
New Crowdin translations
2016-11-28 19:45:44 +01:00
James Cole
f0fd5324ea Fixes #417 2016-11-28 19:45:36 +01:00
James Cole
addebad810 Approved. Step name: Proofread 2016-11-28 19:41:17 +01:00
James Cole
253466c533 Translated 2016-11-28 19:41:15 +01:00
James Cole
885d0f1464 Merge pull request #431 from JC5/l10n_develop
New Crowdin translations
2016-11-28 19:35:46 +01:00
James Cole
4743cc40a2 New translations 2016-11-28 19:02:46 +01:00
James Cole
92bf9c9214 New translations 2016-11-28 19:02:40 +01:00
James Cole
cd80d82ad4 New translations 2016-11-28 19:02:37 +01:00
James Cole
1b7b6a676d New translations 2016-11-28 19:02:34 +01:00
James Cole
1c61afca07 New translations 2016-11-28 19:02:27 +01:00
James Cole
d4d812c195 New translations 2016-11-28 19:02:23 +01:00
James Cole
ab7803f210 New translations 2016-11-28 19:02:18 +01:00
James Cole
11007f0476 New translations 2016-11-28 19:02:13 +01:00
James Cole
6b1884a9e0 New translations 2016-11-28 19:02:10 +01:00
James Cole
1112a0761f Approved. Step name: Proofread 2016-11-28 19:02:04 +01:00
James Cole
807947fcd8 Approved. Step name: Proofread 2016-11-28 19:01:58 +01:00
James Cole
7afd8f99cb Translated 2016-11-28 19:01:53 +01:00
James Cole
b14a15ce49 New translations 2016-11-28 19:01:50 +01:00
James Cole
2e6ad0ce5d New translations 2016-11-28 19:01:48 +01:00
James Cole
8cdbc96aa5 Add BIC support. This fixes #430 2016-11-28 18:55:56 +01:00
371 changed files with 9447 additions and 9723 deletions

View File

@@ -2,6 +2,29 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [4.2.2] - 2016-12-18
### Added
- New budget report (still a bit of a beta)
- Can now edit user
### Changed
- New config for specific events. Still need to build Notifications.
### Fixed
- Various bugs
- Issue #472 thanks to @zjean
## [4.2.1] - 2016-12-09
### Added
- BIC support (see #430)
- New category report section and chart (see the general financial report)
### Changed
- Date range picker now also available on mobile devices (see #435)
- Extended range of amounts for issue #439
- Rewrote all routes. Old bookmarks may break.
## [4.2.0] - 2016-11-27
### Added
- Lots of (empty) tests

View File

@@ -4,9 +4,11 @@
## 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)
[![The index of Firefly III](https://i.nder.be/hurdhgyg/400)](https://i.nder.be/h2b37243) [![The account overview of Firefly III](https://i.nder.be/hnkfkdpr/400)](https://i.nder.be/hv70pbwc)
[![Screenshot](https://i.nder.be/g63q05m0/400)](https://i.nder.be/g63q05m0) [![Screenshot](https://i.nder.be/c2g30ngg/400)](https://i.nder.be/c2g30ngg)
[![The useful financial reports of Firefly III](https://i.nder.be/h7sk6nb7/400)](https://i.nder.be/ccn0u2mp) [![Even more useful reports in Firefly III](https://i.nder.be/g237hr35/400)](https://i.nder.be/gm8hbh7z)
_(You can click on the images for a better view)_
"Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money.

View File

@@ -50,7 +50,7 @@ class CreateImport extends Command
}
/**
*
*
*/
public function handle()
{

View File

@@ -72,12 +72,11 @@ class UpgradeDatabase extends Command
}
$subQuery = TransactionJournal
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->groupBy(['transaction_journals.id'])
->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]);
$subQuery = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->groupBy(['transaction_journals.id'])
->select(['transaction_journals.id', DB::raw('COUNT(transactions.id) AS t_count')]);
$result = DB::table(DB::raw('(' . $subQuery->toSql() . ') AS derived'))
->mergeBindings($subQuery->getQuery())
@@ -98,11 +97,10 @@ class UpgradeDatabase extends Command
try {
/** @var Transaction $opposing */
$opposing = Transaction
::where('transaction_journal_id', $journalId)
->where('amount', $amount)->where('identifier', '=', 0)
->whereNotIn('id', $processed)
->first();
$opposing = Transaction::where('transaction_journal_id', $journalId)
->where('amount', $amount)->where('identifier', '=', 0)
->whereNotIn('id', $processed)
->first();
} catch (QueryException $e) {
Log::error($e->getMessage());
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');

View File

@@ -17,8 +17,6 @@ use Crypt;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
@@ -102,14 +100,13 @@ class VerifyDatabase extends Command
*/
private function reportAccounts()
{
$set = Account
::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('users', 'accounts.user_id', '=', 'users.id')
->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'])
->whereNull('transactions.account_id')
->get(
['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']
);
$set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('users', 'accounts.user_id', '=', 'users.id')
->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'])
->whereNull('transactions.account_id')
->get(
['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']
);
/** @var stdClass $entry */
foreach ($set as $entry) {
@@ -125,12 +122,11 @@ class VerifyDatabase extends Command
*/
private function reportBudgetLimits()
{
$set = Budget
::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
->groupBy(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email'])
->whereNull('budget_limits.id')
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']);
$set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
->groupBy(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email'])
->whereNull('budget_limits.id')
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']);
/** @var stdClass $entry */
foreach ($set as $entry) {
@@ -148,22 +144,21 @@ class VerifyDatabase extends Command
*/
private function reportDeletedAccounts()
{
$set = Account
::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereNotNull('accounts.deleted_at')
->whereNotNull('transactions.id')
->where(
function (Builder $q) {
$q->whereNull('transactions.deleted_at');
$q->orWhereNull('transaction_journals.deleted_at');
}
)
->get(
['accounts.id as account_id', 'accounts.deleted_at as account_deleted_at', 'transactions.id as transaction_id',
'transactions.deleted_at as transaction_deleted_at', 'transaction_journals.id as journal_id',
'transaction_journals.deleted_at as journal_deleted_at']
);
$set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereNotNull('accounts.deleted_at')
->whereNotNull('transactions.id')
->where(
function (Builder $q) {
$q->whereNull('transactions.deleted_at');
$q->orWhereNull('transaction_journals.deleted_at');
}
)
->get(
['accounts.id as account_id', 'accounts.deleted_at as account_deleted_at', 'transactions.id as transaction_id',
'transactions.deleted_at as transaction_deleted_at', 'transaction_journals.id as journal_id',
'transaction_journals.deleted_at as journal_deleted_at']
);
/** @var stdClass $entry */
foreach ($set as $entry) {
$date = is_null($entry->transaction_deleted_at) ? $entry->journal_deleted_at : $entry->transaction_deleted_at;
@@ -187,16 +182,18 @@ class VerifyDatabase extends Command
TransactionType::TRANSFER => [AccountType::EXPENSE, AccountType::REVENUE],
];
foreach ($configuration as $transactionType => $accountTypes) {
$set = TransactionJournal
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id')
->leftJoin('users', 'users.id', '=', 'transaction_journals.user_id')
->where('transaction_types.type', $transactionType)
->whereIn('account_types.type', $accountTypes)
->whereNull('transaction_journals.deleted_at')
->get(['transaction_journals.id', 'transaction_journals.user_id', 'users.email', 'account_types.type as a_type', 'transaction_types.type']);
$set = TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id')
->leftJoin('users', 'users.id', '=', 'transaction_journals.user_id')
->where('transaction_types.type', $transactionType)
->whereIn('account_types.type', $accountTypes)
->whereNull('transaction_journals.deleted_at')
->get(
['transaction_journals.id', 'transaction_journals.user_id', 'users.email', 'account_types.type as a_type',
'transaction_types.type']
);
foreach ($set as $entry) {
$this->error(
sprintf(
@@ -218,19 +215,18 @@ class VerifyDatabase extends Command
*/
private function reportJournals()
{
$set = TransactionJournal
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNotNull('transaction_journals.deleted_at')// USE THIS
->whereNull('transactions.deleted_at')
->whereNotNull('transactions.id')
->get(
[
'transaction_journals.id as journal_id',
'transaction_journals.description',
'transaction_journals.deleted_at as journal_deleted',
'transactions.id as transaction_id',
'transactions.deleted_at as transaction_deleted_at']
);
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNotNull('transaction_journals.deleted_at')// USE THIS
->whereNull('transactions.deleted_at')
->whereNotNull('transactions.id')
->get(
[
'transaction_journals.id as journal_id',
'transaction_journals.description',
'transaction_journals.deleted_at as journal_deleted',
'transactions.id as transaction_id',
'transactions.deleted_at as transaction_deleted_at']
);
/** @var stdClass $entry */
foreach ($set as $entry) {
$this->error(
@@ -245,11 +241,10 @@ class VerifyDatabase extends Command
*/
private function reportNoTransactions()
{
$set = TransactionJournal
::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->groupBy('transaction_journals.id')
->whereNull('transactions.transaction_journal_id')
->get(['transaction_journals.id']);
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->groupBy('transaction_journals.id')
->whereNull('transactions.transaction_journal_id')
->get(['transaction_journals.id']);
foreach ($set as $entry) {
$this->error(
@@ -268,11 +263,11 @@ class VerifyDatabase extends Command
$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']);
->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) {
@@ -315,12 +310,12 @@ class VerifyDatabase extends Command
private function reportTransactions()
{
$set = Transaction::leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNotNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at')
->get(
['transactions.id as transaction_id', 'transactions.deleted_at as transaction_deleted', 'transaction_journals.id as journal_id',
'transaction_journals.deleted_at']
);
->whereNotNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at')
->get(
['transactions.id as transaction_id', 'transactions.deleted_at as transaction_deleted', 'transaction_journals.id as journal_id',
'transaction_journals.deleted_at']
);
/** @var stdClass $entry */
foreach ($set as $entry) {
$this->error(
@@ -335,12 +330,11 @@ class VerifyDatabase extends Command
*/
private function reportTransfersBudgets()
{
$set = TransactionJournal
::distinct()
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
->where('transaction_types.type', TransactionType::TRANSFER)
->whereNotNull('budget_transaction_journal.budget_id')->get(['transaction_journals.id']);
$set = TransactionJournal::distinct()
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
->where('transaction_types.type', TransactionType::TRANSFER)
->whereNotNull('budget_transaction_journal.budget_id')->get(['transaction_journals.id']);
/** @var TransactionJournal $entry */
foreach ($set as $entry) {

View File

@@ -0,0 +1,41 @@
<?php
/**
* BlockedBadLogin.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 Illuminate\Queue\SerializesModels;
/**
* Class LockedOutUser
*
* @package FireflyIII\Events
*/
class BlockedBadLogin extends Event
{
use SerializesModels;
public $email;
public $ipAddress;
/**
* Create a new event instance. This event is triggered when a user gets themselves locked out.
*
* @param string $email
* @param string $ipAddress
*/
public function __construct(string $email, string $ipAddress)
{
$this->email = $email;
$this->ipAddress = $ipAddress;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* BlockedUseOfDomain.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 Illuminate\Queue\SerializesModels;
/**
* Class BlockedUseOfDomain
*
* @package FireflyIII\Events
*/
class BlockedUseOfDomain extends Event
{
use SerializesModels;
public $email;
public $ipAddress;
/**
* Create a new event instance. This event is triggered when a user tries to register with a banned domain (on blocked domain list).
*
* @param string $email
* @param string $ipAddress
*/
public function __construct(string $email, string $ipAddress)
{
$this->email = $email;
$this->ipAddress = $ipAddress;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* BlockedUseOfEmail.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 Illuminate\Queue\SerializesModels;
/**
* Class BlockedUseOfEmail
*
* @package FireflyIII\Events
*/
class BlockedUseOfEmail extends Event
{
use SerializesModels;
public $email;
public $ipAddress;
/**
* Create a new event instance. This event is triggered when a user tries to register with a banned email address (already used before).
*
* @param string $email
* @param string $ipAddress
*/
public function __construct(string $email, string $ipAddress)
{
$this->email = $email;
$this->ipAddress = $ipAddress;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* BlockedUserLogin.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 BlockedUserLogin
*
* @package FireflyIII\Events
*/
class BlockedUserLogin extends Event
{
use SerializesModels;
public $ipAddress;
public $user;
/**
* Create a new event instance. This event is triggered when a blocked user logs in.
*
* @param User $user
* @param string $ipAddress
*/
public function __construct(User $user, string $ipAddress)
{
$this->user = $user;
$this->ipAddress = $ipAddress;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* DeletedUser.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 Illuminate\Queue\SerializesModels;
/**
* Class DeletedUser
*
* @package FireflyIII\Events
*/
class DeletedUser extends Event
{
use SerializesModels;
public $email;
/**
* Create a new event instance. This event is triggered when a user deletes themselves.
*
* @param string $email
*/
public function __construct(string $email)
{
$this->email = $email;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* LockedOutUser.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 Illuminate\Queue\SerializesModels;
/**
* Class LockedOutUser
*
* @package FireflyIII\Events
*/
class LockedOutUser extends Event
{
use SerializesModels;
public $email;
public $ipAddress;
/**
* Create a new event instance. This event is triggered when a user gets themselves locked out.
*
* @param string $email
* @param string $ipAddress
*/
public function __construct(string $email, string $ipAddress)
{
$this->email = $email;
$this->ipAddress = $ipAddress;
}
}

View File

@@ -292,57 +292,56 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
private function getWorkSet()
{
$accountIds = $this->accounts->pluck('id')->toArray();
$this->workSet = Transaction
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin(
'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', '=', DB::raw('opposing.identifier'));
}
)
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('accounts AS opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id')
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', 'transaction_types.id')
->leftJoin('transaction_currencies', 'transaction_journals.transaction_currency_id', '=', 'transaction_currencies.id')
->whereIn('transactions.account_id', $accountIds)
->where('transaction_journals.user_id', $this->job->user_id)
->where('transaction_journals.date', '>=', $this->start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $this->end->format('Y-m-d'))
->where('transaction_journals.completed', 1)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->whereNull('opposing.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transactions.identifier', 'ASC')
->get(
[
'transactions.id',
'transactions.amount',
'transactions.description',
'transactions.account_id',
'accounts.name as account_name',
'accounts.encrypted as account_name_encrypted',
'transactions.identifier',
$this->workSet = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin(
'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', '=', DB::raw('opposing.identifier'));
}
)
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('accounts AS opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id')
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', 'transaction_types.id')
->leftJoin('transaction_currencies', 'transaction_journals.transaction_currency_id', '=', 'transaction_currencies.id')
->whereIn('transactions.account_id', $accountIds)
->where('transaction_journals.user_id', $this->job->user_id)
->where('transaction_journals.date', '>=', $this->start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $this->end->format('Y-m-d'))
->where('transaction_journals.completed', 1)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->whereNull('opposing.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transactions.identifier', 'ASC')
->get(
[
'transactions.id',
'transactions.amount',
'transactions.description',
'transactions.account_id',
'accounts.name as account_name',
'accounts.encrypted as account_name_encrypted',
'transactions.identifier',
'opposing.id as opposing_id',
'opposing.amount AS opposing_amount',
'opposing.description as opposing_description',
'opposing.account_id as opposing_account_id',
'opposing_accounts.name as opposing_account_name',
'opposing_accounts.encrypted as opposing_account_encrypted',
'opposing.identifier as opposing_identifier',
'opposing.id as opposing_id',
'opposing.amount AS opposing_amount',
'opposing.description as opposing_description',
'opposing.account_id as opposing_account_id',
'opposing_accounts.name as opposing_account_name',
'opposing_accounts.encrypted as opposing_account_encrypted',
'opposing.identifier as opposing_identifier',
'transaction_journals.id as transaction_journal_id',
'transaction_journals.date',
'transaction_journals.description as journal_description',
'transaction_journals.encrypted as journal_encrypted',
'transaction_journals.transaction_type_id',
'transaction_types.type as transaction_type',
'transaction_journals.transaction_currency_id',
'transaction_currencies.code AS transaction_currency_code',
'transaction_journals.id as transaction_journal_id',
'transaction_journals.date',
'transaction_journals.description as journal_description',
'transaction_journals.encrypted as journal_encrypted',
'transaction_journals.transaction_type_id',
'transaction_types.type as transaction_type',
'transaction_journals.transaction_currency_id',
'transaction_currencies.code AS transaction_currency_code',
]
);
]
);
}
}

View File

@@ -94,7 +94,7 @@ class UploadCollector extends BasicCollector implements CollectorInterface
*
* @return bool
*/
private function collectVintageUploads():bool
private function collectVintageUploads(): bool
{
// grab upload directory.
$files = $this->uploadDisk->files();

View File

@@ -30,7 +30,7 @@ use ZipArchive;
*
* @package FireflyIII\Export
*/
class Processor
class Processor implements ProcessorInterface
{
/** @var Collection */

View File

@@ -0,0 +1,67 @@
<?php
/**
* ProcessorInterface.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\Export;
use Illuminate\Support\Collection;
/**
* Interface ProcessorInterface
*
* @package FireflyIII\Export
*/
interface ProcessorInterface
{
/**
* Processor constructor.
*
* @param array $settings
*/
public function __construct(array $settings);
/**
* @return bool
*/
public function collectAttachments(): bool;
/**
* @return bool
*/
public function collectJournals(): bool;
/**
* @return bool
*/
public function collectOldUploads(): bool;
/**
* @return bool
*/
public function convertJournals(): bool;
/**
* @return bool
*/
public function createZipFile(): bool;
/**
* @return bool
*/
public function exportJournals(): bool;
/**
* @return Collection
*/
public function getFiles(): Collection;
}

View File

@@ -1,71 +0,0 @@
<?php
/**
* AccountChartGeneratorInterface.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\Chart\Account;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
/**
* Interface AccountChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Account
*/
interface AccountChartGeneratorInterface
{
/**
* @param array $values
* @param array $names
*
* @return array
*/
public function pieChart(array $values, array $names): array;
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function frontpage(Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array;
/**
* @param Account $account
* @param array $labels
* @param array $dataSet
*
* @return array
*/
public function single(Account $account, array $labels, array $dataSet): array;
}

View File

@@ -1,164 +0,0 @@
<?php
/**
* ChartJsAccountChartGenerator.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\Chart\Account;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Support\ChartColour;
use Illuminate\Support\Collection;
/**
* Class ChartJsAccountChartGenerator
*
* @package FireflyIII\Generator\Chart\Account
*/
class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end): array
{
$data = [
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.spent'),
'data' => []]]];
foreach ($accounts as $account) {
if ($account->difference > 0) {
$data['labels'][] = $account->name;
$data['datasets'][0]['data'][] = $account->difference;
}
}
return $data;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function frontpage(Collection $accounts, Carbon $start, Carbon $end): array
{
// language:
$format = (string)trans('config.month_and_day');
$data = ['count' => 0, 'labels' => [], 'datasets' => [],];
$current = clone $start;
while ($current <= $end) {
$data['labels'][] = $current->formatLocalized($format);
$current->addDay();
}
foreach ($accounts as $account) {
$data['datasets'][] = [
'label' => $account->name,
'fillColor' => 'rgba(220,220,220,0.2)',
'strokeColor' => 'rgba(220,220,220,1)',
'pointColor' => 'rgba(220,220,220,1)',
'pointStrokeColor' => '#fff',
'pointHighlightFill' => '#fff',
'pointHighlightStroke' => 'rgba(220,220,220,1)',
'data' => $account->balances,
];
}
$data['count'] = count($data['datasets']);
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
* @param Carbon $end
*
* @return array
*/
public function revenueAccounts(Collection $accounts, Carbon $start, Carbon $end): array
{
$data = [
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.earned'),
'data' => []]]];
foreach ($accounts as $account) {
if ($account->difference > 0) {
$data['labels'][] = $account->name;
$data['datasets'][0]['data'][] = $account->difference;
}
}
return $data;
}
/**
* @param Account $account
* @param array $labels
* @param array $dataSet
*
* @return array
*/
public function single(Account $account, array $labels, array $dataSet): array
{
$data = [
'count' => 1,
'labels' => $labels,
'datasets' => [
[
'label' => $account->name,
'data' => $dataSet,
],
],
];
return $data;
}
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* ChartJsGenerator.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\Chart\Basic;
use FireflyIII\Support\ChartColour;
/**
* Class ChartJsGenerator
*
* @package FireflyIII\Generator\Chart\Basic
*/
class ChartJsGenerator implements GeneratorInterface
{
/**
* Will generate a Chart JS compatible array from the given input. Expects this format
*
* Will take labels for all from first set.
*
* 0: [
* 'label' => 'label of set',
* 'type' => bar or line, optional
* 'entries' =>
* [
* 'label-of-entry' => 'value'
* ]
* ]
* 1: [
* 'label' => 'label of another set',
* 'type' => bar or line, optional
* 'entries' =>
* [
* 'label-of-entry' => 'value'
* ]
* ]
*
*
* @param array $data
*
* @return array
*/
public function multiSet(array $data): array
{
reset($data);
$first = current($data);
$labels = array_keys($first['entries']);
$chartData = [
'count' => count($data),
'labels' => $labels, // take ALL labels from the first set.
'datasets' => [],
];
unset($first, $labels);
foreach ($data as $set) {
$chartData['datasets'][] = [
'label' => $set['label'],
'type' => $set['type'] ?? 'line',
'data' => array_values($set['entries']),
];
}
return $chartData;
}
/**
* Expects data as:
*
* key => value
*
* @param array $data
*
* @return array
*/
public function pieChart(array $data): array
{
$chartData = [
'datasets' => [
0 => [],
],
'labels' => [],
];
$index = 0;
foreach ($data as $key => $value) {
// make larger than 0
if (bccomp($value, '0') === -1) {
$value = bcmul($value, '-1');
}
$chartData['datasets'][0]['data'][] = round($value, 2);
$chartData['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
$chartData['labels'][] = $key;
$index++;
}
return $chartData;
}
/**
* Will generate a (ChartJS) compatible array from the given input. Expects this format:
*
* 'label-of-entry' => value
* 'label-of-entry' => value
*
* @param string $setLabel
* @param array $data
*
* @return array
*/
public function singleSet(string $setLabel, array $data): array
{
$chartData = [
'count' => 1,
'labels' => array_keys($data), // take ALL labels from the first set.
'datasets' => [
[
'label' => $setLabel,
'data' => array_values($data),
],
],
];
return $chartData;
}
}

View File

@@ -0,0 +1,73 @@
<?php
/**
* GeneratorInterface.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\Chart\Basic;
/**
* Interface GeneratorInterface
*
* @package FireflyIII\Generator\Chart\Basic
*/
interface GeneratorInterface
{
/**
* Will generate a (ChartJS) compatible array from the given input. Expects this format:
*
* 0: [
* 'label' => 'label of set',
* 'entries' =>
* [
* 'label-of-entry' => 'value'
* ]
* ]
* 1: [
* 'label' => 'label of another set',
* 'entries' =>
* [
* 'label-of-entry' => 'value'
* ]
* ]
*
*
* @param array $data
*
* @return array
*/
public function multiSet(array $data): array;
/**
* Expects data as:
*
* key => value
*
* @param array $data
*
* @return array
*/
public function pieChart(array $data): array;
/**
* Will generate a (ChartJS) compatible array from the given input. Expects this format:
*
* 'label-of-entry' => value
* 'label-of-entry' => value
*
* @param string $setLabel
* @param array $data
*
* @return array
*/
public function singleSet(string $setLabel, array $data): array;
}

View File

@@ -1,44 +0,0 @@
<?php
/**
* BillChartGeneratorInterface.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\Chart\Bill;
use FireflyIII\Models\Bill;
use Illuminate\Support\Collection;
/**
* Interface BillChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Bill
*/
interface BillChartGeneratorInterface
{
/**
* @param string $paid
* @param string $unpaid
*
* @return array
*/
public function frontpage(string $paid, string $unpaid): array;
/**
* @param Bill $bill
* @param Collection $entries
*
* @return array
*/
public function single(Bill $bill, Collection $entries): array;
}

View File

@@ -1,94 +0,0 @@
<?php
/**
* ChartJsBillChartGenerator.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\Chart\Bill;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Support\ChartColour;
use Illuminate\Support\Collection;
/**
* Class ChartJsBillChartGenerator
*
* @package FireflyIII\Generator\Chart\Bill
*/
class ChartJsBillChartGenerator implements BillChartGeneratorInterface
{
/**
* @param string $paid
* @param string $unpaid
*
* @return array
*/
public function frontpage(string $paid, string $unpaid): array
{
$data = [
'datasets' => [
[
'data' => [round($unpaid, 2), round(bcmul($paid, '-1'), 2)],
'backgroundColor' => [ChartColour::getColour(0), ChartColour::getColour(1)],
],
],
'labels' => [strval(trans('firefly.unpaid')), strval(trans('firefly.paid'))],
];
return $data;
}
/**
* @param Bill $bill
* @param Collection $entries
*
* @return array
*/
public function single(Bill $bill, Collection $entries): array
{
$format = (string)trans('config.month');
$data = ['count' => 3, 'labels' => [], 'datasets' => [],];
$minAmount = [];
$maxAmount = [];
$actualAmount = [];
/** @var Transaction $entry */
foreach ($entries as $entry) {
$data['labels'][] = $entry->date->formatLocalized($format);
$minAmount[] = round($bill->amount_min, 2);
$maxAmount[] = round($bill->amount_max, 2);
// journalAmount has been collected in BillRepository::getJournals
$actualAmount[] = bcmul($entry->transaction_amount, '-1');
}
$data['datasets'][] = [
'type' => 'bar',
'label' => trans('firefly.minAmount'),
'data' => $minAmount,
];
$data['datasets'][] = [
'type' => 'line',
'label' => trans('firefly.billEntry'),
'data' => $actualAmount,
];
$data['datasets'][] = [
'type' => 'bar',
'label' => trans('firefly.maxAmount'),
'data' => $maxAmount,
];
$data['count'] = count($data['datasets']);
return $data;
}
}

View File

@@ -1,47 +0,0 @@
<?php
/**
* BudgetChartGeneratorInterface.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\Chart\Budget;
use Illuminate\Support\Collection;
/**
* Interface BudgetChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Budget
*/
interface BudgetChartGeneratorInterface
{
/**
* @param Collection $entries
* @param string $dateFormat
*
* @return array
*/
public function budgetLimit(Collection $entries, string $dateFormat): array;
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries): array;
/**
* @param array $entries
*
* @return array
*/
public function period(array $entries): array;
}

View File

@@ -1,136 +0,0 @@
<?php
/**
* ChartJsBudgetChartGenerator.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\Chart\Budget;
use Illuminate\Support\Collection;
/**
* Class ChartJsBudgetChartGenerator
*
* @package FireflyIII\Generator\Chart\Budget
*/
class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
{
/**
*
* @param Collection $entries
* @param string $dateFormat
*
* @return array
*/
public function budgetLimit(Collection $entries, string $dateFormat = 'month_and_day'): array
{
$format = strval(trans('config.' . $dateFormat));
$data = [
'labels' => [],
'datasets' => [
[
'label' => 'Amount',
'data' => [],
],
],
];
/** @var array $entry */
foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized($format);
$data['datasets'][0]['data'][] = $entry[1];
}
$data['count'] = count($data['datasets']);
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries): array
{
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
$left = [];
$spent = [];
$overspent = [];
$filtered = $entries->filter(
function ($entry) {
return ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0);
}
);
foreach ($filtered as $entry) {
$data['labels'][] = $entry[0];
$left[] = round($entry[1], 2);
$spent[] = round(bcmul($entry[2], '-1'), 2); // spent is coming in negative, must be positive
$overspent[] = round(bcmul($entry[3], '-1'), 2); // same
}
$data['datasets'][] = [
'label' => trans('firefly.overspent'),
'data' => $overspent,
];
$data['datasets'][] = [
'label' => trans('firefly.left'),
'data' => $left,
];
$data['datasets'][] = [
'label' => trans('firefly.spent'),
'data' => $spent,
];
$data['count'] = 3;
return $data;
}
/**
* @param array $entries
*
* @return array
*/
public function period(array $entries) : array
{
$data = [
'labels' => array_keys($entries),
'datasets' => [
0 => [
'label' => trans('firefly.budgeted'),
'data' => [],
],
1 => [
'label' => trans('firefly.spent'),
'data' => [],
],
],
'count' => 2,
];
foreach ($entries as $label => $entry) {
// data set 0 is budgeted
// data set 1 is spent:
$data['datasets'][0]['data'][] = $entry['budgeted'];
$data['datasets'][1]['data'][] = round(($entry['spent'] * -1), 2);
}
return $data;
}
}

View File

@@ -1,61 +0,0 @@
<?php
/**
* CategoryChartGeneratorInterface.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\Chart\Category;
use Illuminate\Support\Collection;
/**
* Interface CategoryChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Category
*/
interface CategoryChartGeneratorInterface
{
/**
* @param Collection $entries
*
* @return array
*/
public function all(Collection $entries): array;
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries): array;
/**
* @param array $entries
*
* @return array
*/
public function mainReportChart(array $entries): array;
/**
* @param Collection $entries
*
* @return array
*/
public function period(Collection $entries): array;
/**
* @param array $data
*
* @return array
*/
public function pieChart(array $data): array;
}

View File

@@ -1,174 +0,0 @@
<?php
/**
* ChartJsCategoryChartGenerator.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\Chart\Category;
use FireflyIII\Support\ChartColour;
use Illuminate\Support\Collection;
/**
* Class ChartJsCategoryChartGenerator
*
* @package FireflyIII\Generator\Chart\Category
*/
class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
{
/**
* @param Collection $entries
*
* @return array
*/
public function all(Collection $entries): array
{
$data = [
'count' => 2,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.spent'),
'data' => [],
],
[
'label' => trans('firefly.earned'),
'data' => [],
],
],
];
foreach ($entries as $entry) {
$data['labels'][] = $entry[1];
$spent = $entry[2];
$earned = $entry[3];
$data['datasets'][0]['data'][] = bccomp($spent, '0') === 0 ? null : round(bcmul($spent, '-1'), 4);
$data['datasets'][1]['data'][] = bccomp($earned, '0') === 0 ? null : round($earned, 4);
}
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries): array
{
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.spent'),
'data' => [],
],
],
];
foreach ($entries as $entry) {
if ($entry->spent != 0) {
$data['labels'][] = $entry->name;
$data['datasets'][0]['data'][] = round(bcmul($entry->spent, '-1'), 2);
}
}
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
*
* @return array
*/
public function period(Collection $entries): array
{
return $this->all($entries);
}
/**
* @param array $entries
*
* @return array
*/
public function pieChart(array $entries): array
{
$data = [
'datasets' => [
0 => [],
],
'labels' => [],
];
$index = 0;
foreach ($entries as $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

@@ -1,58 +0,0 @@
<?php
/**
* ChartJsPiggyBankChartGenerator.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\Chart\PiggyBank;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Class ChartJsPiggyBankChartGenerator
*
* @package FireflyIII\Generator\Chart\PiggyBank
*/
class ChartJsPiggyBankChartGenerator implements PiggyBankChartGeneratorInterface
{
/**
* @param Collection $set
*
* @return array
*/
public function history(Collection $set): array
{
// language:
$format = (string)trans('config.month_and_day');
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => 'Diff',
'data' => [],
],
],
];
$sum = '0';
foreach ($set as $key => $value) {
$date = new Carbon($key);
$sum = bcadd($sum, $value);
$data['labels'][] = $date->formatLocalized($format);
$data['datasets'][0]['data'][] = round($sum, 2);
}
return $data;
}
}

View File

@@ -1,31 +0,0 @@
<?php
/**
* PiggyBankChartGeneratorInterface.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\Chart\PiggyBank;
use Illuminate\Support\Collection;
/**
* Interface PiggyBankChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\PiggyBank
*/
interface PiggyBankChartGeneratorInterface
{
/**
* @param Collection $set
*
* @return array
*/
public function history(Collection $set): array;
}

View File

@@ -1,180 +0,0 @@
<?php
/**
* ChartJsReportChartGenerator.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\Chart\Report;
use Illuminate\Support\Collection;
/**
* Class ChartJsReportChartGenerator
*
* @package FireflyIII\Generator\Chart\Report
*/
class ChartJsReportChartGenerator implements ReportChartGeneratorInterface
{
/**
* Same as above but other translations.
*
* @param Collection $entries
*
* @return array
*/
public function multiYearInOut(Collection $entries): array
{
$data = [
'count' => 2,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.income'),
'data' => [],
],
[
'label' => trans('firefly.expenses'),
'data' => [],
],
],
];
foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized('%Y');
$data['datasets'][0]['data'][] = round($entry[1], 2);
$data['datasets'][1]['data'][] = round($entry[2], 2);
}
return $data;
}
/**
* @param string $income
* @param string $expense
* @param int $count
*
* @return array
*/
public function multiYearInOutSummarized(string $income, string $expense, int $count): array
{
$data = [
'count' => 2,
'labels' => [trans('firefly.sum_of_years'), trans('firefly.average_of_years')],
'datasets' => [
[
'label' => trans('firefly.income'),
'data' => [],
],
[
'label' => trans('firefly.expenses'),
'data' => [],
],
],
];
$data['datasets'][0]['data'][] = round($income, 2);
$data['datasets'][1]['data'][] = round($expense, 2);
$data['datasets'][0]['data'][] = round(($income / $count), 2);
$data['datasets'][1]['data'][] = round(($expense / $count), 2);
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function netWorth(Collection $entries) : array
{
$format = (string)trans('config.month_and_day');
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.net_worth'),
'data' => [],
],
],
];
foreach ($entries as $entry) {
$data['labels'][] = trim($entry['date']->formatLocalized($format));
$data['datasets'][0]['data'][] = round($entry['net-worth'], 2);
}
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function yearInOut(Collection $entries): array
{
// language:
$format = (string)trans('config.month');
$data = [
'count' => 2,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.income'),
'data' => [],
],
[
'label' => trans('firefly.expenses'),
'data' => [],
],
],
];
foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized($format);
$data['datasets'][0]['data'][] = round($entry[1], 2);
$data['datasets'][1]['data'][] = round($entry[2], 2);
}
return $data;
}
/**
* @param string $income
* @param string $expense
* @param int $count
*
* @return array
*/
public function yearInOutSummarized(string $income, string $expense, int $count): array
{
$data = [
'count' => 2,
'labels' => [trans('firefly.sum_of_year'), trans('firefly.average_of_year')],
'datasets' => [
[
'label' => trans('firefly.income'),
'data' => [],
],
[
'label' => trans('firefly.expenses'),
'data' => [],
],
],
];
$data['datasets'][0]['data'][] = round($income, 2);
$data['datasets'][1]['data'][] = round($expense, 2);
$data['datasets'][0]['data'][] = round(($income / $count), 2);
$data['datasets'][1]['data'][] = round(($expense / $count), 2);
return $data;
}
}

View File

@@ -1,65 +0,0 @@
<?php
/**
* ReportChartGeneratorInterface.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\Chart\Report;
use Illuminate\Support\Collection;
/**
* Interface ReportChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Report
*/
interface ReportChartGeneratorInterface
{
/**
* @param Collection $entries
*
* @return array
*/
public function multiYearInOut(Collection $entries): array;
/**
* @param string $income
* @param string $expense
* @param int $count
*
* @return array
*/
public function multiYearInOutSummarized(string $income, string $expense, int $count): array;
/**
* @param Collection $entries
*
* @return array
*/
public function netWorth(Collection $entries) : array;
/**
* @param Collection $entries
*
* @return array
*/
public function yearInOut(Collection $entries): array;
/**
* @param string $income
* @param string $expense
* @param int $count
*
* @return array
*/
public function yearInOutSummarized(string $income, string $expense, int $count): array;
}

View File

@@ -25,7 +25,7 @@ use Steam;
/**
* Class MonthReportGenerator
*
* @package FireflyIII\Generator\Report\Standard
* @package FireflyIII\Generator\Report\Audit
*/
class MonthReportGenerator implements ReportGeneratorInterface
{
@@ -78,9 +78,9 @@ class MonthReportGenerator implements ReportGeneratorInterface
$auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
}
$reportType = 'audit';
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
$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.
@@ -88,10 +88,9 @@ class MonthReportGenerator implements ReportGeneratorInterface
'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)
@@ -111,6 +110,16 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this;
}
/**
* @param Collection $budgets
*
* @return ReportGeneratorInterface
*/
public function setBudgets(Collection $budgets): ReportGeneratorInterface
{
return $this;
}
/**
* @param Collection $categories
*

View File

@@ -0,0 +1,247 @@
<?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\Budget;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Generator\Report\Support;
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\Budget
*/
class MonthReportGenerator extends Support implements ReportGeneratorInterface
{
/** @var Collection */
private $accounts;
/** @var Collection */
private $budgets;
/** @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());
$budgetIds = join(',', $this->budgets->pluck('id')->toArray());
$expenses = $this->getExpenses();
$accountSummary = $this->summarizeByAccount($expenses);
$budgetSummary = $this->summarizeByBudget($expenses);
$averageExpenses = $this->getAverages($expenses, SORT_ASC);
$topExpenses = $this->getTopExpenses();
// render!
return view('reports.budget.month', compact('accountIds', 'budgetIds', 'accountSummary', 'budgetSummary', 'averageExpenses', 'topExpenses'))
->with('start', $this->start)->with('end', $this->end)
->with('budgets', $this->budgets)
->with('accounts', $this->accounts)
->render();
}
/**
* @param Collection $accounts
*
* @return ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface
{
$this->accounts = $accounts;
return $this;
}
/**
* @param Collection $budgets
*
* @return ReportGeneratorInterface
*/
public function setBudgets(Collection $budgets): ReportGeneratorInterface
{
$this->budgets = $budgets;
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;
}
/**
* @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;
$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])
->setBudgets($this->budgets)->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 getTopExpenses(): Collection
{
$transactions = $this->getExpenses()->sortBy('transaction_amount');
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 summarizeByBudget(Collection $collection): array
{
$result = [];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$jrnlBudId = intval($transaction->transaction_journal_budget_id);
$transBudId = intval($transaction->transaction_budget_id);
$budgetId = max($jrnlBudId, $transBudId);
$result[$budgetId] = $result[$budgetId] ?? '0';
$result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]);
}
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\Budget;
/**
* Class MultiYearReportGenerator
*
* @package FireflyIII\Generator\Report\Budget
*/
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\Budget;
/**
* Class YearReportGenerator
*
* @package FireflyIII\Generator\Report\Budget
*/
class YearReportGenerator extends MonthReportGenerator
{
/**
* Doesn't do anything different.
*/
}

View File

@@ -15,8 +15,8 @@ namespace FireflyIII\Generator\Report\Category;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Generator\Report\Support;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
@@ -95,6 +95,16 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $this;
}
/**
* @param Collection $budgets
*
* @return ReportGeneratorInterface
*/
public function setBudgets(Collection $budgets): ReportGeneratorInterface
{
return $this;
}
/**
* @param Collection $categories
*
@@ -148,8 +158,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
// 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,
@@ -267,14 +275,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
{
$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;
}
@@ -285,14 +285,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
{
$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;
}

View File

@@ -36,6 +36,13 @@ interface ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface;
/**
* @param Collection $budgets
*
* @return ReportGeneratorInterface
*/
public function setBudgets(Collection $budgets): ReportGeneratorInterface;
/**
* @param Collection $categories
*

View File

@@ -63,6 +63,16 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this;
}
/**
* @param Collection $budgets
*
* @return ReportGeneratorInterface
*/
public function setBudgets(Collection $budgets): ReportGeneratorInterface
{
return $this;
}
/**
* @param Collection $categories
*

View File

@@ -60,6 +60,16 @@ class MultiYearReportGenerator implements ReportGeneratorInterface
return $this;
}
/**
* @param Collection $budgets
*
* @return ReportGeneratorInterface
*/
public function setBudgets(Collection $budgets): ReportGeneratorInterface
{
return $this;
}
/**
* @param Collection $categories
*

View File

@@ -60,6 +60,16 @@ class YearReportGenerator implements ReportGeneratorInterface
return $this;
}
/**
* @param Collection $budgets
*
* @return ReportGeneratorInterface
*/
public function setBudgets(Collection $budgets): ReportGeneratorInterface
{
return $this;
}
/**
* @param Collection $categories
*

View File

@@ -11,7 +11,7 @@
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Category;
namespace FireflyIII\Generator\Report;
use FireflyIII\Models\Transaction;
use Illuminate\Support\Collection;

View File

@@ -34,25 +34,25 @@ class BudgetEventHandler
/**
* This method creates a new budget limit repetition when a new budget limit has been created.
*
* @param StoredBudgetLimit $event
* @param StoredBudgetLimit $budgetLimitEvent
*
* @return bool
*/
public function storeRepetition(StoredBudgetLimit $event):bool
public function storeRepetition(StoredBudgetLimit $budgetLimitEvent): bool
{
return $this->processRepetitionChange($event->budgetLimit, $event->end);
return $this->processRepetitionChange($budgetLimitEvent->budgetLimit, $budgetLimitEvent->end);
}
/**
* Updates, if present the budget limit repetition part of a budget limit.
*
* @param UpdatedBudgetLimit $event
* @param UpdatedBudgetLimit $budgetLimitEvent
*
* @return bool
*/
public function updateRepetition(UpdatedBudgetLimit $event): bool
public function updateRepetition(UpdatedBudgetLimit $budgetLimitEvent): bool
{
return $this->processRepetitionChange($event->budgetLimit, $event->end);
return $this->processRepetitionChange($budgetLimitEvent->budgetLimit, $budgetLimitEvent->end);
}
/**
@@ -61,7 +61,7 @@ class BudgetEventHandler
*
* @return bool
*/
private function processRepetitionChange(BudgetLimit $budgetLimit, Carbon $date):bool
private function processRepetitionChange(BudgetLimit $budgetLimit, Carbon $date): bool
{
$set = $budgetLimit->limitrepetitions()
->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00'))

View File

@@ -33,15 +33,15 @@ class StoredJournalEventHandler
/**
* This method connects a new transfer to a piggy bank.
*
* @param StoredTransactionJournal $event
* @param StoredTransactionJournal $storedJournalEvent
*
* @return bool
*/
public function connectToPiggyBank(StoredTransactionJournal $event): bool
public function connectToPiggyBank(StoredTransactionJournal $storedJournalEvent): bool
{
/** @var TransactionJournal $journal */
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
$journal = $storedJournalEvent->journal;
$piggyBankId = $storedJournalEvent->piggyBankId;
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
@@ -101,11 +101,11 @@ class StoredJournalEventHandler
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
$repetition->save();
/** @var PiggyBankEvent $event */
$event = PiggyBankEvent::create(
/** @var PiggyBankEvent $storedJournalEvent */
$storedJournalEvent = PiggyBankEvent::create(
['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]
);
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
Log::debug(sprintf('Created piggy bank event #%d', $storedJournalEvent->id));
return true;
}
@@ -113,14 +113,14 @@ class StoredJournalEventHandler
/**
* This method grabs all the users rules and processes them.
*
* @param StoredTransactionJournal $event
* @param StoredTransactionJournal $storedJournalEvent
*
* @return bool
*/
public function processRules(StoredTransactionJournal $event): bool
public function processRules(StoredTransactionJournal $storedJournalEvent): bool
{
// get all the user's rule groups, with the rules, order by 'order'.
$journal = $event->journal;
$journal = $storedJournalEvent->journal;
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
@@ -150,13 +150,13 @@ class StoredJournalEventHandler
/**
* This method calls a special bill scanner that will check if the stored journal is part of a bill.
*
* @param StoredTransactionJournal $event
* @param StoredTransactionJournal $storedJournalEvent
*
* @return bool
*/
public function scanBills(StoredTransactionJournal $event): bool
public function scanBills(StoredTransactionJournal $storedJournalEvent): bool
{
$journal = $event->journal;
$journal = $storedJournalEvent->journal;
BillScanner::scan($journal);
return true;

View File

@@ -31,14 +31,14 @@ class UpdatedJournalEventHandler
/**
* This method will check all the rules when a journal is updated.
*
* @param UpdatedTransactionJournal $event
* @param UpdatedTransactionJournal $updatedJournalEvent
*
* @return bool
*/
public function processRules(UpdatedTransactionJournal $event):bool
public function processRules(UpdatedTransactionJournal $updatedJournalEvent): bool
{
// get all the user's rule groups, with the rules, order by 'order'.
$journal = $event->journal;
$journal = $updatedJournalEvent->journal;
$groups = $journal->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
@@ -67,13 +67,13 @@ class UpdatedJournalEventHandler
/**
* This method calls a special bill scanner that will check if the updated journal is part of a bill.
*
* @param UpdatedTransactionJournal $event
* @param UpdatedTransactionJournal $updatedJournalEvent
*
* @return bool
*/
public function scanBills(UpdatedTransactionJournal $event): bool
public function scanBills(UpdatedTransactionJournal $updatedJournalEvent): bool
{
$journal = $event->journal;
$journal = $updatedJournalEvent->journal;
BillScanner::scan($journal);
return true;

View File

@@ -15,10 +15,17 @@ namespace FireflyIII\Handlers\Events;
use Exception;
use FireflyConfig;
use FireflyIII\Events\BlockedBadLogin;
use FireflyIII\Events\BlockedUseOfDomain;
use FireflyIII\Events\BlockedUseOfEmail;
use FireflyIII\Events\BlockedUserLogin;
use FireflyIII\Events\ConfirmedUser;
use FireflyIII\Events\DeletedUser;
use FireflyIII\Events\LockedOutUser;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Events\ResentConfirmation;
use FireflyIII\Models\Configuration;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Mail\Message;
@@ -75,6 +82,210 @@ class UserEventHandler
return true;
}
/**
* @param BlockedBadLogin $event
*
* @return bool
*/
public function reportBadLogin(BlockedBadLogin $event)
{
$email = $event->email;
$owner = env('SITE_OWNER');
$ipAddress = $event->ipAddress;
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_bad_login', config('firefly.configuration.mail_for_bad_login'));
Log::debug(sprintf('Now in reportBadLogin for email address %s', $email));
Log::error(sprintf('User %s tried to login with bad credentials.', $email));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
try {
Mail::send(
['emails.blocked-bad-creds-html', 'emails.blocked-bad-creds-text'], ['email' => $email, 'ip' => $ipAddress],
function (Message $message) use ($owner) {
$message->to($owner, $owner)->subject('Blocked login attempt with bad credentials');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param BlockedUserLogin $event
*
* @return bool
*/
public function reportBlockedUser(BlockedUserLogin $event): bool
{
$user = $event->user;
$owner = env('SITE_OWNER');
$email = $user->email;
$ipAddress = $event->ipAddress;
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_blocked_login', config('firefly.configuration.mail_for_blocked_login'));
Log::debug(sprintf('Now in reportBlockedUser for email address %s', $email));
Log::error(sprintf('User #%d (%s) has their accout blocked (blocked_code is "%s") but tried to login.', $user->id, $email, $user->blocked_code));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
// send email message:
try {
Mail::send(
['emails.blocked-login-html', 'emails.blocked-login-text'],
[
'user_id' => $user->id,
'user_address' => $email,
'ip' => $ipAddress,
'code' => $user->blocked_code,
], function (Message $message) use ($owner, $user) {
$message->to($owner, $owner)->subject('Blocked login attempt of blocked user');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param LockedOutUser $event
*
* @return bool
*/
public function reportLockout(LockedOutUser $event): bool
{
$email = $event->email;
$owner = env('SITE_OWNER');
$ipAddress = $event->ipAddress;
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_lockout', config('firefly.configuration.mail_for_lockout'));
Log::debug(sprintf('Now in respondToLockout for email address %s', $email));
Log::error(sprintf('User %s was locked out after too many invalid login attempts.', $email));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
// send email message:
try {
Mail::send(
['emails.locked-out-html', 'emails.locked-out-text'], ['email' => $email, 'ip' => $ipAddress], function (Message $message) use ($owner) {
$message->to($owner, $owner)->subject('User was locked out');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param BlockedUseOfDomain $event
*
* @return bool
*/
public function reportUseBlockedDomain(BlockedUseOfDomain $event): bool
{
$email = $event->email;
$owner = env('SITE_OWNER');
$ipAddress = $event->ipAddress;
$parts = explode('@', $email);
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_blocked_domain', config('firefly.configuration.mail_for_blocked_domain'));
Log::debug(sprintf('Now in reportUseBlockedDomain for email address %s', $email));
Log::error(sprintf('Somebody tried to register using an email address (%s) connected to a banned domain (%s).', $email, $parts[1]));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
// send email message:
try {
Mail::send(
['emails.blocked-registration-html', 'emails.blocked-registration-text'],
[
'email_address' => $email,
'blocked_domain' => $parts[1],
'ip' => $ipAddress,
], function (Message $message) use ($owner) {
$message->to($owner, $owner)->subject('Blocked registration attempt with blocked domain');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param BlockedUseOfEmail $event
*
* @return bool
*/
public function reportUseOfBlockedEmail(BlockedUseOfEmail $event): bool
{
$email = $event->email;
$owner = env('SITE_OWNER');
$ipAddress = $event->ipAddress;
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_blocked_email', config('firefly.configuration.mail_for_blocked_email'));
Log::debug(sprintf('Now in reportUseOfBlockedEmail for email address %s', $email));
Log::error(sprintf('Somebody tried to register using email address %s which is blocked (SHA2 hash).', $email));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
// send email message:
try {
Mail::send(
['emails.blocked-email-html', 'emails.blocked-email-text'],
[
'user_address' => $email,
'ip' => $ipAddress,
], function (Message $message) use ($owner) {
$message->to($owner, $owner)->subject('Blocked registration attempt with blocked email address');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param DeletedUser $event
*
* @return bool
*/
public function saveEmailAddress(DeletedUser $event): bool
{
Preferences::mark();
$email = hash('sha256', $event->email);
Log::debug(sprintf('Hash of email is %s', $email));
/** @var Configuration $configuration */
$configuration = FireflyConfig::get('deleted_users', []);
$content = $configuration->data;
if (!is_array($content)) {
$content = [];
}
$content[] = $email;
$configuration->data = $content;
Log::debug('New content of deleted_users is ', $content);
FireflyConfig::set('deleted_users', $content);
Preferences::mark();
return true;
}
/**
* This method will send a newly registered user a confirmation message, urging him or her to activate their account.
*
@@ -152,7 +363,7 @@ class UserEventHandler
try {
Mail::send(
['emails.registered-html', 'emails.registered-text'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
$message->to($email, $email)->subject('Welcome to Firefly III!');
}
);
} catch (Swift_TransportException $e) {
@@ -194,7 +405,6 @@ class UserEventHandler
}
/**
* @param User $user
* @param string $ipAddress

View File

@@ -79,6 +79,22 @@ class BillLine
$this->bill = $bill;
}
/**
* @return Carbon
*/
public function getLastHitDate(): Carbon
{
return $this->lastHitDate;
}
/**
* @param Carbon $lastHitDate
*/
public function setLastHitDate(Carbon $lastHitDate)
{
$this->lastHitDate = $lastHitDate;
}
/**
* @return string
*/
@@ -151,21 +167,5 @@ 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

@@ -1,85 +0,0 @@
<?php
/**
* Expense.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\Collection;
use Illuminate\Support\Collection;
use stdClass;
/**
*
* Class Expense
*
* @package FireflyIII\Helpers\Collection
*/
class Expense
{
/** @var Collection */
protected $expenses;
/** @var string */
protected $total = '0';
/**
*
*/
public function __construct()
{
$this->expenses = new Collection;
}
/**
* @param stdClass $entry
*/
public function addOrCreateExpense(stdClass $entry)
{
$this->expenses->put($entry->id, $entry);
}
/**
* @param string $add
*/
public function addToTotal(string $add)
{
$add = strval(round($add, 2));
if (bccomp('0', $add) === -1) {
$add = bcmul($add, '-1');
}
// if amount is positive, the original transaction
// was a transfer. But since this is an expense report,
// that amount must be negative.
$this->total = bcadd($this->total, $add);
}
/**
* @return Collection
*/
public function getExpenses(): Collection
{
$set = $this->expenses->sortBy(
function (stdClass $object) {
return $object->amount;
}
);
return $set;
}
/**
* @return string
*/
public function getTotal(): string
{
return strval(round($this->total, 2));
}
}

View File

@@ -1,81 +0,0 @@
<?php
/**
* Income.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\Collection;
use Illuminate\Support\Collection;
use stdClass;
/**
*
* Class Income
*
* @package FireflyIII\Helpers\Collection
*/
class Income
{
/** @var Collection */
protected $incomes;
/** @var string */
protected $total = '0';
/**
*
*/
public function __construct()
{
$this->incomes = new Collection;
}
/**
* @param stdClass $entry
*/
public function addOrCreateIncome(stdClass $entry)
{
$this->incomes->put($entry->id, $entry);
}
/**
* @param string $add
*/
public function addToTotal(string $add)
{
$add = strval(round($add, 2));
$this->total = bcadd($this->total, $add);
}
/**
* @return Collection
*/
public function getIncomes(): Collection
{
$set = $this->incomes->sortByDesc(
function (stdClass $object) {
return $object->amount;
}
);
return $set;
}
/**
* @return string
*/
public function getTotal(): string
{
return strval(round($this->total, 2));
}
}

View File

@@ -1,4 +1,14 @@
<?php
/**
* JournalCollector.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;
@@ -16,6 +26,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\User;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
@@ -60,6 +71,8 @@ class JournalCollector implements JournalCollectorInterface
'account_types.type as account_type',
];
/** @var bool */
private $filterInternalTransfers;
/** @var bool */
private $filterTransfers = false;
/** @var bool */
private $joinedBudget = false;
@@ -127,6 +140,26 @@ class JournalCollector implements JournalCollectorInterface
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function disableInternalFilter(): JournalCollectorInterface
{
$this->filterInternalTransfers = false;
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function enableInternalFilter(): JournalCollectorInterface
{
$this->filterInternalTransfers = true;
return $this;
}
/**
* @return Collection
*/
@@ -138,12 +171,25 @@ class JournalCollector implements JournalCollectorInterface
$set = $this->filterTransfers($set);
Log::debug(sprintf('Count of set after filterTransfers() is %d', $set->count()));
// possibly filter "internal" transfers:
$set = $this->filterInternalTransfers($set);
Log::debug(sprintf('Count of set after filterInternalTransfers() is %d', $set->count()));
// loop for decryption.
$set->each(
function (Transaction $transaction) {
$transaction->date = new Carbon($transaction->date);
$transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description;
$transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : '';
// optionally decrypted:
try {
$transaction->opposing_account_name = Crypt::decrypt($transaction->opposing_account_name);
} catch (DecryptException $e) {
// if this fails its already decrypted.
}
}
);
@@ -501,6 +547,47 @@ class JournalCollector implements JournalCollectorInterface
return $this;
}
/**
* @param Collection $set
*
* @return Collection
*/
private function filterInternalTransfers(Collection $set): Collection
{
if ($this->filterInternalTransfers === false) {
Log::debug('Did NO filtering for internal transfers on given set.');
return $set;
}
if ($this->joinedOpposing === false) {
Log::error('Cannot filter internal transfers because no opposing information is present.');
return $set;
}
$accountIds = $this->accountIds;
$set = $set->filter(
function (Transaction $transaction) use ($accountIds) {
// both id's in $accountids?
if (in_array($transaction->account_id, $accountIds) && in_array($transaction->opposing_account_id, $accountIds)) {
Log::debug(
sprintf(
'Transaction #%d has #%d and #%d in set, so removed',
$transaction->id, $transaction->account_id, $transaction->opposing_account_id
), $accountIds
);
return false;
}
return $transaction;
}
);
return $set;
}
/**
* If the set of accounts used by the collector includes more than one asset
* account, chances are the set include double entries: transfers get selected
@@ -583,6 +670,7 @@ class JournalCollector implements JournalCollectorInterface
private function joinOpposingTables()
{
if (!$this->joinedOpposing) {
Log::debug('joinedOpposing is false');
// join opposing transaction (hard):
$this->query->leftJoin(
'transactions as opposing', function (JoinClause $join) {
@@ -595,11 +683,12 @@ class JournalCollector implements JournalCollectorInterface
$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';
$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';
$this->joinedOpposing = true;
Log::debug('joinedOpposing is now true!');
}
}
@@ -620,20 +709,19 @@ class JournalCollector implements JournalCollectorInterface
*/
private function startQuery(): EloquentBuilder
{
$query = Transaction
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
->whereNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at')
->where('transaction_journals.user_id', $this->user->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC');
/** @var EloquentBuilder $query */
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id')
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
->whereNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at')
->where('transaction_journals.user_id', $this->user->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC');
return $query;

View File

@@ -38,6 +38,16 @@ interface JournalCollectorInterface
*/
public function disableFilter(): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function disableInternalFilter(): JournalCollectorInterface;
/**
* @return JournalCollectorInterface
*/
public function enableInternalFilter(): JournalCollectorInterface;
/**
* @return Collection
*/
@@ -46,7 +56,7 @@ interface JournalCollectorInterface
/**
* @return LengthAwarePaginator
*/
public function getPaginatedJournals():LengthAwarePaginator;
public function getPaginatedJournals(): LengthAwarePaginator;
/**
* @param Collection $accounts

View File

@@ -88,7 +88,7 @@ class Help implements HelpInterface
*
* @return bool
*/
public function hasRoute(string $route):bool
public function hasRoute(string $route): bool
{
return Route::has($route);
}
@@ -99,7 +99,7 @@ class Help implements HelpInterface
*
* @return bool
*/
public function inCache(string $route, string $language):bool
public function inCache(string $route, string $language): bool
{
$line = sprintf('help.%s.%s', $route, $language);
$result = Cache::has($line);
@@ -120,7 +120,6 @@ class Help implements HelpInterface
* @param string $language
* @param string $content
*
* @internal param $title
*/
public function putInCache(string $route, string $language, string $content)
{

View File

@@ -34,7 +34,7 @@ interface HelpInterface
*
* @return string
*/
public function getFromGithub(string $language, string $route):string;
public function getFromGithub(string $language, string $route): string;
/**
* @param string $route

View File

@@ -51,13 +51,13 @@ class BalanceReportHelper implements BalanceReportHelperInterface
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts): Balance
public function getBalanceReport(Collection $accounts, Carbon $start, Carbon $end): Balance
{
$balance = new Balance;
$header = new BalanceHeader;

View File

@@ -26,11 +26,11 @@ use Illuminate\Support\Collection;
interface BalanceReportHelperInterface
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts): Balance;
public function getBalanceReport(Collection $accounts, Carbon $start, Carbon $end): Balance;
}

View File

@@ -42,22 +42,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface
$this->repository = $repository;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array
{
$budgets = $this->repository->getBudgets();
$report = $this->repository->getBudgetPeriodReport($budgets, $accounts, $start, $end);
$data = $this->filterBudgetPeriodReport($report);
return $data;
}
/**
* @param Carbon $start
* @param Carbon $end
@@ -162,31 +146,4 @@ class BudgetReportHelper implements BudgetReportHelperInterface
return $array;
}
/**
* Filters empty results from getBudgetPeriodReport
*
* @param array $data
*
* @return array
*/
private function filterBudgetPeriodReport(array $data): array
{
/**
* @var int $budgetId
* @var array $set
*/
foreach ($data as $budgetId => $set) {
$sum = '0';
foreach ($set['entries'] as $amount) {
$sum = bcadd($amount, $sum);
}
$data[$budgetId]['sum'] = $sum;
if (bccomp('0', $sum) === 0) {
unset($data[$budgetId]);
}
}
return $data;
}
}

View File

@@ -26,15 +26,6 @@ use Illuminate\Support\Collection;
interface BudgetReportHelperInterface
{
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array;
/**
* @param Carbon $start
* @param Carbon $end

View File

@@ -17,19 +17,15 @@ use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Bill as BillCollection;
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\JournalCollectorInterface;
use FireflyIII\Helpers\FiscalHelperInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Category;
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 Illuminate\Support\Collection;
use stdClass;
/**
* Class ReportHelper
@@ -118,7 +114,7 @@ class ReportHelper implements ReportHelperInterface
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts): CategoryCollection
public function getCategoryReport(Collection $accounts, Carbon $start, Carbon $end): CategoryCollection
{
$object = new CategoryCollection;
/** @var CategoryRepositoryInterface $repository */
@@ -136,57 +132,6 @@ class ReportHelper implements ReportHelperInterface
return $object;
}
/**
* Get a full report on the users expenses during the period for a list of accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Expense
*/
public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): Expense
{
$object = new Expense;
/** @var AccountTaskerInterface $tasker */
$tasker = app(AccountTaskerInterface::class);
$collection = $tasker->expenseReport($accounts, $accounts, $start, $end);
/** @var stdClass $entry */
foreach ($collection as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateExpense($entry);
}
return $object;
}
/**
* Get a full report on the users incomes during the period for the given accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Income
*/
public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): Income
{
$object = new Income;
/** @var AccountTaskerInterface $tasker */
$tasker = app(AccountTaskerInterface::class);
$collection = $tasker->incomeReport($accounts, $accounts, $start, $end);
/** @var stdClass $entry */
foreach ($collection as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateIncome($entry);
}
return $object;
}
/**
* @param Carbon $date
*

View File

@@ -49,29 +49,7 @@ interface ReportHelperInterface
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts): CategoryCollection;
/**
* Get a full report on the users expenses during the period for a list of accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Expense
*/
public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): Expense;
/**
* Get a full report on the users incomes during the period for the given accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Income
*/
public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): Income;
public function getCategoryReport(Collection $accounts, Carbon $start, Carbon $end): CategoryCollection;
/**
* @param Carbon $date

View File

@@ -171,6 +171,7 @@ class AccountController extends Controller
'accountRole' => $account->getMeta('accountRole'),
'ccType' => $account->getMeta('ccType'),
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'BIC' => $account->getMeta('BIC'),
'openingBalanceDate' => $openingBalanceDate,
'openingBalance' => $openingBalanceAmount,
'virtualBalance' => $account->virtual_balance,
@@ -234,11 +235,12 @@ class AccountController extends Controller
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$subTitle = $account->name;
$range = Preferences::get('viewRange', '1M')->data;
$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);
$chartUri = route('chart.account.single', [$account->id]);
// grab those journals:
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
@@ -247,7 +249,7 @@ class AccountController extends Controller
// 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'));
return view('accounts.show', compact('account', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
}
/**
@@ -261,6 +263,7 @@ class AccountController extends Controller
$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);
$chartUri = route('chart.account.all', [$account->id]);
// replace with journal collector:
$collector = new JournalCollector(auth()->user());
@@ -272,7 +275,8 @@ class AccountController extends Controller
$start = $repository->oldestJournalDate($account);
$end = $repository->newestJournalDate($account);
return view('accounts.show_with_date', compact('account', 'journals', 'subTitle', 'start', 'end'));
// same call, except "entries".
return view('accounts.show', compact('account', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
}
/**
@@ -281,7 +285,7 @@ class AccountController extends Controller
*
* @return View
*/
public function showWithDate(Account $account, string $date)
public function showByDate(Account $account, string $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
@@ -290,6 +294,7 @@ class AccountController extends Controller
$subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')';
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$chartUri = route('chart.account.period', [$account->id, $carbon->format('Y-m-d')]);
// replace with journal collector:
$collector = new JournalCollector(auth()->user());
@@ -297,7 +302,8 @@ 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', 'start', 'end'));
// same call, except "entries".
return view('accounts.show', compact('account', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri'));
}
/**

View File

@@ -61,8 +61,21 @@ class ConfigurationController extends Controller
$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;
$siteOwner = env('SITE_OWNER');
return view('admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode', 'mustConfirmAccount', 'isDemoSite'));
// email settings:
$sendErrorMessage = [
'mail_for_lockout' => FireflyConfig::get('mail_for_lockout', config('firefly.configuration.mail_for_lockout'))->data,
'mail_for_blocked_domain' => FireflyConfig::get('mail_for_blocked_domain', config('firefly.configuration.mail_for_blocked_domain'))->data,
'mail_for_blocked_email' => FireflyConfig::get('mail_for_blocked_email', config('firefly.configuration.mail_for_blocked_email'))->data,
'mail_for_bad_login' => FireflyConfig::get('mail_for_bad_login', config('firefly.configuration.mail_for_bad_login'))->data,
'mail_for_blocked_login' => FireflyConfig::get('mail_for_blocked_login', config('firefly.configuration.mail_for_blocked_login'))->data,
];
return view(
'admin.configuration.index',
compact('subTitle', 'subTitleIcon', 'singleUserMode', 'mustConfirmAccount', 'isDemoSite', 'sendErrorMessage', 'siteOwner')
);
}
@@ -71,7 +84,7 @@ class ConfigurationController extends Controller
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(ConfigurationRequest $request)
public function postIndex(ConfigurationRequest $request)
{
// get config values:
$data = $request->getConfigurationData();
@@ -81,6 +94,13 @@ class ConfigurationController extends Controller
FireflyConfig::set('must_confirm_account', $data['must_confirm_account']);
FireflyConfig::set('is_demo_site', $data['is_demo_site']);
// email settings
FireflyConfig::set('mail_for_lockout', $data['mail_for_lockout']);
FireflyConfig::set('mail_for_blocked_domain', $data['mail_for_blocked_domain']);
FireflyConfig::set('mail_for_blocked_email', $data['mail_for_blocked_email']);
FireflyConfig::set('mail_for_bad_login', $data['mail_for_bad_login']);
FireflyConfig::set('mail_for_blocked_login', $data['mail_for_blocked_login']);
// flash message
Session::flash('success', strval(trans('firefly.configuration_updated')));
Preferences::mark();

View File

@@ -16,9 +16,13 @@ namespace FireflyIII\Http\Controllers\Admin;
use FireflyConfig;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\UserFormRequest;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Preferences;
use Session;
use URL;
use View;
/**
* Class UserController
@@ -27,28 +31,56 @@ use Preferences;
*/
class UserController extends Controller
{
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
View::share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o');
return $next($request);
}
);
}
/**
* @param User $user
*
* @return int
* @return View
*/
public function edit(User $user)
{
return $user->id;
// put previous url in session if not redirect from store (not "return_to_edit").
if (session('users.edit.fromUpdate') !== true) {
Session::put('users.edit.url', URL::previous());
}
Session::forget('users.edit.fromUpdate');
$subTitle = strval(trans('firefly.edit_user', ['email' => $user->email]));
$subTitleIcon = 'fa-user-o';
$codes = [
'' => strval(trans('firefly.no_block_code')),
'bounced' => strval(trans('firefly.block_code_bounced')),
'expired' => strval(trans('firefly.block_code_expired')),
];
return view('admin.users.edit', compact('user', 'subTitle', 'subTitleIcon', 'codes'));
}
/**
* @param UserRepositoryInterface $repository
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return View
*/
public function index(UserRepositoryInterface $repository)
{
$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;
@@ -77,7 +109,7 @@ class UserController extends Controller
);
return view('admin.users.index', compact('title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'users'));
return view('admin.users.index', compact('subTitle', 'subTitleIcon', 'users'));
}
@@ -128,5 +160,41 @@ class UserController extends Controller
);
}
/**
* @param UserFormRequest $request
* @param User $user
*
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function update(UserFormRequest $request, User $user)
{
$data = $request->getUserData();
// update password
if (strlen($data['password']) > 0) {
$user->password = bcrypt($data['password']);
$user->save();
}
// change blocked status and code:
$user->blocked = $data['blocked'];
$user->blocked_code = $data['blocked_code'];
$user->save();
Session::flash('success', strval(trans('firefly.updated_user', ['email' => $user->email])));
Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('users.edit.fromUpdate', true);
return redirect(route('admin.users.edit', [$user->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return redirect(session('users.edit.url'));
}
}

View File

@@ -14,15 +14,14 @@ namespace FireflyIII\Http\Controllers\Auth;
use Config;
use FireflyConfig;
use FireflyIII\Events\BlockedBadLogin;
use FireflyIII\Events\BlockedUserLogin;
use FireflyIII\Events\LockedOutUser;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\User;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Lang;
use Log;
use Mail;
use Swift_TransportException;
/**
* Class LoginController
@@ -31,16 +30,6 @@ use Swift_TransportException;
*/
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
@@ -49,7 +38,7 @@ class LoginController extends Controller
*
* @var string
*/
protected $redirectTo = '/home';
protected $redirectTo = '/';
/**
* Create a new controller instance.
@@ -71,19 +60,17 @@ class LoginController extends Controller
public function login(Request $request)
{
$this->validateLogin($request);
// 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.
$lockedOut = $this->hasTooManyLoginAttempts($request);
if ($lockedOut) {
$this->fireLockoutEvent($request);
event(new LockedOutUser($request->get('email'), $request->ip()));
return $this->sendLockoutResponse($request);
}
$credentials = $this->credentials($request);
$credentials['blocked'] = 0; // most not be blocked.
$credentials['blocked'] = 0; // must not be blocked.
if ($this->guard()->attempt($credentials, $request->has('remember'))) {
return $this->sendLoginResponse($request);
@@ -94,10 +81,15 @@ class LoginController extends Controller
/** @var User $foundUser */
$foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first();
if (!is_null($foundUser)) {
// if it exists, show message:
// user exists, but is blocked:
$code = strlen(strval($foundUser->blocked_code)) > 0 ? $foundUser->blocked_code : 'general_blocked';
$errorMessage = strval(trans('firefly.' . $code . '_error', ['email' => $credentials['email']]));
$this->reportBlockedUserLoginAttempt($foundUser, $code, $request->ip());
event(new BlockedUserLogin($foundUser, $request->ip()));
}
// simply a bad login.
if (is_null($foundUser)) {
event(new BlockedBadLogin($credentials['email'], $request->ip()));
}
// If the login attempt was unsuccessful we will increment the number of attempts
@@ -167,34 +159,4 @@ class LoginController extends Controller
]
);
}
/**
* Send a message home about the blocked attempt to login.
* Perhaps in a later stage, simply log these messages.
*
* @param User $user
* @param string $code
* @param string $ipAddress
*/
private function reportBlockedUserLoginAttempt(User $user, string $code, string $ipAddress)
{
try {
$email = env('SITE_OWNER', false);
$fields = [
'user_id' => $user->id,
'user_address' => $user->email,
'code' => $code,
'ip' => $ipAddress,
];
Mail::send(
['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) . '.');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
}
}

View File

@@ -31,16 +31,6 @@ use Illuminate\Support\Facades\Password;
*/
class PasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;

View File

@@ -14,9 +14,11 @@ namespace FireflyIII\Http\Controllers\Auth;
use Auth;
use Config;
use FireflyConfig;
use FireflyIII\Events\BlockedUseOfDomain;
use FireflyIII\Events\BlockedUseOfEmail;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
@@ -34,16 +36,6 @@ use Validator;
*/
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
@@ -93,8 +85,19 @@ class RegisterController extends Controller
if ($this->isBlockedDomain($data['email'])) {
$validator->getMessageBag()->add('email', (string)trans('validation.invalid_domain'));
$this->reportBlockedDomainRegistrationAttempt($data['email'], $request->ip());
event(new BlockedUseOfDomain($data['email'], $request->ip()));
$this->throwValidationException($request, $validator);
}
// is user a deleted user?
$hash = hash('sha256', $data['email']);
$configuration = FireflyConfig::get('deleted_users', []);
$set = $configuration->data;
Log::debug(sprintf('Hash of email is %s', $hash));
Log::debug('Hashes of deleted users: ', $set);
if (in_array($hash, $set)) {
$validator->getMessageBag()->add('email', (string)trans('validation.deleted_user'));
event(new BlockedUseOfEmail($data['email'], $request->ip()));
$this->throwValidationException($request, $validator);
}
@@ -202,31 +205,4 @@ class RegisterController extends Controller
return false;
}
/**
* Send a message home about a blocked domain and the address attempted to register.
*
* @param string $registrationMail
* @param string $ipAddress
*/
private function reportBlockedDomainRegistrationAttempt(string $registrationMail, string $ipAddress)
{
try {
$email = env('SITE_OWNER', false);
$parts = explode('@', $registrationMail);
$domain = $parts[1];
$fields = [
'email_address' => $registrationMail,
'blocked_domain' => $domain,
'ip' => $ipAddress,
];
Mail::send(
['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 . '.');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
}
}

View File

@@ -22,16 +22,6 @@ use Illuminate\Foundation\Auth\ResetsPasswords;
*/
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;

View File

@@ -38,10 +38,16 @@ class TwoFactorController extends Controller
$user = auth()->user();
// to make sure the validator in the next step gets the secret, we push it in session
$secret = Preferences::get('twoFactorAuthSecret', '')->data;
$secret = Preferences::get('twoFactorAuthSecret', null)->data;
$title = strval(trans('firefly.two_factor_title'));
if (strlen($secret) === 0) {
// make sure the user has two factor configured:
$has2FA = Preferences::get('twoFactorAuthEnabled', null)->data;
if (is_null($has2FA) || $has2FA === false) {
return redirect(route('index'));
}
if (strlen(strval($secret)) === 0) {
throw new FireflyException('Your two factor authentication secret is empty, which it cannot be at this point. Please check the log files.');
}
Session::flash('two-factor-secret', $secret);

View File

@@ -299,12 +299,12 @@ class BudgetController extends Controller
public function show(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget)
{
/** @var Carbon $start */
$start = session('first', Carbon::create()->startOfYear());
$end = new Carbon;
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
$start = session('first', Carbon::create()->startOfYear());
$end = new Carbon;
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
$repetition = null;
// collector:
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
@@ -332,7 +332,7 @@ class BudgetController extends Controller
* @return View
* @throws FireflyException
*/
public function showWithRepetition(Budget $budget, LimitRepetition $repetition)
public function showByRepetition(Budget $budget, LimitRepetition $repetition)
{
if ($repetition->budgetLimit->budget->id != $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');

View File

@@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\CategoryFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Category;
@@ -60,7 +61,6 @@ class CategoryController extends Controller
*/
public function create()
{
// put previous url in session if not redirect from store (not "create another").
if (session('categories.create.fromStore') !== true) {
Session::put('categories.create.url', URL::previous());
}
@@ -190,7 +190,7 @@ class CategoryController extends Controller
$subTitleIcon = 'fa-bar-chart';
// use journal collector
$collector = new JournalCollector(auth()->user());
$collector = app(JournalCollectorInterface::class);
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
$journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id);
@@ -245,7 +245,7 @@ class CategoryController extends Controller
*
* @return View
*/
public function showWithDate(Category $category, string $date)
public function showByDate(Category $category, string $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
@@ -257,13 +257,13 @@ class CategoryController extends Controller
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// new collector:
$collector = new JournalCollector(auth()->user());
$collector = app(JournalCollectorInterface::class);
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
$journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id . '/' . $date);
return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
return view('categories.show-by-date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
}
/**

View File

@@ -16,7 +16,7 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
@@ -42,7 +42,7 @@ use Steam;
class AccountController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface */
/** @var GeneratorInterface */
protected $generator;
/**
@@ -51,8 +51,49 @@ class AccountController extends Controller
public function __construct()
{
parent::__construct();
// create chart generator:
$this->generator = app(AccountChartGeneratorInterface::class);
$this->generator = app(GeneratorInterface::class);
}
/**
* @param Account $account
*
* @return \Illuminate\Http\JsonResponse
*/
public function all(Account $account)
{
$cache = new CacheProperties();
$cache->addProperty('chart.account.all');
$cache->addProperty($account->id);
if ($cache->has()) {
Log::debug('Return chart.account.all from cache.');
return Response::json($cache->get());
}
Log::debug('Regenerate chart.account.all from scratch.');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$start = $repository->oldestJournalDate($account);
$end = new Carbon;
$format = (string)trans('config.month_and_day');
$range = Steam::balanceInRange($account, $start, $end);
$current = clone $start;
$previous = array_values($range)[0];
$chartData = [];
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
$balance = $range[$theDate] ?? $previous;
$label = $current->formatLocalized($format);
$chartData[$label] = $balance;
$previous = $balance;
$current->addDay();
}
$data = $this->generator->singleSet($account->name, $chartData);
$cache->store($data);
return Response::json($data);
}
/**
@@ -69,36 +110,29 @@ class AccountController extends Controller
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expenseAccounts');
$cache->addProperty('accounts');
$cache->addProperty('chart.account.expense-accounts');
if ($cache->has()) {
return Response::json($cache->get());
}
$accounts = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
$start->subDay();
$accounts = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
$ids = $accounts->pluck('id')->toArray();
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$chartData = [];
$accounts->each(
function (Account $account) use ($startBalances, $endBalances) {
$id = $account->id;
$startBalance = $startBalances[$id] ?? '0';
$endBalance = $endBalances[$id] ?? '0';
$diff = bcsub($endBalance, $startBalance);
$account->difference = round($diff, 2);
foreach ($accounts as $account) {
$id = $account->id;
$startBalance = $startBalances[$id] ?? '0';
$endBalance = $endBalances[$id] ?? '0';
$diff = bcsub($endBalance, $startBalance);
if (bccomp($diff, '0') !== 0) {
$chartData[$account->name] = round($diff, 2);
}
);
$accounts = $accounts->sortByDesc(
function (Account $account) {
return $account->difference;
}
);
$data = $this->generator->expenseAccounts($accounts, $start, $end);
}
arsort($chartData);
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
$cache->store($data);
return Response::json($data);
@@ -112,34 +146,39 @@ class AccountController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseByBudget(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
public function expenseBudget(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expenseByBudget');
$cache->addProperty('chart.account.expense-budget');
if ($cache->has()) {
return Response::json($cache->get());
}
// grab all journals:
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionType::WITHDRAWAL]);
$collector->setAccounts(new Collection([$account]))
->setRange($start, $end)
->withBudgetInformation()
->setTypes([TransactionType::WITHDRAWAL]);
$transactions = $collector->getJournals();
$chartData = [];
$result = [];
$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);
$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);
foreach ($result as $budgetId => $amount) {
$chartData[$names[$budgetId]] = $amount;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
@@ -153,32 +192,36 @@ class AccountController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
public function expenseCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expenseByCategory');
$cache->addProperty('chart.account.expense-category');
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 = [];
$chartData = [];
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$jrnlCatId = intval($transaction->transaction_journal_category_id);
$transCatId = intval($transaction->transaction_category_id);
$categoryId = max($jrnlCatId, $transCatId);
$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);
foreach ($result as $categoryId => $amount) {
$chartData[$names[$categoryId]] = $amount;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
@@ -194,12 +237,20 @@ class AccountController extends Controller
*/
public function frontpage(AccountRepositoryInterface $repository)
{
$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);
$start = clone session('start', Carbon::now()->startOfMonth());
$end = clone session('end', Carbon::now()->endOfMonth());
$defaultSet = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray();
Log::debug('Default set is ', $defaultSet);
$frontPage = Preferences::get('frontPageAccounts', $defaultSet);
Log::debug('Frontpage preference set is ', $frontPage->data);
if (count($frontPage->data) === 0) {
$frontPage->data = $defaultSet;
Log::debug('frontpage set is empty!');
$frontPage->save();
}
$accounts = $repository->getAccountsById($frontPage->data);
return Response::json($this->accountBalanceChart($start, $end, $accounts));
return Response::json($this->accountBalanceChart($accounts, $start, $end));
}
/**
@@ -210,13 +261,13 @@ class AccountController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*/
public function incomeByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
public function incomeCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('incomeByCategory');
$cache->addProperty('chart.account.income-category');
if ($cache->has()) {
return Response::json($cache->get());
}
@@ -225,23 +276,74 @@ class AccountController extends Controller
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::DEPOSIT]);
$transactions = $collector->getJournals();
$result = [];
$chartData = [];
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$jrnlCatId = intval($transaction->transaction_journal_category_id);
$transCatId = intval($transaction->transaction_category_id);
$categoryId = max($jrnlCatId, $transCatId);
$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);
foreach ($result as $categoryId => $amount) {
$chartData[$names[$categoryId]] = $amount;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param Account $account
* @param string $date
*
* @return \Illuminate\Http\JsonResponse
* @throws FireflyException
*/
public function period(Account $account, string $date)
{
try {
$start = new Carbon($date);
} catch (Exception $e) {
Log::error($e->getMessage());
throw new FireflyException('"' . e($date) . '" does not seem to be a valid date. Should be in the format YYYY-MM-DD');
}
$range = Preferences::get('viewRange', '1M')->data;
$end = Navigation::endOfPeriod($start, $range);
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('chart.account.period');
$cache->addProperty($account->id);
if ($cache->has()) {
return Response::json($cache->get());
}
$format = (string)trans('config.month_and_day');
$range = Steam::balanceInRange($account, $start, $end);
$current = clone $start;
$previous = array_values($range)[0];
$chartData = [];
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
$balance = $range[$theDate] ?? $previous;
$label = $current->formatLocalized($format);
$chartData[$label] = $balance;
$previous = $balance;
$current->addDay();
}
$data = $this->generator->singleSet($account->name, $chartData);
$cache->store($data);
return Response::json($data);
}
/**
* Shows the balances for a given set of dates and accounts.
*
@@ -251,9 +353,9 @@ class AccountController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*/
public function report(Carbon $start, Carbon $end, Collection $accounts)
public function report(Collection $accounts, Carbon $start, Carbon $end)
{
return Response::json($this->accountBalanceChart($start, $end, $accounts));
return Response::json($this->accountBalanceChart($accounts, $start, $end));
}
/**
@@ -265,13 +367,13 @@ class AccountController extends Controller
*/
public function revenueAccounts(AccountRepositoryInterface $repository)
{
$start = clone session('start', Carbon::now()->startOfMonth());
$end = clone session('end', Carbon::now()->endOfMonth());
$cache = new CacheProperties;
$start = clone session('start', Carbon::now()->startOfMonth());
$end = clone session('end', Carbon::now()->endOfMonth());
$chartData = [];
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('revenueAccounts');
$cache->addProperty('accounts');
$cache->addProperty('chart.account.revenue-accounts');
if ($cache->has()) {
return Response::json($cache->get());
}
@@ -282,25 +384,19 @@ class AccountController extends Controller
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$accounts->each(
function (Account $account) use ($startBalances, $endBalances) {
$id = $account->id;
$startBalance = $startBalances[$id] ?? '0';
$endBalance = $endBalances[$id] ?? '0';
$diff = bcsub($endBalance, $startBalance);
$diff = bcmul($diff, '-1');
$account->difference = round($diff, 2);
foreach ($accounts as $account) {
$id = $account->id;
$startBalance = $startBalances[$id] ?? '0';
$endBalance = $endBalances[$id] ?? '0';
$diff = bcsub($endBalance, $startBalance);
$diff = bcmul($diff, '-1');
if (bccomp($diff, '0') !== 0) {
$chartData[$account->name] = round($diff, 2);
}
);
}
$accounts = $accounts->sortByDesc(
function (Account $account) {
return $account->difference;
}
);
$data = $this->generator->revenueAccounts($accounts, $start, $end);
arsort($chartData);
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
$cache->store($data);
return Response::json($data);
@@ -322,8 +418,7 @@ class AccountController extends Controller
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('frontpage');
$cache->addProperty('single');
$cache->addProperty('chart.account.single');
$cache->addProperty($account->id);
if ($cache->has()) {
return Response::json($cache->get());
@@ -333,112 +428,65 @@ class AccountController extends Controller
$range = Steam::balanceInRange($account, $start, $end);
$current = clone $start;
$previous = array_values($range)[0];
$labels = [];
$chartData = [];
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
$balance = $range[$theDate] ?? $previous;
$labels[] = $current->formatLocalized($format);
$chartData[] = $balance;
$previous = $balance;
$theDate = $current->format('Y-m-d');
$balance = $range[$theDate] ?? $previous;
$label = $current->formatLocalized($format);
$chartData[$label] = $balance;
$previous = $balance;
$current->addDay();
}
$data = $this->generator->single($account, $labels, $chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param Account $account
* @param string $date
*
* @return \Illuminate\Http\JsonResponse
* @throws FireflyException
*/
public function specificPeriod(Account $account, string $date)
{
try {
$start = new Carbon($date);
} catch (Exception $e) {
Log::error($e->getMessage());
throw new FireflyException('"' . e($date) . '" does not seem to be a valid date. Should be in the format YYYY-MM-DD');
}
$range = Preferences::get('viewRange', '1M')->data;
$end = Navigation::endOfPeriod($start, $range);
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('frontpage');
$cache->addProperty('specificPeriod');
$cache->addProperty($account->id);
if ($cache->has()) {
return Response::json($cache->get());
}
$format = (string)trans('config.month_and_day');
$range = Steam::balanceInRange($account, $start, $end);
$current = clone $start;
$previous = array_values($range)[0];
$labels = [];
$chartData = [];
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
$balance = $range[$theDate] ?? $previous;
$labels[] = $current->formatLocalized($format);
$chartData[] = $balance;
$previous = $balance;
$current->addDay();
}
$data = $this->generator->single($account, $labels, $chartData);
$data = $this->generator->singleSet($account->name, $chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
private function accountBalanceChart(Carbon $start, Carbon $end, Collection $accounts): array
private function accountBalanceChart(Collection $accounts, Carbon $start, Carbon $end): array
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('account-balance-chart');
$cache->addProperty('chart.account.account-balance-chart');
$cache->addProperty($accounts);
if ($cache->has()) {
Log::debug('Return chart.account.account-balance-chart from cache.');
return $cache->get();
}
Log::debug('Regenerate chart.account.account-balance-chart from scratch.');
$chartData = [];
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();
$currentSet = [
'label' => $account->name,
'entries' => [],
];
$currentStart = clone $start;
$range = Steam::balanceInRange($account, $start, clone $end);
$previous = round(array_values($range)[0], 2);
while ($currentStart <= $end) {
$format = $currentStart->format('Y-m-d');
$label = $currentStart->formatLocalized(strval(trans('config.month_and_day')));
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
$previous = $balance;
$currentStart->addDay();
$currentSet['entries'][$label] = $balance;
}
$account->balances = $balances;
$chartData[] = $currentSet;
}
$data = $this->generator->frontpage($accounts, $start, $end);
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return $data;

View File

@@ -14,8 +14,8 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
@@ -32,7 +32,7 @@ use Response;
class BillController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface */
/** @var GeneratorInterface */
protected $generator;
/**
@@ -41,8 +41,7 @@ class BillController extends Controller
public function __construct()
{
parent::__construct();
// create chart generator:
$this->generator = app(BillChartGeneratorInterface::class);
$this->generator = app(GeneratorInterface::class);
}
/**
@@ -54,45 +53,81 @@ class BillController extends Controller
*/
public function frontpage(BillRepositoryInterface $repository)
{
$start = session('start', Carbon::now()->startOfMonth());
$end = session('end', Carbon::now()->endOfMonth());
$paid = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
$unpaid = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
$data = $this->generator->frontpage($paid, $unpaid);
$start = session('start', Carbon::now()->startOfMonth());
$end = session('end', Carbon::now()->endOfMonth());
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('chart.bill.frontpage');
if ($cache->has()) {
return Response::json($cache->get());
}
$paid = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
$unpaid = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
$chartData = [
strval(trans('firefly.unpaid')) => $unpaid,
strval(trans('firefly.paid')) => $paid,
];
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* Shows the overview for a bill. The min/max amount and matched journals.
* @param JournalCollectorInterface $collector
* @param Bill $bill
*
* @param Bill $bill
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function single(Bill $bill)
public function single(JournalCollectorInterface $collector, Bill $bill)
{
$cache = new CacheProperties;
$cache->addProperty('single');
$cache->addProperty('bill');
$cache->addProperty('chart.bill.single');
$cache->addProperty($bill->id);
if ($cache->has()) {
return Response::json($cache->get());
}
// get first transaction or today for start:
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]));
$results = $collector->getJournals();
// resort:
$results = $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->getJournals();
$results = $results->sortBy(
function (Transaction $transaction) {
return $transaction->date->format('U');
}
);
$data = $this->generator->single($bill, $results);
$chartData = [
[
'type' => 'bar',
'label' => trans('firefly.min-amount'),
'entries' => [],
],
[
'type' => 'bar',
'label' => trans('firefly.max-amount'),
'entries' => [],
],
[
'type' => 'line',
'label' => trans('firefly.journal-amount'),
'entries' => [],
],
];
/** @var Transaction $entry */
foreach ($results as $entry) {
$date = $entry->date->formatLocalized(strval(trans('config.month_and_day')));
// minimum amount of bill:
$chartData[0]['entries'][$date] = $bill->amount_min;
// maximum amount of bill:
$chartData[1]['entries'][$date] = $bill->amount_max;
// amount of journal:
$chartData[2]['entries'][$date] = bcmul($entry->transaction_amount, '-1');
}
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);

View File

@@ -14,7 +14,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
@@ -36,7 +36,7 @@ use Response;
class BudgetController extends Controller
{
/** @var BudgetChartGeneratorInterface */
/** @var GeneratorInterface */
protected $generator;
/**
@@ -45,8 +45,7 @@ class BudgetController extends Controller
public function __construct()
{
parent::__construct();
// create chart generator:
$this->generator = app(BudgetChartGeneratorInterface::class);
$this->generator = app(GeneratorInterface::class);
}
/**
@@ -66,7 +65,7 @@ class BudgetController extends Controller
$cache = new CacheProperties();
$cache->addProperty($first);
$cache->addProperty($last);
$cache->addProperty('budget');
$cache->addProperty('chart.budget.budget');
if ($cache->has()) {
return Response::json($cache->get());
@@ -77,7 +76,7 @@ class BudgetController extends Controller
$budgetCollection = new Collection([$budget]);
$last = Navigation::endOfX($last, $range, $final); // not to overshoot.
$entries = new Collection;
$entries = [];
while ($first < $last) {
// periodspecific dates:
@@ -85,13 +84,14 @@ class BudgetController extends Controller
$currentEnd = Navigation::endOfPeriod($first, $range);
// sub another day because reasons.
$currentEnd->subDay();
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
$entry = [$first, ($spent * -1)];
$entries->push($entry);
$first = Navigation::addPeriod($first, $range, 0);
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
$format = Navigation::periodShow($first, $range);
$entries[$format] = bcmul($spent, '-1');
$first = Navigation::addPeriod($first, $range, 0);
}
$data = $this->generator->budgetLimit($entries, 'month');
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $entries);
$cache->store($data);
return Response::json($data);
@@ -113,25 +113,25 @@ class BudgetController extends Controller
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-limit');
$cache->addProperty($budget->id);
$cache->addProperty('chart.budget.budget.limit');
$cache->addProperty($repetition->id);
if ($cache->has()) {
return Response::json($cache->get());
}
$entries = new Collection;
$entries = [];
$amount = $repetition->amount;
$budgetCollection = new Collection([$budget]);
while ($start <= $end) {
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $start, $start);
$amount = bcadd($amount, $spent);
$entries->push([clone $start, round($amount, 2)]);
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $start, $start);
$amount = bcadd($amount, $spent);
$format = $start->formatLocalized(strval(trans('config.month_and_day')));
$entries[$format] = $amount;
$start->addDay();
}
$data = $this->generator->budgetLimit($entries, 'month_and_day');
$data = $this->generator->singleSet(strval(trans('firefly.left')), $entries);
$cache->store($data);
return Response::json($data);
@@ -152,14 +152,30 @@ class BudgetController extends Controller
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget');
$cache->addProperty('all');
$cache->addProperty('chart.budget.frontpage');
if ($cache->has()) {
return Response::json($cache->get());
}
$budgets = $repository->getActiveBudgets();
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$allEntries = new Collection;
$chartData = [
[
'label' => strval(trans('firefly.spent_in_budget')),
'entries' => [],
'type' => 'bar',
],
[
'label' => strval(trans('firefly.left_to_spend')),
'entries' => [],
'type' => 'bar',
],
[
'label' => strval(trans('firefly.overspent')),
'entries' => [],
'type' => 'bar',
],
];
/** @var Budget $budget */
foreach ($budgets as $budget) {
@@ -167,17 +183,34 @@ class BudgetController extends Controller
$reps = $this->filterRepetitions($repetitions, $budget, $start, $end);
if ($reps->count() === 0) {
$collection = $this->spentInPeriodSingle($repository, $budget, $start, $end);
$allEntries = $allEntries->merge($collection);
$row = $this->spentInPeriodSingle($repository, $budget, $start, $end);
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['repetition_left'], '0') !== 0) {
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
}
continue;
}
$collection = $this->spentInPeriodMulti($repository, $budget, $reps);
$allEntries = $allEntries->merge($collection);
$rows = $this->spentInPeriodMulti($repository, $budget, $reps);
foreach ($rows as $row) {
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['repetition_left'], '0') !== 0) {
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
}
}
unset($rows, $row);
}
$entry = $this->spentInPeriodWithout($start, $end);
$allEntries->push($entry);
$data = $this->generator->frontpage($allEntries);
// for no budget:
$row = $this->spentInPeriodWithout($start, $end);
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['repetition_left'], '0') !== 0) {
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
}
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);
@@ -193,7 +226,7 @@ class BudgetController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*/
public function period(BudgetRepositoryInterface $repository, Budget $budget, Carbon $start, Carbon $end, Collection $accounts)
public function period(BudgetRepositoryInterface $repository, Budget $budget, Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties();
@@ -201,20 +234,19 @@ class BudgetController extends Controller
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty($budget->id);
$cache->addProperty('budget');
$cache->addProperty('period');
$cache->addProperty('chart.budget.period');
if ($cache->has()) {
return Response::json($cache->get());
return Response::json($cache->get());
}
// the expenses:
// get the expenses
$budgeted = [];
$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:
// get the budget limits (if any)
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$current = clone $start;
while ($current < $end) {
@@ -235,18 +267,66 @@ class BudgetController extends Controller
$current = clone $currentEnd;
}
// join them:
$result = [];
// join them into one set of data:
$chartData = [
[
'label' => strval(trans('firefly.spent')),
'type' => 'bar',
'entries' => [],
],
[
'label' => strval(trans('firefly.budgeted')),
'type' => 'bar',
'entries' => [],
],
];
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,
];
$label = $periods[$period];
$spent = isset($entries[$budget->id]['entries'][$period]) ? $entries[$budget->id]['entries'][$period] : '0';
$limit = isset($entries[$period]) ? $budgeted[$period] : 0;
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
$chartData[1]['entries'][$label] = $limit;
}
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param BudgetRepositoryInterface $repository
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse
*/
public function periodNoBudget(BudgetRepositoryInterface $repository, Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty('chart.budget.no-budget');
if ($cache->has()) {
return Response::json($cache->get());
}
$data = $this->generator->period($result);
// the expenses:
$periods = Navigation::listOfPeriods($start, $end);
$entries = $repository->getNoBudgetPeriodReport($accounts, $start, $end);
$chartData = [];
// join them:
foreach (array_keys($periods) as $period) {
$label = $periods[$period];
$spent = isset($entries['entries'][$period]) ? $entries['entries'][$period] : '0';
$chartData[$label] = bcmul($spent, '-1');
}
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
$cache->store($data);
return Response::json($data);
@@ -275,17 +355,25 @@ class BudgetController extends Controller
}
/**
* Returns an array with the following values:
* 0 =>
* 'name' => name of budget + repetition
* 'repetition_left' => left in budget repetition (always zero)
* 'repetition_overspent' => spent more than budget repetition? (always zero)
* 'spent' => actually spent in period for budget
* 1 => (etc)
*
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param Collection $repetitions
*
* @return Collection
* @return array
*/
private function spentInPeriodMulti(BudgetRepositoryInterface $repository, Budget $budget, Collection $repetitions): Collection
private function spentInPeriodMulti(BudgetRepositoryInterface $repository, Budget $budget, Collection $repetitions): array
{
$format = strval(trans('config.month_and_day'));
$collection = new Collection;
$name = $budget->name;
$return = [];
$format = strval(trans('config.month_and_day'));
$name = $budget->name;
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate);
@@ -300,35 +388,52 @@ class BudgetController extends Controller
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
$spent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcmul($amount, '-1') : $expenses;
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
$array = [$name, $left, $spent, $overspent, $amount, $spent];
$collection->push($array);
$return[] = [
'name' => $name,
'repetition_left' => $left,
'repetition_overspent' => $overspent,
'spent' => $spent,
];
}
return $collection;
return $return;
}
/**
* Returns an array with the following values:
* 'name' => name of budget
* 'repetition_left' => left in budget repetition (always zero)
* 'repetition_overspent' => spent more than budget repetition? (always zero)
* 'spent' => actually spent in period for budget
*
*
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
* @return array
*/
private function spentInPeriodSingle(BudgetRepositoryInterface $repository, Budget $budget, Carbon $start, Carbon $end): Collection
private function spentInPeriodSingle(BudgetRepositoryInterface $repository, Budget $budget, Carbon $start, Carbon $end): array
{
$collection = new Collection;
$amount = '0';
$left = '0';
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
$overspent = '0';
$array = [$budget->name, $left, $spent, $overspent, $amount, $spent];
$collection->push($array);
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
$array = [
'name' => $budget->name,
'repetition_left' => '0',
'repetition_overspent' => '0',
'spent' => $spent,
];
return $collection;
return $array;
}
/**
* Returns an array with the following values:
* 'name' => "no budget" in local language
* 'repetition_left' => left in budget repetition (always zero)
* 'repetition_overspent' => spent more than budget repetition? (always zero)
* 'spent' => actually spent in period for budget
*
* @param Carbon $start
* @param Carbon $end
*
@@ -346,7 +451,13 @@ class BudgetController extends Controller
foreach ($journals as $entry) {
$sum = bcadd($entry->transaction_amount, $sum);
}
$array = [
'name' => strval(trans('firefly.no_budget')),
'repetition_left' => '0',
'repetition_overspent' => '0',
'spent' => $sum,
];
return [trans('firefly.no_budget'), '0', '0', $sum, '0', '0'];
return $array;
}
}

View File

@@ -0,0 +1,292 @@
<?php
/**
* BudgetReportController.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\Basic\GeneratorInterface;
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Navigation;
use Response;
/**
* Separate controller because many helper functions are shared.
*
* Class BudgetReportController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class BudgetReportController extends Controller
{
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var BudgetRepositoryInterface */
private $budgetRepository;
/** @var GeneratorInterface */
private $generator;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->generator = app(GeneratorInterface::class);
$this->budgetRepository = app(BudgetRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Collection $accounts
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
* @param string $others
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$cache = new CacheProperties;
$cache->addProperty('chart.budget.report.account-expense');
$cache->addProperty($accounts);
$cache->addProperty($budgets);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
$names = [];
$set = $this->getExpenses($accounts, $budgets, $start, $end);
$grouped = $this->groupByOpposingAccount($set);
$chartData = [];
$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);
$chartData[$names[$accountId]] = $amount;
}
// also collect all transactions NOT in these budgets.
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');
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param Collection $accounts
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
* @param string $others
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$cache = new CacheProperties;
$cache->addProperty('chart.budget.report.budget-expense');
$cache->addProperty($accounts);
$cache->addProperty($budgets);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
$names = [];
$set = $this->getExpenses($accounts, $budgets, $start, $end);
$grouped = $this->groupByBudget($set);
$total = '0';
$chartData = [];
foreach ($grouped as $budgetId => $amount) {
if (!isset($names[$budgetId])) {
$budget = $this->budgetRepository->find(intval($budgetId));
$names[$budgetId] = $budget->name;
}
$amount = bcmul($amount, '-1');
$total = bcadd($total, $amount);
$chartData[$names[$budgetId]] = $amount;
}
// also collect all transactions NOT in these budgets.
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');
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param Collection $accounts
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse
*/
public function mainChart(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty('chart.budget.report.main');
$cache->addProperty($accounts);
$cache->addProperty($budgets);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
$function = Navigation::preferredEndOfPeriod($start, $end);
$chartData = [];
$currentStart = clone $start;
// prep chart data:
foreach ($budgets as $budget) {
$chartData[$budget->id] = [
'label' => $budget->name,
'type' => 'bar',
'entries' => [],
];
}
while ($currentStart < $end) {
$currentEnd = clone $currentStart;
$currentEnd = $currentEnd->$function();
$expenses = $this->groupByBudget($this->getExpenses($accounts, $budgets, $currentStart, $currentEnd));
$label = $currentStart->formatLocalized($format);
/** @var Budget $budget */
foreach ($budgets as $budget) {
$chartData[$budget->id]['entries'][$label] = $expenses[$budget->id] ?? '0';
}
$currentStart = clone $currentEnd;
$currentStart->addDay();
}
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param Collection $accounts
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
private function getExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): Collection
{
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setBudgets($budgets)->withOpposingAccount()->disableFilter();
$accountIds = $accounts->pluck('id')->toArray();
$transactions = $collector->getJournals();
$set = MonthReportGenerator::filterExpenses($transactions, $accountIds);
return $set;
}
/**
* @param Collection $set
*
* @return array
*/
private function groupByBudget(Collection $set): array
{
// group by category ID:
$grouped = [];
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$jrnlBudId = intval($transaction->transaction_journal_budget_id);
$transBudId = intval($transaction->transaction_budget_id);
$budgetId = max($jrnlBudId, $transBudId);
$grouped[$budgetId] = $grouped[$budgetId] ?? '0';
$grouped[$budgetId] = bcadd($transaction->transaction_amount, $grouped[$budgetId]);
}
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

@@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Category;
@@ -26,7 +26,6 @@ use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
use stdClass;
/**
* Class CategoryController
@@ -35,7 +34,7 @@ use stdClass;
*/
class CategoryController extends Controller
{
/** @var CategoryChartGeneratorInterface */
/** @var GeneratorInterface */
protected $generator;
/**
@@ -45,7 +44,7 @@ class CategoryController extends Controller
{
parent::__construct();
// create chart generator:
$this->generator = app(CategoryChartGeneratorInterface::class);
$this->generator = app(GeneratorInterface::class);
}
/**
@@ -59,34 +58,42 @@ class CategoryController extends Controller
*/
public function all(CRI $repository, AccountRepositoryInterface $accountRepository, Category $category)
{
$start = $repository->firstUseDate($category);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$categoryCollection = new Collection([$category]);
$end = new Carbon;
$entries = new Collection;
$cache = new CacheProperties;
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('all');
$cache->addProperty('categories');
$cache = new CacheProperties;
$cache->addProperty('chart.category.all');
$cache->addProperty($category->id);
if ($cache->has()) {
return Response::json($cache->get());
}
$start = $repository->firstUseDate($category);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = new Carbon;
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$chartData = [
[
'label' => strval(trans('firefly.spent')),
'entries' => [],
'type' => 'bar',
],
[
'label' => strval(trans('firefly.earned')),
'entries' => [],
'type' => 'bar',
],
];
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriod($categoryCollection, $accounts, $start, $currentEnd);
$earned = $repository->earnedInPeriod($categoryCollection, $accounts, $start, $currentEnd);
$date = Navigation::periodShow($start, $range);
$entries->push([clone $start, $date, $spent, $earned]);
$start = Navigation::addPeriod($start, $range, 0);
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
$label = Navigation::periodShow($start, $range);
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
$chartData[1]['entries'][$label] = $earned;
$start = Navigation::addPeriod($start, $range, 0);
}
$entries = $entries->reverse();
$entries = $entries->slice(0, 48);
$entries = $entries->reverse();
$data = $this->generator->all($entries);
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);
@@ -122,34 +129,125 @@ class CategoryController extends Controller
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category');
$cache->addProperty('frontpage');
$cache->addProperty('chart.category.frontpage');
if ($cache->has()) {
return Response::json($cache->get());
}
$chartData = [];
$categories = $repository->getCategories();
$accounts = $accountRepository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
$set = new Collection;
/** @var Category $category */
foreach ($categories as $category) {
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
if (bccomp($spent, '0') === -1) {
$category->spent = $spent;
$set->push($category);
$chartData[$category->name] = bcmul($spent, '-1');
}
}
// this is a "fake" entry for the "no category" entry.
$entry = new stdClass;
$entry->name = trans('firefly.no_category');
$entry->spent = $repository->spentInPeriodWithoutCategory(new Collection, $start, $end);
$set->push($entry);
$chartData[strval(trans('firefly.no_category'))] = bcmul($repository->spentInPeriodWithoutCategory(new Collection, $start, $end), '-1');
$set = $set->sortBy('spent');
$data = $this->generator->frontpage($set);
// sort
arsort($chartData);
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param CRI $repository
* @param Category $category
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse|mixed
*/
public function reportPeriod(CRI $repository, Category $category, Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('chart.category.period');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($category);
if ($cache->has()) {
return $cache->get();
}
$expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end);
$income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end);
$periods = Navigation::listOfPeriods($start, $end);
$chartData = [
[
'label' => strval(trans('firefly.spent')),
'entries' => [],
'type' => 'bar',
],
[
'label' => strval(trans('firefly.earned')),
'entries' => [],
'type' => 'bar',
],
];
foreach (array_keys($periods) as $period) {
$label = $periods[$period];
$spent = $expenses[$category->id]['entries'][$period] ?? '0';
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
$chartData[1]['entries'][$label] = $income[$category->id]['entries'][$period] ?? '0';
}
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param CRI $repository
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse|mixed
*/
public function reportPeriodNoCategory(CRI $repository, Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('chart.category.period.no-cat');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$expenses = $repository->periodExpensesNoCategory($accounts, $start, $end);
$income = $repository->periodIncomeNoCategory($accounts, $start, $end);
$periods = Navigation::listOfPeriods($start, $end);
$chartData = [
[
'label' => strval(trans('firefly.spent')),
'entries' => [],
'type' => 'bar',
],
[
'label' => strval(trans('firefly.earned')),
'entries' => [],
'type' => 'bar',
],
];
foreach (array_keys($periods) as $period) {
$label = $periods[$period];
$spent = $expenses['entries'][$period] ?? '0';
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
$chartData[1]['entries'][$label] = $income['entries'][$period] ?? '0';
}
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);
}
/**
@@ -182,33 +280,47 @@ class CategoryController extends Controller
*/
private function makePeriodChart(CRI $repository, Category $category, Carbon $start, Carbon $end)
{
$categoryCollection = new Collection([$category]);
$cache = new CacheProperties;
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($category->id);
$cache->addProperty('chart.category.period-chart');
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty($category->id);
$cache->addProperty('specific-period');
if ($cache->has()) {
return $cache->get();
}
$entries = new Collection;
// chart data
$chartData = [
[
'label' => strval(trans('firefly.spent')),
'entries' => [],
'type' => 'bar',
],
[
'label' => strval(trans('firefly.earned')),
'entries' => [],
'type' => 'bar',
],
];
while ($start <= $end) {
$spent = $repository->spentInPeriod($categoryCollection, $accounts, $start, $start);
$earned = $repository->earnedInPeriod($categoryCollection, $accounts, $start, $start);
$date = Navigation::periodShow($start, '1D');
$entries->push([clone $start, $date, $spent, $earned]);
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start);
$label = Navigation::periodShow($start, '1D');
$chartData[0]['entries'][$label] = bcmul($spent, '-1');
$chartData[1]['entries'][$label] = $earned;
$start->addDay();
}
$data = $this->generator->period($entries);
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return $data;

View File

@@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Controllers\Controller;
@@ -24,8 +24,9 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Log;
use Navigation;
use Response;
@@ -43,7 +44,7 @@ class CategoryReportController extends Controller
private $accountRepository;
/** @var CategoryRepositoryInterface */
private $categoryRepository;
/** @var CategoryChartGeneratorInterface */
/** @var GeneratorInterface */
private $generator;
/**
@@ -54,7 +55,7 @@ class CategoryReportController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->generator = app(CategoryChartGeneratorInterface::class);
$this->generator = app(GeneratorInterface::class);
$this->categoryRepository = app(CategoryRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
@@ -76,38 +77,45 @@ class CategoryReportController extends Controller
{
/** @var bool $others */
$others = intval($others) === 1;
$names = [];
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.account-expense');
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
// collect journals (just like the category report does):
$set = $this->getExpenses($accounts, $categories, $start, $end);
$grouped = $this->groupByOpposingAccount($set);
$names = [];
$set = $this->getExpenses($accounts, $categories, $start, $end);
$grouped = $this->groupByOpposingAccount($set);
$chartData = [];
$total = '0';
// 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];
$amount = bcmul($amount, '-1');
$total = bcadd($total, $amount);
$chartData[$names[$accountId]] = $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];
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcmul($sum, '-1');
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($result);
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
@@ -125,36 +133,44 @@ class CategoryReportController extends Controller
{
/** @var bool $others */
$others = intval($others) === 1;
$names = [];
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.account-income');
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
// 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';
$names = [];
$set = $this->getIncome($accounts, $categories, $start, $end);
$grouped = $this->groupByOpposingAccount($set);
$chartData = [];
$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];
$total = bcadd($total, $amount);
$chartData[$names[$accountId]] = $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];
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($result);
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
@@ -172,38 +188,45 @@ class CategoryReportController extends Controller
{
/** @var bool $others */
$others = intval($others) === 1;
$names = [];
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.category-expense');
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
// collect journals (just like the category report does):
$set = $this->getExpenses($accounts, $categories, $start, $end);
$grouped = $this->groupByCategory($set);
$names = [];
$set = $this->getExpenses($accounts, $categories, $start, $end);
$grouped = $this->groupByCategory($set);
$total = '0';
$chartData = [];
// 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];
$amount = bcmul($amount, '-1');
$total = bcadd($total, $amount);
$chartData[$names[$categoryId]] = $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];
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcmul($sum, '-1');
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($result);
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
@@ -221,36 +244,42 @@ class CategoryReportController extends Controller
{
/** @var bool $others */
$others = intval($others) === 1;
$names = [];
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.category-income');
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
// collect journals (just like the category report does):
$set = $this->getIncome($accounts, $categories, $start, $end);
$grouped = $this->groupByCategory($set);
$names = [];
$set = $this->getIncome($accounts, $categories, $start, $end);
$grouped = $this->groupByCategory($set);
$total = '0';
$chartData = [];
// 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];
$total = bcadd($total, $amount);
$chartData[$names[$categoryId]] = $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];
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($result);
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
@@ -265,52 +294,56 @@ class CategoryReportController extends Controller
*/
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';
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.main');
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
if ($start->diffInMonths($end) > 13) {
$period = '1Y';
$format = 'year';
$function = 'endOfYear';
}
Log::debug(sprintf('Period is %s', $period));
$data = [];
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
$function = Navigation::preferredEndOfPeriod($start, $end);
$chartData = [];
$currentStart = clone $start;
// prep chart data:
foreach ($categories as $category) {
$chartData[$category->id . '-in'] = [
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.income'))) . ')',
'type' => 'bar',
'entries' => [],
];
$chartData[$category->id . '-out'] = [
'label' => $category->name . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')',
'type' => 'bar',
'entries' => [],
];
}
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' => [],
];
$label = $currentStart->formatLocalized($format);
/** @var Category $category */
foreach ($categories as $category) {
$labelIn = $category->id . '-in';
$labelOut = $category->id . '-out';
// 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';
$chartData[$labelIn]['entries'][$label] = $income[$category->id] ?? '0';
$chartData[$labelOut]['entries'][$label] = $expenses[$category->id] ?? '0';
}
$currentStart = clone $currentEnd;
$currentStart->addDay();
}
$data = $this->generator->mainReportChart($data);
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);
}

View File

@@ -13,13 +13,12 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Chart;
use FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGeneratorInterface;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Response;
@@ -31,7 +30,7 @@ use Response;
class PiggyBankController extends Controller
{
/** @var PiggyBankChartGeneratorInterface */
/** @var GeneratorInterface */
protected $generator;
/**
@@ -41,7 +40,7 @@ class PiggyBankController extends Controller
{
parent::__construct();
// create chart generator:
$this->generator = app(PiggyBankChartGeneratorInterface::class);
$this->generator = app(GeneratorInterface::class);
}
/**
@@ -56,26 +55,24 @@ class PiggyBankController extends Controller
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('piggy-history');
$cache->addProperty('chart.piggy-bank.history');
$cache->addProperty($piggyBank->id);
if ($cache->has()) {
return Response::json($cache->get());
}
$set = $repository->getEvents($piggyBank);
$set = $set->reverse();
$collection = [];
$set = $repository->getEvents($piggyBank);
$set = $set->reverse();
$chartData = [];
$sum = '0';
/** @var PiggyBankEvent $entry */
foreach ($set as $entry) {
$date = $entry->date->format('Y-m-d');
$amount = $entry->amount;
if (isset($collection[$date])) {
$amount = bcadd($amount, $collection[$date]);
}
$collection[$date] = $amount;
$label = $entry->date->formatLocalized(strval(trans('config.month_and_day')));
$sum = bcadd($sum, $entry->amount);
$chartData[$label] = $sum;
}
$data = $this->generator->history(new Collection($collection));
$data = $this->generator->singleSet($piggyBank->name, $chartData);
$cache->store($data);
return Response::json($data);

View File

@@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Report\ReportChartGeneratorInterface;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Support\CacheProperties;
@@ -32,7 +32,7 @@ use Steam;
class ReportController extends Controller
{
/** @var ReportChartGeneratorInterface */
/** @var GeneratorInterface */
protected $generator;
/**
@@ -42,47 +42,42 @@ class ReportController extends Controller
{
parent::__construct();
// create chart generator:
$this->generator = app(ReportChartGeneratorInterface::class);
$this->generator = app(GeneratorInterface::class);
}
/**
* 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 Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function netWorth(Carbon $start, Carbon $end, Collection $accounts)
public function netWorth(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('netWorth');
$cache->addProperty('chart.report.net-worth');
$cache->addProperty($start);
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
$ids = $accounts->pluck('id')->toArray();
$current = clone $start;
$entries = new Collection;
$ids = $accounts->pluck('id')->toArray();
$current = clone $start;
$chartData = [];
while ($current < $end) {
$balances = Steam::balancesById($ids, $current);
$sum = $this->arraySum($balances);
$entries->push(
[
'date' => clone $current,
'net-worth' => $sum,
]
);
$balances = Steam::balancesById($ids, $current);
$sum = $this->arraySum($balances);
$label = $current->formatLocalized(strval(trans('config.month_and_day')));
$chartData[$label] = $sum;
$current->addDays(7);
}
$data = $this->generator->netWorth($entries);
$data = $this->generator->singleSet(strval(trans('firefly.net_worth')), $chartData);
$cache->store($data);
return Response::json($data);
@@ -90,216 +85,141 @@ class ReportController extends Controller
/**
* Shows income and expense, debet/credit: operations
*
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInOut(Carbon $start, Carbon $end, Collection $accounts)
public function operations(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('yearInOut');
$cache->addProperty('chart.report.operations');
$cache->addProperty($start);
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
$source = $this->getChartData($accounts, $start, $end);
$chartData = [
[
'label' => trans('firefly.income'),
'type' => 'bar',
'entries' => [],
],
[
'label' => trans('firefly.expenses'),
'type' => 'bar',
'entries' => [],
],
];
$chartSource = $this->getYearData($accounts, $start, $end);
if ($start->diffInMonths($end) > 12) {
// data = method X
$data = $this->multiYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data);
return Response::json($data);
foreach ($source['earned'] as $date => $amount) {
$carbon = new Carbon($date);
$label = $carbon->formatLocalized($format);
$earned = $chartData[0]['entries'][$label] ?? '0';
$chartData[0]['entries'][$label] = bcadd($earned, $amount);
}
foreach ($source['spent'] as $date => $amount) {
$carbon = new Carbon($date);
$label = $carbon->formatLocalized($format);
$spent = $chartData[1]['entries'][$label] ?? '0';
$chartData[1]['entries'][$label] = bcadd($spent, $amount);
}
// data = method Y
$data = $this->singleYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end);
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* Shows sum income and expense, debet/credit: operations
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
* @internal param AccountRepositoryInterface $repository
*/
public function yearInOutSummarized(Carbon $start, Carbon $end, Collection $accounts)
public function sum(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('yearInOutSummarized');
$cache->addProperty('chart.report.sum');
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
if ($cache->has()) {
return Response::json($cache->get());
}
$chartSource = $this->getYearData($accounts, $start, $end);
if ($start->diffInMonths($end) > 12) {
// per year
$data = $this->multiYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end);
$cache->store($data);
return Response::json($data);
$source = $this->getChartData($accounts, $start, $end);
$numbers = [
'sum_earned' => '0',
'avg_earned' => '0',
'count_earned' => 0,
'sum_spent' => '0',
'avg_spent' => '0',
'count_spent' => 0,
];
foreach ($source['earned'] as $amount) {
$numbers['sum_earned'] = bcadd($amount, $numbers['sum_earned']);
$numbers['count_earned']++;
}
// per month!
$data = $this->singleYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end);
if ($numbers['count_earned'] > 0) {
$numbers['avg_earned'] = $numbers['sum_earned'] / $numbers['count_earned'];
}
foreach ($source['spent'] as $amount) {
$numbers['sum_spent'] = bcadd($amount, $numbers['sum_spent']);
$numbers['count_spent']++;
}
if ($numbers['count_spent'] > 0) {
$numbers['avg_spent'] = $numbers['sum_spent'] / $numbers['count_spent'];
}
$chartData = [
[
'label' => strval(trans('firefly.income')),
'type' => 'bar',
'entries' => [
strval(trans('firefly.sum_of_period')) => $numbers['sum_earned'],
strval(trans('firefly.average_in_period')) => $numbers['avg_earned'],
],
],
[
'label' => trans('firefly.expenses'),
'type' => 'bar',
'entries' => [
strval(trans('firefly.sum_of_period')) => $numbers['sum_spent'],
strval(trans('firefly.average_in_period')) => $numbers['avg_spent'],
],
],
];
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param array $earned
* @param array $spent
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function multiYearInOut(array $earned, array $spent, Carbon $start, Carbon $end)
{
$entries = new Collection;
while ($start < $end) {
$incomeSum = $this->pluckFromArray($start->year, $earned);
$expenseSum = $this->pluckFromArray($start->year, $spent);
$entries->push([clone $start, $incomeSum, $expenseSum]);
$start->addYear();
}
$data = $this->generator->multiYearInOut($entries);
return $data;
}
/**
* @param array $earned
* @param array $spent
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function multiYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end)
{
$income = '0';
$expense = '0';
$count = 0;
while ($start < $end) {
$currentIncome = $this->pluckFromArray($start->year, $earned);
$currentExpense = $this->pluckFromArray($start->year, $spent);
$income = bcadd($income, $currentIncome);
$expense = bcadd($expense, $currentExpense);
$count++;
$start->addYear();
}
$data = $this->generator->multiYearInOutSummarized($income, $expense, $count);
return $data;
}
/**
* @param int $year
* @param array $set
*
* @return string
*/
protected function pluckFromArray($year, array $set)
{
$sum = '0';
foreach ($set as $date => $amount) {
if (substr($date, 0, 4) == $year) {
$sum = bcadd($sum, $amount);
}
}
return $sum;
}
/**
* @param array $earned
* @param array $spent
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function singleYearInOut(array $earned, array $spent, Carbon $start, Carbon $end)
{
// per month? simply use each month.
$entries = new Collection;
while ($start < $end) {
// total income and total expenses:
$date = $start->format('Y-m');
$incomeSum = isset($earned[$date]) ? $earned[$date] : 0;
$expenseSum = isset($spent[$date]) ? $spent[$date] : 0;
$entries->push([clone $start, $incomeSum, $expenseSum]);
$start->addMonth();
}
$data = $this->generator->yearInOut($entries);
return $data;
}
/**
* @param array $earned
* @param array $spent
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function singleYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end)
{
$income = '0';
$expense = '0';
$count = 0;
while ($start < $end) {
$date = $start->format('Y-m');
$currentIncome = isset($earned[$date]) ? $earned[$date] : 0;
$currentExpense = isset($spent[$date]) ? $spent[$date] : 0;
$income = bcadd($income, $currentIncome);
$expense = bcadd($expense, $currentExpense);
$count++;
$start->addMonth();
}
$data = $this->generator->yearInOutSummarized($income, $expense, $count);
return $data;
}
/**
* @param $array
*
* @return string
*/
private function arraySum($array) : string
private function arraySum($array): string
{
$sum = '0';
foreach ($array as $entry) {
@@ -310,31 +230,45 @@ class ReportController extends Controller
}
/**
* Collects the incomes and expenses for the given periods, grouped per month. Will cache its results
*
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getYearData(Collection $accounts, Carbon $start, Carbon $end): array
private function getChartData(Collection $accounts, Carbon $start, Carbon $end): array
{
$cache = new CacheProperties;
$cache->addProperty('chart.report.get-chart-data');
$cache->addProperty($start);
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
return $cache->get();
}
$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);
$currentEnd = Navigation::endOfPeriod($currentStart, '1M');
$label = $currentStart->format('Y-m') . '-01';
$spent = $tasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd);
$earned = $tasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd);
$spentArray[$label] = bcmul($spent, '-1');
$earnedArray[$label] = $earned;
$currentStart = Navigation::addPeriod($currentStart, '1M', 0);
}
return [
$result = [
'spent' => $spentArray,
'earned' => $earnedArray,
];
$cache->store($result);
return $result;
}
}

View File

@@ -60,14 +60,14 @@ class CurrencyController extends Controller
$subTitle = trans('firefly.create_currency');
// put previous url in session if not redirect from store (not "create another").
if (session('currency.create.fromStore') !== true) {
Session::put('currency.create.url', URL::previous());
if (session('currencies.create.fromStore') !== true) {
Session::put('currencies.create.url', URL::previous());
}
Session::forget('currency.create.fromStore');
Session::forget('currencies.create.fromStore');
Session::flash('gaEventCategory', 'currency');
Session::flash('gaEventAction', 'create');
return view('currency.create', compact('subTitleIcon', 'subTitle'));
return view('currencies.create', compact('subTitleIcon', 'subTitle'));
}
/**
@@ -85,54 +85,54 @@ class CurrencyController extends Controller
Cache::forget('FFCURRENCYSYMBOL');
Cache::forget('FFCURRENCYCODE');
return redirect(route('currency.index'));
return redirect(route('currencies.index'));
}
/**
* @param TransactionCurrency $currency
* @param CurrencyRepositoryInterface $repository
* @param TransactionCurrency $currency
*
* @return \Illuminate\Http\RedirectResponse|View
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function delete(TransactionCurrency $currency)
public function delete(CurrencyRepositoryInterface $repository, TransactionCurrency $currency)
{
if (!$this->canDeleteCurrency($currency)) {
if (!$repository->canDeleteCurrency($currency)) {
Session::flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
return redirect(route('currency.index'));
return redirect(route('currencies.index'));
}
// put previous url in session
Session::put('currency.delete.url', URL::previous());
Session::put('currencies.delete.url', URL::previous());
Session::flash('gaEventCategory', 'currency');
Session::flash('gaEventAction', 'delete');
$subTitle = trans('form.delete_currency', ['name' => $currency->name]);
return view('currency.delete', compact('currency', 'subTitle'));
return view('currencies.delete', compact('currency', 'subTitle'));
}
/**
* @param TransactionCurrency $currency
* @param CurrencyRepositoryInterface $repository
* @param TransactionCurrency $currency
*
* @return \Illuminate\Http\RedirectResponse
* @throws \Exception
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(TransactionCurrency $currency)
public function destroy(CurrencyRepositoryInterface $repository, TransactionCurrency $currency)
{
if (!$this->canDeleteCurrency($currency)) {
if (!$repository->canDeleteCurrency($currency)) {
Session::flash('error', trans('firefly.cannot_delete_currency', ['name' => $currency->name]));
return redirect(route('currency.index'));
return redirect(route('currencies.index'));
}
$repository->destroy($currency);
Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name]));
if (auth()->user()->hasRole('owner')) {
$currency->forceDelete();
}
return redirect(session('currency.delete.url'));
return redirect(session('currencies.delete.url'));
}
/**
@@ -147,14 +147,14 @@ class CurrencyController extends Controller
$currency->symbol = htmlentities($currency->symbol);
// put previous url in session if not redirect from store (not "return_to_edit").
if (session('currency.edit.fromUpdate') !== true) {
Session::put('currency.edit.url', URL::previous());
if (session('currencies.edit.fromUpdate') !== true) {
Session::put('currencies.edit.url', URL::previous());
}
Session::forget('currency.edit.fromUpdate');
Session::forget('currencies.edit.fromUpdate');
Session::flash('gaEventCategory', 'currency');
Session::flash('gaEventAction', 'edit');
return view('currency.edit', compact('currency', 'subTitle', 'subTitleIcon'));
return view('currencies.edit', compact('currency', 'subTitle', 'subTitleIcon'));
}
@@ -170,11 +170,11 @@ class CurrencyController extends Controller
if (!auth()->user()->hasRole('owner')) {
Session::flash('warning', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
Session::flash('warning', trans('firefly.ask_site_owner', ['site_owner' => env('SITE_OWNER')]));
}
return view('currency.index', compact('currencies', 'defaultCurrency'));
return view('currencies.index', compact('currencies', 'defaultCurrency'));
}
/**
@@ -189,7 +189,7 @@ class CurrencyController extends Controller
if (!auth()->user()->hasRole('owner')) {
Log::error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.');
return redirect(session('currency.create.url'));
return redirect(session('currencies.create.url'));
}
$data = $request->getCurrencyData();
@@ -197,13 +197,13 @@ class CurrencyController extends Controller
Session::flash('success', trans('firefly.created_currency', ['name' => $currency->name]));
if (intval(Input::get('create_another')) === 1) {
Session::put('currency.create.fromStore', true);
Session::put('currencies.create.fromStore', true);
return redirect(route('currency.create'))->withInput();
return redirect(route('currencies.create'))->withInput();
}
// redirect to previous URL.
return redirect(session('currency.create.url'));
return redirect(session('currencies.create.url'));
}
@@ -226,49 +226,13 @@ class CurrencyController extends Controller
if (intval(Input::get('return_to_edit')) === 1) {
Session::put('currency.edit.fromUpdate', true);
Session::put('currencies.edit.fromUpdate', true);
return redirect(route('currency.edit', [$currency->id]));
return redirect(route('currencies.edit', [$currency->id]));
}
// redirect to previous URL.
return redirect(session('currency.edit.url'));
return redirect(session('currencies.edit.url'));
}
/**
* @param TransactionCurrency $currency
*
* @return bool
*/
private function canDeleteCurrency(TransactionCurrency $currency): bool
{
$repository = app(CurrencyRepositoryInterface::class);
// has transactions still
if ($repository->countJournals($currency) > 0) {
return false;
}
// is the only currency left
if ($repository->get()->count() === 1) {
return false;
}
// is the default currency for the user or the system
$defaultCode = Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR'))->data;
if ($currency->code === $defaultCode) {
return false;
}
// is the default currency for the system
$defaultSystemCode = config('firefly.default_currency', 'EUR');
if ($currency->code === $defaultSystemCode) {
return false;
}
// can be deleted
return true;
}
}

View File

@@ -17,7 +17,7 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Export\Processor;
use FireflyIII\Export\ProcessorInterface;
use FireflyIII\Http\Requests\ExportFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ExportJob;
@@ -71,7 +71,6 @@ class ExportController extends Controller
throw new FireflyException('Against all expectations, zip file "' . $file . '" does not exist.');
}
$job->change('export_downloaded');
return response($disk->get($file), 200)
@@ -133,7 +132,6 @@ class ExportController extends Controller
*/
public function postIndex(ExportFormRequest $request, AccountRepositoryInterface $repository, EJRI $jobs)
{
set_time_limit(0);
$job = $jobs->findByKey($request->get('job'));
$settings = [
'accounts' => $repository->getAccountsById($request->get('accounts')),
@@ -146,7 +144,9 @@ class ExportController extends Controller
];
$job->change('export_status_make_exporter');
$processor = new Processor($settings);
/** @var ProcessorInterface $processor */
$processor = app(ProcessorInterface::class, [$settings]);
/*
* Collect journals:

View File

@@ -166,9 +166,51 @@ class HomeController extends Controller
public function routes()
{
// these routes are not relevant for the help pages:
$ignore = ['login', 'registe', 'logout', 'two-fac', 'lost-two', 'confirm', 'resend', 'do_confirm', 'testFla', 'json.', 'piggy-banks.add',
'piggy-banks.remove', 'preferences.', 'rules.rule.up', 'rules.rule.down', 'rules.rule-group.up', 'rules.rule-group.down', 'popup.report',
'admin.users.domains.block-', 'import.json', 'help.',
$ignore = [
// login and two-factor routes:
'login',
'registe',
'password.rese',
'logout',
'two-fac',
'lost-two',
'confirm',
'resend',
'do_confirm',
// test troutes
'test-flash',
'all-routes',
// json routes
'json.',
// routes that point to modals or that redirect immediately.
'piggy-banks.add',
'piggy-banks.remove',
'rules.rule.up',
'attachments.download',
'bills.rescan',
'rules.rule.down',
'rules.rule-group.up',
'rules.rule-group.down',
'popup.',
'error',
'flush',
//'preferences.',
'admin.users.domains.block-',
'help.',
// ajax routes:
'import.json',
// charts:
'chart.',
// report data:
'report-data.',
// others:
'debugbar',
'attachments.preview',
'budgets.income',
'currencies.default',
];
$routes = Route::getRoutes();
$return = '<pre>';

View File

@@ -145,10 +145,13 @@ class ImportController extends Controller
return $this->redirectToCorrectStep($job);
}
// if there is a tag (there might not be), we can link to it:
$tagId = $job->extended_status['importTag'] ?? 0;
$subTitle = trans('firefly.import_finished');
$subTitleIcon = 'fa-star';
return view('import.finished', compact('job', 'subTitle', 'subTitleIcon'));
return view('import.finished', compact('job', 'subTitle', 'subTitleIcon', 'tagId'));
}
/**
@@ -318,7 +321,8 @@ class ImportController extends Controller
{
set_time_limit(0);
if ($job->status == 'settings_complete') {
ImportProcedure::runImport($job);
$importProcedure = new ImportProcedure;
$importProcedure->runImport($job);
}
}

View File

@@ -102,7 +102,6 @@ class JsonController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*
* @internal param ARI $accountRepository
*/
public function boxIn(AccountTaskerInterface $accountTasker, AccountRepositoryInterface $repository)
{

View File

@@ -46,7 +46,7 @@ class ReportController extends Controller
* @return \Illuminate\Http\JsonResponse
* @throws FireflyException
*/
public function info(Request $request)
public function general(Request $request)
{
$attributes = $request->get('attributes') ?? [];
$attributes = $this->parseAttributes($attributes);

View File

@@ -74,7 +74,7 @@ class PreferencesController extends Controller
Session::flash('success', strval(trans('firefly.pref_two_factor_auth_disabled')));
Session::flash('info', strval(trans('firefly.pref_two_factor_auth_remove_it')));
return redirect(route('preferences'));
return redirect(route('preferences.index'));
}
/**
@@ -122,7 +122,7 @@ class PreferencesController extends Controller
Session::flash('success', strval(trans('firefly.saved_preferences')));
Preferences::mark();
return redirect(route('preferences'));
return redirect(route('preferences.index'));
}
/**
@@ -206,13 +206,13 @@ class PreferencesController extends Controller
return redirect(route('preferences.code'));
}
return redirect(route('preferences'));
return redirect(route('preferences.index'));
}
/**
* @return string
*/
private function getDomain() : string
private function getDomain(): string
{
$url = url()->to('/');
$parts = parse_url($url);

View File

@@ -15,9 +15,9 @@ namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
use FireflyIII\Http\Requests\ProfileFormRequest;
use FireflyIII\User;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Hash;
use Preferences;
use Log;
use Session;
use View;
@@ -108,16 +108,16 @@ class ProfileController extends Controller
Session::flash('success', strval(trans('firefly.password_changed')));
return redirect(route('profile'));
return redirect(route('profile.index'));
}
/**
* @param UserRepositoryInterface $repository
* @param DeleteAccountFormRequest $request
*
* @return \Illuminate\Http\RedirectResponse
* @throws \Exception
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function postDeleteAccount(DeleteAccountFormRequest $request)
public function postDeleteAccount(UserRepositoryInterface $repository, DeleteAccountFormRequest $request)
{
// old, new1, new2
if (!Hash::check($request->get('password'), auth()->user()->password)) {
@@ -125,34 +125,16 @@ class ProfileController extends Controller
return redirect(route('profile.delete-account'));
}
// store some stuff for the future:
$registration = Preferences::get('registration_ip_address')->data;
$confirmation = Preferences::get('confirmation_ip_address')->data;
// DELETE!
$email = auth()->user()->email;
auth()->user()->delete();
$user = auth()->user();
Log::info(sprintf('User #%d has opted to delete their account', auth()->user()->id));
// make repository delete user:
auth()->logout();
Session::flush();
$repository->destroy($user);
Session::flash('gaEventCategory', 'user');
Session::flash('gaEventAction', 'delete-account');
// create a new user with the same email address so re-registration is blocked.
$newUser = User::create(
[
'email' => $email,
'password' => 'deleted',
'blocked' => 1,
'blocked_code' => 'deleted',
]
);
if (strlen($registration) > 0) {
Preferences::setForUser($newUser, 'registration_ip_address', $registration);
}
if (strlen($confirmation) > 0) {
Preferences::setForUser($newUser, 'confirmation_ip_address', $confirmation);
}
return redirect(route('index'));
}

View File

@@ -29,13 +29,13 @@ class AccountController extends Controller
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return mixed|string
*/
public function accountReport(Carbon $start, Carbon $end, Collection $accounts)
public function general(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
@@ -47,9 +47,9 @@ class AccountController extends Controller
return $cache->get();
}
/** @var AccountTaskerInterface $accountTasker */
$accountTasker = app(AccountTaskerInterface::class);
$accountReport = $accountTasker->getAccountReport($start, $end, $accounts);
$accountReport = $accountTasker->getAccountReport($accounts, $start, $end);
$result = view('reports.partials.accounts', compact('accountReport'))->render();
$cache->store($result);

View File

@@ -30,13 +30,13 @@ class BalanceController extends Controller
/**
* @param BalanceReportHelperInterface $helper
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
* @return mixed|string
*/
public function balanceReport(BalanceReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
public function general(BalanceReportHelperInterface $helper, Collection $accounts, Carbon $start, Carbon $end)
{
@@ -50,7 +50,7 @@ class BalanceController extends Controller
return $cache->get();
}
$balance = $helper->getBalanceReport($start, $end, $accounts);
$balance = $helper->getBalanceReport($accounts, $start, $end);
$result = view('reports.partials.balance', compact('balance'))->render();
$cache->store($result);

View File

@@ -17,6 +17,7 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Navigation;
@@ -29,43 +30,16 @@ use Navigation;
class BudgetController extends Controller
{
/**
*
* @param BudgetReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function budgetPeriodReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-period-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$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;
}
/**
* @param BudgetReportHelperInterface $helper
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
* @return mixed|string
*/
public function budgetReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
public function general(BudgetReportHelperInterface $helper, Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
@@ -86,4 +60,64 @@ class BudgetController extends Controller
return $result;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
*/
public function period(Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-period-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
// generate budget report right here.
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$budgets = $repository->getBudgets();
$data = $repository->getBudgetPeriodReport($budgets, $accounts, $start, $end);
$data[0] = $repository->getNoBudgetPeriodReport($accounts, $start, $end); // append report data for "no budget"
$report = $this->filterBudgetPeriodReport($data);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.budget-period', compact('report', 'periods'))->render();
$cache->store($result);
return $result;
}
/**
* Filters empty results from getBudgetPeriodReport
*
* @param array $data
*
* @return array
*/
private function filterBudgetPeriodReport(array $data): array
{
/**
* @var int $budgetId
* @var array $set
*/
foreach ($data as $budgetId => $set) {
$sum = '0';
foreach ($set['entries'] as $amount) {
$sum = bcadd($amount, $sum);
}
$data[$budgetId]['sum'] = $sum;
if (bccomp('0', $sum) === 0) {
unset($data[$budgetId]);
}
}
return $data;
}
}

View File

@@ -17,8 +17,12 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Log;
use Navigation;
/**
* Class CategoryController
@@ -27,16 +31,82 @@ use Illuminate\Support\Collection;
*/
class CategoryController extends Controller
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
*/
public function expenses(Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-period-expenses-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
Log::debug('Return report from cache');
return $cache->get();
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$data = $repository->periodExpenses($categories, $accounts, $start, $end);
$data[0] = $repository->periodExpensesNoCategory($accounts, $start, $end);
$report = $this->filterReport($data);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.category-period', compact('report', 'periods'))->render();
$cache->store($result);
return $result;
}
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function categoryReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
public function income(Collection $accounts, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-period-income-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
Log::debug('Return report from cache');
return $cache->get();
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$data = $repository->periodIncome($categories, $accounts, $start, $end);
$data[0] = $repository->periodIncomeNoCategory($accounts, $start, $end);
$report = $this->filterReport($data);
$periods = Navigation::listOfPeriods($start, $end);
$result = view('reports.partials.category-period', compact('report', 'periods'))->render();
$cache->store($result);
return $result;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
* @internal param ReportHelperInterface $helper
*/
public function operations(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
@@ -48,12 +118,56 @@ class CategoryController extends Controller
return $cache->get();
}
$categories = $helper->getCategoryReport($start, $end, $accounts);
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$report = [];
/** @var Category $category */
foreach ($categories as $category) {
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
if (bccomp($spent, '0') !== 0) {
$report[$category->id] = ['name' => $category->name, 'spent' => $spent];
}
}
$result = view('reports.partials.categories', compact('categories'))->render();
// sort the result
// Obtain a list of columns
$sum = [];
foreach ($report as $categoryId => $row) {
$sum[$categoryId] = floatval($row['spent']);
}
array_multisort($sum, SORT_ASC, $report);
$result = view('reports.partials.categories', compact('report'))->render();
$cache->store($result);
return $result;
}
/**
* Filters empty results from category period report
*
* @param array $data
*
* @return array
*/
private function filterReport(array $data): array
{
foreach ($data as $categoryId => $set) {
$sum = '0';
foreach ($set['entries'] as $amount) {
$sum = bcadd($amount, $sum);
}
$data[$categoryId]['sum'] = $sum;
if (bccomp('0', $sum) === 0) {
unset($data[$categoryId]);
}
}
return $data;
}
}

View File

@@ -1,119 +0,0 @@
<?php
/**
* InOutController.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\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class InOutController
*
* @package FireflyIII\Http\Controllers\Report
*/
class InOutController extends Controller
{
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$expenses = $helper->getExpenseReport($start, $end, $accounts);
$result = view('reports.partials.expenses', compact('expenses'))->render();
$cache->store($result);
return $result;
}
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function incExpReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('inc-exp-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$incomes = $helper->getIncomeReport($start, $end, $accounts);
$expenses = $helper->getExpenseReport($start, $end, $accounts);
$result = view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render();
$cache->store($result);
return $result;
}
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function incomeReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('income-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$incomes = $helper->getIncomeReport($start, $end, $accounts);
$result = view('reports.partials.income', compact('incomes'))->render();
$cache->store($result);
return $result;
}
}

View File

@@ -0,0 +1,244 @@
<?php
/**
* OperationsController.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\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class OperationsController
*
* @package FireflyIII\Http\Controllers\Report
*/
class OperationsController extends Controller
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
*/
public function expenses(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$expenses = $this->getExpenseReport($start, $end, $accounts);
$result = view('reports.partials.expenses', compact('expenses'))->render();
$cache->store($result);
return $result;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function income(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('income-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$income = $this->getIncomeReport($start, $end, $accounts);
$result = view('reports.partials.income', compact('income'))->render();
$cache->store($result);
return $result;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
*/
public function operations(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('inc-exp-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$incomes = $this->getIncomeReport($start, $end, $accounts);
$expenses = $this->getExpenseReport($start, $end, $accounts);
$incomeSum = array_sum(
array_map(
function ($item) {
return $item['sum'];
}, $incomes
)
);
$expensesSum = array_sum(
array_map(
function ($item) {
return $item['sum'];
}, $expenses
)
);
$result = view('reports.partials.operations', compact('incomeSum', 'expensesSum'))->render();
$cache->store($result);
return $result;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
private function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): array
{
// get all expenses for the given accounts in the given period!
// also transfers!
// get all transactions:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->withOpposingAccount()
->enableInternalFilter();
$transactions = $collector->getJournals();
$transactions = $transactions->filter(
function (Transaction $transaction) {
// return negative amounts only.
if (bccomp($transaction->transaction_amount, '0') === -1) {
return $transaction;
}
return false;
}
);
$expenses = $this->groupByOpposing($transactions);
// sort the result
// Obtain a list of columns
$sum = [];
foreach ($expenses as $accountId => $row) {
$sum[$accountId] = floatval($row['sum']);
}
array_multisort($sum, SORT_ASC, $expenses);
return $expenses;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
private function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): array
{
// get all expenses for the given accounts in the given period!
// also transfers!
// get all transactions:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end);
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->withOpposingAccount()
->enableInternalFilter();
$transactions = $collector->getJournals();
$transactions = $transactions->filter(
function (Transaction $transaction) {
// return positive amounts only.
if (bccomp($transaction->transaction_amount, '0') === 1) {
return $transaction;
}
return false;
}
);
$income = $this->groupByOpposing($transactions);
// sort the result
// Obtain a list of columns
$sum = [];
foreach ($income as $accountId => $row) {
$sum[$accountId] = floatval($row['sum']);
}
array_multisort($sum, SORT_DESC, $income);
return $income;
}
/**
* @param Collection $transactions
*
* @return array
*/
private function groupByOpposing(Collection $transactions): array
{
$expenses = [];
// join the result together:
foreach ($transactions as $transaction) {
$opposingId = $transaction->opposing_account_id;
$name = $transaction->opposing_account_name;
if (!isset($expenses[$opposingId])) {
$expenses[$opposingId] = [
'id' => $opposingId,
'name' => $name,
'sum' => '0',
'count' => 0,
];
}
$expenses[$opposingId]['sum'] = bcadd($expenses[$opposingId]['sum'], $transaction->transaction_amount);
$expenses[$opposingId]['count']++;
}
return $expenses;
}
}

View File

@@ -20,6 +20,7 @@ use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Requests\ReportFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection;
@@ -61,14 +62,13 @@ class ReportController extends Controller
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
* @throws FireflyException
*/
public function auditReport(Carbon $start, Carbon $end, Collection $accounts)
public function auditReport(Collection $accounts, Carbon $start, Carbon $end)
{
if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date'));
@@ -97,15 +97,50 @@ class ReportController extends Controller
}
/**
* @param Collection $accounts
* @param Collection $budgets
* @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)
public function budgetReport(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end)
{
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_budget',
[
'start' => $start->formatLocalized($this->monthFormat),
'end' => $end->formatLocalized($this->monthFormat),
]
)
);
$generator = ReportGeneratorFactory::reportGenerator('Budget', $start, $end);
$generator->setAccounts($accounts);
$generator->setBudgets($budgets);
$result = $generator->generate();
return $result;
}
/**
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function categoryReport(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
{
if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date'));
@@ -134,14 +169,13 @@ class ReportController extends Controller
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
* @throws FireflyException
*/
public function defaultReport(Carbon $start, Carbon $end, Collection $accounts)
public function defaultReport(Collection $accounts, Carbon $start, Carbon $end)
{
if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date'));
@@ -201,6 +235,9 @@ class ReportController extends Controller
case 'category':
$result = $this->categoryReportOptions();
break;
case 'budget':
$result = $this->budgetReportOptions();
break;
}
return Response::json(['html' => $result]);
@@ -220,6 +257,7 @@ class ReportController extends Controller
$end = $request->getEndDate()->format('Ymd');
$accounts = join(',', $request->getAccountList()->pluck('id')->toArray());
$categories = join(',', $request->getCategoryList()->pluck('id')->toArray());
$budgets = join(',', $request->getBudgetList()->pluck('id')->toArray());
if ($request->getAccountList()->count() === 0) {
Session::flash('error', trans('firefly.select_more_than_one_account'));
@@ -227,6 +265,18 @@ class ReportController extends Controller
return redirect(route('reports.index'));
}
if ($request->getCategoryList()->count() === 0 && $reportType === 'category') {
Session::flash('error', trans('firefly.select_more_than_one_category'));
return redirect(route('reports.index'));
}
if ($request->getBudgetList()->count() === 0 && $reportType === 'budget') {
Session::flash('error', trans('firefly.select_more_than_one_budget'));
return redirect(route('reports.index'));
}
if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date'));
}
@@ -240,19 +290,36 @@ class ReportController extends Controller
default:
throw new FireflyException(sprintf('Firefly does not support the "%s"-report yet.', $reportType));
case 'category':
$uri = route('reports.report.category', [$start, $end, $accounts, $categories]);
$uri = route('reports.report.category', [$accounts, $categories, $start, $end]);
break;
case 'default':
$uri = route('reports.report.default', [$start, $end, $accounts]);
$uri = route('reports.report.default', [$accounts, $start, $end]);
break;
case 'audit':
$uri = route('reports.report.audit', [$start, $end, $accounts]);
$uri = route('reports.report.audit', [$accounts, $start, $end]);
break;
case 'budget':
$uri = route('reports.report.budget', [$accounts, $budgets, $start, $end]);
break;
}
return redirect($uri);
}
/**
* @return string
*/
private function budgetReportOptions(): string
{
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$budgets = $repository->getBudgets();
$result = view('reports.options.budget', compact('budgets'))->render();
return $result;
}
/**
* @return string
*/

View File

@@ -104,7 +104,6 @@ class RuleController extends Controller
* @param Rule $rule
*
* @return View
* @internal param RuleRepositoryInterface $repository
*/
public function delete(Rule $rule)
{

View File

@@ -15,14 +15,12 @@ namespace FireflyIII\Http\Controllers;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Requests\TagFormRequest;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
use Input;
use Preferences;
use Response;
use Session;
use URL;
use View;
@@ -170,33 +168,14 @@ class TagController extends Controller
return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'tagOptions'));
}
/**
* @param $state
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function hideTagHelp(string $state)
{
$newState = $state == 'true' ? true : false;
Preferences::set('hideTagHelp', $newState);
return Response::json([true]);
}
/**
*
*/
public function index()
{
/** @var Preference $helpHiddenPref */
$helpHiddenPref = Preferences::get('hideTagHelp', false);
$title = 'Tags';
$mainTitleIcon = 'fa-tags';
$helpHidden = $helpHiddenPref->data;
// group years.
$types = ['nothing', 'balancingAct', 'advancePayment'];
$title = 'Tags';
$mainTitleIcon = 'fa-tags';
$types = ['nothing', 'balancingAct', 'advancePayment'];
// loop each types and get the tags, group them by year.
$collection = [];
@@ -223,7 +202,7 @@ class TagController extends Controller
}
}
return view('tags.index', compact('title', 'mainTitleIcon', 'types', 'helpHidden', 'collection'));
return view('tags.index', compact('title', 'mainTitleIcon', 'types', 'collection'));
}
/**

View File

@@ -62,7 +62,7 @@ class ConvertController extends Controller
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function convert(TransactionType $destinationType, TransactionJournal $journal)
public function index(TransactionType $destinationType, TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
@@ -115,7 +115,7 @@ class ConvertController extends Controller
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function submit(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal)
public function postIndex(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
@@ -172,14 +172,14 @@ class ConvertController extends Controller
switch ($joined) {
default:
throw new FireflyException('Cannot handle ' . $joined);
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: // one
$destination = $sourceAccount;
break;
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: // two
$destination = $accountRepository->find(intval($data['destination_account_asset']));
break;
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: // three
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: // five
$data = [
'name' => $data['destination_account_expense'],
'accountType' => 'expense',
@@ -189,8 +189,8 @@ class ConvertController extends Controller
];
$destination = $accountRepository->store($data);
break;
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: // four
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: // six
$destination = $destinationAccount;
break;
}

View File

@@ -58,7 +58,7 @@ class MassController extends Controller
*
* @return View
*/
public function massDelete(Collection $journals)
public function delete(Collection $journals)
{
$subTitle = trans('firefly.mass_delete_journals');
@@ -77,7 +77,7 @@ class MassController extends Controller
*
* @return mixed
*/
public function massDestroy(MassDeleteJournalRequest $request, JournalRepositoryInterface $repository)
public function destroy(MassDeleteJournalRequest $request, JournalRepositoryInterface $repository)
{
$ids = $request->get('confirm_mass_delete');
$set = new Collection;
@@ -114,7 +114,7 @@ class MassController extends Controller
*
* @return View
*/
public function massEdit(Collection $journals)
public function edit(Collection $journals)
{
$subTitle = trans('firefly.mass_edit_journals');
@@ -187,7 +187,7 @@ class MassController extends Controller
*
* @return mixed
*/
public function massUpdate(MassEditJournalRequest $request, JournalRepositoryInterface $repository)
public function update(MassEditJournalRequest $request, JournalRepositoryInterface $repository)
{
$journalIds = $request->get('journals');
$count = 0;

View File

@@ -179,12 +179,12 @@ class SingleController extends Controller
$count = $journal->transactions()->count();
if ($count > 2) {
return redirect(route('transactions.edit-split', [$journal->id]));
return redirect(route('transactions.split.edit', [$journal->id]));
}
$what = strtolower(TransactionJournal::transactionTypeStr($journal));
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getBudgets());
// view related code
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
@@ -286,7 +286,7 @@ class SingleController extends Controller
if ($doSplit === true) {
// redirect to edit screen:
return redirect(route('transactions.edit-split', [$journal->id]));
return redirect(route('transactions.split.edit', [$journal->id]));
}

View File

@@ -131,6 +131,7 @@ class SplitController extends Controller
*/
public function update(Request $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
@@ -157,7 +158,7 @@ class SplitController extends Controller
// set value so edit routine will not overwrite URL:
Session::put('transactions.edit-split.fromUpdate', true);
return redirect(route('transactions.edit-split', [$journal->id]))->withInput(['return_to_edit' => 1]);
return redirect(route('transactions.split.edit', [$journal->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
@@ -249,8 +250,8 @@ class SplitController extends Controller
$transactions = $this->tasker->getTransactionsOverview($journal);
$return = [];
/** @var array $transaction */
foreach ($transactions as $transaction) {
$return[] = [
foreach ($transactions as $index => $transaction) {
$set = [
'description' => $transaction['description'],
'source_account_id' => $transaction['source_account_id'],
'source_account_name' => $transaction['source_account_name'],
@@ -260,6 +261,15 @@ class SplitController extends Controller
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'],
];
// set initial category and/or budget:
if (count($transactions) === 1 && $index === 0) {
$set['budget_id'] = TransactionJournal::budgetId($journal);
$set['category'] = TransactionJournal::categoryAsString($journal);
}
$return[] = $set;
}
return $return;

View File

@@ -146,7 +146,7 @@ class TransactionController extends Controller
*
* @return View
*/
public function indexDate(Request $request, string $what, string $date)
public function indexByDate(Request $request, string $what, string $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;

View File

@@ -57,7 +57,7 @@ class AuthenticateTwoFactor
$has2faSecret = !is_null(Preferences::get('twoFactorAuthSecret'));
$is2faAuthed = Session::get('twofactor-authenticated');
if ($is2faEnabled && $has2faSecret && !$is2faAuthed) {
return redirect(route('two-factor'));
return redirect(route('two-factor.index'));
}
return $next($request);

View File

@@ -46,7 +46,12 @@ class IsConfirmed
return redirect()->guest('login');
}
// must the user be confirmed in the first place?
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
$confirmPreference = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'));
$mustConfirmAccount = false;
if (!is_null($confirmPreference)) {
$mustConfirmAccount = $confirmPreference->data;
}
// user must be logged in, then continue:
$isConfirmed = Preferences::get('user_confirmed', false)->data;

View File

@@ -17,6 +17,7 @@ use Closure;
use FireflyConfig;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Log;
use Preferences;
/**
@@ -47,9 +48,13 @@ class IsNotConfirmed
}
// must the user be confirmed in the first place?
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
Log::debug(sprintf('mustConfirmAccount is %s', $mustConfirmAccount));
// user must be logged in, then continue:
$isConfirmed = Preferences::get('user_confirmed', false)->data;
Log::debug(sprintf('isConfirmed is %s', $isConfirmed));
if ($isConfirmed || $mustConfirmAccount === false) {
Log::debug('User is confirmed or user does not have to confirm account. Redirect home.');
// user account is confirmed, simply send them home.
return redirect(route('home'));
}

View File

@@ -61,7 +61,6 @@ class Range
* @param string|null $guard
*
* @return mixed
* @internal param Closure $next
*/
public function handle(Request $request, Closure $next, $guard = null)
{

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