Compare commits

...

362 Commits
4.3.0 ... 4.3.1

Author SHA1 Message Date
James Cole
600c3e75bb Merge branch 'release/4.3.1' 2017-01-04 05:06:13 +01:00
James Cole
6349fccd0f Merge pull request #518 from firefly-iii/l10n_develop
New Crowdin translations
2017-01-04 05:05:26 +01:00
James Cole
6ececdad26 New translations 2017-01-04 05:04:11 +01:00
James Cole
c67f1a7b93 New translations 2017-01-04 05:04:05 +01:00
James Cole
8617ea760a Approved. Step name: Proofread 2017-01-04 05:03:56 +01:00
James Cole
41a2406f07 New translations 2017-01-04 05:03:46 +01:00
James Cole
adae8e45a9 New translations 2017-01-04 05:03:42 +01:00
James Cole
e346ae533d New translations 2017-01-04 05:03:35 +01:00
James Cole
31789255c9 New translations 2017-01-04 05:03:19 +01:00
James Cole
dbe6edd133 New translations 2017-01-04 05:03:09 +01:00
James Cole
7cfbcec56e New translations 2017-01-04 05:02:52 +01:00
James Cole
9f9a055f64 New translations 2017-01-04 05:02:28 +01:00
James Cole
d3614d3505 New strings for translation [skip ci] 2017-01-04 04:57:04 +01:00
James Cole
800f67908e New change log. 2017-01-04 04:49:07 +01:00
James Cole
e2c613c422 New config. 2017-01-04 04:48:58 +01:00
James Cole
457037ed99 Should not be a final class, dummy. 2017-01-03 20:05:20 +01:00
James Cole
f9f21efd36 Update config to fix #519 2017-01-03 20:02:48 +01:00
James Cole
2d59b6718d Various code style fixes. 2017-01-03 17:46:08 +01:00
James Cole
0c6d213296 Update scrutiniser configuration. 2017-01-03 17:26:31 +01:00
James Cole
c34fb7f037 Cleaned up the category and budget pie charts. 2017-01-03 17:02:17 +01:00
James Cole
796be319b7 Piggy banks for #511 [skip ci] 2017-01-03 08:27:48 +01:00
James Cole
d28fcdc6a5 Translated 2017-01-02 23:12:00 +01:00
James Cole
d0afcb6cfa New translations 2017-01-02 23:11:57 +01:00
James Cole
7bd4de937a New translations 2017-01-02 23:11:54 +01:00
James Cole
3025693178 New translations 2017-01-02 23:01:50 +01:00
James Cole
c9cc3bf3ff New translations 2017-01-02 22:51:33 +01:00
James Cole
1f670f7a05 New translations 2017-01-02 22:51:31 +01:00
James Cole
48d735b53b New translations 2017-01-02 22:41:29 +01:00
James Cole
b91f6c7ce6 More alignment for #511 2017-01-02 21:04:17 +01:00
James Cole
ad116d1959 Various code cleanup [skip ci] 2017-01-02 20:42:29 +01:00
James Cole
a0de10870d New translations 2017-01-02 20:12:12 +01:00
James Cole
eb0c00896f New translations 2017-01-02 20:01:56 +01:00
James Cole
deccd4e9fe New translations 2017-01-02 19:51:50 +01:00
James Cole
8be4ec08ad New translations 2017-01-02 19:41:48 +01:00
James Cole
59ad0624f2 New translations 2017-01-02 19:32:00 +01:00
James Cole
f0c69ca84f New translations 2017-01-02 19:31:58 +01:00
James Cole
3ba1c07f68 New translations 2017-01-02 19:31:56 +01:00
James Cole
14cd4aaac8 New translations 2017-01-02 19:31:54 +01:00
James Cole
8a1fae5d9d New translations 2017-01-02 19:31:51 +01:00
James Cole
e323f5a2d5 This should fix most amounts for #511 2017-01-02 19:27:27 +01:00
James Cole
c5c1cbd66f New translations 2017-01-02 19:21:45 +01:00
James Cole
4cc9dbbe6a New translations 2017-01-02 19:21:42 +01:00
James Cole
3649991ad6 New translations 2017-01-02 19:11:55 +01:00
James Cole
1d25691aa2 New translations 2017-01-02 19:02:01 +01:00
James Cole
235076b465 New translations 2017-01-02 19:02:00 +01:00
James Cole
c2670fa379 New translations 2017-01-02 19:01:58 +01:00
James Cole
a769a5391d New translations 2017-01-02 19:01:55 +01:00
James Cole
1f58c46f67 New translations 2017-01-02 18:51:46 +01:00
James Cole
f4c9f2d0e7 New translations 2017-01-02 18:51:43 +01:00
James Cole
851b9136fe New translations 2017-01-02 18:51:41 +01:00
James Cole
0fe10e470d New translations 2017-01-02 18:41:49 +01:00
James Cole
8c8ea17fac New translations 2017-01-02 18:41:46 +01:00
James Cole
7c546b8d3a New translations 2017-01-02 18:32:01 +01:00
James Cole
63334a61ad New translations 2017-01-02 18:31:59 +01:00
James Cole
f61e65cf54 New translations 2017-01-02 18:31:56 +01:00
James Cole
05bf752629 Translated 2017-01-02 18:21:54 +01:00
James Cole
5096a90e34 New translations 2017-01-02 18:21:52 +01:00
James Cole
03792b3905 New translations 2017-01-02 18:12:05 +01:00
James Cole
995b049a5f New translations 2017-01-02 18:12:03 +01:00
James Cole
bde37ec2c7 New translations 2017-01-02 18:12:00 +01:00
James Cole
d6b3fe7e1b New translations 2017-01-02 18:02:21 +01:00
James Cole
954b394987 New translations 2017-01-02 15:32:39 +01:00
James Cole
97dae6dde5 New translations 2017-01-02 15:32:37 +01:00
James Cole
fe039500d6 New translations 2017-01-02 15:32:36 +01:00
James Cole
6952957794 New translations 2017-01-02 15:32:34 +01:00
James Cole
01cc97ad55 New translations 2017-01-02 15:32:32 +01:00
James Cole
b5c8e005e2 New translations 2017-01-02 15:32:28 +01:00
James Cole
1c2602438f New translations 2017-01-02 15:32:19 +01:00
James Cole
33da756a2f New translations 2017-01-02 15:32:18 +01:00
James Cole
488d4a38b8 New translations 2017-01-02 15:32:04 +01:00
James Cole
e60f60b0f8 New translations 2017-01-02 15:32:03 +01:00
James Cole
8aa2e3d2f5 Various code cleanup. 2017-01-02 15:22:30 +01:00
James Cole
d5f65e5d07 New translations 2017-01-02 15:22:00 +01:00
James Cole
c8511a6e2a New translations 2017-01-02 15:12:14 +01:00
James Cole
379b15be1d New translations 2017-01-02 14:51:54 +01:00
James Cole
2ee1fea293 New translations 2017-01-02 14:41:48 +01:00
James Cole
4385d71c6f New translations 2017-01-02 14:31:56 +01:00
James Cole
cf6ea64aba New translations 2017-01-02 14:22:06 +01:00
James Cole
101317cb16 New translations 2017-01-02 14:22:04 +01:00
James Cole
5990a21c46 Translated 2017-01-02 14:22:02 +01:00
James Cole
a9bc007327 New translations 2017-01-02 14:22:00 +01:00
James Cole
0c71770b1d New translations 2017-01-02 12:41:57 +01:00
James Cole
5bae7e9bdb New translations 2017-01-02 12:31:51 +01:00
James Cole
1818a596fe Translated 2017-01-02 12:21:47 +01:00
James Cole
8f7541b841 New translations 2017-01-02 12:21:45 +01:00
James Cole
090546cda3 New translations 2017-01-02 12:21:43 +01:00
James Cole
dcd89d38e7 Small JS fixes [skip ci] 2017-01-02 12:18:29 +01:00
James Cole
800478d437 Add support for Polish #517 [skip ci] 2017-01-02 12:18:18 +01:00
James Cole
f797344106 Default values [skip ci] 2017-01-02 12:15:33 +01:00
James Cole
9352ee3e25 Various Javascript fixes. 2017-01-02 12:09:46 +01:00
James Cole
811026dc4a Merge pull request #512 from firefly-iii/l10n_develop
New Crowdin translations
2017-01-02 11:48:57 +01:00
James Cole
479a4dac7b Missing translations [skip ci] #517 2017-01-02 11:41:14 +01:00
James Cole
499fbbeb17 New translations 2017-01-02 11:31:45 +01:00
James Cole
a35bcf6415 New translations 2017-01-02 11:21:43 +01:00
James Cole
818ffdfc85 New translations 2017-01-02 11:02:27 +01:00
James Cole
d5e19c7ac0 New translations 2017-01-02 11:02:26 +01:00
James Cole
37639b0ff4 New translations 2017-01-02 11:02:25 +01:00
James Cole
740d89dce6 New translations 2017-01-02 11:02:23 +01:00
James Cole
4a7b08fc4e New translations 2017-01-02 11:02:21 +01:00
James Cole
48a5f83f00 New translations 2017-01-02 11:02:20 +01:00
James Cole
48819c928d New translations 2017-01-02 11:02:18 +01:00
James Cole
45a6866dd0 New translations 2017-01-02 11:02:16 +01:00
James Cole
6690586406 New translations 2017-01-02 11:02:15 +01:00
James Cole
909e54845c New translations 2017-01-02 11:02:13 +01:00
James Cole
a7204eb9fa New translations 2017-01-02 11:02:10 +01:00
James Cole
6463166c00 New translations 2017-01-02 11:02:07 +01:00
James Cole
f8268a864b This should fix most Javascript errors. 2017-01-02 10:34:01 +01:00
James Cole
721fff29b3 Approved. Step name: Proofread 2017-01-02 10:12:07 +01:00
James Cole
4cf312d3d4 Approved. Step name: Proofread 2017-01-02 10:12:04 +01:00
James Cole
36f1b6a834 New account tests. 2017-01-02 10:05:02 +01:00
James Cole
050d7e8f00 Remove specific commands from composer installation routine. 2017-01-02 09:53:22 +01:00
James Cole
7c5bed2bb5 Fix export controller tests. 2017-01-02 09:23:24 +01:00
James Cole
87316cf7c1 Include local JS directory. [skip ci] 2017-01-02 09:09:37 +01:00
James Cole
f7d61e5a9b Update scrutinizer config. 2017-01-02 09:02:58 +01:00
James Cole
b2030a72a0 Fix some javascript things [skip ci] 2017-01-02 08:46:45 +01:00
James Cole
533797fc9e Update javascript code. 2017-01-02 08:30:20 +01:00
James Cole
5688234b9d New translations 2017-01-02 08:23:00 +01:00
James Cole
9335789362 New translations 2017-01-02 08:22:55 +01:00
James Cole
9e6a2a3fa4 New translations 2017-01-02 08:22:49 +01:00
James Cole
122fc77357 New translations 2017-01-02 08:22:35 +01:00
James Cole
c978e7965f New translations 2017-01-02 08:22:30 +01:00
James Cole
b0e4e24603 New translations 2017-01-02 08:22:25 +01:00
James Cole
56de307a3e New translations 2017-01-02 08:22:18 +01:00
James Cole
e1dd9ed41b New translations 2017-01-02 08:21:54 +01:00
James Cole
17a64764d3 New translations 2017-01-02 08:21:49 +01:00
James Cole
3cd0540474 Missing language lines [skip ci] 2017-01-02 08:13:10 +01:00
James Cole
27cd9fac8a This fixes #513 [skip ci] 2017-01-02 08:07:28 +01:00
James Cole
1d2012cc23 New translations 2017-01-01 20:42:59 +01:00
James Cole
1b00835dd1 New translations 2017-01-01 20:42:54 +01:00
James Cole
413dcf8164 New translations 2017-01-01 20:42:48 +01:00
James Cole
7f17e8fb2f New translations 2017-01-01 20:42:36 +01:00
James Cole
254d8994d0 New translations 2017-01-01 20:42:30 +01:00
James Cole
4f72519ad9 New translations 2017-01-01 20:42:23 +01:00
James Cole
900b246183 New translations 2017-01-01 20:42:17 +01:00
James Cole
abddb29f37 New translations 2017-01-01 20:41:56 +01:00
James Cole
8d429ef753 New translations 2017-01-01 20:41:50 +01:00
James Cole
b7679b5c60 Updated composer.lock. 2017-01-01 20:33:38 +01:00
James Cole
49982d6eb1 Updated various links to reflect the new repository location [skip ci] 2017-01-01 20:32:39 +01:00
James Cole
3191a6c12b Update readme [skip ci] 2017-01-01 20:21:51 +01:00
James Cole
32f8747f2e Update read [skip ci] 2017-01-01 20:14:32 +01:00
James Cole
38e45a62cf New translations 2017-01-01 20:13:12 +01:00
James Cole
c0e2e78780 New translations 2017-01-01 20:13:06 +01:00
James Cole
3fe3ddbc49 New translations 2017-01-01 20:12:59 +01:00
James Cole
5ca532a54a New translations 2017-01-01 20:12:48 +01:00
James Cole
a120df090a New translations 2017-01-01 20:12:43 +01:00
James Cole
22d359503a New translations 2017-01-01 20:12:38 +01:00
James Cole
e8d84abe43 New translations 2017-01-01 20:12:32 +01:00
James Cole
98937cedaa New translations 2017-01-01 20:12:10 +01:00
James Cole
d592d6cd7a New translations 2017-01-01 20:12:04 +01:00
James Cole
0341a04ee3 Warning about locale. [skip ci] 2017-01-01 20:10:30 +01:00
James Cole
540fc4f924 Fix sort [skip ci] 2017-01-01 17:01:29 +01:00
James Cole
04290bf9b6 Fix small issue with piggy banks. 2017-01-01 16:55:18 +01:00
James Cole
ecbc0c1778 Speed up various tests. 2016-12-30 14:24:16 +01:00
James Cole
44b8e48c3a Fix tests. 2016-12-30 13:54:11 +01:00
James Cole
a5036c86dc Updated test database. 2016-12-30 13:47:23 +01:00
James Cole
ac86e75233 Updated various files for #506 2016-12-30 13:45:02 +01:00
James Cole
9ec3febbfa Do not round pie chart values. #506 2016-12-30 12:00:37 +01:00
James Cole
1c5dc6ab6d Don’t round amount when exporting #506 2016-12-30 11:58:23 +01:00
James Cole
abb8eafec2 Fix tests. 2016-12-30 11:57:21 +01:00
James Cole
eb8f5512c5 Charts will respect decimal places #506 2016-12-30 11:51:58 +01:00
James Cole
d146476c91 Sort a nasty bug in budget limit collection #508 2016-12-30 09:02:48 +01:00
James Cole
7a57670925 Fix small bug in database verifier. [skip ci] 2016-12-30 08:51:26 +01:00
James Cole
8a49e98246 Fix small bug in database verifier. [skip ci] 2016-12-30 08:49:27 +01:00
James Cole
cf0845d190 Reinstated limit repetitions call #508 [skip ci] 2016-12-30 08:44:48 +01:00
James Cole
02bbdcc251 Fix reports and charts for #508 2016-12-30 08:41:48 +01:00
James Cole
13f6bd759b Views must pick up on this #508 2016-12-30 07:39:42 +01:00
James Cole
497400587d Update budget limit #508 2016-12-29 20:52:02 +01:00
James Cole
a58cd83ea7 Fixed routes and budgets.index #508 2016-12-29 20:48:33 +01:00
James Cole
3f802fe27a Removed limit repetition events #508 2016-12-29 20:48:12 +01:00
James Cole
6a13dd317d Will also upgrade database. #508 2016-12-29 20:19:20 +01:00
James Cole
a442d3d952 Migration for #508 2016-12-29 19:35:27 +01:00
James Cole
0d4febff85 New test database just to be sure. 2016-12-29 18:20:49 +01:00
James Cole
ba222eaf77 No changes to budget limit for now. 2016-12-29 18:10:29 +01:00
James Cole
b14719464c No changes to budget limit for now. 2016-12-29 18:10:14 +01:00
James Cole
c756b80962 These are changes to budget limit. FF must work with them. 2016-12-29 18:01:06 +01:00
James Cole
a54a886bf0 Chart was negative. [skip ci] 2016-12-29 17:56:12 +01:00
James Cole
dbe9628cc5 Update to budget limits. 2016-12-29 17:43:11 +01:00
James Cole
7a3b39886e Changed to budget controller chart. 2016-12-29 17:42:46 +01:00
James Cole
fab511cc53 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  New translations
  Approved. Step name: Proofread
