Compare commits

..

241 Commits
4.1.1 ... 4.1.6

Author SHA1 Message Date
James Cole
fdb8f61e37 Merge branch 'release/4.1.6' 2016-11-06 16:18:19 +01:00
James Cole
69422cc796 Code for 4.1.6 2016-11-06 16:17:22 +01:00
James Cole
5f9a9bc89a Change log for 4.1.6 2016-11-06 16:16:05 +01:00
James Cole
4d0d05e0f8 Merge pull request #383 from JC5/l10n_develop
New Crowdin translations
2016-11-06 16:14:05 +01:00
James Cole
0113fedbd4 Translated 2016-11-06 15:10:25 +01:00
James Cole
a7d35cd1c3 Fix multi year report. [skip ci] 2016-11-06 15:09:44 +01:00
James Cole
43600fe6cb Merge pull request #382 from JC5/l10n_develop
New Crowdin translations
2016-11-06 15:09:23 +01:00
James Cole
0b5e25960f Fix small JS bug. 2016-11-06 15:04:35 +01:00
James Cole
0c8a1b51e9 Quick bug fix: missing class. 2016-11-06 15:01:04 +01:00
James Cole
cb49f5e8d8 New translations 2016-11-06 15:01:02 +01:00
James Cole
a0e3088ca3 New translations 2016-11-06 15:00:54 +01:00
James Cole
b86be6f52f New translations 2016-11-06 15:00:47 +01:00
James Cole
4c573e1300 New translations 2016-11-06 15:00:43 +01:00
James Cole
1a3d77f117 New translations 2016-11-06 15:00:36 +01:00
James Cole
2656da13b1 Approved. Step name: Proofread 2016-11-06 15:00:29 +01:00
James Cole
d272ebd95c New translations 2016-11-06 15:00:18 +01:00
James Cole
7612f1f91a Small changes to twig files. 2016-11-06 14:52:48 +01:00
James Cole
22a2fe3f61 Improved search. 2016-11-06 14:52:31 +01:00
James Cole
1ebb59b352 Remove .twig extension. [skip ci] 2016-11-06 08:11:43 +01:00
James Cole
77e2cf40df Removed more getJournals functions in favour of the collector. 2016-11-06 08:08:06 +01:00
James Cole
0edffd8ea1 Lighter icon [skip ci] 2016-11-05 18:57:45 +01:00
James Cole
ee6e047596 Do not order the count query. 2016-11-05 18:55:09 +01:00
James Cole
bd55636b3f Add repository move info. 2016-11-05 18:51:26 +01:00
James Cole
b24e97a449 Update version and change log. 2016-11-05 18:50:13 +01:00
James Cole
d45355fc3f Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Translated
2016-11-05 18:46:10 +01:00
James Cole
b2206f640a Merge pull request #381 from JC5/l10n_develop
New Crowdin translations
2016-11-05 18:45:42 +01:00
James Cole
962cad33e2 Code cleanup. 2016-11-05 18:43:18 +01:00
James Cole
d65214b75a Translated 2016-11-05 18:40:12 +01:00
James Cole
7b4c151df5 Merge pull request #380 from JC5/l10n_develop
New Crowdin translations
2016-11-05 18:30:52 +01:00
James Cole
28d6f51961 New translations 2016-11-05 18:10:55 +01:00
James Cole
d2f9deb82b New translations 2016-11-05 18:10:48 +01:00
James Cole
d9b05b5f59 New translations 2016-11-05 18:10:42 +01:00
James Cole
a8f4b33c57 New translations 2016-11-05 18:10:39 +01:00
James Cole
ee849ea12f New translations 2016-11-05 18:10:32 +01:00
James Cole
f9d3cf231f Approved. Step name: Proofread 2016-11-05 18:10:27 +01:00
James Cole
0713ca7709 New translations 2016-11-05 18:10:17 +01:00
James Cole
1e2124c5ed Moved more stuff to the journal collector. 2016-11-05 18:08:44 +01:00
James Cole
37435da459 Moved more stuff to the journal collector. 2016-11-05 17:47:50 +01:00
James Cole
05dbd30bbd Rename another collector. 2016-11-05 17:17:56 +01:00
James Cole
4b947638a7 Merge pull request #379 from JC5/l10n_develop
New Crowdin translations
2016-11-05 15:16:25 +01:00
James Cole
3d113b9aae New translations 2016-11-05 14:30:14 +01:00
James Cole
d1b3681bf3 New translations 2016-11-05 14:20:16 +01:00
James Cole
9dd4b07314 New translations 2016-11-05 14:20:15 +01:00
James Cole
3814f0f3c3 New translations 2016-11-05 14:20:13 +01:00
James Cole
b1e907fae9 New translations 2016-11-05 14:10:15 +01:00
James Cole
5c03a1a9c8 New translations 2016-11-05 14:10:14 +01:00
James Cole
20ac07a386 Translated 2016-11-05 14:10:11 +01:00
James Cole
13e1292bb7 Automated code cleanup [skip ci] 2016-11-05 11:47:21 +01:00
James Cole
8e542531b3 Move collecting journals to the collector. 2016-11-05 11:44:41 +01:00
James Cole
43afdb021a Move collecting journals to the collector. 2016-11-05 11:24:15 +01:00
James Cole
aeca2ef3b2 Move some code around 2016-11-05 10:42:31 +01:00
James Cole
205a593721 Removed unused method. 2016-11-05 10:28:10 +01:00
James Cole
46649fe228 Solved group thing. 2016-11-05 10:26:57 +01:00
James Cole
adb97fcb05 Fix small javascript bug. 2016-11-05 08:47:05 +01:00
James Cole
98160e9b63 Expand use of journal collector. 2016-11-05 08:46:55 +01:00
James Cole
9c5d192d90 Journal collector may not have been a bad idea after all! 2016-11-05 08:27:25 +01:00
James Cole
47bebb614e Only withdrawal can have a budget. 2016-11-05 07:43:23 +01:00
James Cole
5f7fb77db2 Code to fix #378 2016-11-04 16:04:36 +01:00
James Cole
1d15bc0b10 I am changing some string concatenations to sprintf() routines because they are more readable and safer. [skip ci] 2016-11-03 21:54:07 +01:00
James Cole
7bc4c6d115 Update change log [skip ci] 2016-11-03 21:49:08 +01:00
James Cole
45973a53f5 Merge pull request #376 from JC5/l10n_develop
New Crowdin translations
2016-11-03 21:45:55 +01:00
James Cole
8e5e3de8b0 Update change log (prematurely). [skip ci] 2016-11-03 21:45:35 +01:00
James Cole
8738cd4b04 Approved. Step name: Proofread 2016-11-03 21:40:29 +01:00
James Cole
24a7dac235 Approved. Step name: Proofread 2016-11-03 21:40:28 +01:00
James Cole
a3088f6806 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
  Approved. Step name: Proofread
  Approved. Step name: Proofread
  New translations
  New translations
  New translations
  Translated
2016-11-03 21:07:48 +01:00
James Cole
72f7b5f3ea This fixes #375 2016-11-03 21:07:12 +01:00
James Cole
a636c508a2 Merge pull request #374 from JC5/l10n_develop
New Crowdin translations
2016-11-03 16:42:52 +01:00
James Cole
599db95f73 New translations 2016-11-02 22:31:16 +01:00
James Cole
f5f78ab79b New translations 2016-11-02 22:31:15 +01:00
James Cole
9af9383c29 New translations 2016-11-02 22:31:08 +01:00
James Cole
4b97b86c09 New translations 2016-11-02 22:31:07 +01:00
James Cole
11fb46830c New translations 2016-11-02 22:31:00 +01:00
James Cole
e8dec6d95c New translations 2016-11-02 22:31:00 +01:00
James Cole
bb4ee7470d New translations 2016-11-02 22:30:57 +01:00
James Cole
2e8071db9e New translations 2016-11-02 22:30:54 +01:00
James Cole
4d2901aa02 New translations 2016-11-02 22:30:50 +01:00
James Cole
37bbfab20a Approved. Step name: Proofread 2016-11-02 22:30:46 +01:00
James Cole
fb9161b82d Approved. Step name: Proofread 2016-11-02 22:30:43 +01:00
James Cole
000c9d8974 New translations 2016-11-02 22:30:36 +01:00
James Cole
878b664930 New translations 2016-11-02 22:30:33 +01:00
James Cole
afe28b5581 New translations 2016-11-02 22:30:33 +01:00
James Cole
4106b2e4c0 Remove some help entries in favour of help pages in the top right corner. 2016-11-02 22:23:40 +01:00
James Cole
e1be4909b9 Redirect when 0 accounts.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 21:24:16 +01:00
James Cole
7a0347c0c2 Translated 2016-11-02 21:00:38 +01:00
James Cole
a7e0e3fc15 Small additions and bug fixes.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 20:52:40 +01:00
James Cole
5e480eca36 Clean up some report code.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 20:45:11 +01:00
James Cole
6c8d594df7 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
  Approved. Step name: Proofread
  New translations
  New translations

Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 20:08:38 +01:00
James Cole
e24f5ec9f3 Multi year report move to AJAX.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 20:08:11 +01:00
James Cole
1379c0652e Merge pull request #373 from JC5/l10n_develop
New Crowdin translations
2016-11-02 19:50:10 +01:00
James Cole
1f87b0bd2d New translations 2016-11-02 14:41:56 +01:00
James Cole
787a437ca4 New translations 2016-11-02 14:41:44 +01:00
James Cole
c0bdb35cb3 New translations 2016-11-02 14:41:34 +01:00
James Cole
4b9cf67413 New translations 2016-11-02 14:41:26 +01:00
James Cole
86ff3be741 Approved. Step name: Proofread 2016-11-02 14:41:09 +01:00
James Cole
8bc8e8d9fe New translations 2016-11-02 14:40:59 +01:00
James Cole
227a12d75d New translations 2016-11-02 14:40:54 +01:00
James Cole
2ddd4314f1 Extend help pages. 2016-11-02 14:33:57 +01:00
James Cole
b980b5baea Small optimisations.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 07:23:11 +01:00
James Cole
4ba34ab511 Show sum [skip ci]
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 07:16:46 +01:00
James Cole
5be317d73c sprintf ALL THE THINGS
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 07:04:14 +01:00
James Cole
af16205965 Merge branch 'master' into develop
* master:
  New budget table for multi year report.
  Removed everything pointless from multi year report.

Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 07:03:17 +01:00
James Cole
39917b77c1 New GitHub move repository instructions
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 07:02:22 +01:00
James Cole
124ecb1372 New budget table for multi year report.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-02 04:55:44 +01:00
James Cole
33c0c1bea6 Removed everything pointless from multi year report.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-01 19:06:35 +01:00
James Cole
a66990459e Merge branch 'release/4.1.5' 2016-11-01 18:45:25 +01:00
James Cole
fecbdc7fbf New version.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-11-01 18:44:26 +01:00
James Cole
0369ace5f7 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii: (28 commits)
  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
  New translations
  New translations
  New translations
  New translations
  ...
2016-11-01 18:41:15 +01:00
James Cole
1657048181 Fixed bug #370 2016-11-01 18:40:35 +01:00
James Cole
b9bdaa7a56 Merge pull request #369 from JC5/l10n_develop
New Crowdin translations
2016-11-01 11:43:00 +01:00
James Cole
f28d07e17b Approved. Step name: Proofread 2016-11-01 11:40:36 +01:00
James Cole
8b8bf1debc New translations 2016-10-31 18:41:44 +01:00
James Cole
aff1c1e3ef New translations 2016-10-31 18:41:40 +01:00
James Cole
169bb2c9bb New translations 2016-10-31 18:41:29 +01:00
James Cole
fb1eafef43 New translations 2016-10-31 18:41:26 +01:00
James Cole
bfe26ceb39 New translations 2016-10-31 18:41:25 +01:00
James Cole
050f305e80 New translations 2016-10-31 18:41:24 +01:00
James Cole
63a6a4f823 New translations 2016-10-31 18:41:23 +01:00
James Cole
a3b167cab5 New translations 2016-10-31 18:41:23 +01:00
James Cole
48327948e2 New translations 2016-10-31 18:41:22 +01:00
James Cole
93856d4577 New translations 2016-10-31 18:41:21 +01:00
James Cole
7ff068aa95 New translations 2016-10-31 18:41:20 +01:00
James Cole
b2f00c869e New translations 2016-10-31 18:41:18 +01:00
James Cole
b717cab8f6 New translations 2016-10-31 18:41:17 +01:00
James Cole
adaff52707 New translations 2016-10-31 18:41:16 +01:00
James Cole
54050edcc6 New translations 2016-10-31 18:41:16 +01:00
James Cole
9acbb69a6a New translations 2016-10-31 18:41:15 +01:00
James Cole
a5e6de047a New translations 2016-10-31 18:41:14 +01:00
James Cole
3d8d35207b New translations 2016-10-31 18:41:13 +01:00
James Cole
0a95f59813 New translations 2016-10-31 18:41:10 +01:00
James Cole
43a3d28dbd New translations 2016-10-31 18:41:09 +01:00
James Cole
685cb7a505 Translated 2016-10-31 18:41:06 +01:00
James Cole
dd82466d07 Translated 2016-10-31 18:41:05 +01:00
James Cole
2cbe4a013e Translated 2016-10-31 18:41:04 +01:00
James Cole
fb85341844 Translated 2016-10-31 18:41:03 +01:00
James Cole
116b3ecdad Approved. Step name: Proofread 2016-10-31 18:40:58 +01:00
James Cole
af85fbf0a3 New translations 2016-10-31 18:40:50 +01:00
James Cole
1d250593c0 New translations 2016-10-31 18:40:45 +01:00
James Cole
ed33a054ad This update will make the help method fall back to the English content, if it is available. 2016-10-31 18:31:52 +01:00
James Cole
4e3e015912 Fix multi year account report [skip ci] 2016-10-30 20:13:49 +01:00
James Cole
7821c52842 Ajax some report parts. 2016-10-30 18:29:26 +01:00
James Cole
0a6f299ae6 Merge branch 'release/4.1.4' 2016-10-30 08:56:35 +01:00
James Cole
73f87e30c2 Changelog.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-30 08:52:09 +01:00
James Cole
838ece2c89 Merge pull request #368 from JC5/l10n_develop
New Crowdin translations
2016-10-30 07:32:23 +01:00
James Cole
d8b88ea2c0 New translations 2016-10-30 07:20:17 +01:00
James Cole
5908951b75 New translations 2016-10-30 07:20:17 +01:00
James Cole
0b41f4c4d2 New translations 2016-10-30 07:20:16 +01:00
James Cole
35439d4fbc New translations 2016-10-30 07:20:16 +01:00
James Cole
fdce40310f New translations 2016-10-30 07:20:15 +01:00
James Cole
6b4785ae32 New translations 2016-10-30 07:20:15 +01:00
James Cole
f74e8e9cb7 New translations 2016-10-30 07:20:13 +01:00
James Cole
5a4eb7e09e New translations 2016-10-30 07:20:12 +01:00
James Cole
3bc4df03cc New translations 2016-10-30 07:20:11 +01:00
James Cole
5d585132fb New translations 2016-10-30 07:20:11 +01:00
James Cole
eff4905883 New translations 2016-10-30 07:20:10 +01:00
James Cole
8923ac4fe3 New translations 2016-10-30 07:20:09 +01:00
James Cole
073535e5ed New translations 2016-10-30 07:20:09 +01:00
James Cole
d304b90ca6 New translations 2016-10-30 07:20:08 +01:00
James Cole
816c26e14e New translations 2016-10-30 07:20:07 +01:00
James Cole
b1244ffa01 New translations 2016-10-30 07:20:07 +01:00
James Cole
fc1342bff9 New translations 2016-10-30 07:20:06 +01:00
James Cole
d7b95194b5 New translations 2016-10-30 07:20:04 +01:00
James Cole
b58bdeccd2 New translations 2016-10-30 07:20:04 +01:00
James Cole
f260b9bdee New translations 2016-10-30 07:20:02 +01:00
James Cole
fb1bdc9ec5 New translations 2016-10-30 07:20:01 +01:00
James Cole
697eff48fc New translations 2016-10-30 07:20:00 +01:00
James Cole
c05019339a Approved. Step name: Proofread 2016-10-30 07:20:00 +01:00
James Cole
8438efaf41 Approved. Step name: Proofread 2016-10-30 07:19:59 +01:00
James Cole
81c019cc99 Translated 2016-10-30 07:19:59 +01:00
James Cole
c773fdc435 Approved. Step name: Proofread 2016-10-30 07:19:58 +01:00
James Cole
c1406f51f1 Approved. Step name: Proofread 2016-10-30 07:19:58 +01:00
James Cole
92affd3440 Approved. Step name: Proofread 2016-10-30 07:19:57 +01:00
James Cole
d3da0652ef Approved. Step name: Proofread 2016-10-30 07:19:57 +01:00
James Cole
e3fbbd6cf1 Translated 2016-10-30 07:19:56 +01:00
James Cole
fcff13470c Approved. Step name: Proofread 2016-10-30 07:19:56 +01:00
James Cole
e3061ee7e7 Approved. Step name: Proofread 2016-10-30 07:19:55 +01:00
James Cole
0ee305fc4a Approved. Step name: Proofread 2016-10-30 07:19:55 +01:00
James Cole
58b93fd0c4 Approved. Step name: Proofread 2016-10-30 07:19:53 +01:00
James Cole
b30217fa2d Approved. Step name: Proofread 2016-10-30 07:19:52 +01:00
James Cole
ae48eec3a2 Translated 2016-10-30 07:19:52 +01:00
James Cole
948233ba27 Translated 2016-10-30 07:19:52 +01:00
James Cole
c2db9b183a New translations 2016-10-30 07:19:51 +01:00
James Cole
6d2b88fa0b New translations 2016-10-30 07:19:51 +01:00
James Cole
1d5da825c5 New translations 2016-10-30 07:19:50 +01:00
James Cole
330c9b53d6 New translations 2016-10-30 07:19:50 +01:00
James Cole
751fe7d4fb New translations 2016-10-30 07:19:49 +01:00
James Cole
9df1fc6e5d New translations 2016-10-30 07:19:48 +01:00
James Cole
8d660f1701 New translations 2016-10-30 07:19:46 +01:00
James Cole
4d61d3c4aa New translations 2016-10-30 07:19:46 +01:00
James Cole
0457088c99 New translations 2016-10-30 07:19:45 +01:00
James Cole
8e575da74e New translations 2016-10-30 07:19:45 +01:00
James Cole
48ed28888e Translated 2016-10-30 07:19:43 +01:00
James Cole
4084b1124e Translated 2016-10-30 07:19:42 +01:00
James Cole
60ba607027 Translated 2016-10-30 07:19:42 +01:00
James Cole
3df2c11b4a Translated 2016-10-30 07:19:41 +01:00
James Cole
c93221923a Translated 2016-10-30 07:19:41 +01:00
James Cole
375317e932 New translations 2016-10-30 07:19:40 +01:00
James Cole
7ce527957a New translations 2016-10-30 07:19:40 +01:00
James Cole
6946521199 New translations 2016-10-30 07:19:39 +01:00
James Cole
18ee20e680 Update crowdin file [skip ci]
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-30 06:37:00 +01:00
James Cole
c53da15219 Update composer.lock in anticipation of new release.
Signed-off-by: James Cole <thegrumpydictator@gmail.com>
2016-10-30 06:20:18 +01:00
James Cole
d4995e342f Fixed #330 2016-10-30 06:14:07 +01:00
James Cole
c9f14da294 Translations. 2016-10-29 17:30:55 +02:00
James Cole
e9c2446cba Debug log for #366 2016-10-29 16:16:10 +02:00
James Cole
35f179625c New queries for #366 2016-10-29 16:11:54 +02:00
James Cole
39749aa113 First code set for #330 2016-10-29 15:14:33 +02:00
James Cole
ba65e982fd Forgot to include model [skip ci] 2016-10-29 11:42:04 +02:00
James Cole
b50e5d7e59 Can also change destination in new rule. 2016-10-29 09:22:51 +02:00
James Cole
a3148dc172 Code for #321 2016-10-29 09:03:14 +02:00
James Cole
73f1491d2d Updates for translations. 2016-10-29 07:44:46 +02:00
James Cole
28eb54dc96 Initial split for report options. 2016-10-28 19:01:52 +02:00
James Cole
21fb426524 Make sure date is localised. 2016-10-28 18:16:30 +02:00
James Cole
d5710ca809 Update Crowdin configuration file 2016-10-28 14:06:41 +02:00
James Cole
0ba6cdda17 Merge pull request #364 from schoentoon/develop
Added dockerfile
2016-10-27 19:40:27 +02:00
James Cole
afdcfa8525 Tweak reports. 2016-10-26 19:45:10 +02:00
James Cole
5db4f8512b Tweak reports. 2016-10-26 19:37:19 +02:00
James Cole
dc0c1b73bc Better view for expenses. 2016-10-26 19:32:19 +02:00
James Cole
f999257095 New verify database routine. 2016-10-26 19:32:07 +02:00
James Cole
7182909e28 Keep the box [skip ci] 2016-10-26 16:54:52 +02:00
James Cole
fe3f015171 Add more stuff to ajax controllers, making report controller simpler. 2016-10-26 16:46:43 +02:00
Toon Schoenmakers
5bb668be63 Added dockerfile 2016-10-26 15:07:36 +02:00
James Cole
01de147900 Display message about common error. 2016-10-26 06:41:50 +02:00
James Cole
a7e5fcc806 Move some stuff over to AJAX thing. 2016-10-25 18:53:54 +02:00
James Cole
e2d187d74b Various small bug fixes. 2016-10-24 18:01:15 +02:00
James Cole
48b0620629 New help thing. 2016-10-23 17:33:53 +02:00
James Cole
19e9f382e4 Add some rounding to make forms more neat. 2016-10-23 16:56:18 +02:00
James Cole
446eaf6588 Some code cleanup [skip ci] 2016-10-23 14:58:39 +02:00
James Cole
78deb1420d Some fixes for bills. 2016-10-23 14:56:05 +02:00
James Cole
e092515dff Better export. 2016-10-23 12:55:07 +02:00
James Cole
81f6fef978 Add new line to files [skip ci] 2016-10-23 12:42:44 +02:00
James Cole
6a2f8fa9ee No use models directly. 2016-10-23 12:41:54 +02:00
James Cole
a79a8c8874 Various small upgrades. 2016-10-23 12:37:12 +02:00
James Cole
c39659b064 Remove a lot of references to user id. 2016-10-23 12:19:32 +02:00
James Cole
9a30fbd05a Move stuff to request classes for #339 2016-10-23 12:10:22 +02:00
James Cole
83f48418f6 Small updates [skip ci] 2016-10-23 09:57:04 +02:00
James Cole
bcd7b41c91 Simplified export. 2016-10-23 09:44:14 +02:00
James Cole
cefb7d12bc Merge branch 'release/4.1.3' 2016-10-22 22:45:27 +02:00
James Cole
3c0c15103e This fixes #361 2016-10-22 22:44:57 +02:00
James Cole
a8a8afc2be More for #339 2016-10-22 22:03:00 +02:00
James Cole
49e32abd3f Move some code for #339 2016-10-22 21:40:31 +02:00
James Cole
7977eefaca Merge branch 'release/4.1.2' 2016-10-22 20:52:54 +02:00
James Cole
f1fa6c3108 Fixed a bug in the store transaction routine. 2016-10-22 20:50:20 +02:00
373 changed files with 12979 additions and 8010 deletions