2016-12-29 10:30:16 +01:00
James Cole
4979d9d0bf Merge pull request #507 from JC5/l10n_develop
New Crowdin translations
2016-12-29 10:29:59 +01:00
James Cole
45914b2e9e First set of views will respect decimal places configuration. 2016-12-29 09:15:50 +01:00
James Cole
1e9aaf2d2a Code cleanup. [skip ci] 2016-12-29 09:05:02 +01:00
James Cole
de56c18c6e Clean up some money formatting routines. 2016-12-29 09:02:23 +01:00
James Cole
eaefb7136a Looks like somebody had to google how the decimal thing works. 2016-12-28 22:21:55 +01:00
James Cole
fe9344cd0a Round on max number of decimals #506 2016-12-28 21:55:46 +01:00
James Cole
f010c17ae6 Up the scales for #506 2016-12-28 21:55:09 +01:00
James Cole
f63cd74965 New translations 2016-12-28 21:52:46 +01:00
James Cole
9e8f8f76a4 New translations 2016-12-28 21:52:43 +01:00
James Cole
d88c6a82d0 New translations 2016-12-28 21:52:41 +01:00
James Cole
a8fdf7ffad New translations 2016-12-28 21:52:36 +01:00
James Cole
245389d74f New translations 2016-12-28 21:52:33 +01:00
James Cole
26933637dd New translations 2016-12-28 21:52:31 +01:00
James Cole
98312ac554 New translations 2016-12-28 21:52:25 +01:00
James Cole
1ba03088c9 New translations 2016-12-28 21:52:21 +01:00
James Cole
c0dfc554b3 New translations 2016-12-28 21:52:18 +01:00
James Cole
5c691491e8 New translations 2016-12-28 21:52:15 +01:00
James Cole
9731b59174 New translations 2016-12-28 21:52:12 +01:00
James Cole
52bf358978 New translations 2016-12-28 21:52:07 +01:00
James Cole
c92a56c980 New translations 2016-12-28 21:52:04 +01:00
James Cole
3142151fc3 New translations 2016-12-28 21:51:47 +01:00
James Cole
fb555f5b96 New translations 2016-12-28 21:51:44 +01:00
James Cole
8f1c693d3d New translations 2016-12-28 21:51:41 +01:00
James Cole
b8a8becd0c New translations 2016-12-28 21:51:39 +01:00
James Cole
b71abd3f6a Approved. Step name: Proofread 2016-12-28 21:51:33 +01:00
James Cole
9ae74b4278 Expand views to allow editing of currency decimals #506 2016-12-28 21:48:37 +01:00
James Cole
bdbf434006 Increase number of decimals. #506 2016-12-28 21:34:02 +01:00
James Cole
1a5e93c739 Merge pull request #505 from JC5/l10n_develop
New Crowdin translations
2016-12-28 19:59:52 +01:00
James Cole
8e42ba74c6 New translations 2016-12-28 19:03:10 +01:00
James Cole
42bb083e99 New translations 2016-12-28 19:03:05 +01:00
James Cole
ae4eecc7f2 New translations 2016-12-28 19:02:59 +01:00
James Cole
c4f25b6191 New translations 2016-12-28 19:02:49 +01:00
James Cole
29b200040f New translations 2016-12-28 19:02:44 +01:00
James Cole
7cb1598fb1 New translations 2016-12-28 19:02:39 +01:00
James Cole
65b6f162d8 New translations 2016-12-28 19:02:31 +01:00
James Cole
c56d2e08f4 New translations 2016-12-28 19:02:10 +01:00
James Cole
ca0a0886b1 New translations 2016-12-28 19:02:04 +01:00
James Cole
e9822ae1a3 Code cleanup. [skip ci] 2016-12-28 19:00:39 +01:00
James Cole
04b284f030 No longer able to manage blocked domains. 2016-12-28 18:57:15 +01:00
James Cole
9ef24c0a43 Registration process no longer cares about blocked domains or previously deleted users. 2016-12-28 18:54:15 +01:00
James Cole
7ee650ba7a Some code cleanup [skip ci] 2016-12-28 18:49:30 +01:00
James Cole
96cafed154 New translations 2016-12-28 18:33:05 +01:00
James Cole
f65c2ff4fb New translations 2016-12-28 18:33:01 +01:00
James Cole
121deec62f New translations 2016-12-28 18:32:56 +01:00
James Cole
838d0808c0 New translations 2016-12-28 18:32:42 +01:00
James Cole
974fbe9e5b New translations 2016-12-28 18:32:37 +01:00
James Cole
f26f94ad3b New translations 2016-12-28 18:32:32 +01:00
James Cole
7410f1944c New translations 2016-12-28 18:32:12 +01:00
James Cole
c34947f657 New translations 2016-12-28 18:32:06 +01:00
James Cole
54092118e1 Approved. Step name: Proofread 2016-12-28 18:31:57 +01:00
James Cole
866a7d7401 Add missing translation [skip ci] 2016-12-28 18:27:32 +01:00
James Cole
32ab916707 Remove debug text [skip ci] 2016-12-28 18:26:44 +01:00
James Cole
1a245f1303 Fix import command instruction [skip ci] #504 2016-12-28 18:22:20 +01:00
James Cole
a23c61ee3c Fix tests 2016-12-28 17:14:32 +01:00
James Cole
f44336f7aa Remove a lot of stuff that deals with user activation. 2016-12-28 17:07:44 +01:00
James Cole
98d4bc48b6 Users can no longer be required to activate their account. They are always activated. 2016-12-28 16:55:53 +01:00
James Cole
a3f1b72bac Admin area reports on less user details. 2016-12-28 16:46:57 +01:00
James Cole
a37f70947b Various code cleanup. 2016-12-28 16:45:44 +01:00
James Cole
71195aa789 Merge pull request #503 from JC5/l10n_develop
New Crowdin translations
2016-12-28 15:16:31 +01:00
James Cole
f6511bed32 Called the wrong method [skip ci] 2016-12-28 13:05:40 +01:00
James Cole
619500ca64 Various cod e clean up. 2016-12-28 13:02:56 +01:00
James Cole
986d290434 New translations 2016-12-28 13:01:42 +01:00
James Cole
878f8c58bb New translations 2016-12-28 12:11:53 +01:00
James Cole
e067da1fe9 Translated 2016-12-28 12:01:50 +01:00
James Cole
f340c636fe New translations 2016-12-28 12:01:48 +01:00
James Cole
ce260a1a1e New translations 2016-12-28 11:51:38 +01:00
James Cole
a21c9f15e3 New translations 2016-12-28 11:41:39 +01:00
James Cole
e64b778d13 Various small fixes. 2016-12-28 11:34:00 +01:00
James Cole
a1f139f62a Translated 2016-12-28 11:31:16 +01:00
James Cole
8ae1d1c963 New translations 2016-12-28 10:23:19 +01:00
James Cole
8f8016179b New translations 2016-12-28 10:23:17 +01:00
James Cole
2e32e994c3 New translations 2016-12-28 10:23:14 +01:00
James Cole
1575e3b045 New translations 2016-12-28 10:23:11 +01:00
James Cole
9ab5f68601 New translations 2016-12-28 10:23:06 +01:00
James Cole
7fcb806dfe New translations 2016-12-28 10:23:02 +01:00
James Cole
5ae736c7cc New translations 2016-12-28 10:22:50 +01:00
James Cole
d77ba9970b New translations 2016-12-28 10:22:46 +01:00
James Cole
49f97a2c7b New translations 2016-12-28 10:22:42 +01:00
James Cole
659ff89062 New translations 2016-12-28 10:22:38 +01:00
James Cole
5529641bea New translations 2016-12-28 10:22:32 +01:00
James Cole
b38f1d7b2a New translations 2016-12-28 10:22:28 +01:00
James Cole
53ba202b14 New translations 2016-12-28 10:22:08 +01:00
James Cole
11cc333de7 New translations 2016-12-28 10:22:04 +01:00
James Cole
70e47ab4d0 New translations 2016-12-28 10:22:00 +01:00
James Cole
1582b35ae2 New translations 2016-12-28 10:21:58 +01:00
James Cole
62c27cee6c Approved. Step name: Proofread 2016-12-28 10:21:50 +01:00
James Cole
81b8bc9e93 Approved. Step name: Proofread 2016-12-28 10:21:47 +01:00
James Cole
49758c4e72 Remove deprecated settings. 2016-12-28 10:16:20 +01:00
James Cole
001ef4fe1c Removed deprecated event and handlers. (store ip addresses) 2016-12-28 10:08:34 +01:00
James Cole
94d0401f4e Removed deprecated event and handlers. (block use of email and deleted user) 2016-12-28 10:07:36 +01:00
James Cole
2dbd9bd0b1 Removed deprecated event and handlers. (block use of domain) 2016-12-28 10:05:37 +01:00
James Cole
9168c97eb6 Removed deprecated event and handlers. (locked out user) 2016-12-28 10:04:09 +01:00
James Cole
758953b6e3 Removed deprecated event and handlers. (blocked user login) 2016-12-28 10:02:43 +01:00
James Cole
8ccdf9ea83 Removed deprecated event and handlers. 2016-12-28 10:00:58 +01:00
James Cole
9c6a3e4ad5 This fixes the tests. 2016-12-28 09:56:07 +01:00
James Cole
6151d4a0ec Flash info message [skip ci] 2016-12-28 09:45:08 +01:00
James Cole
61014d45f4 Optimize view [skip ci] 2016-12-28 06:19:18 +01:00
James Cole
05a93a2426 Add budget information. [skip ci] 2016-12-28 06:14:58 +01:00
James Cole
a4c7412220 Hide more from account overview [skip ci] 2016-12-28 06:12:21 +01:00
James Cole
94e51952f4 More fields for query. 2016-12-28 06:09:48 +01:00
James Cole
ebdd64f46f Fix small issues in the category overview and the journal collector. 2016-12-28 05:48:41 +01:00
James Cole
2dc70ece44 Some tweaks for #502 2016-12-28 05:25:58 +01:00
James Cole
c23ea5ea76 Start of new change log. 2016-12-28 05:17:07 +01:00
James Cole
6521a7c604 This fixes #501 2016-12-28 05:16:55 +01:00
James Cole
02e792148c Forgot sprintf call. 2016-12-27 22:11:29 +01:00
James Cole
69c350dcca Merge pull request #500 from JC5/l10n_develop
New Crowdin translations
2016-12-27 20:52:16 +01:00
James Cole
1aee3d8e2c Various code cleanup 2016-12-27 20:52:00 +01:00
James Cole
02695d852c Various code cleanup 2016-12-27 20:45:23 +01:00
James Cole
7405138489 Various code cleanup 2016-12-27 20:07:28 +01:00
James Cole
4804257fd1 Various code cleanup 2016-12-27 19:59:56 +01:00
James Cole
67f2e3a32a New translations 2016-12-27 19:42:47 +01:00
James Cole
8d709f9cf4 New translations 2016-12-27 19:42:45 +01:00
James Cole
4d3132f1c9 New translations 2016-12-27 19:42:43 +01:00
James Cole
36b44f1814 New translations 2016-12-27 19:42:42 +01:00
James Cole
32761aeda0 New translations 2016-12-27 19:42:40 +01:00
James Cole
851b05c110 New translations 2016-12-27 19:42:38 +01:00
James Cole
997e951aca New translations 2016-12-27 19:42:32 +01:00
James Cole
448dc6b7c6 New translations 2016-12-27 19:42:30 +01:00
James Cole
84458fa46f New translations 2016-12-27 19:42:29 +01:00
James Cole
50bb8a0d91 New translations 2016-12-27 19:42:27 +01:00
James Cole
997b3c3061 New translations 2016-12-27 19:42:26 +01:00
James Cole
4f240c004c New translations 2016-12-27 19:42:24 +01:00
James Cole
597a8d36af New translations 2016-12-27 19:42:22 +01:00
James Cole
cf52a4c5c2 New translations 2016-12-27 19:42:21 +01:00
James Cole
c29180a094 Add support for Russian [skip ci] 2016-12-27 19:39:39 +01:00
James Cole
10f4304559 New demo text for the import routine. 2016-12-27 19:37:05 +01:00
James Cole
30447bcf70 New stubs for demo user. 2016-12-27 19:34:39 +01:00
James Cole
9ff9385c47 Demo user is no longer capable of uploading files. 2016-12-27 19:34:27 +01:00
James Cole
6c5499e848 Small code cleanup. 2016-12-27 19:34:05 +01:00
James Cole
3bacbe8536 Merge pull request #499 from JC5/l10n_develop
New Crowdin translations
2016-12-27 19:12:53 +01:00
James Cole
09c7a69050 New translations 2016-12-27 17:51:54 +01:00
James Cole
5dc727580f New translations 2016-12-27 17:51:53 +01:00
James Cole
248a4ed527 New translations 2016-12-27 17:51:52 +01:00
James Cole
db95185eee New translations 2016-12-27 17:51:50 +01:00
James Cole
85dae15a0d New translations 2016-12-27 17:51:49 +01:00
James Cole
3e61a1e12b New translations 2016-12-27 17:51:45 +01:00
James Cole
6cd4186ac9 Merge pull request #497 from JC5/l10n_develop
New Crowdin translations
2016-12-27 16:00:47 +01:00
James Cole
cbbadc3d6d Less logging in often used class. 2016-12-27 15:54:49 +01:00
James Cole
fc0024faa2 Can now inform about the lack of certain charts [skip ci] 2016-12-27 15:54:36 +01:00
James Cole
0f3d4062d7 Translated 2016-12-27 15:51:14 +01:00
James Cole
7ba8a88989 Merge pull request #496 from JC5/l10n_develop
New Crowdin translations
2016-12-27 15:47:53 +01:00
James Cole
349d254193 This fixes the tests. 2016-12-27 15:46:52 +01:00
James Cole
be201e811d Clean up report code. 2016-12-27 15:31:17 +01:00
James Cole
84a032fbb4 New translations 2016-12-27 13:12:24 +01:00
James Cole
4815602558 New translations 2016-12-27 13:12:22 +01:00
James Cole
e4b83392be New translations 2016-12-27 13:12:21 +01:00
James Cole
0658c17adb New translations 2016-12-27 13:12:20 +01:00
James Cole
bdc72aee42 New translations 2016-12-27 13:12:18 +01:00
James Cole
689d91e30f New translations 2016-12-27 13:12:17 +01:00
James Cole
6b785e4318 New translations 2016-12-27 13:12:14 +01:00
James Cole
f46cf55912 New translations 2016-12-27 13:12:13 +01:00
James Cole
d520849ce1 Some more demo text [skip ci] 2016-12-27 13:06:41 +01:00
James Cole
50661bbb3b Some more demo text [skip ci] 2016-12-27 13:05:31 +01:00
James Cole
d2d5b1ac76 More subtle currency warning [skip ci] 2016-12-27 11:08:52 +01:00
James Cole
244972e0f8 Better category overview [skip ci] 2016-12-27 11:02:14 +01:00
James Cole
f80e6c2efa New translations 2016-12-27 10:52:55 +01:00
James Cole
e9e32eda3c New translations 2016-12-27 10:52:50 +01:00
James Cole
73844e223f New translations 2016-12-27 10:52:46 +01:00
James Cole
6583a6d9c6 New translations 2016-12-27 10:52:36 +01:00
James Cole
ca4824adcd New translations 2016-12-27 10:52:27 +01:00
James Cole
80b5cc08bb New translations 2016-12-27 10:52:21 +01:00
James Cole
afbcc79a06 New translations 2016-12-27 10:52:12 +01:00
James Cole
3371bd2e04 New translations 2016-12-27 10:52:01 +01:00
James Cole
5efdf53c06 ShowAll method for category 2016-12-27 10:46:11 +01:00
James Cole
c9112de8ba New translations 2016-12-27 10:03:05 +01:00
James Cole
fd4b589a13 New translations 2016-12-27 10:03:00 +01:00
James Cole
df813dbac9 New translations 2016-12-27 10:02:50 +01:00
James Cole
004fb362ec New translations 2016-12-27 10:02:42 +01:00
James Cole
3cd749753a New translations 2016-12-27 10:02:37 +01:00
James Cole
c7964f7693 New translations 2016-12-27 10:02:30 +01:00
James Cole
57bba2fd3f New translations 2016-12-27 10:02:21 +01:00
James Cole
04c9b2a7a8 Approved. Step name: Proofread 2016-12-27 10:02:05 +01:00
James Cole
b9d142c2b7 Small view updates [skip ci] 2016-12-27 09:56:30 +01:00
James Cole
6ab52e282f Merge pull request #495 from JC5/l10n_develop
New Crowdin translations
2016-12-26 15:04:15 +01:00
James Cole
b14adf8c3f New translations 2016-12-26 12:42:09 +01:00
James Cole
4e0b162f5f New translations 2016-12-26 12:42:08 +01:00
James Cole
62d47ff7f0 New translations 2016-12-26 12:42:07 +01:00
James Cole
7f025380f0 New translations 2016-12-26 12:42:06 +01:00
James Cole
7d1e981bca New translations 2016-12-26 12:42:04 +01:00
James Cole
a08103f996 New translations 2016-12-26 12:42:03 +01:00
James Cole
dd4991a4f8 New translations 2016-12-26 12:42:00 +01:00
James Cole
5442292d23 New translations 2016-12-26 12:41:59 +01:00
James Cole
3f050d3d03 Some demo page text [skip ci] 2016-12-26 12:32:48 +01:00
320 changed files with 6694 additions and 5037 deletions

View File

@@ -40,6 +40,7 @@ SHOW_INCOMPLETE_TRANSLATIONS=false
CACHE_PREFIX=firefly
GOOGLE_MAPS_API_KEY=
ANALYTICS_ID=
SITE_OWNER=mail@example.com
@@ -48,4 +49,5 @@ PUSHER_SECRET=
PUSHER_APP_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
DEMO_PASSWORD=

1
.github/CONTRIBUTING vendored Normal file
View File

@@ -0,0 +1 @@
If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/) first. Thanks!

View File

@@ -2,7 +2,50 @@
tools:
external_code_coverage: false
filter:
excluded_paths:
- app/Support/Migration/*
- app/database/migrations/*
- database/migrations/*
paths:
- app/*
- public/js/ff/*
excluded_paths:
- "database/migrations/*"
- "bootstrap/*"
- "config/*"
- "docker/*"
- "public/js/lib/*"
- "public/lib/adminlte/js/*"
- "public/lib/bootstrap/js/*"
- "resources/*"
- "routes/*"
- "storage/*"
checks:
php:
use_self_instead_of_fqcn: true
uppercase_constants: true
return_doc_comments: true
return_doc_comment_if_not_inferrable: true
remove_extra_empty_lines: true
parameter_doc_comments: true
optional_parameters_at_the_end: true
no_short_variable_names:
minimum: '3'
no_short_method_names:
minimum: '3'
no_long_variable_names:
maximum: '20'
no_goto: true
newline_at_end_of_file: true
encourage_single_quotes: true
avoid_todo_comments: true
avoid_perl_style_comments: true
avoid_fixme_comments: true
avoid_multiple_statements_on_same_line: true
align_assignments: true
duplication: false
javascript: true
coding_style:
php:
spaces:
around_operators:
concatenation: true
other:
after_type_cast: false

View File

@@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [4.3.1] - 2017-01-04
### Added
- Support for Russian and Polish.
- Support for a proper demo website.
- Support for custom decimal places in currencies (#506, suggested by @xpfgsyb).
- Most amounts are now right-aligned (#511, suggested by @xpfgsyb).
- German is now a "complete" language, more than 75% translated!
### Changed
- **[New Github repository!](github.com/firefly-iii/firefly-iii)**
- Better category overview.
- #502, thanks to @zjean
### Removed
- Removed a lot of administration functions.
- Removed ability to activate users.
### Fixed
- #501, thanks to @zjean
- #513, thanks to @skibbipl
### Security
- #519, thanks to @xpfgsyb
## [4.3.0] - 2015-12-26
### Added
- New method of keeping track of available budget, see issue #489
@@ -279,7 +303,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
- Bug in the mass edit routines.
- Firefly III over a proxy will now work (see [issue #290](https://github.com/JC5/firefly-iii/issues/290)), thanks @dfiel for reporting.
- Firefly III over a proxy will now work (see [issue #290](https://github.com/firefly-iii/firefly-iii/issues/290)), thanks @dfiel for reporting.
- Sneaky bug in the import routine, fixed by @Bonno
## [3.10.1] - 2016-08-25

View File

@@ -1,15 +1,11 @@
# Firefly III: A personal finances manager
[![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master)
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
[![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [![Build Status](https://travis-ci.org/firefly-iii/firefly-iii.svg?branch=master)](https://travis-ci.org/firefly-iii/firefly-iii) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
[![The index of Firefly III](https://i.nder.be/hurdhgyg/400)](https://i.nder.be/h2b37243) [![The account overview of Firefly III](https://i.nder.be/hnkfkdpr/400)](https://i.nder.be/hv70pbwc)
[![The useful financial reports of Firefly III](https://i.nder.be/h7sk6nb7/400)](https://i.nder.be/ccn0u2mp) [![Even more useful reports in Firefly III](https://i.nder.be/g237hr35/400)](https://i.nder.be/gm8hbh7z)
_(You can click on the images for a better view)_
"Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money.
## Try it out!

View File

@@ -15,6 +15,8 @@ namespace FireflyIII\Console\Commands;
use DB;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command;
@@ -57,8 +59,29 @@ class UpgradeDatabase extends Command
public function handle()
{
$this->setTransactionIdentifier();
$this->migrateRepetitions();
}
private function migrateRepetitions()
{
if (!Schema::hasTable('budget_limits')) {
return;
}
// get all budget limits with end_date NULL
$set = BudgetLimit::whereNull('end_date')->get();
$this->line(sprintf('Found %d budget limit(s) to update', $set->count()));
/** @var BudgetLimit $budgetLimit */
foreach ($set as $budgetLimit) {
// get limit repetition (should be just one):
/** @var LimitRepetition $repetition */
$repetition = $budgetLimit->limitrepetitions()->first();
if (!is_null($repetition)) {
$budgetLimit->end_date = $repetition->enddate;
$budgetLimit->save();
$this->line(sprintf('Updated budget limit #%d', $budgetLimit->id));
$repetition->delete();
}
}
}
/**
@@ -85,42 +108,52 @@ class UpgradeDatabase extends Command
$journalIds = array_unique($result->pluck('id')->toArray());
foreach ($journalIds as $journalId) {
// grab all positive transactiosn from this journal that are not deleted.
// for each one, grab the negative opposing one which has 0 as an identifier and give it the same identifier.
$identifier = 0;
$processed = [];
$transactions = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
// find opposing:
$amount = bcmul(strval($transaction->amount), '-1');
$this->updateJournal(intval($journalId));
}
}
try {
/** @var Transaction $opposing */
$opposing = Transaction::where('transaction_journal_id', $journalId)
->where('amount', $amount)->where('identifier', '=', 0)
->whereNotIn('id', $processed)
->first();
} catch (QueryException $e) {
Log::error($e->getMessage());
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
$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;
}
if (!is_null($opposing)) {
// give both a new identifier:
$transaction->identifier = $identifier;
$transaction->save();
$opposing->identifier = $identifier;
$opposing->save();
$processed[] = $transaction->id;
$processed[] = $opposing->id;
$this->line(sprintf('Database upgrade for journal #%d, transactions #%d and #%d', $journalId, $transaction->id, $opposing->id));
}
$identifier++;
/**
* grab all positive transactiosn from this journal that are not deleted. for each one, grab the negative opposing one
* which has 0 as an identifier and give it the same identifier.
*
* @param int $journalId
*/
private function updateJournal(int $journalId)
{
$identifier = 0;
$processed = [];
$transactions = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
// find opposing:
$amount = bcmul(strval($transaction->amount), '-1');
try {
/** @var Transaction $opposing */
$opposing = Transaction::where('transaction_journal_id', $journalId)
->where('amount', $amount)->where('identifier', '=', 0)
->whereNotIn('id', $processed)
->first();
} catch (QueryException $e) {
Log::error($e->getMessage());
$this->error('Firefly III could not find the "identifier" field in the "transactions" table.');
$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.');
return;
}
if (!is_null($opposing)) {
// give both a new identifier:
$transaction->identifier = $identifier;
$transaction->save();
$opposing->identifier = $identifier;
$opposing->save();
$processed[] = $transaction->id;
$processed[] = $opposing->id;
$this->line(sprintf('Database upgrade for journal #%d, transactions #%d and #%d', $journalId, $transaction->id, $opposing->id));
}
$identifier++;
}
}
}

View File

@@ -63,20 +63,21 @@ class UpgradeFireflyInstructions extends Command
}
if (is_null($text)) {
$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('+------------------------------------------------------------------------------+');
$this->line('');
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
$this->info(wordwrap($text));
$this->line('');
$this->line('+------------------------------------------------------------------------------+');
return;
}
$this->line('+------------------------------------------------------------------------------+');
$this->line('');
$this->line(sprintf('Thank you for installing Firefly III, v%s', $version));
$this->info(wordwrap($text));
$this->line('');
$this->line('+------------------------------------------------------------------------------+');
}
}

View File

@@ -130,10 +130,9 @@ class VerifyDatabase extends Command
/** @var Budget $entry */
foreach ($set as $entry) {
$name = $entry->encrypted ? Crypt::decrypt($entry->name) : $entry->name;
$line = sprintf(
'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.',
$entry->user_id, $entry->email, $entry->id, $name
$entry->user_id, $entry->email, $entry->id, $entry->name
);
$this->line($line);
}
@@ -174,10 +173,8 @@ class VerifyDatabase extends Command
$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],
];

View File

@@ -21,7 +21,6 @@ use FireflyIII\Console\Commands\ScanAttachments;
use FireflyIII\Console\Commands\UpgradeDatabase;
use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
use FireflyIII\Console\Commands\VerifyDatabase;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
/**
@@ -76,15 +75,4 @@ class Kernel extends ConsoleKernel
{
require base_path('routes/console.php');
}
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
*
* @return void
*/
protected function schedule(Schedule $schedule)
{
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,50 +0,0 @@
<?php
/**
* StoredBudgetLimit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Events;
use Carbon\Carbon;
use FireflyIII\Models\BudgetLimit;
use Illuminate\Queue\SerializesModels;
/**
* Class StoredBudgetLimit
*
* @package FireflyIII\Events
*/
class StoredBudgetLimit extends Event
{
use SerializesModels;
/** @var BudgetLimit */
public $budgetLimit;
/** @var Carbon */
public $end; // the only variable we can't get from the budget limit (if necessary).
/**
* BudgetLimitEvents constructor.
*
* @param BudgetLimit $budgetLimit
* @param Carbon $end
*/
public function __construct(BudgetLimit $budgetLimit, Carbon $end)
{
//
$this->budgetLimit = $budgetLimit;
$this->end = $end;
}
}

View File

@@ -1,50 +0,0 @@
<?php
/**
* UpdatedBudgetLimit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Events;
use Carbon\Carbon;
use FireflyIII\Models\BudgetLimit;
use Illuminate\Queue\SerializesModels;
/**
* Class UpdatedBudgetLimit
*
* @package FireflyIII\Events
*/
class UpdatedBudgetLimit extends Event
{
use SerializesModels;
/** @var BudgetLimit */
public $budgetLimit;
/** @var Carbon */
public $end; // the only variable we can't get from the budget limit (if necessary).
/**
* BudgetLimitEvents constructor.
*
* @param BudgetLimit $budgetLimit
* @param Carbon $end
*/
public function __construct(BudgetLimit $budgetLimit, Carbon $end)
{
//
$this->budgetLimit = $budgetLimit;
$this->end = $end;
}
}

View File

@@ -21,6 +21,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Validation\ValidationException as ValException;
use Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
/**
@@ -72,6 +73,7 @@ class Handler extends ExceptionHandler
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param Exception $exception
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
*
* @return void
*/
@@ -98,8 +100,8 @@ class Handler extends ExceptionHandler
];
// create job that will mail.
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$job = new MailError($userData, env('SITE_OWNER', ''), $ip, $data);
$ipAddress = Request::ip() ?? '0.0.0.0';
$job = new MailError($userData, env('SITE_OWNER', ''), $ipAddress, $data);
dispatch($job);
}

View File

@@ -35,6 +35,8 @@ interface CollectorInterface
/**
* @param Collection $entries
*
* @return void
*
*/
public function setEntries(Collection $entries);

View File

@@ -287,7 +287,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac
}
/**
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
private function getWorkSet()
{

View File

@@ -29,6 +29,7 @@ use Crypt;
*
*
* Class Entry
* @SuppressWarnings(PHPMD.LongVariable)
*
* @package FireflyIII\Export\Entry
*/
@@ -71,33 +72,21 @@ final class Entry
*/
public static function fromObject($object): Entry
{
$entry = new self;
// 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;
// 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;
// destination information
$entry = new self;
$entry->journal_id = $object->transaction_journal_id;
$entry->description = self::decrypt($object->journal_encrypted, $object->journal_description);
$entry->amount = $object->amount;
$entry->date = $object->date;
$entry->transaction_type = $object->transaction_type;
$entry->currency_code = $object->transaction_currency_code;
$entry->source_account_id = $object->account_id;
$entry->source_account_name = self::decrypt($object->account_name_encrypted, $object->account_name);
$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;
// 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 ?? '';
$entry->destination_account_name = self::decrypt($object->opposing_account_encrypted, $object->opposing_account_name);
$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) {
@@ -107,4 +96,19 @@ final class Entry
return $entry;
}
/**
* @param int $isEncrypted
* @param $value
*
* @return string
*/
protected static function decrypt(int $isEncrypted, $value)
{
if ($isEncrypted === 1) {
return Crypt::decrypt($value);
}
return $value;
}
}

View File

@@ -40,6 +40,8 @@ interface ExporterInterface
/**
* @param Collection $entries
*
* @return void
*
*/
public function setEntries(Collection $entries);

View File

@@ -155,7 +155,7 @@ class Processor implements ProcessorInterface
$zip->close();
// delete the files:
$this->deleteFiles($disk);
$this->deleteFiles();
return true;
}
@@ -183,10 +183,11 @@ class Processor implements ProcessorInterface
}
/**
* @param FilesystemAdapter $disk
*
*/
private function deleteFiles(FilesystemAdapter $disk)
private function deleteFiles()
{
$disk = Storage::disk('export');
foreach ($this->getFiles() as $file) {
$disk->delete($file);
}

View File

@@ -27,6 +27,7 @@ interface ProcessorInterface
* Processor constructor.
*
* @param array $settings
*
*/
public function __construct(array $settings);

View File

@@ -49,6 +49,7 @@ class ChartJsGenerator implements GeneratorInterface
* ]
* ]
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five.
*
* @param array $data
*
@@ -58,7 +59,7 @@ class ChartJsGenerator implements GeneratorInterface
{
reset($data);
$first = current($data);
$labels = array_keys($first['entries']);
$labels = is_array($first['entries']) ? array_keys($first['entries']) : [];
$chartData = [
'count' => count($data),
@@ -111,7 +112,7 @@ class ChartJsGenerator implements GeneratorInterface
$value = bcmul($value, '-1');
}
$chartData['datasets'][0]['data'][] = round($value, 2);
$chartData['datasets'][0]['data'][] = $value;
$chartData['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
$chartData['labels'][] = $key;
$index++;

View File

@@ -16,7 +16,7 @@ namespace FireflyIII\Generator\Report\Audit;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use Illuminate\Support\Collection;
@@ -49,33 +49,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
/** @var Account $account */
foreach ($this->accounts as $account) {
// balance the day before:
$id = $account->id;
$dayBeforeBalance = Steam::balance($account, $dayBefore);
$collector = new JournalCollector(auth()->user());
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end);
$journals = $collector->getJournals();
$journals = $journals->reverse();
$startBalance = $dayBeforeBalance;
/** @var Transaction $journal */
foreach ($journals as $transaction) {
$transaction->before = $startBalance;
$transactionAmount = $transaction->transaction_amount;
$newBalance = bcadd($startBalance, $transactionAmount);
$transaction->after = $newBalance;
$startBalance = $newBalance;
}
/*
* Reverse set again.
*/
$auditData[$id]['journals'] = $journals->reverse();
$auditData[$id]['exists'] = $journals->count() > 0;
$auditData[$id]['end'] = $this->end->formatLocalized(strval(trans('config.month_and_day')));
$auditData[$id]['endBalance'] = Steam::balance($account, $this->end);
$auditData[$id]['dayBefore'] = $dayBefore->formatLocalized(strval(trans('config.month_and_day')));
$auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
$id = $account->id;
$auditData[$id] = $this->getAuditReport($account, $dayBefore);
}
$defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
@@ -153,4 +128,46 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this;
}
/**
* @param Account $account
* @param Carbon $date
*
* @return array
*/
private function getAuditReport(Account $account, Carbon $date): array
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end);
$journals = $collector->getJournals();
$journals = $journals->reverse();
$dayBeforeBalance = Steam::balance($account, $date);
$startBalance = $dayBeforeBalance;
/** @var Transaction $journal */
foreach ($journals as $transaction) {
$transaction->before = $startBalance;
$transactionAmount = $transaction->transaction_amount;
$newBalance = bcadd($startBalance, $transactionAmount);
$transaction->after = $newBalance;
$startBalance = $newBalance;
}
/*
* Reverse set again.
*/
$return = [
'journals' => $journals->reverse(),
'exists' => $journals->count() > 0,
'end' => $this->end->formatLocalized(strval(trans('config.month_and_day'))),
'endBalance' => Steam::balance($account, $this->end),
'dayBefore' => $date->formatLocalized(strval(trans('config.month_and_day'))),
'dayBeforeBalance' => $dayBeforeBalance,
];
return $return;
}
}

View File

@@ -17,7 +17,7 @@ namespace FireflyIII\Generator\Report\Budget;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Generator\Report\Support;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
@@ -184,7 +184,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $this->expenses;
}
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::WITHDRAWAL])
->setBudgets($this->budgets)->withOpposingAccount()->disableFilter();

View File

@@ -17,7 +17,7 @@ namespace FireflyIII\Generator\Report\Category;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Generator\Report\Support;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
@@ -194,7 +194,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $this->expenses;
}
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setCategories($this->categories)->withOpposingAccount()->disableFilter();
@@ -216,7 +217,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $this->income;
}
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->setCategories($this->categories)->withOpposingAccount();
@@ -229,6 +231,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
* @param array $spent
* @param array $earned
*

View File

@@ -34,27 +34,7 @@ class Support
*/
public static function filterExpenses(Collection $collection, array $accounts): Collection
{
$result = $collection->filter(
function (Transaction $transaction) use ($accounts) {
$opposing = $transaction->opposing_account_id;
// remove internal transfer
if (in_array($opposing, $accounts)) {
Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id));
return null;
}
// remove positive amount
if (bccomp($transaction->transaction_amount, '0') === 1) {
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
return null;
}
return $transaction;
}
);
return $result;
return self::filterTransactions($collection, $accounts, 1);
}
/**
@@ -64,9 +44,21 @@ class Support
* @return Collection
*/
public static function filterIncome(Collection $collection, array $accounts): Collection
{
return self::filterTransactions($collection, $accounts, -1);
}
/**
* @param Collection $collection
* @param array $accounts
* @param int $modifier
*
* @return Collection
*/
public static function filterTransactions(Collection $collection, array $accounts, int $modifier): Collection
{
$result = $collection->filter(
function (Transaction $transaction) use ($accounts) {
function (Transaction $transaction) use ($accounts, $modifier) {
$opposing = $transaction->opposing_account_id;
// remove internal transfer
if (in_array($opposing, $accounts)) {
@@ -75,7 +67,7 @@ class Support
return null;
}
// remove positive amount
if (bccomp($transaction->transaction_amount, '0') === -1) {
if (bccomp($transaction->transaction_amount, '0') === $modifier) {
Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount));
return null;

View File

@@ -1,93 +0,0 @@
<?php
/**
* BudgetEventHandler.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\Handlers\Events;
use Carbon\Carbon;
use FireflyIII\Events\StoredBudgetLimit;
use FireflyIII\Events\UpdatedBudgetLimit;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Database\QueryException;
use Log;
/**
* Handles budget related events.
*
* Class BudgetEventHandler
*
* @package FireflyIII\Handlers\Events
*/
class BudgetEventHandler
{
/**
* This method creates a new budget limit repetition when a new budget limit has been created.
*
* @param StoredBudgetLimit $budgetLimitEvent
*
* @return bool
*/
public function storeRepetition(StoredBudgetLimit $budgetLimitEvent): bool
{
return $this->processRepetitionChange($budgetLimitEvent->budgetLimit, $budgetLimitEvent->end);
}
/**
* Updates, if present the budget limit repetition part of a budget limit.
*
* @param UpdatedBudgetLimit $budgetLimitEvent
*
* @return bool
*/
public function updateRepetition(UpdatedBudgetLimit $budgetLimitEvent): bool
{
return $this->processRepetitionChange($budgetLimitEvent->budgetLimit, $budgetLimitEvent->end);
}
/**
* @param BudgetLimit $budgetLimit
* @param Carbon $date
*
* @return bool
*/
private function processRepetitionChange(BudgetLimit $budgetLimit, Carbon $date): bool
{
$set = $budgetLimit->limitrepetitions()
->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00'))
->where('enddate', $date->format('Y-m-d 00:00:00'))
->get();
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $date;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);
try {
$repetition->save();
} catch (QueryException $e) {
Log::error('Trying to save new LimitRepetition failed: ' . $e->getMessage());
}
}
if ($set->count() == 1) {
$repetition = $set->first();
$repetition->amount = $budgetLimit->amount;
$repetition->save();
}
return true;
}
}

View File

@@ -16,6 +16,7 @@ namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\StoredTransactionJournal;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionJournal;
@@ -33,79 +34,40 @@ class StoredJournalEventHandler
/**
* This method connects a new transfer to a piggy bank.
*
* @param StoredTransactionJournal $storedJournalEvent
* @param StoredTransactionJournal $event
*
* @return bool
*/
public function connectToPiggyBank(StoredTransactionJournal $storedJournalEvent): bool
public function connectToPiggyBank(StoredTransactionJournal $event): bool
{
/** @var TransactionJournal $journal */
$journal = $storedJournalEvent->journal;
$piggyBankId = $storedJournalEvent->piggyBankId;
$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.*']);
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')));
/*
* Verify existence of piggy bank:
*/
if (!$this->verifyExistence($event)) {
Log::error(sprintf('No such piggy bank or no 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;
}
}
/*
* Get relevant data:
*/
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
$amount = $this->getExactAmount($journal, $piggyBank, $repetition);
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
$repetition->save();
/** @var PiggyBankEvent $storedJournalEvent */
$storedJournalEvent = PiggyBankEvent::create(
/** @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', $storedJournalEvent->id));
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
return true;
}
@@ -161,4 +123,81 @@ class StoredJournalEventHandler
return true;
}
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 6 but I can live with it.
* @param TransactionJournal $journal
* @param PiggyBank $piggyBank
* @param PiggyBankRepetition $repetition
*
* @return string
*/
private function getExactAmount(TransactionJournal $journal, PiggyBank $piggyBank, PiggyBankRepetition $repetition): string
{
$amount = TransactionJournal::amountPositive($journal);
$sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray();
$room = bcsub(strval($piggyBank->targetamount), strval($repetition->currentamount));
$compare = bcmul($repetition->currentamount, '-1');
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
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 && bccomp($room, $amount) === -1) {
// amount is positive and $room is smaller than $amount
Log::debug(sprintf('Room in piggy bank for extra money is %f', $room));
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));
return $room;
}
// amount is negative and $currentamount is smaller than $amount
if (bccomp($amount, '0') === -1 && bccomp($compare, $amount) === 1) {
Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount));
Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name));
Log::debug(sprintf('New amount is %f', $compare));
return $compare;
}
return $amount;
}
/**
* @param StoredTransactionJournal $event
*
* @return bool
*/
private function verifyExistence(StoredTransactionJournal $event): bool
{
/** @var TransactionJournal $journal */
$journal = $event->journal;
$piggyBankId = $event->piggyBankId;
/** @var PiggyBank $piggyBank */
$piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank)) {
Log::error('No such piggy bank!');
return false;
}
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 false;
}
return true;
}
}

View File