View File

@@ -2,7 +2,60 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [4.1.6] - 2016-11-06
### Added
- New budget table for multi year report.
### Changed
- Greatly expanded help pages and their function.
- Built a new transaction collector, which I think was the idea of @roberthorlings originally.
- Rebuilt seach engine.
### Fixed
- #375, thanks to @schoentoon which made it impossible to resurrect currencies.
- #370 thanks to @ksmolder
- #378, thanks to @HomelessAvatar
## [4.1.5] - 2016-11-01
### Changed
- Report parts are loaded using AJAX, making a lot of code more simple.
- Help content will fall back to English.
- Help content is translated through Crowdin.
### Fixed
- Issue #370
## [4.1.4] - 2016-10-30
### Added
- New Dockerfile thanks to @schoentoon
- Added changing the destination account as rule action.
- Added changing the source account as rule action.
- Can convert transactions into different types.
### Changed
- Changed the export routine to be more future-proof.
- Improved help routine.
- Integrated CrowdIn translations.
- Simplified reports
- Change error message to refer to solution.
### Fixed
- #367 thanks to @HungryFeline
- #366 thanks to @3mz3t
- #362 and #341 thanks to @bnw
- #355 thanks to @roberthorlings
## [4.1.3] - 2016-10-22
### Fixed
- Some event handlers called the wrong method.
## [4.1.2] - 2016-10-22
### Fixed
- A bug is fixed in the journal event handler that prevented Firefly III from actually storing journals.
## [4.1.1] - 2016-10-22
### Added
- Option to show deposit accounts on the front page.
- Script to upgrade split transactions

42
Dockerfile Normal file
View File

@@ -0,0 +1,42 @@
FROM php:7-apache
RUN apt-get update -y && \
apt-get install -y --no-install-recommends libcurl4-openssl-dev \
zlib1g-dev \
libjpeg62-turbo-dev \
libpng12-dev \
libicu-dev \
libmcrypt-dev \
libedit-dev \
libtidy-dev \
libxml2-dev \
libsqlite3-dev \
libbz2-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2
# Enable apache mod rewrite..
RUN a2enmod rewrite
# Setup the Composer installer
RUN curl -o /tmp/composer-setup.php https://getcomposer.org/installer && \
curl -o /tmp/composer-setup.sig https://composer.github.io/installer.sig && \
php -r "if (hash('SHA384', file_get_contents('/tmp/composer-setup.php')) !== trim(file_get_contents('/tmp/composer-setup.sig'))) { unlink('/tmp/composer-setup.php'); echo 'Invalid installer' . PHP_EOL; exit(1); }" && \
chmod +x /tmp/composer-setup.php && \
php /tmp/composer-setup.php && \
mv composer.phar /usr/local/bin/composer && \
rm -f /tmp/composer-setup.{php,sig}
ADD . /var/www/firefly-iii
RUN chown -R www-data:www-data /var/www/
ADD docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
USER www-data
WORKDIR /var/www/firefly-iii
RUN composer install --no-scripts --no-dev
USER root

View File

@@ -66,7 +66,7 @@ class Import extends Command
return;
}
$this->line('Going to import job with key "' . $job->key . '" of type ' . $job->file_type);
$this->line(sprintf('Going to import job with key "%s" of type "%s"', $job->key, $job->file_type));
$monolog = Log::getMonolog();
$handler = new CommandHandler($this);

View File

@@ -0,0 +1,86 @@
<?php
/**
* MoveRepository.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\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
/**
* Class MoveRepository
*
* @package FireflyIII\Console\Commands
*/
class MoveRepository extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Alerts the user that the Github repository will move, if they are interested to know this.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:github-move';
/**
* Create a new command instance.
*
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$moveDate = new Carbon('2017-01-01');
$final = new Carbon('2017-03-01');
$now = new Carbon;
// display message before 2017-01-01
if ($moveDate > $now) {
$this->line('+------------------------------------------------------------------------------+');
$this->line('');
$this->line('The Github repository for Firefly III will MOVE');
$this->line('This move will be on January 1st 2017');
$this->line('');
$this->error('READ THIS WIKI PAGE FOR MORE INFORMATION');
$this->line('');
$this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository');
$this->line('');
$this->line('+------------------------------------------------------------------------------+');
}
// display message after 2017-01-01 but before 2017-03-01
if ($moveDate <= $now && $now <= $final) {
$this->line('+------------------------------------------------------------------------------+');
$this->line('');
$this->line('The Github repository for Firefly III has MOVED');
$this->line('This move was on January 1st 2017!');
$this->line('');
$this->error('READ THIS WIKI PAGE FOR MORE INFORMATION');
$this->line('');
$this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository');
$this->line('');
$this->line('+------------------------------------------------------------------------------+');
}
}
}

View File

@@ -99,7 +99,7 @@ class UpgradeDatabase extends Command
} catch (QueryException $e) {
Log::error($e->getMessage());
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
$this->error('This field is required for Firefly III version ' . config('firefly.version') . ' to run.');
$this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version')));
$this->error('Please run "php artisan migrate" to add this field to the table.');
$this->info('Then, run "php artisan firefly:upgrade-database" to try again.');
break 2;
@@ -118,4 +118,4 @@ class UpgradeDatabase extends Command
}
}
}
}
}

View File

@@ -63,21 +63,20 @@ class UpgradeFireflyInstructions extends Command
}
$this->line('+------------------------------------------------------------------------------+');
$this->line('');
if (is_null($text)) {
$this->line('Thank you for installing Firefly III, v' . $version);
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
$this->info('There are no extra upgrade instructions.');
$this->line('Firefly III should be ready for use.');
} else {
$this->line('Thank you for installing Firefly III, v' . $version);
$this->line('If you are upgrading from a previous version,');
$this->line('please follow these upgrade instructions carefully:');
$this->line('+------------------------------------------------------------------------------+');
$this->line('');
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
$this->info(wordwrap($text));
$this->line('');
$this->line('+------------------------------------------------------------------------------+');
}
$this->line('');
$this->line('+------------------------------------------------------------------------------+');
}
}

View File

@@ -15,6 +15,7 @@ namespace FireflyIII\Console\Commands;
use Crypt;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
@@ -84,6 +85,9 @@ class VerifyDatabase extends Command
// transfers with budgets.
$this->reportTransfersBudgets();
// report on journals with the wrong types of accounts.
$this->reportIncorrectJournals();
}
/**
@@ -123,8 +127,11 @@ class VerifyDatabase extends Command
/** @var stdClass $entry */
foreach ($set as $entry) {
$line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has budget #' . $entry->id . ' ("' . Crypt::decrypt($entry->name)
. '") which has no budget limits.';
$line = sprintf(
'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.',
$entry->user_id, $entry->email, $entry->id, Crypt::decrypt($entry->name)
);
$this->line($line);
}
}
@@ -202,6 +209,45 @@ class VerifyDatabase extends Command
}
}
private function reportIncorrectJournals()
{
$configuration = [
// a withdrawal can not have revenue account:
TransactionType::WITHDRAWAL => [AccountType::REVENUE],
// deposit cannot have an expense account:
TransactionType::DEPOSIT => [AccountType::EXPENSE],
// transfer cannot have either:
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']);
foreach ($set as $entry) {
$this->error(
sprintf(
'Transaction journal #%d (user #%d, %s) is of type "%s" but ' .
'is linked to a "%s". The transaction journal should be recreated.',
$entry->id,
$entry->user_id,
$entry->email,
$entry->type,
$entry->a_type
)
);
}
}
}
/**
* Any deleted transaction journals that have transactions that are NOT deleted:
*/

View File

@@ -16,6 +16,7 @@ namespace FireflyIII\Console;
use FireflyIII\Console\Commands\CreateImport;
use FireflyIII\Console\Commands\EncryptFile;
use FireflyIII\Console\Commands\Import;
use FireflyIII\Console\Commands\MoveRepository;
use FireflyIII\Console\Commands\ScanAttachments;
use FireflyIII\Console\Commands\UpgradeDatabase;
use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
@@ -63,7 +64,7 @@ class Kernel extends ConsoleKernel
EncryptFile::class,
ScanAttachments::class,
UpgradeDatabase::class,
MoveRepository::class,
];
/**

View File

@@ -29,7 +29,7 @@ class ConfirmedUser extends Event
public $user;
/**
* Create a new event instance.
* Create a new event instance. This event is triggered when a user confirms their new account.
*
* @param User $user
* @param string $ipAddress

View File

@@ -29,7 +29,7 @@ class RegisteredUser extends Event
public $user;
/**
* Create a new event instance.
* Create a new event instance. This event is triggered when a new user registers.
*
* @param User $user
* @param string $ipAddress

View File

@@ -29,7 +29,7 @@ class ResentConfirmation extends Event
public $user;
/**
* Create a new event instance.
* Create a new event instance. This event is triggered when a users wants a new confirmation.
*
* @param User $user
* @param string $ipAddress

View File

@@ -13,11 +13,10 @@ declare(strict_types = 1);
namespace FireflyIII\Export\Collector;
use Amount;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\ExportJob;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Collection;
@@ -31,12 +30,14 @@ use Storage;
*/
class AttachmentCollector extends BasicCollector implements CollectorInterface
{
/** @var string */
private $explanationString = '';
/** @var Carbon */
private $end;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk;
/** @var AttachmentRepositoryInterface */
private $repository;
/** @var Carbon */
private $start;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $uploadDisk;
@@ -69,34 +70,17 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
$this->exportAttachment($attachment);
}
// put the explanation string in a file and attach it as well.
$file = $this->job->key . '-Source of all your attachments explained.txt';
$this->exportDisk->put($file, $this->explanationString);
$this->getFiles()->push($file);
return true;
}
/**
* @param Attachment $attachment
* @param Carbon $start
* @param Carbon $end
*/
private function explain(Attachment $attachment)
public function setDates(Carbon $start, Carbon $end)
{
/** @var TransactionJournal $journal */
$journal = $attachment->attachable;
$args = [
'attachment_name' => e($attachment->filename),
'attachment_id' => $attachment->id,
'type' => strtolower($journal->transactionType->type),
'description' => e($journal->description),
'journal_id' => $journal->id,
'date' => $journal->date->formatLocalized(strval(trans('config.month_and_day'))),
'amount' => Amount::formatJournal($journal, false),
];
$string = trans('firefly.attachment_explanation', $args) . "\n";
Log::debug('Appended explanation string', ['string' => $string]);
$this->explanationString .= $string;
$this->start = $start;
$this->end = $end;
}
/**
@@ -112,10 +96,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
$decrypted = Crypt::decrypt($this->uploadDisk->get($file));
$exportFile = $this->exportFileName($attachment);
$this->exportDisk->put($exportFile, $decrypted);
$this->getFiles()->push($exportFile);
$this->getEntries()->push($exportFile);
// explain:
$this->explain($attachment);
} catch (DecryptException $e) {
Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage());
}
@@ -143,7 +125,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
*/
private function getAttachments(): Collection
{
$attachments = $this->repository->get();
$attachments = $this->repository->getBetween($this->start, $this->end);
return $attachments;
}

View File

@@ -27,7 +27,7 @@ class BasicCollector
/** @var ExportJob */
protected $job;
/** @var Collection */
private $files;
private $entries;
/**
* BasicCollector constructor.
@@ -36,24 +36,24 @@ class BasicCollector
*/
public function __construct(ExportJob $job)
{
$this->files = new Collection;
$this->job = $job;
$this->entries = new Collection;
$this->job = $job;
}
/**
* @return Collection
*/
public function getFiles(): Collection
public function getEntries(): Collection
{
return $this->files;
return $this->entries;
}
/**
* @param Collection $files
* @param Collection $entries
*/
public function setFiles(Collection $files)
public function setEntries(Collection $entries)
{
$this->files = $files;
$this->entries = $entries;
}

View File

@@ -25,7 +25,7 @@ interface CollectorInterface
/**
* @return Collection
*/
public function getFiles(): Collection;
public function getEntries(): Collection;
/**
* @return bool
@@ -33,9 +33,9 @@ interface CollectorInterface
public function run(): bool;
/**
* @param Collection $files
* @param Collection $entries
*
*/
public function setFiles(Collection $files);
public function setEntries(Collection $entries);
}

View File