@@ -13,25 +13,12 @@ declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use Exception;
use FireflyConfig;
use FireflyIII\Events\BlockedBadLogin;
use FireflyIII\Events\BlockedUseOfDomain;
use FireflyIII\Events\BlockedUseOfEmail;
use FireflyIII\Events\BlockedUserLogin;
use FireflyIII\Events\ConfirmedUser;
use FireflyIII\Events\DeletedUser;
use FireflyIII\Events\LockedOutUser;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Events\ResentConfirmation;
use FireflyIII\Models\Configuration;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Preferences;
use Session;
use Swift_TransportException;
@@ -42,7 +29,6 @@ use Swift_TransportException;
*
* The method name reflects what is being done. This is in the present tense.
*
*
* @package FireflyIII\Handlers\Events
*/
class UserEventHandler
@@ -82,237 +68,6 @@ class UserEventHandler
return true;
}
/**
* @param BlockedBadLogin $event
*
* @return bool
*/
public function reportBadLogin(BlockedBadLogin $event)
{
$email = $event->email;
$owner = env('SITE_OWNER');
$ipAddress = $event->ipAddress;
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_bad_login', config('firefly.configuration.mail_for_bad_login'));
Log::debug(sprintf('Now in reportBadLogin for email address %s', $email));
Log::error(sprintf('User %s tried to login with bad credentials.', $email));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
try {
Mail::send(
['emails.blocked-bad-creds-html', 'emails.blocked-bad-creds-text'], ['email' => $email, 'ip' => $ipAddress],
function (Message $message) use ($owner) {
$message->to($owner, $owner)->subject('Blocked login attempt with bad credentials');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param BlockedUserLogin $event
*
* @return bool
*/
public function reportBlockedUser(BlockedUserLogin $event): bool
{
$user = $event->user;
$owner = env('SITE_OWNER');
$email = $user->email;
$ipAddress = $event->ipAddress;
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_blocked_login', config('firefly.configuration.mail_for_blocked_login'));
Log::debug(sprintf('Now in reportBlockedUser for email address %s', $email));
Log::error(sprintf('User #%d (%s) has their accout blocked (blocked_code is "%s") but tried to login.', $user->id, $email, $user->blocked_code));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
// send email message:
try {
Mail::send(
['emails.blocked-login-html', 'emails.blocked-login-text'],
[
'user_id' => $user->id,
'user_address' => $email,
'ip' => $ipAddress,
'code' => $user->blocked_code,
], function (Message $message) use ($owner, $user) {
$message->to($owner, $owner)->subject('Blocked login attempt of blocked user');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param LockedOutUser $event
*
* @return bool
*/
public function reportLockout(LockedOutUser $event): bool
{
$email = $event->email;
$owner = env('SITE_OWNER');
$ipAddress = $event->ipAddress;
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_lockout', config('firefly.configuration.mail_for_lockout'));
Log::debug(sprintf('Now in respondToLockout for email address %s', $email));
Log::error(sprintf('User %s was locked out after too many invalid login attempts.', $email));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
// send email message:
try {
Mail::send(
['emails.locked-out-html', 'emails.locked-out-text'], ['email' => $email, 'ip' => $ipAddress], function (Message $message) use ($owner) {
$message->to($owner, $owner)->subject('User was locked out');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param BlockedUseOfDomain $event
*
* @return bool
*/
public function reportUseBlockedDomain(BlockedUseOfDomain $event): bool
{
$email = $event->email;
$owner = env('SITE_OWNER');
$ipAddress = $event->ipAddress;
$parts = explode('@', $email);
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_blocked_domain', config('firefly.configuration.mail_for_blocked_domain'));
Log::debug(sprintf('Now in reportUseBlockedDomain for email address %s', $email));
Log::error(sprintf('Somebody tried to register using an email address (%s) connected to a banned domain (%s).', $email, $parts[1]));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
// send email message:
try {
Mail::send(
['emails.blocked-registration-html', 'emails.blocked-registration-text'],
[
'email_address' => $email,
'blocked_domain' => $parts[1],
'ip' => $ipAddress,
], function (Message $message) use ($owner) {
$message->to($owner, $owner)->subject('Blocked registration attempt with blocked domain');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param BlockedUseOfEmail $event
*
* @return bool
*/
public function reportUseOfBlockedEmail(BlockedUseOfEmail $event): bool
{
$email = $event->email;
$owner = env('SITE_OWNER');
$ipAddress = $event->ipAddress;
/** @var Configuration $sendmail */
$sendmail = FireflyConfig::get('mail_for_blocked_email', config('firefly.configuration.mail_for_blocked_email'));
Log::debug(sprintf('Now in reportUseOfBlockedEmail for email address %s', $email));
Log::error(sprintf('Somebody tried to register using email address %s which is blocked (SHA2 hash).', $email));
if (is_null($sendmail) || (!is_null($sendmail) && $sendmail->data === false)) {
return true;
}
// send email message:
try {
Mail::send(
['emails.blocked-email-html', 'emails.blocked-email-text'],
[
'user_address' => $email,
'ip' => $ipAddress,
], function (Message $message) use ($owner) {
$message->to($owner, $owner)->subject('Blocked registration attempt with blocked email address');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
return true;
}
/**
* @param DeletedUser $event
*
* @return bool
*/
public function saveEmailAddress(DeletedUser $event): bool
{
Preferences::mark();
$email = hash('sha256', $event->email);
Log::debug(sprintf('Hash of email is %s', $email));
/** @var Configuration $configuration */
$configuration = FireflyConfig::get('deleted_users', []);
$content = $configuration->data;
if (!is_array($content)) {
$content = [];
}
$content[] = $email;
$configuration->data = $content;
Log::debug('New content of deleted_users is ', $content);
FireflyConfig::set('deleted_users', $content);
Preferences::mark();
return true;
}
/**
* This method will send a newly registered user a confirmation message, urging him or her to activate their account.
*
* @param RegisteredUser $event
*
* @return bool
*/
public function sendConfirmationMessage(RegisteredUser $event): bool
{
return $this->sendConfirmation($event->user, $event->ipAddress);
}
/**
* If the user has somehow lost his or her confirmation message, this event will send it to the user again.
*
* At the moment, this method is exactly the same as the ::sendConfirmationMessage method, but that will change.
*
* @param ResentConfirmation $event
*
* @return bool
*/
function sendConfirmationMessageAgain(ResentConfirmation $event): bool
{
return $this->sendConfirmation($event->user, $event->ipAddress);
}
/**
* @param RequestedNewPassword $event
*
@@ -372,75 +127,4 @@ class UserEventHandler
return true;
}
/**
* When the user is confirmed, this method stores the IP address of the user
* as a preference. Since this preference cannot be edited, it is effectively hidden
* from the user yet stored conveniently.
*
* @param ConfirmedUser $event
*
* @return bool
*/
public function storeConfirmationIpAddress(ConfirmedUser $event): bool
{
Preferences::setForUser($event->user, 'confirmation_ip_address', $event->ipAddress);
return true;
}
/**
* This message stores the users IP address on registration, in much the same
* fashion as the previous method.
*
* @param RegisteredUser $event
*
* @return bool
*/
public function storeRegistrationIpAddress(RegisteredUser $event): bool
{
Preferences::setForUser($event->user, 'registration_ip_address', $event->ipAddress);
return true;
}
/**
* @param User $user
* @param string $ipAddress
*
* @return bool
*/
private function sendConfirmation(User $user, string $ipAddress): bool
{
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
if ($mustConfirmAccount === false) {
Preferences::setForUser($user, 'user_confirmed', true);
Preferences::setForUser($user, 'user_confirmed_last_mail', 0);
Preferences::mark();
return true;
}
$email = $user->email;
$code = str_random(16);
$route = route('do_confirm_account', [$code]);
Preferences::setForUser($user, 'user_confirmed', false);
Preferences::setForUser($user, 'user_confirmed_last_mail', time());
Preferences::setForUser($user, 'user_confirmed_code', $code);
try {
Mail::send(
['emails.confirm-account-html', 'emails.confirm-account-text'], ['route' => $route, 'ip' => $ipAddress],
function (Message $message) use ($email) {
$message->to($email, $email)->subject('Please confirm your Firefly III account');
}
);
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
} catch (Exception $e) {
Log::error($e->getMessage());
}
return true;
}
}

View File

@@ -16,7 +16,6 @@ use Crypt;
use FireflyIII\Models\Attachment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\MessageBag;
use Input;
use Storage;
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -81,20 +80,19 @@ class AttachmentHelper implements AttachmentHelperInterface
}
/**
* @param Model $model
* @param Model $model
* @param array|null $files
*
* @return bool
*/
public function saveAttachmentsForModel(Model $model): bool
public function saveAttachmentsForModel(Model $model, array $files = null): bool
{
$files = $this->getFiles();
if (!is_null($files) && !is_array($files)) {
$this->processFile($files, $model);
}
if (is_array($files)) {
$this->processFiles($files, $model);
foreach ($files as $entry) {
if (!is_null($entry)) {
$this->processFile($entry, $model);
}
}
}
return true;
@@ -227,37 +225,4 @@ class AttachmentHelper implements AttachmentHelperInterface
return true;
}
/**
* @return array|null|UploadedFile
*/
private function getFiles()
{
$files = null;
if (Input::hasFile('attachments')) {
$files = Input::file('attachments');
}
return $files;
}
/**
* @param array $files
*
* @param Model $model
*
* @return bool
*/
private function processFiles(array $files, Model $model): bool
{
foreach ($files as $entry) {
if (!is_null($entry)) {
$this->processFile($entry, $model);
}
}
return true;
}
}

View File

@@ -46,6 +46,6 @@ interface AttachmentHelperInterface
*
* @return bool
*/
public function saveAttachmentsForModel(Model $model): bool;
public function saveAttachmentsForModel(Model $model, array $files = null): bool;
}

View File

@@ -0,0 +1,278 @@
<?php
/**
* MetaPieChart.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Report\Support;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\User;
use Illuminate\Support\Collection;
/**
* Class MetaPieChart
*
* @package FireflyIII\Helpers\Chart
*/
class MetaPieChart implements MetaPieChartInterface
{
/** @var Collection */
protected $accounts;
/** @var Collection */
protected $budgets;
/** @var Collection */
protected $categories;
/** @var bool */
protected $collectOtherObjects = false;
/** @var Carbon */
protected $end;
/** @var array */
protected $grouping
= [
'account' => ['opposing_account_id'],
'budget' => ['transaction_journal_budget_id', 'transaction_budget_id'],
'category' => ['transaction_journal_category_id', 'transaction_category_id'],
];
/** @var array */
protected $repositories
= [
'account' => AccountRepositoryInterface::class,
'budget' => BudgetRepositoryInterface::class,
'category' => CategoryRepositoryInterface::class,
];
/** @var Carbon */
protected $start;
/** @var string */
protected $total = '0';
/** @var User */
protected $user;
public function __construct()
{
$this->accounts = new Collection;
$this->budgets = new Collection;
$this->categories = new Collection;
}
/**
* @param string $direction
* @param string $group
*
* @return array
*/
public function generate(string $direction, string $group): array
{
$transactions = $this->getTransactions($direction);
$grouped = $this->groupByFields($transactions, $this->grouping[$group]);
$chartData = $this->organizeByType($group, $grouped);
// also collect all other transactions
if ($this->collectOtherObjects && $direction === 'expense') {
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [$this->user]);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::WITHDRAWAL]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcmul($sum, '-1');
$sum = bcsub($sum, $this->total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
if ($this->collectOtherObjects && $direction === 'income') {
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcsub($sum, $this->total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
return $chartData;
}
/**
* @param Collection $accounts
*
* @return MetaPieChartInterface
*/
public function setAccounts(Collection $accounts): MetaPieChartInterface
{
$this->accounts = $accounts;
return $this;
}
/**
* @param Collection $budgets
*
* @return MetaPieChartInterface
*/
public function setBudgets(Collection $budgets): MetaPieChartInterface
{
$this->budgets = $budgets;
return $this;
}
/**
* @param Collection $categories
*
* @return MetaPieChartInterface
*/
public function setCategories(Collection $categories): MetaPieChartInterface
{
$this->categories = $categories;
return $this;
}
/**
* @param bool $collectOtherObjects
*
* @return MetaPieChartInterface
*/
public function setCollectOtherObjects(bool $collectOtherObjects): MetaPieChartInterface
{
$this->collectOtherObjects = $collectOtherObjects;
return $this;
}
/**
* @param Carbon $end
*
* @return MetaPieChartInterface
*/
public function setEnd(Carbon $end): MetaPieChartInterface
{
$this->end = $end;
return $this;
}
/**
* @param Carbon $start
*
* @return MetaPieChartInterface
*/
public function setStart(Carbon $start): MetaPieChartInterface
{
$this->start = $start;
return $this;
}
/**
* @param User $user
*
* @return MetaPieChartInterface
*/
public function setUser(User $user): MetaPieChartInterface
{
$this->user = $user;
return $this;
}
protected function getTransactions(string $direction)
{
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
$modifier = -1;
if ($direction === 'expense') {
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
$modifier = 1;
}
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($this->accounts);
$collector->setRange($this->start, $this->end);
$collector->setTypes($types);
$collector->withOpposingAccount();
if ($direction === 'income') {
$collector->disableFilter();
}
if ($this->budgets->count() > 0) {
$collector->setBudgets($this->budgets);
}
if ($this->categories->count() > 0) {
$collector->setCategories($this->categories);
}
$accountIds = $this->accounts->pluck('id')->toArray();
$transactions = $collector->getJournals();
$set = Support::filterTransactions($transactions, $accountIds, $modifier);
return $set;
}
/**
* @param Collection $set
* @param array $fields
*
* @return array
*/
protected function groupByFields(Collection $set, array $fields)
{
$grouped = [];
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$values = [];
foreach ($fields as $field) {
$values[] = intval($transaction->$field);
}
$value = max($values);
$grouped[$value] = $grouped[$value] ?? '0';
$grouped[$value] = bcadd($transaction->transaction_amount, $grouped[$value]);
}
return $grouped;
}
/**
* @param string $type
* @param array $array
*
* @return array
*/
protected function organizeByType(string $type, array $array): array
{
$chartData = [];
$names = [];
$repository = app($this->repositories[$type], [$this->user]);
foreach ($array as $objectId => $amount) {
if (!isset($names[$objectId])) {
$object = $repository->find(intval($objectId));
$names[$objectId] = $object->name;
}
if (bccomp($amount, '0') === -1) {
$amount = bcmul($amount, '-1');
}
$this->total = bcadd($this->total, $amount);
$chartData[$names[$objectId]] = $amount;
}
return $chartData;
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* MetaPieChartInterface.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Chart;
use Carbon\Carbon;
use FireflyIII\User;
use Illuminate\Support\Collection;
/**
* Interface MetaPieChartInterface
*
* @package FireflyIII\Helpers\Chart
*/
interface MetaPieChartInterface
{
/**
* @param string $direction
* @param string $group
*
* @return array
*/
public function generate(string $direction, string $group): array;
/**
* @param Collection $accounts
*
* @return MetaPieChartInterface
*/
public function setAccounts(Collection $accounts): MetaPieChartInterface;
/**
* @param Collection $budgets
*
* @return MetaPieChartInterface
*/
public function setBudgets(Collection $budgets): MetaPieChartInterface;
/**
* @param Collection $categories
*
* @return MetaPieChartInterface
*/
public function setCategories(Collection $categories): MetaPieChartInterface;
/**
* @param bool $collectOtherObjects
*
* @return MetaPieChartInterface
*/
public function setCollectOtherObjects(bool $collectOtherObjects): MetaPieChartInterface;
/**
* @param Carbon $end
*
* @return MetaPieChartInterface
*/
public function setEnd(Carbon $end): MetaPieChartInterface;
/**
* @param Carbon $start
*
* @return MetaPieChartInterface
*/
public function setStart(Carbon $start): MetaPieChartInterface;
/**
* @param User $user
*
* @return MetaPieChartInterface
*/
public function setUser(User $user): MetaPieChartInterface;
}

View File

@@ -14,6 +14,7 @@ namespace FireflyIII\Helpers\Collection;
use Carbon\Carbon;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\BudgetLimit;
use Illuminate\Support\Collection;
/**
@@ -34,12 +35,10 @@ class BalanceLine
/** @var BudgetModel */
protected $budget;
/** @var Carbon */
protected $endDate;
/** @var BudgetLimit */
protected $budgetLimit;
/** @var int */
protected $role = self::ROLE_DEFAULTROLE;
/** @var Carbon */
protected $startDate;
/**
*
@@ -90,20 +89,28 @@ class BalanceLine
$this->budget = $budget;
}
/**
* @return BudgetLimit
*/
public function getBudgetLimit(): BudgetLimit
{
return $this->budgetLimit;
}
/**
* @param BudgetLimit $budgetLimit
*/
public function setBudgetLimit(BudgetLimit $budgetLimit)
{
$this->budgetLimit = $budgetLimit;
}
/**
* @return Carbon
*/
public function getEndDate()
{
return $this->endDate;
}
/**
* @param Carbon $endDate
*/
public function setEndDate($endDate)
{
$this->endDate = $endDate;
return $this->budgetLimit->end_date ?? new Carbon;
}
/**
@@ -127,18 +134,11 @@ class BalanceLine
*/
public function getStartDate()
{
return $this->startDate;
}
/**
* @param Carbon $startDate
*/
public function setStartDate($startDate)
{
$this->startDate = $startDate;
return $this->budgetLimit->start_date ?? new Carbon;
}
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @return string
*/
public function getTitle(): string
@@ -147,13 +147,13 @@ class BalanceLine
return $this->getBudget()->name;
}
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
return trans('firefly.no_budget');
return strval(trans('firefly.no_budget'));
}
if ($this->getRole() == self::ROLE_TAGROLE) {
return trans('firefly.coveredWithTags');
return strval(trans('firefly.coveredWithTags'));
}
if ($this->getRole() == self::ROLE_DIFFROLE) {
return trans('firefly.leftUnbalanced');
return strval(trans('firefly.leftUnbalanced'));
}
return '';
@@ -169,7 +169,7 @@ class BalanceLine
*/
public function leftOfRepetition(): string
{
$start = $this->budget->amount ?? '0';
$start = $this->budgetLimit->amount ?? '0';
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$start = bcadd($balanceEntry->getSpent(), $start);

View File

@@ -60,7 +60,6 @@ class Budget
*/
public function addBudgeted(string $add): Budget
{
$add = strval(round($add, 2));
$this->budgeted = bcadd($this->budgeted, $add);
return $this;
@@ -73,7 +72,6 @@ class Budget
*/
public function addLeft(string $add): Budget
{
$add = strval(round($add, 2));
$this->left = bcadd($this->left, $add);
return $this;
@@ -86,7 +84,6 @@ class Budget
*/
public function addOverspent(string $add): Budget
{
$add = strval(round($add, 2));
$this->overspent = bcadd($this->overspent, $add);
return $this;
@@ -99,7 +96,6 @@ class Budget
*/
public function addSpent(string $add): Budget
{
$add = strval(round($add, 2));
$this->spent = bcadd($this->spent, $add);
return $this;
@@ -168,7 +164,7 @@ class Budget
*/
public function setOverspent(string $overspent): Budget
{
$this->overspent = strval(round($overspent, 2));
$this->overspent = $overspent;
return $this;
}
@@ -188,7 +184,7 @@ class Budget
*/
public function setSpent(string $spent): Budget
{
$this->spent = strval(round($spent, 2));
$this->spent = $spent;
return $this;
}

View File

@@ -13,7 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\BudgetLimit;
/**
*
@@ -26,14 +26,14 @@ class BudgetLine
/** @var BudgetModel */
protected $budget;
/** @var BudgetLimit */
protected $budgetLimit;
/** @var string */
protected $budgeted = '0';
/** @var string */
protected $left = '0';
/** @var string */
protected $overspent = '0';
/** @var LimitRepetition */
protected $repetition;
/** @var string */
protected $spent = '0';
@@ -57,6 +57,26 @@ class BudgetLine
return $this;
}
/**
* @return BudgetLimit
*/
public function getBudgetLimit(): BudgetLimit
{
return $this->budgetLimit ?? new BudgetLimit;
}
/**
* @param BudgetLimit $budgetLimit
*
* @return BudgetLimit
*/
public function setBudgetLimit(BudgetLimit $budgetLimit): BudgetLine
{
$this->budgetLimit = $budgetLimit;
return $this;
}
/**
* @return string
*/
@@ -117,26 +137,6 @@ class BudgetLine
return $this;
}
/**
* @return LimitRepetition
*/
public function getRepetition(): LimitRepetition
{
return $this->repetition ?? new LimitRepetition;
}
/**
* @param LimitRepetition $repetition
*
* @return BudgetLine
*/
public function setRepetition(LimitRepetition $repetition): BudgetLine
{
$this->repetition = $repetition;
return $this;
}
/**
* @return string
*/

View File

@@ -55,7 +55,6 @@ class Category
*/
public function addTotal(string $add)
{
$add = strval(round($add, 2));
$this->total = bcadd($this->total, $add);
}
@@ -79,7 +78,7 @@ class Category
*/
public function getTotal(): string
{
return strval(round($this->total, 2));
return $this->total;
}

View File

@@ -563,7 +563,7 @@ class JournalCollector implements JournalCollectorInterface
return $set;
}
if ($this->joinedOpposing === false) {
Log::error('Cannot filter internal transfers because no opposing information is present.');
Log::info('Cannot filter internal transfers because no opposing information is present.');
return $set;
}
@@ -649,9 +649,17 @@ class JournalCollector implements JournalCollectorInterface
// 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('budgets as transaction_journal_budgets', 'transaction_journal_budgets.id', '=', 'budget_transaction_journal.budget_id');
$this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
$this->query->leftJoin('budgets as transaction_budgets', 'transaction_budgets.id', '=', 'budget_transaction.budget_id');
$this->fields[] = 'budget_transaction_journal.budget_id as transaction_journal_budget_id';
$this->fields[] = 'transaction_journal_budgets.encrypted as transaction_journal_budget_encrypted';
$this->fields[] = 'transaction_journal_budgets.name as transaction_journal_budget_name';
$this->fields[] = 'budget_transaction.budget_id as transaction_budget_id';
$this->fields[] = 'transaction_budgets.encrypted as transaction_budget_encrypted';
$this->fields[] = 'transaction_budgets.name as transaction_budget_name';
}
}
@@ -664,12 +672,26 @@ class JournalCollector implements JournalCollectorInterface
// 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(
'categories as transaction_journal_categories', 'transaction_journal_categories.id', '=', 'category_transaction_journal.category_id'
);
$this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id');
$this->query->leftJoin('categories as transaction_categories', 'transaction_categories.id', '=', 'category_transaction.category_id');
$this->fields[] = 'category_transaction_journal.category_id as transaction_journal_category_id';
$this->fields[] = 'transaction_journal_categories.encrypted as transaction_journal_category_encrypted';
$this->fields[] = 'transaction_journal_categories.name as transaction_journal_category_name';
$this->fields[] = 'category_transaction.category_id as transaction_category_id';
$this->fields[] = 'transaction_categories.encrypted as transaction_category_encrypted';
$this->fields[] = 'transaction_categories.name as transaction_category_name';
}
}
/**
*
*/
private function joinOpposingTables()
{
if (!$this->joinedOpposing) {

View File

@@ -74,12 +74,8 @@ class Help implements HelpInterface
$converter = new CommonMarkConverter();
$content = $converter->convertToHtml($content);
}
if (strlen($content) === 0) {
Log::warning('Raw content length is zero.');
}
return $content;
}
/**

View File

@@ -19,8 +19,7 @@ use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\BalanceEntry;
use FireflyIII\Helpers\Collection\BalanceHeader;
use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
@@ -31,6 +30,7 @@ use Log;
/**
* Class BalanceReportHelper
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // I can't really help it.
* @package FireflyIII\Helpers\Report
*/
class BalanceReportHelper implements BalanceReportHelperInterface
@@ -61,19 +61,17 @@ class BalanceReportHelper implements BalanceReportHelperInterface
public function getBalanceReport(Collection $accounts, Carbon $start, Carbon $end): Balance
{
Log::debug('Start of balance report');
$balance = new Balance;
$header = new BalanceHeader;
$limitRepetitions = $this->budgetRepository->getAllBudgetLimitRepetitions($start, $end);
$balance = new Balance;
$header = new BalanceHeader;
$budgetLimits = $this->budgetRepository->getAllBudgetLimits($start, $end);
foreach ($accounts as $account) {
Log::debug(sprintf('Add account %s to headers.', $account->name));
$header->addAccount($account);
}
/** @var LimitRepetition $repetition */
foreach ($limitRepetitions as $repetition) {
$budget = $this->budgetRepository->find($repetition->budget_id);
Log::debug(sprintf('Create balance line for budget #%d ("%s") and repetition #%d', $budget->id, $budget->name, $repetition->id));
$line = $this->createBalanceLine($budget, $repetition, $accounts);
/** @var BudgetLimit $budgetLimit */
foreach ($budgetLimits as $budgetLimit) {
$line = $this->createBalanceLine($budgetLimit, $accounts);
$balance->addBalanceLine($line);
}
Log::debug('Create rest of the things.');
@@ -145,26 +143,23 @@ class BalanceReportHelper implements BalanceReportHelperInterface
/**
* @param Budget $budget
* @param LimitRepetition $repetition
* @param Collection $accounts
* @param BudgetLimit $budgetLimit
* @param Collection $accounts
*
* @return BalanceLine
*/
private function createBalanceLine(Budget $budget, LimitRepetition $repetition, Collection $accounts): BalanceLine
private function createBalanceLine(BudgetLimit $budgetLimit, Collection $accounts): BalanceLine
{
$line = new BalanceLine;
$budget->amount = $repetition->amount;
$line->setBudget($budget);
$line->setStartDate($repetition->startdate);
$line->setEndDate($repetition->enddate);
$line = new BalanceLine;
$line->setBudget($budgetLimit->budget);
$line->setBudgetLimit($budgetLimit);
// loop accounts:
foreach ($accounts as $account) {
$balanceEntry = new BalanceEntry;
$balanceEntry->setAccount($account);
$spent = $this->budgetRepository->spentInPeriod(
new Collection([$budget]), new Collection([$account]), $repetition->startdate, $repetition->enddate
new Collection([$budgetLimit->budget]), new Collection([$account]), $budgetLimit->start_date, $budgetLimit->end_date
);
$balanceEntry->setSpent($spent);
$line->addBalanceEntry($balanceEntry);

View File

@@ -18,7 +18,7 @@ use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\BudgetLine;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Support\Collection;
@@ -43,6 +43,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface
}
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
@@ -56,13 +57,9 @@ class BudgetReportHelper implements BudgetReportHelperInterface
/** @var Budget $budget */
foreach ($set as $budget) {
$repetitions = $budget->limitrepetitions()->before($end)->after($start)->get();
// no repetition(s) for this budget:
if ($repetitions->count() == 0) {
// spent for budget in time range:
$spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);
$budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end);
if ($budgetLimits->count() == 0) { // no budget limit(s) for this budget
$spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range
if ($spent > 0) {
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget)->setOverspent($spent);
@@ -70,26 +67,20 @@ class BudgetReportHelper implements BudgetReportHelperInterface
}
continue;
}
// one or more repetitions for budget:
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$data = $this->calculateExpenses($budget, $repetition, $accounts);
/** @var BudgetLimit $budgetLimit */
foreach ($budgetLimits as $budgetLimit) { // one or more repetitions for budget
$data = $this->calculateExpenses($budget, $budgetLimit, $accounts);
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget)->setRepetition($repetition)
$budgetLine->setBudget($budget)->setBudgetLimit($budgetLimit)
->setLeft($data['left'])->setSpent($data['expenses'])->setOverspent($data['overspent'])
->setBudgeted(strval($repetition->amount));
->setBudgeted(strval($budgetLimit->amount));
$object->addBudgeted(strval($repetition->amount))->addSpent($data['spent'])
$object->addBudgeted(strval($budgetLimit->amount))->addSpent($data['spent'])
->addLeft($data['left'])->addOverspent($data['overspent'])->addBudgetLine($budgetLine);
}
}
// stuff outside of budgets:
$noBudget = $this->repository->spentInPeriodWithoutBudget($accounts, $start, $end);
$noBudget = $this->repository->spentInPeriodWithoutBudget($accounts, $start, $end); // stuff outside of budgets
$budgetLine = new BudgetLine;
$budgetLine->setOverspent($noBudget)->setSpent($noBudget);
$object->addOverspent($noBudget)->addBudgetLine($budgetLine);
@@ -128,19 +119,19 @@ class BudgetReportHelper implements BudgetReportHelperInterface
}
/**
* @param Budget $budget
* @param LimitRepetition $repetition
* @param Collection $accounts
* @param Budget $budget
* @param BudgetLimit $budgetLimit
* @param Collection $accounts
*
* @return array
*/
private function calculateExpenses(Budget $budget, LimitRepetition $repetition, Collection $accounts): array
private function calculateExpenses(Budget $budget, BudgetLimit $budgetLimit, Collection $accounts): array
{
$array = [];
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate);
$array['left'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? bcadd($repetition->amount, $expenses) : '0';
$array['spent'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? $expenses : '0';
$array['overspent'] = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $repetition->amount);
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date);
$array['left'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? bcadd($budgetLimit->amount, $expenses) : '0';
$array['spent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? $expenses : '0';
$array['overspent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $budgetLimit->amount);
$array['expenses'] = $expenses;
return $array;

View File

@@ -53,6 +53,8 @@ class ReportHelper implements ReportHelperInterface
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
*
* Excludes bills which have not had a payment on the mentioned accounts.
*
* @param Carbon $start
@@ -80,8 +82,6 @@ class ReportHelper implements ReportHelperInterface
$billLine->setMin(strval($bill->amount_min));
$billLine->setMax(strval($bill->amount_max));
$billLine->setHit(false);
// is hit in period?
$entry = $journals->filter(
function (Transaction $transaction) use ($bill) {
return $transaction->bill_id === $bill->id;
@@ -94,14 +94,10 @@ class ReportHelper implements ReportHelperInterface
$billLine->setLastHitDate($first->date);
$billLine->setHit(true);
}
// bill is active, or bill is hit:
if ($billLine->isActive() || $billLine->isHit()) {
$collection->addBill($billLine);
}
}
// do some extra filtering.
$collection->filterBills();
return $collection;

View File

@@ -17,7 +17,6 @@ use Amount;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
@@ -28,8 +27,8 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Input;
use Log;
use Navigation;
use Preferences;
@@ -117,18 +116,19 @@ class AccountController extends Controller
}
/**
* @param Request $request
* @param ARI $repository
* @param Account $account
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(ARI $repository, Account $account)
public function destroy(Request $request, ARI $repository, Account $account)
{
$type = $account->accountType->type;
$typeName = config('firefly.shortNamesByFullName.' . $type);
$name = $account->name;
$accountId = $account->id;
$moveTo = $repository->find(intval(Input::get('move_account_before_delete')));
$moveTo = $repository->find(intval($request->get('move_account_before_delete')));
$repository->destroy($account, $moveTo);
@@ -228,12 +228,13 @@ class AccountController extends Controller
}
/**
* @param Request $request
* @param JournalCollectorInterface $collector
* @param Account $account
*
* @return View
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function show(JournalCollectorInterface $collector, Account $account)
public function show(Request $request, JournalCollectorInterface $collector, Account $account)
{
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
return $this->redirectToOriginalAccount($account);
@@ -244,7 +245,7 @@ class AccountController extends Controller
$range = Preferences::get('viewRange', '1M')->data;
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$chartUri = route('chart.account.single', [$account->id]);
@@ -260,20 +261,22 @@ class AccountController extends Controller
}
/**
* @param Request $request
* @param ARI $repository
* @param Account $account
*
* @return View
*/
public function showAll(AccountRepositoryInterface $repository, Account $account)
public function showAll(Request $request, AccountRepositoryInterface $repository, Account $account)
{
$subTitle = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything')));
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$chartUri = route('chart.account.all', [$account->id]);
// replace with journal collector:
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id . '/all');
@@ -287,24 +290,26 @@ class AccountController extends Controller
}
/**
* @param Request $request
* @param Account $account
* @param string $date
*
* @return View
*/
public function showByDate(Account $account, string $date)
public function showByDate(Request $request, 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')) === 0 ? 1 : intval(Input::get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$chartUri = route('chart.account.period', [$account->id, $carbon->format('Y-m-d')]);
// replace with journal collector:
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page);
$journals = $collector->getPaginatedJournals();
$journals->setPath('accounts/show/' . $account->id . '/' . $date);
@@ -335,7 +340,7 @@ class AccountController extends Controller
Preferences::set('frontPageAccounts', $frontPage);
}
if (intval(Input::get('create_another')) === 1) {
if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('accounts.create.fromStore', true);
@@ -361,7 +366,7 @@ class AccountController extends Controller
Session::flash('success', strval(trans('firefly.updated_account', ['name' => $account->name])));
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('accounts.edit.fromUpdate', true);

View File

@@ -58,23 +58,13 @@ class ConfigurationController extends Controller
// all available configuration and their default value in case
// they don't exist yet.
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
$siteOwner = env('SITE_OWNER');
// email settings:
$sendErrorMessage = [
'mail_for_lockout' => FireflyConfig::get('mail_for_lockout', config('firefly.configuration.mail_for_lockout'))->data,
'mail_for_blocked_domain' => FireflyConfig::get('mail_for_blocked_domain', config('firefly.configuration.mail_for_blocked_domain'))->data,
'mail_for_blocked_email' => FireflyConfig::get('mail_for_blocked_email', config('firefly.configuration.mail_for_blocked_email'))->data,
'mail_for_bad_login' => FireflyConfig::get('mail_for_bad_login', config('firefly.configuration.mail_for_bad_login'))->data,
'mail_for_blocked_login' => FireflyConfig::get('mail_for_blocked_login', config('firefly.configuration.mail_for_blocked_login'))->data,
];
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
$siteOwner = env('SITE_OWNER');
return view(
'admin.configuration.index',
compact('subTitle', 'subTitleIcon', 'singleUserMode', 'mustConfirmAccount', 'isDemoSite', 'sendErrorMessage', 'siteOwner')
compact('subTitle', 'subTitleIcon', 'singleUserMode', 'isDemoSite', 'siteOwner')
);
}
@@ -91,16 +81,8 @@ class ConfigurationController extends Controller
// store config values
FireflyConfig::set('single_user_mode', $data['single_user_mode']);
FireflyConfig::set('must_confirm_account', $data['must_confirm_account']);
FireflyConfig::set('is_demo_site', $data['is_demo_site']);
// email settings
FireflyConfig::set('mail_for_lockout', $data['mail_for_lockout']);
FireflyConfig::set('mail_for_blocked_domain', $data['mail_for_blocked_domain']);
FireflyConfig::set('mail_for_blocked_email', $data['mail_for_blocked_email']);
FireflyConfig::set('mail_for_bad_login', $data['mail_for_bad_login']);
FireflyConfig::set('mail_for_blocked_login', $data['mail_for_blocked_login']);
// flash message
Session::flash('success', strval(trans('firefly.configuration_updated')));
Preferences::mark();

View File

@@ -1,140 +0,0 @@
<?php
/**
* DomainController.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\Admin;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\User;
use Illuminate\Http\Request;
use Session;
/**
* Class DomainController
*
* @package FireflyIII\Http\Controllers\Admin
*/
class DomainController extends Controller
{
/**
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function domains()
{
$title = strval(trans('firefly.administration'));
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = strval(trans('firefly.blocked_domains'));
$subTitleIcon = 'fa-exclamation-circle';
$domains = FireflyConfig::get('blocked-domains', [])->data;
// known domains
$knownDomains = $this->getKnownDomains();
return view('admin.domains.index', compact('title', 'mainTitleIcon', 'knownDomains', 'subTitle', 'subTitleIcon', 'domains'));
}
/**
* @param Request $request
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function manual(Request $request)
{
if (strlen($request->get('domain')) === 0) {
Session::flash('error', trans('firefly.no_domain_filled_in'));
return redirect(route('admin.users.domains'));
}
$domain = strtolower($request->get('domain'));
$blocked = FireflyConfig::get('blocked-domains', [])->data;
if (in_array($domain, $blocked)) {
Session::flash('error', trans('firefly.domain_already_blocked', ['domain' => $domain]));
return redirect(route('admin.users.domains'));
}
$blocked[] = $domain;
FireflyConfig::set('blocked-domains', $blocked);
Session::flash('success', trans('firefly.domain_is_now_blocked', ['domain' => $domain]));
return redirect(route('admin.users.domains'));
}
/**
* @param string $domain
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function toggleDomain(string $domain)
{
$domain = strtolower($domain);
$blocked = FireflyConfig::get('blocked-domains', [])->data;
if (in_array($domain, $blocked)) {
$key = array_search($domain, $blocked);
unset($blocked[$key]);
sort($blocked);
FireflyConfig::set('blocked-domains', $blocked);
Session::flash('message', trans('firefly.domain_now_unblocked', ['domain' => $domain]));
return redirect(route('admin.users.domains'));
}
$blocked[] = $domain;
FireflyConfig::set('blocked-domains', $blocked);
Session::flash('message', trans('firefly.domain_now_blocked', ['domain' => $domain]));
return redirect(route('admin.users.domains'));
}
/**
* @return array
*/
private function getKnownDomains(): array
{
/** @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);
$set[] = strtolower($parts[1]);
}
$set = array_unique($set);
// filter for already banned domains:
$blocked = FireflyConfig::get('blocked-domains', [])->data;
foreach ($set as $domain) {
// in the block array? ignore it.
if (!in_array($domain, $blocked)) {
$filtered[] = $domain;
}
}
return $filtered;
}
}

View File

@@ -14,7 +14,6 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Admin;
use FireflyConfig;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\UserFormRequest;
use FireflyIII\Repositories\User\UserRepositoryInterface;
@@ -81,30 +80,20 @@ class UserController extends Controller
*/
public function index(UserRepositoryInterface $repository)
{
$subTitle = strval(trans('firefly.user_administration'));
$subTitleIcon = 'fa-users';
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
$users = $repository->all();
$subTitle = strval(trans('firefly.user_administration'));
$subTitleIcon = 'fa-users';
$users = $repository->all();
// add meta stuff.
$users->each(
function (User $user) use ($mustConfirmAccount) {
$list = ['user_confirmed', 'twoFactorAuthEnabled', 'twoFactorAuthSecret', 'registration_ip_address', 'confirmation_ip_address'];
$preferences = Preferences::getArrayForUser($user, $list);
$user->activated = true;
if (!($preferences['user_confirmed'] === true) && $mustConfirmAccount === true) {
$user->activated = false;
}
function (User $user) {
$list = ['twoFactorAuthEnabled', 'twoFactorAuthSecret'];
$preferences = Preferences::getArrayForUser($user, $list);
$user->isAdmin = $user->hasRole('owner');
$is2faEnabled = $preferences['twoFactorAuthEnabled'] === true;
$has2faSecret = !is_null($preferences['twoFactorAuthSecret']);
$user->has2FA = false;
if ($is2faEnabled && $has2faSecret) {
$user->has2FA = true;
}
$user->prefs = $preferences;
$user->has2FA = ($is2faEnabled && $has2faSecret) ? true : false;
$user->prefs = $preferences;
}
);
@@ -125,37 +114,12 @@ class UserController extends Controller
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = strval(trans('firefly.single_user_administration', ['email' => $user->email]));
$subTitleIcon = 'fa-user';
// get IP info:
$defaultIp = '0.0.0.0';
$regPref = Preferences::getForUser($user, 'registration_ip_address');
$registration = $defaultIp;
$conPref = Preferences::getForUser($user, 'confirmation_ip_address');
$confirmation = $defaultIp;
if (!is_null($regPref)) {
$registration = $regPref->data;
}
if (!is_null($conPref)) {
$confirmation = $conPref->data;
}
$registrationHost = '';
$confirmationHost = '';
if ($registration != $defaultIp) {
$registrationHost = gethostbyaddr($registration);
}
if ($confirmation != $defaultIp) {
$confirmationHost = gethostbyaddr($confirmation);
}
$information = $repository->getUserData($user);
$information = $repository->getUserData($user);
return view(
'admin.users.show',
compact(
'title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'information',
'user', 'registration', 'confirmation', 'registrationHost', 'confirmationHost'
'title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'information', 'user'
)
);
}

View File

@@ -13,24 +13,22 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Crypt;
use File;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Requests\AttachmentFormRequest;
use FireflyIII\Models\Attachment;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Input;
use Log;
use Preferences;
use Response;
use Session;
use Storage;
use URL;
use View;
/**
* Class AttachmentController
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it's 13.
*
* @package FireflyIII\Http\Controllers
*/
class AttachmentController extends Controller
@@ -57,7 +55,7 @@ class AttachmentController extends Controller
/**
* @param Attachment $attachment
*
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
* @return View
*/
public function delete(Attachment $attachment)
{
@@ -101,9 +99,6 @@ class AttachmentController extends Controller
$content = $repository->getContent($attachment);
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
Log::debug('Send file to user', ['file' => $quoted, 'size' => strlen($content)]);
return response($content, 200)
->header('Content-Description', 'File Transfer')
->header('Content-Type', 'application/octet-stream')
@@ -115,7 +110,6 @@ class AttachmentController extends Controller
->header('Pragma', 'public')
->header('Content-Length', strlen($content));
}
throw new FireflyException('Could not find the indicated attachment. The file is no longer there.');
}
@@ -147,7 +141,6 @@ class AttachmentController extends Controller
{
$image = 'images/page_green.png';
if ($attachment->mime == 'application/pdf') {
$image = 'images/page_white_acrobat.png';
}
@@ -174,7 +167,7 @@ class AttachmentController extends Controller
Session::flash('success', strval(trans('firefly.attachment_updated', ['name' => $attachment->filename])));
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('attachments.edit.fromUpdate', true);

View File

@@ -1,90 +0,0 @@
<?php
/**
* ConfirmationController.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\Auth;
use FireflyIII\Events\ConfirmedUser;
use FireflyIII\Events\ResentConfirmation;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Preferences;
use Session;
/**
* Class ConfirmationController
*
* @package FireflyIII\Http\Controllers\Auth
*/
class ConfirmationController extends Controller
{
/**
* @return mixed
*/
public function confirmationError()
{
return view('auth.confirmation.error');
}
/**
* @param Request $request
* @param string $code
*
* @return mixed
* @throws FireflyException
*/
public function doConfirmation(Request $request, string $code)
{
// check user_confirmed_last_mail
$database = Preferences::get('user_confirmed_code')->data;
$time = Preferences::get('user_confirmed_last_mail', 0)->data;
$now = time();
$maxDiff = config('firefly.confirmation_age');
if ($database === $code && ($now - $time <= $maxDiff)) {
// trigger user registration event:
event(new ConfirmedUser(auth()->user(), $request->ip()));
Preferences::setForUser(auth()->user(), 'user_confirmed', true);
Preferences::setForUser(auth()->user(), 'user_confirmed_confirmed', time());
Session::flash('success', strval(trans('firefly.account_is_confirmed')));
return redirect(route('home'));
}
throw new FireflyException(trans('firefly.invalid_activation_code'));
}
/**
* @param Request $request
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function resendConfirmation(Request $request)
{
$time = Preferences::get('user_confirmed_last_mail', 0)->data;
$now = time();
$maxDiff = config('firefly.resend_confirmation');
$owner = env('SITE_OWNER', 'mail@example.com');
$view = 'auth.confirmation.no-resent';
if ($now - $time > $maxDiff) {
event(new ResentConfirmation(auth()->user(), $request->ip()));
$view = 'auth.confirmation.resent';
}
return view($view, ['owner' => $owner]);
}
}

View File

@@ -14,9 +14,6 @@ namespace FireflyIII\Http\Controllers\Auth;
use Config;
use FireflyConfig;
use FireflyIII\Events\BlockedBadLogin;
use FireflyIII\Events\BlockedUserLogin;
use FireflyIII\Events\LockedOutUser;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\User;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
@@ -64,8 +61,6 @@ class LoginController extends Controller
if ($lockedOut) {
$this->fireLockoutEvent($request);
event(new LockedOutUser($request->get('email'), $request->ip()));
return $this->sendLockoutResponse($request);
}
@@ -76,25 +71,8 @@ class LoginController extends Controller
return $this->sendLoginResponse($request);
}
// check if user is blocked:
$errorMessage = '';
/** @var User $foundUser */
$foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first();
if (!is_null($foundUser)) {
// user exists, but is blocked:
$code = strlen(strval($foundUser->blocked_code)) > 0 ? $foundUser->blocked_code : 'general_blocked';
$errorMessage = strval(trans('firefly.' . $code . '_error', ['email' => $credentials['email']]));
event(new BlockedUserLogin($foundUser, $request->ip()));
}
$errorMessage = $this->getBlockedError($credentials['email']);
// simply a bad login.
if (is_null($foundUser)) {
event(new BlockedBadLogin($credentials['email'], $request->ip()));
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
if (!$lockedOut) {
$this->incrementLoginAttempts($request);
}
@@ -159,4 +137,24 @@ class LoginController extends Controller
]
);
}
/**
* @param string $email
*
* @return string
*/
private function getBlockedError(string $email): string
{
// check if user is blocked:
$errorMessage = '';
/** @var User $foundUser */
$foundUser = User::where('email', $email)->where('blocked', 1)->first();
if (!is_null($foundUser)) {
// user exists, but is blocked:
$code = strlen(strval($foundUser->blocked_code)) > 0 ? $foundUser->blocked_code : 'general_blocked';
$errorMessage = strval(trans('firefly.' . $code . '_error', ['email' => $email]));
}
return $errorMessage;
}
}

View File

@@ -47,6 +47,7 @@ class PasswordController extends Controller
/**
* Send a reset link to the given user.
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 7 but ok
*
* @param \Illuminate\Http\Request $request
*

View File

@@ -15,14 +15,11 @@ namespace FireflyIII\Http\Controllers\Auth;
use Auth;
use Config;
use FireflyConfig;
use FireflyIII\Events\BlockedUseOfDomain;
use FireflyIII\Events\BlockedUseOfEmail;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
use Log;
use Session;
use Validator;
@@ -75,30 +72,6 @@ class RegisterController extends Controller
$this->throwValidationException($request, $validator);
}
$data = $request->all();
$data['password'] = bcrypt($data['password']);
// is user email domain blocked?
if ($this->isBlockedDomain($data['email'])) {
$validator->getMessageBag()->add('email', (string)trans('validation.invalid_domain'));
event(new BlockedUseOfDomain($data['email'], $request->ip()));
$this->throwValidationException($request, $validator);
}
// is user a deleted user?
$hash = hash('sha256', $data['email']);
$configuration = FireflyConfig::get('deleted_users', []);
$set = $configuration->data;
Log::debug(sprintf('Hash of email is %s', $hash));
Log::debug('Hashes of deleted users: ', $set);
if (in_array($hash, $set)) {
$validator->getMessageBag()->add('email', (string)trans('validation.deleted_user'));
event(new BlockedUseOfEmail($data['email'], $request->ip()));
$this->throwValidationException($request, $validator);
}
$user = $this->create($request->all());
// trigger user registration event:
@@ -126,9 +99,6 @@ class RegisterController extends Controller
// is demo site?
$isDemoSite = FireflyConfig::get('is_demo_site', Config::get('firefly.configuration.is_demo_site'))->data;
// activate account?
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', Config::get('firefly.configuration.must_confirm_account'))->data;
// is allowed to?
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
$userCount = User::count();
@@ -140,7 +110,7 @@ class RegisterController extends Controller
$email = $request->old('email');
return view('auth.register', compact('isDemoSite', 'email', 'mustConfirmAccount'));
return view('auth.register', compact('isDemoSite', 'email'));
}
/**
@@ -176,30 +146,4 @@ class RegisterController extends Controller
]
);
}
/**
* @return array
*/
private function getBlockedDomains()
{
return FireflyConfig::get('blocked-domains', [])->data;
}
/**
* @param string $email
*
* @return bool
*/
private function isBlockedDomain(string $email)
{
$parts = explode('@', $email);
$blocked = $this->getBlockedDomains();
if (isset($parts[1]) && in_array($parts[1], $blocked)) {
return true;
}
return false;
}
}

View File

@@ -76,6 +76,7 @@ class TwoFactorController extends Controller
/**
* @param TokenFormRequest $request
* @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation.
*
* @return mixed
*/

View File

@@ -14,13 +14,13 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Input;
use Preferences;
use Session;
use URL;
@@ -197,24 +197,27 @@ class BillController extends Controller
}
/**
* @param Request $request
* @param BillRepositoryInterface $repository
* @param Bill $bill
*
* @return View
*/
public function show(BillRepositoryInterface $repository, Bill $bill)
public function show(Request $request, BillRepositoryInterface $repository, Bill $bill)
{
/** @var Carbon $date */
$date = session('start');
$year = $date->year;
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$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);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setLimit($pageSize)->setPage($page)->withBudgetInformation()
->withCategoryInformation();
$journals = $collector->getPaginatedJournals();
$journals->setPath('/bills/show/' . $bill->id);
@@ -238,7 +241,7 @@ class BillController extends Controller
Session::flash('success', strval(trans('firefly.stored_new_bill', ['name' => e($bill->name)])));
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('bills.create.fromStore', true);
@@ -265,7 +268,7 @@ class BillController extends Controller
Session::flash('success', strval(trans('firefly.updated_bill', ['name' => e($bill->name)])));
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('bills.edit.fromUpdate', true);

View File

@@ -16,16 +16,16 @@ namespace FireflyIII\Http\Controllers;
use Amount;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Http\Requests\BudgetIncomeRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Input;
use Preferences;
use Response;
use Session;
@@ -64,26 +64,26 @@ class BudgetController extends Controller
}
/**
* @param Request $request
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function amount(BudgetRepositoryInterface $repository, Budget $budget)
public function amount(Request $request, BudgetRepositoryInterface $repository, Budget $budget)
{
$amount = intval(Input::get('amount'));
$amount = intval($request->get('amount'));
/** @var Carbon $start */
$start = session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
$end = session('end', Carbon::now()->endOfMonth());
$viewRange = Preferences::get('viewRange', '1M')->data;
$limitRepetition = $repository->updateLimitAmount($budget, $start, $end, $viewRange, $amount);
$end = session('end', Carbon::now()->endOfMonth());
$budgetLimit = $repository->updateLimitAmount($budget, $start, $end, $amount);
if ($amount == 0) {
$limitRepetition = null;
$budgetLimit = null;
}
Preferences::mark();
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition ? $limitRepetition->id : 0, 'amount' => $amount]);
return Response::json(['name' => $budget->name, 'limit' => $budgetLimit ? $budgetLimit->id : 0, 'amount' => $amount]);
}
@@ -189,20 +189,22 @@ class BudgetController extends Controller
return view(
'budgets.index',
compact('available', 'periodStart', 'periodEnd', 'budgetInformation', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
compact('available', 'periodStart', 'periodEnd', 'budgetInformation', 'inactive', 'budgets', 'spent', 'budgeted')
);
}
/**
* @param Request $request
*
* @return View
*/
public function noBudget()
public function noBudget(Request $request)
{
/** @var Carbon $start */
$start = session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
$end = session('end', Carbon::now()->endOfMonth());
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = trans(
'firefly.without_budget_between',
@@ -210,7 +212,8 @@ class BudgetController extends Controller
);
// collector
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget();
$journals = $collector->getPaginatedJournals();
$journals->setPath('/budgets/list/noBudget');
@@ -235,35 +238,37 @@ class BudgetController extends Controller
}
/**
* @param Request $request
* @param BudgetRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
* @param Budget $budget
*
* @return View
*/
public function show(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget)
public function show(Request $request, 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'));
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]);
$repetition = null;
// collector:
$collector = new JournalCollector(auth()->user());
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation();
$journals = $collector->getPaginatedJournals();
$journals->setPath('/budgets/show/' . $budget->id);
$set = $budget->limitrepetitions()->orderBy('startdate', 'DESC')->get();
$set = $repository->getBudgetLimits($budget, $start, $end);
$subTitle = e($budget->name);
$limits = new Collection();
/** @var LimitRepetition $entry */
/** @var BudgetLimit $entry */
foreach ($set as $entry) {
$entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->startdate, $entry->enddate);
$entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->start_date, $entry->end_date);
$limits->push($entry);
}
@@ -271,15 +276,16 @@ class BudgetController extends Controller
}
/**
* @param Budget $budget
* @param LimitRepetition $repetition
* @param Request $request
* @param Budget $budget
* @param BudgetLimit $budgetLimit
*
* @return View
* @throws FireflyException
*/
public function showByRepetition(Budget $budget, LimitRepetition $repetition)
public function showByBudgetLimit(Request $request, Budget $budget, BudgetLimit $budgetLimit)
{
if ($repetition->budgetLimit->budget->id != $budget->id) {
if ($budgetLimit->budget->id != $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');
}
@@ -287,27 +293,30 @@ class BudgetController extends Controller
$repository = app(BudgetRepositoryInterface::class);
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$start = $repetition->startdate;
$end = $repetition->enddate;
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = trans(
'firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]
'firefly.budget_in_period', [
'name' => $budget->name,
'start' => $budgetLimit->start_date->formatLocalized($this->monthAndDayFormat),
'end' => $budgetLimit->end_date->formatLocalized($this->monthAndDayFormat),
]
);
$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);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAllAssetAccounts()->setRange($budgetLimit->start_date, $budgetLimit->end_date)
->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation();
$journals = $collector->getPaginatedJournals();
$journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id);
$journals->setPath('/budgets/show/' . $budget->id . '/' . $budgetLimit->id);
$repetition->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate);
$limits = new Collection([$repetition]);
$budgetLimit->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date);
$limits = new Collection([$budgetLimit]);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
return view('budgets.show', compact('limits', 'budget', 'budgetLimit', 'journals', 'subTitle'));
}
@@ -325,7 +334,7 @@ class BudgetController extends Controller
Session::flash('success', strval(trans('firefly.stored_new_budget', ['name' => e($budget->name)])));
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('budgets.create.fromStore', true);
@@ -352,7 +361,7 @@ class BudgetController extends Controller
Session::flash('success', strval(trans('firefly.updated_budget', ['name' => e($budget->name)])));
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('budgets.edit.fromUpdate', true);
@@ -399,24 +408,22 @@ class BudgetController extends Controller
'budgeted' => '0',
'currentRep' => false,
];
$allRepetitions = $this->repository->getAllBudgetLimitRepetitions($start, $end);
$otherRepetitions = new Collection;
$budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end);
$otherLimits = new Collection;
// get all the limit repetitions relevant between start and end and examine them:
/** @var LimitRepetition $repetition */
foreach ($allRepetitions as $repetition) {
if ($repetition->budget_id == $budget->id) {
if ($repetition->startdate->isSameDay($start) && $repetition->enddate->isSameDay($end)
) {
$return[$budgetId]['currentRep'] = $repetition;
$return[$budgetId]['budgeted'] = $repetition->amount;
continue;
}
// otherwise it's just one of the many relevant repetitions:
$otherRepetitions->push($repetition);
// get all the budget limits relevant between start and end and examine them:
/** @var BudgetLimit $limit */
foreach ($budgetLimits as $limit) {
if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)
) {
$return[$budgetId]['currentLimit'] = $limit;
$return[$budgetId]['budgeted'] = $limit->amount;
continue;
}
// otherwise it's just one of the many relevant repetitions:
$otherLimits->push($limit);
}
$return[$budgetId]['otherRepetitions'] = $otherRepetitions;
$return[$budgetId]['otherLimits'] = $otherLimits;
}
return $return;