@@ -0,0 +1,348 @@
<?php
/**
* JournalExportCollector.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\Collector;
use Carbon\Carbon;
use Crypt;
use DB;
use FireflyIII\Models\Transaction;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
* Class JournalExportCollector
*
* @package FireflyIII\Export\Collector
*/
class JournalExportCollector extends BasicCollector implements CollectorInterface
{
/** @var Collection */
private $accounts;
/** @var Carbon */
private $end;
/** @var Carbon */
private $start;
/** @var Collection */
private $workSet;
/**
* @return bool
*/
public function run(): bool
{
/*
* Instead of collecting journals we collect transactions for the given accounts.
* We left join the OPPOSING transaction AND some journal data.
* After that we complement this info with budgets, categories, etc.
*
* This is way more efficient and will also work on split journals.
*/
$this->getWorkSet();
/*
* Extract:
* possible budget ids for journals
* possible category ids journals
* possible budget ids for transactions
* possible category ids for transactions
*
* possible IBAN and account numbers?
*
*/
$journals = $this->extractJournalIds();
$transactions = $this->extractTransactionIds();
// extend work set with category data from journals:
$this->categoryDataForJournals($journals);
// extend work set with category cate from transactions (overrules journals):
$this->categoryDataForTransactions($transactions);
// same for budgets:
$this->budgetDataForJournals($journals);
$this->budgetDataForTransactions($transactions);
$this->setEntries($this->workSet);
return true;
}
/**
* @param Collection $accounts
*/
public function setAccounts(Collection $accounts)
{
$this->accounts = $accounts;
}
/**
* @param Carbon $start
* @param Carbon $end
*/
public function setDates(Carbon $start, Carbon $end)
{
$this->start = $start;
$this->end = $end;
}
/**
* @param array $journals
*
* @return bool
*/
private function budgetDataForJournals(array $journals): bool
{
$set = DB::table('budget_transaction_journal')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->whereIn('budget_transaction_journal.transaction_journal_id', $journals)
->get(
[
'budget_transaction_journal.budget_id',
'budget_transaction_journal.transaction_journal_id',
'budgets.name',
'budgets.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_journal_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
if (isset($array[$obj->transaction_journal_id])) {
$obj->budget_id = $array[$obj->transaction_journal_id]['id'];
$obj->budget_name = $array[$obj->transaction_journal_id]['name'];
}
}
);
return true;
}
/**
* @param array $transactions
*
* @return bool
*/
private function budgetDataForTransactions(array $transactions): bool
{
$set = DB::table('budget_transaction')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction.budget_id')
->whereIn('budget_transaction.transaction_id', $transactions)
->get(
[
'budget_transaction.budget_id',
'budget_transaction.transaction_id',
'budgets.name',
'budgets.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
// first transaction
if (isset($array[$obj->id])) {
$obj->budget_id = $array[$obj->id]['id'];
$obj->budget_name = $array[$obj->id]['name'];
}
}
);
return true;
}
/**
* @param array $journals
*
* @return bool
*/
private function categoryDataForJournals(array $journals): bool
{
$set = DB::table('category_transaction_journal')
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->whereIn('category_transaction_journal.transaction_journal_id', $journals)
->get(
[
'category_transaction_journal.category_id',
'category_transaction_journal.transaction_journal_id',
'categories.name',
'categories.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_journal_id] = ['id' => $obj->category_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
if (isset($array[$obj->transaction_journal_id])) {
$obj->category_id = $array[$obj->transaction_journal_id]['id'];
$obj->category_name = $array[$obj->transaction_journal_id]['name'];
}
}
);
return true;
}
/**
* @param array $transactions
*
* @return bool
*/
private function categoryDataForTransactions(array $transactions): bool
{
$set = DB::table('category_transaction')
->leftJoin('categories', 'categories.id', '=', 'category_transaction.category_id')
->whereIn('category_transaction.transaction_id', $transactions)
->get(
[
'category_transaction.category_id',
'category_transaction.transaction_id',
'categories.name',
'categories.encrypted',
]
);
$set->each(
function ($obj) {
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
}
);
$array = [];
foreach ($set as $obj) {
$array[$obj->transaction_id] = ['id' => $obj->category_id, 'name' => $obj->name];
}
$this->workSet->each(
function ($obj) use ($array) {
// first transaction
if (isset($array[$obj->id])) {
$obj->category_id = $array[$obj->id]['id'];
$obj->category_name = $array[$obj->id]['name'];
}
}
);
return true;
}
/**
* @return array
*/
private function extractJournalIds(): array
{
return $this->workSet->pluck('transaction_journal_id')->toArray();
}
/**
* @return array
*/
private function extractTransactionIds()
{
$set = $this->workSet->pluck('id')->toArray();
$opposing = $this->workSet->pluck('opposing_id')->toArray();
$complete = $set + $opposing;
return array_unique($complete);
}
/**
*
*/
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', '=', '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',
'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

@@ -26,16 +26,14 @@ use Storage;
*/
class UploadCollector extends BasicCollector implements CollectorInterface
{
/** @var string */
private $expected;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk;
private $importKeys = [];
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $uploadDisk;
/** @var string */
private $vintageFormat;
/**
*
* AttachmentCollector constructor.
*
* @param ExportJob $job
@@ -51,50 +49,74 @@ class UploadCollector extends BasicCollector implements CollectorInterface
$this->exportDisk = Storage::disk('export');
// file names associated with the old import routine.
$this->expected = 'csv-upload-' . auth()->user()->id . '-';
$this->vintageFormat = sprintf('csv-upload-%d-', auth()->user()->id);
// for the new import routine:
$this->getImportKeys();
}
/**
* Is called from the outside to actually start the export.
*
* @return bool
*/
public function run(): bool
{
// grab upload directory.
$files = $this->uploadDisk->files();
// collect old upload files (names beginning with "csv-upload".
$this->collectVintageUploads();
foreach ($files as $entry) {
$this->processUpload($entry);
// then collect current upload files:
$this->collectModernUploads();
return true;
}
/**
* This method collects all the uploads that are uploaded using the new importer. So after the summer of 2016.
*
* @return bool
*/
private function collectModernUploads(): bool
{
$set = $this->job->user->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']);
$keys = [];
if ($set->count() > 0) {
$keys = $set->pluck('key')->toArray();
}
foreach ($keys as $key) {
$this->processModernUpload($key);
}
return true;
}
/**
* This method collects all the uploads that are uploaded using the "old" importer. So from before the summer of 2016.
*
* @return bool
*/
private function getImportKeys()
private function collectVintageUploads():bool
{
$set = auth()->user()->importJobs()->where('status', 'import_complete')->get(['import_jobs.*']);
if ($set->count() > 0) {
$keys = $set->pluck('key')->toArray();
$this->importKeys = $keys;
// grab upload directory.
$files = $this->uploadDisk->files();
foreach ($files as $entry) {
$this->processVintageUpload($entry);
}
Log::debug('Valid import keys are ', $this->importKeys);
return true;
}
/**
* This method tells you when the vintage upload file was actually uploaded.
*
* @param string $entry
*
* @return string
*/
private function getOriginalUploadDate(string $entry): string
private function getVintageUploadDate(string $entry): string
{
// this is an original upload.
$parts = explode('-', str_replace(['.csv.encrypted', $this->expected], '', $entry));
$parts = explode('-', str_replace(['.csv.encrypted', $this->vintageFormat], '', $entry));
$originalUpload = intval($parts[1]);
$date = date('Y-m-d \a\t H-i-s', $originalUpload);
@@ -102,33 +124,17 @@ class UploadCollector extends BasicCollector implements CollectorInterface
}
/**
* Tells you if a file name is a vintage upload.
*
* @param string $entry
*
* @return bool
*/
private function isImportFile(string $entry): bool
private function isVintageImport(string $entry): bool
{
$name = str_replace('.upload', '', $entry);
if (in_array($name, $this->importKeys)) {
Log::debug(sprintf('Import file "%s" is in array', $name), $this->importKeys);
return true;
}
Log::debug(sprintf('Import file "%s" is NOT in array', $name), $this->importKeys);
return false;
}
/**
* @param string $entry
*
* @return bool
*/
private function isOldImport(string $entry): bool
{
$len = strlen($this->expected);
$len = strlen($this->vintageFormat);
// file is part of the old import routine:
if (substr($entry, 0, $len) === $this->expected) {
if (substr($entry, 0, $len) === $this->vintageFormat) {
return true;
}
@@ -137,49 +143,62 @@ class UploadCollector extends BasicCollector implements CollectorInterface
}
/**
* @param $entry
* @param string $key
*
* @return bool
*/
private function processUpload(string $entry)
{
// file is old import:
if ($this->isOldImport($entry)) {
$this->saveOldImportFile($entry);
}
// file is current import.
if ($this->isImportFile($entry)) {
$this->saveImportFile($entry);
}
}
/**
* @param string $entry
*/
private function saveImportFile(string $entry)
private function processModernUpload(string $key): bool
{
// find job associated with import file:
$name = str_replace('.upload', '', $entry);
$job = auth()->user()->importJobs()->where('key', $name)->first();
$content = '';
try {
$content = Crypt::decrypt($this->uploadDisk->get($entry));
} catch (DecryptException $e) {
Log::error('Could not decrypt old import file ' . $entry . '. Skipped because ' . $e->getMessage());
$job = $this->job->user->importJobs()->where('key', $key)->first();
if (is_null($job)) {
return false;
}
if (!is_null($job) && strlen($content) > 0) {
// find the file for this import:
$content = '';
try {
$content = Crypt::decrypt($this->uploadDisk->get(sprintf('%s.upload', $key)));
} catch (DecryptException $e) {
Log::error(sprintf('Could not decrypt old import file "%s". Skipped because: %s', $key, $e->getMessage()));
}
if (strlen($content) > 0) {
// add to export disk.
$date = $job->created_at->format('Y-m-d');
$file = sprintf('%s-Old %s import dated %s.%s', $this->job->key, strtoupper($job->file_type), $date, $job->file_type);
$this->exportDisk->put($file, $content);
$this->getFiles()->push($file);
$this->getEntries()->push($file);
}
return true;
}
/**
* If the file is a vintage upload, process it.
*
* @param string $entry
*
* @return bool
*/
private function processVintageUpload(string $entry): bool
{
if ($this->isVintageImport($entry)) {
$this->saveVintageImportFile($entry);
return true;
}
return false;
}
/**
* This will store the content of the old vintage upload somewhere.
*
* @param string $entry
*/
private function saveOldImportFile(string $entry)
private function saveVintageImportFile(string $entry)
{
$content = '';
try {
@@ -190,10 +209,10 @@ class UploadCollector extends BasicCollector implements CollectorInterface
if (strlen($content) > 0) {
// add to export disk.
$date = $this->getOriginalUploadDate($entry);
$date = $this->getVintageUploadDate($entry);
$file = $this->job->key . '-Old import dated ' . $date . '.csv';
$this->exportDisk->put($file, $content);
$this->getFiles()->push($file);
$this->getEntries()->push($file);
}
}

View File

@@ -1,68 +0,0 @@
<?php
/**
* ConfigurationFile.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 FireflyIII\Export\Entry\Entry;
use FireflyIII\Models\ExportJob;
use Storage;
/**
* Class ConfigurationFile
*
* @package FireflyIII\Export
*/
class ConfigurationFile
{
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
private $exportDisk;
/** @var ExportJob */
private $job;
/**
* ConfigurationFile constructor.
*
* @param ExportJob $job
*/
public function __construct(ExportJob $job)
{
$this->job = $job;
$this->exportDisk = Storage::disk('export');
}
/**
* @return string
*/
public function make(): string
{
$fields = array_keys(Entry::getFieldsAndTypes());
$types = Entry::getFieldsAndTypes();
$configuration = [
'date-format' => 'Y-m-d', // unfortunately, this is hard-coded.
'has-headers' => true,
'map' => [], // we could build a map if necessary for easy re-import.
'roles' => [],
'mapped' => [],
'specifix' => [],
];
foreach ($fields as $field) {
$configuration['roles'][] = $types[$field];
}
$file = $this->job->key . '-configuration.json';
$this->exportDisk->put($file, json_encode($configuration, JSON_PRETTY_PRINT));
return $file;
}
}

View File

@@ -13,8 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use Crypt;
/**
* To extend the exported object, in case of new features in Firefly III for example,
@@ -35,98 +34,77 @@ use Illuminate\Support\Collection;
*/
final class Entry
{
/** @var string */
public $amount;
/** @var EntryBill */
public $bill;
/** @var EntryBudget */
public $budget;
/** @var EntryCategory */
public $category;
/** @var string */
// @formatter:off
public $journal_id;
public $date;
/** @var string */
public $description;
/** @var EntryAccount */
public $destinationAccount;
/** @var Collection */
public $destinationAccounts;
/** @var EntryAccount */
public $sourceAccount;
/** @var Collection */
public $sourceAccounts;
public $currency_code;
public $amount;
public $transaction_type;
public $source_account_id;
public $source_account_name;
public $destination_account_id;
public $destination_account_name;
public $budget_id;
public $budget_name;
public $category_id;
public $category_name;
// @formatter:on
/**
* Entry constructor.
*/
private function __construct()
{
$this->sourceAccounts = new Collection;
$this->destinationAccounts = new Collection;
}
/**
* @param TransactionJournal $journal
* @param $object
*
* @return Entry
*/
public static function fromJournal(TransactionJournal $journal)
public static function fromObject($object): Entry
{
$entry = new self;
$entry = new self;
$entry->description = $journal->description;
$entry->date = $journal->date->format('Y-m-d');
$entry->amount = TransactionJournal::amount($journal);
// journal information:
$entry->journal_id = $object->transaction_journal_id;
$entry->description = $object->journal_encrypted === 1 ? Crypt::decrypt($object->journal_description) : $object->journal_description;
$entry->amount = round($object->amount, 2); // always positive
$entry->date = $object->date;
$entry->transaction_type = $object->transaction_type;
$entry->currency_code = $object->transaction_currency_code;
$entry->budget = new EntryBudget($journal->budgets->first());
$entry->category = new EntryCategory($journal->categories->first());
$entry->bill = new EntryBill($journal->bill);
// source information:
$entry->source_account_id = $object->account_id;
$entry->source_account_name = $object->account_name_encrypted === 1 ? Crypt::decrypt($object->account_name) : $object->account_name;
$sources = TransactionJournal::sourceAccountList($journal);
$destinations = TransactionJournal::destinationAccountList($journal);
$entry->sourceAccount = new EntryAccount($sources->first());
$entry->destinationAccount = new EntryAccount($destinations->first());
foreach ($sources as $source) {
$entry->sourceAccounts->push(new EntryAccount($source));
}
// destination information
$entry->destination_account_id = $object->opposing_account_id;
$entry->destination_account_name = $object->opposing_account_encrypted === 1 ? Crypt::decrypt($object->opposing_account_name)
: $object->opposing_account_name;
foreach ($destinations as $destination) {
$entry->destinationAccounts->push(new EntryAccount($destination));
// category and budget
$entry->category_id = $object->category_id ?? '';
$entry->category_name = $object->category_name ?? '';
$entry->budget_id = $object->budget_id ?? '';
$entry->budget_name = $object->budget_name ?? '';
// update description when transaction description is different:
if (!is_null($object->description) && $object->description != $entry->description) {
$entry->description = $entry->description . ' (' . $object->description . ')';
}
return $entry;
}
/**
* @return array
*/
public static function getFieldsAndTypes(): array
{
// key = field name (see top of class)
// value = field type (see csv.php under 'roles')
return [
'description' => 'description',
'amount' => 'amount',
'date' => 'date-transaction',
'source_account_id' => 'account-id',
'source_account_name' => 'account-name',
'source_account_iban' => 'account-iban',
'source_account_type' => '_ignore',
'source_account_number' => 'account-number',
'destination_account_id' => 'opposing-id',
'destination_account_name' => 'opposing-name',
'destination_account_iban' => 'opposing-iban',
'destination_account_type' => '_ignore',
'destination_account_number' => 'account-number',
'budget_id' => 'budget-id',
'budget_name' => 'budget-name',
'category_id' => 'category-id',
'category_name' => 'category-name',
'bill_id' => 'bill-id',
'bill_name' => 'bill-name',
];
}
}

View File

@@ -1,49 +0,0 @@
<?php
/**
* EntryAccount.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\Entry;
use FireflyIII\Models\Account;
/**
* Class EntryAccount
*
* @package FireflyIII\Export\Entry
*/
class EntryAccount
{
/** @var int */
public $accountId;
/** @var string */
public $iban;
/** @var string */
public $name;
/** @var string */
public $number;
/** @var string */
public $type;
/**
* EntryAccount constructor.
*
* @param Account $account
*/
public function __construct(Account $account)
{
$this->accountId = $account->id;
$this->name = $account->name;
$this->iban = $account->iban;
$this->type = $account->accountType->type;
$this->number = $account->getMeta('accountNumber');
}
}

View File

@@ -1,43 +0,0 @@
<?php
/**
* EntryBill.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\Entry;
use FireflyIII\Models\Bill;
/**
* Class EntryBill
*
* @package FireflyIII\Export\Entry
*/
class EntryBill
{
/** @var int */
public $billId = '';
/** @var string */
public $name = '';
/**
* EntryBill constructor.
*
* @param Bill $bill
*/
public function __construct(Bill $bill = null)
{
if (!is_null($bill)) {
$this->billId = $bill->id;
$this->name = $bill->name;
}
}
}

View File

@@ -1,43 +0,0 @@
<?php
/**
* EntryBudget.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\Entry;
use FireflyIII\Models\Budget;
/**
* Class EntryBudget
*
* @package FireflyIII\Export\Entry
*/
class EntryBudget
{
/** @var int */
public $budgetId = '';
/** @var string */
public $name = '';
/**
* EntryBudget constructor.
*
* @param Budget $budget
*/
public function __construct(Budget $budget = null)
{
if (!is_null($budget)) {
$this->budgetId = $budget->id;
$this->name = $budget->name;
}
}
}

View File

@@ -1,42 +0,0 @@
<?php
/**
* EntryCategory.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\Entry;
use FireflyIII\Models\Category;
/**
* Class EntryCategory
*
* @package FireflyIII\Export\Entry
*/
class EntryCategory
{
/** @var int */
public $categoryId = '';
/** @var string */
public $name = '';
/**
* EntryCategory constructor.
*
* @param Category $category
*/
public function __construct(Category $category = null)
{
if (!is_null($category)) {
$this->categoryId = $category->id;
$this->name = $category->name;
}
}
}

View File

@@ -14,9 +14,7 @@ declare(strict_types = 1);
namespace FireflyIII\Export\Exporter;
use FireflyIII\Export\Entry\Entry;
use FireflyIII\Export\Entry\EntryAccount;
use FireflyIII\Models\ExportJob;
use Illuminate\Support\Collection;
use League\Csv\Writer;
use SplFileObject;
@@ -62,110 +60,24 @@ class CsvExporter extends BasicExporter implements ExporterInterface
$writer = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w');
$rows = [];
// Count the maximum number of sources and destinations each entry has. May need to expand the number of export fields:
$maxSourceAccounts = 1;
$maxDestAccounts = 1;
/** @var Entry $entry */
foreach ($this->getEntries() as $entry) {
$sources = $entry->sourceAccounts->count();
$destinations = $entry->destinationAccounts->count();
$maxSourceAccounts = max($maxSourceAccounts, $sources);
$maxDestAccounts = max($maxDestAccounts, $destinations);
}
$rows[] = array_keys($this->getFieldsAndTypes($maxSourceAccounts, $maxDestAccounts));
// get field names for header row:
$first = $this->getEntries()->first();
$headers = array_keys(get_object_vars($first));
$rows[] = $headers;
/** @var Entry $entry */
foreach ($this->getEntries() as $entry) {
// order is defined in Entry::getFieldsAndTypes.
$current = [$entry->description, $entry->amount, $entry->date];
$sourceData = $this->getAccountData($maxSourceAccounts, $entry->sourceAccounts);
$current = array_merge($current, $sourceData);
$destData = $this->getAccountData($maxDestAccounts, $entry->destinationAccounts);
$current = array_merge($current, $destData);
$rest = [$entry->budget->budgetId, $entry->budget->name, $entry->category->categoryId, $entry->category->name, $entry->bill->billId,
$entry->bill->name];
$current = array_merge($current, $rest);
$rows[] = $current;
$line = [];
foreach ($headers as $header) {
$line[] = $entry->$header;
}
$rows[] = $line;
}
$writer->insertAll($rows);
return true;
}
/**
* @param int $max
* @param Collection $accounts
*
* @return array
*/
private function getAccountData(int $max, Collection $accounts): array
{
$current = [];
for ($i = 0; $i < $max; $i++) {
/** @var EntryAccount $source */
$source = $accounts->get($i);
$currentId = '';
$currentName = '';
$currentIban = '';
$currentType = '';
$currentNumber = '';
if ($source) {
$currentId = $source->accountId;
$currentName = $source->name;
$currentIban = $source->iban;
$currentType = $source->type;
$currentNumber = $source->number;
}
$current[] = $currentId;
$current[] = $currentName;
$current[] = $currentIban;
$current[] = $currentType;
$current[] = $currentNumber;
}
unset($source);
return $current;
}
/**
* @param int $sources
* @param int $destinations
*
* @return array
*/
private function getFieldsAndTypes(int $sources, int $destinations): array
{
// key = field name (see top of class)
// value = field type (see csv.php under 'roles')
$array = [
'description' => 'description',
'amount' => 'amount',
'date' => 'date-transaction',
];
for ($i = 0; $i < $sources; $i++) {
$array['source_account_' . $i . '_id'] = 'account-id';
$array['source_account_' . $i . '_name'] = 'account-name';
$array['source_account_' . $i . '_iban'] = 'account-iban';
$array['source_account_' . $i . '_type'] = '_ignore';
$array['source_account_' . $i . '_number'] = 'account-number';
}
for ($i = 0; $i < $destinations; $i++) {
$array['destination_account_' . $i . '_id'] = 'account-id';
$array['destination_account_' . $i . '_name'] = 'account-name';
$array['destination_account_' . $i . '_iban'] = 'account-iban';
$array['destination_account_' . $i . '_type'] = '_ignore';
$array['destination_account_' . $i . '_number'] = 'account-number';
}
$array['budget_id'] = 'budget-id';
$array['budget_name'] = 'budget-name';
$array['category_id'] = 'category-id';
$array['category_name'] = 'category-name';
$array['bill_id'] = 'bill-id';
$array['bill_name'] = 'bill-name';
return $array;
}
private function tempFile()
{

View File

@@ -15,13 +15,13 @@ namespace FireflyIII\Export;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Export\Collector\AttachmentCollector;
use FireflyIII\Export\Collector\JournalExportCollector;
use FireflyIII\Export\Collector\UploadCollector;
use FireflyIII\Export\Entry\Entry;
use FireflyIII\Models\ExportJob;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Collection;
use Log;
use Storage;
use ZipArchive;
@@ -40,15 +40,11 @@ class Processor
/** @var bool */
public $includeAttachments;
/** @var bool */
public $includeConfig;
/** @var bool */
public $includeOldUploads;
/** @var ExportJob */
public $job;
/** @var array */
public $settings;
/** @var \FireflyIII\Export\ConfigurationFile */
private $configurationMaker;
/** @var Collection */
private $exportEntries;
/** @var Collection */
@@ -68,7 +64,6 @@ class Processor
$this->accounts = $settings['accounts'];
$this->exportFormat = $settings['exportFormat'];
$this->includeAttachments = $settings['includeAttachments'];
$this->includeConfig = $settings['includeConfig'];
$this->includeOldUploads = $settings['includeOldUploads'];
$this->job = $settings['job'];
$this->journals = new Collection;
@@ -84,8 +79,9 @@ class Processor
{
/** @var AttachmentCollector $attachmentCollector */
$attachmentCollector = app(AttachmentCollector::class, [$this->job]);
$attachmentCollector->setDates($this->settings['startDate'], $this->settings['endDate']);
$attachmentCollector->run();
$this->files = $this->files->merge($attachmentCollector->getFiles());
$this->files = $this->files->merge($attachmentCollector->getEntries());
return true;
}
@@ -95,9 +91,13 @@ class Processor
*/
public function collectJournals(): bool
{
/** @var JournalTaskerInterface $tasker */
$tasker = app(JournalTaskerInterface::class);
$this->journals = $tasker->getJournalsInRange($this->accounts, $this->settings['startDate'], $this->settings['endDate']);
/** @var JournalExportCollector $collector */
$collector = app(JournalExportCollector::class, [$this->job]);
$collector->setDates($this->settings['startDate'], $this->settings['endDate']);
$collector->setAccounts($this->settings['accounts']);
$collector->run();
$this->journals = $collector->getEntries();
Log::debug(sprintf('Count %d journals in collectJournals() ', $this->journals->count()));
return true;
}
@@ -111,7 +111,7 @@ class Processor
$uploadCollector = app(UploadCollector::class, [$this->job]);
$uploadCollector->run();
$this->files = $this->files->merge($uploadCollector->getFiles());
$this->files = $this->files->merge($uploadCollector->getEntries());
return true;
}
@@ -122,22 +122,11 @@ class Processor
public function convertJournals(): bool
{
$count = 0;
/** @var TransactionJournal $journal */
foreach ($this->journals as $journal) {
$this->exportEntries->push(Entry::fromJournal($journal));
foreach ($this->journals as $object) {
$this->exportEntries->push(Entry::fromObject($object));
$count++;
}
return true;
}
/**
* @return bool
*/
public function createConfigFile(): bool
{
$this->configurationMaker = app(ConfigurationFile::class, [$this->job]);
$this->files->push($this->configurationMaker->make());
Log::debug(sprintf('Count %d entries in exportEntries (convertJournals)', $this->exportEntries->count()));
return true;
}

View File

@@ -14,7 +14,7 @@ declare(strict_types = 1);
namespace FireflyIII\Generator\Chart\Bill;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Transaction;
use Illuminate\Support\Collection;
/**
@@ -61,13 +61,13 @@ class ChartJsBillChartGenerator implements BillChartGeneratorInterface
$minAmount = [];
$maxAmount = [];
$actualAmount = [];
/** @var TransactionJournal $entry */
/** @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[] = round(TransactionJournal::amountPositive($entry), 2);
$actualAmount[] = bcmul($entry->transaction_amount, '-1');
}
$data['datasets'][] = [

View File

@@ -38,13 +38,6 @@ interface BudgetChartGeneratorInterface
*/
public function frontpage(Collection $entries): array;
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries): array;
/**
* @param Collection $entries
* @param string $viewRange

View File

@@ -100,40 +100,6 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries): array
{
// dataset:
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
// get labels from one of the budgets (assuming there's at least one):
$first = $entries->first();
$keys = array_keys($first['budgeted']);
foreach ($keys as $year) {
$data['labels'][] = strval($year);
}
// then, loop all entries and create datasets:
foreach ($entries as $entry) {
$name = $entry['name'];
$spent = $entry['spent'];
$budgeted = $entry['budgeted'];
$data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)];
$data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)];
}
$data['count'] = count($data['datasets']);
return $data;
}
/**
* @param Collection $entries
* @param string $viewRange

View File

@@ -45,13 +45,6 @@ interface CategoryChartGeneratorInterface
*/
public function frontpage(Collection $entries): array;
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries): array;
/**
* @param Collection $entries
*

View File

@@ -117,34 +117,6 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries): array
{
// get labels from one of the categories (assuming there's at least one):
$first = $entries->first();
$data = ['count' => 0, 'labels' => array_keys($first['spent']), 'datasets' => [],];
// then, loop all entries and create datasets:
foreach ($entries as $entry) {
$name = $entry['name'];
$spent = $entry['spent'];
$earned = $entry['earned'];
if (array_sum(array_values($spent)) != 0) {
$data['datasets'][] = ['label' => 'Spent in category ' . $name, 'data' => array_values($spent)];
}
if (array_sum(array_values($earned)) != 0) {
$data['datasets'][] = ['label' => 'Earned in category ' . $name, 'data' => array_values($earned)];
}
}
$data['count'] = count($data['datasets']);
return $data;
}
/**
*
* @param Collection $entries

View File

@@ -76,7 +76,7 @@ class BudgetEventHandler
*
* @return bool
*/
public function update(UpdatedBudgetLimit $event): bool
public function updateRepetition(UpdatedBudgetLimit $event): bool
{
$budgetLimit = $event->budgetLimit;
$end = $event->end;
@@ -107,4 +107,4 @@ class BudgetEventHandler
return true;
}
}
}

View File

@@ -21,6 +21,7 @@ use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Rules\Processor;
use FireflyIII\Support\Events\BillScanner;
use Log;
/**
* Class StoredJournalEventHandler
@@ -42,30 +43,69 @@ class StoredJournalEventHandler
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId));
/** @var PiggyBank $piggyBank */
$piggyBank = $journal->user()->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank)) {
Log::error('No such piggy bank!');
return true;
}
Log::debug(sprintf('Found piggy bank #%d: "%s"', $piggyBank->id, $piggyBank->name));
// update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d')));
return true;
}
$amount = TransactionJournal::amountPositive($journal);
Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
// if piggy account matches source account, the amount is positive
$sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray();
if (in_array($piggyBank->account_id, $sources)) {
$amount = bcmul($amount, '-1');
Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id));
}
// if the amount is positive:
// make sure it fits in piggy bank:
if (bccomp($amount, '0') === 1) {
// amount is positive
$room = bcsub(strval($piggyBank->targetamount), strval($repetition->currentamount));
Log::debug(sprintf('Room in piggy bank for extra money is %f', $room));
if (bccomp($room, $amount) === -1) {
// $room is smaller than $amount
Log::debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
Log::debug(sprintf('New amount is %f', $room));
$amount = $room;
}
}
if (bccomp($amount, '0') === -1) {
// amount is negative
Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount));
$compare = bcmul($repetition->currentamount, '-1');
if (bccomp($compare, $amount) === 1) {
// $currentamount is smaller than $amount
Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
Log::debug(sprintf('New amount is %f', $compare));
$amount = $compare;
}
}
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
$repetition->save();
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]);
/** @var PiggyBankEvent $event */
$event = 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));
return true;
}
@@ -121,4 +161,4 @@ class StoredJournalEventHandler
return true;
}
}
}

View File

@@ -15,11 +15,8 @@ namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\UpdatedTransactionJournal;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Rules\Processor;
use FireflyIII\Support\Events\BillScanner;
@@ -30,50 +27,6 @@ use FireflyIII\Support\Events\BillScanner;
*/
class UpdatedJournalEventHandler
{
/**
* This method will try to reconnect a journal to a piggy bank, updating the piggy bank repetition.
*
* @param UpdatedTransactionJournal $event
*
* @return bool
*/
public function connectToPiggyBank(UpdatedTransactionJournal $event): bool
{
$journal = $event->journal;
if (!$journal->isTransfer()) {
return true;
}
// get the event connected to this journal:
/** @var PiggyBankEvent $event */
$event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first();
if (is_null($event)) {
return false;
}
$piggyBank = $event->piggyBank()->first();
$repetition = null;
if (!is_null($piggyBank)) {
/** @var PiggyBankRepetition $repetition */
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
}
if (is_null($repetition)) {
return false;
}
$amount = TransactionJournal::amount($journal);
$diff = bcsub($amount, $event->amount); // update current repetition
$repetition->currentamount = bcadd($repetition->currentamount, $diff);
$repetition->save();
$event->amount = $amount;
$event->save();
return true;
}
/**
* This method will check all the rules when a journal is updated.
@@ -125,4 +78,4 @@ class UpdatedJournalEventHandler
return true;
}
}
}

View File

@@ -63,7 +63,7 @@ class UserEventHandler
*
* @return bool
*/
public function onUserLogout(): bool
public function logoutUser(): bool
{
// dump stuff from the session:
Session::forget('twofactor-authenticated');
@@ -222,4 +222,4 @@ class UserEventHandler
}
}
}

View File

@@ -17,10 +17,8 @@ use FireflyIII\Models\Attachment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\MessageBag;
use Input;
use Log;
use Storage;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use TypeError;
/**
* Class AttachmentHelper
@@ -236,13 +234,8 @@ class AttachmentHelper implements AttachmentHelperInterface
private function getFiles()
{
$files = null;
try {
if (Input::hasFile('attachments')) {
$files = Input::file('attachments');
}
} catch (TypeError $e) {
// Log it, do nothing else.
Log::error($e->getMessage());
if (Input::hasFile('attachments')) {
$files = Input::file('attachments');
}
return $files;

View File

@@ -0,0 +1,485 @@
<?php
declare(strict_types = 1);
namespace FireflyIII\Helpers\Collector;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
/**
* Maybe this is a good idea after all...
*
* Class JournalCollector
*
* @package FireflyIII\Helpers\Collector
*/
class JournalCollector
{
/** @var int */
private $count = 0;
/** @var array */
private $fields
= [
'transaction_journals.id as journal_id',
'transaction_journals.description',
'transaction_journals.date',
'transaction_journals.encrypted',
//'transaction_journals.transaction_currency_id',
'transaction_currencies.code as transaction_currency_code',
//'transaction_currencies.symbol as transaction_currency_symbol',
'transaction_types.type as transaction_type_type',
'transaction_journals.bill_id',
'bills.name as bill_name',
'transactions.id as id',
'transactions.amount as transaction_amount',
'transactions.description as transaction_description',
'transactions.account_id',
'transactions.identifier',
'transactions.transaction_journal_id',
'accounts.name as account_name',
'accounts.encrypted as account_encrypted',
'account_types.type as account_type',
];
/** @var bool */
private $filterTransfers = false;
/** @var bool */
private $joinedBudget = false;
/** @var bool */
private $joinedCategory = false;
/** @var bool */
private $joinedTag = false;
/** @var int */
private $limit;
/** @var int */
private $offset;
/** @var int */
private $page = 1;
/** @var EloquentBuilder */
private $query;
/** @var bool */
private $run = false;
/** @var User */
private $user;
/**
* JournalCollector constructor.
*
* @param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
$this->query = $this->startQuery();
}
/**
* @return int
* @throws FireflyException
*/
public function count(): int
{
if ($this->run === true) {
throw new FireflyException('Cannot count after run in JournalCollector.');
}
$countQuery = clone $this->query;
// dont need some fields:
$countQuery->getQuery()->limit = null;
$countQuery->getQuery()->offset = null;
$countQuery->getQuery()->unionLimit = null;
$countQuery->getQuery()->groups = null;
$countQuery->getQuery()->orders = null;
$countQuery->groupBy('accounts.user_id');
$this->count = $countQuery->count();
return $this->count;
}
/**
* @return Collection
*/
public function getJournals(): Collection
{
$this->run = true;
$set = $this->query->get(array_values($this->fields));
$set = $this->filterTransfers($set);
// 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) : '';
}
);
return $set;
}
/**
* @return LengthAwarePaginator
* @throws FireflyException
*/
public function getPaginatedJournals():LengthAwarePaginator
{
if ($this->run === true) {
throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.');
}
$this->count();
$set = $this->getJournals();
$journals = new LengthAwarePaginator($set, $this->count, $this->limit, $this->page);
return $journals;
}
/**
* @param Collection $accounts
*
* @return JournalCollector
*/
public function setAccounts(Collection $accounts): JournalCollector
{
if ($accounts->count() > 0) {
$accountIds = $accounts->pluck('id')->toArray();
$this->query->whereIn('transactions.account_id', $accountIds);
}
if ($accounts->count() > 1) {
$this->filterTransfers = true;
}
return $this;
}
/**
* @return JournalCollector
*/
public function setAllAssetAccounts(): JournalCollector
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class, [$this->user]);
$accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
if ($accounts->count() > 0) {
$accountIds = $accounts->pluck('id')->toArray();
$this->query->whereIn('transactions.account_id', $accountIds);
}
if ($accounts->count() > 1) {
$this->filterTransfers = true;
}
return $this;
}
/**
* @param Collection $bills
*
* @return JournalCollector
*/
public function setBills(Collection $bills): JournalCollector
{
if ($bills->count() > 0) {
$billIds = $bills->pluck('id')->toArray();
$this->query->whereIn('transaction_journals.bill_id', $billIds);
}
return $this;
}
/**
* @param Budget $budget
*
* @return JournalCollector
*/
public function setBudget(Budget $budget): JournalCollector
{
$this->joinBudgetTables();
$this->query->where(
function (EloquentBuilder $q) use ($budget) {
$q->where('budget_transaction.budget_id', $budget->id);
$q->orWhere('budget_transaction_journal.budget_id', $budget->id);
}
);
return $this;
}
/**
* @param Category $category
*
* @return JournalCollector
*/
public function setCategory(Category $category): JournalCollector
{
$this->joinCategoryTables();
$this->query->where(
function (EloquentBuilder $q) use ($category) {
$q->where('category_transaction.category_id', $category->id);
$q->orWhere('category_transaction_journal.category_id', $category->id);
}
);
return $this;
}
/**
* @param int $limit
*
* @return JournalCollector
*/
public function setLimit(int $limit): JournalCollector
{
$this->limit = $limit;
$this->query->limit($limit);
Log::debug(sprintf('Set limit to %d', $limit));
return $this;
}
/**
* @param int $offset
*
* @return JournalCollector
*/
public function setOffset(int $offset): JournalCollector
{
$this->offset = $offset;
return $this;
}
/**
* @param int $page
*
* @return JournalCollector
*/
public function setPage(int $page): JournalCollector
{
$this->page = $page;
if ($page > 0) {
$page--;
}
Log::debug(sprintf('Page is %d', $page));
if (!is_null($this->limit)) {
$offset = ($this->limit * $page);
$this->offset = $offset;
$this->query->skip($offset);
Log::debug(sprintf('Changed offset to %d', $offset));
}
if (is_null($this->limit)) {
Log::debug('The limit is zero, cannot set the page.');
}
return $this;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return JournalCollector
*/
public function setRange(Carbon $start, Carbon $end): JournalCollector
{
if ($start <= $end) {
$this->query->where('transaction_journals.date', '>=', $start->format('Y-m-d'));
$this->query->where('transaction_journals.date', '<=', $end->format('Y-m-d'));
}
return $this;
}
/**
* @param Tag $tag
*
* @return JournalCollector
*/
public function setTag(Tag $tag): JournalCollector
{
$this->joinTagTables();
$this->query->where('tag_transaction_journal.tag_id', $tag->id);
return $this;
}
/**
* @param array $types
*
* @return JournalCollector
*/
public function setTypes(array $types): JournalCollector
{
if (count($types) > 0) {
$this->query->whereIn('transaction_types.type', $types);
}
return $this;
}
/**
* @return JournalCollector
*/
public function withoutBudget(): JournalCollector
{
$this->joinBudgetTables();
$this->query->where(
function (EloquentBuilder $q) {
$q->whereNull('budget_transaction.budget_id');
$q->whereNull('budget_transaction_journal.budget_id');
}
);
return $this;
}
/**
* @return JournalCollector
*/
public function withoutCategory(): JournalCollector
{
$this->joinCategoryTables();
$this->query->where(
function (EloquentBuilder $q) {
$q->whereNull('category_transaction.category_id');
$q->whereNull('category_transaction_journal.category_id');
}
);
return $this;
}
/**
* 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
* on both the source, and then again on the destination account.
*
* This method filters them out.
*
* @param Collection $set
*
* @return Collection
*/
private function filterTransfers(Collection $set): Collection
{
if ($this->filterTransfers) {
$set = $set->filter(
function (Transaction $transaction) {
if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) {
Log::debug(
sprintf(
'Included journal #%d (transaction #%d) because its a %s with amount %f',
$transaction->transaction_journal_id,
$transaction->id,
$transaction->transaction_type_type,
$transaction->transaction_amount
)
);
return $transaction;
}
Log::debug(
sprintf(
'Removed journal #%d (transaction #%d) because its a %s with amount %f',
$transaction->transaction_journal_id,
$transaction->id,
$transaction->transaction_type_type,
$transaction->transaction_amount
)
);
return false;
}
);
}
return $set;
}
/**
*
*/
private function joinBudgetTables()
{
if (!$this->joinedBudget) {
// join some extra tables:
$this->joinedBudget = true;
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
$this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
}
}
/**
*
*/
private function joinCategoryTables()
{
if (!$this->joinedCategory) {
// join some extra tables:
$this->joinedCategory = true;
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
$this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id');
}
}
/**
*
*/
private function joinTagTables()
{
if (!$this->joinedTag) {
// join some extra tables:
$this->joinedTag = true;
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
}
}
/**
* @return EloquentBuilder
*/
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');
return $query;
}
}

View File

@@ -14,7 +14,9 @@ namespace FireflyIII\Helpers\Help;
use Cache;
use League\CommonMark\CommonMarkConverter;
use Log;
use Requests;
use Requests_Exception;
use Route;
/**
@@ -24,49 +26,57 @@ use Route;
*/
class Help implements HelpInterface
{
/** @var string */
protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36';
/**
*
* @param string $key
* @param string $route
* @param string $language
*
* @return string
*/
public function getFromCache(string $key): string
public function getFromCache(string $route, string $language): string
{
return Cache::get($key);
$line = sprintf('help.%s.%s', $route, $language);
return Cache::get($line);
}
/**
* @param string $language
* @param string $route
*
* @return array
* @return string
*/
public function getFromGithub(string $language, string $route): array
public function getFromGithub(string $language, string $route): string
{
$uri = sprintf('https://raw.githubusercontent.com/JC5/firefly-iii-help/master/%s/%s.md', $language, $route);
$routeIndex = str_replace('.', '-', $route);
$title = trans('help.' . $routeIndex);
$content = [
'text' => '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>',
'title' => $title,
];
$uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route);
Log::debug(sprintf('Trying to get %s...', $uri));
$opt = ['useragent' => $this->userAgent];
$content = '';
try {
$result = Requests::get($uri, [], $opt);
} catch (Requests_Exception $e) {
Log::error($e);
return '';
}
$result = Requests::get($uri);
Log::debug(sprintf('Status code is %d', $result->status_code));
if ($result->status_code === 200) {
$content['text'] = $result->body;
$content = trim($result->body);
}
if (strlen(trim($content['text'])) == 0) {
$content['text'] = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
if (strlen($content) > 0) {
Log::debug('Content is longer than zero. Expect something.');
$converter = new CommonMarkConverter();
$content = $converter->convertToHtml($content);
}
if (strlen($content) === 0) {
Log::warning('Raw content length is zero.');
}
$converter = new CommonMarkConverter();
$content['text'] = $converter->convertToHtml($content['text']);
return $content;
@@ -84,27 +94,42 @@ class Help implements HelpInterface
}
/**
*
* @param string $route
* @param string $language
*
* @return bool
*/
public function inCache(string $route):bool
public function inCache(string $route, string $language):bool
{
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
$line = sprintf('help.%s.%s', $route, $language);
$result = Cache::has($line);
if ($result) {
Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language));
}
if (!$result) {
Log::debug(sprintf('Cache does not have this entry: %s', 'help.' . $route . '.' . $language));
}
return $result;
}
/**
*
* @param string $route
* @param string $language
* @param array $content
* @param string $content
*
* @internal param $title
*/
public function putInCache(string $route, string $language, array $content)
public function putInCache(string $route, string $language, string $content)
{
Cache::put('help.' . $route . '.text.' . $language, $content['text'], 10080); // a week.
Cache::put('help.' . $route . '.title.' . $language, $content['title'], 10080);
$key = sprintf('help.%s.%s', $route, $language);
if (strlen($content) > 0) {
Log::debug(sprintf('Will store entry in cache: %s', $key));
Cache::put($key, $content, 10080); // a week.
return;
}
Log::info(sprintf('Will not cache %s because content is empty.', $key));
}
}

View File

@@ -21,19 +21,20 @@ interface HelpInterface
{
/**
* @param string $key
* @param string $route
* @param string $language
*
* @return string
*/
public function getFromCache(string $key): string;
public function getFromCache(string $route, string $language): string;
/**
* @param string $language
* @param string $route
*
* @return array
* @return string
*/
public function getFromGithub(string $language, string $route):array;
public function getFromGithub(string $language, string $route):string;
/**
* @param string $route
@@ -44,15 +45,16 @@ interface HelpInterface
/**
* @param string $route
* @param string $language
*
* @return bool
*/
public function inCache(string $route): bool;
public function inCache(string $route, string $language): bool;
/**
* @param string $route
* @param string $language
* @param array $content
* @param string $content
*/
public function putInCache(string $route, string $language, array $content);
public function putInCache(string $route, string $language, string $content);
}

View File