View File

@@ -14,16 +14,15 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\CategoryFormRequest;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Input;
use Navigation;
use Preferences;
use Session;
@@ -89,13 +88,14 @@ class CategoryController extends Controller
return view('categories.delete', compact('category', 'subTitle'));
}
/**
* @param CRI $repository
* @param Category $category
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(CRI $repository, Category $category)
public function destroy(CategoryRepositoryInterface $repository, Category $category)
{
$name = $category->name;
@@ -136,11 +136,11 @@ class CategoryController extends Controller
}
/**
* @param CRI $repository
* @param CategoryRepositoryInterface $repository
*
* @return View
*/
public function index(CRI $repository)
public function index(CategoryRepositoryInterface $repository)
{
$categories = $repository->getCategories();
@@ -164,7 +164,8 @@ class CategoryController extends Controller
$end = session('end', Carbon::now()->startOfMonth());
// new collector:
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals();
$journals = $collector->getJournals();
$subTitle = trans(
@@ -176,109 +177,98 @@ class CategoryController extends Controller
}
/**
* @param CRI $repository
* @param AccountRepositoryInterface $accountRepository
* @param Category $category
* @param Request $request
* @param JournalCollectorInterface $collector
* @param Category $category
*
* @return View
*/
public function show(CRI $repository, AccountRepositoryInterface $accountRepository, Category $category)
public function show(Request $request, JournalCollectorInterface $collector, Category $category)
{
$range = Preferences::get('viewRange', '1M')->data;
/** @var Carbon $start */
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
/** @var Carbon $end */
$range = Preferences::get('viewRange', '1M')->data;
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$hideCategory = true; // used in list.
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
// use journal collector
$collector = app(JournalCollectorInterface::class);
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation();
$journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id);
// oldest transaction in category:
$entries = $this->getGroupedEntries($category);
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
}
/**
* @param Request $request
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return View
*/
public function showAll(Request $request, CategoryRepositoryInterface $repository, Category $category)
{
$range = Preferences::get('viewRange', '1M')->data;
$start = $repository->firstUseDate($category);
if ($start->year == 1900) {
$start = new Carbon;
}
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
$end = Navigation::endOfPeriod(new Carbon, $range);
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
$hideCategory = true; // used in list.
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
$showAll = true;
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-show');
$cache->addProperty($category->id);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setCategory($category)->withBudgetInformation();
$journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id . '/all');
if ($cache->has()) {
$entries = $cache->get();
return view('categories.show', compact('category', 'journals', 'entries', 'subTitleIcon', 'hideCategory', 'subTitle'));
}
$categoryCollection = new Collection([$category]);
while ($end >= $start) {
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
$spent = $repository->spentInPeriod($categoryCollection, $accounts, $end, $currentEnd);
$earned = $repository->earnedInPeriod($categoryCollection, $accounts, $end, $currentEnd);
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned]);
$end = Navigation::subtractPeriod($end, $range, 1);
}
$cache->store($entries);
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon'));
return view('categories.show', compact('category', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end', 'showAll'));
}
/**
* @param Request $request
* @param Category $category
* @param $date
* @param string $date
*
* @return View
*/
public function showByDate(Category $category, string $date)
public function showByDate(Request $request, Category $category, string $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart';
$hideCategory = true; // used in list.
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
// new collector:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category);
$collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation();
$journals = $collector->getPaginatedJournals();
$journals->setPath('categories/show/' . $category->id . '/' . $date);
return view('categories.show-by-date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
return view('categories.show', compact('category', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end'));
}
/**
* @param CategoryFormRequest $request
* @param CRI $repository
* @param CategoryFormRequest $request
* @param CategoryRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function store(CategoryFormRequest $request, CRI $repository)
public function store(CategoryFormRequest $request, CategoryRepositoryInterface $repository)
{
$data = $request->getCategoryData();
$category = $repository->store($data);
@@ -286,7 +276,7 @@ class CategoryController extends Controller
Session::flash('success', strval(trans('firefly.stored_category', ['name' => e($category->name)])));
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
if (intval($request->get('create_another')) === 1) {
Session::put('categories.create.fromStore', true);
return redirect(route('categories.create'))->withInput();
@@ -297,13 +287,13 @@ class CategoryController extends Controller
/**
* @param CategoryFormRequest $request
* @param CRI $repository
* @param Category $category
* @param CategoryFormRequest $request
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function update(CategoryFormRequest $request, CRI $repository, Category $category)
public function update(CategoryFormRequest $request, CategoryRepositoryInterface $repository, Category $category)
{
$data = $request->getCategoryData();
$repository->update($category, $data);
@@ -311,7 +301,7 @@ class CategoryController extends Controller
Session::flash('success', strval(trans('firefly.updated_category', ['name' => e($category->name)])));
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
Session::put('categories.edit.fromUpdate', true);
return redirect(route('categories.edit', [$category->id]));
@@ -322,4 +312,48 @@ class CategoryController extends Controller
}
/**
* @param Category $category
*
* @return Collection
*/
private function getGroupedEntries(Category $category): Collection
{
$repository = app(CategoryRepositoryInterface::class);
$accountRepository = app(AccountRepositoryInterface::class);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$first = $repository->firstUseDate($category);
if ($first->year == 1900) {
$first = new Carbon;
}
$range = Preferences::get('viewRange', '1M')->data;
$first = Navigation::startOfPeriod($first, $range);
$end = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
// properties for entries with their amounts.
$cache = new CacheProperties();
$cache->addProperty($first);
$cache->addProperty($end);
$cache->addProperty('categories.entries');
$cache->addProperty($category->id);
if ($cache->has()) {
return $cache->get();
}
while ($end >= $first) {
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned]);
$end = Navigation::subtractPeriod($end, $range, 1);
}
$cache->store($entries);
return $entries;
}
}

View File

@@ -128,7 +128,7 @@ class AccountController extends Controller
$endBalance = $endBalances[$id] ?? '0';
$diff = bcsub($endBalance, $startBalance);
if (bccomp($diff, '0') !== 0) {
$chartData[$account->name] = round($diff, 2);
$chartData[$account->name] = $diff;
}
}
arsort($chartData);
@@ -391,7 +391,7 @@ class AccountController extends Controller
$diff = bcsub($endBalance, $startBalance);
$diff = bcmul($diff, '-1');
if (bccomp($diff, '0') !== 0) {
$chartData[$account->name] = round($diff, 2);
$chartData[$account->name] = $diff;
}
}
@@ -475,11 +475,11 @@ class AccountController extends Controller
];
$currentStart = clone $start;
$range = Steam::balanceInRange($account, $start, clone $end);
$previous = round(array_values($range)[0], 2);
$previous = array_values($range)[0];
while ($currentStart <= $end) {
$format = $currentStart->format('Y-m-d');
$label = $currentStart->formatLocalized(strval(trans('config.month_and_day')));
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
$balance = isset($range[$format]) ? round($range[$format], 12) : $previous;
$previous = $balance;
$currentStart->addDay();
$currentSet['entries'][$label] = $balance;

View File

@@ -91,40 +91,24 @@ class BillController extends Controller
return Response::json($cache->get());
}
$results = $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->getJournals();
$results = $results->sortBy(
$results = $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->getJournals();
$results = $results->sortBy(
function (Transaction $transaction) {
return $transaction->date->format('U');
}
);
$chartData = [
[
'type' => 'bar',
'label' => trans('firefly.min-amount'),
'entries' => [],
],
[
'type' => 'bar',
'label' => trans('firefly.max-amount'),
'entries' => [],
],
[
'type' => 'line',
'label' => trans('firefly.journal-amount'),
'entries' => [],
],
['type' => 'bar', 'label' => trans('firefly.min-amount'), 'entries' => [],],
['type' => 'bar', 'label' => trans('firefly.max-amount'), 'entries' => [],],
['type' => 'line', 'label' => trans('firefly.journal-amount'), 'entries' => [],],
];
/** @var Transaction $entry */
foreach ($results as $entry) {
$date = $entry->date->formatLocalized(strval(trans('config.month_and_day')));
// minimum amount of bill:
$chartData[0]['entries'][$date] = $bill->amount_min;
// maximum amount of bill:
$chartData[1]['entries'][$date] = $bill->amount_max;
// amount of journal:
$chartData[2]['entries'][$date] = bcmul($entry->transaction_amount, '-1');
$date = $entry->date->formatLocalized(strval(trans('config.month_and_day')));
$chartData[0]['entries'][$date] = $bill->amount_min; // minimum amount of bill
$chartData[1]['entries'][$date] = $bill->amount_max; // maximum amount of bill
$chartData[2]['entries'][$date] = bcmul($entry->transaction_amount, '-1'); // amount of journal
}
$data = $this->generator->multiSet($chartData);

View File

@@ -14,11 +14,12 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
@@ -31,6 +32,8 @@ use Response;
/**
* Class BudgetController
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // can't realy be helped.
*
* @package FireflyIII\Http\Controllers\Chart
*/
class BudgetController extends Controller
@@ -39,26 +42,35 @@ class BudgetController extends Controller
/** @var GeneratorInterface */
protected $generator;
/** @var BudgetRepositoryInterface */
protected $repository;
/**
* BudgetController constructor.
*/
public function __construct()
{
parent::__construct();
$this->generator = app(GeneratorInterface::class);
$this->middleware(
function ($request, $next) {
$this->generator = app(GeneratorInterface::class);
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
);
}
/**
* checked
*
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param Budget $budget
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budget(BudgetRepositoryInterface $repository, Budget $budget)
public function budget(Budget $budget)
{
$first = $repository->firstUseDate($budget);
$first = $this->repository->firstUseDate($budget);
$range = Preferences::get('viewRange', '1M')->data;
$last = session('end', new Carbon);
@@ -73,7 +85,6 @@ class BudgetController extends Controller
$final = clone $last;
$final->addYears(2);
$budgetCollection = new Collection([$budget]);
$last = Navigation::endOfX($last, $range, $final); // not to overshoot.
$entries = [];
@@ -84,7 +95,7 @@ class BudgetController extends Controller
$currentEnd = Navigation::endOfPeriod($first, $range);
// sub another day because reasons.
$currentEnd->subDay();
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
$format = Navigation::periodShow($first, $range);
$entries[$format] = bcmul($spent, '-1');
$first = Navigation::addPeriod($first, $range, 0);
@@ -100,31 +111,36 @@ class BudgetController extends Controller
/**
* Shows the amount left in a specific budget limit.
*
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
* @param Budget $budget
* @param BudgetLimit $budgetLimit
*
* @return \Symfony\Component\HttpFoundation\Response
* @throws FireflyException
*/
public function budgetLimit(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
public function budgetLimit(Budget $budget, BudgetLimit $budgetLimit)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
if ($budgetLimit->budget->id != $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');
}
$start = clone $budgetLimit->start_date;
$end = clone $budgetLimit->end_date;
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('chart.budget.budget.limit');
$cache->addProperty($repetition->id);
$cache->addProperty($budgetLimit->id);
if ($cache->has()) {
return Response::json($cache->get());
}
$entries = [];
$amount = $repetition->amount;
$amount = $budgetLimit->amount;
$budgetCollection = new Collection([$budget]);
while ($start <= $end) {
$spent = $repository->spentInPeriod($budgetCollection, new Collection, $start, $start);
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $start, $start);
$amount = bcadd($amount, $spent);
$format = $start->formatLocalized(strval(trans('config.month_and_day')));
$entries[$format] = $amount;
@@ -139,12 +155,12 @@ class BudgetController extends Controller
/**
* Shows a budget list with spent/left/overspent.
*
* @param BudgetRepositoryInterface $repository
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // 46 lines, I'm fine with this.
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(BudgetRepositoryInterface $repository)
public function frontpage()
{
$start = session('start', Carbon::now()->startOfMonth());
$end = session('end', Carbon::now()->endOfMonth());
@@ -156,58 +172,32 @@ class BudgetController extends Controller
if ($cache->has()) {
return Response::json($cache->get());
}
$budgets = $repository->getActiveBudgets();
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$chartData = [
[
'label' => strval(trans('firefly.spent_in_budget')),
'entries' => [],
'type' => 'bar',
],
[
'label' => strval(trans('firefly.left_to_spend')),
'entries' => [],
'type' => 'bar',
],
[
'label' => strval(trans('firefly.overspent')),
'entries' => [],
'type' => 'bar',
],
$budgets = $this->repository->getActiveBudgets();
$chartData = [
['label' => strval(trans('firefly.spent_in_budget')), 'entries' => [], 'type' => 'bar',],
['label' => strval(trans('firefly.left_to_spend')), 'entries' => [], 'type' => 'bar',],
['label' => strval(trans('firefly.overspent')), 'entries' => [], 'type' => 'bar',],
];
/** @var Budget $budget */
foreach ($budgets as $budget) {
// get relevant repetitions:
$reps = $this->filterRepetitions($repetitions, $budget, $start, $end);
if ($reps->count() === 0) {
$row = $this->spentInPeriodSingle($repository, $budget, $start, $end);
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['repetition_left'], '0') !== 0) {
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
}
continue;
$limits = $this->repository->getBudgetLimits($budget, $start, $end);
$expenses = $this->getExpensesForBudget($limits, $budget, $start, $end);
foreach ($expenses as $name => $row) {
$chartData[0]['entries'][$name] = $row['spent'];
$chartData[1]['entries'][$name] = $row['left'];
$chartData[2]['entries'][$name] = $row['overspent'];
}
$rows = $this->spentInPeriodMulti($repository, $budget, $reps);
foreach ($rows as $row) {
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['repetition_left'], '0') !== 0) {
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
}
}
unset($rows, $row);
}
// for no budget:
$row = $this->spentInPeriodWithout($start, $end);
if (bccomp($row['repetition_overspent'], '0') !== 0) {
$chartData[0]['entries'][$row['name']] = bcmul($row['spent'], '-1');
$chartData[1]['entries'][$row['name']] = $row['repetition_left'];
$chartData[2]['entries'][$row['name']] = bcmul($row['repetition_overspent'], '-1');
$spent = $this->spentInPeriodWithout($start, $end);
$name = strval(trans('firefly.no_budget'));
if (bccomp($spent, '0') !== 0) {
$chartData[0]['entries'][$name] = bcmul($spent, '-1');
$chartData[1]['entries'][$name] = '0';
$chartData[2]['entries'][$name] = '0';
}
$data = $this->generator->multiSet($chartData);
@@ -218,15 +208,16 @@ class BudgetController extends Controller
/**
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
*
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function period(BudgetRepositoryInterface $repository, Budget $budget, Collection $accounts, Carbon $start, Carbon $end)
public function period(Budget $budget, Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties();
@@ -238,56 +229,22 @@ class BudgetController extends Controller
if ($cache->has()) {
return Response::json($cache->get());
}
// get the expenses
$budgeted = [];
$periods = Navigation::listOfPeriods($start, $end);
$entries = $repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end);
$key = Navigation::preferredCarbonFormat($start, $end);
$range = Navigation::preferredRangeFormat($start, $end);
// get the budget limits (if any)
$repetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$current = clone $start;
while ($current < $end) {
$currentStart = Navigation::startOfPeriod($current, $range);
$currentEnd = Navigation::endOfPeriod($current, $range);
$reps = $repetitions->filter(
function (LimitRepetition $repetition) use ($budget, $currentStart, $currentEnd) {
if ($repetition->budget_id === $budget->id && $repetition->startdate >= $currentStart && $repetition->enddate <= $currentEnd) {
return true;
}
return false;
}
);
$index = $currentStart->format($key);
$budgeted[$index] = $reps->sum('amount');
$currentEnd->addDay();
$current = clone $currentEnd;
}
$entries = $this->repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); // get the expenses
$budgeted = $this->getBudgetedInPeriod($budget, $start, $end);
// join them into one set of data:
$chartData = [
[
'label' => strval(trans('firefly.spent')),
'type' => 'bar',
'entries' => [],
],
[
'label' => strval(trans('firefly.budgeted')),
'type' => 'bar',
'entries' => [],
],
['label' => strval(trans('firefly.spent')), 'type' => 'bar', 'entries' => [],],
['label' => strval(trans('firefly.budgeted')), 'type' => 'bar', 'entries' => [],],
];
foreach (array_keys($periods) as $period) {
$label = $periods[$period];
$spent = isset($entries[$budget->id]['entries'][$period]) ? $entries[$budget->id]['entries'][$period] : '0';
$limit = isset($budgeted[$period]) ? $budgeted[$period] : 0;
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 2);
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
$chartData[1]['entries'][$label] = $limit;
}
$data = $this->generator->multiSet($chartData);
$cache->store($data);
@@ -296,14 +253,13 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse
*/
public function periodNoBudget(BudgetRepositoryInterface $repository, Collection $accounts, Carbon $start, Carbon $end)
public function periodNoBudget(Collection $accounts, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties();
@@ -317,7 +273,7 @@ class BudgetController extends Controller
// the expenses:
$periods = Navigation::listOfPeriods($start, $end);
$entries = $repository->getNoBudgetPeriodReport($accounts, $start, $end);
$entries = $this->repository->getNoBudgetPeriodReport($accounts, $start, $end);
$chartData = [];
// join them:
@@ -333,98 +289,115 @@ class BudgetController extends Controller
}
/**
* @param Collection $repetitions
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getBudgetedInPeriod(Budget $budget, Carbon $start, Carbon $end): array
{
$key = Navigation::preferredCarbonFormat($start, $end);
$range = Navigation::preferredRangeFormat($start, $end);
$current = clone $start;
$budgeted = [];
while ($current < $end) {
$currentStart = Navigation::startOfPeriod($current, $range);
$currentEnd = Navigation::endOfPeriod($current, $range);
$budgetLimits = $this->repository->getBudgetLimits($budget, $currentStart, $currentEnd);
$index = $currentStart->format($key);
$budgeted[$index] = $budgetLimits->sum('amount');
$currentEnd->addDay();
$current = clone $currentEnd;
}
return $budgeted;
}
/**
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 6 but ok.
*
* @param Collection $limits
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
private function filterRepetitions(Collection $repetitions, Budget $budget, Carbon $start, Carbon $end): Collection
{
return $repetitions->filter(
function (LimitRepetition $repetition) use ($budget, $start, $end) {
if ($repetition->startdate < $end && $repetition->enddate > $start && $repetition->budget_id === $budget->id) {
return true;
}
return false;
}
);
}
/**
* Returns an array with the following values:
* 0 =>
* 'name' => name of budget + repetition
* 'repetition_left' => left in budget repetition (always zero)
* 'repetition_overspent' => spent more than budget repetition? (always zero)
* 'spent' => actually spent in period for budget
* 1 => (etc)
*
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param Collection $repetitions
*
* @return array
*/
private function spentInPeriodMulti(BudgetRepositoryInterface $repository, Budget $budget, Collection $repetitions): array
private function getExpensesForBudget(Collection $limits, Budget $budget, Carbon $start, Carbon $end): array
{
$return = [];
$format = strval(trans('config.month_and_day'));
$name = $budget->name;
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate);
if ($repetitions->count() > 1) {
$name = $budget->name . ' ' . trans(
'firefly.between_dates',
['start' => $repetition->startdate->formatLocalized($format), 'end' => $repetition->enddate->formatLocalized($format)]
);
if ($limits->count() === 0) {
$spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
if (bccomp($spent, '0') !== 0) {
$return[$budget->name]['spent'] = bcmul($spent, '-1');
$return[$budget->name]['left'] = 0;
$return[$budget->name]['overspent'] = 0;
}
$amount = $repetition->amount;
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
$spent = $expenses;
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
$return[] = [
'name' => $name,
'repetition_left' => $left,
'repetition_overspent' => $overspent,
'spent' => $spent,
];
return $return;
}
$rows = $this->spentInPeriodMulti($budget, $limits);
foreach ($rows as $name => $row) {
if (bccomp($row['spent'], '0') !== 0 || bccomp($row['left'], '0') !== 0) {
$return[$name]['spent'] = bcmul($row['spent'], '-1');
$return[$name]['left'] = $row['left'];
$return[$name]['overspent'] = bcmul($row['overspent'], '-1');
}
}
unset($rows, $row);
return $return;
}
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
*
* Returns an array with the following values:
* 'name' => name of budget
* 'repetition_left' => left in budget repetition (always zero)
* 'repetition_overspent' => spent more than budget repetition? (always zero)
* 'spent' => actually spent in period for budget
* 0 =>
* 'name' => name of budget + repetition
* 'left' => left in budget repetition (always zero)
* 'overspent' => spent more than budget repetition? (always zero)
* 'spent' => actually spent in period for budget
* 1 => (etc)
*
*
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param Budget $budget
* @param Collection $limits
*
* @return array
*/
private function spentInPeriodSingle(BudgetRepositoryInterface $repository, Budget $budget, Carbon $start, Carbon $end): array
private function spentInPeriodMulti(Budget $budget, Collection $limits): array
{
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
$array = [
'name' => $budget->name,
'repetition_left' => '0',
'repetition_overspent' => '0',
'spent' => $spent,
];
$return = [];
$format = strval(trans('config.month_and_day'));
$name = $budget->name;
/** @var BudgetLimit $budgetLimit */
foreach ($limits as $budgetLimit) {
$expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date);
return $array;
if ($limits->count() > 1) {
$name = $budget->name . ' ' . trans(
'firefly.between_dates',
[
'start' => $budgetLimit->start_date->formatLocalized($format),
'end' => $budgetLimit->end_date->formatLocalized($format),
]
);
}
$amount = $budgetLimit->amount;
$left = bccomp(bcadd($amount, $expenses), '0') < 1 ? '0' : bcadd($amount, $expenses);
$spent = $expenses;
$overspent = bccomp(bcadd($amount, $expenses), '0') < 1 ? bcadd($amount, $expenses) : '0';
$return[$name] = [
'left' => $left,
'overspent' => $overspent,
'spent' => $spent,
];
}
return $return;
}
/**
@@ -437,12 +410,13 @@ class BudgetController extends Controller
* @param Carbon $start
* @param Carbon $end
*
* @return array
* @return string
*/
private function spentInPeriodWithout(Carbon $start, Carbon $end): array
private function spentInPeriodWithout(Carbon $start, Carbon $end): string
{
// collector
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$types = [TransactionType::WITHDRAWAL];
$collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget();
$journals = $collector->getJournals();
@@ -451,13 +425,7 @@ class BudgetController extends Controller
foreach ($journals as $entry) {
$sum = bcadd($entry->transaction_amount, $sum);
}
$array = [
'name' => strval(trans('firefly.no_budget')),
'repetition_left' => '0',
'repetition_overspent' => $sum,
'spent' => '0',
];
return $array;
return $sum;
}
}