@@ -15,13 +15,17 @@ namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use DB;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\BudgetLine;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use stdClass;
/**
* Class BudgetReportHelper
@@ -97,6 +101,66 @@ class BudgetReportHelper implements BudgetReportHelperInterface
return $return;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array
{
$accountIds = $accounts->pluck('id')->toArray();
$query = TransactionJournal
::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->where('transaction_types.type', 'Withdrawal')
->where('transaction_journals.user_id', auth()->user()->id);
if (count($accountIds) > 0) {
$query->whereIn('transactions.account_id', $accountIds);
}
$query->groupBy(['budget_transaction_journal.budget_id', 'the_year']);
$queryResult = $query->get(
[
'budget_transaction_journal.budget_id',
DB::raw('DATE_FORMAT(transaction_journals.date,"%Y") AS the_year'),
DB::raw('SUM(transactions.amount) as sum_of_period'),
]
);
$data = [];
$budgets = $this->repository->getBudgets();
$years = $this->listOfYears($start, $end);
// do budget "zero"
$emptyBudget = new Budget;
$emptyBudget->id = 0;
$emptyBudget->name = strval(trans('firefly.no_budget'));
$budgets->push($emptyBudget);
// get all budgets and years.
foreach ($budgets as $budget) {
$data[$budget->id] = [
'name' => $budget->name,
'entries' => $this->filterAmounts($queryResult, $budget->id, $years),
'sum' => '0',
];
}
// filter out empty ones and fill sum:
$data = $this->getBudgetMultiYearMeta($data);
return $data;
}
/**
* @param Carbon $start
* @param Carbon $end
@@ -183,30 +247,21 @@ class BudgetReportHelper implements BudgetReportHelperInterface
}
/**
* Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay
* and sum up everything in the array in the given range.
*
* @param Carbon $start
* @param Carbon $end
* @param array $array
*
* @return string
* @return array
*/
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
public function listOfYears(Carbon $start, Carbon $end): array
{
$sum = '0';
$currentStart = clone $start; // to not mess with the original one
$currentEnd = clone $end; // to not mess with the original one
while ($currentStart <= $currentEnd) {
$date = $currentStart->format('Y-m-d');
if (isset($array[$date])) {
$sum = bcadd($sum, $array[$date]);
}
$currentStart->addDay();
$begin = clone $start;
$years = [];
while ($begin < $end) {
$years[] = $begin->year;
$begin->addYear();
}
return $sum;
return $years;
}
/**
@@ -247,6 +302,59 @@ class BudgetReportHelper implements BudgetReportHelperInterface
return $headers;
}
/**
* @param Collection $set
* @param int $budgetId
* @param array $years
*
* @return array
*/
private function filterAmounts(Collection $set, int $budgetId, array $years):array
{
$arr = [];
foreach ($years as $year) {
/** @var stdClass $object */
$result = $set->filter(
function (TransactionJournal $object) use ($budgetId, $year) {
return intval($object->the_year) === $year && $budgetId === intval($object->budget_id);
}
);
$amount = '0';
if (!is_null($result->first())) {
$amount = $result->first()->sum_of_period;
}
$arr[$year] = $amount;
}
return $arr;
}
/**
* @param array $data
*
* @return array
*/
private function getBudgetMultiYearMeta(array $data): array
{
/**
* @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;
}
/**
* @param Carbon $current
* @param Carbon $end

View File

@@ -34,6 +34,15 @@ interface BudgetReportHelperInterface
*/
public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection;
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array;
/**
* @param Carbon $start
* @param Carbon $end
@@ -52,4 +61,12 @@ interface BudgetReportHelperInterface
*/
public function getBudgetsWithExpenses(Carbon $start, Carbon $end, Collection $accounts): Collection;
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function listOfYears(Carbon $start, Carbon $end): array;
}

View File

@@ -19,11 +19,12 @@ use FireflyIII\Helpers\Collection\BillLine;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\FiscalHelperInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
@@ -77,7 +78,9 @@ class ReportHelper implements ReportHelperInterface
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$bills = $repository->getBillsForAccounts($accounts);
$journals = $repository->getAllJournalsInRange($bills, $start, $end);
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setBills($bills);
$journals = $collector->getJournals();
$collection = new BillCollection;
/** @var Bill $bill */
@@ -91,18 +94,19 @@ class ReportHelper implements ReportHelperInterface
// is hit in period?
$entry = $journals->filter(
function (TransactionJournal $journal) use ($bill) {
return $journal->bill_id === $bill->id;
function (Transaction $transaction) use ($bill) {
return $transaction->bill_id === $bill->id;
}
);
$first = $entry->first();
if (!is_null($first)) {
$billLine->setTransactionJournalId($first->id);
$billLine->setAmount($first->journalAmount);
$billLine->setAmount($first->transaction_amount);
$billLine->setHit(true);
}
if ($billLine->isActive()) {
// non active AND non hit? do not add:
if ($billLine->isActive() || $billLine->isHit()) {
$collection->addBill($billLine);
}
}
@@ -304,30 +308,4 @@ class ReportHelper implements ReportHelperInterface
return $collection;
}
/**
* Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay
* and sum up everything in the array in the given range.
*
* @param Carbon $start
* @param Carbon $end
* @param array $array
*
* @return string
*/
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
{
$sum = '0';
$currentStart = clone $start; // to not mess with the original one
$currentEnd = clone $end; // to not mess with the original one
while ($currentStart <= $currentEnd) {
$date = $currentStart->format('Y-m-d');
if (isset($array[$date])) {
$sum = bcadd($sum, $array[$date]);
}
$currentStart->addDay();
}
return $sum;
}
}

View File

@@ -15,13 +15,15 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input;
use Navigation;
@@ -44,8 +46,16 @@ class AccountController extends Controller
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', trans('firefly.accounts'));
// translations:
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', trans('firefly.accounts'));
return $next($request);
}
);
}
/**
@@ -146,7 +156,7 @@ class AccountController extends Controller
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalanceDate,
'openingBalance' => $openingBalanceAmount,
'virtualBalance' => round($account->virtual_balance, 2),
'virtualBalance' => $account->virtual_balance,
];
Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'accounts');
@@ -200,6 +210,9 @@ class AccountController extends Controller
*/
public function show(AccountTaskerInterface $tasker, ARI $repository, Account $account)
{
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
return $this->redirectToOriginalAccount($account);
}
// show journals from current period only:
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$subTitle = $account->name;
@@ -208,13 +221,13 @@ class AccountController extends Controller
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
/** @var Carbon $end */
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
$page = intval(Input::get('page'));
$pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize;
$set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
$count = $set->count();
$subSet = $set->splice($offset, $pageSize);
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// replace with journal collector:
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id);
// grouped other months thing:
@@ -262,27 +275,25 @@ class AccountController extends Controller
}
/**
* @param AccountTaskerInterface $tasker
* @param Account $account
* @param string $date
* @param Account $account
* @param string $date
*
* @return View
*/
public function showWithDate(AccountTaskerInterface $tasker, Account $account, string $date)
public function showWithDate(Account $account, string $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')';
$page = intval(Input::get('page'));
$page = $page === 0 ? 1 : $page;
$pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize;
$set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
$count = $set->count();
$subSet = $set->splice($offset, $pageSize);
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// replace with journal collector:
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id . '/' . $date);
return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon'));
@@ -297,23 +308,8 @@ class AccountController extends Controller
*/
public function store(AccountFormRequest $request, ARI $repository)
{
$accountData = [
'name' => trim($request->input('name')),
'accountType' => $request->input('what'),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'virtualBalanceCurrency' => intval($request->input('amount_currency_id_virtualBalance')),
'active' => true,
'user' => auth()->user()->id,
'iban' => trim($request->input('iban')),
'accountNumber' => trim($request->input('accountNumber')),
'accountRole' => $request->input('accountRole'),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
];
$account = $repository->store($accountData);
$data = $request->getAccountData();
$account = $repository->store($data);
Session::flash('success', strval(trans('firefly.stored_new_account', ['name' => $account->name])));
Preferences::mark();
@@ -345,22 +341,8 @@ class AccountController extends Controller
*/
public function update(AccountFormRequest $request, ARI $repository, Account $account)
{
$accountData = [
'name' => $request->input('name'),
'active' => $request->input('active'),
'user' => auth()->user()->id,
'iban' => $request->input('iban'),
'accountNumber' => $request->input('accountNumber'),
'accountRole' => $request->input('accountRole'),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
'ccType' => $request->input('ccType'),
'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'),
];
$repository->update($account, $accountData);
$data = $request->getAccountData();
$repository->update($account, $data);
Session::flash('success', strval(trans('firefly.updated_account', ['name' => $account->name])));
Preferences::mark();
@@ -392,4 +374,29 @@ class AccountController extends Controller
return '';
}
/**
* @param Account $account
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws FireflyException
*/
private function redirectToOriginalAccount(Account $account)
{
/** @var Transaction $transaction */
$transaction = $account->transactions()->first();
if (is_null($transaction)) {
throw new FireflyException('Expected a transaction. This account has none. BEEP, error.');
}
$journal = $transaction->transactionJournal;
/** @var Transaction $opposingTransaction */
$opposingTransaction = $journal->transactions()->where('transactions.id', '!=', $transaction->id)->first();
if (is_null($opposingTransaction)) {
throw new FireflyException('Expected an opposing transaction. This account has none. BEEP, error.');
}
return redirect(route('accounts.show', [$opposingTransaction->account_id]));
}
}

View File

@@ -37,8 +37,15 @@ class ConfigurationController extends Controller
{
parent::__construct();
View::share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o');
$this->middleware(
function ($request, $next) {
View::share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o');
return $next($request);
}
);
}
@@ -66,10 +73,10 @@ class ConfigurationController extends Controller
public function store(ConfigurationRequest $request)
{
// get config values:
$singleUserMode = intval($request->get('single_user_mode')) === 1 ? true : false;
$data = $request->getConfigurationData();
// store config values
FireflyConfig::set('single_user_mode', $singleUserMode);
FireflyConfig::set('single_user_mode', $data['single_user_mode']);
// flash message
Session::flash('success', strval(trans('firefly.configuration_updated')));

View File

@@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\User;
use Illuminate\Http\Request;
@@ -83,6 +84,7 @@ class DomainController extends Controller
*/
public function toggleDomain(string $domain)
{
$domain = strtolower($domain);
$blocked = FireflyConfig::get('blocked-domains', [])->data;
if (in_array($domain, $blocked)) {
@@ -111,15 +113,16 @@ class DomainController extends Controller
*/
private function getKnownDomains(): array
{
$users = User::get();
$set = [];
$filtered = [];
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
$users = $repository->all();
$set = [];
$filtered = [];
/** @var User $user */
foreach ($users as $user) {
$email = $user->email;
$parts = explode('@', $email);
$domain = $parts[1];
$set[] = $domain;
$email = $user->email;
$parts = explode('@', $email);
$set[] = strtolower($parts[1]);
}
$set = array_unique($set);
// filter for already banned domains:
@@ -131,7 +134,6 @@ class DomainController extends Controller
$filtered[] = $domain;
}
}
asort($filtered);
return $filtered;
}

View File

@@ -42,8 +42,16 @@ class AttachmentController extends Controller
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-paperclip');
View::share('title', trans('firefly.attachments'));
// translations:
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-paperclip');
View::share('title', trans('firefly.attachments'));
return $next($request);
}
);
}
/**
@@ -164,14 +172,8 @@ class AttachmentController extends Controller
*/
public function update(AttachmentFormRequest $request, AttachmentRepositoryInterface $repository, Attachment $attachment)
{
$attachmentData = [
'title' => $request->input('title'),
'description' => $request->input('description'),
'notes' => $request->input('notes'),
];
$repository->update($attachment, $attachmentData);
$data = $request->getAttachmentData();
$repository->update($attachment, $data);
Session::flash('success', strval(trans('firefly.attachment_updated', ['name' => $attachment->filename])));
Preferences::mark();

View File

@@ -14,10 +14,12 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Support\Collection;
use Input;
use Preferences;
use Session;
@@ -38,8 +40,16 @@ class BillController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.bills'));
View::share('mainTitleIcon', 'fa-calendar-o');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.bills'));
View::share('mainTitleIcon', 'fa-calendar-o');
return $next($request);
}
);
}
/**
@@ -137,23 +147,15 @@ class BillController extends Controller
$bills = $repository->getBills();
$bills->each(
function (Bill $bill) use ($repository, $start, $end) {
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon);
$bill->lastFoundMatch = $repository->lastFoundMatch($bill);
$journals = $repository->getJournalsInRange($bill, $start, $end);
// loop journals, find average:
$average = '0';
$count = $journals->count();
if ($count > 0) {
$sum = '0';
foreach ($journals as $journal) {
$sum = bcadd($sum, TransactionJournal::amountPositive($journal));
}
$average = bcdiv($sum, strval($count));
// paid in this period?
$bill->paidDates = $repository->getPaidDatesInRange($bill, $start, $end);
$bill->payDates = $repository->getPayDatesInRange($bill, $start, $end);
$lastDate = clone $start;
if ($bill->paidDates->count() >= $bill->payDates->count()) {
$lastDate = $end;
}
$bill->lastPaidAmount = $average;
$bill->paidInPeriod = ($start <= $bill->lastFoundMatch) && ($end >= $bill->lastFoundMatch);
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, $lastDate);
}
);
@@ -200,10 +202,15 @@ class BillController extends Controller
$year = $date->year;
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$journals = $repository->getJournals($bill, $page, $pageSize);
$yearAverage = $repository->getYearAverage($bill, $date);
$overallAverage = $repository->getOverallAverage($bill);
// use collector:
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setPage($page)->setLimit($pageSize);
$journals = $collector->getPaginatedJournals();
$journals->setPath('/bills/show/' . $bill->id);
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon);
$hideBill = true;
$subTitle = e($bill->name);

View File

@@ -17,15 +17,16 @@ use Amount;
use Carbon\Carbon;
use Config;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input;
use Log;
use Navigation;
use Preferences;
use Response;
@@ -47,9 +48,17 @@ class BudgetController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.budgets'));
View::share('mainTitleIcon', 'fa-tasks');
View::share('hideBudgets', true);
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.budgets'));
View::share('mainTitleIcon', 'fa-tasks');
return $next($request);
}
);
}
/**
@@ -191,10 +200,12 @@ class BudgetController extends Controller
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
$startAsString = $start->format('Y-m-d');
$endAsString = $end->format('Y-m-d');
Log::debug('Now at /budgets');
// loop the budgets:
/** @var Budget $budget */
foreach ($budgets as $budget) {
Log::debug(sprintf('Now at budget #%d ("%s")', $budget->id, $budget->name));
$budget->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);
$allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$otherRepetitions = new Collection;
@@ -236,31 +247,28 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
*
* @return View
*/
public function noBudget(BudgetRepositoryInterface $repository)
public function noBudget()
{
/** @var Carbon $start */
$start = session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
$end = session('end', Carbon::now()->endOfMonth());
$end = session('end', Carbon::now()->endOfMonth());
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize;
$journals = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); // budget
$count = $journals->count();
$journals = $journals->slice($offset, $pageSize);
$list = new LengthAwarePaginator($journals, $count, $pageSize);
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = trans(
'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
$list->setPath('/budgets/list/noBudget');
return view('budgets.noBudget', compact('list', 'subTitle'));
// collector
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget();
$journals = $collector->getPaginatedJournals();
$journals->setPath('/budgets/list/noBudget');
return view('budgets.no-budget', compact('journals', 'subTitle'));
}
/**
@@ -282,25 +290,25 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param BudgetRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
* @param Budget $budget
*
* @return View
* @throws FireflyException
*/
public function show(BudgetRepositoryInterface $repository, Budget $budget)
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 = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize;
$journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget
$count = $journals->count();
$journals = $journals->slice($offset, $pageSize);
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
// collector:
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
$journals->setPath('/budgets/show/' . $budget->id);
@@ -310,7 +318,7 @@ class BudgetController extends Controller
/** @var LimitRepetition $entry */
foreach ($set as $entry) {
$entry->spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $entry->startdate, $entry->enddate);
$entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->startdate, $entry->enddate);
$limits->push($entry);
}
@@ -318,33 +326,36 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
* @param BudgetRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return View
* @throws FireflyException
*/
public function showWithRepetition(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
public function showWithRepetition(
BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget, LimitRepetition $repetition
) {
if ($repetition->budgetLimit->budget->id != $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');
}
$start = $repetition->startdate;
$end = $repetition->enddate;
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize;
$journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget
$count = $journals->count();
$journals = $journals->slice($offset, $pageSize);
$journals = new LengthAwarePaginator($journals, $count, $pageSize);
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
// collector:
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
$journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id);
$repetition->spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate);
$repetition->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate);
$limits = new Collection([$repetition]);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
@@ -359,11 +370,8 @@ class BudgetController extends Controller
*/
public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository)
{
$budgetData = [
'name' => $request->input('name'),
'user' => auth()->user()->id,
];
$budget = $repository->store($budgetData);
$data = $request->getBudgetData();
$budget = $repository->store($data);
Session::flash('success', strval(trans('firefly.stored_new_budget', ['name' => e($budget->name)])));
Preferences::mark();
@@ -389,12 +397,8 @@ class BudgetController extends Controller
*/
public function update(BudgetFormRequest $request, BudgetRepositoryInterface $repository, Budget $budget)
{
$budgetData = [
'name' => $request->input('name'),
'active' => intval($request->input('active')) == 1,
];
$repository->update($budget, $budgetData);
$data = $request->getBudgetData();
$repository->update($budget, $data);
Session::flash('success', strval(trans('firefly.updated_budget', ['name' => e($budget->name)])));
Preferences::mark();

View File

@@ -14,13 +14,13 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Requests\CategoryFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
use FireflyIII\Support\CacheProperties;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input;
use Navigation;
@@ -43,8 +43,16 @@ class CategoryController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.categories'));
View::share('mainTitleIcon', 'fa-bar-chart');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.categories'));
View::share('mainTitleIcon', 'fa-bar-chart');
return $next($request);
}
);
}
/**
@@ -139,23 +147,25 @@ class CategoryController extends Controller
}
/**
* @param CRI $repository
*
* @return View
*/
public function noCategory(CRI $repository)
public function noCategory()
{
/** @var Carbon $start */
$start = session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
$end = session('end', Carbon::now()->startOfMonth());
$list = $repository->journalsInPeriodWithoutCategory(new Collection(), [], $start, $end); // category
$end = session('end', Carbon::now()->startOfMonth());
// new collector:
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals();
$journals = $collector->getJournals();
$subTitle = trans(
'firefly.without_category_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
return view('categories.noCategory', compact('list', 'subTitle'));
return view('categories.no-category', compact('journals', 'subTitle'));
}
/**
@@ -172,16 +182,17 @@ class CategoryController extends Controller
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
/** @var Carbon $end */
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$hideCategory = true; // used in list.
$page = intval(Input::get('page'));
$pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize;
$set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category
$count = $set->count();
$subSet = $set->splice($offset, $pageSize);
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
// use journal collector
$collector = new JournalCollector(auth()->user());
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
$journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id);
// oldest transaction in category:
@@ -210,7 +221,7 @@ class CategoryController extends Controller
$categoryCollection = new Collection([$category]);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
while ($end >= $start) {
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
@@ -225,18 +236,16 @@ class CategoryController extends Controller
}
$cache->store($entries);
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle'));
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon'));
}
/**
* @param CRI $repository
* @param Category $category
*
* @param $date
* @param Category $category
* @param $date
*
* @return View
*/
public function showWithDate(CRI $repository, Category $category, string $date)
public function showWithDate(Category $category, string $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
@@ -244,15 +253,16 @@ class CategoryController extends Controller
$end = Navigation::endOfPeriod($carbon, $range);
$subTitle = $category->name;
$hideCategory = true; // used in list.
$page = intval(Input::get('page'));
$pageSize = Preferences::get('transactionPageSize', 50)->data;
$offset = ($page - 1) * $pageSize;
$set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category
$count = $set->count();
$subSet = $set->splice($offset, $pageSize);
$journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page);
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// new collector:
$collector = new JournalCollector(auth()->user());
$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'));
}
@@ -264,11 +274,8 @@ class CategoryController extends Controller
*/
public function store(CategoryFormRequest $request, CRI $repository)
{
$categoryData = [
'name' => trim($request->input('name')),
'user' => auth()->user()->id,
];
$category = $repository->store($categoryData);
$data = $request->getCategoryData();
$category = $repository->store($data);
Session::flash('success', strval(trans('firefly.stored_category', ['name' => e($category->name)])));
Preferences::mark();
@@ -292,11 +299,8 @@ class CategoryController extends Controller
*/
public function update(CategoryFormRequest $request, CRI $repository, Category $category)
{
$categoryData = [
'name' => $request->input('name'),
];
$repository->update($category, $categoryData);
$data = $request->getCategoryData();
$repository->update($category, $data);
Session::flash('success', strval(trans('firefly.updated_category', ['name' => e($category->name)])));
Preferences::mark();

View File

@@ -15,11 +15,13 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Response;
/**
@@ -64,12 +66,11 @@ class BillController extends Controller
/**
* Shows the overview for a bill. The min/max amount and matched journals.
*
* @param BillRepositoryInterface $repository
* @param Bill $bill
* @param Bill $bill
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function single(BillRepositoryInterface $repository, Bill $bill)
public function single(Bill $bill)
{
$cache = new CacheProperties;
$cache->addProperty('single');
@@ -80,12 +81,14 @@ class BillController extends Controller
}
// get first transaction or today for start:
$results = $repository->getJournals($bill, 1, 200);
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]));
$results = $collector->getJournals();
// resort:
$results = $results->sortBy(
function (TransactionJournal $journal) {
return $journal->date->format('U');
function (Transaction $transaction) {
return $transaction->date->format('U');
}
);

View File

@@ -15,10 +15,12 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
@@ -175,7 +177,7 @@ class BudgetController extends Controller
$allEntries = $allEntries->merge($collection);
}
$entry = $this->spentInPeriodWithout($repository, $start, $end);
$entry = $this->spentInPeriodWithout($start, $end);
$allEntries->push($entry);
$data = $this->generator->frontpage($allEntries);
$cache->store($data);
@@ -183,80 +185,6 @@ class BudgetController extends Controller
return Response::json($data);
}
/**
*
* @param BudgetRepositoryInterface $repository
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Collection $budgets
*
*
* @return \Illuminate\Http\JsonResponse
*/
public function multiYear(BudgetRepositoryInterface $repository, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets)
{
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty($budgets);
$cache->addProperty('multiYearBudget');
if ($cache->has()) {
return Response::json($cache->get());
}
$budgetIds = $budgets->pluck('id')->toArray();
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$budgeted = [];
$entries = new Collection;
// filter budgets once:
$repetitions = $repetitions->filter(
function (LimitRepetition $repetition) use ($budgetIds) {
if (in_array(strval($repetition->budget_id), $budgetIds)) {
return true;
}
return false;
}
);
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$year = $repetition->startdate->year;
if (isset($budgeted[$repetition->budget_id][$year])) {
$budgeted[$repetition->budget_id][$year] = bcadd($budgeted[$repetition->budget_id][$year], $repetition->amount);
continue;
}
$budgeted[$repetition->budget_id][$year] = $repetition->amount;
}
foreach ($budgets as $budget) {
$currentStart = clone $start;
$entry = ['name' => $budget->name, 'spent' => [], 'budgeted' => []];
while ($currentStart < $end) {
// fix the date:
$currentEnd = clone $currentStart;
$year = $currentStart->year;
$currentEnd->endOfYear();
$spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $currentStart, $currentEnd);
// jump to next year.
$currentStart = clone $currentEnd;
$currentStart->addDay();
$entry['spent'][$year] = round($spent * -1, 2);
$entry['budgeted'][$year] = isset($budgeted[$budget->id][$year]) ? round($budgeted[$budget->id][$year], 2) : 0;
}
$entries->push($entry);
}
$data = $this->generator->multiYear($entries);
$cache->store($data);
return Response::json($data);
}
/**
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
@@ -393,19 +321,22 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function spentInPeriodWithout(BudgetRepositoryInterface $repository, Carbon $start, Carbon $end):array
private function spentInPeriodWithout(Carbon $start, Carbon $end):array
{
$list = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); // budget
$sum = '0';
/** @var TransactionJournal $entry */
foreach ($list as $entry) {
$sum = bcadd(TransactionJournal::amount($entry), $sum);
// collector
$collector = new JournalCollector(auth()->user());
$types = [TransactionType::WITHDRAWAL];
$collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget();
$journals = $collector->getJournals();
$sum = '0';
/** @var Transaction $entry */
foreach ($journals as $entry) {
$sum = bcadd($entry->transaction_amount, $sum);
}
return [trans('firefly.no_budget'), '0', '0', $sum, '0', '0'];

View File

@@ -153,78 +153,6 @@ class CategoryController extends Controller
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Collection $categories
*
* @return \Illuminate\Http\JsonResponse
*/
public function multiYear(Carbon $start, Carbon $end, Collection $accounts, Collection $categories)
{
/** @var CRI $repository */
$repository = app(CRI::class);
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty('multiYearCategory');
if ($cache->has()) {
return Response::json($cache->get());
}
$entries = new Collection;
/** @var Category $category */
foreach ($categories as $category) {
$entry = ['name' => '', 'spent' => [], 'earned' => []];
$currentStart = clone $start;
while ($currentStart < $end) {
// fix the date:
$year = $currentStart->year;
$currentEnd = clone $currentStart;
$currentEnd->endOfYear();
// get data:
if (is_null($category->id)) {
$entry['name'] = trans('firefly.noCategory');
$entry['spent'][$year] = ($repository->spentInPeriodWithoutCategory($accounts, $currentStart, $currentEnd) * -1);
$entry['earned'][$year] = $repository->earnedInPeriodWithoutCategory($accounts, $currentStart, $currentEnd);
// jump to next year.
$currentStart = clone $currentEnd;
$currentStart->addDay();
continue;
}
// alternative is a normal category:
$entry['name'] = $category->name;
$entry['spent'][$year] = ($repository->spentInPeriod(new Collection([$category]), $accounts, $currentStart, $currentEnd) * -1);
$entry['earned'][$year] = $repository->earnedInPeriod(new Collection([$category]), $accounts, $currentStart, $currentEnd);
// jump to next year.
$currentStart = clone $currentEnd;
$currentStart->addDay();
}
$entries->push($entry);
}
// generate chart with data:
$data = $this->generator->multiYear($entries);
$cache->store($data);
return Response::json($data);
}
/**
* @param CRI $repository
* @param Category $category

View File

@@ -13,7 +13,6 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
@@ -46,38 +45,19 @@ class Controller extends BaseController
View::share('hideBills', false);
View::share('hideTags', false);
// save some formats:
$this->monthFormat = (string)trans('config.month');
$this->monthAndDayFormat = (string)trans('config.month_and_day');
$this->dateTimeFormat = (string)trans('config.date_time');
}
// translations:
/**
* Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay
* and sum up everything in the array in the given range.
*
* @param Carbon $start
* @param Carbon $end
* @param array $array
*
* @return string
*/
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
{
$sum = '0';
$currentStart = clone $start; // to not mess with the original one
$currentEnd = clone $end; // to not mess with the original one
$this->middleware(
function ($request, $next) {
$this->monthFormat = (string)trans('config.month');
$this->monthAndDayFormat = (string)trans('config.month_and_day');
$this->dateTimeFormat = (string)trans('config.date_time');
while ($currentStart <= $currentEnd) {
$date = $currentStart->format('Y-m-d');
if (isset($array[$date])) {
$sum = bcadd($sum, $array[$date]);
return $next($request);
}
$currentStart->addDay();
}
);
return $sum;
}
}

View File

@@ -39,8 +39,16 @@ class CurrencyController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.currencies'));
View::share('mainTitleIcon', 'fa-usd');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.currencies'));
View::share('mainTitleIcon', 'fa-usd');
return $next($request);
}
);
}
/**
@@ -121,7 +129,7 @@ class CurrencyController extends Controller
Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name]));
if (auth()->user()->hasRole('owner')) {
$currency->delete();
$currency->forceDelete();
}
return redirect(session('currency.delete.url'));

View File

@@ -41,8 +41,16 @@ class ExportController extends Controller
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-file-archive-o');
View::share('title', trans('firefly.export_data'));
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-file-archive-o');
View::share('title', trans('firefly.export_data'));
return $next($request);
}
);
}
/**
@@ -133,7 +141,6 @@ class ExportController extends Controller
'endDate' => new Carbon($request->get('export_end_range')),
'exportFormat' => $request->get('exportFormat'),
'includeAttachments' => intval($request->get('include_attachments')) === 1,
'includeConfig' => intval($request->get('include_config')) === 1,
'includeOldUploads' => intval($request->get('include_old_uploads')) === 1,
'job' => $job,
];
@@ -177,15 +184,6 @@ class ExportController extends Controller
$job->change('export_status_collected_old_uploads');
}
/*
* Generate / collect config file.
*/
if ($settings['includeConfig']) {
$job->change('export_status_creating_config_file');
$processor->createConfigFile();
$job->change('export_status_created_config_file');
}
/*
* Create ZIP file:
*/