View File

@@ -17,17 +17,17 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Log;
use Navigation;
use Response;
@@ -76,50 +76,19 @@ class BudgetReportController extends Controller
*/
public function accountExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$cache = new CacheProperties;
$cache->addProperty('chart.budget.report.account-expense');
$cache->addProperty($accounts);
$cache->addProperty($budgets);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
$names = [];
$set = $this->getExpenses($accounts, $budgets, $start, $end);
$grouped = $this->groupByOpposingAccount($set);
$chartData = [];
$total = '0';
foreach ($grouped as $accountId => $amount) {
if (!isset($names[$accountId])) {
$account = $this->accountRepository->find(intval($accountId));
$names[$accountId] = $account->name;
}
$amount = bcmul($amount, '-1');
$total = bcadd($total, $amount);
$chartData[$names[$accountId]] = $amount;
}
// also collect all transactions NOT in these budgets.
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcmul($sum, '-1');
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setBudgets($budgets);
$helper->setUser(auth()->user());
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(intval($others) === 1);
$chartData = $helper->generate('expense', 'account');
$data = $this->generator->pieChart($chartData);
return Response::json($data);
}
/**
@@ -133,48 +102,16 @@ class BudgetReportController extends Controller
*/
public function budgetExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$cache = new CacheProperties;
$cache->addProperty('chart.budget.report.budget-expense');
$cache->addProperty($accounts);
$cache->addProperty($budgets);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
$names = [];
$set = $this->getExpenses($accounts, $budgets, $start, $end);
$grouped = $this->groupByBudget($set);
$total = '0';
$chartData = [];
foreach ($grouped as $budgetId => $amount) {
if (!isset($names[$budgetId])) {
$budget = $this->budgetRepository->find(intval($budgetId));
$names[$budgetId] = $budget->name;
}
$amount = bcmul($amount, '-1');
$total = bcadd($total, $amount);
$chartData[$names[$budgetId]] = $amount;
}
// also collect all transactions NOT in these budgets.
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcmul($sum, '-1');
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setBudgets($budgets);
$helper->setUser(auth()->user());
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(intval($others) === 1);
$chartData = $helper->generate('expense', 'budget');
$data = $this->generator->pieChart($chartData);
return Response::json($data);
}
@@ -204,7 +141,6 @@ class BudgetReportController extends Controller
$function = Navigation::preferredEndOfPeriod($start, $end);
$chartData = [];
$currentStart = clone $start;
$limits = $repository->getAllBudgetLimitRepetitions($start, $end); // also for ALL budgets.
// prep chart data:
foreach ($budgets as $budget) {
@@ -229,8 +165,9 @@ class BudgetReportController extends Controller
'entries' => [],
];
}
$sumOfExpenses = [];
$leftOfLimits = [];
$allBudgetLimits = $repository->getAllBudgetLimits($start, $end);
$sumOfExpenses = [];
$leftOfLimits = [];
while ($currentStart < $end) {
$currentEnd = clone $currentStart;
$currentEnd = $currentEnd->$function();
@@ -239,20 +176,20 @@ class BudgetReportController extends Controller
/** @var Budget $budget */
foreach ($budgets as $budget) {
// get budget limit(s) for this period):
$budgetLimits = $this->filterBudgetLimits($allBudgetLimits, $budget, $currentStart, $currentEnd);
$currentExpenses = $expenses[$budget->id] ?? '0';
$sumOfExpenses[$budget->id] = $sumOfExpenses[$budget->id] ?? '0';
$sumOfExpenses[$budget->id] = bcadd($currentExpenses, $sumOfExpenses[$budget->id]);
$chartData[$budget->id]['entries'][$label] = round(bcmul($currentExpenses, '-1'), 2);
$chartData[$budget->id . '-sum']['entries'][$label] = round(bcmul($sumOfExpenses[$budget->id], '-1'), 2);
$chartData[$budget->id]['entries'][$label] = bcmul($currentExpenses, '-1');
$chartData[$budget->id . '-sum']['entries'][$label] = bcmul($sumOfExpenses[$budget->id], '-1');
$limit = $this->filterLimits($limits, $budget, $currentStart);
if (!is_null($limit->id)) {
$leftOfLimits[$limit->id] = $leftOfLimits[$limit->id] ?? strval($limit->amount);
$leftOfLimits[$limit->id] = bcadd($leftOfLimits[$limit->id], $currentExpenses);
$chartData[$budget->id . '-left']['entries'][$label] = round($leftOfLimits[$limit->id], 2);
if (count($budgetLimits) > 0) {
$budgetLimitId = $budgetLimits->first()->id;
$leftOfLimits[$budgetLimitId] = $leftOfLimits[$budgetLimitId] ?? strval($budgetLimits->sum('amount'));
$leftOfLimits[$budgetLimitId] = bcadd($leftOfLimits[$budgetLimitId], $currentExpenses);
$chartData[$budget->id . '-left']['entries'][$label] = $leftOfLimits[$budgetLimitId];
}
}
$currentStart = clone $currentEnd;
$currentStart->addDay();
@@ -265,44 +202,32 @@ class BudgetReportController extends Controller
}
/**
* @param $limits
* @param $budget
* @param $currentStart
* Returns the budget limits belonging to the given budget and valid on the given day.
*
* @return LimitRepetition
* @param Collection $budgetLimits
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
private function filterLimits(Collection $limits, Budget $budget, Carbon $date): LimitRepetition
private function filterBudgetLimits(Collection $budgetLimits, Budget $budget, Carbon $start, Carbon $end): Collection
{
Log::debug(sprintf('Start of filterLimits with %d limits.', $limits->count()));
$filtered = $limits->filter(
function (LimitRepetition $limit) use ($budget, $date) {
if ($limit->budget_id !== $budget->id) {
Log::debug(sprintf('LimitRepetition has budget #%d but expecting #%d', $limit->budget_id, $budget->id));
return false;
}
if ($date < $limit->startdate || $date > $limit->enddate) {
Log::debug(
sprintf(
'Date %s is not between %s and %s',
$date->format('Y-m-d'), $limit->startdate->format('Y-m-d'), $limit->enddate->format('Y-m-d')
)
);
return false;
$set = $budgetLimits->filter(
function (BudgetLimit $budgetLimit) use ($budget, $start, $end) {
if ($budgetLimit->budget_id === $budget->id
&& $budgetLimit->start_date->lte($start) // start of budget limit is on or before start
&& $budgetLimit->end_date->gte($end) // end of budget limit is on or after end
) {
return $budgetLimit;
}
return $limit;
return false;
}
);
if ($filtered->count() === 1) {
return $filtered->first();
}
return new LimitRepetition;
return $set;
}
/**
* @param Collection $accounts
* @param Collection $budgets
@@ -313,7 +238,8 @@ class BudgetReportController extends Controller
*/
private function getExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): Collection
{
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setBudgets($budgets)->withOpposingAccount()->disableFilter();
$accountIds = $accounts->pluck('id')->toArray();

View File

@@ -65,7 +65,12 @@ class CategoryController extends Controller
return Response::json($cache->get());
}
$start = $repository->firstUseDate($category);
$start = $repository->firstUseDate($category);
if ($start->year == 1900) {
$start = new Carbon;
}
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = new Carbon;

View File

@@ -17,7 +17,8 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Generator\Report\Category\MonthReportGenerator;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Chart\MetaPieChartInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Models\Transaction;
@@ -75,48 +76,16 @@ class CategoryReportController extends Controller
*/
public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.account-expense');
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
$names = [];
$set = $this->getExpenses($accounts, $categories, $start, $end);
$grouped = $this->groupByOpposingAccount($set);
$chartData = [];
$total = '0';
foreach ($grouped as $accountId => $amount) {
if (!isset($names[$accountId])) {
$account = $this->accountRepository->find(intval($accountId));
$names[$accountId] = $account->name;
}
$amount = bcmul($amount, '-1');
$total = bcadd($total, $amount);
$chartData[$names[$accountId]] = $amount;
}
// also collect all transactions NOT in these categories.
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcmul($sum, '-1');
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setCategories($categories);
$helper->setUser(auth()->user());
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(intval($others) === 1);
$chartData = $helper->generate('expense', 'account');
$data = $this->generator->pieChart($chartData);
return Response::json($data);
}
@@ -132,47 +101,16 @@ class CategoryReportController extends Controller
*/
public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.account-income');
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($others);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get());
}
$names = [];
$set = $this->getIncome($accounts, $categories, $start, $end);
$grouped = $this->groupByOpposingAccount($set);
$chartData = [];
$total = '0';
foreach ($grouped as $accountId => $amount) {
if (!isset($names[$accountId])) {
$account = $this->accountRepository->find(intval($accountId));
$names[$accountId] = $account->name;
}
$total = bcadd($total, $amount);
$chartData[$names[$accountId]] = $amount;
}
// also collect others?
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setCategories($categories);
$helper->setUser(auth()->user());
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(intval($others) === 1);
$chartData = $helper->generate('income', 'account');
$data = $this->generator->pieChart($chartData);
return Response::json($data);
}
@@ -188,48 +126,16 @@ class CategoryReportController extends Controller
*/
public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.category-expense');
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
$names = [];
$set = $this->getExpenses($accounts, $categories, $start, $end);
$grouped = $this->groupByCategory($set);
$total = '0';
$chartData = [];
foreach ($grouped as $categoryId => $amount) {
if (!isset($names[$categoryId])) {
$category = $this->categoryRepository->find(intval($categoryId));
$names[$categoryId] = $category->name;
}
$amount = bcmul($amount, '-1');
$total = bcadd($total, $amount);
$chartData[$names[$categoryId]] = $amount;
}
// also collect all transactions NOT in these categories.
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcmul($sum, '-1');
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setCategories($categories);
$helper->setUser(auth()->user());
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(intval($others) === 1);
$chartData = $helper->generate('expense', 'category');
$data = $this->generator->pieChart($chartData);
return Response::json($data);
}
@@ -245,45 +151,17 @@ class CategoryReportController extends Controller
*/
public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others)
{
/** @var bool $others */
$others = intval($others) === 1;
$cache = new CacheProperties;
$cache->addProperty('chart.category.report.category-income');
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($others);
if ($cache->has()) {
return Response::json($cache->get());
}
$names = [];
$set = $this->getIncome($accounts, $categories, $start, $end);
$grouped = $this->groupByCategory($set);
$total = '0';
$chartData = [];
foreach ($grouped as $categoryId => $amount) {
if (!isset($names[$categoryId])) {
$category = $this->categoryRepository->find(intval($categoryId));
$names[$categoryId] = $category->name;
}
$total = bcadd($total, $amount);
$chartData[$names[$categoryId]] = $amount;
}
if ($others) {
$collector = new JournalCollector(auth()->user());
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]);
$journals = $collector->getJournals();
$sum = strval($journals->sum('transaction_amount'));
$sum = bcsub($sum, $total);
$chartData[strval(trans('firefly.everything_else'))] = $sum;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
/** @var MetaPieChartInterface $helper */
$helper = app(MetaPieChartInterface::class);
$helper->setAccounts($accounts);
$helper->setCategories($categories);
$helper->setUser(auth()->user());
$helper->setStart($start);
$helper->setEnd($end);
$helper->setCollectOtherObjects(intval($others) === 1);
$chartData = $helper->generate('income', 'category');
$data = $this->generator->pieChart($chartData);
return Response::json($data);
}
@@ -405,7 +283,8 @@ class CategoryReportController extends Controller
*/
private function getExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
{
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setCategories($categories)->withOpposingAccount()->disableFilter();
$accountIds = $accounts->pluck('id')->toArray();
@@ -425,7 +304,8 @@ class CategoryReportController extends Controller
*/
private function getIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection
{
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->setCategories($categories)->withOpposingAccount();
$accountIds = $accounts->pluck('id')->toArray();

View File

@@ -13,6 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use FireflyConfig;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
@@ -23,7 +24,6 @@ use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Session;
use View;
use FireflyConfig;
/**
* Class Controller
@@ -52,8 +52,8 @@ class Controller extends BaseController
View::share('hideTags', false);
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
View::share('IS_DEMO_SITE', $isDemoSite);
View::share('DEMO_USERNAME', env('DEMO_USERNAME',''));
View::share('DEMO_PASSWORD', env('DEMO_PASSWORD',''));
View::share('DEMO_USERNAME', env('DEMO_USERNAME', ''));
View::share('DEMO_PASSWORD', env('DEMO_PASSWORD', ''));
// translations:

View File

@@ -17,7 +17,6 @@ use Cache;
use FireflyIII\Http\Requests\CurrencyFormRequest;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Input;
use Log;
use Preferences;
use Session;
@@ -170,7 +169,7 @@ class CurrencyController extends Controller
if (!auth()->user()->hasRole('owner')) {
Session::flash('warning', trans('firefly.ask_site_owner', ['site_owner' => env('SITE_OWNER')]));
Session::flash('info', trans('firefly.ask_site_owner', ['owner' => env('SITE_OWNER')]));
}
@@ -196,7 +195,7 @@ class CurrencyController extends Controller
$currency = $repository->store($data);
Session::flash('success', trans('firefly.created_currency', ['name' => $currency->name]));
if (intval(Input::get('create_another')) === 1) {
if (intval($request->get('create_another')) === 1) {
Session::put('currencies.create.fromStore', true);
return redirect(route('currencies.create'))->withInput();
@@ -225,7 +224,7 @@ class CurrencyController extends Controller
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
Session::put('currencies.edit.fromUpdate', true);
return redirect(route('currencies.edit', [$currency->id]));

View File

@@ -26,7 +26,6 @@ use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface;
use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI;
use Preferences;
use Response;
use Storage;
use View;
/**
@@ -145,7 +144,7 @@ class ExportController extends Controller
'job' => $job,
];
$job->change('export_status_make_exporter');
$jobs->changeStatus($job, 'export_status_make_exporter');
/** @var ProcessorInterface $processor */
$processor = app(ProcessorInterface::class, [$settings]);
@@ -153,47 +152,46 @@ class ExportController extends Controller
/*
* Collect journals:
*/
$job->change('export_status_collecting_journals');
$jobs->changeStatus($job, 'export_status_collecting_journals');
$processor->collectJournals();
$job->change('export_status_collected_journals');
$jobs->changeStatus($job, 'export_status_collected_journals');
/*
* Transform to exportable entries:
*/
$job->change('export_status_converting_to_export_format');
$jobs->changeStatus($job, 'export_status_converting_to_export_format');
$processor->convertJournals();
$job->change('export_status_converted_to_export_format');
$jobs->changeStatus($job, 'export_status_converted_to_export_format');
/*
* Transform to (temporary) file:
*/
$job->change('export_status_creating_journal_file');
$jobs->changeStatus($job, 'export_status_creating_journal_file');
$processor->exportJournals();
$job->change('export_status_created_journal_file');
$jobs->changeStatus($job, 'export_status_created_journal_file');
/*
* Collect attachments, if applicable.
*/
if ($settings['includeAttachments']) {
$job->change('export_status_collecting_attachments');
$jobs->changeStatus($job, 'export_status_collecting_attachments');
$processor->collectAttachments();
$job->change('export_status_collected_attachments');
$jobs->changeStatus($job, 'export_status_collected_attachments');
}
/*
* Collect old uploads
*/
if ($settings['includeOldUploads']) {
$job->change('export_status_collecting_old_uploads');
$jobs->changeStatus($job, 'export_status_collecting_old_uploads');
$processor->collectOldUploads();
$job->change('export_status_collected_old_uploads');
$jobs->changeStatus($job, 'export_status_collected_old_uploads');
}
/*
* Create ZIP file:
*/
$job->change('export_status_creating_zip_file');
$jobs->changeStatus($job, 'export_status_creating_zip_file');
$processor->createZipFile();
$job->change('export_status_created_zip_file');
$job->change('export_status_finished');
$jobs->changeStatus($job, 'export_status_created_zip_file');
$jobs->changeStatus($job, 'export_status_finished');
return Response::json('ok');
}

View File

@@ -63,7 +63,6 @@ class HomeController extends Controller
// a possible problem with the budgets.
if ($label === strval(trans('firefly.everything')) || $label === strval(trans('firefly.customRange'))) {
$isCustomRange = true;
//Preferences::set('viewRange', 'custom');
Log::debug('Range is now marked as "custom".');
}
@@ -175,9 +174,6 @@ class HomeController extends Controller
'logout',
'two-fac',
'lost-two',
'confirm',
'resend',
'do_confirm',
// test troutes
'test-flash',
'all-routes',

View File

@@ -23,6 +23,7 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Http\Request;
use Log;
use Response;
use Session;
use SplFileObject;
use Storage;
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -368,14 +369,33 @@ class ImportController extends Controller
$content = $uploaded->fread($uploaded->getSize());
$contentEncrypted = Crypt::encrypt($content);
$disk = Storage::disk('upload');
$disk->put($newName, $contentEncrypted);
Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]);
// user is demo user, replace upload with prepared file.
if (auth()->user()->hasRole('demo')) {
$stubsDisk = Storage::disk('stubs');
$content = $stubsDisk->get('demo-import.csv');
$contentEncrypted = Crypt::encrypt($content);
$disk->put($newName, $contentEncrypted);
Log::debug('Replaced upload with demo file.');
// store configuration file's content into the job's configuration
// thing.
// otherwise, leave it empty.
if ($request->files->has('configuration_file')) {
// also set up prepared configuration.
$configuration = json_decode($stubsDisk->get('demo-configuration.json'), true);
$job->configuration = $configuration;
$job->save();
Log::debug('Set configuration for demo user', $configuration);
// also flash info
Session::flash('info', trans('demo.import-configure-security'));
}
if (!auth()->user()->hasRole('demo')) {
// user is not demo, process original upload:
$disk->put($newName, $contentEncrypted);
Log::debug('Uploaded file', ['name' => $upload->getClientOriginalName(), 'size' => $upload->getSize(), 'mime' => $upload->getClientMimeType()]);
}
// store configuration file's content into the job's configuration thing. Otherwise, leave it empty.
// demo user's configuration upload is ignored completely.
if ($request->files->has('configuration_file') && !auth()->user()->hasRole('demo')) {
/** @var UploadedFile $configFile */
$configFile = $request->files->get('configuration_file');
Log::debug(
@@ -394,6 +414,9 @@ class ImportController extends Controller
}
}
// if user is demo user, replace config with prepared config:
return redirect(route('import.configure', [$job->key]));
}

View File