View File

@@ -41,11 +41,9 @@ class HelpController extends Controller
*/
public function show(HelpInterface $help, string $route)
{
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
$content = [
'text' => '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>',
'title' => 'Help',
];
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
if (!$help->hasRoute($route)) {
Log::error('No such route: ' . $route);
@@ -53,17 +51,32 @@ class HelpController extends Controller
return Response::json($content);
}
if ($help->inCache($route)) {
$content = [
'text' => $help->getFromCache('help.' . $route . '.text.' . $language),
'title' => $help->getFromCache('help.' . $route . '.title.' . $language),
];
if ($help->inCache($route, $language)) {
$content = $help->getFromCache($route, $language);
Log::debug(sprintf('Help text %s was in cache.', $language));
return Response::json($content);
}
$content = $help->getFromGithub($language, $route);
// get backup language content (try English):
if (strlen($content) === 0) {
$language = 'en_US';
if ($help->inCache($route, $language)) {
Log::debug(sprintf('Help text %s was in cache.', $language));
$content = $help->getFromCache($route, $language);
}
if (!$help->inCache($route, $language)) {
$content = $help->getFromGithub($language, $route);
$content = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>' . $content;
}
}
if (strlen($content) === 0) {
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
}
$help->putInCache($route, $language, $content);
return Response::json($content);

View File

@@ -15,10 +15,10 @@ namespace FireflyIII\Http\Controllers;
use Artisan;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
@@ -26,7 +26,7 @@ use Log;
use Preferences;
use Route;
use Session;
use View;
/**
* Class HomeController
@@ -41,6 +41,8 @@ class HomeController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', 'Firefly III');
View::share('mainTitleIcon', 'fa-fire');
}
/**
@@ -113,12 +115,11 @@ class HomeController extends Controller
}
/**
* @param ARI $repository
* @param AccountTaskerInterface $tasker
* @param ARI $repository
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function index(ARI $repository, AccountTaskerInterface $tasker)
public function index(ARI $repository)
{
$types = config('firefly.accountTypesByIdentifier.asset');
@@ -128,11 +129,9 @@ class HomeController extends Controller
return redirect(route('new-user.index'));
}
$title = 'Firefly';
$subTitle = trans('firefly.welcomeBack');
$mainTitleIcon = 'fa-fire';
$transactions = [];
$frontPage = Preferences::get(
$subTitle = trans('firefly.welcomeBack');
$transactions = [];
$frontPage = Preferences::get(
'frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray()
);
/** @var Carbon $start */
@@ -144,8 +143,9 @@ class HomeController extends Controller
$showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data;
foreach ($accounts as $account) {
$set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
$set = $set->splice(0, 10);
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit(10)->setPage(1);
$set = $collector->getJournals();
if (count($set) > 0) {
$transactions[] = [$set, $account];
@@ -164,36 +164,29 @@ class HomeController extends Controller
public function routes()
{
// these routes are not relevant for the help pages:
$ignore = [
$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.',
];
$routes = Route::getRoutes();
echo '<pre>';
/** @var \Illuminate\Routing\Route $route */
foreach ($routes as $route) {
$name = $route->getName();
$methods = $route->getMethods();
$search = [
'{account}', '{what}', '{rule}', '{tj}', '{category}', '{budget}', '{code}', '{date}', '{attachment}', '{bill}', '{limitrepetition}',
'{currency}', '{jobKey}', '{piggyBank}', '{ruleGroup}', '{rule}', '{route}', '{unfinishedJournal}',
'{reportType}', '{start_date}', '{end_date}', '{accountList}', '{tag}', '{journalList}',
];
$replace = [1, 'asset', 1, 1, 1, 1, 'abc', '2016-01-01', 1, 1, 1, 1, 1, 1, 1, 1, 'index', 1,
'default', '20160101', '20160131', '1,2', 1, '1,2',
];
if (count($search) != count($replace)) {
echo 'count';
exit;
}
$url = str_replace($search, $replace, $route->getUri());
if (!is_null($name) && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) {
echo '<a href="/' . $url . '" title="' . $name . '">' . $name . '</a><br>' . "\n";
if (!is_null($name) && strlen($name) > 0 && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) {
echo sprintf('touch %s.md', $name) . "\n";
}
}
echo '</pre>';
return '<hr>';
echo '<hr />';
return '&nbsp;';
}
/**

View File

@@ -41,8 +41,15 @@ class ImportController extends Controller
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-archive');
View::share('title', trans('firefly.import_data_full'));
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-archive');
View::share('title', trans('firefly.import_data_full'));
return $next($request);
}
);
}
/**

View File

@@ -15,12 +15,12 @@ namespace FireflyIII\Http\Controllers;
use Amount;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Input;
@@ -270,17 +270,20 @@ class JsonController extends Controller
}
/**
* @param JournalTaskerInterface $tasker
* @param $what
* @param $what
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function transactionJournals(JournalTaskerInterface $tasker, $what)
public function transactionJournals($what)
{
$descriptions = [];
$type = config('firefly.transactionTypesByWhat.' . $what);
$types = [$type];
$journals = $tasker->getJournals($types, 1, 50);
// use journal collector instead:
$collector = new JournalCollector(auth()->user());
$collector->setTypes($types)->setLimit(100)->setPage(1);
$journals = $collector->getJournals();
foreach ($journals as $j) {
$descriptions[] = $j->description;
}

View File

@@ -32,6 +32,13 @@ class NewUserController extends Controller
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
return $next($request);
}
);
}
@@ -107,7 +114,6 @@ class NewUserController extends Controller
'accountType' => 'asset',
'virtualBalance' => 0,
'active' => true,
'user' => auth()->user()->id,
'accountRole' => 'defaultAsset',
'openingBalance' => round($request->input('bank_balance'), 2),
'openingBalanceDate' => new Carbon,
@@ -133,7 +139,6 @@ class NewUserController extends Controller
'accountType' => 'asset',
'virtualBalance' => 0,
'active' => true,
'user' => auth()->user()->id,
'accountRole' => 'savingAsset',
'openingBalance' => round($request->input('savings_balance'), 2),
'openingBalanceDate' => new Carbon,
@@ -158,7 +163,6 @@ class NewUserController extends Controller
'accountType' => 'asset',
'virtualBalance' => round($request->get('credit_card_limit'), 2),
'active' => true,
'user' => auth()->user()->id,
'accountRole' => 'ccAsset',
'openingBalance' => null,
'openingBalanceDate' => null,

View File

@@ -45,8 +45,16 @@ class PiggyBankController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.piggyBanks'));
View::share('mainTitleIcon', 'fa-sort-amount-asc');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.piggyBanks'));
View::share('mainTitleIcon', 'fa-sort-amount-asc');
return $next($request);
}
);
}
/**
@@ -99,6 +107,12 @@ class PiggyBankController extends Controller
$subTitle = trans('firefly.new_piggy_bank');
$subTitleIcon = 'fa-plus';
if (count($accounts) === 0) {
Session::flash('error', strval(trans('firefly.need_at_least_one_account')));
return redirect(route('new-user.index'));
}
// put previous url in session if not redirect from store (not "create another").
if (session('piggy-banks.create.fromStore') !== true) {
Session::put('piggy-banks.create.url', URL::previous());
@@ -364,18 +378,8 @@ class PiggyBankController extends Controller
*/
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{
$piggyBankData = [
'name' => $request->get('name'),
'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')),
'targetamount' => round($request->get('targetamount'), 2),
'order' => $repository->getMaxOrder() + 1,
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
'note' => $request->get('note'),
];
$piggyBank = $repository->store($piggyBankData);
$data = $request->getPiggyBankData();
$piggyBank = $repository->store($data);
Session::flash('success', strval(trans('firefly.stored_piggy_bank', ['name' => e($piggyBank->name)])));
Preferences::mark();
@@ -400,16 +404,8 @@ class PiggyBankController extends Controller
*/
public function update(PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request, PiggyBank $piggyBank)
{
$piggyBankData = [
'name' => $request->get('name'),
'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate,
'account_id' => intval($request->get('account_id')),
'targetamount' => round($request->get('targetamount'), 2),
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
'note' => $request->get('note'),
];
$piggyBank = $repository->update($piggyBank, $piggyBankData);
$data = $request->getPiggyBankData();
$piggyBank = $repository->update($piggyBank, $data);
Session::flash('success', strval(trans('firefly.updated_piggy_bank', ['name' => e($piggyBank->name)])));
Preferences::mark();

View File

@@ -17,12 +17,12 @@ namespace FireflyIII\Http\Controllers\Popup;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\Binder\AccountList;
@@ -98,20 +98,39 @@ class ReportController extends Controller
$repository = app(AccountRepositoryInterface::class);
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::WITHDRAWAL];
switch (true) {
case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)):
$journals = $budgetRepository->journalsInPeriod(
new Collection([$budget]), new Collection([$account]), $attributes['startDate'], $attributes['endDate']
);
$collector = new JournalCollector(auth()->user());
$collector
->setAccounts(new Collection([$account]))
->setRange($attributes['startDate'], $attributes['endDate'])
->setBudget($budget);
$journals = $collector->getJournals();
break;
case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)):
$budget->name = strval(trans('firefly.no_budget'));
$journals = $budgetRepository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
// collector
$collector = new JournalCollector(auth()->user());
$collector
->setAccounts(new Collection([$account]))
->setTypes($types)
->setRange($attributes['startDate'], $attributes['endDate'])
->withoutBudget();
$journals = $collector->getJournals();
break;
case ($role === BalanceLine::ROLE_DIFFROLE):
// journals no budget, not corrected by a tag.
$journals = $budgetRepository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
$collector = new JournalCollector(auth()->user());
$collector
->setAccounts(new Collection([$account]))
->setTypes($types)
->setRange($attributes['startDate'], $attributes['endDate'])
->withoutBudget();
$journals = $collector->getJournals();
$budget->name = strval(trans('firefly.leftUnbalanced'));
$journals = $journals->filter(
function (TransactionJournal $journal) {
@@ -148,14 +167,21 @@ class ReportController extends Controller
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$budget = $repository->find(intval($attributes['budgetId']));
if (is_null($budget->id)) {
$journals = $repository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
} else {
// get all expenses in budget in period:
$journals = $repository->journalsInPeriod(new Collection([$budget]), $attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
}
$collector = new JournalCollector(auth()->user());
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
$collector
->setAccounts($attributes['accounts'])
->setRange($attributes['startDate'], $attributes['endDate']);
if (is_null($budget->id)) {
$collector->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
}
if (!is_null($budget->id)) {
// get all expenses in budget in period:
$collector->setBudget($budget);
}
$journals = $collector->getJournals();
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
return $view;
}
@@ -173,8 +199,15 @@ class ReportController extends Controller
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$category = $repository->find(intval($attributes['categoryId']));
$journals = $repository->journalsInPeriod(new Collection([$category]), $attributes['accounts'], [], $attributes['startDate'], $attributes['endDate']);
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
// get journal collector instead:
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($attributes['accounts'])->setTypes($types)
->setRange($attributes['startDate'], $attributes['endDate'])
->setCategory($category);
$journals = $collector->getJournals(); // 7193
$view = view('popup.report.category-entry', compact('journals', 'category'))->render();
return $view;
}
@@ -189,14 +222,14 @@ class ReportController extends Controller
*/
private function expenseEntry(array $attributes): string
{
/** @var AccountTaskerInterface $tasker */
$tasker = app(AccountTaskerInterface::class);
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
$journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']);
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
$journals = $collector->getJournals();
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
// filter for transfers and withdrawals TO the given $account
@@ -225,14 +258,14 @@ class ReportController extends Controller
*/
private function incomeEntry(array $attributes): string
{
/** @var AccountTaskerInterface $tasker */
$tasker = app(AccountTaskerInterface::class);
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
$journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']);
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types);
$journals = $collector->getJournals();
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
// filter the set so the destinations outside of $attributes['accounts'] are not included.
$journals = $journals->filter(

View File

@@ -35,8 +35,16 @@ class PreferencesController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.preferences'));
View::share('mainTitleIcon', 'fa-gear');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.preferences'));
View::share('mainTitleIcon', 'fa-gear');
return $next($request);
}
);
}
/**

View File

@@ -13,7 +13,6 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Events\DeletedUser;
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
use FireflyIII\Http\Requests\ProfileFormRequest;
use FireflyIII\User;
@@ -36,8 +35,15 @@ class ProfileController extends Controller
{
parent::__construct();
View::share('title', trans('firefly.profile'));
View::share('mainTitleIcon', 'fa-user');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.profile'));
View::share('mainTitleIcon', 'fa-user');
return $next($request);
}
);
}
/**
@@ -45,9 +51,11 @@ class ProfileController extends Controller
*/
public function changePassword()
{
return view('profile.change-password')->with('title', auth()->user()->email)->with('subTitle', trans('firefly.change_your_password'))->with(
'mainTitleIcon', 'fa-user'
);
$title = auth()->user()->email;
$subTitle = strval(trans('firefly.change_your_password'));
$subTitleIcon = 'fa-key';
return view('profile.change-password', compact('title', 'subTitle', 'subTitleIcon'));
}
/**
@@ -55,9 +63,11 @@ class ProfileController extends Controller
*/
public function deleteAccount()
{
return view('profile.delete-account')->with('title', auth()->user()->email)->with('subTitle', trans('firefly.delete_account'))->with(
'mainTitleIcon', 'fa-user'
);
$title = auth()->user()->email;
$subTitle = strval(trans('firefly.delete_account'));
$subTitleIcon = 'fa-trash';
return view('profile.delete-account', compact('title', 'subTitle', 'subTitleIcon'));
}
/**

View File

@@ -17,6 +17,7 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
@@ -36,9 +37,23 @@ class AccountController extends Controller
*/
public function accountReport(Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('account-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$accountTasker = app(AccountTaskerInterface::class);
$accountReport = $accountTasker->getAccountReport($start, $end, $accounts);
return view('reports.partials.accounts', compact('accountReport'));
$result = view('reports.partials.accounts', compact('accountReport'))->render();
$cache->store($result);
return $result;
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* BalanceController.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\BalanceReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class BalanceController
*
* @package FireflyIII\Http\Controllers\Report
*/
class BalanceController extends Controller
{
/**
* @param BalanceReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function balanceReport(BalanceReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('balance-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$balance = $helper->getBalanceReport($start, $end, $accounts);
$result = view('reports.partials.balance', compact('balance'))->render();
$cache->store($result);
return $result;
}
}

View File

@@ -0,0 +1,120 @@
<?php
/**
* BudgetController.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\BudgetReportHelperInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class BudgetController
*
* @package FireflyIII\Http\Controllers\Report
*/
class BudgetController extends Controller
{
/**
* @param BudgetReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return mixed|string
*/
public function budgetMultiYear(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-mult-year-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$years = $helper->listOfYears($start, $end);
$budgetMultiYear = $helper->getBudgetMultiYear($start, $end, $accounts);
$result = view('reports.partials.budget-multi-year', compact('budgetMultiYear', 'years'))->render();
$cache->store($result);
return $result;
}
/**
* @param BudgetReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function budgetReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$budgets = $helper->getBudgetReport($start, $end, $accounts);
$result = view('reports.partials.budgets', compact('budgets'))->render();
$cache->store($result);
return $result;
}
/**
* @param BudgetReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function budgetYearOverview(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget-year-overview');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$budgets = $helper->budgetYearOverview($start, $end, $accounts);
$result = view('reports.partials.budget-year-overview', compact('budgets'))->render();
$cache->store($result);
return $result;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* CategoryController.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 CategoryController
*
* @package FireflyIII\Http\Controllers\Report
*/
class CategoryController extends Controller
{
/**
* @param ReportHelperInterface $helper
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function categoryReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-report');
$cache->addProperty($accounts->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get();
}
$categories = $helper->getCategoryReport($start, $end, $accounts);
$result = view('reports.partials.categories', compact('categories'))->render();
$cache->store($result);
return $result;
}
}

View File

@@ -0,0 +1,119 @@
<?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

@@ -15,17 +15,12 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Report\BalanceReportHelperInterface;
use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Support\Collection;
use Preferences;
use Session;
@@ -39,12 +34,6 @@ use View;
*/
class ReportController extends Controller
{
/** @var BalanceReportHelperInterface */
protected $balanceHelper;
/** @var BudgetReportHelperInterface */
protected $budgetHelper;
/** @var ReportHelperInterface */
protected $helper;
@@ -55,8 +44,17 @@ class ReportController extends Controller
{
parent::__construct();
View::share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart');
$this->helper = app(ReportHelperInterface::class);
return $next($request);
}
);
}
@@ -67,7 +65,7 @@ class ReportController extends Controller
*/
public function index(AccountRepositoryInterface $repository)
{
$this->createRepositories();
/** @var Carbon $start */
$start = clone session('first');
$months = $this->helper->listOfMonths($start);
@@ -98,7 +96,6 @@ class ReportController extends Controller
*/
public function report(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$this->createRepositories();
// throw an error if necessary.
if ($end < $start) {
throw new FireflyException('End date cannot be before start date, silly!');
@@ -153,8 +150,6 @@ class ReportController extends Controller
*/
private function auditReport(Carbon $start, Carbon $end, Collection $accounts)
{
/** @var AccountTaskerInterface $tasker */
$tasker = app(AccountTaskerInterface::class);
$auditData = [];
$dayBefore = clone $start;
$dayBefore->subDay();
@@ -163,9 +158,11 @@ class ReportController extends Controller
// balance the day before:
$id = $account->id;
$dayBeforeBalance = Steam::balance($account, $dayBefore);
$journals = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end);
$journals = $journals->reverse();
$startBalance = $dayBeforeBalance;
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($start, $end);
$journals = $collector->getJournals();
$journals = $journals->reverse();
$startBalance = $dayBeforeBalance;
/** @var Transaction $journal */
@@ -206,16 +203,6 @@ class ReportController extends Controller
return view('reports.audit.report', compact('start', 'end', 'reportType', 'accountIds', 'accounts', 'auditData', 'hideable', 'defaultShow'));
}
/**
*
*/
private function createRepositories()
{
$this->helper = app(ReportHelperInterface::class);
$this->budgetHelper = app(BudgetReportHelperInterface::class);
$this->balanceHelper = app(BalanceReportHelperInterface::class);
}
/**
* @param $reportType
* @param Carbon $start
@@ -226,17 +213,8 @@ class ReportController extends Controller
*/
private function defaultMonth(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$incomeTopLength = 8;
$expenseTopLength = 8;
// get report stuff!
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
$budgets = $this->budgetHelper->getBudgetReport($start, $end, $accounts);
$categories = $this->helper->getCategoryReport($start, $end, $accounts);
$balance = $this->balanceHelper->getBalanceReport($start, $end, $accounts);
$bills = $this->helper->getBillReport($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
$bills = $this->helper->getBillReport($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
// and some id's, joined:
$accountIds = join(',', $accounts->pluck('id')->toArray());
@@ -245,14 +223,11 @@ class ReportController extends Controller
return view(
'reports.default.month',
compact(
'start', 'end', 'reportType',
'start', 'end',
'tags',
'incomes', 'incomeTopLength',
'expenses', 'expenseTopLength',
'budgets', 'balance',
'categories',
'bills',
'accountIds', 'reportType'
'accountIds',
'reportType'
)
);
}
@@ -267,15 +242,9 @@ class ReportController extends Controller
*/
private function defaultMultiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
// need all budgets
// need all years.
$incomeTopLength = 8;
$expenseTopLength = 8;
// list of users stuff:
$budgets = app(BudgetRepositoryInterface::class)->getActiveBudgets();
$categories = app(CategoryRepositoryInterface::class)->getCategories();
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
// and some id's, joined:
$accountIds = [];
@@ -288,9 +257,7 @@ class ReportController extends Controller
return view(
'reports.default.multi-year',
compact(
'budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'reportType',
'incomes', 'expenses',
'incomeTopLength', 'expenseTopLength', 'tags'
'accounts', 'start', 'end', 'accountIds', 'reportType'
)
);
}
@@ -305,14 +272,6 @@ class ReportController extends Controller
*/
private function defaultYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$incomeTopLength = 8;
$expenseTopLength = 8;
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
$tags = $this->helper->tagReport($start, $end, $accounts);
$budgets = $this->budgetHelper->budgetYearOverview($start, $end, $accounts);
Session::flash('gaEventCategory', 'report');
Session::flash('gaEventAction', 'year');
Session::flash('gaEventLabel', $start->format('Y'));
@@ -328,8 +287,8 @@ class ReportController extends Controller
return view(
'reports.default.year',
compact(
'start', 'incomes', 'reportType', 'accountIds', 'end',
'expenses', 'incomeTopLength', 'expenseTopLength', 'tags', 'budgets'
'start', 'reportType',
'accountIds', 'end'
)
);
}

View File

@@ -42,8 +42,16 @@ class RuleController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.rules'));
View::share('mainTitleIcon', 'fa-random');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.rules'));
View::share('mainTitleIcon', 'fa-random');
return $next($request);
}
);
}
/**
@@ -238,23 +246,8 @@ class RuleController extends Controller
*/
public function store(RuleFormRequest $request, RuleRepositoryInterface $repository, RuleGroup $ruleGroup)
{
// process the rule itself:
$data = [
'rule_group_id' => $ruleGroup->id,
'title' => $request->get('title'),
'user_id' => auth()->user()->id,
'trigger' => $request->get('trigger'),
'description' => $request->get('description'),
'rule-triggers' => $request->get('rule-trigger'),
'rule-trigger-values' => $request->get('rule-trigger-value'),
'rule-trigger-stop' => $request->get('rule-trigger-stop'),
'rule-actions' => $request->get('rule-action'),
'rule-action-values' => $request->get('rule-action-value'),
'rule-action-stop' => $request->get('rule-action-stop'),
'stop_processing' => $request->get('stop_processing'),
];
$data = $request->getRuleData();
$data['rule_group_id'] = $ruleGroup->id;
$rule = $repository->store($data);
Session::flash('success', trans('firefly.stored_new_rule', ['title' => $rule->title]));
@@ -313,7 +306,7 @@ class RuleController extends Controller
}
// Return json response
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
$view = view('list.journals-tiny-tasker', ['transactions' => $matchingTransactions])->render();
return Response::json(['html' => $view, 'warning' => $warning]);
}
@@ -341,21 +334,7 @@ class RuleController extends Controller
*/
public function update(RuleRepositoryInterface $repository, RuleFormRequest $request, Rule $rule)
{
// process the rule itself:
$data = [
'title' => $request->get('title'),
'active' => intval($request->get('active')) == 1,
'trigger' => $request->get('trigger'),
'description' => $request->get('description'),
'rule-triggers' => $request->get('rule-trigger'),
'rule-trigger-values' => $request->get('rule-trigger-value'),
'rule-trigger-stop' => $request->get('rule-trigger-stop'),
'rule-actions' => $request->get('rule-action'),
'rule-action-values' => $request->get('rule-action-value'),
'rule-action-stop' => $request->get('rule-action-stop'),
'stop_processing' => intval($request->get('stop_processing')) == 1,
];
$data = $request->getRuleData();
$repository->update($rule, $data);
Session::flash('success', trans('firefly.updated_rule', ['title' => $rule->title]));
@@ -381,7 +360,6 @@ class RuleController extends Controller
$data = [
'rule_group_id' => $repository->getFirstRuleGroup()->id,
'stop_processing' => 0,
'user_id' => auth()->user()->id,
'title' => trans('firefly.default_rule_name'),
'description' => trans('firefly.default_rule_description'),
'trigger' => 'store-journal',
@@ -410,11 +388,10 @@ class RuleController extends Controller
{
/** @var RuleGroupRepositoryInterface $repository */
$repository = app('FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface');
$repository = app(RuleGroupRepositoryInterface::class);
if ($repository->count() === 0) {
$data = [
'user_id' => auth()->user()->id,
'title' => trans('firefly.default_rule_group_name'),
'description' => trans('firefly.default_rule_group_description'),
];

View File

@@ -41,8 +41,16 @@ class RuleGroupController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.rules'));
View::share('mainTitleIcon', 'fa-random');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.rules'));
View::share('mainTitleIcon', 'fa-random');
return $next($request);
}
);
}
/**
@@ -204,12 +212,7 @@ class RuleGroupController extends Controller
*/
public function store(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository)
{
$data = [
'title' => $request->input('title'),
'description' => $request->input('description'),
'user_id' => auth()->user()->id,
];
$data = $request->getRuleGroupData();
$ruleGroup = $repository->store($data);
Session::flash('success', strval(trans('firefly.created_new_rule_group', ['title' => $ruleGroup->title])));

View File

@@ -14,7 +14,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Support\Search\SearchInterface;
use Input;
use Illuminate\Http\Request;
/**
* Class SearchController
@@ -29,6 +29,7 @@ class SearchController extends Controller
public function __construct()
{
parent::__construct();
}
/**
@@ -38,16 +39,21 @@ class SearchController extends Controller
*
* @return $this
*/
public function index(SearchInterface $searcher)
public function index(Request $request, SearchInterface $searcher)
{
$minSearchLen = 1;
$subTitle = null;
$query = null;
$result = [];
$title = trans('firefly.search');
$limit = 20;
$mainTitleIcon = 'fa-search';
if (!is_null(Input::get('q')) && strlen(Input::get('q')) > 0) {
$query = trim(Input::get('q'));
// set limit for search:
$searcher->setLimit($limit);
if (!is_null($request->get('q')) && strlen($request->get('q')) >= $minSearchLen) {
$query = trim(strtolower($request->get('q')));
$words = explode(' ', $query);
$subTitle = trans('firefly.search_results_for', ['query' => $query]);
@@ -60,7 +66,7 @@ class SearchController extends Controller
}
return view('search.index', compact('title', 'subTitle', 'mainTitleIcon', 'query', 'result'));
return view('search.index', compact('title', 'subTitle', 'limit', 'mainTitleIcon', 'query', 'result'));
}
}

View File

@@ -13,10 +13,11 @@ declare(strict_types = 1);
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\TransactionJournal;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
use Input;
@@ -50,15 +51,22 @@ class TagController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', 'Tags');
View::share('mainTitleIcon', 'fa-tags');
View::share('hideTags', true);
$this->tagOptions = [
'nothing' => trans('firefly.regular_tag'),
'balancingAct' => trans('firefly.balancing_act'),
'advancePayment' => trans('firefly.advance_payment'),
];
View::share('tagOptions', $this->tagOptions);
$this->middleware(
function ($request, $next) {
View::share('title', 'Tags');
View::share('mainTitleIcon', 'fa-tags');
$this->tagOptions = [
'nothing' => trans('firefly.regular_tag'),
'balancingAct' => trans('firefly.balancing_act'),
'advancePayment' => trans('firefly.advance_payment'),
];
View::share('tagOptions', $this->tagOptions);
return $next($request);
}
);
}
/**
@@ -219,19 +227,27 @@ class TagController extends Controller
}
/**
* @param Tag $tag
* @param TagRepositoryInterface $repository
* @param Tag $tag
*
* @return View
*/
public function show(Tag $tag, TagRepositoryInterface $repository)
public function show(Tag $tag)
{
$subTitle = $tag->tag;
$subTitleIcon = 'fa-tag';
$journals = $repository->getJournals($tag);
$sum = $journals->sum(
function (TransactionJournal $journal) {
return TransactionJournal::amount($journal);
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// use collector:
// replace with journal collector:
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTag($tag);
$journals = $collector->getPaginatedJournals();
$journals->setPath('tags/show/' . $tag->id);
$sum = $journals->sum(
function (Transaction $transaction) {
return $transaction->transaction_amount;
}
);

View File

@@ -0,0 +1,240 @@
<?php
/**
* ConvertController.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\Transaction;
use ExpandedForm;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Http\Request;
use Session;
use View;
/**
* Class ConvertController
*
* @package FireflyIII\Http\Controllers\Transaction
*/
class ConvertController extends Controller
{
/** @var AccountRepositoryInterface */
private $accounts;
/**
* ConvertController constructor.
*/
public function __construct()
{
parent::__construct();
// some useful repositories:
$this->middleware(
function ($request, $next) {
$this->accounts = app(AccountRepositoryInterface::class);
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-exchange');
return $next($request);
}
);
}
/**
* @param TransactionType $destinationType
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function convert(TransactionType $destinationType, TransactionJournal $journal)
{
$positiveAmount = TransactionJournal::amountPositive($journal);
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$sourceType = $journal->transactionType;
$subTitle = trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]);
$subTitleIcon = 'fa-exchange';
// cannot convert to its own type.
if ($sourceType->type === $destinationType->type) {
Session::flash('info', trans('firefly.convert_is_already_type_' . $destinationType->type));
return redirect(route('transactions.show', [$journal->id]));
}
// cannot convert split.
if ($journal->transactions()->count() > 2) {
Session::flash('error', trans('firefly.cannot_convert_split_journl'));
return redirect(route('transactions.show', [$journal->id]));
}
// get source and destination account:
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
return view(
'transactions.convert',
compact(
'sourceType', 'destinationType', 'journal', 'assetAccounts',
'positiveAmount', 'sourceAccount', 'destinationAccount', 'sourceType',
'subTitle', 'subTitleIcon'
)
);
// convert withdrawal to deposit requires a new source account ()
// or to transfer requires
}
/**
* @param Request $request
* @param JournalRepositoryInterface $repository
* @param TransactionType $destinationType
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function submit(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal)
{
$data = $request->all();
// cannot convert to its own type.
if ($journal->transactionType->type === $destinationType->type) {
Session::flash('error', trans('firefly.convert_is_already_type_' . $destinationType->type));
return redirect(route('transactions.show', [$journal->id]));
}
// cannot convert split.
if ($journal->transactions()->count() > 2) {
Session::flash('error', trans('firefly.cannot_convert_split_journl'));
return redirect(route('transactions.show', [$journal->id]));
}
// get the new source and destination account:
$source = $this->getSourceAccount($journal, $destinationType, $data);
$destination = $this->getDestinationAccount($journal, $destinationType, $data);
// update the journal:
$errors = $repository->convert($journal, $destinationType, $source, $destination);
if ($errors->count() > 0) {
return redirect(route('transactions.convert', [strtolower($destinationType->type), $journal->id]))->withErrors($errors)->withInput();
}
Session::flash('success', trans('firefly.converted_to_' . $destinationType->type));
return redirect(route('transactions.show', [$journal->id]));
}
/**
* @param TransactionJournal $journal
* @param TransactionType $destinationType
* @param array $data
*
* @return Account
* @throws FireflyException
*/
private function getDestinationAccount(TransactionJournal $journal, TransactionType $destinationType, array $data): Account
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
$sourceType = $journal->transactionType;
$destination = null;
$joined = $sourceType->type . '-' . $destinationType->type;
switch ($joined) {
default:
throw new FireflyException('Cannot handle ' . $joined);
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
$destination = $sourceAccount;
break;
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
$data = [
'name' => $data['destination_account_expense'],
'accountType' => 'expense',
'virtualBalance' => 0,
'active' => true,
'iban' => null,
];
$destination = $accountRepository->store($data);
break;
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
$destination = $destinationAccount;
break;
}
return $destination;
}
/**
* @param TransactionJournal $journal
* @param TransactionType $destinationType
* @param array $data
*
* @return Account
* @throws FireflyException
*/
private function getSourceAccount(TransactionJournal $journal, TransactionType $destinationType, array $data): Account
{
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$sourceAccount = TransactionJournal::sourceAccountList($journal)->first();
$destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
$sourceType = $journal->transactionType;
$joined = $sourceType->type . '-' . $destinationType->type;
switch ($joined) {
default:
throw new FireflyException('Cannot handle ' . $joined);
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
$data = [
'name' => $data['source_account_revenue'],
'accountType' => 'revenue',
'virtualBalance' => 0,
'active' => true,
'iban' => null,
];
$source = $accountRepository->store($data);
break;
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five
$source = $sourceAccount;
break;
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three
$source = $destinationAccount;
break;
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
$source = $accountRepository->find(intval($data['source_account_asset']));
break;
}
return $source;
}
}

View File

@@ -41,8 +41,16 @@ class MassController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
return $next($request);
}
);
}
/**
@@ -209,7 +217,6 @@ class MassController extends Controller
'destination_account_id' => intval($destAccountId),
'destination_account_name' => $destAccountName,
'amount' => round($request->get('amount')[$journal->id], 4),
'user' => auth()->user()->id,
'amount_currency_id_amount' => intval($request->get('amount_currency_id_amount_' . $journal->id)),
'date' => new Carbon($request->get('date')[$journal->id]),
'interest_date' => $journal->interest_date,

View File

@@ -59,8 +59,6 @@ class SingleController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
@@ -75,11 +73,13 @@ class SingleController extends Controller
$this->piggyBanks = app(PiggyBankRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
return $next($request);
}
);
}
/**
@@ -171,7 +171,6 @@ class SingleController extends Controller
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
$piggyBankList = ExpandedForm::makeSelectListWithEmpty($this->piggyBanks->getPiggyBanks());
// view related code
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
@@ -188,7 +187,6 @@ class SingleController extends Controller
'process_date' => TransactionJournal::dateAsString($journal, 'process_date'),
'category' => TransactionJournal::categoryAsString($journal),
'budget_id' => TransactionJournal::budgetId($journal),
'piggy_bank_id' => TransactionJournal::piggyBankId($journal),
'tags' => join(',', $journal->tags->pluck('tag')->toArray()),
'source_account_id' => $sourceAccounts->first()->id,
'source_account_name' => $sourceAccounts->first()->name,
@@ -225,7 +223,7 @@ class SingleController extends Controller
return view(
'transactions.edit',
compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'piggyBankList', 'subTitle')
compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'subTitle')
)->with('data', $preFilled);
}
@@ -328,4 +326,4 @@ class SingleController extends Controller
}
}
}

View File

@@ -26,6 +26,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use Illuminate\Http\Request;
use Log;
use Preferences;
use Session;
use Steam;
@@ -62,8 +63,7 @@ class SplitController extends Controller
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-share-alt');
View::share('title', trans('firefly.split-transactions'));
// some useful repositories:
$this->middleware(
@@ -73,6 +73,8 @@ class SplitController extends Controller
$this->tasker = app(JournalTaskerInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
$this->currencies = app(CurrencyRepositoryInterface::class);
View::share('mainTitleIcon', 'fa-share-alt');
View::share('title', trans('firefly.split-transactions'));
return $next($request);
}
@@ -264,6 +266,7 @@ class SplitController extends Controller
$return = [];
$transactions = $request->get('transactions');
foreach ($transactions as $transaction) {
$return[] = [
'description' => $transaction['description'],
'source_account_id' => $transaction['source_account_id'] ?? 0,
@@ -273,9 +276,9 @@ class SplitController extends Controller
'amount' => round($transaction['amount'] ?? 0, 2),
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'] ?? '',
'user' => auth()->user()->id, // needed for accounts.
];
}
Log::debug(sprintf('Found %d splits in request data.', count($return)));
return $return;
}

View File

@@ -14,6 +14,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
@@ -35,27 +36,36 @@ class TransactionController extends Controller
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
$this->middleware(
function ($request, $next) {
View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat');
return $next($request);
}
);
}
/**
* @param Request $request
* @param JournalTaskerInterface $tasker
* @param string $what
* @param Request $request
* @param string $what
*
* @return View
*/
public function index(Request $request, JournalTaskerInterface $tasker, string $what)
public function index(Request $request, string $what)
{
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
$types = config('firefly.transactionTypesByWhat.' . $what);
$subTitle = trans('firefly.title_' . $what);
$page = intval($request->get('page'));
$journals = $tasker->getJournals($types, $page, $pageSize);
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$collector = new JournalCollector(auth()->user());
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
$journals = $collector->getPaginatedJournals();
$journals->setPath('transactions/' . $what);
return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals'));

View File

@@ -70,17 +70,29 @@ class Range
// set start, end and finish:
$this->setRange();
// set view variables.
$this->configureView();
// get variables for date range:
$this->datePicker();
// set view variables.
$this->configureView();
// set more view variables:
$this->configureList();
}
return $theNext($request);
}
/**
*
*/
private function configureList()
{
$pref = Preferences::get('list-length', config('firefly.list_length', 10))->data;
View::share('listLength', $pref);
}
private function configureView()
{
$pref = Preferences::get('language', config('firefly.default_language', 'en_US'));

View File

@@ -13,8 +13,8 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\Account;
use Input;
use Carbon\Carbon;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
/**
* Class AccountFormRequest
@@ -33,20 +33,44 @@ class AccountFormRequest extends Request
return auth()->check();
}
/**
* @return array
*/
public function getAccountData(): array
{
return [
'name' => trim($this->input('name')),
'active' => intval($this->input('active')) === 1,
'accountType' => $this->input('what'),
'virtualBalance' => round($this->input('virtualBalance'), 2),
'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')),
'iban' => trim($this->input('iban')),
'accountNumber' => trim($this->input('accountNumber')),
'accountRole' => $this->input('accountRole'),
'openingBalance' => round($this->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$this->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($this->input('amount_currency_id_openingBalance')),
'ccType' => $this->input('ccType'),
'ccMonthlyPaymentDate' => $this->input('ccMonthlyPaymentDate'),
];
}
/**
* @return array
*/
public function rules()
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$accountRoles = join(',', array_keys(config('firefly.accountRoles')));
$types = join(',', array_keys(config('firefly.subTitlesByIdentifier')));
$ccPaymentTypes = join(',', array_keys(config('firefly.ccTypes')));
$nameRule = 'required|min:1|uniqueAccountForUser';
$idRule = '';
if (Account::find(Input::get('id'))) {
if (!is_null($repository->find(intval($this->get('id')))->id)) {
$idRule = 'belongsToUser:accounts';
$nameRule = 'required|min:1|uniqueAccountForUser:' . Input::get('id');
$nameRule = 'required|min:1|uniqueAccountForUser:' . $this->get('id');
}
return [

View File

@@ -30,6 +30,18 @@ class AttachmentFormRequest extends Request
return auth()->check();
}
/**
* @return array
*/
public function getAttachmentData(): array
{
return [
'title' => trim($this->input('title')),
'description' => trim($this->input('description')),
'notes' => trim($this->input('notes')),
];
}
/**
* @return array
*/

View File

@@ -14,7 +14,6 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use Carbon\Carbon;
use Input;
/**
* Class BillFormRequest
@@ -46,7 +45,6 @@ class BillFormRequest extends Request
'amount_currency_id_amount_max' => intval($this->get('amount_currency_id_amount_max')),
'amount_max' => round($this->get('amount_max'), 2),
'date' => new Carbon($this->get('date')),
'user' => auth()->user()->id,
'repeat_freq' => $this->get('repeat_freq'),
'skip' => intval($this->get('skip')),
'automatch' => intval($this->get('automatch')) === 1,
@@ -61,9 +59,9 @@ class BillFormRequest extends Request
{
$nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name';
$matchRule = 'required|between:1,255|uniqueObjectForUser:bills,match';
if (intval(Input::get('id')) > 0) {
$nameRule .= ',' . intval(Input::get('id'));
$matchRule .= ',' . intval(Input::get('id'));
if (intval($this->get('id')) > 0) {
$nameRule .= ',' . intval($this->get('id'));
$matchRule .= ',' . intval($this->get('id'));
}
$rules = [

View File

@@ -13,8 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\Budget;
use Input;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
/**
* Class BudgetFormRequest
@@ -29,19 +28,30 @@ class BudgetFormRequest extends Request
*/
public function authorize()
{
// Only allow logged in users
return auth()->check();
}
/**
* @return array
*/
public function getBudgetData(): array
{
return [
'name' => trim($this->input('name')),
'active' => intval($this->input('active')) == 1,
];
}
/**
* @return array
*/
public function rules()
{
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name';
if (Budget::find(Input::get('id'))) {
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,' . intval(Input::get('id'));
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name';
if (!is_null($repository->find(intval($this->get('id')))->id)) {
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,' . intval($this->get('id'));
}
return [

View File

@@ -13,8 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\Category;
use Input;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
/**
* Class CategoryFormRequest
@@ -33,15 +32,26 @@ class CategoryFormRequest extends Request
return auth()->check();
}
/**
* @return array
*/
public function getCategoryData(): array
{
return [
'name' => trim($this->input('name')),
];
}
/**
* @return array
*/
public function rules()
{
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name';
if (Category::find(Input::get('id'))) {
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,' . intval(Input::get('id'));
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name';
if (!is_null($repository->find(intval($this->get('id')))->id)) {
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,' . intval($this->get('id'));
}
return [

View File

@@ -30,6 +30,16 @@ class ConfigurationRequest extends Request
return auth()->check() && auth()->user()->hasRole('owner');
}
/**
* @return array
*/
public function getConfigurationData(): array
{
return [
'single_user_mode' => intval($this->get('single_user_mode')) === 1,
];
}
/**
* @return array
*/

View File

@@ -13,8 +13,6 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use Input;
/**
* Class BillFormRequest
*
@@ -55,7 +53,7 @@ class CurrencyFormRequest extends Request
'name' => 'required|max:48|min:1|unique:transaction_currencies,name',
'symbol' => 'required|min:1|max:8|unique:transaction_currencies,symbol',
];
if (intval(Input::get('id')) > 0) {
if (intval($this->get('id')) > 0) {
$rules = [
'code' => 'required|min:3|max:3',
'name' => 'required|max:48|min:1',

View File

@@ -16,7 +16,6 @@ namespace FireflyIII\Http\Requests;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionType;
use Input;
/**
* Class JournalFormRequest
@@ -44,7 +43,6 @@ class JournalFormRequest extends Request
{
$data = [
'what' => $this->get('what'), // type. can be 'deposit', 'withdrawal' or 'transfer'
'user' => auth()->user()->id,
'date' => new Carbon($this->get('date')),
'tags' => explode(',', $this->getFieldOrEmptyString('tags')),
'currency_id' => intval($this->get('amount_currency_id_amount')),
@@ -80,7 +78,7 @@ class JournalFormRequest extends Request
*/
public function rules()
{
$what = Input::get('what');
$what = $this->get('what');
$rules = [
'what' => 'required|in:withdrawal,deposit,transfer',
'date' => 'required|date',

View File

@@ -13,7 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use Input;
use Carbon\Carbon;
/**
* Class PiggyBankFormRequest
@@ -32,6 +32,21 @@ class PiggyBankFormRequest extends Request
return auth()->check();
}
/**
* @return array
*/
public function getPiggyBankData(): array
{
return [
'name' => trim($this->get('name')),
'startdate' => new Carbon,
'account_id' => intval($this->get('account_id')),
'targetamount' => round($this->get('targetamount'), 2),
'targetdate' => strlen($this->get('targetdate')) > 0 ? new Carbon($this->get('targetdate')) : null,
'note' => trim($this->get('note')),
];
}
/**
* @return array
*/
@@ -40,8 +55,8 @@ class PiggyBankFormRequest extends Request
$nameRule = 'required|between:1,255|uniquePiggyBankForUser';
$targetDateRule = 'date';
if (intval(Input::get('id'))) {
$nameRule = 'required|between:1,255|uniquePiggyBankForUser:' . intval(Input::get('id'));
if (intval($this->get('id'))) {
$nameRule = 'required|between:1,255|uniquePiggyBankForUser:' . intval($this->get('id'));
}

View File

@@ -13,8 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\RuleGroup;
use Input;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
/**
* Class RuleFormRequest
@@ -33,12 +32,33 @@ class RuleFormRequest extends Request
return auth()->check();
}
/**
* @return array
*/
public function getRuleData(): array
{
return [
'title' => trim($this->get('title')),
'active' => intval($this->get('active')) == 1,
'trigger' => trim($this->get('trigger')),
'description' => trim($this->get('description')),
'rule-triggers' => $this->get('rule-trigger'),
'rule-trigger-values' => $this->get('rule-trigger-value'),
'rule-trigger-stop' => $this->get('rule-trigger-stop'),
'rule-actions' => $this->get('rule-action'),
'rule-action-values' => $this->get('rule-action-value'),
'rule-action-stop' => $this->get('rule-action-stop'),
'stop_processing' => intval($this->get('stop_processing')) === 1,
];
}
/**
* @return array
*/
public function rules()
{
/** @var RuleGroupRepositoryInterface $repository */
$repository = app(RuleGroupRepositoryInterface::class);
$validTriggers = array_keys(config('firefly.rule-triggers'));
$validActions = array_keys(config('firefly.rule-actions'));
@@ -46,8 +66,8 @@ class RuleFormRequest extends Request
$contextActions = join(',', config('firefly.rule-actions-text'));
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title';
if (RuleGroup::find(Input::get('id'))) {
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval(Input::get('id'));
if (!is_null($repository->find(intval($this->get('id')))->id)) {
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval($this->get('id'));
}
$rules = [

View File

@@ -20,8 +20,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\RuleGroup;
use Input;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
/**
* Class RuleGroupFormRequest
@@ -40,15 +39,27 @@ class RuleGroupFormRequest extends Request
return auth()->check();
}
/**
* @return array
*/
public function getRuleGroupData(): array
{
return [
'title' => trim($this->input('title')),
'description' => trim($this->input('description')),
];
}
/**
* @return array
*/
public function rules()
{
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title';
if (RuleGroup::find(Input::get('id'))) {
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval(Input::get('id'));
/** @var RuleGroupRepositoryInterface $repository */
$repository = app(RuleGroupRepositoryInterface::class, [auth()->user()]);
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title';
if (!is_null($repository->find(intval($this->get('id')))->id)) {
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval($this->get('id'));
}
return [

View File

@@ -13,8 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use Carbon\Carbon;
use FireflyIII\Models\Tag;
use Input;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
/**
* Class TagFormRequest
@@ -38,7 +37,7 @@ class TagFormRequest extends Request
*/
public function collectTagData() :array
{
if (Input::get('setTag') == 'true') {
if ($this->get('setTag') == 'true') {
$latitude = $this->get('latitude');
$longitude = $this->get('longitude');
$zoomLevel = $this->get('zoomLevel');
@@ -69,11 +68,13 @@ class TagFormRequest extends Request
*/
public function rules()
{
$idRule = '';
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag';
if (Tag::find(Input::get('id'))) {
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$idRule = '';
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag';
if (!is_null($repository->find(intval($this->get('id')))->id)) {
$idRule = 'belongsToUser:tags';
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag,' . Input::get('id');
$tagRule = 'required|min:1|uniqueObjectForUser:tags,tag,' . $this->get('id');
}
return [

View File

@@ -25,6 +25,7 @@ use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\User;
/**
@@ -595,13 +596,24 @@ Breadcrumbs::register(
Breadcrumbs::register(
'transactions.show', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) {
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$what = strtolower($journal->transactionType->type);
$breadcrumbs->parent('transactions.index', $what);
$breadcrumbs->push($journal->description, route('transactions.show', [$journal->id]));
}
);
Breadcrumbs::register(
'transactions.convert', function (BreadCrumbGenerator $breadcrumbs, TransactionType $destinationType, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push(
trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]),
route('transactions.convert', [strtolower($destinationType->type), $journal->id])
);
}
);
/**
* SPLIT
*/

View File

@@ -323,6 +323,7 @@ class CsvSetup implements SetupInterface
foreach ($config['column-do-mapping'] as $index => $mustBeMapped) {
if ($mustBeMapped) {
$column = $config['column-roles'][$index] ?? '_ignore';
// is valid column?
@@ -369,6 +370,11 @@ class CsvSetup implements SetupInterface
foreach ($results as $rowIndex => $row) {
// skip first row?
if ($rowIndex === 0 && $config['has-headers']) {
continue;
}
// run specifics here:
// and this is the point where the specifix go to work.
foreach ($config['specifics'] as $name => $enabled) {

View File

@@ -58,4 +58,4 @@ class PresidentsChoice implements SpecificInterface
}
}
}

View File

@@ -14,8 +14,8 @@ declare(strict_types = 1);
namespace FireflyIII\Jobs;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -129,16 +129,16 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
public function handle()
{
// Lookup all journals that match the parameters specified
$journals = $this->collectJournals();
$transactions = $this->collectJournals();
// Find processors for each rule within the current rule group
$processors = $this->collectProcessors();
// Execute the rules for each transaction
foreach ($journals as $journal) {
foreach ($transactions as $transaction) {
/** @var Processor $processor */
foreach ($processors as $processor) {
$processor->handleTransactionJournal($journal);
$processor->handleTransaction($transaction);
// Stop processing this group if the rule specifies 'stop_processing'
if ($processor->getRule()->stop_processing) {
@@ -155,10 +155,10 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
*/
protected function collectJournals()
{
/** @var JournalTaskerInterface $tasker */
$tasker = app(JournalTaskerInterface::class);
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
return $tasker->getJournalsInRange($this->accounts, $this->startDate, $this->endDate);
return $collector->getJournals();
}
/**

View File

@@ -46,13 +46,12 @@ class Note extends Model
/**
* @param $value
*
* @return string
*/
public function getMarkdownAttribute(): string
{
$converter = new CommonMarkConverter;
$converter = new CommonMarkConverter;
return $converter->convertToHtml($this->text);
}
@@ -64,4 +63,4 @@ class Note extends Model
return $this->morphTo();
}
}
}

View File

@@ -23,22 +23,22 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* FireflyIII\Models\PiggyBank
*
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property \Carbon\Carbon $deleted_at
* @property integer $account_id
* @property string $name
* @property float $targetamount
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $targetdate
* @property integer $order
* @property boolean $encrypted
* @property-read Account $account
* @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankRepetition[] $piggyBankRepetitions
* @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankEvent[] $piggyBankEvents
* @property string $reminder
* @property PiggyBankRepetition $currentRep
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property \Carbon\Carbon $deleted_at
* @property integer $account_id
* @property string $name
* @property float $targetamount
* @property \Carbon\Carbon $startdate
* @property \Carbon\Carbon $targetdate
* @property integer $order
* @property boolean $encrypted
* @property-read Account $account
* @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankRepetition[] $piggyBankRepetitions
* @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankEvent[] $piggyBankEvents
* @property string $reminder
* @property PiggyBankRepetition $currentRep
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereUpdatedAt($value)
@@ -54,7 +54,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereDeletedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereEncrypted($value)
* @mixin \Eloquent
* @property boolean $active
* @property boolean $active
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereActive($value)
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Note[] $notes
*/

View File

@@ -58,9 +58,12 @@ class Preference extends Model
$data = Crypt::decrypt($value);
} catch (DecryptException $e) {
Log::error('Could not decrypt preference.', ['id' => $this->id, 'name' => $this->name, 'data' => $value]);
throw new FireflyException('Could not decrypt preference #' . $this->id . '.');
throw new FireflyException(
sprintf('Could not decrypt preference #%d. If this error persists, please run "php artisan cache:clear" on the command line.', $this->id)
);
}
return json_decode($data);
}

View File

@@ -408,7 +408,9 @@ class TransactionJournal extends TransactionJournalSupport
if (!self::isJoined($query, 'transaction_types')) {
$query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
}
$query->whereIn('transaction_types.type', $types);
if (count($types) > 0) {
$query->whereIn('transaction_types.type', $types);
}
}
/**

View File

@@ -15,6 +15,7 @@ namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* FireflyIII\Models\TransactionType
@@ -43,6 +44,25 @@ class TransactionType extends Model
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
/**
* @param string $type
*
* @return Model|null|static
*/
public static function routeBinder(string $type)
{
if (!auth()->check()) {
throw new NotFoundHttpException;
}
$transactionType = self::where('type', $type)->first();
if (!is_null($transactionType)) {
return $transactionType;
}
throw new NotFoundHttpException;
}
/**
* @return bool
*/

View File

@@ -70,7 +70,6 @@ class EventServiceProvider extends ServiceProvider
'FireflyIII\Events\UpdatedTransactionJournal' => // is a Transaction Journal related event.
[
'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@scanBills',
'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@connectToPiggyBank',
'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@processRules',
],

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