@@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers;
use Amount;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountTaskerInterface;
@@ -23,7 +23,7 @@ use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Input;
use Illuminate\Http\Request;
use Preferences;
use Response;
@@ -43,11 +43,13 @@ class JsonController extends Controller
}
/**
* @param Request $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function action()
public function action(Request $request)
{
$count = intval(Input::get('count')) > 0 ? intval(Input::get('count')) : 1;
$count = intval($request->get('count')) > 0 ? intval($request->get('count')) : 1;
$keys = array_keys(config('firefly.rule-actions'));
$actions = [];
foreach ($keys as $key) {
@@ -269,18 +271,18 @@ class JsonController extends Controller
}
/**
* @param $what
* @param JournalCollectorInterface $collector
* @param string $what
*
* @return \Illuminate\Http\JsonResponse
*/
public function transactionJournals($what)
public function transactionJournals(JournalCollectorInterface $collector, string $what)
{
$descriptions = [];
$type = config('firefly.transactionTypesByWhat.' . $what);
$types = [$type];
// use journal collector instead:
$collector = new JournalCollector(auth()->user());
$collector->setTypes($types)->setLimit(100)->setPage(1);
$journals = $collector->getJournals();
foreach ($journals as $j) {
@@ -296,11 +298,13 @@ class JsonController extends Controller
}
/**
* @param Request $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function trigger()
public function trigger(Request $request)
{
$count = intval(Input::get('count')) > 0 ? intval(Input::get('count')) : 1;
$count = intval($request->get('count')) > 0 ? intval($request->get('count')) : 1;
$keys = array_keys(config('firefly.rule-triggers'));
$triggers = [];
foreach ($keys as $key) {

View File

@@ -117,7 +117,7 @@ class NewUserController extends Controller
'virtualBalance' => 0,
'active' => true,
'accountRole' => 'defaultAsset',
'openingBalance' => round($request->input('bank_balance'), 2),
'openingBalance' => round($request->input('bank_balance'), 12),
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => intval($request->input('amount_currency_id_bank_balance')),
];
@@ -142,7 +142,7 @@ class NewUserController extends Controller
'virtualBalance' => 0,
'active' => true,
'accountRole' => 'savingAsset',
'openingBalance' => round($request->input('savings_balance'), 2),
'openingBalance' => round($request->input('savings_balance'), 12),
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => intval($request->input('amount_currency_id_savings_balance')),
];
@@ -163,7 +163,7 @@ class NewUserController extends Controller
'name' => 'Credit card',
'iban' => null,
'accountType' => 'asset',
'virtualBalance' => round($request->get('credit_card_limit'), 2),
'virtualBalance' => round($request->get('credit_card_limit'), 12),
'active' => true,
'accountRole' => 'ccAsset',
'openingBalance' => null,

View File

@@ -20,8 +20,8 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Input;
use Log;
use Preferences;
use Response;
@@ -219,7 +219,7 @@ class PiggyBankController extends Controller
$accounts = [];
/** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) {
$piggyBank->savedSoFar = round($piggyBank->currentRelevantRep()->currentamount, 2);
$piggyBank->savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$piggyBank->percentage = $piggyBank->savedSoFar != 0 ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
$piggyBank->leftToSave = bcsub($piggyBank->targetamount, strval($piggyBank->savedSoFar));
$piggyBank->percentage = $piggyBank->percentage > 100 ? 100 : $piggyBank->percentage;
@@ -234,7 +234,7 @@ class PiggyBankController extends Controller
'balance' => Steam::balanceIgnoreVirtual($account, $end),
'leftForPiggyBanks' => $piggyBank->leftOnAccount($end),
'sumOfSaved' => strval($piggyBank->savedSoFar),
'sumOfTargets' => strval(round($piggyBank->targetamount, 2)),
'sumOfTargets' => $piggyBank->targetamount,
'leftToSave' => $piggyBank->leftToSave,
];
} else {
@@ -248,13 +248,14 @@ class PiggyBankController extends Controller
}
/**
* @param Request $request
* @param PiggyBankRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
*/
public function order(PiggyBankRepositoryInterface $repository)
public function order(Request $request, PiggyBankRepositoryInterface $repository)
{
$data = Input::get('order');
$data = $request->get('order');
// set all users piggy banks to zero:
$repository->reset();
@@ -270,22 +271,24 @@ class PiggyBankController extends Controller
}
/**
* @param Request $request
* @param PiggyBankRepositoryInterface $repository
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postAdd(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
public function postAdd(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$amount = strval(round(Input::get('amount'), 2));
$amount = $request->get('amount');
Log::debug(sprintf('Found amount is %s', $amount));
/** @var Carbon $date */
$date = session('end', Carbon::now()->endOfMonth());
$leftOnAccount = $piggyBank->leftOnAccount($date);
$savedSoFar = strval($piggyBank->currentRelevantRep()->currentamount);
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
$maxAmount = round(min($leftOnAccount, $leftToSave), 2);
$maxAmount = strval(min(round($leftOnAccount, 12), round($leftToSave, 12)));
if ($amount <= $maxAmount) {
if (bccomp($amount, $maxAmount) <= 0) {
$repetition = $piggyBank->currentRelevantRep();
$currentAmount = $repetition->currentamount ?? '0';
$repetition->currentamount = bcadd($currentAmount, $amount);
@@ -309,18 +312,19 @@ class PiggyBankController extends Controller
}
/**
* @param Request $request
* @param PiggyBankRepositoryInterface $repository
* @param PiggyBank $piggyBank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postRemove(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
public function postRemove(Request $request, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$amount = strval(round(Input::get('amount'), 2));
$amount = strval(round($request->get('amount'), 12));
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
if ($amount <= $savedSoFar) {
if (bccomp($amount, $savedSoFar) === -1) {
$repetition = $piggyBank->currentRelevantRep();
$repetition->currentamount = bcsub($repetition->currentamount, $amount);
$repetition->save();
@@ -394,7 +398,7 @@ class PiggyBankController extends Controller
Session::flash('success', strval(trans('firefly.stored_piggy_bank', ['name' => e($piggyBank->name)])));
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
if (intval($request->get('create_another')) === 1) {
Session::put('piggy-banks.create.fromStore', true);
return redirect(route('piggy-banks.create'))->withInput();
@@ -420,7 +424,7 @@ class PiggyBankController extends Controller
Session::flash('success', strval(trans('firefly.updated_piggy_bank', ['name' => e($piggyBank->name)])));
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
Session::put('piggy-banks.edit.fromUpdate', true);
return redirect(route('piggy-banks.edit', [$piggyBank->id]));

View File

@@ -17,7 +17,7 @@ namespace FireflyIII\Http\Controllers\Popup;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
@@ -102,7 +102,8 @@ class ReportController extends Controller
switch (true) {
case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)):
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector
->setAccounts(new Collection([$account]))
->setRange($attributes['startDate'], $attributes['endDate'])
@@ -112,8 +113,8 @@ class ReportController extends Controller
break;
case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)):
$budget->name = strval(trans('firefly.no_budget'));
// collector
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector
->setAccounts(new Collection([$account]))
->setTypes($types)
@@ -122,8 +123,8 @@ class ReportController extends Controller
$journals = $collector->getJournals();
break;
case ($role === BalanceLine::ROLE_DIFFROLE):
// journals no budget, not corrected by a tag.
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector
->setAccounts(new Collection([$account]))
->setTypes($types)
@@ -134,7 +135,7 @@ class ReportController extends Controller
$budget->name = strval(trans('firefly.leftUnbalanced'));
$journals = $journals->filter(
function (Transaction $transaction) {
$tags = $transaction->transactionJournal->tags()->where('tagMode', 'balancingAct')->count();
$tags = $transaction->transactionJournal->tags()->where('tagMode', 'balancingAct')->count();
if ($tags === 0) {
return true;
}
@@ -167,7 +168,8 @@ class ReportController extends Controller
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$budget = $repository->find(intval($attributes['budgetId']));
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector
->setAccounts($attributes['accounts'])
@@ -200,8 +202,8 @@ class ReportController extends Controller
$repository = app(CategoryRepositoryInterface::class);
$category = $repository->find(intval($attributes['categoryId']));
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
// get journal collector instead:
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($attributes['accounts'])->setTypes($types)
->setRange($attributes['startDate'], $attributes['endDate'])
->setCategory($category);
@@ -225,9 +227,10 @@ class ReportController extends Controller
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
$collector = new JournalCollector(auth()->user());
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [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
@@ -262,7 +265,8 @@ class ReportController extends Controller
$repository = app(AccountRepositoryInterface::class);
$account = $repository->find(intval($attributes['accountId']));
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [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

View File

@@ -113,6 +113,7 @@ class PreferencesController extends Controller
* @param TokenFormRequest $request
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation.
*/
public function postCode(TokenFormRequest $request)
{

View File

@@ -48,8 +48,9 @@ class OperationsController extends Controller
if ($cache->has()) {
return $cache->get();
}
$expenses = $this->getExpenseReport($start, $end, $accounts);
$result = view('reports.partials.expenses', compact('expenses'))->render();
$entries = $this->getExpenseReport($start, $end, $accounts);
$type = 'expense-entry';
$result = view('reports.partials.income-expenses', compact('entries', 'type'))->render();
$cache->store($result);
return $result;
@@ -74,9 +75,10 @@ class OperationsController extends Controller
if ($cache->has()) {
return $cache->get();
}
$income = $this->getIncomeReport($start, $end, $accounts);
$entries = $this->getIncomeReport($start, $end, $accounts);
$type = 'income-entry';
$result = view('reports.partials.income-expenses', compact('entries', 'type'))->render();
$result = view('reports.partials.income', compact('income'))->render();
$cache->store($result);
return $result;
@@ -227,15 +229,22 @@ class OperationsController extends Controller
$name = $transaction->opposing_account_name;
if (!isset($expenses[$opposingId])) {
$expenses[$opposingId] = [
'id' => $opposingId,
'name' => $name,
'sum' => '0',
'count' => 0,
'id' => $opposingId,
'name' => $name,
'sum' => '0',
'average' => '0',
'count' => 0,
];
}
$expenses[$opposingId]['sum'] = bcadd($expenses[$opposingId]['sum'], $transaction->transaction_amount);
$expenses[$opposingId]['count']++;
}
// do averages:
foreach ($expenses as $key => $entry) {
if ($expenses[$key]['count'] > 1) {
$expenses[$key]['average'] = bcdiv($expenses[$key]['sum'], strval($expenses[$key]['count']));
}
}
return $expenses;

View File

@@ -23,7 +23,6 @@ use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Rules\TransactionMatcher;
use Illuminate\Http\Request;
use Input;
use Preferences;
use Response;
use Session;
@@ -58,11 +57,12 @@ class RuleController extends Controller
/**
* Create a new rule. It will be stored under the given $ruleGroup.
*
* @param Request $request
* @param RuleGroup $ruleGroup
*
* @return View
*/
public function create(RuleGroup $ruleGroup)
public function create(Request $request, RuleGroup $ruleGroup)
{
// count for possible present previous entered triggers/actions.
$triggerCount = 0;
@@ -73,13 +73,13 @@ class RuleController extends Controller
$oldActions = [];
// has old input?
if (Input::old()) {
if ($request->old()) {
// process old triggers.
$oldTriggers = $this->getPreviousTriggers();
$oldTriggers = $this->getPreviousTriggers($request);
$triggerCount = count($oldTriggers);
// process old actions
$oldActions = $this->getPreviousActions();
$oldActions = $this->getPreviousActions($request);
$actionCount = count($oldActions);
}
@@ -154,12 +154,13 @@ class RuleController extends Controller
}
/**
* @param Request $request
* @param RuleRepositoryInterface $repository
* @param Rule $rule
*
* @return View
*/
public function edit(RuleRepositoryInterface $repository, Rule $rule)
public function edit(Request $request, RuleRepositoryInterface $repository, Rule $rule)
{
$oldTriggers = $this->getCurrentTriggers($rule);
$triggerCount = count($oldTriggers);
@@ -167,10 +168,10 @@ class RuleController extends Controller
$actionCount = count($oldActions);
// has old input?
if (Input::old()) {
$oldTriggers = $this->getPreviousTriggers();
if ($request->old()) {
$oldTriggers = $this->getPreviousTriggers($request);
$triggerCount = count($oldTriggers);
$oldActions = $this->getPreviousActions();
$oldActions = $this->getPreviousActions($request);
$actionCount = count($oldActions);
}
@@ -255,7 +256,7 @@ class RuleController extends Controller
Session::flash('success', trans('firefly.stored_new_rule', ['title' => $rule->title]));
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('rules.create.fromStore', true);
@@ -342,7 +343,7 @@ class RuleController extends Controller
Session::flash('success', trans('firefly.updated_rule', ['title' => $rule->title]));
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('rules.edit.fromUpdate', true);
@@ -461,22 +462,24 @@ class RuleController extends Controller
}
/**
* @param Request $request
*
* @return array
*/
private function getPreviousActions()
private function getPreviousActions(Request $request)
{
$newIndex = 0;
$actions = [];
/** @var array $oldActions */
$oldActions = is_array(Input::old('rule-action')) ? Input::old('rule-action') : [];
$oldActions = is_array($request->old('rule-action')) ? $request->old('rule-action') : [];
foreach ($oldActions as $index => $entry) {
$count = ($newIndex + 1);
$checked = isset(Input::old('rule-action-stop')[$index]) ? true : false;
$checked = isset($request->old('rule-action-stop')[$index]) ? true : false;
$actions[] = view(
'rules.partials.action',
[
'oldTrigger' => $entry,
'oldValue' => Input::old('rule-action-value')[$index],
'oldValue' => $request->old('rule-action-value')[$index],
'oldChecked' => $checked,
'count' => $count,
]
@@ -488,22 +491,24 @@ class RuleController extends Controller
}
/**
* @param Request $request
*
* @return array
*/
private function getPreviousTriggers()
private function getPreviousTriggers(Request $request)
{
$newIndex = 0;
$triggers = [];
/** @var array $oldTriggers */
$oldTriggers = is_array(Input::old('rule-trigger')) ? Input::old('rule-trigger') : [];
$oldTriggers = is_array($request->old('rule-trigger')) ? $request->old('rule-trigger') : [];
foreach ($oldTriggers as $index => $entry) {
$count = ($newIndex + 1);
$oldChecked = isset(Input::old('rule-trigger-stop')[$index]) ? true : false;
$oldChecked = isset($request->old('rule-trigger-stop')[$index]) ? true : false;
$triggers[] = view(
'rules.partials.trigger',
[
'oldTrigger' => $entry,
'oldValue' => Input::old('rule-trigger-value')[$index],
'oldValue' => $request->old('rule-trigger-value')[$index],
'oldChecked' => $oldChecked,
'count' => $count,
]

View File

@@ -22,7 +22,7 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use Input;
use Illuminate\Http\Request;
use Preferences;
use Session;
use URL;
@@ -94,17 +94,17 @@ class RuleGroupController extends Controller
}
/**
* @param Request $request
* @param RuleGroupRepositoryInterface $repository
*
* @param RuleGroup $ruleGroup
*
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup)
public function destroy(Request $request, RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup)
{
$title = $ruleGroup->title;
$moveTo = auth()->user()->ruleGroups()->find(intval(Input::get('move_rules_before_delete')));
$moveTo = auth()->user()->ruleGroups()->find(intval($request->get('move_rules_before_delete')));
$repository->destroy($ruleGroup, $moveTo);
@@ -218,7 +218,7 @@ class RuleGroupController extends Controller
Session::flash('success', strval(trans('firefly.created_new_rule_group', ['title' => $ruleGroup->title])));
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('rule-groups.create.fromStore', true);
@@ -263,7 +263,7 @@ class RuleGroupController extends Controller
Session::flash('success', strval(trans('firefly.updated_rule_group', ['title' => $ruleGroup->title])));
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('rule-groups.edit.fromUpdate', true);

View File

@@ -13,13 +13,13 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\TagFormRequest;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Input;
use Preferences;
use Session;
use URL;
@@ -68,17 +68,20 @@ class TagController extends Controller
}
/**
* @param Request $request
*
* @return View
*/
public function create()
public function create(Request $request)
{
$subTitle = trans('firefly.new_tag');
$subTitleIcon = 'fa-tag';
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
$preFilled = [
'tagMode' => 'nothing',
];
if (!Input::old('tagMode')) {
if (!$request->old('tagMode')) {
Session::flash('preFilled', $preFilled);
}
// put previous url in session if not redirect from store (not "create another").
@@ -89,7 +92,7 @@ class TagController extends Controller
Session::flash('gaEventCategory', 'tags');
Session::flash('gaEventAction', 'create');
return view('tags.create', compact('subTitle', 'subTitleIcon'));
return view('tags.create', compact('subTitle', 'subTitleIcon', 'apiKey'));
}
/**
@@ -136,6 +139,7 @@ class TagController extends Controller
{
$subTitle = trans('firefly.edit_tag', ['tag' => $tag->tag]);
$subTitleIcon = 'fa-tag';
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
/*
* Default tag options (again)
@@ -165,7 +169,7 @@ class TagController extends Controller
Session::flash('gaEventCategory', 'tags');
Session::flash('gaEventAction', 'edit');
return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'tagOptions'));
return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'tagOptions', 'apiKey'));
}
/**
@@ -206,21 +210,21 @@ class TagController extends Controller
}
/**
* @param Tag $tag
* @param Request $request
* @param JournalCollectorInterface $collector
* @param Tag $tag
*
* @return View
*/
public function show(Tag $tag)
public function show(Request $request, JournalCollectorInterface $collector, Tag $tag)
{
$subTitle = $tag->tag;
$subTitleIcon = 'fa-tag';
$page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page'));
$page = intval($request->get('page')) === 0 ? 1 : intval($request->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);
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTag($tag)->withBudgetInformation()->withCategoryInformation();
$journals = $collector->getPaginatedJournals();
$journals->setPath('tags/show/' . $tag->id);
@@ -248,7 +252,7 @@ class TagController extends Controller
Session::flash('success', strval(trans('firefly.created_tag', ['tag' => e($data['tag'])])));
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('tags.create.fromStore', true);
@@ -275,7 +279,7 @@ class TagController extends Controller
Session::flash('success', strval(trans('firefly.updated_tag', ['tag' => e($data['tag'])])));
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('tags.edit.fromUpdate', true);

View File

@@ -217,8 +217,8 @@ class ConvertController extends Controller
switch ($joined) {
default:
throw new FireflyException('Cannot handle ' . $joined);
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: // one
case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: // six
$data = [
'name' => $data['source_account_revenue'],
'accountType' => 'revenue',
@@ -228,14 +228,14 @@ class ConvertController extends Controller
];
$source = $accountRepository->store($data);
break;
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five
case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: // two
case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: // five
$source = $sourceAccount;
break;
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three
case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: // three
$source = $destinationAccount;
break;
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: // four
$source = $accountRepository->find(intval($data['source_account_asset']));
break;
}

View File

@@ -214,7 +214,7 @@ class MassController extends Controller
'source_account_name' => $sourceAccountName,
'destination_account_id' => intval($destAccountId),
'destination_account_name' => $destAccountName,
'amount' => round($request->get('amount')[$journal->id], 4),
'amount' => round($request->get('amount')[$journal->id], 12),
'currency_id' => intval($request->get('amount_currency_id_amount_' . $journal->id)),
'date' => new Carbon($request->get('date')[$journal->id]),
'interest_date' => $journal->interest_date,

View File

@@ -265,8 +265,8 @@ class SingleController extends Controller
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
}
$this->attachments->saveAttachmentsForModel($journal);
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
$this->attachments->saveAttachmentsForModel($journal, $files);
// store the journal only, flash the rest.
if (count($this->attachments->getErrors()->get('attachments')) > 0) {
@@ -315,7 +315,8 @@ class SingleController extends Controller
$data = $request->getJournalData();
$journal = $repository->update($journal, $data);
$this->attachments->saveAttachmentsForModel($journal);
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
$this->attachments->saveAttachmentsForModel($journal, $files);
// flash errors
if (count($this->attachments->getErrors()->get('attachments')) > 0) {

View File

@@ -138,9 +138,9 @@ class SplitController extends Controller
$data = $this->arrayFromInput($request);
$journal = $repository->updateSplitJournal($journal, $data);
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
// save attachments:
$this->attachments->saveAttachmentsForModel($journal);
$this->attachments->saveAttachmentsForModel($journal, $files);
event(new UpdatedTransactionJournal($journal));
// update, get events by date and sort DESC
@@ -257,7 +257,7 @@ class SplitController extends Controller
'source_account_name' => $transaction['source_account_name'],
'destination_account_id' => $transaction['destination_account_id'],
'destination_account_name' => $transaction['destination_account_name'],
'amount' => round($transaction['destination_amount'], 2),
'amount' => round($transaction['destination_amount'], 12),
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'],
];
@@ -292,7 +292,7 @@ class SplitController extends Controller
'source_account_name' => $transaction['source_account_name'] ?? '',
'destination_account_id' => $transaction['destination_account_id'] ?? 0,
'destination_account_name' => $transaction['destination_account_name'] ?? '',
'amount' => round($transaction['amount'] ?? 0, 2),
'amount' => round($transaction['amount'] ?? 0, 12),
'budget_id' => isset($transaction['budget_id']) ? intval($transaction['budget_id']) : 0,
'category' => $transaction['category'] ?? '',
];

View File

@@ -71,10 +71,10 @@ class TransactionController extends Controller
$start = session('start', Navigation::startOfPeriod(new Carbon, $range));
$end = session('end', Navigation::endOfPeriod(new Carbon, $range));
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
$collector->setRange($start, $end);
$collector->setRange($start, $end)->withBudgetInformation()->withCategoryInformation();
// do not filter transfers if $what = transfer.
if (!in_array($what, ['transfer', 'transfers'])) {
@@ -123,7 +123,7 @@ class TransactionController extends Controller
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->withBudgetInformation()->withCategoryInformation();
// do not filter transfers if $what = transfer.
if (!in_array($what, ['transfer', 'transfers'])) {
@@ -160,9 +160,10 @@ class TransactionController extends Controller
Log::debug(sprintf('Transaction index by date will show between %s and %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts();
$collector->setRange($start, $end);
$collector->setRange($start, $end)->withBudgetInformation()->withCategoryInformation();
// do not filter transfers if $what = transfer.
if (!in_array($what, ['transfer', 'transfers'])) {

View File

@@ -17,8 +17,6 @@ use FireflyIII\Http\Middleware\AuthenticateTwoFactor;
use FireflyIII\Http\Middleware\Binder;
use FireflyIII\Http\Middleware\EncryptCookies;
use FireflyIII\Http\Middleware\IsAdmin;
use FireflyIII\Http\Middleware\IsConfirmed;
use FireflyIII\Http\Middleware\IsNotConfirmed;
use FireflyIII\Http\Middleware\Range;
use FireflyIII\Http\Middleware\RedirectIfAuthenticated;
use FireflyIII\Http\Middleware\RedirectIfTwoFactorAuthenticated;
@@ -124,7 +122,6 @@ class Kernel extends HttpKernel
SubstituteBindings::class,
Authenticate::class,
AuthenticateTwoFactor::class,
IsNotConfirmed::class,
],
// MUST be logged in
@@ -153,7 +150,6 @@ class Kernel extends HttpKernel
SubstituteBindings::class,
Authenticate::class,
AuthenticateTwoFactor::class,
IsConfirmed::class,
Range::class,
Binder::class,
],
@@ -171,11 +167,9 @@ class Kernel extends HttpKernel
SubstituteBindings::class,
Authenticate::class,
AuthenticateTwoFactor::class,
IsConfirmed::class,
IsAdmin::class,
Range::class,
Binder::class,
],

View File

@@ -26,8 +26,7 @@ use Illuminate\Support\Facades\Auth;
class IsAdmin
{
/**
* Handle an incoming request. User account must be confirmed for this routine to let
* the user pass.
* Handle an incoming request. Must be admin.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next

View File

@@ -1,67 +0,0 @@
<?php
/**
* IsConfirmed.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\Middleware;
use Closure;
use FireflyConfig;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Preferences;
/**
* Class IsConfirmed
*
* @package FireflyIII\Http\Middleware
*/
class IsConfirmed
{
/**
* Handle an incoming request. User account must be confirmed for this routine to let
* the user pass.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
*
* @return mixed
*/
public function handle(Request $request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
}
return redirect()->guest('login');
}
// must the user be confirmed in the first place?
$confirmPreference = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'));
$mustConfirmAccount = false;
if (!is_null($confirmPreference)) {
$mustConfirmAccount = $confirmPreference->data;
}
// user must be logged in, then continue:
$isConfirmed = Preferences::get('user_confirmed', false)->data;
if ($isConfirmed === false && $mustConfirmAccount === true) {
// user account is not confirmed, redirect to
// confirmation page:
return redirect(route('confirmation_error'));
}
return $next($request);
}
}

View File

@@ -1,64 +0,0 @@
<?php
/**
* IsNotConfirmed.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\Middleware;
use Closure;
use FireflyConfig;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Log;
use Preferences;
/**
* Class IsNotConfirmed
*
* @package FireflyIII\Http\Middleware
*/
class IsNotConfirmed
{
/**
* Handle an incoming request. User account must be confirmed for this routine to let
* the user pass.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
*
* @return mixed
*/
public function handle(Request $request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
}
return redirect()->guest('login');
}
// must the user be confirmed in the first place?
$mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data;
Log::debug(sprintf('mustConfirmAccount is %s', $mustConfirmAccount));
// user must be logged in, then continue:
$isConfirmed = Preferences::get('user_confirmed', false)->data;
Log::debug(sprintf('isConfirmed is %s', $isConfirmed));
if ($isConfirmed || $mustConfirmAccount === false) {
Log::debug('User is confirmed or user does not have to confirm account. Redirect home.');
// user account is confirmed, simply send them home.
return redirect(route('home'));
}
return $next($request);
}
}

View File

@@ -13,6 +13,7 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Middleware;
use Amount;
use App;
use Carbon\Carbon;
use Closure;
@@ -109,19 +110,21 @@ class Range
$monthFormat = (string)trans('config.month');
$monthAndDayFormat = (string)trans('config.month_and_day');
$dateTimeFormat = (string)trans('config.date_time');
$defaultCurrency = Amount::getDefaultCurrency();
// change localeconv to a new array:
$numberFormatter = numfmt_create($lang, NumberFormatter::CURRENCY);
$localeconv = [
'mon_decimal_point' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL)),
'mon_thousands_sep' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL)),
'frac_digits' => $numberFormatter->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS),
'frac_digits' => $defaultCurrency->decimal_places,
];
View::share('monthFormat', $monthFormat);
View::share('monthAndDayFormat', $monthAndDayFormat);
View::share('dateTimeFormat', $dateTimeFormat);
View::share('language', $lang);
View::share('localeconv', $localeconv);
View::share('defaultCurrency', $defaultCurrency);
}
/**

View File

@@ -43,13 +43,13 @@ class AccountFormRequest extends Request
'active' => intval($this->input('active')) === 1,
'accountType' => $this->input('what'),
'currency_id' => intval($this->input('currency_id')),
'virtualBalance' => round($this->input('virtualBalance'), 2),
'virtualBalance' => round($this->input('virtualBalance'), 12),
'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')),
'iban' => trim(strval($this->input('iban'))),
'BIC' => trim(strval($this->input('BIC'))),
'accountNumber' => trim(strval($this->input('accountNumber'))),
'accountRole' => $this->input('accountRole'),
'openingBalance' => round($this->input('openingBalance'), 2),
'openingBalance' => round($this->input('openingBalance'), 12),
'openingBalanceDate' => new Carbon((string)$this->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($this->input('amount_currency_id_openingBalance')),
'ccType' => $this->input('ccType'),

View File

@@ -40,10 +40,10 @@ class BillFormRequest extends Request
return [
'name' => $this->get('name'),
'match' => $this->get('match'),
'amount_min' => round($this->get('amount_min'), 2),
'amount_min' => round($this->get('amount_min'), 12),
'amount_currency_id_amount_min' => intval($this->get('amount_currency_id_amount_min')),
'amount_currency_id_amount_max' => intval($this->get('amount_currency_id_amount_max')),
'amount_max' => round($this->get('amount_max'), 2),
'amount_max' => round($this->get('amount_max'), 12),
'date' => new Carbon($this->get('date')),
'repeat_freq' => $this->get('repeat_freq'),
'skip' => intval($this->get('skip')),

View File

@@ -36,14 +36,8 @@ class ConfigurationRequest extends Request
public function getConfigurationData(): array
{
return [
'single_user_mode' => intval($this->get('single_user_mode')) === 1,
'must_confirm_account' => intval($this->get('must_confirm_account')) === 1,
'is_demo_site' => intval($this->get('is_demo_site')) === 1,
'mail_for_lockout' => intval($this->get('mail_for_lockout')) === 1,
'mail_for_blocked_domain' => intval($this->get('mail_for_blocked_domain')) === 1,
'mail_for_blocked_email' => intval($this->get('mail_for_blocked_email')) === 1,
'mail_for_bad_login' => intval($this->get('mail_for_bad_login')) === 1,
'mail_for_blocked_login' => intval($this->get('mail_for_blocked_login')) === 1,
'single_user_mode' => intval($this->get('single_user_mode')) === 1,
'is_demo_site' => intval($this->get('is_demo_site')) === 1,
];
}
@@ -53,14 +47,8 @@ class ConfigurationRequest extends Request
public function rules()
{
$rules = [
'single_user_mode' => 'between:0,1|numeric',
'must_confirm_account' => 'between:0,1|numeric',
'is_demo_site' => 'between:0,1|numeric',
'mail_for_lockout' => 'between:0,1|numeric',
'mail_for_blocked_domain' => 'between:0,1|numeric',
'mail_for_blocked_email' => 'between:0,1|numeric',
'mail_for_bad_login' => 'between:0,1|numeric',
'mail_for_blocked_login' => 'between:0,1|numeric',
'single_user_mode' => 'between:0,1|numeric',
'is_demo_site' => 'between:0,1|numeric',
];
return $rules;

View File

@@ -36,9 +36,10 @@ class CurrencyFormRequest extends Request
public function getCurrencyData()
{
return [
'name' => $this->get('name'),
'code' => $this->get('code'),
'symbol' => $this->get('symbol'),
'name' => $this->get('name'),
'code' => $this->get('code'),
'symbol' => $this->get('symbol'),
'decimal_places' => intval($this->get('decimal_places')),
];
}
@@ -49,15 +50,17 @@ class CurrencyFormRequest extends Request
{
$rules = [
'code' => 'required|min:3|max:3|unique:transaction_currencies,code',
'name' => 'required|max:48|min:1|unique:transaction_currencies,name',
'symbol' => 'required|min:1|max:8|unique:transaction_currencies,symbol',
'name' => 'required|max:48|min:1|unique:transaction_currencies,name',
'code' => 'required|min:3|max:3|unique:transaction_currencies,code',
'symbol' => 'required|min:1|max:8|unique:transaction_currencies,symbol',
'decimal_places' => 'required|min:0|max:12|numeric',
];
if (intval($this->get('id')) > 0) {
$rules = [
'code' => 'required|min:3|max:3',
'name' => 'required|max:48|min:1',
'symbol' => 'required|min:1|max:8',
'name' => 'required|max:48|min:1',
'code' => 'required|min:3|max:3',
'symbol' => 'required|min:1|max:8',
'decimal_places' => 'required|min:0|max:12|numeric',
];
}

View File

@@ -59,7 +59,7 @@ class JournalFormRequest extends Request
// transaction / journal data:
'description' => $this->getFieldOrEmptyString('description'),
'amount' => round($this->get('amount'), 2),
'amount' => round($this->get('amount'), 12),
'budget_id' => intval($this->get('budget_id')),
'category' => $this->getFieldOrEmptyString('category'),
'source_account_id' => intval($this->get('source_account_id')),

View File

@@ -41,7 +41,7 @@ class PiggyBankFormRequest extends Request
'name' => trim($this->get('name')),
'startdate' => new Carbon,
'account_id' => intval($this->get('account_id')),
'targetamount' => round($this->get('targetamount'), 2),
'targetamount' => round($this->get('targetamount'), 12),
'targetdate' => strlen(strval($this->get('targetdate'))) > 0 ? new Carbon($this->get('targetdate')) : null,
'note' => trim(strval($this->get('note'))),
];

View File

@@ -93,7 +93,7 @@ class SplitJournalFormRequest extends Request
$category = $this->get('category')[$index] ?? '';
$transaction = [
'description' => $description,
'amount' => round($this->get('amount')[$index], 2),
'amount' => round($this->get('amount')[$index], 12),
'budget_id' => $this->get('budget_id')[$index] ? intval($this->get('budget_id')[$index]) : 0,
'category' => trim($category),
'source_account_id' => isset($this->get('source_account_id')[$index])

View File

@@ -17,9 +17,9 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Category;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
@@ -274,11 +274,20 @@ Breadcrumbs::register(
);
Breadcrumbs::register(
'budgets.show.repetition', function (BreadCrumbGenerator $breadcrumbs, Budget $budget, LimitRepetition $repetition) {
'budgets.show.limit', function (BreadCrumbGenerator $breadcrumbs, Budget $budget, BudgetLimit $budgetLimit) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push(e($budget->name), route('budgets.show.repetition', [$budget->id, $repetition->id]));
$breadcrumbs->push(e($budget->name), route('budgets.show', [$budget->id]));
$title = trans(
'firefly.budget_in_period_breadcrumb', [
'name' => $budget->name,
'start' => $budgetLimit->start_date->formatLocalized(strval(trans('config.month_and_day'))),
'end' => $budgetLimit->end_date->formatLocalized(strval(trans('config.month_and_day'))),
]
);
$breadcrumbs->push(
Navigation::periodShow($repetition->startdate, $repetition->budgetLimit->repeat_freq), route('budgets.show', [$budget->id, $repetition->id])
$title, route('budgets.show.limit', [$budget->id, $budgetLimit->id])
);
}
);
@@ -320,6 +329,14 @@ Breadcrumbs::register(
}
);
Breadcrumbs::register(
'categories.show.all', function (BreadCrumbGenerator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push(e($category->name) . '(' . strtolower(trans('firefly.all_periods')) . ')', route('categories.show.all', [$category->id]));
}
);
Breadcrumbs::register(
'categories.show.date', function (BreadCrumbGenerator $breadcrumbs, Category $category, Carbon $date) {

View File

@@ -62,7 +62,7 @@ class Amount extends BasicConverter implements ConverterInterface
$this->setCertainty(90);
return round(floatval($value), 4);
return round(floatval($value), 12);
}
}

View File

@@ -14,7 +14,7 @@ declare(strict_types = 1);
namespace FireflyIII\Jobs;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
@@ -155,7 +155,8 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
*/
protected function collectJournals()
{
$collector = new JournalCollector(auth()->user());
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class, [auth()->user()]);
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
return $collector->getJournals();

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