Compare commits

..

282 Commits
3.3.2 ... 3.3.8

Author SHA1 Message Date
James Cole
23faef845c Merge branch 'release/3.3.8' 2015-04-20 18:07:02 +02:00
James Cole
daf859b977 Updated read me. 2015-04-20 18:06:37 +02:00
James Cole
f47ba6c977 Update example file. 2015-04-20 18:06:21 +02:00
James Cole
4b9b207d92 Removed development references. 2015-04-20 17:59:04 +02:00
James Cole
875cbf66af 1 should be 0 2015-04-19 19:10:53 +02:00
James Cole
412d6d4fd7 Small CSS fix for piggy banks [skip ci] 2015-04-19 17:43:04 +02:00
James Cole
91c6deeb1f Renamed another field. 2015-04-19 14:28:17 +02:00
James Cole
6d1978fd9a Showed the wrong value. 2015-04-19 14:27:08 +02:00
James Cole
52d23b6ef5 Referred to an id which turned out to be a index. 2015-04-19 14:19:25 +02:00
James Cole
0656ccbdd9 Fixed a bug where the query would include deleted transactions. 2015-04-19 13:57:43 +02:00
James Cole
e774ebd0a3 Possible bug in account create thing. [skip ci] 2015-04-18 16:04:21 +02:00
James Cole
edbda32a84 Fix balance. 2015-04-13 21:13:05 +02:00
James Cole
721fa04e3c Added home screen information about piggy banks. 2015-04-13 20:43:58 +02:00
James Cole
9fd8d8915d amount > queryAmount 2015-04-12 10:53:53 +02:00
James Cole
7a9f5ebdd1 Added "what" variable. 2015-04-12 10:22:15 +02:00
James Cole
a3423f0321 Add the correct path to category overview. 2015-04-12 10:19:37 +02:00
James Cole
c37e9a4467 Covered the JSON controller. 2015-04-11 19:59:41 +02:00
James Cole
6157d82a0b time format 2015-04-11 18:52:03 +02:00
James Cole
38b27fec92 Without moment.js 2015-04-11 18:49:57 +02:00
James Cole
c43439bb68 Test for date. 2015-04-11 18:47:37 +02:00
James Cole
8d9561d7a5 Removed data-value. 2015-04-11 18:45:01 +02:00
James Cole
2a72cce3b7 Included date format. 2015-04-11 15:11:45 +02:00
James Cole
d3bbb6fb1f Removed date value. 2015-04-11 15:11:19 +02:00
James Cole
7b6723e9a2 Update date format. 2015-04-11 15:10:29 +02:00
James Cole
1ee741460d Updated empty entries as well. 2015-04-11 15:09:48 +02:00
James Cole
967b0b493b Switched dates around. 2015-04-11 15:07:42 +02:00
James Cole
b53aaf7dde Fixed sort format. 2015-04-11 15:06:10 +02:00
James Cole
714c13bdbf Include moment.js 2015-04-11 15:04:16 +02:00
James Cole
651a4fd3cc Sortable accounts. 2015-04-11 15:01:42 +02:00
James Cole
505aee22bb Sortable categories. 2015-04-11 14:53:22 +02:00
James Cole
ca3d59dc33 Another fix. 2015-04-11 12:51:58 +02:00
James Cole
13890e32a1 Fixed a view and a botched include. 2015-04-11 12:50:56 +02:00
James Cole
df8976eabe Journals that no longer match a bill will be removed. 2015-04-11 12:40:47 +02:00
James Cole
3e01daa172 Query & loop optimizations. 2015-04-11 12:36:17 +02:00
James Cole
f6999f355b Some foreach loop cleaning up. 2015-04-11 12:27:56 +02:00
James Cole
5c06b45eb1 Code cleanup. 2015-04-11 07:32:11 +02:00
James Cole
1802bb967a Moved JSON money boxes to individual functions. 2015-04-11 07:24:07 +02:00
James Cole
d8d92f147f Removed logging, found bug. 2015-04-10 07:29:36 +02:00
James Cole
4a95bdd8ba Added temporary debug to google chart. 2015-04-10 07:25:24 +02:00
James Cole
db5d94d956 Added temporary debug entries. 2015-04-10 07:19:45 +02:00
James Cole
06f2e34bb5 Renamed the amount variable so it stops interfering with another variable 2015-04-10 06:57:39 +02:00
James Cole
2ef7a01945 Fixed a bug where the bill chart and boxes would inaccurately reflect the amount of bills yet to pay. 2015-04-10 06:52:36 +02:00
James Cole
d3f642551d Fixed a small bug in the bill-chart and simplified yet another Json box. 2015-04-09 21:36:58 +02:00
James Cole
490997157e Switched two queries around. 2015-04-09 21:30:35 +02:00
James Cole
2432af7883 Simplified the in and out-box. 2015-04-09 21:29:58 +02:00
James Cole
7abeece3f0 Simplified the "out"-box. 2015-04-09 21:27:35 +02:00
James Cole
3851652821 Renamed various fields from 'amount' to 'queryAmount' to prevent interfering with the getAmountAttribute property. 2015-04-09 21:18:18 +02:00
James Cole
1d67d2250a Renamed various fields from 'amount' to 'queryAmount' to prevent interfering with the getAmountAttribute property. 2015-04-09 21:16:35 +02:00
James Cole
37f40d8637 Renamed various fields from 'amount' to 'queryAmount' to prevent interfering with the getAmountAttribute property. 2015-04-09 21:14:15 +02:00
James Cole
0224d1d59b Accidentally commented out date limits in query. 2015-04-09 21:01:08 +02:00
James Cole
587b94153d Removed some foreach() routines that are no longer necessary now that getAmountAttribute() exists. 2015-04-09 20:58:57 +02:00
James Cole
5124ce0302 Fixed a bug where getAmountAttribute would interfere with queries that contained a "amount"-field. 2015-04-09 20:52:47 +02:00
James Cole
950c4045b0 Fix total savings to accurately reflect end of current period. 2015-04-09 19:10:24 +02:00
James Cole
9c8ba66873 Order categories by name. 2015-04-09 17:54:24 +02:00
James Cole
25d1d1be1b Merge pull request #72 from vbali/feature/savings-total
Add savings total for savings widget on dashboard
2015-04-09 17:47:24 +02:00
James Cole
339c352505 Fixed a bug where categories would be created even though they already existed. 2015-04-09 17:46:47 +02:00
Balazs Varkonyi
c587081fe4 Add savings total for savings widget on dashboard 2015-04-09 11:52:35 +02:00
James Cole
8f9b1b866b Included full home controller tests. 2015-04-07 20:54:43 +02:00
James Cole
ff5ecf6182 More tests. 2015-04-07 19:58:49 +02:00
James Cole
05670cf393 Optimize test code. 2015-04-07 18:52:15 +02:00
James Cole
f10f5d30bf Mock previously unmocked calls. 2015-04-07 18:48:34 +02:00
James Cole
0d336727e8 Automated code cleanup. 2015-04-07 18:26:14 +02:00
James Cole
bf354275b3 Some general cleaning up. 2015-04-07 18:25:21 +02:00
James Cole
1932bf277a More tests 2015-04-07 17:51:22 +02:00
James Cole
192db4bb6e Accidentally included JS twice. 2015-04-07 10:14:20 +02:00
James Cole
701efb943d Lots of new tests. 2015-04-07 10:14:10 +02:00
James Cole
b2674971f1 Updated composer.lock. 2015-04-06 09:20:45 +02:00
James Cole
c907cb4cf1 General cleanup and new (incomplete) tests. 2015-04-06 09:15:59 +02:00
James Cole
bb1462b4d9 Merge branch 'release/3.3.7' 2015-04-05 21:15:01 +02:00
James Cole
1035f0e139 Merge branch 'release/3.3.7' into develop 2015-04-05 21:15:01 +02:00
James Cole
8475757716 Removed iconv() commented code. See other commit. 2015-04-05 21:14:41 +02:00
James Cole
20ffd8d61b New read me. 2015-04-05 21:14:25 +02:00
James Cole
8601428c4c Fixed a bug in the form request that wouldn't accept fairly long names. 2015-04-05 21:12:54 +02:00
James Cole
59998573ee Removed iconv thing that doesn't seem to matter. See screenshot at https://imgur.com/8r26ZZu 2015-04-05 21:12:31 +02:00
James Cole
293cd72001 Add currency tests. 2015-04-05 20:47:19 +02:00
James Cole
22ab9ebb2f Included tests for the category controller. 2015-04-05 19:43:20 +02:00
James Cole
9136e592d3 Included bill controller. 2015-04-05 18:20:06 +02:00
James Cole
ed8e392616 Fixed budget controller tests. 2015-04-05 15:53:03 +02:00
James Cole
e57ce6e644 Test now works. 2015-04-05 15:25:35 +02:00
James Cole
8204e46086 Removed inspiring quotes. 2015-04-05 10:38:53 +02:00
James Cole
9e365d9f80 Mar tests. 2015-04-05 10:36:28 +02:00
James Cole
6b40a933e9 And.. completed the account controller. 2015-04-04 22:53:49 +02:00
James Cole
8520a5002f Another attempt at fixing some tests. I'm probably searching in the wrong direction with this. 2015-04-04 22:27:51 +02:00
James Cole
b38ed06f6e New tests. 2015-04-04 21:52:56 +02:00
James Cole
48427b1143 A mediocre fix for the database failures. 2015-04-04 21:33:36 +02:00
James Cole
5d505f4ed0 Fixed more tests but the factories are not perfect yet. 2015-04-04 21:23:37 +02:00
James Cole
0c02a08954 Merge pull request #65 from vbali/master
Fixes for chart and account creation
2015-04-04 06:50:30 +02:00
Balazs Varkonyi
0221bd0f80 Merge branch 'hotfix/diff–fix' 2015-04-04 01:56:25 +02:00
Balazs Varkonyi
28216cbcb5 Removed merge conflict from CategoryController 2015-04-04 01:56:19 +02:00
Balazs Varkonyi
3e08a8cd6b Merge branch 'hotfix/account-fix' 2015-04-04 01:37:05 +02:00
Balazs Varkonyi
67fafdeef7 Return the new account from firstOrCreateEncrypted 2015-04-04 01:36:55 +02:00
Balazs Varkonyi
cc82505b66 Merge branch 'hotfix/chart-fix' 2015-04-04 01:33:36 +02:00
Balazs Varkonyi
fd5f075f63 Chart fix for accounts with accented letters 2015-04-04 01:31:57 +02:00
James Cole
a115960411 Merge branch 'release/3.3.6'
Conflicts:
	app/Http/Controllers/CategoryController.php
2015-04-03 23:31:29 +02:00
James Cole
e9fa4ca816 New version in read me. 2015-04-03 23:30:54 +02:00
James Cole
75d92bc7f0 Allow FF to send events. 2015-04-03 23:22:23 +02:00
James Cole
74aa6e911e Make boxes respond to unpaid / paid credit card bills. 2015-04-03 23:10:51 +02:00
James Cole
9d000c1898 Added amount because debug. 2015-04-03 23:05:50 +02:00
James Cole
b2254875b2 Expanded to credit cards. 2015-04-03 22:54:21 +02:00
James Cole
78d04230d5 New CC help text. 2015-04-03 21:25:04 +02:00
James Cole
1c04477393 Support credit cards (in name only). 2015-04-03 21:12:54 +02:00
James Cole
2a9679c94e Textual fix in bill [skip ci] 2015-04-03 21:05:41 +02:00
James Cole
6009f8ecde Better chart for bills. 2015-04-03 21:05:01 +02:00
James Cole
35cdbec70a Clean up for budget limits. 2015-04-03 19:39:36 +02:00
James Cole
0faef542c1 Fixed a bug in the budget controller. 2015-04-03 19:11:55 +02:00
James Cole
8b085af6a1 First attempt at account controller::index. 2015-04-03 12:14:40 +02:00
James Cole
e3b11a9eb2 New test for edit form that is more inclusive. 2015-04-03 09:30:44 +02:00
James Cole
72fc88f3c6 Even more tests. 2015-04-03 08:55:24 +02:00
James Cole
a5b759f268 Updates TestCase.php 2015-04-03 08:37:53 +02:00
James Cole
6a9ffae25d New tests and factories. 2015-04-03 08:31:50 +02:00
James Cole
64c031c7fe Small updates [skip ci] 2015-04-03 07:44:44 +02:00
James Cole
720926c50d Tests now run without a database, as they should. 2015-04-03 07:33:18 +02:00
James Cole
36ec974284 Add heavy but working migrate routine. Will fix the tests but must be optimised. Also, should not be necessary. 2015-04-02 22:51:00 +02:00
James Cole
cff77c39e2 Update travis-ci and composer.lock and composer.json 2015-04-02 22:46:50 +02:00
James Cole
056a0a1736 Smaller boxes. 2015-04-02 22:43:12 +02:00
James Cole
f301da83c6 Fixed the tests. 2015-04-02 22:39:31 +02:00
James Cole
5362231efa Ignore virtual balance in select places. 2015-04-01 19:46:16 +02:00
James Cole
4bb14cad73 Allow virtual balance to be ignored. 2015-04-01 19:45:13 +02:00
James Cole
7f5188f5a4 Added something fancy called "virtual balance". 2015-04-01 19:42:14 +02:00
James Cole
a80b7aac6c Fix more redirections. 2015-04-01 18:59:34 +02:00
James Cole
83f32478fa Fixed more redirections. 2015-04-01 09:43:19 +02:00
James Cole
7578ec6801 Fix redirections in budget. 2015-04-01 09:40:19 +02:00
James Cole
e14a32f76f Also fix delete/destroy redirection routine. 2015-04-01 09:23:51 +02:00
James Cole
58faa189ac Can now actually store transactions without an expense / revenue account. 2015-04-01 09:17:07 +02:00
James Cole
9b4f87d44a Fix checkbox for edit screen. 2015-04-01 09:16:51 +02:00
James Cole
7830a64170 Fix redirect. 2015-04-01 09:16:41 +02:00
James Cole
8f0fa02107 Better redirection for new transactions. 2015-04-01 09:12:49 +02:00
James Cole
e000fb5d80 Optimized decryption. 2015-03-31 22:46:11 +02:00
James Cole
566fadad15 Add decryption routine. 2015-03-31 21:18:22 +02:00
James Cole
cf23858c10 Gokje. 2015-03-31 20:52:48 +02:00
James Cole
216a223617 Fix in year list. 2015-03-31 20:49:02 +02:00
James Cole
638f38d823 New flush method. 2015-03-31 20:46:37 +02:00
James Cole
98ee70f04e Fix range thing. 2015-03-31 20:45:06 +02:00
James Cole
b365db5a4b New hidden id field. 2015-03-31 20:39:28 +02:00
James Cole
02e55496df Remove reminders when piggy bank is reminded. 2015-03-31 20:22:51 +02:00
James Cole
e11e53913a Some new code + GA 2015-03-31 19:21:49 +02:00
James Cole
f8a5fb4225 Fixed tests. 2015-03-31 17:49:47 +02:00
James Cole
3a49450461 Made sure migrations can be rolled back. 2015-03-31 15:38:44 +02:00
James Cole
3c375fb955 Remove build stuff because sqlite does not work with change things. 2015-03-31 15:24:13 +02:00
James Cole
011fea2cc6 Fix encryption in home chart. 2015-03-31 15:10:22 +02:00
James Cole
a079eec2cb Better unicity for objects. 2015-03-31 14:16:25 +02:00
James Cole
776b37f4ea Clean up some code, optimise the 503 error. 2015-03-30 21:24:56 +02:00
James Cole
a4d8bbe3da Fixed a bug where certain types of accounts could not be validated. 2015-03-30 20:16:33 +02:00
James Cole
72166743fa Merge branches 'develop' and 'develop' of https://github.com/JC5/firefly-iii into develop 2015-03-30 20:08:40 +02:00
James Cole
c4312c0b11 Extra large code cleanup. 2015-03-30 20:08:27 +02:00
James Cole
38ba645415 Add hidden-xs class. 2015-03-30 09:23:18 +02:00
James Cole
ac12a47071 Hide inactive budgets when there are none. 2015-03-29 21:29:37 +02:00
James Cole
bea321939e Code rearrange and optimise. 2015-03-29 21:27:51 +02:00
James Cole
210f84b6ea Allow edit from budget overview, and allow changing of active setting. 2015-03-29 21:27:13 +02:00
James Cole
d4c642741f Fixed division by zero. 2015-03-29 21:09:33 +02:00
James Cole
e6b1b58379 Enable inactive budgets. 2015-03-29 19:50:29 +02:00
James Cole
8d982c1a90 Some extensions. 2015-03-29 19:44:59 +02:00
James Cole
be10e836dc Updated composer.lock 2015-03-29 19:40:22 +02:00
James Cole
9be05e7e17 Small optimisations. 2015-03-29 18:28:49 +02:00
James Cole
bd96b2819f Removed double code. 2015-03-29 12:32:00 +02:00
James Cole
f601af3da0 Cleaned up report controller. 2015-03-29 12:25:46 +02:00
James Cole
70a01c082b Removed some double code. 2015-03-29 12:12:06 +02:00
James Cole
bcf066ead7 Cleaned up some double code. 2015-03-29 11:51:26 +02:00
James Cole
24dd3578ed Merge branch 'release/3.3.5' into develop 2015-03-29 08:43:55 +02:00
James Cole
d5c39d54d8 Merge branch 'release/3.3.5' 2015-03-29 08:43:54 +02:00
James Cole
6e3f6abc67 New read me. 2015-03-29 08:43:46 +02:00
James Cole
463201df2c New gitignore. 2015-03-29 08:43:03 +02:00
James Cole
a7501c396f Renamed some methods. 2015-03-29 08:24:56 +02:00
James Cole
4c8bc49a7e Add newlines. 2015-03-29 08:22:48 +02:00
James Cole
f254674f88 Add newlines. 2015-03-29 08:21:33 +02:00
James Cole
a97c267378 Added new lines 2015-03-29 08:20:37 +02:00
James Cole
eaa947894b Updated composer.son 2015-03-29 08:20:30 +02:00
James Cole
fb3a26510b Removed some unused use statements. 2015-03-29 08:20:20 +02:00
James Cole
bfe8b14e49 Cleaned up. 2015-03-29 08:18:15 +02:00
James Cole
afb47eb742 Add newlines. 2015-03-29 08:14:32 +02:00
James Cole
ba97f96288 Removed unused use statements. 2015-03-29 08:03:53 +02:00
James Cole
ecbafb8f4b Removed commented out code. 2015-03-29 07:59:18 +02:00
James Cole
12d3800d67 Removed unused use statements. 2015-03-29 07:51:56 +02:00
James Cole
d586b82372 New composer.son file. 2015-03-29 07:45:14 +02:00
James Cole
ba9877c9b4 Add typehints to object parameters 2015-03-29 07:43:20 +02:00
James Cole
907933f3df Removed debug logic and bad operators. 2015-03-29 07:32:24 +02:00
James Cole
2e91d21a88 Add return to form routine to budgets. 2015-03-28 18:22:36 +01:00
James Cole
dca395a018 Merge branch 'release/3.3.4' 2015-03-28 17:45:56 +01:00
James Cole
625070a5b9 Merge branch 'release/3.3.4' into develop 2015-03-28 17:45:56 +01:00
James Cole
f875c7cbf5 Updated read me file. 2015-03-28 17:45:46 +01:00
James Cole
eed88fc103 Don't fail on repeated piggy banks, just log it. 2015-03-28 17:45:15 +01:00
James Cole
6fc54d1d27 Fixed the tests 2015-03-28 07:00:53 +01:00
James Cole
0ef52bf909 Remove recurring transactions [skip ci] 2015-03-28 06:59:43 +01:00
James Cole
95e34af6ef Update script [skip ci] 2015-03-28 06:51:54 +01:00
James Cole
650b99ead2 Ignore apple files. [skip ci] 2015-03-28 06:50:26 +01:00
James Cole
57941a19de Excutable. 2015-03-28 06:48:54 +01:00
James Cole
6157db0b6a Some new tests 2015-03-28 06:48:38 +01:00
James Cole
c9358acf5d Update tests and token replace routine. 2015-03-28 06:25:36 +01:00
James Cole
ed29eb8a5d New lock file. 2015-03-28 06:19:37 +01:00
James Cole
bcf7452312 Some new tests. 2015-03-28 06:19:09 +01:00
James Cole
b9000519e4 Fixed unique piggy check. 2015-03-27 20:20:52 +01:00
James Cole
2694297466 Fixed a bug where start / end would be used incorrectly. 2015-03-27 19:48:55 +01:00
James Cole
53ff5c1490 Wrong operator, 2015-03-27 19:47:31 +01:00
James Cole
32a036b076 Add certainty to chart. 2015-03-27 19:45:47 +01:00
James Cole
aae2ab2693 Unnecessary if statement. 2015-03-27 19:43:53 +01:00
James Cole
4ef23e28a3 Update year report. 2015-03-27 19:43:20 +01:00
James Cole
38beb7f8d2 Fallback. 2015-03-27 19:42:35 +01:00
James Cole
0129a66906 Debug hidden accounts in year reports. 2015-03-27 19:37:00 +01:00
James Cole
45bced7b34 Hide accounts with no activity during a year. 2015-03-27 19:34:37 +01:00
James Cole
7f5a55dffb Small updates to read me. 2015-03-27 19:21:51 +01:00
James Cole
80289df6d3 Capital letter 2015-03-27 18:58:28 +01:00
James Cole
2115c3ced8 Read me 2015-03-27 18:58:11 +01:00
James Cole
1c2d0e6618 Updated read me file 2015-03-27 18:57:49 +01:00
James Cole
ea4f7f79ce Removed more references. 2015-03-27 18:17:15 +01:00
James Cole
b17f8ac311 Removed references to repeated expenses. 2015-03-27 18:15:10 +01:00
James Cole
6870d13b72 Removed repeated expenses. 2015-03-27 18:14:17 +01:00
James Cole
81fa304fd2 Removed repeated expenses. 2015-03-27 18:13:53 +01:00
James Cole
a663ddb0d9 Hide accounts with no activity. 2015-03-27 13:39:12 +01:00
James Cole
f7969afc22 Hide accounts with no activity in budget report. 2015-03-27 13:36:33 +01:00
James Cole
52ce4cd313 Resort array. 2015-03-27 13:25:46 +01:00
James Cole
247175881f Moved some code. 2015-03-27 13:24:04 +01:00
James Cole
fcf5009338 Moved some code around. 2015-03-27 13:20:48 +01:00
James Cole
12542d8f63 Auto fill transaction descriptions. 2015-03-27 13:16:14 +01:00
James Cole
e94194e28b New favicon. 2015-03-27 13:04:53 +01:00
James Cole
115499f3b2 New favicon. 2015-03-27 11:59:07 +01:00
James Cole
12af05b94f Only display budgets when there's money in the envelope or when there's money spent. 2015-03-27 09:43:29 +01:00
James Cole
88b83d8164 Merge branch 'release/3.3.3' into develop 2015-03-27 09:36:04 +01:00
James Cole
fcc22c692a Merge branch 'release/3.3.3' 2015-03-27 09:36:03 +01:00
James Cole
a876e8005c New version. 2015-03-27 09:35:52 +01:00
James Cole
2288e3705a Fixed order view. 2015-03-27 09:29:14 +01:00
James Cole
e8b58154e0 Bug fix. 2015-03-27 09:24:26 +01:00
James Cole
4393475af3 Remove debug. 2015-03-27 09:22:08 +01:00
James Cole
3fc560597c Validation bug. 2015-03-27 09:21:09 +01:00
James Cole
9f9af0b693 Moved to account list. 2015-03-27 08:11:13 +01:00
James Cole
a83fe2caea Correct ordering (everywhere). 2015-03-27 07:32:06 +01:00
James Cole
30c5376217 Fix order. 2015-03-27 07:29:01 +01:00
James Cole
6dddd6629d Thing with order and should fix Travis. 2015-03-27 07:20:32 +01:00
James Cole
f80de12cb5 Fix sorting. 2015-03-26 22:53:34 +01:00
James Cole
544ffca3a5 Sortable transactions. 2015-03-26 22:52:49 +01:00
James Cole
e0730c7b39 Fixed double named accounts. 2015-03-26 18:05:23 +01:00
James Cole
3d59d141c4 Push! 2015-03-26 17:46:08 +01:00
James Cole
ccddc2623d Merge branch 'develop' of github.com:JC5/firefly-iii into develop 2015-03-26 17:45:58 +01:00
James Cole
fcc47b58b4 Return to edit / create screen when user indicates. 2015-03-26 17:45:03 +01:00
James Cole
f67ac2e25e Update AccountController.php 2015-03-26 14:50:49 +01:00
James Cole
26127c9ccf Update AccountController.php 2015-03-26 14:49:02 +01:00
James Cole
d624efa799 Fix registration bug. 2015-03-25 22:30:36 +01:00
James Cole
ed3d40a4e0 Whoops. 2015-03-25 22:29:32 +01:00
James Cole
6e90ce5496 Cant dismiss reminders again. 2015-03-25 19:12:33 +01:00
James Cole
ab851b1be4 Fixed transfers 2015-03-25 17:44:06 +01:00
James Cole
340de53825 Use normal logging. 2015-03-25 17:42:33 +01:00
James Cole
a867b60af0 Update CategoryController.php 2015-03-25 16:14:28 +01:00
James Cole
26eafb0bd2 Update CategoryController.php 2015-03-25 16:13:39 +01:00
James Cole
0dbe44764b Update GoogleChartController.php 2015-03-25 16:00:06 +01:00
James Cole
2159df6802 Update GoogleChartController.php
Fixed chart.
2015-03-25 15:58:37 +01:00
James Cole
23389b9f17 Update FireflyValidator.php
Update uniqueForUser.
2015-03-25 15:56:32 +01:00
James Cole
6d3e3b894a Update AccountFormRequest.php
Update rules to include belongsToUser.
2015-03-25 15:55:39 +01:00
James Cole
55340aefa3 Remove code. 2015-03-24 21:06:45 +01:00
James Cole
94bc751e41 Sum starts at 0. 2015-03-24 21:05:15 +01:00
James Cole
89259c11e2 Fix chart. 2015-03-24 21:04:53 +01:00
James Cole
6bbaf1523c Fixed a bug in relevantOnDate 2015-03-24 21:00:42 +01:00
James Cole
fa3f18b60f Expand log for event. 2015-03-24 20:56:58 +01:00
James Cole
3dc794002f This should catch the missing piggy bank events. 2015-03-24 20:54:25 +01:00
James Cole
f50b133f2e Cleaned up some icons, improved routine for repeated expenses. 2015-03-21 21:33:52 +01:00
James Cole
1d6f6d28c9 Update font in calendar. 2015-03-21 10:29:25 +01:00
James Cole
c913de3c8b Fix percentage. 2015-03-21 08:56:24 +01:00
James Cole
bbc7b54a38 Catch 0 start balance. 2015-03-21 08:55:55 +01:00
James Cole
05fa1b40d1 Support > 100% 2015-03-21 08:53:43 +01:00
James Cole
6a88c8634d Added a block with savings. 2015-03-21 08:51:34 +01:00
James Cole
8ae1efa230 Added type savings account. 2015-03-20 23:17:59 +01:00
James Cole
ab8df4c8ab Fixed css reference. 2015-03-20 22:50:50 +01:00
James Cole
c259a46ed3 Clean up sql codes. 2015-03-20 22:39:07 +01:00
James Cole
5dc0677599 Fixed search sort 2015-03-20 18:30:17 +01:00
James Cole
7ed662ecc2 Fixed search in encrypted entries. 2015-03-20 18:21:14 +01:00
James Cole
4361cc69d4 Update font options in charts. 2015-03-20 15:54:40 +01:00
James Cole
461cbcbc28 Update to use Roboto font. 2015-03-20 13:34:59 +01:00
James Cole
09ae6c488b Fixed a bug where the session date would be edited by a chart. 2015-03-20 08:16:16 +01:00
James Cole
271a0ade26 Add code coverage to phpunit. 2015-03-18 22:24:54 +01:00
James Cole
4b076d227a Touch db so it can be created. 2015-03-18 22:17:18 +01:00
James Cole
3c4e7158a1 Config for test database. 2015-03-18 22:16:00 +01:00
James Cole
0b16765f37 Add sudo: false to enable container based builds. 2015-03-18 21:51:56 +01:00
James Cole
20c2ff3443 Add debug output to phpunit. 2015-03-18 21:43:19 +01:00
James Cole
96f2e598f4 Better position for titles. 2015-03-16 19:03:52 +01:00
James Cole
7374f0f9dd Removed Material Design. 2015-03-16 18:02:52 +01:00
James Cole
ccc44a74a0 Removed debug information. 2015-03-15 19:02:33 +01:00
James Cole
66cd63a68d Fixed a bug. 2015-03-15 18:08:52 +01:00
James Cole
decf7e5485 Set order in title. 2015-03-15 18:02:40 +01:00
James Cole
063c8025aa Allow piggy banks to be ordered. 2015-03-15 18:00:33 +01:00
James Cole
2fa7d2bd56 Add some text alignments. 2015-03-15 14:24:25 +01:00
James Cole
e55e7bce74 Misspelled a class name. 2015-03-15 14:23:26 +01:00
James Cole
f572445a65 Small updates. 2015-03-15 09:34:57 +01:00
James Cole
8b4f656d90 Switched to Google Material design line charts. 2015-03-15 08:38:12 +01:00
James Cole
6c1e093ebd Merge branch 'release/3.3.2' into develop 2015-03-14 22:06:12 +01:00
246 changed files with 8308 additions and 3180 deletions

View File

@@ -1,5 +1,5 @@
APP_ENV=local APP_ENV=production
APP_DEBUG=true APP_DEBUG=false
APP_KEY=SomeRandomString APP_KEY=SomeRandomString
DB_CONNECTION=mysql DB_CONNECTION=mysql
@@ -12,5 +12,7 @@ CACHE_DRIVER=file
SESSION_DRIVER=file SESSION_DRIVER=file
EMAIL_SMTP= EMAIL_SMTP=
EMAIL_DRIVER=smtp
EMAIL_USERNAME= EMAIL_USERNAME=
EMAIL_PASSWORD= EMAIL_PASSWORD=
ANALYTICS_ID=

View File

@@ -11,3 +11,7 @@ DB_PASSWORD=secret
CACHE_DRIVER=file CACHE_DRIVER=file
SESSION_DRIVER=file SESSION_DRIVER=file
EMAIL_SMTP=
EMAIL_USERNAME=
EMAIL_PASSWORD=
ANALYTICS_ID=ABC

2
.gitignore vendored
View File

@@ -26,3 +26,5 @@ db.sqlite-journal
tests/_output/* tests/_output/*
.env .env
clover.xml clover.xml
node_modules/
addNewLines.php

View File

@@ -1,4 +1,6 @@
language: php language: php
sudo: false
php: php:
- 5.5 - 5.5
@@ -9,13 +11,12 @@ addons:
repo_token: 26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61 repo_token: 26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61
install: install:
- rm composer.lock - composer update
- composer install
- php artisan env - php artisan env
- mv -v .env.testing .env - mv -v .env.testing .env
script: script:
- phpunit - phpunit --debug
after_script: after_script:
- php vendor/bin/coveralls - php vendor/bin/coveralls

View File

@@ -1,11 +1,12 @@
Firefly III (v3.3.2) Firefly III (v3.3.8)
=========== ===========
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii) [![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii) [![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
[![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii) [![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii)
[![Test Coverage](https://codeclimate.com/github/JC5/firefly-iii/badges/coverage.svg)](https://codeclimate.com/github/JC5/firefly-iii) [![Coverage Status](https://coveralls.io/repos/JC5/firefly-iii/badge.svg?branch=master)](https://coveralls.io/r/JC5/firefly-iii?branch=master)
[![Coverage Status](https://coveralls.io/repos/JC5/firefly-iii/badge.svg?branch=master)](https://coveralls.io/r/JC5/firefly-iii?branch=develop)
[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![Total Downloads](https://poser.pugx.org/grumpydictator/firefly-iii/downloads.svg)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Total Downloads](https://poser.pugx.org/grumpydictator/firefly-iii/downloads.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
@@ -18,16 +19,25 @@ Firefly Mark III is a new version of Firefly built upon best practices and lesso
from building [Firefly](https://github.com/JC5/Firefly). It's Mark III since the original Firefly never made it outside of my from building [Firefly](https://github.com/JC5/Firefly). It's Mark III since the original Firefly never made it outside of my
laptop and [Firefly II](https://github.com/JC5/Firefly) is live. laptop and [Firefly II](https://github.com/JC5/Firefly) is live.
If you're not sure if this tool is for you, please read the [full description](https://github.com/JC5/firefly-iii/wiki/full-description).
To install and use Firefly III, please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable) and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**
## Current features ## Current features
- [A double-entry bookkeeping system](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system); - [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system);
- You can store, edit and remove withdrawals, deposits and transfers. This allows you full financial management; - You can store, edit and remove withdrawals, deposits and transfers. This allows you full financial management;
- It's possible to create, change and manage money using _budgets_; - You can manage different types of accounts
- Asset accounts
- Shared asset accounts (household accounts)
- Saving accounts
- Credit cards
- It's possible to create, change and manage money using _[budgets](https://en.wikipedia.org/wiki/Envelope_system)_;
- Organize transactions using categories; - Organize transactions using categories;
- Save towards a goal using piggy banks; - Save towards a goal using piggy banks;
- Predict and anticipate large expenses using "repeated expenses" (ie. yearly taxes); - Predict and anticipate bills;
- Predict and anticipate bills using "recurring transactions" (rent for example); - View income / expense reports;
- View basic income / expense reports.
- Lots of help text in case you don't get it; - Lots of help text in case you don't get it;
Everything is organised: Everything is organised:
@@ -44,9 +54,7 @@ Firefly III will feature, but does not feature yet:
- More control over other resources outside of personal finance - More control over other resources outside of personal finance
- Accounts shared with a partner (household accounts)
- Debts - Debts
- Credit cards
- More test-coverage; - More test-coverage;
- Firefly will be able to split transactions; a single purchase can be split in multiple entries, for more fine-grained control. - Firefly will be able to split transactions; a single purchase can be split in multiple entries, for more fine-grained control.
- Firefly will be able to join transactions. - Firefly will be able to join transactions.
@@ -55,7 +63,6 @@ Firefly III will feature, but does not feature yet:
Some stuff has been removed: Some stuff has been removed:
- The nesting of budgets, categories and beneficiaries is removed because it was pretty pointless. - The nesting of budgets, categories and beneficiaries is removed because it was pretty pointless.
- Firefly will not encrypt the content of the (MySQL) tables. Old versions of Firefly had this capability but it sucks when searching, sorting and organizing entries.
## Screenshots ## Screenshots
@@ -70,7 +77,4 @@ Some stuff has been removed:
## Current state ## Current state
I have the basics up and running. Test coverage is currently coming, slowly. I have the basics up and running. Test coverage is currently coming, slowly.
Although I have not checked extensively, some forms and views have CSRF vulnerabilities. This is because not all
views escape all characters by default. Will be fixed.
Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)! Questions, ideas or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)!

View File

@@ -1,37 +0,0 @@
<?php namespace FireflyIII\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Foundation\Inspiring;
/**
* Class Inspire
*
* @package FireflyIII\Console\Commands
*/
class Inspire extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Display an inspiring quote';
/**
* The console command name.
*
* @var string
*/
protected $name = 'inspire';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->comment(PHP_EOL . Inspiring::quote() . PHP_EOL);
}
}

View File

@@ -18,7 +18,6 @@ class Kernel extends ConsoleKernel
*/ */
protected $commands protected $commands
= [ = [
'FireflyIII\Console\Commands\Inspire',
]; ];
/** /**
@@ -30,8 +29,6 @@ class Kernel extends ConsoleKernel
*/ */
protected function schedule(Schedule $schedule) protected function schedule(Schedule $schedule)
{ {
$schedule->command('inspire')
->hourly();
} }
} }

View File

@@ -1,11 +1,15 @@
<?php namespace FireflyIII\Events; <?php namespace FireflyIII\Events;
use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class JournalCreated extends Event { /**
* Class JournalCreated
*
* @package FireflyIII\Events
*/
class JournalCreated extends Event
{
use SerializesModels; use SerializesModels;
@@ -23,9 +27,6 @@ class JournalCreated extends Event {
$this->journal = $journal; $this->journal = $journal;
$this->piggyBankId = $piggyBankId; $this->piggyBankId = $piggyBankId;
} }
} }

View File

@@ -1,11 +1,10 @@
<?php namespace FireflyIII\Events; <?php namespace FireflyIII\Events;
use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class JournalSaved extends Event { class JournalSaved extends Event
{
use SerializesModels; use SerializesModels;

View File

@@ -38,6 +38,9 @@ class ConnectJournalToPiggyBank
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
$journal = $event->journal; $journal = $event->journal;
$piggyBankId = $event->piggyBankId; $piggyBankId = $event->piggyBankId;
if (intval($piggyBankId) < 1) {
return;
}
Log::debug('JournalCreated event: ' . $journal->id . ', ' . $piggyBankId); Log::debug('JournalCreated event: ' . $journal->id . ', ' . $piggyBankId);
@@ -48,14 +51,7 @@ class ConnectJournalToPiggyBank
return; return;
} }
Log::debug('Found a piggy bank'); Log::debug('Found a piggy bank');
$amount = 0; $amount = $journal->amount;
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id === $piggyBank->account_id) {
// this transaction is the relevant one.
$amount = floatval($transaction->amount);
}
}
Log::debug('Amount: ' . $amount); Log::debug('Amount: ' . $amount);
if ($amount == 0) { if ($amount == 0) {
return; return;
@@ -63,6 +59,8 @@ class ConnectJournalToPiggyBank
// update piggy bank rep for date of transaction journal. // update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) { if (is_null($repetition)) {
Log::debug('Found no repetition for piggy bank for date ' . $journal->date->format('Y M d'));
return; return;
} }

View File

@@ -1,16 +1,18 @@
<?php namespace FireflyIII\Handlers\Events; <?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalDeleted; use FireflyIII\Events\JournalDeleted;
use Illuminate\Contracts\Queue\ShouldBeQueued;
use Illuminate\Queue\InteractsWithQueue;
/**
* Class JournalDeletedHandler
*
* @package FireflyIII\Handlers\Events
*/
class JournalDeletedHandler class JournalDeletedHandler
{ {
/** /**
* Create the event handler. * Create the event handler.
* *
* @return void
*/ */
public function __construct() public function __construct()
{ {

View File

@@ -1,8 +1,8 @@
<?php namespace FireflyIII\Handlers\Events; <?php namespace FireflyIII\Handlers\Events;
use App;
use FireflyIII\Events\JournalSaved; use FireflyIII\Events\JournalSaved;
use Log; use Log;
use App;
/** /**
* Class RescanJournal * Class RescanJournal

View File

@@ -3,8 +3,12 @@
use FireflyIII\Events\JournalSaved; use FireflyIII\Events\JournalSaved;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
/**
* Class UpdateJournalConnection
*
* @package FireflyIII\Handlers\Events
*/
class UpdateJournalConnection class UpdateJournalConnection
{ {
@@ -41,17 +45,8 @@ class UpdateJournalConnection
if (is_null($repetition)) { if (is_null($repetition)) {
return; return;
} }
$amount = 0; $amount = $journal->amount;
/** @var Transaction $transaction */ $diff = $amount - $event->amount;// update current repetition
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id === $piggyBank->account_id) {
// this transaction is the relevant one.
$amount = floatval($transaction->amount);
}
}
// update current repetition:
$diff = $amount - $event->amount;
$repetition->currentamount += $diff; $repetition->currentamount += $diff;
$repetition->save(); $repetition->save();

85
app/Helpers/Help/Help.php Normal file
View File

@@ -0,0 +1,85 @@
<?php
namespace FireflyIII\Helpers\Help;
use Cache;
use ErrorException;
use League\CommonMark\CommonMarkConverter;
use Log;
use Route;
/**
* Class Help
*
* @package FireflyIII\Helpers\Help
*/
class Help implements HelpInterface
{
/**
* @param $key
*
* @return string
*/
public function getFromCache($key)
{
return Cache::get($key);
}
/**
* @param $route
*
* @return array
*/
public function getFromGithub($route)
{
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
$content = [
'text' => '<p>There is no help for this route!</p>',
'title' => $route,
];
try {
$content['text'] = file_get_contents($uri);
} catch (ErrorException $e) {
Log::error(trim($e->getMessage()));
}
if (strlen(trim($content['text'])) == 0) {
$content['text'] = '<p>There is no help for this route.</p>';
}
$converter = new CommonMarkConverter();
$content['text'] = $converter->convertToHtml($content['text']);
return $content;
}
/**
* @return boolean
*/
public function hasRoute($route)
{
return Route::has($route);
}
/**
* @param $title
* @param array $content
*
* @return void
*/
public function putInCache($route, array $content)
{
Cache::put('help.' . $route . '.text', $content['text'], 10080); // a week.
Cache::put('help.' . $route . '.title', $content['title'], 10080);
}
/**
* @param $route
*
* @return bool
*/
public function inCache($route)
{
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FireflyIII\Helpers\Help;
/**
* Interface HelpInterface
*
* @package FireflyIII\Helpers\Help
*/
interface HelpInterface
{
/**
* @param $key
*
* @return string
*/
public function getFromCache($key);
/**
* @return boolean
*/
public function hasRoute($route);
/**
* @param $route
*
* @return array
*/
public function getFromGithub($route);
/**
* @param $route
* @param array $content
*
* @return void
*/
public function putInCache($route, array $content);
/**
* @param $route
*
* @return bool
*/
public function inCache($route);
}

View File

@@ -25,10 +25,7 @@ class ReminderHelper implements ReminderHelperInterface
*/ */
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end) public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end)
{ {
$reminder = Auth::user()->reminders() $reminder = Auth::user()->reminders()->where('remindersable_id', $piggyBank->id)->onDates($start, $end)->first();
->where('remindersable_id', $piggyBank->id)
->onDates($start, $end)
->first();
if (is_null($reminder)) { if (is_null($reminder)) {
if (!is_null($piggyBank->targetdate)) { if (!is_null($piggyBank->targetdate)) {
@@ -98,7 +95,6 @@ class ReminderHelper implements ReminderHelperInterface
if (!is_null($piggyBank->targetdate)) { if (!is_null($piggyBank->targetdate)) {
// count back until now. // count back until now.
// echo 'Count back!<br>';
$start = $piggyBank->targetdate; $start = $piggyBank->targetdate;
$end = $piggyBank->startdate; $end = $piggyBank->startdate;
@@ -134,6 +130,7 @@ class ReminderHelper implements ReminderHelperInterface
{ {
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
$piggyBank = $reminder->remindersable; $piggyBank = $reminder->remindersable;
if (is_null($piggyBank)) { if (is_null($piggyBank)) {
return 'Piggy bank no longer exists.'; return 'Piggy bank no longer exists.';
} }

View File

@@ -2,16 +2,17 @@
namespace FireflyIII\Helpers\Reminders; namespace FireflyIII\Helpers\Reminders;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\PiggyBank;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder;
/** /**
* Interface ReminderHelperInterface * Interface ReminderHelperInterface
* *
* @package FireflyIII\Helpers\Reminders * @package FireflyIII\Helpers\Reminders
*/ */
interface ReminderHelperInterface { interface ReminderHelperInterface
{
/** /**
* Takes a reminder, finds the piggy bank and tells you what to do now. * Takes a reminder, finds the piggy bank and tells you what to do now.
* Aka how much money to put in. * Aka how much money to put in.

View File

@@ -2,6 +2,7 @@
namespace FireflyIII\Helpers\Report; namespace FireflyIII\Helpers\Report;
use App;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
@@ -40,40 +41,43 @@ class ReportHelper implements ReportHelperInterface
* This method gets some kind of list for a monthly overview. * This method gets some kind of list for a monthly overview.
* *
* @param Carbon $date * @param Carbon $date
* @param bool $showSharedReports
* *
* @return Collection * @return Collection
*/ */
public function getBudgetsForMonth(Carbon $date) public function getBudgetsForMonth(Carbon $date, $showSharedReports = false)
{ {
/** @var \FireflyIII\Helpers\Report\ReportQueryInterface $query */
$query = App::make('FireflyIII\Helpers\Report\ReportQueryInterface');
$start = clone $date; $start = clone $date;
$start->startOfMonth(); $start->startOfMonth();
$end = clone $date; $end = clone $date;
$end->endOfMonth(); $end->endOfMonth();
// all budgets $set = Auth::user()->budgets()->orderBy('budgets.name', 'ASC')
$set = Auth::user()->budgets()
->leftJoin( ->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) { 'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d')); $join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
} }
) )
->get(['budgets.*', 'budget_limits.amount as amount']); ->get(['budgets.*', 'budget_limits.amount as queryAmount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['queryAmount'] = isset($budgets[0]['queryAmount']) ? $budgets[0]['queryAmount'] : 0.0;
$budgets[0]['name'] = 'No budget';
$budgets = $this->_helper->makeArray($set); // find transactions to shared asset accounts, which are without a budget by default:
$amountSet = $this->_queries->journalsByBudget($start, $end); // which is only relevant when shared asset accounts are hidden.
$amounts = $this->_helper->makeArray($amountSet); if ($showSharedReports === false) {
$combined = $this->_helper->mergeArrays($budgets, $amounts); $transfers = $query->sharedExpenses($start, $end)->sum('queryAmount');
$combined[0]['spent'] = isset($combined[0]['spent']) ? $combined[0]['spent'] : 0.0; $budgets[0]['spent'] += floatval($transfers) * -1;
$combined[0]['amount'] = isset($combined[0]['amount']) ? $combined[0]['amount'] : 0.0;
$combined[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
$transfers = $this->_queries->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$combined[0]['spent'] += floatval($transfer->amount) * -1;
} }
return $combined; return $budgets;
} }
/** /**
@@ -113,6 +117,9 @@ class ReportHelper implements ReportHelperInterface
$years[] = $start->format('Y'); $years[] = $start->format('Y');
$start->addYear(); $start->addYear();
} }
$years[] = Carbon::now()->format('Y');
// force the current year.
$years = array_unique($years);
return $years; return $years;
} }
@@ -155,9 +162,12 @@ class ReportHelper implements ReportHelperInterface
$end->endOfYear(); $end->endOfYear();
foreach ($accounts as $account) { foreach ($accounts as $account) {
$startBalance = Steam::balance($account, $start);
$endBalance = Steam::balance($account, $end);
$report[] = [ $report[] = [
'start' => Steam::balance($account, $start), 'start' => $startBalance,
'end' => Steam::balance($account, $end), 'end' => $endBalance,
'hide' => ($startBalance == 0 && $endBalance == 0),
'account' => $account, 'account' => $account,
'shared' => $account->accountRole == 'sharedAsset' 'shared' => $account->accountRole == 'sharedAsset'
]; ];

View File

@@ -4,10 +4,12 @@ namespace FireflyIII\Helpers\Report;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Crypt;
use DB; use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Steam; use Steam;
@@ -87,11 +89,14 @@ class ReportQuery implements ReportQueryInterface
->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at') ->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at')
->whereNull('otherJournals.deleted_at') ->whereNull('otherJournals.deleted_at')
->where('transactions.account_id', $account->id) ->where('transactions.account_id', $account->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->whereNotNull('transaction_group_transaction_journal.transaction_group_id') ->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
->get( ->get(
[ [
'transaction_journals.*', 'transaction_journals.*',
'transactions.amount' 'transactions.amount as queryAmount'
] ]
); );
@@ -106,17 +111,11 @@ class ReportQuery implements ReportQueryInterface
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @return Collection * @return float
*/ */
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end) public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end)
{ {
$set = $this->balancedTransactionsList($account, $start, $end); return floatval($this->balancedTransactionsList($account, $start, $end)->sum('queryAmount'));
$sum = 0;
foreach ($set as $entry) {
$sum += floatval($entry->amount);
}
return $sum;
} }
/** /**
@@ -140,7 +139,7 @@ class ReportQuery implements ReportQueryInterface
) )
->orderBy('accounts.name', 'ASC') ->orderBy('accounts.name', 'ASC')
->where( ->where(
function (Builder $query) use ($showSharedReports) { function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedAsset"'); $query->where('account_meta.data', '!=', '"sharedAsset"');
$query->orWhereNull('account_meta.data'); $query->orWhereNull('account_meta.data');
@@ -171,26 +170,9 @@ class ReportQuery implements ReportQueryInterface
*/ */
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end) public function getBudgetSummary(Account $account, Carbon $start, Carbon $end)
{ {
$set = TransactionJournal:: $query = $this->queryJournalsNoBudget($account, $start, $end);
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end)
->after($start)
->where('accounts.id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC')
->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) as `amount`')]);
return $set; return $query->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) as `queryAmount`')]);
} }
@@ -206,26 +188,9 @@ class ReportQuery implements ReportQueryInterface
*/ */
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end) public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end)
{ {
$set = TransactionJournal:: $query = $this->queryJournalsNoBudget($account, $start, $end);
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end)
->after($start)
->where('accounts.id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_types.type', 'Withdrawal')
->whereNull('budgets.id')
->orderBy('transaction_journals.date', 'ASC')
->get(['budgets.name', 'transactions.amount', 'transaction_journals.*']);
return $set; return $query->get(['budgets.name', 'transactions.amount as queryAmount', 'transaction_journals.*']);
} }
/** /**
@@ -241,43 +206,20 @@ class ReportQuery implements ReportQueryInterface
*/ */
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false) public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false)
{ {
$query = TransactionJournal:: $query = $this->queryJournalsWithTransactions($start, $end);
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
if ($showSharedReports === false) { if ($showSharedReports === false) {
// only get deposits not to a shared account // only get deposits not to a shared account
// and transfers to a shared account. // and transfers to a shared account.
$query->where( $query->where(
function ($query) { function (Builder $query) {
$query->where( $query->where(
function ($q) { function (Builder $q) {
$q->where('transaction_types.type', 'Deposit'); $q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedAsset"'); $q->where('acm_to.data', '!=', '"sharedAsset"');
} }
); );
$query->orWhere( $query->orWhere(
function ($q) { function (Builder $q) {
$q->where('transaction_types.type', 'Transfer'); $q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedAsset"'); $q->where('acm_from.data', '=', '"sharedAsset"');
} }
@@ -288,20 +230,29 @@ class ReportQuery implements ReportQueryInterface
// any deposit is fine. // any deposit is fine.
$query->where('transaction_types.type', 'Deposit'); $query->where('transaction_types.type', 'Deposit');
} }
$query->before($end)->after($start) $query->groupBy('t_from.account_id')->orderBy('transaction_journals.date');
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('transaction_journals.date');
return $query->get( // get everything, decrypt and return
$data = $query->get(
['transaction_journals.id', ['transaction_journals.id',
'transaction_journals.description', 'transaction_journals.description',
'transaction_journals.encrypted', 'transaction_journals.encrypted',
'transaction_types.type', 'transaction_types.type',
DB::Raw('SUM(`t_to`.`amount`) as `amount`'), DB::Raw('SUM(`t_to`.`amount`) as `queryAmount`'),
'transaction_journals.date', 'transaction_journals.date',
't_from.account_id as account_id', 't_from.account_id as account_id',
'ac_from.name as name'] 'ac_from.name as name',
'ac_from.encrypted as account_encrypted'
]
); );
$data->each(
function (Model $object) {
$object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
} }
/** /**
@@ -377,9 +328,17 @@ class ReportQuery implements ReportQueryInterface
->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal') ->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id') ->groupBy('categories.id')
->orderBy('amount'); ->orderBy('queryAmount');
return $query->get(['categories.id', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `amount`')]); $data = $query->get(['categories.id', 'categories.encrypted', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `queryAmount`')]);
// decrypt data:
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
} }
@@ -397,42 +356,20 @@ class ReportQuery implements ReportQueryInterface
*/ */
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false) public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{ {
$query = TransactionJournal::leftJoin( $query = $this->queryJournalsWithTransactions($start, $end);
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
if ($showSharedReports === false) { if ($showSharedReports === false) {
// get all withdrawals not from a shared accounts // get all withdrawals not from a shared accounts
// and all transfers to a shared account // and all transfers to a shared account
$query->where( $query->where(
function ($query) { function (Builder $query) {
$query->where( $query->where(
function ($q) { function (Builder $q) {
$q->where('transaction_types.type', 'Withdrawal'); $q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"'); $q->where('acm_from.data', '!=', '"sharedAsset"');
} }
); );
$query->orWhere( $query->orWhere(
function ($q) { function (Builder $q) {
$q->where('transaction_types.type', 'Transfer'); $q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"'); $q->where('acm_to.data', '=', '"sharedAsset"');
} }
@@ -443,13 +380,21 @@ class ReportQuery implements ReportQueryInterface
// any withdrawal goes: // any withdrawal goes:
$query->where('transaction_types.type', 'Withdrawal'); $query->where('transaction_types.type', 'Withdrawal');
} }
$query->before($end) $query->before($end)->after($start)
->after($start)
->where('transaction_journals.user_id', Auth::user()->id) ->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id') ->groupBy('t_to.account_id')
->orderBy('amount', 'DESC'); ->orderBy('queryAmount', 'DESC');
return $query->get(['t_to.account_id as id', 'ac_to.name as name', DB::Raw('SUM(t_to.amount) as `amount`')]); $data = $query->get(['t_to.account_id as id', 'ac_to.name as name', 'ac_to.encrypted', DB::Raw('SUM(t_to.amount) as `queryAmount`')]);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
} }
/** /**
@@ -463,44 +408,21 @@ class ReportQuery implements ReportQueryInterface
*/ */
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false) public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{ {
$query = TransactionJournal:: $query = $this->queryJournalsWithTransactions($start, $end);
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
if ($showSharedReports === false) { if ($showSharedReports === false) {
// show queries where transfer type is deposit, and its not to a shared account // show queries where transfer type is deposit, and its not to a shared account
// or where its a transfer and its from a shared account (both count as incomes) // or where its a transfer and its from a shared account (both count as incomes)
$query->where( $query->where(
function ($query) { function (Builder $query) {
$query->where( $query->where(
function ($q) { function (Builder $q) {
$q->where('transaction_types.type', 'Deposit'); $q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedAsset"'); $q->where('acm_to.data', '!=', '"sharedAsset"');
} }
); );
$query->orWhere( $query->orWhere(
function ($q) { function (Builder $q) {
$q->where('transaction_types.type', 'Transfer'); $q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedAsset"'); $q->where('acm_from.data', '=', '"sharedAsset"');
} }
@@ -511,11 +433,20 @@ class ReportQuery implements ReportQueryInterface
// any deposit goes: // any deposit goes:
$query->where('transaction_types.type', 'Deposit'); $query->where('transaction_types.type', 'Deposit');
} }
$query->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('amount');
return $query->get(['t_from.account_id as account_id', 'ac_from.name as name', DB::Raw('SUM(t_from.amount) as `amount`')]); $query->groupBy('t_from.account_id')->orderBy('queryAmount');
$data = $query->get(
['t_from.account_id as account_id', 'ac_from.name as name', 'ac_from.encrypted as encrypted', DB::Raw('SUM(t_from.amount) as `queryAmount`')]
);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
} }
/** /**
@@ -551,7 +482,7 @@ class ReportQuery implements ReportQueryInterface
->where('transaction_journals.user_id', Auth::user()->id) ->where('transaction_journals.user_id', Auth::user()->id)
->get( ->get(
['transaction_journals.id', 'transaction_journals.description', 'transactions.account_id', 'accounts.name', ['transaction_journals.id', 'transaction_journals.description', 'transactions.account_id', 'accounts.name',
'transactions.amount'] 'transactions.amount as queryAmount']
); );
} }
@@ -596,9 +527,79 @@ class ReportQuery implements ReportQueryInterface
[ [
'categories.id', 'categories.id',
'categories.name as name', 'categories.name as name',
DB::Raw('SUM(`transactions`.`amount`) * -1 AS `amount`') DB::Raw('SUM(`transactions`.`amount`) * -1 AS `queryAmount`')
] ]
); );
} }
/**
*
* This query will get all transaction journals and budget information for a specified account
* in a certain date range, where the transaction journal does not have a budget.
* There is no get() specified, this is up to the method itself.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Builder
*/
protected function queryJournalsNoBudget(Account $account, Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end)
->after($start)
->where('accounts.id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC');
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Builder
*/
protected function queryJournalsWithTransactions(Carbon $start, Carbon $end)
{
$query = TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
$query->before($end)->after($start)->where('transaction_journals.user_id', Auth::user()->id);
return $query;
}
} }

View File

@@ -43,7 +43,7 @@ interface ReportQueryInterface
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @return Collection * @return float
*/ */
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end); public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end);

View File

@@ -7,7 +7,6 @@ use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\AccountFormRequest; use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Input; use Input;
use Redirect; use Redirect;
use Session; use Session;
@@ -37,7 +36,7 @@ class AccountController extends Controller
*/ */
public function create($what = 'asset') public function create($what = 'asset')
{ {
$subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $what); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$subTitle = 'Create a new ' . e($what) . ' account'; $subTitle = 'Create a new ' . e($what) . ' account';
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle')); return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle'));
@@ -83,6 +82,7 @@ class AccountController extends Controller
*/ */
public function edit(Account $account, AccountRepositoryInterface $repository) public function edit(Account $account, AccountRepositoryInterface $repository)
{ {
$what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type]; $what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"'; $subTitle = 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
@@ -92,15 +92,19 @@ class AccountController extends Controller
// the opening balance is tricky: // the opening balance is tricky:
$openingBalanceAmount = null; $openingBalanceAmount = null;
if ($openingBalance) { if ($openingBalance) {
$transaction = $openingBalance->transactions()->where('account_id', $account->id)->first(); $transaction = $repository->getFirstTransaction($openingBalance, $account);
$openingBalanceAmount = $transaction->amount; $openingBalanceAmount = $transaction->amount;
} }
$preFilled = [ $preFilled = [
'accountRole' => $account->getMeta('accountRole'), 'accountRole' => $account->getMeta('accountRole'),
'ccType' => $account->getMeta('ccType'),
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null, 'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount 'openingBalance' => $openingBalanceAmount,
'virtualBalance' => floatval($account->virtual_balance)
]; ];
Session::flash('preFilled', $preFilled); Session::flash('preFilled', $preFilled);
@@ -108,50 +112,31 @@ class AccountController extends Controller
} }
/** /**
* @param string $what * @param $what
* @param AccountRepositoryInterface $repository
* *
* @return View * @return View
*/ */
public function index($what = 'default') public function index($what, AccountRepositoryInterface $repository)
{ {
$subTitle = Config::get('firefly.subTitlesByIdentifier.' . $what); $subTitle = Config::get('firefly.subTitlesByIdentifier.' . $what);
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$types = Config::get('firefly.accountTypesByIdentifier.' . $what); $types = Config::get('firefly.accountTypesByIdentifier.' . $what);
$size = 50; $accounts = $repository->getAccounts($types);
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$offset = ($page - 1) * $size;
// move to repository:
$set = Auth::user()->accounts()->with(
['accountmeta' => function ($query) {
$query->where('name', 'accountRole');
}]
)->accountTypeIn($types)->take($size)->offset($offset)->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$total = Auth::user()->accounts()->accountTypeIn($types)->count();
// last activity: // last activity:
$start = clone Session::get('start'); /**
* HERE WE ARE
*/
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$start->subDay(); $start->subDay();
$set->each( $accounts->each(
function (Account $account) use ($start) { function (Account $account) use ($start, $repository) {
$lastTransaction = $account->transactions()->leftJoin( $account->lastActivityDate = $repository->getLastActivity($account);
'transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->orderBy('transaction_journals.date', 'DESC')->first(['transactions.*', 'transaction_journals.date']);
if ($lastTransaction) {
$account->lastActivityDate = $lastTransaction->transactionjournal->date;
} else {
$account->lastActivityDate = null;
}
$account->startBalance = Steam::balance($account, $start); $account->startBalance = Steam::balance($account, $start);
$account->endBalance = Steam::balance($account, Session::get('end')); $account->endBalance = Steam::balance($account, clone Session::get('end', Carbon::now()->endOfMonth()));
} }
); );
$accounts = new LengthAwarePaginator($set, $total, $size, $page);
$accounts->setPath(route('accounts.index', $what));
return view('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts')); return view('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts'));
} }
@@ -168,6 +153,8 @@ class AccountController extends Controller
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type); $what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$journals = $repository->getJournals($account, $page); $journals = $repository->getJournals($account, $page);
$subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"'; $subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
$journals->setPath('accounts/show/' . $account->id);
return view('accounts.show', compact('account', 'what', 'subTitleIcon', 'journals', 'subTitle')); return view('accounts.show', compact('account', 'what', 'subTitleIcon', 'journals', 'subTitle'));
} }
@@ -183,6 +170,7 @@ class AccountController extends Controller
$accountData = [ $accountData = [
'name' => $request->input('name'), 'name' => $request->input('name'),
'accountType' => $request->input('what'), 'accountType' => $request->input('what'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'active' => true, 'active' => true,
'user' => Auth::user()->id, 'user' => Auth::user()->id,
'accountRole' => $request->input('accountRole'), 'accountRole' => $request->input('accountRole'),
@@ -196,7 +184,7 @@ class AccountController extends Controller
Session::flash('success', 'New account "' . $account->name . '" stored!'); Session::flash('success', 'New account "' . $account->name . '" stored!');
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
return Redirect::route('accounts.create', $request->input('what')); return Redirect::route('accounts.create', $request->input('what'))->withInput();
} }
return Redirect::route('accounts.index', $request->input('what')); return Redirect::route('accounts.index', $request->input('what'));
@@ -218,9 +206,12 @@ class AccountController extends Controller
'active' => $request->input('active'), 'active' => $request->input('active'),
'user' => Auth::user()->id, 'user' => Auth::user()->id,
'accountRole' => $request->input('accountRole'), 'accountRole' => $request->input('accountRole'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'openingBalance' => floatval($request->input('openingBalance')), 'openingBalance' => floatval($request->input('openingBalance')),
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')), 'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')), 'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'ccType' => $request->input('ccType'),
'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'),
]; ];
$repository->update($account, $accountData); $repository->update($account, $accountData);

View File

@@ -5,6 +5,7 @@ use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\Registrar; use Illuminate\Contracts\Auth\Registrar;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers; use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Mail; use Mail;
use Session; use Session;
@@ -63,20 +64,25 @@ class AuthController extends Controller
); );
} }
$this->auth->login($this->registrar->create($request->all())); $data = $request->all();
$data['password'] = bcrypt($data['password']);
$this->auth->login($this->registrar->create($data));
// get the email address // get the email address
$email = $this->auth->user()->email; $email = $this->auth->user()->email;
// send email. // send email.
Mail::send( Mail::send(
'emails.registered', [], function ($message) use ($email) { 'emails.registered', [], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III!'); $message->to($email, $email)->subject('Welcome to Firefly III!');
} }
); );
// set flash message // set flash message
Session::flash('success', 'You have registered successfully!'); Session::flash('success', 'You have registered successfully!');
Session::flash('gaEventCategory', 'user');
Session::flash('gaEventAction', 'new-registration');
return redirect($this->redirectPath()); return redirect($this->redirectPath());

View File

@@ -1,12 +1,14 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth; use Config;
use Carbon\Carbon;
use FireflyIII\Http\Requests; use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BillFormRequest; use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input; use Input;
use Redirect; use Redirect;
@@ -22,18 +24,64 @@ use View;
class BillController extends Controller class BillController extends Controller
{ {
/**
*
*/
public function __construct() public function __construct()
{ {
View::share('title', 'Bills'); View::share('title', 'Bills');
View::share('mainTitleIcon', 'fa-calendar-o'); View::share('mainTitleIcon', 'fa-calendar-o');
} }
/**
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse
*/
public function add(Bill $bill, AccountRepositoryInterface $repository)
{
$matches = explode(',', $bill->match);
$description = [];
$expense = null;
// get users expense accounts:
$accounts = $repository->getAccounts(Config::get('firefly.accountTypesByIdentifier.expense'));
foreach ($matches as $match) {
$match = strtolower($match);
// find expense account for each word if not found already:
if (is_null($expense)) {
/** @var Account $account */
foreach ($accounts as $account) {
$name = strtolower($account->name);
if (!(strpos($name, $match) === false)) {
$expense = $account;
break;
}
}
}
if (is_null($expense)) {
$description[] = $match;
}
}
$parameters = [
'description' => ucfirst(join(' ', $description)),
'expense_account' => is_null($expense) ? '' : $expense->name,
'amount' => round(($bill->amount_min + $bill->amount_max), 2),
];
Session::put('preFilled', $parameters);
return Redirect::to(route('transactions.create', 'withdrawal'));
}
/** /**
* @return $this * @return $this
*/ */
public function create() public function create()
{ {
$periods = \Config::get('firefly.periods_to_text'); $periods = Config::get('firefly.periods_to_text');
return view('bills.create')->with('periods', $periods)->with('subTitle', 'Create new'); return view('bills.create')->with('periods', $periods)->with('subTitle', 'Create new');
} }
@@ -53,9 +101,10 @@ class BillController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(Bill $bill) public function destroy(Bill $bill, BillRepositoryInterface $repository)
{ {
$bill->delete(); $repository->destroy($bill);
Session::flash('success', 'The bill was deleted.'); Session::flash('success', 'The bill was deleted.');
return Redirect::route('bills.index'); return Redirect::route('bills.index');
@@ -69,7 +118,7 @@ class BillController extends Controller
*/ */
public function edit(Bill $bill) public function edit(Bill $bill)
{ {
$periods = \Config::get('firefly.periods_to_text'); $periods = Config::get('firefly.periods_to_text');
return view('bills.edit')->with('periods', $periods)->with('bill', $bill)->with('subTitle', 'Edit "' . e($bill->name) . '"'); return view('bills.edit')->with('periods', $periods)->with('bill', $bill)->with('subTitle', 'Edit "' . e($bill->name) . '"');
} }
@@ -81,15 +130,11 @@ class BillController extends Controller
*/ */
public function index(BillRepositoryInterface $repository) public function index(BillRepositoryInterface $repository)
{ {
$bills = Auth::user()->bills()->orderBy('name', 'ASC')->get(); $bills = $repository->getBills();
$bills->each( $bills->each(
function (Bill $bill) use ($repository) { function (Bill $bill) use ($repository) {
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill); $bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$last = $bill->transactionjournals()->orderBy('date', 'DESC')->first(); $bill->lastFoundMatch = $repository->lastFoundMatch($bill);
$bill->lastFoundMatch = null;
if ($last) {
$bill->lastFoundMatch = $last->date;
}
} }
); );
@@ -106,25 +151,15 @@ class BillController extends Controller
if (intval($bill->active) == 0) { if (intval($bill->active) == 0) {
Session::flash('warning', 'Inactive bills cannot be scanned.'); Session::flash('warning', 'Inactive bills cannot be scanned.');
return Redirect::intended('/'); return Redirect::to(URL::previous());
} }
$set = \DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max)->get( $journals = $repository->getPossiblyRelatedJournals($bill);
['transaction_journal_id']
);
$ids = [];
/** @var Transaction $entry */
foreach ($set as $entry) {
$ids[] = intval($entry->transaction_journal_id);
}
if (count($ids) > 0) {
$journals = Auth::user()->transactionjournals()->whereIn('id', $ids)->get();
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$repository->scan($bill, $journal); $repository->scan($bill, $journal);
} }
}
Session::flash('success', 'Rescanned everything.'); Session::flash('success', 'Rescanned everything.');
@@ -138,11 +173,10 @@ class BillController extends Controller
*/ */
public function show(Bill $bill, BillRepositoryInterface $repository) public function show(Bill $bill, BillRepositoryInterface $repository)
{ {
$journals = $bill->transactionjournals()->withRelevantData()->orderBy('date', 'DESC')->get(); $journals = $repository->getJournals($bill);
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill); $bill->nextExpectedMatch = $repository->nextExpectedMatch($bill);
$hideBill = true; $hideBill = true;
return view('bills.show', compact('journals', 'hideBill', 'bill'))->with('subTitle', e($bill->name)); return view('bills.show', compact('journals', 'hideBill', 'bill'))->with('subTitle', e($bill->name));
} }
@@ -151,23 +185,7 @@ class BillController extends Controller
*/ */
public function store(BillFormRequest $request, BillRepositoryInterface $repository) public function store(BillFormRequest $request, BillRepositoryInterface $repository)
{ {
$billData = $request->getBillData();
var_dump($request->all());
$billData = [
'name' => $request->get('name'),
'match' => $request->get('match'),
'amount_min' => floatval($request->get('amount_min')),
'amount_currency_id' => floatval($request->get('amount_currency_id')),
'amount_max' => floatval($request->get('amount_max')),
'date' => new Carbon($request->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $request->get('repeat_freq'),
'skip' => intval($request->get('skip')),
'automatch' => intval($request->get('automatch')) === 1,
'active' => intval($request->get('active')) === 1,
];
$bill = $repository->store($billData); $bill = $repository->store($billData);
Session::flash('success', 'Bill "' . e($bill->name) . '" stored.'); Session::flash('success', 'Bill "' . e($bill->name) . '" stored.');
@@ -186,20 +204,7 @@ class BillController extends Controller
*/ */
public function update(Bill $bill, BillFormRequest $request, BillRepositoryInterface $repository) public function update(Bill $bill, BillFormRequest $request, BillRepositoryInterface $repository)
{ {
$billData = [ $billData = $request->getBillData();
'name' => $request->get('name'),
'match' => $request->get('match'),
'amount_min' => floatval($request->get('amount_min')),
'amount_currency_id' => floatval($request->get('amount_currency_id')),
'amount_max' => floatval($request->get('amount_max')),
'date' => new Carbon($request->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $request->get('repeat_freq'),
'skip' => intval($request->get('skip')),
'automatch' => intval($request->get('automatch')) === 1,
'active' => intval($request->get('active')) === 1,
];
$bill = $repository->update($bill, $billData); $bill = $repository->update($bill, $billData);
if (intval(Input::get('return_to_edit')) === 1) { if (intval(Input::get('return_to_edit')) === 1) {
@@ -210,42 +215,6 @@ class BillController extends Controller
return Redirect::route('bills.index'); return Redirect::route('bills.index');
// $data = Input::except('_token');
// $data['active'] = intval(Input::get('active'));
// $data['automatch'] = intval(Input::get('automatch'));
// $data['user_id'] = Auth::user()->id;
//
// // always validate:
// $messages = $this->_repository->validate($data);
//
// // flash messages:
// Session::flash('warnings', $messages['warnings']);
// Session::flash('successes', $messages['successes']);
// Session::flash('errors', $messages['errors']);
// if ($messages['errors']->count() > 0) {
// Session::flash('error', 'Could not update bill: ' . $messages['errors']->first());
//
// return Redirect::route('bills.edit', $bill->id)->withInput();
// }
//
// // return to update screen:
// if ($data['post_submit_action'] == 'validate_only') {
// return Redirect::route('bills.edit', $bill->id)->withInput();
// }
//
// // update
// $this->_repository->update($bill, $data);
// Session::flash('success', 'Bill "' . e($data['name']) . '" updated.');
//
// // go back to list
// if ($data['post_submit_action'] == 'update') {
// return Redirect::route('bills.index');
// }
//
// // go back to update screen.
// return Redirect::route('bills.edit', $bill->id)->withInput(['post_submit_action' => 'return_to_edit']);
} }
} }

View File

@@ -5,6 +5,7 @@ use Carbon\Carbon;
use FireflyIII\Http\Requests; use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BudgetFormRequest; use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Input; use Input;
@@ -12,6 +13,7 @@ use Preferences;
use Redirect; use Redirect;
use Response; use Response;
use Session; use Session;
use URL;
use View; use View;
/** /**
@@ -22,6 +24,9 @@ use View;
class BudgetController extends Controller class BudgetController extends Controller
{ {
/**
*
*/
public function __construct() public function __construct()
{ {
View::share('title', 'Budgets'); View::share('title', 'Budgets');
@@ -32,7 +37,6 @@ class BudgetController extends Controller
* @param Budget $budget * @param Budget $budget
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
* @throws Exception
*/ */
public function amount(Budget $budget, BudgetRepositoryInterface $repository) public function amount(Budget $budget, BudgetRepositoryInterface $repository)
{ {
@@ -49,6 +53,12 @@ class BudgetController extends Controller
*/ */
public function create() public function create()
{ {
// put previous url in session if not redirect from store (not "create another").
if (Session::get('budgets.create.fromStore') !== true) {
Session::put('budgets.create.url', URL::previous());
}
Session::forget('budgets.create.fromStore');
return view('budgets.create')->with('subTitle', 'Create a new budget'); return view('budgets.create')->with('subTitle', 'Create a new budget');
} }
@@ -61,6 +71,9 @@ class BudgetController extends Controller
{ {
$subTitle = 'Delete budget' . e($budget->name) . '"'; $subTitle = 'Delete budget' . e($budget->name) . '"';
// put previous url in session
Session::put('budgets.delete.url', URL::previous());
return view('budgets.delete', compact('budget', 'subTitle')); return view('budgets.delete', compact('budget', 'subTitle'));
} }
@@ -75,9 +88,10 @@ class BudgetController extends Controller
$name = $budget->name; $name = $budget->name;
$repository->destroy($budget); $repository->destroy($budget);
Session::flash('success', 'The budget "' . e($name) . '" was deleted.'); Session::flash('success', 'The budget "' . e($name) . '" was deleted.');
return Redirect::route('budgets.index'); return Redirect::to(Session::get('budgets.delete.url'));
} }
/** /**
@@ -89,6 +103,12 @@ class BudgetController extends Controller
{ {
$subTitle = 'Edit budget "' . e($budget->name) . '"'; $subTitle = 'Edit budget "' . e($budget->name) . '"';
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('budgets.edit.fromUpdate') !== true) {
Session::put('budgets.edit.url', URL::previous());
}
Session::forget('budgets.edit.fromUpdate');
return view('budgets.edit', compact('budget', 'subTitle')); return view('budgets.edit', compact('budget', 'subTitle'));
} }
@@ -98,44 +118,44 @@ class BudgetController extends Controller
*/ */
public function index(BudgetRepositoryInterface $repository) public function index(BudgetRepositoryInterface $repository)
{ {
$budgets = Auth::user()->budgets()->get(); $budgets = $repository->getActiveBudgets();
$inactive = $repository->getInactiveBudgets();
/**
* Do some cleanup:
*/
$repository->cleanupBudgets();
// loop the budgets: // loop the budgets:
$budgets->each( $budgets->each(
function (Budget $budget) use ($repository) { function (Budget $budget) use ($repository) {
$date = Session::get('start', Carbon::now()->startOfMonth()); $date = Session::get('start', Carbon::now()->startOfMonth());
$budget->spent = $repository->spentInMonth($budget, $date); $budget->spent = $repository->spentInMonth($budget, $date);
$budget->currentRep = $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']); $budget->currentRep = $repository->getCurrentRepetition($budget, $date);
} }
); );
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY'); $dateAsString = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
$spent = $budgets->sum('spent'); $spent = $budgets->sum('spent');
$amount = Preferences::get('budgetIncomeTotal' . $date, 1000)->data; $amount = Preferences::get('budgetIncomeTotal' . $dateAsString, 1000)->data;
$overspent = $spent > $amount; $overspent = $spent > $amount;
$spentPCT = $overspent ? ceil($amount / $spent * 100) : ceil($spent / $amount * 100); $spentPCT = $overspent ? ceil($amount / $spent * 100) : ceil($spent / $amount * 100);
$budgetMax = Preferences::get('budgetMaximum', 1000); $budgetMax = Preferences::get('budgetMaximum', 1000);
$budgetMaximum = $budgetMax->data; $budgetMaximum = $budgetMax->data;
return view('budgets.index', compact('budgetMaximum', 'budgets', 'spent', 'spentPCT', 'overspent', 'amount')); return view('budgets.index', compact('budgetMaximum', 'inactive', 'budgets', 'spent', 'spentPCT', 'overspent', 'amount'));
} }
/** /**
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function noBudget() public function noBudget(BudgetRepositoryInterface $repository)
{ {
$start = \Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = \Session::get('end', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->startOfMonth());
$list = Auth::user() $list = $repository->getWithoutBudget($start, $end);
->transactionjournals() $subTitle = 'Transactions without a budget between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y');
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('budget_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a budget in ' . $start->format('F Y');
return view('budgets.noBudget', compact('list', 'subTitle')); return view('budgets.noBudget', compact('list', 'subTitle'));
} }
@@ -152,20 +172,6 @@ class BudgetController extends Controller
return Redirect::route('budgets.index'); return Redirect::route('budgets.index');
} }
public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository)
{
$budgetData = [
'name' => $request->input('name'),
'user' => Auth::user()->id,
];
$budget = $repository->store($budgetData);
Session::flash('success', 'New budget "' . $budget->name . '" stored!');
return Redirect::route('budgets.index');
}
/** /**
* *
* @param Budget $budget * @param Budget $budget
@@ -181,12 +187,40 @@ class BudgetController extends Controller
$hideBudget = true; // used in transaction list. $hideBudget = true; // used in transaction list.
$journals = $repository->getJournals($budget, $repetition); $journals = $repository->getJournals($budget, $repetition);
$limits = !is_null($repetition->id) ? [$repetition->budgetLimit] : $budget->budgetLimits()->orderBy('startdate', 'DESC')->get(); $limits = !is_null($repetition->id) ? [$repetition->budgetLimit] : $repository->getBudgetLimits($budget);
$subTitle = !is_null($repetition->id) ? e($budget->name) . ' in ' . $repetition->startdate->format('F Y') : e($budget->name); $subTitle = !is_null($repetition->id) ? e($budget->name) . ' in ' . $repetition->startdate->format('F Y') : e($budget->name);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle', 'hideBudget')); return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle', 'hideBudget'));
} }
/**
* @param BudgetFormRequest $request
* @param BudgetRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository)
{
$budgetData = [
'name' => $request->input('name'),
'user' => Auth::user()->id,
];
$budget = $repository->store($budgetData);
Session::flash('success', 'New budget "' . $budget->name . '" stored!');
if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('budgets.create.fromStore', true);
return Redirect::route('budgets.create')->withInput();
}
// redirect to previous URL.
return Redirect::to(Session::get('budgets.create.url'));
}
/** /**
* @param Budget $budget * @param Budget $budget
* @param BudgetFormRequest $request * @param BudgetFormRequest $request
@@ -198,13 +232,22 @@ class BudgetController extends Controller
{ {
$budgetData = [ $budgetData = [
'name' => $request->input('name'), 'name' => $request->input('name'),
'active' => intval($request->input('active')) == 1
]; ];
$repository->update($budget, $budgetData); $repository->update($budget, $budgetData);
Session::flash('success', 'Budget "' . $budget->name . '" updated.'); Session::flash('success', 'Budget "' . $budget->name . '" updated.');
return Redirect::route('budgets.index'); if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('budgets.edit.fromUpdate', true);
return Redirect::route('budgets.edit', $budget->id)->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return Redirect::to(Session::get('budgets.edit.url'));
} }

View File

@@ -10,9 +10,9 @@ use Illuminate\Pagination\LengthAwarePaginator;
use Input; use Input;
use Redirect; use Redirect;
use Session; use Session;
use URL;
use View; use View;
/** /**
* Class CategoryController * Class CategoryController
* *
@@ -35,6 +35,12 @@ class CategoryController extends Controller
*/ */
public function create() public function create()
{ {
// put previous url in session if not redirect from store (not "create another").
if (Session::get('categories.create.fromStore') !== true) {
Session::put('categories.create.url', URL::previous());
}
Session::forget('categories.create.fromStore');
return view('categories.create')->with('subTitle', 'Create a new category'); return view('categories.create')->with('subTitle', 'Create a new category');
} }
@@ -47,6 +53,9 @@ class CategoryController extends Controller
{ {
$subTitle = 'Delete category' . e($category->name) . '"'; $subTitle = 'Delete category' . e($category->name) . '"';
// put previous url in session
Session::put('categories.delete.url', URL::previous());
return view('categories.delete', compact('category', 'subTitle')); return view('categories.delete', compact('category', 'subTitle'));
} }
@@ -63,7 +72,7 @@ class CategoryController extends Controller
Session::flash('success', 'The category "' . e($name) . '" was deleted.'); Session::flash('success', 'The category "' . e($name) . '" was deleted.');
return Redirect::route('categories.index'); return Redirect::to(Session::get('categories.delete.url'));
} }
/** /**
@@ -75,23 +84,27 @@ class CategoryController extends Controller
{ {
$subTitle = 'Edit category "' . e($category->name) . '"'; $subTitle = 'Edit category "' . e($category->name) . '"';
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('categories.edit.fromUpdate') !== true) {
Session::put('categories.edit.url', URL::previous());
}
Session::forget('categories.edit.fromUpdate');
return view('categories.edit', compact('category', 'subTitle')); return view('categories.edit', compact('category', 'subTitle'));
} }
/** /**
* @return $this * @return $this
*
*/ */
public function index() public function index(CategoryRepositoryInterface $repository)
{ {
$categories = Auth::user()->categories()->orderBy('name', 'ASC')->get(); $categories = $repository->getCategories();
$categories->each( $categories->each(
function (Category $category) { function (Category $category) use ($repository) {
$latest = $category->transactionjournals()->orderBy('date', 'DESC')->first(); $category->lastActivity = $repository->getLatestActivity($category);
if ($latest) {
$category->lastActivity = $latest->date;
}
} }
); );
@@ -101,19 +114,11 @@ class CategoryController extends Controller
/** /**
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function noCategory() public function noCategory(CategoryRepositoryInterface $repository)
{ {
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->startOfMonth());
$list = Auth::user() $list = $repository->getWithoutCategory($start, $end);
->transactionjournals()
->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('category_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->get(['transaction_journals.*']);
$subTitle = 'Transactions without a category between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y'); $subTitle = 'Transactions without a category between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y');
return view('categories.noCategory', compact('list', 'subTitle')); return view('categories.noCategory', compact('list', 'subTitle'));
@@ -128,13 +133,10 @@ class CategoryController extends Controller
{ {
$hideCategory = true; // used in list. $hideCategory = true; // used in list.
$page = intval(Input::get('page')); $page = intval(Input::get('page'));
$offset = $page > 0 ? $page * 50 : 0; $set = $repository->getJournals($category, $page);
$set = $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)->orderBy('date', 'DESC')->get( $count = $repository->countJournals($category);
['transaction_journals.*']
);
$count = $category->transactionJournals()->count();
$journals = new LengthAwarePaginator($set, $count, 50, $page); $journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('categories/show/' . $category->id);
return view('categories.show', compact('category', 'journals', 'hideCategory')); return view('categories.show', compact('category', 'journals', 'hideCategory'));
} }
@@ -155,8 +157,13 @@ class CategoryController extends Controller
Session::flash('success', 'New category "' . $category->name . '" stored!'); Session::flash('success', 'New category "' . $category->name . '" stored!');
return Redirect::route('categories.index'); if (intval(Input::get('create_another')) === 1) {
Session::put('categories.create.fromStore', true);
return Redirect::route('categories.create')->withInput();
}
return Redirect::route('categories.index');
} }
@@ -177,7 +184,14 @@ class CategoryController extends Controller
Session::flash('success', 'Category "' . $category->name . '" updated.'); Session::flash('success', 'Category "' . $category->name . '" updated.');
return Redirect::route('categories.index'); if (intval(Input::get('return_to_edit')) === 1) {
Session::put('categories.edit.fromUpdate', true);
return Redirect::route('categories.edit', $category->id);
}
// redirect to previous URL.
return Redirect::to(Session::get('categories.edit.url'));
} }

View File

@@ -5,13 +5,14 @@ use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\CurrencyFormRequest; use FireflyIII\Http\Requests\CurrencyFormRequest;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Input; use Input;
use Preferences; use Preferences;
use Redirect; use Redirect;
use Session; use Session;
use URL;
use View; use View;
/** /**
* Class CurrencyController * Class CurrencyController
* *
@@ -39,6 +40,12 @@ class CurrencyController extends Controller
$subTitleIcon = 'fa-plus'; $subTitleIcon = 'fa-plus';
$subTitle = 'Create a new currency'; $subTitle = 'Create a new currency';
// put previous url in session if not redirect from store (not "create another").
if (Session::get('currency.create.fromStore') !== true) {
Session::put('currency.create.url', URL::previous());
}
Session::forget('currency.create.fromStore');
return view('currency.create', compact('subTitleIcon', 'subTitle')); return view('currency.create', compact('subTitleIcon', 'subTitle'));
} }
@@ -50,9 +57,7 @@ class CurrencyController extends Controller
public function defaultCurrency(TransactionCurrency $currency) public function defaultCurrency(TransactionCurrency $currency)
{ {
$currencyPreference = Preferences::get('currencyPreference', 'EUR'); Preferences::set('currencyPreference', $currency->code);
$currencyPreference->data = $currency->code;
$currencyPreference->save();
Session::flash('success', $currency->name . ' is now the default currency.'); Session::flash('success', $currency->name . ' is now the default currency.');
Cache::forget('FFCURRENCYSYMBOL'); Cache::forget('FFCURRENCYSYMBOL');
@@ -67,14 +72,18 @@ class CurrencyController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/ */
public function delete(TransactionCurrency $currency) public function delete(TransactionCurrency $currency, CurrencyRepositoryInterface $repository)
{ {
if ($currency->transactionJournals()->count() > 0) {
if ($repository->countJournals($currency) > 0) {
Session::flash('error', 'Cannot delete ' . e($currency->name) . ' because there are still transactions attached to it.'); Session::flash('error', 'Cannot delete ' . e($currency->name) . ' because there are still transactions attached to it.');
return Redirect::route('currency.index'); return Redirect::route('currency.index');
} }
// put previous url in session
Session::put('currency.delete.url', URL::previous());
return view('currency.delete', compact('currency')); return view('currency.delete', compact('currency'));
} }
@@ -84,10 +93,10 @@ class CurrencyController extends Controller
* *
* @return \Illuminate\Http\RedirectResponse * @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(TransactionCurrency $currency) public function destroy(TransactionCurrency $currency, CurrencyRepositoryInterface $repository)
{ {
if ($currency->transactionJournals()->count() > 0) { if ($repository->countJournals($currency) > 0) {
Session::flash('error', 'Cannot delete ' . e($currency->name) . ' because there are still transactions attached to it.'); Session::flash('error', 'Cannot destroy ' . e($currency->name) . ' because there are still transactions attached to it.');
return Redirect::route('currency.index'); return Redirect::route('currency.index');
} }
@@ -96,7 +105,7 @@ class CurrencyController extends Controller
$currency->delete(); $currency->delete();
return Redirect::route('currency.index'); return Redirect::to(Session::get('currency.delete.url'));
} }
/** /**
@@ -110,6 +119,12 @@ class CurrencyController extends Controller
$subTitle = 'Edit currency "' . e($currency->name) . '"'; $subTitle = 'Edit currency "' . e($currency->name) . '"';
$currency->symbol = htmlentities($currency->symbol); $currency->symbol = htmlentities($currency->symbol);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('currency.edit.fromUpdate') !== true) {
Session::put('currency.edit.url', URL::previous());
}
Session::forget('currency.edit.fromUpdate');
return view('currency.edit', compact('currency', 'subTitle', 'subTitleIcon')); return view('currency.edit', compact('currency', 'subTitle', 'subTitleIcon'));
} }
@@ -117,12 +132,10 @@ class CurrencyController extends Controller
/** /**
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function index() public function index(CurrencyRepositoryInterface $repository)
{ {
$currencies = TransactionCurrency::get(); $currencies = $repository->get();
$currencyPreference = Preferences::get('currencyPreference', 'EUR'); $defaultCurrency = $repository->getCurrencyByPreference(Preferences::get('currencyPreference', 'EUR'));
$defaultCurrency = TransactionCurrency::whereCode($currencyPreference->data)->first();
return view('currency.index', compact('currencies', 'defaultCurrency')); return view('currency.index', compact('currencies', 'defaultCurrency'));
} }
@@ -132,26 +145,22 @@ class CurrencyController extends Controller
* *
* @return $this|\Illuminate\Http\RedirectResponse * @return $this|\Illuminate\Http\RedirectResponse
*/ */
public function store(CurrencyFormRequest $request) public function store(CurrencyFormRequest $request, CurrencyRepositoryInterface $repository)
{ {
$data = $request->getCurrencyData();
$currency = $repository->store($data);
// no repository, because the currency controller is relatively simple.
$currency = TransactionCurrency::create(
[
'name' => $request->get('name'),
'code' => $request->get('code'),
'symbol' => $request->get('symbol'),
]
);
Session::flash('success', 'Currency "' . $currency->name . '" created'); Session::flash('success', 'Currency "' . $currency->name . '" created');
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
return Redirect::route('currency.create'); Session::put('currency.create.fromStore', true);
return Redirect::route('currency.create')->withInput();
} }
return Redirect::route('currency.index'); // redirect to previous URL.
return Redirect::to(Session::get('currency.create.url'));
} }
@@ -161,22 +170,22 @@ class CurrencyController extends Controller
* *
* @return $this|\Illuminate\Http\RedirectResponse * @return $this|\Illuminate\Http\RedirectResponse
*/ */
public function update(TransactionCurrency $currency, CurrencyFormRequest $request) public function update(TransactionCurrency $currency, CurrencyFormRequest $request, CurrencyRepositoryInterface $repository)
{ {
$data = $request->getCurrencyData();
$currency->code = $request->get('code'); $currency = $repository->update($currency, $data);
$currency->symbol = $request->get('symbol');
$currency->name = $request->get('name');
$currency->save();
Session::flash('success', 'Currency "' . e($currency->name) . '" updated.'); Session::flash('success', 'Currency "' . e($currency->name) . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) { if (intval(Input::get('return_to_edit')) === 1) {
Session::put('currency.edit.fromUpdate', true);
return Redirect::route('currency.edit', $currency->id); return Redirect::route('currency.edit', $currency->id);
} }
return Redirect::route('currency.index'); // redirect to previous URL.
return Redirect::to(Session::get('currency.edit.url'));
} }

View File

@@ -1,12 +1,8 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use App;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use Crypt;
use Exception;
use FireflyIII\Helpers\Report\ReportQueryInterface; use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
@@ -16,11 +12,12 @@ use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Grumpydictator\Gchart\GChart; use Grumpydictator\Gchart\GChart;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation; use Navigation;
use Preferences; use Preferences;
@@ -52,9 +49,11 @@ class GoogleChartController extends Controller
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start; $current = clone $start;
$today = new Carbon;
while ($end >= $current) { while ($end >= $current) {
$chart->addRow(clone $current, Steam::balance($account, $current), false); $certain = $current < $today;
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
$current->addDay(); $current->addDay();
} }
@@ -69,19 +68,15 @@ class GoogleChartController extends Controller
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function allAccountsBalanceChart(GChart $chart) public function allAccountsBalanceChart(GChart $chart, AccountRepositoryInterface $repository)
{ {
$chart->addColumn('Day of the month', 'date'); $chart->addColumn('Day of the month', 'date');
$frontPage = Preferences::get('frontPageAccounts', []); $frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage);
if ($frontPage->data == []) {
$accounts = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
} else {
$accounts = Auth::user()->accounts()->whereIn('id', $frontPage->data)->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
}
$index = 1; $index = 1;
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
@@ -115,22 +110,16 @@ class GoogleChartController extends Controller
*/ */
public function allBudgetsAndSpending($year, GChart $chart, BudgetRepositoryInterface $repository) public function allBudgetsAndSpending($year, GChart $chart, BudgetRepositoryInterface $repository)
{ {
try { $budgets = $repository->getBudgets();
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$budgets = Auth::user()->budgets()->get();
$budgets->sortBy('name');
$chart->addColumn('Month', 'date'); $chart->addColumn('Month', 'date');
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number'); $chart->addColumn($budget->name, 'number');
} }
$start = Carbon::createFromDate(intval($year), 1, 1); $start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start; $end = clone $start;
$end->endOfYear(); $end->endOfYear();
while ($start <= $end) { while ($start <= $end) {
$row = [clone $start]; $row = [clone $start];
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
@@ -141,7 +130,6 @@ class GoogleChartController extends Controller
$start->addMonth(); $start->addMonth();
} }
$chart->generate(); $chart->generate();
return Response::json($chart->getData()); return Response::json($chart->getData());
@@ -153,73 +141,44 @@ class GoogleChartController extends Controller
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function allBudgetsHomeChart(GChart $chart) public function allBudgetsHomeChart(GChart $chart, BudgetRepositoryInterface $repository)
{ {
$chart->addColumn('Budget', 'string'); $chart->addColumn('Budget', 'string');
$chart->addColumn('Budgeted', 'number'); $chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number'); $chart->addColumn('Spent', 'number');
$budgets = Auth::user()->budgets()->orderBy('name', 'DESC')->get(); $budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$allEntries = new Collection;
/** @var Budget $budget */
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
/** @var Collection $repetitions */
$repetitions = LimitRepetition::
leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00'))
->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->get(['limit_repetitions.*']);
// no results? search entire range for expenses and list those.
if ($repetitions->count() == 0) { if ($repetitions->count() == 0) {
$expenses = floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1; $expenses = $repository->sumBudgetExpensesInPeriod($budget, $start, $end);
if ($expenses > 0) { $allEntries->push([$budget->name, 0, $expenses]);
$chart->addRow($budget->name, 0, $expenses); continue;
} }
} else {
// add with foreach:
/** @var LimitRepetition $repetition */ /** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) { foreach ($repetitions as $repetition) {
$expenses = $repository->sumBudgetExpensesInPeriod($budget, $repetition->startdate, $repetition->enddate);
$expenses $allEntries->push([$budget->name . ' (' . $repetition->startdate->format('j M Y') . ')', floatval($repetition->amount), $expenses]);
=
floatval($budget->transactionjournals()->before($repetition->enddate)->after($repetition->startdate)->lessThan(0)->sum('amount')) * -1;
if ($expenses > 0) {
$chart->addRow($budget->name . ' (' . $repetition->startdate->format('j M Y') . ')', floatval($repetition->amount), $expenses);
}
} }
} }
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push(['(no budget)', 0, $noBudgetExpenses]);
foreach ($allEntries as $entry) {
if ($entry[2] > 0) {
$chart->addRow($entry[0], $entry[1], $entry[2]);
}
} }
$noBudgetSet = Auth::user()
->transactionjournals()
->whereNotIn(
'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) {
$query
->select('transaction_journals.id')
->from('transaction_journals')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'))
->whereNotNull('budget_transaction_journal.budget_id');
}
)
->before($end)
->after($start)
->lessThan(0)
->transactionTypes(['Withdrawal'])
->get();
$sum = $noBudgetSet->sum('amount') * -1;
$chart->addRow('No budget', 0, $sum);
$chart->generate(); $chart->generate();
return Response::json($chart->getData()); return Response::json($chart->getData());
} }
/** /**
@@ -227,35 +186,20 @@ class GoogleChartController extends Controller
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function allCategoriesHomeChart(GChart $chart) public function allCategoriesHomeChart(GChart $chart, CategoryRepositoryInterface $repository)
{ {
$chart->addColumn('Category', 'string'); $chart->addColumn('Category', 'string');
$chart->addColumn('Spent', 'number'); $chart->addColumn('Spent', 'number');
// query!
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$set = TransactionJournal::leftJoin( $set = $repository->getCategoriesAndExpenses($start, $end);
'transactions',
function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->before($end)
->after($start)
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('sum', 'DESC')
->get(['categories.id', 'categories.name', \DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
foreach ($set as $entry) { foreach ($set as $entry) {
$entry->name = strlen($entry->name) == 0 ? '(no category)' : $entry->name; $isEncrypted = intval($entry->encrypted) == 1 ? true : false;
$chart->addRow($entry->name, floatval($entry->sum)); $name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$chart->addRow($name, floatval($entry->sum));
} }
$chart->generate(); $chart->generate();
@@ -266,40 +210,23 @@ class GoogleChartController extends Controller
/** /**
* @param Bill $bill * @param Bill $bill
* @param GChart $chart
* *
* @return \Illuminate\Http\JsonResponse * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function billOverview(Bill $bill, GChart $chart) public function billOverview(Bill $bill, GChart $chart, BillRepositoryInterface $repository)
{ {
$chart->addColumn('Date', 'date'); $chart->addColumn('Date', 'date');
$chart->addColumn('Max amount', 'number'); $chart->addColumn('Max amount', 'number');
$chart->addColumn('Min amount', 'number'); $chart->addColumn('Min amount', 'number');
$chart->addColumn('Current entry', 'number'); $chart->addColumn('Recorded bill entry', 'number');
// get first transaction or today for start: // get first transaction or today for start:
$first = $bill->transactionjournals()->orderBy('date', 'ASC')->first(); $results = $repository->getJournals($bill);
if ($first) { /** @var TransactionJournal $result */
$start = $first->date; foreach ($results as $result) {
} else { $chart->addRow(clone $result->date, floatval($bill->amount_max), floatval($bill->amount_min), floatval($result->amount));
$start = new Carbon;
}
$end = new Carbon;
while ($start <= $end) {
$result = $bill->transactionjournals()->before($end)->after($start)->first();
if ($result) {
/** @var Transaction $tr */
foreach ($result->transactions()->get() as $tr) {
if (floatval($tr->amount) > 0) {
$amount = floatval($tr->amount);
}
}
} else {
$amount = 0;
}
unset($result);
$chart->addRow(clone $start, $bill->amount_max, $bill->amount_min, $amount);
$start = Navigation::addPeriod($start, $bill->repeat_freq, 0);
} }
$chart->generate(); $chart->generate();
@@ -313,18 +240,21 @@ class GoogleChartController extends Controller
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function billsOverview(GChart $chart, BillRepositoryInterface $repository) public function billsOverview(GChart $chart, BillRepositoryInterface $repository, AccountRepositoryInterface $accounts)
{ {
$chart->addColumn('Name', 'string'); $chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number'); $chart->addColumn('Amount', 'number');
$paid = ['items' => [], 'amount' => 0];
$unpaid = ['items' => [], 'amount' => 0];
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$bills = $repository->getActiveBills();
$bills = Auth::user()->bills()->where('active', 1)->get(); $paid = new Collection; // journals.
$unpaid = new Collection; // bills
// loop paid and create single entry:
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($bills as $bill) { foreach ($bills as $bill) {
@@ -332,28 +262,56 @@ class GoogleChartController extends Controller
foreach ($ranges as $range) { foreach ($ranges as $range) {
// paid a bill in this range? // paid a bill in this range?
$count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count(); $journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
if ($count == 0) { if ($journals->count() == 0) {
$unpaid['items'][] = $bill->name . ' (' . $range['start']->format('jS M Y') . ')'; $unpaid->push([$bill, $range['start']]);
$unpaid['amount'] += ($bill->amount_max + $bill->amount_min / 2);
} else { } else {
$journal = $bill->transactionjournals()->with('transactions')->before($range['end'])->after($range['start'])->first(); $paid = $paid->merge($journals);
$paid['items'][] = $journal->description;
$amount = 0;
foreach ($journal->transactions as $t) {
if (floatval($t->amount) > 0) {
$amount = floatval($t->amount);
}
}
$paid['amount'] += $amount;
} }
} }
} }
$chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']); $creditCards = $accounts->getCreditCards();
$chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']); foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$amount = $balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $amount);
unset($description, $amount);
$unpaid->push([$fakeBill, $date]);
}
if ($balance == 0) {
// find transfer(s) TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$journals = $accounts->getTransfersInRange($creditCard, $start, $end);
$paid = $paid->merge($journals);
}
}
/** @var TransactionJournal $entry */
foreach ($paid as $entry) {
$paidDescriptions[] = $entry->description;
$paidAmount += floatval($entry->amount);
}
// loop unpaid:
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$unpaidDescriptions[] = $description;
$unpaidAmount += $amount;
unset($amount, $description);
}
$chart->addRow('Unpaid: ' . join(', ', $unpaidDescriptions), $unpaidAmount);
$chart->addRow('Paid: ' . join(', ', $paidDescriptions), $paidAmount);
$chart->generate(); $chart->generate();
return Response::json($chart->getData()); return Response::json($chart->getData());
@@ -366,7 +324,7 @@ class GoogleChartController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function budgetLimitSpending(Budget $budget, LimitRepetition $repetition, GChart $chart) public function budgetLimitSpending(Budget $budget, LimitRepetition $repetition, GChart $chart, BudgetRepositoryInterface $repository)
{ {
$start = clone $repetition->startdate; $start = clone $repetition->startdate;
$end = $repetition->enddate; $end = $repetition->enddate;
@@ -381,7 +339,7 @@ class GoogleChartController extends Controller
/* /*
* Sum of expenses on this day: * Sum of expenses on this day:
*/ */
$sum = floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($start)->sum('amount')); $sum = $repository->expensesOnDay($budget, $start);
$amount += $sum; $amount += $sum;
$chart->addRow(clone $start, $amount); $chart->addRow(clone $start, $amount);
$start->addDay(); $start->addDay();
@@ -393,37 +351,22 @@ class GoogleChartController extends Controller
} }
/** /**
*
* @param Budget $budget * @param Budget $budget
*
* @param int $year * @param int $year
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* *
* @return \Illuminate\Http\JsonResponse * @return \Symfony\Component\HttpFoundation\Response
*/ */
public function budgetsAndSpending(Budget $budget, $year = 0) public function budgetsAndSpending(Budget $budget, $year = 0, GChart $chart, BudgetRepositoryInterface $repository)
{ {
$chart = App::make('Grumpydictator\Gchart\GChart');
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepository');
$chart->addColumn('Month', 'date'); $chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number'); $chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number'); $chart->addColumn('Spent', 'number');
if ($year == 0) {
// grab the first budgetlimit ever:
$firstLimit = $budget->budgetlimits()->orderBy('startdate', 'ASC')->first();
if ($firstLimit) {
$start = new Carbon($firstLimit->startdate);
} else {
$start = Carbon::now()->startOfYear();
}
// grab the last budget limit ever: if ($year == 0) {
$lastLimit = $budget->budgetlimits()->orderBy('startdate', 'DESC')->first(); $start = $repository->getFirstBudgetLimitDate($budget);
if ($lastLimit) { $end = $repository->getLastBudgetLimitDate($budget);
$end = new Carbon($lastLimit->startdate);
} else {
$end = Carbon::now()->endOfYear();
}
} else { } else {
$start = Carbon::createFromDate(intval($year), 1, 1); $start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start; $end = clone $start;
@@ -432,18 +375,7 @@ class GoogleChartController extends Controller
while ($start <= $end) { while ($start <= $end) {
$spent = $repository->spentInMonth($budget, $start); $spent = $repository->spentInMonth($budget, $start);
$repetition = LimitRepetition::leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') $budgeted = $repository->getLimitAmountOnDate($budget, $start);
->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->first(['limit_repetitions.*']);
if ($repetition) {
$budgeted = floatval($repetition->amount);
\Log::debug('Found a repetition on ' . $start->format('Y-m-d') . ' for budget ' . $budget->name . '!');
} else {
\Log::debug('No repetition on ' . $start->format('Y-m-d') . ' for budget ' . $budget->name);
$budgeted = null;
}
$chart->addRow(clone $start, $budgeted, $spent); $chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth(); $start->addMonth();
} }
@@ -461,12 +393,11 @@ class GoogleChartController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function categoryOverviewChart(Category $category, GChart $chart) public function categoryOverviewChart(Category $category, GChart $chart, CategoryRepositoryInterface $repository)
{ {
// oldest transaction in category: // oldest transaction in category:
/** @var TransactionJournal $first */ $start = $repository->getFirstActivityDate($category);
$first = $category->transactionjournals()->orderBy('date', 'ASC')->first();
$start = $first->date;
/** @var Preference $range */ /** @var Preference $range */
$range = Preferences::get('viewRange', '1M'); $range = Preferences::get('viewRange', '1M');
// jump to start of week / month / year / etc (TODO). // jump to start of week / month / year / etc (TODO).
@@ -479,13 +410,12 @@ class GoogleChartController extends Controller
while ($start <= $end) { while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range->data); $currentEnd = Navigation::endOfPeriod($start, $range->data);
$spent = floatval($category->transactionjournals()->before($currentEnd)->after($start)->lessThan(0)->sum('amount')) * -1; $spent = $repository->spentInPeriodSum($category, $start, $currentEnd);
$chart->addRow(clone $start, $spent); $chart->addRow(clone $start, $spent);
$start = Navigation::addPeriod($start, $range->data, 0); $start = Navigation::addPeriod($start, $range->data, 0);
} }
$chart->generate(); $chart->generate();
return Response::json($chart->getData()); return Response::json($chart->getData());
@@ -499,17 +429,15 @@ class GoogleChartController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function categoryPeriodChart(Category $category, GChart $chart) public function categoryPeriodChart(Category $category, GChart $chart, CategoryRepositoryInterface $repository)
{ {
// oldest transaction in category: $start = clone Session::get('start', Carbon::now()->startOfMonth());
/** @var TransactionJournal $first */
$start = Session::get('start');
$chart->addColumn('Period', 'date'); $chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number'); $chart->addColumn('Spent', 'number');
$end = Session::get('end'); $end = Session::get('end', Carbon::now()->endOfMonth());
while ($start <= $end) { while ($start <= $end) {
$spent = floatval($category->transactionjournals()->onDate($start)->lessThan(0)->sum('amount')) * -1; $spent = $repository->spentOnDaySum($category, $start);
$chart->addRow(clone $start, $spent); $chart->addRow(clone $start, $spent);
$start->addDay(); $start->addDay();
} }
@@ -527,15 +455,18 @@ class GoogleChartController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function piggyBankHistory(PiggyBank $piggyBank, GChart $chart) public function piggyBankHistory(PiggyBank $piggyBank, GChart $chart, PiggyBankRepositoryInterface $repository)
{ {
$chart->addColumn('Date', 'date'); $chart->addColumn('Date', 'date');
$chart->addColumn('Balance', 'number'); $chart->addColumn('Balance', 'number');
$set = \DB::table('piggy_bank_events')->where('piggy_bank_id', $piggyBank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]); /** @var Collection $set */
$set = $repository->getEventSummarySet($piggyBank);
$sum = 0;
foreach ($set as $entry) { foreach ($set as $entry) {
$chart->addRow(new Carbon($entry->date), floatval($entry->sum)); $sum += floatval($entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
} }
$chart->generate(); $chart->generate();
@@ -552,11 +483,7 @@ class GoogleChartController extends Controller
*/ */
public function yearInExp($year, GChart $chart, ReportQueryInterface $query) public function yearInExp($year, GChart $chart, ReportQueryInterface $query)
{ {
try {
$start = new Carbon('01-01-' . $year); $start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$chart->addColumn('Month', 'date'); $chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number'); $chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number'); $chart->addColumn('Expenses', 'number');
@@ -571,19 +498,9 @@ class GoogleChartController extends Controller
while ($start < $end) { while ($start < $end) {
$currentEnd = clone $start; $currentEnd = clone $start;
$currentEnd->endOfMonth(); $currentEnd->endOfMonth();
// total income: // total income && total expenses:
$income = $query->incomeByPeriod($start, $currentEnd, $showSharedReports); $incomeSum = floatval($query->incomeByPeriod($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$incomeSum = 0; $expenseSum = floatval($query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
foreach ($income as $entry) {
$incomeSum += floatval($entry->amount);
}
// total expenses:
$expense = $query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports);
$expenseSum = 0;
foreach ($expense as $entry) {
$expenseSum += floatval($entry->amount);
}
$chart->addRow(clone $start, $incomeSum, $expenseSum); $chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth(); $start->addMonth();
@@ -604,11 +521,7 @@ class GoogleChartController extends Controller
*/ */
public function yearInExpSum($year, GChart $chart, ReportQueryInterface $query) public function yearInExpSum($year, GChart $chart, ReportQueryInterface $query)
{ {
try {
$start = new Carbon('01-01-' . $year); $start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$chart->addColumn('Summary', 'string'); $chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number'); $chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number'); $chart->addColumn('Expenses', 'number');
@@ -626,18 +539,9 @@ class GoogleChartController extends Controller
$currentEnd = clone $start; $currentEnd = clone $start;
$currentEnd->endOfMonth(); $currentEnd->endOfMonth();
// total income: // total income:
$incomeResult = $query->incomeByPeriod($start, $currentEnd, $showSharedReports); $incomeSum = floatval($query->incomeByPeriod($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$incomeSum = 0;
foreach ($incomeResult as $entry) {
$incomeSum += floatval($entry->amount);
}
// total expenses: // total expenses:
$expenseResult = $query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports); $expenseSum = floatval($query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$expenseSum = 0;
foreach ($expenseResult as $entry) {
$expenseSum += floatval($entry->amount);
}
$income += $incomeSum; $income += $incomeSum;
$expense += $expenseSum; $expense += $expenseSum;

View File

@@ -2,8 +2,9 @@
use Cache; use Cache;
use ErrorException; use ErrorException;
use FireflyIII\Http\Requests; use FireflyIII\Helpers\Help\HelpInterface;
use League\CommonMark\CommonMarkConverter; use League\CommonMark\CommonMarkConverter;
use Log;
use Response; use Response;
use Route; use Route;
@@ -20,73 +21,34 @@ class HelpController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function show($route) public function show($route, HelpInterface $help)
{ {
$content = [ $content = [
'text' => '<p>There is no help for this route!</p>', 'text' => '<p>There is no help for this route!</p>',
'title' => 'Help', 'title' => 'Help',
]; ];
if (!Route::has($route)) { if (!$help->hasRoute($route)) {
\Log::error('No such route: ' . $route); Log::error('No such route: ' . $route);
return Response::json($content); return Response::json($content);
} }
if ($this->_inCache($route)) { if ($help->inCache($route)) {
$content = [ $content = [
'text' => Cache::get('help.' . $route . '.text'), 'text' => $help->getFromCache('help.' . $route . '.text'),
'title' => Cache::get('help.' . $route . '.title'), 'title' => $help->getFromCache('help.' . $route . '.title'),
]; ];
return Response::json($content); return Response::json($content);
} }
$content = $this->_getFromGithub($route); $content = $help->getFromGithub($route);
$help->putInCache($route, $content);
Cache::put('help.' . $route . '.text', $content['text'], 10080); // a week.
Cache::put('help.' . $route . '.title', $content['title'], 10080);
return Response::json($content); return Response::json($content);
} }
/**
* @param $route
*
* @return bool
*/
protected function _inCache($route)
{
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
}
/**
* @param $route
*
* @return array
*/
protected function _getFromGithub($route)
{
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
$content = [
'text' => '<p>There is no help for this route!</p>',
'title' => $route,
];
try {
$content['text'] = file_get_contents($uri);
} catch (ErrorException $e) {
\Log::error(trim($e->getMessage()));
}
if (strlen(trim($content['text'])) == 0) {
$content['text'] = '<p>There is no help for this route.</p>';
}
$converter = new CommonMarkConverter();
$content['text'] = $converter->convertToHtml($content['text']);
return $content;
}
} }

View File

@@ -1,13 +1,13 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth;
use Cache;
use Carbon\Carbon; use Carbon\Carbon;
use Config;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input; use Input;
use Preferences; use Preferences;
use Redirect; use Redirect;
use Session; use Session;
use Steam;
/** /**
* Class HomeController * Class HomeController
@@ -17,13 +17,6 @@ use Session;
class HomeController extends Controller class HomeController extends Controller
{ {
/**
*
*/
public function __construct()
{
}
public function dateRange() public function dateRange()
{ {
$start = new Carbon(Input::get('start')); $start = new Carbon(Input::get('start'));
@@ -44,18 +37,21 @@ class HomeController extends Controller
*/ */
public function flush() public function flush()
{ {
Cache::flush(); Session::clear();
return Redirect::route('index'); return Redirect::route('index');
} }
/** /**
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function index(AccountRepositoryInterface $repository) public function index(AccountRepositoryInterface $repository)
{ {
$count = $repository->countAssetAccounts(); $types = Config::get('firefly.accountTypesByIdentifier.asset');
$count = $repository->countAccounts($types);
$title = 'Firefly'; $title = 'Firefly';
$subTitle = 'What\'s playing?'; $subTitle = 'What\'s playing?';
$mainTitleIcon = 'fa-fire'; $mainTitleIcon = 'fa-fire';
@@ -64,6 +60,24 @@ class HomeController extends Controller
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage); $accounts = $repository->getFrontpageAccounts($frontPage);
$savings = $repository->getSavingsAccounts();
$piggyBankAccounts = $repository->getPiggyBankAccounts();
$savingsTotal = 0;
foreach ($savings as $savingAccount) {
$savingsTotal += Steam::balance($savingAccount, $end);
}
// check if all books are correct.
$sum = $repository->sumOfEverything();
if ($sum != 0) {
Session::flash(
'error', 'Your transactions are unbalanced. This means a'
. ' withdrawal, deposit or transfer was not stored properly. '
. 'Please check your accounts and transactions for errors.'
);
}
foreach ($accounts as $account) { foreach ($accounts as $account) {
$set = $repository->getFrontpageTransactions($account, $start, $end); $set = $repository->getFrontpageTransactions($account, $start, $end);
@@ -72,9 +86,7 @@ class HomeController extends Controller
} }
} }
// var_dump($transactions); return view('index', compact('count', 'title', 'savings', 'subTitle', 'mainTitleIcon', 'transactions', 'savingsTotal','piggyBankAccounts'));
return view('index', compact('count', 'title', 'subTitle', 'mainTitleIcon', 'transactions'));
} }

View File

@@ -2,14 +2,21 @@
use Amount; use Amount;
use Auth; use Auth;
use DB; use Carbon\Carbon;
use FireflyIII\Http\Requests; use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Support\Collection;
use Preferences; use Preferences;
use Response; use Response;
use Session; use Session;
use Steam;
/** /**
* Class JsonController * Class JsonController
@@ -21,91 +28,122 @@ class JsonController extends Controller
/** /**
* @param BillRepositoryInterface $repository
* *
* @return \Symfony\Component\HttpFoundation\Response
*/ */
public function box(BillRepositoryInterface $repository) public function boxBillsPaid(BillRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$amount = 0;
// these two functions are the same as the chart TODO
$bills = $repository->getActiveBills();
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$amount += $repository->getJournalsInRange($bill, $range['start'], $range['end'])->sum('amount');
}
}
unset($ranges, $bill, $range, $bills);
/**
* Find credit card accounts and possibly unpaid credit card bills.
*/
$creditCards = $accountRepository->getCreditCards();
// if the balance is not zero, the monthly payment is still underway.
/** @var Account $creditCard */
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
if ($balance == 0) {
// find a transfer TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$amount += $accountRepository->getTransfersInRange($creditCard, $start, $end)->sum('amount');
}
}
return Response::json(['box' => 'bills-paid', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
}
/**
* @param BillRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function boxBillsUnpaid(BillRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
{ {
$amount = 0; $amount = 0;
$start = Session::get('start'); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end'); $end = Session::get('end', Carbon::now()->endOfMonth());
$box = 'empty'; $bills = $repository->getActiveBills();
switch (Input::get('box')) { $unpaid = new Collection; // bills
case 'in':
$box = Input::get('box');
$in = Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->before($end)
->after($start)
->transactionTypes(['Deposit'])
->where('transactions.amount', '>', 0)
->first([DB::Raw('SUM(transactions.amount) as `amount`')]);
if (!is_null($in)) {
$amount = floatval($in->amount);
}
break;
case 'out':
$box = Input::get('box');
$in = Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->before($end)
->after($start)
->transactionTypes(['Withdrawal'])
->where('transactions.amount', '>', 0)
->first([DB::Raw('SUM(transactions.amount) as `amount`')]);
if (!is_null($in)) {
$amount = floatval($in->amount);
}
break;
case 'bills-unpaid':
$box = 'bills-unpaid';
$bills = Auth::user()->bills()->where('active', 1)->get();
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($bills as $bill) { foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end); $ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) { foreach ($ranges as $range) {
// paid a bill in this range? $journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
$count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count(); if ($journals->count() == 0) {
if ($count == 0) { $unpaid->push([$bill, $range['start']]);
$amount += floatval($bill->amount_max + $bill->amount_min / 2); }
}
}
unset($bill, $bills, $range, $ranges);
$creditCards = $accountRepository->getCreditCards();
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$fakeAmount = $balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $fakeAmount);
$unpaid->push([$fakeBill, $date]);
}
}
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$current = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$amount += $current;
} }
} return Response::json(['box' => 'bills-unpaid', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
}
break;
case 'bills-paid':
$box = 'bills-paid';
// these two functions are the same as the chart TODO
$bills = Auth::user()->bills()->where('active', 1)->get();
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$count = $bill->transactionjournals()->before($range['end'])->after($range['start'])->count();
if ($count != 0) {
$journal = $bill->transactionjournals()->with('transactions')->before($range['end'])->after($range['start'])->first();
$paid['items'][] = $journal->description;
$currentAmount = 0;
foreach ($journal->transactions as $t) {
if (floatval($t->amount) > 0) {
$currentAmount = floatval($t->amount);
}
}
$amount += $currentAmount;
} }
} /**
} * @param ReportQueryInterface $reportQuery
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function boxIn(ReportQueryInterface $reportQuery)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$amount = $reportQuery->incomeByPeriod($start, $end, true)->sum('queryAmount');
return Response::json(['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
} }
return Response::json(['box' => $box, 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]); /**
* @param ReportQueryInterface $reportQuery
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function boxOut(ReportQueryInterface $reportQuery)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$amount = $reportQuery->journalsByExpenseAccount($start, $end, true)->sum('queryAmount');
return Response::json(['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
} }
/** /**
@@ -113,13 +151,14 @@ class JsonController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function categories() public function categories(CategoryRepositoryInterface $repository)
{ {
$list = Auth::user()->categories()->orderBy('name', 'ASC')->get(); $list = $repository->getCategories();
$return = []; $return = [];
foreach ($list as $entry) { foreach ($list as $entry) {
$return[] = $entry->name; $return[] = $entry->name;
} }
sort($return);
return Response::json($return); return Response::json($return);
} }
@@ -129,9 +168,9 @@ class JsonController extends Controller
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function expenseAccounts() public function expenseAccounts(AccountRepositoryInterface $accountRepository)
{ {
$list = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Expense account', 'Beneficiary account'])->get(); $list = $accountRepository->getAccounts(['Expense account', 'Beneficiary account']);
$return = []; $return = [];
foreach ($list as $entry) { foreach ($list as $entry) {
$return[] = $entry->name; $return[] = $entry->name;
@@ -144,9 +183,9 @@ class JsonController extends Controller
/** /**
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function revenueAccounts() public function revenueAccounts(AccountRepositoryInterface $accountRepository)
{ {
$list = Auth::user()->accounts()->accountTypeIn(['Revenue account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']); $list = $accountRepository->getAccounts(['Revenue account']);
$return = []; $return = [];
foreach ($list as $entry) { foreach ($list as $entry) {
$return[] = $entry->name; $return[] = $entry->name;
@@ -156,16 +195,6 @@ class JsonController extends Controller
} }
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showSharedReports()
{
$pref = Preferences::get('showSharedReports', false);
return Response::json(['value' => $pref->data]);
}
/** /**
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
@@ -179,4 +208,37 @@ class JsonController extends Controller
return Response::json(['value' => $new]); return Response::json(['value' => $new]);
} }
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showSharedReports()
{
$pref = Preferences::get('showSharedReports', false);
return Response::json(['value' => $pref->data]);
}
/**
* @param $what
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function transactionJournals($what, JournalRepositoryInterface $repository)
{
$descriptions = [];
$dbType = $repository->getTransactionType($what);
$journals = $repository->getJournalsOfType($dbType);
foreach ($journals as $j) {
$descriptions[] = $j->description;
}
$descriptions = array_unique($descriptions);
sort($descriptions);
return Response::json($descriptions);
}
} }

View File

@@ -7,7 +7,6 @@ use Config;
use ExpandedForm; use ExpandedForm;
use FireflyIII\Http\Requests; use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\PiggyBankFormRequest; use FireflyIII\Http\Requests\PiggyBankFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -17,6 +16,7 @@ use Input;
use Redirect; use Redirect;
use Session; use Session;
use Steam; use Steam;
use URL;
use View; use View;
/** /**
@@ -50,9 +50,6 @@ class PiggyBankController extends Controller
$leftToSave = $piggyBank->targetamount - $savedSoFar; $leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = min($leftOnAccount, $leftToSave); $maxAmount = min($leftOnAccount, $leftToSave);
\Log::debug('Now going to view for piggy bank #' . $piggyBank->id . ' (' . $piggyBank->name . ')');
return view('piggy-banks.add', compact('piggyBank', 'maxAmount')); return view('piggy-banks.add', compact('piggyBank', 'maxAmount'));
} }
@@ -69,6 +66,12 @@ class PiggyBankController extends Controller
$subTitle = 'Create new piggy bank'; $subTitle = 'Create new piggy bank';
$subTitleIcon = 'fa-plus'; $subTitleIcon = 'fa-plus';
// put previous url in session if not redirect from store (not "create another").
if (Session::get('piggy-banks.create.fromStore') !== true) {
Session::put('piggy-banks.create.url', URL::previous());
}
Session::forget('piggy-banks.create.fromStore');
return view('piggy-banks.create', compact('accounts', 'periods', 'subTitle', 'subTitleIcon')); return view('piggy-banks.create', compact('accounts', 'periods', 'subTitle', 'subTitleIcon'));
} }
@@ -81,6 +84,9 @@ class PiggyBankController extends Controller
{ {
$subTitle = 'Delete "' . e($piggyBank->name) . '"'; $subTitle = 'Delete "' . e($piggyBank->name) . '"';
// put previous url in session
Session::put('piggy-banks.delete.url', URL::previous());
return view('piggy-banks.delete', compact('piggyBank', 'subTitle')); return view('piggy-banks.delete', compact('piggyBank', 'subTitle'));
} }
@@ -95,7 +101,7 @@ class PiggyBankController extends Controller
Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.'); Session::flash('success', 'Piggy bank "' . e($piggyBank->name) . '" deleted.');
$piggyBank->delete(); $piggyBank->delete();
return Redirect::route('piggy-banks.index'); return Redirect::to(Session::get('piggy-banks.delete.url'));
} }
/** /**
@@ -133,17 +139,22 @@ class PiggyBankController extends Controller
]; ];
Session::flash('preFilled', $preFilled); Session::flash('preFilled', $preFilled);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('piggy-banks.edit.fromUpdate') !== true) {
Session::put('piggy-banks.edit.url', URL::previous());
}
Session::forget('piggy-banks.edit.fromUpdate');
return view('piggy-banks.edit', compact('subTitle', 'subTitleIcon', 'piggyBank', 'accounts', 'periods', 'preFilled')); return view('piggy-banks.edit', compact('subTitle', 'subTitleIcon', 'piggyBank', 'accounts', 'periods', 'preFilled'));
} }
/** /**
* @return $this * @return $this
*/ */
public function index(AccountRepositoryInterface $repository) public function index(AccountRepositoryInterface $repository)
{ {
/** @var Collection $piggyBanks */ /** @var Collection $piggyBanks */
$piggyBanks = Auth::user()->piggyBanks()->where('repeats', 0)->get(); $piggyBanks = Auth::user()->piggyBanks()->orderBy('order', 'ASC')->get();
$accounts = []; $accounts = [];
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
@@ -159,7 +170,7 @@ class PiggyBankController extends Controller
if (!isset($accounts[$account->id])) { if (!isset($accounts[$account->id])) {
$accounts[$account->id] = [ $accounts[$account->id] = [
'name' => $account->name, 'name' => $account->name,
'balance' => Steam::balance($account), 'balance' => Steam::balance($account, null, true),
'leftForPiggyBanks' => $repository->leftOnAccount($account), 'leftForPiggyBanks' => $repository->leftOnAccount($account),
'sumOfSaved' => $piggyBank->savedSoFar, 'sumOfSaved' => $piggyBank->savedSoFar,
'sumOfTargets' => floatval($piggyBank->targetamount), 'sumOfTargets' => floatval($piggyBank->targetamount),
@@ -175,6 +186,22 @@ class PiggyBankController extends Controller
return view('piggy-banks.index', compact('piggyBanks', 'accounts')); return view('piggy-banks.index', compact('piggyBanks', 'accounts'));
} }
/**
* Allow user to order piggy banks.
*/
public function order(PiggyBankRepositoryInterface $repository)
{
$data = Input::get('order');
// set all users piggy banks to zero:
$repository->reset();
if (is_array($data)) {
foreach ($data as $order => $id) {
$repository->setOrder(intval($id), (intval($order) + 1));
}
}
}
/** /**
* POST add money to piggy bank * POST add money to piggy bank
@@ -276,12 +303,14 @@ class PiggyBankController extends Controller
} }
/** /**
* @param PiggyBankFormRequest $request
* @param PiggyBankRepositoryInterface $repository
* *
* @return $this|\Illuminate\Http\RedirectResponse
*/ */
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository) public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{ {
$piggyBankData = [ $piggyBankData = [
'repeats' => false,
'name' => $request->get('name'), 'name' => $request->get('name'),
'startdate' => new Carbon, 'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')), 'account_id' => intval($request->get('account_id')),
@@ -296,11 +325,14 @@ class PiggyBankController extends Controller
Session::flash('success', 'Stored piggy bank "' . e($piggyBank->name) . '".'); Session::flash('success', 'Stored piggy bank "' . e($piggyBank->name) . '".');
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
Session::put('piggy-banks.create.fromStore', true);
return Redirect::route('piggy-banks.create')->withInput(); return Redirect::route('piggy-banks.create')->withInput();
} }
return Redirect::route('piggy-banks.index'); // redirect to previous URL.
return Redirect::to(Session::get('piggy-banks.create.url'));
} }
/** /**
@@ -313,7 +345,6 @@ class PiggyBankController extends Controller
public function update(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request) public function update(PiggyBank $piggyBank, PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request)
{ {
$piggyBankData = [ $piggyBankData = [
'repeats' => false,
'name' => $request->get('name'), 'name' => $request->get('name'),
'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate, 'startdate' => is_null($piggyBank->startdate) ? $piggyBank->created_at : $piggyBank->startdate,
'account_id' => intval($request->get('account_id')), 'account_id' => intval($request->get('account_id')),
@@ -329,11 +360,14 @@ class PiggyBankController extends Controller
Session::flash('success', 'Updated piggy bank "' . e($piggyBank->name) . '".'); Session::flash('success', 'Updated piggy bank "' . e($piggyBank->name) . '".');
if (intval(Input::get('return_to_edit')) === 1) { if (intval(Input::get('return_to_edit')) === 1) {
Session::put('piggy-banks.edit.fromUpdate', true);
return Redirect::route('piggy-banks.edit', $piggyBank->id); return Redirect::route('piggy-banks.edit', $piggyBank->id);
} }
return Redirect::route('piggy-banks.index'); // redirect to previous URL.
return Redirect::to(Session::get('piggy-banks.edit.url'));
} }

View File

@@ -1,7 +1,6 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth; use Auth;
use FireflyIII\Http\Requests;
use Input; use Input;
use Preferences; use Preferences;
use Redirect; use Redirect;

View File

@@ -45,7 +45,7 @@ class ProfileController extends Controller
return Redirect::route('change-password'); return Redirect::route('change-password');
} }
$result = $this->_validatePassword($request->get('current_password'), $request->get('new_password'), $request->get('new_password_confirmation')); $result = $this->validatePassword($request->get('current_password'), $request->get('new_password'), $request->get('new_password_confirmation'));
if (!($result === true)) { if (!($result === true)) {
Session::flash('error', $result); Session::flash('error', $result);
@@ -70,7 +70,7 @@ class ProfileController extends Controller
* *
* @return string|bool * @return string|bool
*/ */
protected function _validatePassword($old, $new1, $new2) protected function validatePassword($old, $new1, $new2)
{ {
if (strlen($new1) == 0 || strlen($new2) == 0) { if (strlen($new1) == 0 || strlen($new2) == 0) {
return 'Do fill in a password!'; return 'Do fill in a password!';

View File

@@ -1,7 +1,6 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth; use Auth;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -41,19 +40,7 @@ class RelatedController extends Controller
$unique = array_unique($ids); $unique = array_unique($ids);
$journals = new Collection; $journals = new Collection;
if (count($unique) > 0) { if (count($unique) > 0) {
$journals = Auth::user()->transactionjournals()->whereIn('id', $unique)->get(); $journals = Auth::user()->transactionjournals()->whereIn('id', $unique)->get();
$journals->each(
function (TransactionJournal $journal) {
/** @var Transaction $t */
foreach ($journal->transactions()->get() as $t) {
if ($t->amount > 0) {
$journal->amount = $t->amount;
}
}
}
);
} }
$parent = $journal; $parent = $journal;

View File

@@ -3,7 +3,6 @@
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Reminders\ReminderHelperInterface; use FireflyIII\Helpers\Reminders\ReminderHelperInterface;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Reminder; use FireflyIII\Models\Reminder;
use Redirect; use Redirect;
use Session; use Session;
@@ -83,7 +82,7 @@ class ReminderController extends Controller
// inactive reminders // inactive reminders
$inactive = $reminders->filter( $inactive = $reminders->filter(
function (Reminder $reminder) use ($today) { function (Reminder $reminder) {
if ($reminder->active === false) { if ($reminder->active === false) {
return $reminder; return $reminder;
} }
@@ -92,7 +91,7 @@ class ReminderController extends Controller
// dismissed reminders // dismissed reminders
$dismissed = $reminders->filter( $dismissed = $reminders->filter(
function (Reminder $reminder) use ($today) { function (Reminder $reminder) {
if ($reminder->notnow === true) { if ($reminder->notnow === true) {
return $reminder; return $reminder;
} }

View File

@@ -1,203 +0,0 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use Config;
use ExpandedForm;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\PiggyBankFormRequest;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Redirect;
use Session;
use View;
/**
* Class RepeatedExpenseController
*
* @package FireflyIII\Http\Controllers
*/
class RepeatedExpenseController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-left');
}
/**
* @return $this
*/
public function create()
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])
);
return view('repeatedExpense.create', compact('accounts', 'periods'))->with('subTitle', 'Create new repeated expense')->with(
'subTitleIcon', 'fa-plus'
);
}
/**
* @param PiggyBank $repeatedExpense
*
* @return $this
*/
public function delete(PiggyBank $repeatedExpense)
{
$subTitle = 'Delete "' . e($repeatedExpense->name) . '"';
return view('repeatedExpense.delete', compact('repeatedExpense', 'subTitle'));
}
/**
* @param PiggyBank $repeatedExpense
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(PiggyBank $repeatedExpense)
{
Session::flash('success', 'Repeated expense "' . e($repeatedExpense->name) . '" deleted.');
$repeatedExpense->delete();
return Redirect::route('repeated.index');
}
/**
* @param PiggyBank $repeatedExpense
*
* @return $this
*/
public function edit(PiggyBank $repeatedExpense)
{
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = ExpandedForm::makeSelectList(
Auth::user()->accounts()->orderBy('accounts.name', 'ASC')->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*'])
);
$subTitle = 'Edit repeated expense "' . e($repeatedExpense->name) . '"';
$subTitleIcon = 'fa-pencil';
/*
* Flash some data to fill the form.
*/
$preFilled = ['name' => $repeatedExpense->name,
'account_id' => $repeatedExpense->account_id,
'targetamount' => $repeatedExpense->targetamount,
'reminder_skip' => $repeatedExpense->reminder_skip,
'rep_every' => $repeatedExpense->rep_every,
'rep_times' => $repeatedExpense->rep_times,
'targetdate' => $repeatedExpense->targetdate->format('Y-m-d'),
'reminder' => $repeatedExpense->reminder,
'remind_me' => intval($repeatedExpense->remind_me) == 1 || !is_null($repeatedExpense->reminder) ? true : false
];
Session::flash('preFilled', $preFilled);
return view('repeatedExpense.edit', compact('subTitle', 'subTitleIcon', 'repeatedExpense', 'accounts', 'periods', 'preFilled'));
}
/**
* @return \Illuminate\View\View
*/
public function index()
{
$subTitle = 'Overview';
$expenses = Auth::user()->piggyBanks()->where('repeats', 1)->get();
$expenses->each(
function (PiggyBank $piggyBank) {
$piggyBank->currentRelevantRep();
}
);
return view('repeatedExpense.index', compact('expenses', 'subTitle'));
}
/**
* @param PiggyBank $repeatedExpense
*
* @return \Illuminate\View\View
*/
public function show(PiggyBank $repeatedExpense, PiggyBankRepositoryInterface $repository)
{
$subTitle = $repeatedExpense->name;
$today = Carbon::now();
$repetitions = $repeatedExpense->piggyBankRepetitions()->get();
$repetitions->each(
function (PiggyBankRepetition $repetition) use ($repository) {
$repetition->bars = $repository->calculateParts($repetition);
}
);
return view('repeatedExpense.show', compact('repetitions', 'repeatedExpense', 'today', 'subTitle'));
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*/
public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{
$piggyBankData = [
'repeats' => true,
'name' => $request->get('name'),
'startdate' => new Carbon,
'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')),
'targetdate' => new Carbon($request->get('targetdate')),
'reminder' => $request->get('reminder'),
'skip' => intval($request->get('skip')),
'rep_every' => intval($request->get('rep_every')),
'rep_times' => intval($request->get('rep_times')),
];
$piggyBank = $repository->store($piggyBankData);
Session::flash('success', 'Stored repeated expense "' . e($piggyBank->name) . '".');
return Redirect::route('repeated.index');
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param PiggyBank $repeatedExpense
*
* @return $this
*/
public function update(PiggyBank $repeatedExpense, PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository)
{
$piggyBankData = [
'repeats' => false,
'name' => $request->get('name'),
'account_id' => intval($request->get('account_id')),
'targetamount' => floatval($request->get('targetamount')),
'targetdate' => strlen($request->get('targetdate')) > 0 ? new Carbon($request->get('targetdate')) : null,
'rep_length' => $request->get('rep_length'),
'rep_every' => intval($request->get('rep_every')),
'rep_times' => intval($request->get('rep_times')),
'remind_me' => intval($request->get('remind_me')) == 1 ? true : false,
'reminder' => $request->get('reminder'),
];
$piggyBank = $repository->update($repeatedExpense, $piggyBankData);
Session::flash('success', 'Updated repeated expense "' . e($piggyBank->name) . '".');
return Redirect::route('repeated.index');
}
}

View File

@@ -1,14 +1,12 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Helpers\Report\ReportQueryInterface; use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Query\JoinClause;
use Preferences; use Preferences;
use Session; use Session;
use Steam; use Steam;
@@ -22,11 +20,20 @@ use View;
class ReportController extends Controller class ReportController extends Controller
{ {
/** @var ReportHelperInterface */
protected $helper;
/** @var ReportQueryInterface */
protected $query;
/** /**
* * @param ReportHelperInterface $helper
* @param ReportQueryInterface $query
*/ */
public function __construct() public function __construct(ReportHelperInterface $helper, ReportQueryInterface $query)
{ {
$this->query = $query;
$this->helper = $helper;
View::share('title', 'Reports'); View::share('title', 'Reports');
View::share('mainTitleIcon', 'fa-line-chart'); View::share('mainTitleIcon', 'fa-line-chart');
@@ -38,7 +45,7 @@ class ReportController extends Controller
* *
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function budget($year = '2014', $month = '1', ReportQueryInterface $query) public function budget($year = '2014', $month = '1')
{ {
try { try {
new Carbon($year . '-' . $month . '-01'); new Carbon($year . '-' . $month . '-01');
@@ -52,7 +59,7 @@ class ReportController extends Controller
$end->endOfMonth(); $end->endOfMonth();
$start->subDay(); $start->subDay();
// shared accounts preference: /** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false); $pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data; $showSharedReports = $pref->data;
@@ -61,55 +68,34 @@ class ReportController extends Controller
$subTitle = 'Budget report for ' . $date->format('F Y'); $subTitle = 'Budget report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar'; $subTitleIcon = 'fa-calendar';
$dayEarly = $dayEarly->subDay(); $dayEarly = $dayEarly->subDay();
$accounts = $query->getAllAccounts($start, $end, $showSharedReports); $accounts = $this->query->getAllAccounts($start, $end, $showSharedReports);
$start->addDay(); $start->addDay();
$accounts->each( $accounts->each(
function (Account $account) use ($start, $end, $query) { function (Account $account) use ($start, $end) {
$budgets = $query->getBudgetSummary($account, $start, $end); $budgets = $this->query->getBudgetSummary($account, $start, $end);
$balancedAmount = $query->balancedTransactionsSum($account, $start, $end); $balancedAmount = $this->query->balancedTransactionsSum($account, $start, $end);
$array = []; $array = [];
$hide = true;
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
$id = intval($budget->id); $id = intval($budget->id);
$data = $budget->toArray(); $data = $budget->toArray();
$array[$id] = $data; $array[$id] = $data;
if (floatval($data['queryAmount']) != 0) {
$hide = false;
} }
}
$account->hide = $hide;
$account->budgetInformation = $array; $account->budgetInformation = $array;
$account->balancedAmount = $balancedAmount; $account->balancedAmount = $balancedAmount;
} }
); );
$start = clone $date;
$start->startOfMonth();
/** /**
* Start getBudgetsForMonth DONE * Start getBudgetsForMonth DONE
*/ */
$set = Auth::user()->budgets()->orderBy('budgets.name', 'ASC') $budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['amount'] = isset($budgets[0]['amount']) ? $budgets[0]['amount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared asset accounts, which are without a budget by default:
// which is only relevant when shared asset accounts are hidden.
if ($showSharedReports === false) {
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
}
/** /**
* End getBudgetsForMonth DONE * End getBudgetsForMonth DONE
@@ -124,11 +110,11 @@ class ReportController extends Controller
* *
* @return View * @return View
*/ */
public function index(ReportHelperInterface $helper) public function index()
{ {
$start = Session::get('first'); $start = Session::get('first');
$months = $helper->listOfMonths($start); $months = $this->helper->listOfMonths($start);
$years = $helper->listOfYears($start); $years = $this->helper->listOfYears($start);
$title = 'Reports'; $title = 'Reports';
$mainTitleIcon = 'fa-line-chart'; $mainTitleIcon = 'fa-line-chart';
@@ -142,7 +128,7 @@ class ReportController extends Controller
* *
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function modalBalancedTransfers(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query) public function modalBalancedTransfers(Account $account, $year = '2014', $month = '1')
{ {
try { try {
@@ -154,7 +140,7 @@ class ReportController extends Controller
$end = clone $start; $end = clone $start;
$end->endOfMonth(); $end->endOfMonth();
$journals = $query->balancedTransactionsList($account, $start, $end); $journals = $this->query->balancedTransactionsList($account, $start, $end);
return view('reports.modal-journal-list', compact('journals')); return view('reports.modal-journal-list', compact('journals'));
@@ -169,7 +155,7 @@ class ReportController extends Controller
* *
* @return View * @return View
*/ */
public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query) public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1')
{ {
try { try {
new Carbon($year . '-' . $month . '-01'); new Carbon($year . '-' . $month . '-01');
@@ -179,7 +165,7 @@ class ReportController extends Controller
$start = new Carbon($year . '-' . $month . '-01'); $start = new Carbon($year . '-' . $month . '-01');
$end = clone $start; $end = clone $start;
$end->endOfMonth(); $end->endOfMonth();
$set = $query->getTransactionsWithoutBudget($account, $start, $end); $set = $this->query->getTransactionsWithoutBudget($account, $start, $end);
$journals = $set->filter( $journals = $set->filter(
function (TransactionJournal $journal) { function (TransactionJournal $journal) {
@@ -200,7 +186,7 @@ class ReportController extends Controller
* *
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function modalNoBudget(Account $account, $year = '2014', $month = '1', ReportQueryInterface $query) public function modalNoBudget(Account $account, $year = '2014', $month = '1')
{ {
try { try {
new Carbon($year . '-' . $month . '-01'); new Carbon($year . '-' . $month . '-01');
@@ -210,7 +196,7 @@ class ReportController extends Controller
$start = new Carbon($year . '-' . $month . '-01'); $start = new Carbon($year . '-' . $month . '-01');
$end = clone $start; $end = clone $start;
$end->endOfMonth(); $end->endOfMonth();
$journals = $query->getTransactionsWithoutBudget($account, $start, $end); $journals = $this->query->getTransactionsWithoutBudget($account, $start, $end);
return view('reports.modal-journal-list', compact('journals')); return view('reports.modal-journal-list', compact('journals'));
@@ -222,7 +208,7 @@ class ReportController extends Controller
* *
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
public function month($year = '2014', $month = '1', ReportQueryInterface $query) public function month($year = '2014', $month = '1')
{ {
try { try {
new Carbon($year . '-' . $month . '-01'); new Carbon($year . '-' . $month . '-01');
@@ -233,7 +219,7 @@ class ReportController extends Controller
$subTitle = 'Report for ' . $date->format('F Y'); $subTitle = 'Report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar'; $subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report. $displaySum = true; // to show sums in report.
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false); $pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data; $showSharedReports = $pref->data;
@@ -252,14 +238,15 @@ class ReportController extends Controller
/** /**
* Start getIncomeForMonth DONE * Start getIncomeForMonth DONE
*/ */
$income = $query->incomeByPeriod($start, $end, $showSharedReports); $income = $this->query->incomeByPeriod($start, $end, $showSharedReports);
/** /**
* End getIncomeForMonth DONE * End getIncomeForMonth DONE
*/ */
/** /**
* Start getExpenseGroupedForMonth DONE * Start getExpenseGroupedForMonth DONE
*/ */
$set = $query->journalsByExpenseAccount($start, $end, $showSharedReports); $set = $this->query->journalsByExpenseAccount($start, $end, $showSharedReports);
$expenses = Steam::makeArray($set); $expenses = Steam::makeArray($set);
$expenses = Steam::sortArray($expenses); $expenses = Steam::sortArray($expenses);
$expenses = Steam::limitArray($expenses, 10); $expenses = Steam::limitArray($expenses, 10);
@@ -269,28 +256,7 @@ class ReportController extends Controller
/** /**
* Start getBudgetsForMonth DONE * Start getBudgetsForMonth DONE
*/ */
$set = Auth::user()->budgets() $budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['amount'] = isset($budgets[0]['amount']) ? $budgets[0]['amount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
if ($showSharedReports === false) {
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
}
/** /**
* End getBudgetsForMonth DONE * End getBudgetsForMonth DONE
@@ -299,18 +265,20 @@ class ReportController extends Controller
* Start getCategoriesForMonth DONE * Start getCategoriesForMonth DONE
*/ */
// all categories. // all categories.
$result = $query->journalsByCategory($start, $end); $result = $this->query->journalsByCategory($start, $end);
$categories = Steam::makeArray($result); $categories = Steam::makeArray($result);
// all transfers // all transfers
if ($showSharedReports === false) { if ($showSharedReports === false) {
$result = $query->sharedExpensesByCategory($start, $end); $result = $this->query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result); $transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers); $merged = Steam::mergeArrays($categories, $transfers);
} else { } else {
$merged = $categories; $merged = $categories;
} }
// sort. // sort.
$sorted = Steam::sortNegativeArray($merged); $sorted = Steam::sortNegativeArray($merged);
@@ -322,7 +290,7 @@ class ReportController extends Controller
/** /**
* Start getAccountsForMonth * Start getAccountsForMonth
*/ */
$list = $query->accountList($showSharedReports); $list = $this->query->accountList($showSharedReports);
$accounts = []; $accounts = [];
/** @var Account $account */ /** @var Account $account */
foreach ($list as $account) { foreach ($list as $account) {
@@ -356,14 +324,14 @@ class ReportController extends Controller
* *
* @return $this * @return $this
*/ */
public function year($year, ReportHelperInterface $helper, ReportQueryInterface $query) public function year($year)
{ {
try { try {
new Carbon('01-01-' . $year); new Carbon('01-01-' . $year);
} catch (Exception $e) { } catch (Exception $e) {
return view('error')->with('message', 'Invalid date.'); return view('error')->with('message', 'Invalid date.');
} }
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false); $pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data; $showSharedReports = $pref->data;
$date = new Carbon('01-01-' . $year); $date = new Carbon('01-01-' . $year);
@@ -373,11 +341,9 @@ class ReportController extends Controller
$subTitle = $year; $subTitle = $year;
$subTitleIcon = 'fa-bar-chart'; $subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart'; $mainTitleIcon = 'fa-line-chart';
$balances = $helper->yearBalanceReport($date, $showSharedReports); $balances = $this->helper->yearBalanceReport($date, $showSharedReports);
$groupedIncomes = $query->journalsByRevenueAccount($date, $end, $showSharedReports); $groupedIncomes = $this->query->journalsByRevenueAccount($date, $end, $showSharedReports);
$groupedExpenses = $query->journalsByExpenseAccount($date, $end, $showSharedReports); $groupedExpenses = $this->query->journalsByExpenseAccount($date, $end, $showSharedReports);
//$groupedExpenses = $helper-> expensesGroupedByAccount($date, $end, 15);
return view( return view(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon') 'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')

View File

@@ -1,6 +1,5 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use FireflyIII\Http\Requests;
use FireflyIII\Support\Search\SearchInterface; use FireflyIII\Support\Search\SearchInterface;
use Input; use Input;

View File

@@ -1,7 +1,6 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth; use Auth;
use Carbon\Carbon;
use ExpandedForm; use ExpandedForm;
use FireflyIII\Events\JournalCreated; use FireflyIII\Events\JournalCreated;
use FireflyIII\Events\JournalSaved; use FireflyIII\Events\JournalSaved;
@@ -13,10 +12,11 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Input; use Input;
use Redirect; use Redirect;
use Response;
use Session; use Session;
use URL;
use View; use View;
/** /**
* Class TransactionController * Class TransactionController
* *
@@ -61,6 +61,12 @@ class TransactionController extends Controller
} }
Session::put('preFilled', $preFilled); Session::put('preFilled', $preFilled);
// put previous url in session if not redirect from store (not "create another").
if (Session::get('transactions.create.fromStore') !== true) {
Session::put('transactions.create.url', URL::previous());
}
Session::forget('transactions.create.fromStore');
asort($piggies); asort($piggies);
@@ -79,7 +85,10 @@ class TransactionController extends Controller
$type = strtolower($journal->transactionType->type); $type = strtolower($journal->transactionType->type);
$subTitle = 'Delete ' . e($type) . ' "' . e($journal->description) . '"'; $subTitle = 'Delete ' . e($type) . ' "' . e($journal->description) . '"';
return View::make('transactions.delete', compact('journal', 'subTitle')); // put previous url in session
Session::put('transactions.delete.url', URL::previous());
return view('transactions.delete', compact('journal', 'subTitle'));
} }
@@ -91,23 +100,12 @@ class TransactionController extends Controller
*/ */
public function destroy(TransactionJournal $transactionJournal) public function destroy(TransactionJournal $transactionJournal)
{ {
$type = $transactionJournal->transactionType->type;
$return = 'withdrawal';
Session::flash('success', 'Transaction "' . e($transactionJournal->description) . '" destroyed.'); Session::flash('success', 'Transaction "' . e($transactionJournal->description) . '" destroyed.');
$transactionJournal->delete(); $transactionJournal->delete();
switch ($type) { // redirect to previous URL:
case 'Deposit': return Redirect::to(Session::get('transactions.delete.url'));
$return = 'deposit';
break;
case 'Transfer':
$return = 'transfers';
break;
}
return Redirect::route('transactions.index', $return);
} }
/** /**
@@ -151,19 +149,19 @@ class TransactionController extends Controller
$preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->orderBy('date', 'DESC')->first()->piggy_bank_id; $preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->orderBy('date', 'DESC')->first()->piggy_bank_id;
} }
$preFilled['amount'] = 0; $preFilled['amount'] = $journal->amount;
/** @var Transaction $t */
foreach ($transactions as $t) {
if (floatval($t->amount) > 0) {
$preFilled['amount'] = floatval($t->amount);
}
}
$preFilled['account_id'] = $repository->getAssetAccount($journal); $preFilled['account_id'] = $repository->getAssetAccount($journal);
$preFilled['expense_account'] = $transactions[0]->account->name; $preFilled['expense_account'] = $transactions[0]->account->name;
$preFilled['revenue_account'] = $transactions[1]->account->name; $preFilled['revenue_account'] = $transactions[1]->account->name;
$preFilled['account_from_id'] = $transactions[1]->account->id; $preFilled['account_from_id'] = $transactions[1]->account->id;
$preFilled['account_to_id'] = $transactions[0]->account->id; $preFilled['account_to_id'] = $transactions[0]->account->id;
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('transactions.edit.fromUpdate') !== true) {
Session::put('transactions.edit.url', URL::previous());
}
Session::forget('transactions.edit.fromUpdate');
return View::make('transactions.edit', compact('journal', 'accounts', 'what', 'budgets', 'piggies', 'subTitle'))->with('data', $preFilled); return View::make('transactions.edit', compact('journal', 'accounts', 'what', 'budgets', 'piggies', 'subTitle'))->with('data', $preFilled);
} }
@@ -180,21 +178,18 @@ class TransactionController extends Controller
case 'withdrawal': case 'withdrawal':
$subTitleIcon = 'fa-long-arrow-left'; $subTitleIcon = 'fa-long-arrow-left';
$subTitle = 'Expenses'; $subTitle = 'Expenses';
//$journals = $this->_repository->getWithdrawalsPaginated(50);
$types = ['Withdrawal']; $types = ['Withdrawal'];
break; break;
case 'revenue': case 'revenue':
case 'deposit': case 'deposit':
$subTitleIcon = 'fa-long-arrow-right'; $subTitleIcon = 'fa-long-arrow-right';
$subTitle = 'Revenue, income and deposits'; $subTitle = 'Revenue, income and deposits';
// $journals = $this->_repository->getDepositsPaginated(50);
$types = ['Deposit']; $types = ['Deposit'];
break; break;
case 'transfer': case 'transfer':
case 'transfers': case 'transfers':
$subTitleIcon = 'fa-arrows-h'; $subTitleIcon = 'fa-exchange';
$subTitle = 'Transfers'; $subTitle = 'Transfers';
//$journals = $this->_repository->getTransfersPaginated(50);
$types = ['Transfer']; $types = ['Transfer'];
break; break;
} }
@@ -202,7 +197,11 @@ class TransactionController extends Controller
$page = intval(\Input::get('page')); $page = intval(\Input::get('page'));
$offset = $page > 0 ? ($page - 1) * 50 : 0; $offset = $page > 0 ? ($page - 1) * 50 : 0;
$set = Auth::user()->transactionJournals()->transactionTypes($types)->withRelevantData()->take(50)->offset($offset)->orderBy('date', 'DESC')->get( $set = Auth::user()->transactionJournals()->transactionTypes($types)->withRelevantData()->take(50)->offset($offset)
->orderBy('date', 'DESC')
->orderBy('order', 'ASC')
->orderBy('id', 'DESC')
->get(
['transaction_journals.*'] ['transaction_journals.*']
); );
$count = Auth::user()->transactionJournals()->transactionTypes($types)->count(); $count = Auth::user()->transactionJournals()->transactionTypes($types)->count();
@@ -213,6 +212,28 @@ class TransactionController extends Controller
} }
/**
* Reorder transactions (which all must have the same date)
*/
public function reorder()
{
$ids = Input::get('items');
if (count($ids) > 0) {
$order = 0;
foreach ($ids as $id) {
$journal = Auth::user()->transactionjournals()->where('id', $id)->where('date', Input::get('date'))->first();
if ($journal) {
$journal->order = $order;
$order++;
$journal->save();
}
}
}
return Response::json(true);
}
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* *
@@ -225,9 +246,11 @@ class TransactionController extends Controller
$t->before = floatval( $t->before = floatval(
$t->account->transactions()->leftJoin( $t->account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))->where( )
'transaction_journals.created_at', '<=', $journal->created_at->format('Y-m-d H:i:s') ->where('transaction_journals.date', '<=', $journal->date->format('Y-m-d'))
)->where('transaction_journals.id', '!=', $journal->id)->sum('transactions.amount') ->where('transaction_journals.order', '>=', $journal->order)
->where('transaction_journals.id', '!=', $journal->id)
->sum('transactions.amount')
); );
$t->after = $t->before + $t->amount; $t->after = $t->before + $t->amount;
} }
@@ -239,27 +262,21 @@ class TransactionController extends Controller
); );
} }
/**
* @param JournalFormRequest $request
* @param JournalRepositoryInterface $repository
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository) public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
{ {
$journalData = [
'what' => $request->get('what'),
'description' => $request->get('description'),
'account_id' => intval($request->get('account_id')),
'account_from_id' => intval($request->get('account_from_id')),
'account_to_id' => intval($request->get('account_to_id')),
'expense_account' => $request->get('expense_account'),
'revenue_account' => $request->get('revenue_account'),
'amount' => floatval($request->get('amount')),
'user' => Auth::user()->id,
'amount_currency_id' => intval($request->get('amount_currency_id')),
'date' => new Carbon($request->get('date')),
'budget_id' => intval($request->get('budget_id')),
'category' => $request->get('category'),
];
$journalData = $request->getJournalData();
$journal = $repository->store($journalData); $journal = $repository->store($journalData);
// rescan journal, UpdateJournalConnection
event(new JournalSaved($journal)); event(new JournalSaved($journal));
// ConnectJournalToPiggyBank
event(new JournalCreated($journal, intval($request->get('piggy_bank_id')))); event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
if (intval($request->get('reminder_id')) > 0) { if (intval($request->get('reminder_id')) > 0) {
@@ -271,10 +288,14 @@ class TransactionController extends Controller
Session::flash('success', 'New transaction "' . $journal->description . '" stored!'); Session::flash('success', 'New transaction "' . $journal->description . '" stored!');
if (intval(Input::get('create_another')) === 1) { if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('transactions.create.fromStore', true);
return Redirect::route('transactions.create', $request->input('what'))->withInput(); return Redirect::route('transactions.create', $request->input('what'))->withInput();
} }
return Redirect::route('transactions.index', $request->input('what')); // redirect to previous URL.
return Redirect::to(Session::get('transactions.create.url'));
} }
@@ -284,28 +305,11 @@ class TransactionController extends Controller
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind. * @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
* *
* @return $this * @return $this
* @throws FireflyException
*/ */
public function update(TransactionJournal $journal, JournalFormRequest $request, JournalRepositoryInterface $repository) public function update(TransactionJournal $journal, JournalFormRequest $request, JournalRepositoryInterface $repository)
{ {
$journalData = $request->getJournalData();
$journalData = [
'what' => $request->get('what'),
'description' => $request->get('description'),
'account_id' => intval($request->get('account_id')),
'account_from_id' => intval($request->get('account_from_id')),
'account_to_id' => intval($request->get('account_to_id')),
'expense_account' => $request->get('expense_account'),
'revenue_account' => $request->get('revenue_account'),
'amount' => floatval($request->get('amount')),
'user' => Auth::user()->id,
'amount_currency_id' => intval($request->get('amount_currency_id')),
'date' => new Carbon($request->get('date')),
'budget_id' => intval($request->get('budget_id')),
'category' => $request->get('category'),
];
$repository->update($journal, $journalData); $repository->update($journal, $journalData);
event(new JournalSaved($journal)); event(new JournalSaved($journal));
@@ -314,10 +318,14 @@ class TransactionController extends Controller
Session::flash('success', 'Transaction "' . e($journalData['description']) . '" updated.'); Session::flash('success', 'Transaction "' . e($journalData['description']) . '" updated.');
if (intval(Input::get('return_to_edit')) === 1) { if (intval(Input::get('return_to_edit')) === 1) {
return Redirect::route('transactions.edit', $journal->id); // set value so edit routine will not overwrite URL:
Session::put('transactions.edit.fromUpdate', true);
return Redirect::route('transactions.edit', $journal->id)->withInput(['return_to_edit' => 1]);
} }
return Redirect::route('transactions.index', $journalData['what']); // redirect to previous URL.
return Redirect::to(Session::get('transactions.edit.url'));
} }

View File

@@ -22,6 +22,7 @@ class Kernel extends HttpKernel
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse', 'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession', 'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession', 'Illuminate\View\Middleware\ShareErrorsFromSession',
'FireflyIII\Http\Middleware\ReplaceTestVars',
'FireflyIII\Http\Middleware\VerifyCsrfToken', 'FireflyIII\Http\Middleware\VerifyCsrfToken',
]; ];
@@ -37,6 +38,8 @@ class Kernel extends HttpKernel
'guest' => 'FireflyIII\Http\Middleware\RedirectIfAuthenticated', 'guest' => 'FireflyIII\Http\Middleware\RedirectIfAuthenticated',
'range' => 'FireflyIII\Http\Middleware\Range', 'range' => 'FireflyIII\Http\Middleware\Range',
'reminders' => 'FireflyIII\Http\Middleware\Reminders', 'reminders' => 'FireflyIII\Http\Middleware\Reminders',
'piggybanks' => 'FireflyIII\Http\Middleware\PiggyBanks',
]; ];
} }

View File

@@ -2,6 +2,7 @@
use Closure; use Closure;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
/** /**
* Class Authenticate * Class Authenticate
@@ -37,7 +38,7 @@ class Authenticate
* *
* @return mixed * @return mixed
*/ */
public function handle($request, Closure $next) public function handle(Request $request, Closure $next)
{ {
if ($this->auth->guest()) { if ($this->auth->guest()) {
if ($request->ajax()) { if ($request->ajax()) {

View File

@@ -0,0 +1,72 @@
<?php
namespace FireflyIII\Http\Middleware;
use App;
use Closure;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
/**
* Class PiggyBanks
*
* @package FireflyIII\Http\Middleware
*/
class PiggyBanks
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
*
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if ($this->auth->check() && !$request->isXmlHttpRequest() && App::environment() != 'testing') {
// get piggy banks without a repetition:
/** @var Collection $set */
$set = $this->auth->user()->piggybanks()
->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id')
->whereNull('piggy_bank_repetitions.id')
->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']);
if ($set->count() > 0) {
/** @var PiggyBank $partialPiggy */
foreach ($set as $partialPiggy) {
$repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($partialPiggy);
$repetition->startdate = is_null($partialPiggy->startdate) ? null : $partialPiggy->startdate;
$repetition->targetdate = is_null($partialPiggy->targetdate) ? null : $partialPiggy->targetdate;
$repetition->currentamount = 0;
$repetition->save();
}
}
unset($partialPiggy, $set, $repetition);
}
return $next($request);
}
}

View File

@@ -3,9 +3,11 @@
namespace FireflyIII\Http\Middleware; namespace FireflyIII\Http\Middleware;
use App;
use Carbon\Carbon; use Carbon\Carbon;
use Closure; use Closure;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Navigation; use Navigation;
use Preferences; use Preferences;
use Session; use Session;
@@ -44,16 +46,16 @@ class Range
* *
* @return mixed * @return mixed
*/ */
public function handle($request, Closure $theNext) public function handle(Request $request, Closure $theNext)
{ {
if ($this->auth->check()) { if ($this->auth->check() && App::environment() != 'testing') {
// ignore preference. set the range to be the current month: // ignore preference. set the range to be the current month:
if (!Session::has('start') && !Session::has('end')) { if (!Session::has('start') && !Session::has('end')) {
/** @var \FireflyIII\Models\Preference $viewRange */ /** @var \FireflyIII\Models\Preference $viewRange */
$viewRange = Preferences::get('viewRange', '1M'); $viewRange = Preferences::get('viewRange', '1M');
$start = Session::has('start') ? Session::get('start') : new Carbon; $start = new Carbon;
$start = Navigation::updateStartDate($viewRange->data, $start); $start = Navigation::updateStartDate($viewRange->data, $start);
$end = Navigation::updateEndDate($viewRange->data, $start); $end = Navigation::updateEndDate($viewRange->data, $start);
@@ -61,11 +63,16 @@ class Range
Session::put('end', $end); Session::put('end', $end);
} }
if (!Session::has('first')) { if (!Session::has('first')) {
$journal = $this->auth->user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']); /**
* Get helper thing.
*/
/** @var \FireflyIII\Repositories\Journal\JournalRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Journal\JournalRepositoryInterface');
$journal = $repository->first();
if ($journal) { if ($journal) {
Session::put('first', $journal->date); Session::put('first', $journal->date);
} else { } else {
Session::put('first', Carbon::now()); Session::put('first', Carbon::now()->startOfYear());
} }
} }

View File

@@ -3,6 +3,7 @@
use Closure; use Closure;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
/** /**
* Class RedirectIfAuthenticated * Class RedirectIfAuthenticated
@@ -38,7 +39,7 @@ class RedirectIfAuthenticated
* *
* @return mixed * @return mixed
*/ */
public function handle($request, Closure $next) public function handle(Request $request, Closure $next)
{ {
if ($this->auth->check()) { if ($this->auth->check()) {
return new RedirectResponse(url('/')); return new RedirectResponse(url('/'));

View File

@@ -8,6 +8,7 @@ use Closure;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder; use FireflyIII\Models\Reminder;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use View; use View;
/** /**
@@ -43,9 +44,9 @@ class Reminders
* *
* @return mixed * @return mixed
*/ */
public function handle($request, Closure $next) public function handle(Request $request, Closure $next)
{ {
if ($this->auth->check()) { if ($this->auth->check() && !$request->isXmlHttpRequest() && App::environment() != 'testing') {
// do reminders stuff. // do reminders stuff.
$piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get(); $piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get();
$today = new Carbon; $today = new Carbon;
@@ -67,13 +68,12 @@ class Reminders
} }
} }
// delete invalid reminders // delete invalid reminders
$reminders = $this->auth->user()->reminders()->get(); $set = $this->auth->user()->reminders()->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')->whereNull('piggy_banks.id')->get(
foreach($reminders as $reminder) { ['reminders.id']
if(is_null($reminder->remindersable)) { );
foreach ($set as $reminder) {
$reminder->delete(); $reminder->delete();
} }
}
// get and list active reminders: // get and list active reminders:

View File

@@ -0,0 +1,56 @@
<?php
namespace FireflyIII\Http\Middleware;
use Closure;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\Request;
use Log;
/**
* Class ReplaceTestVars
*
* @package FireflyIII\Http\Middleware
*/
class ReplaceTestVars
{
/**
* The application implementation.
*
* @var Application
*/
protected $app;
/**
* Create a new filter instance.
*
* @param Application $app
*
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if ('testing' === $this->app->environment() && $request->has('_token')) {
$input = $request->all();
$input['_token'] = $request->session()->token();
// we need to update _token value to make sure we get the POST / PUT tests passed.
Log::debug('Input token replaced (' . $input['_token'] . ').');
$request->replace($input);
}
return $next($request);
}
}

View File

@@ -30,18 +30,25 @@ class AccountFormRequest extends Request
{ {
$accountRoles = join(',', array_keys(Config::get('firefly.accountRoles'))); $accountRoles = join(',', array_keys(Config::get('firefly.accountRoles')));
$types = join(',', array_keys(Config::get('firefly.subTitlesByIdentifier'))); $types = join(',', array_keys(Config::get('firefly.subTitlesByIdentifier')));
$ccPaymentTypes = join(',', array_keys(Config::get('firefly.ccTypes')));
$nameRule = 'required|between:1,100|uniqueForUser:accounts,name'; $nameRule = 'required|min:1|uniqueAccountForUser';
$idRule = '';
if (Account::find(Input::get('id'))) { if (Account::find(Input::get('id'))) {
$nameRule = 'required|between:1,100'; $idRule = 'belongsToUser:accounts';
$nameRule = 'required|min:1|uniqueAccountForUser:' . Input::get('id');
} }
return [ return [
'id' => $idRule,
'name' => $nameRule, 'name' => $nameRule,
'openingBalance' => 'numeric', 'openingBalance' => 'numeric',
'virtualBalance' => 'numeric',
'openingBalanceDate' => 'date', 'openingBalanceDate' => 'date',
'accountRole' => 'in:' . $accountRoles, 'accountRole' => 'in:' . $accountRoles,
'active' => 'boolean', 'active' => 'boolean',
'ccType' => 'in:' . $ccPaymentTypes,
'ccMonthlyPaymentDate' => 'date',
'balance_currency_id' => 'exists:transaction_currencies,id', 'balance_currency_id' => 'exists:transaction_currencies,id',
'what' => 'in:' . $types 'what' => 'in:' . $types
]; ];

View File

@@ -3,6 +3,7 @@
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
use Auth; use Auth;
use Carbon\Carbon;
use Input; use Input;
/** /**
@@ -21,19 +22,41 @@ class BillFormRequest extends Request
return Auth::check(); return Auth::check();
} }
/**
* @return array
*/
public function getBillData()
{
return [
'name' => $this->get('name'),
'match' => $this->get('match'),
'amount_min' => floatval($this->get('amount_min')),
'amount_currency_id' => floatval($this->get('amount_currency_id')),
'amount_max' => floatval($this->get('amount_max')),
'date' => new Carbon($this->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $this->get('repeat_freq'),
'skip' => intval($this->get('skip')),
'automatch' => intval($this->get('automatch')) === 1,
'active' => intval($this->get('active')) === 1,
];
}
/** /**
* @return array * @return array
*/ */
public function rules() public function rules()
{ {
$nameRule = 'required|between:1,255|uniqueForUser:bills,name'; $nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name,name_encrypted';
$matchRule = 'required|between:1,255|uniqueObjectForUser:bills,match,match_encrypted';
if (intval(Input::get('id')) > 0) { if (intval(Input::get('id')) > 0) {
$nameRule = 'required|between:1,255'; $nameRule .= ',' . intval(Input::get('id'));
$matchRule .= ',' . intval(Input::get('id'));
} }
$rules = [ $rules = [
'name' => $nameRule, 'name' => $nameRule,
'match' => 'required|between:1,255', 'match' => $matchRule,
'amount_min' => 'required|numeric|min:0.01', 'amount_min' => 'required|numeric|min:0.01',
'amount_max' => 'required|numeric|min:0.01', 'amount_max' => 'required|numeric|min:0.01',
'amount_currency_id' => 'required|exists:transaction_currencies,id', 'amount_currency_id' => 'required|exists:transaction_currencies,id',

View File

@@ -28,13 +28,14 @@ class BudgetFormRequest extends Request
public function rules() public function rules()
{ {
$nameRule = 'required|between:1,100|uniqueForUser:budgets,name'; $nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,encrypted';
if (Budget::find(Input::get('id'))) { if (Budget::find(Input::get('id'))) {
$nameRule = 'required|between:1,100'; $nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,encrypted,' . intval(Input::get('id'));
} }
return [ return [
'name' => $nameRule, 'name' => $nameRule,
'active' => 'numeric|between:0,1'
]; ];
} }
} }

View File

@@ -28,9 +28,9 @@ class CategoryFormRequest extends Request
public function rules() public function rules()
{ {
$nameRule = 'required|between:1,100|uniqueForUser:categories,name'; $nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,encrypted';
if (Category::find(Input::get('id'))) { if (Category::find(Input::get('id'))) {
$nameRule = 'required|between:1,100'; $nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,encrypted,' . intval(Input::get('id'));
} }
return [ return [

View File

@@ -21,6 +21,18 @@ class CurrencyFormRequest extends Request
return Auth::check(); return Auth::check();
} }
/**
* @return array
*/
public function getCurrencyData()
{
return [
'name' => $this->get('name'),
'code' => $this->get('code'),
'symbol' => $this->get('symbol'),
];
}
/** /**
* @return array * @return array
*/ */

View File

@@ -3,7 +3,8 @@
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
use Auth; use Auth;
use FireflyIII\Models\Account; use Carbon\Carbon;
use Exception;
use Input; use Input;
/** /**
@@ -25,6 +26,29 @@ class JournalFormRequest extends Request
/** /**
* @return array * @return array
*/ */
public function getJournalData()
{
return [
'what' => $this->get('what'),
'description' => $this->get('description'),
'account_id' => intval($this->get('account_id')),
'account_from_id' => intval($this->get('account_from_id')),
'account_to_id' => intval($this->get('account_to_id')),
'expense_account' => $this->get('expense_account'),
'revenue_account' => $this->get('revenue_account'),
'amount' => floatval($this->get('amount')),
'user' => Auth::user()->id,
'amount_currency_id' => intval($this->get('amount_currency_id')),
'date' => new Carbon($this->get('date')),
'budget_id' => intval($this->get('budget_id')),
'category' => $this->get('category'),
];
}
/**
* @return array
* @throws Exception
*/
public function rules() public function rules()
{ {
// can we switch on the "what"? // can we switch on the "what"?
@@ -62,7 +86,7 @@ class JournalFormRequest extends Request
$rules['category'] = 'between:1,255'; $rules['category'] = 'between:1,255';
break; break;
default: default:
die('Cannot handle ' . $what); throw new Exception('Cannot handle ' . $what);
break; break;
} }

View File

@@ -3,7 +3,6 @@
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
use Auth; use Auth;
use FireflyIII\Models\Account;
use Input; use Input;
/** /**
@@ -28,27 +27,20 @@ class PiggyBankFormRequest extends Request
public function rules() public function rules()
{ {
$nameRule = 'required|between:1,255|uniqueForUser:piggy_banks,name'; $nameRule = 'required|between:1,255|uniquePiggyBankForUser';
$targetDateRule = 'date'; $targetDateRule = 'date';
if (intval(Input::get('id'))) { if (intval(Input::get('id'))) {
$nameRule = 'required|between:1,255'; $nameRule = 'required|between:1,255|uniquePiggyBankForUser:' . intval(Input::get('id'));
} }
if (intval(Input::get('repeats')) == 1) {
$targetDateRule = 'required|date|after:' . date('Y-m-d');
}
$rules = [ $rules = [
'repeats' => 'required|boolean',
'name' => $nameRule, 'name' => $nameRule,
'account_id' => 'required|belongsToUser:accounts', 'account_id' => 'required|belongsToUser:accounts',
'targetamount' => 'required|min:0.01', 'targetamount' => 'required|min:0.01',
'amount_currency_id' => 'exists:transaction_currencies,id', 'amount_currency_id' => 'exists:transaction_currencies,id',
'startdate' => 'date', 'startdate' => 'date',
'targetdate' => $targetDateRule, 'targetdate' => $targetDateRule,
'rep_length' => 'in:day,week,quarter,month,year',
'rep_every' => 'integer|min:0|max:31',
'rep_times' => 'integer|min:0|max:99',
'reminder' => 'in:day,week,quarter,month,year', 'reminder' => 'in:day,week,quarter,month,year',
'reminder_skip' => 'integer|min:0|max:99', 'reminder_skip' => 'integer|min:0|max:99',
'remind_me' => 'boolean|piggyBankReminder', 'remind_me' => 'boolean|piggyBankReminder',

View File

@@ -3,7 +3,6 @@
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
use Auth; use Auth;
use FireflyIII\Models\Account;
/** /**
* Class ProfileFormRequest * Class ProfileFormRequest

View File

@@ -3,13 +3,13 @@ use Carbon\Carbon;
use DaveJamesMiller\Breadcrumbs\Generator; use DaveJamesMiller\Breadcrumbs\Generator;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\TransactionJournal;
/* /*
* Back home. * Back home.
@@ -256,40 +256,6 @@ Breadcrumbs::register(
} }
); );
// repeated expenses
Breadcrumbs::register(
'repeated.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Repeated expenses', route('repeated.index'));
}
);
Breadcrumbs::register(
'repeated.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('repeated.index');
$breadcrumbs->push('Create new repeated expense', route('repeated.create'));
}
);
Breadcrumbs::register(
'repeated.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('repeated.show', $piggyBank);
$breadcrumbs->push('Edit ' . e($piggyBank->name), route('repeated.edit', $piggyBank->id));
}
);
Breadcrumbs::register(
'repeated.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('repeated.show', $piggyBank);
$breadcrumbs->push('Delete ' . e($piggyBank->name), route('repeated.delete', $piggyBank->id));
}
);
Breadcrumbs::register(
'repeated.show', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('repeated.index');
$breadcrumbs->push(e($piggyBank->name), route('repeated.show', $piggyBank->id));
}
);
// reports // reports
Breadcrumbs::register( Breadcrumbs::register(

View File

@@ -28,20 +28,6 @@ Route::bind(
} }
); );
Route::bind(
'repeatedExpense', function ($value, $route) {
if (Auth::check()) {
return PiggyBank::
where('piggy_banks.id', $value)
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
->where('accounts.user_id', Auth::user()->id)
->where('repeats', 1)->first(['piggy_banks.*']);
}
return null;
}
);
Route::bind( Route::bind(
'tjSecond', function ($value, $route) { 'tjSecond', function ($value, $route) {
if (Auth::check()) { if (Auth::check()) {
@@ -121,7 +107,7 @@ Route::bind(
where('piggy_banks.id', $value) where('piggy_banks.id', $value)
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id') ->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
->where('accounts.user_id', Auth::user()->id) ->where('accounts.user_id', Auth::user()->id)
->where('repeats', 0)->first(['piggy_banks.*']); ->first(['piggy_banks.*']);
} }
return null; return null;
@@ -156,7 +142,7 @@ Route::controllers(
* Home Controller * Home Controller
*/ */
Route::group( Route::group(
['middleware' => ['auth', 'range', 'reminders']], function () { ['middleware' => ['auth', 'range', 'reminders', 'piggybanks']], function () {
Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']); Route::get('/', ['uses' => 'HomeController@index', 'as' => 'index']);
Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']); Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']);
Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']); Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']);
@@ -181,9 +167,9 @@ Route::group(
Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching. Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching.
Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']); Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']);
Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']); Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']);
Route::get('/bills/add/{bill}', ['uses' => 'BillController@add', 'as' => 'bills.add']);
Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']); Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']);
Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']); Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']);
Route::post('/bills/store', ['uses' => 'BillController@store', 'as' => 'bills.store']); Route::post('/bills/store', ['uses' => 'BillController@store', 'as' => 'bills.store']);
Route::post('/bills/update/{bill}', ['uses' => 'BillController@update', 'as' => 'bills.update']); Route::post('/bills/update/{bill}', ['uses' => 'BillController@update', 'as' => 'bills.update']);
Route::post('/bills/destroy/{bill}', ['uses' => 'BillController@destroy', 'as' => 'bills.destroy']); Route::post('/bills/destroy/{bill}', ['uses' => 'BillController@destroy', 'as' => 'bills.destroy']);
@@ -239,7 +225,7 @@ Route::group(
Route::get('/chart/home/bills', ['uses' => 'GoogleChartController@billsOverview']); Route::get('/chart/home/bills', ['uses' => 'GoogleChartController@billsOverview']);
Route::get('/chart/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']); Route::get('/chart/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']);
Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']); Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']);
Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending']); Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending'])->where(['year' => '[0-9]+']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']); Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']);
Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']); Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']);
Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']); Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
@@ -259,8 +245,12 @@ Route::group(
Route::get('/json/expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'json.expense-accounts']); Route::get('/json/expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'json.expense-accounts']);
Route::get('/json/revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'json.revenue-accounts']); Route::get('/json/revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'json.revenue-accounts']);
Route::get('/json/categories', ['uses' => 'JsonController@categories', 'as' => 'json.categories']); Route::get('/json/categories', ['uses' => 'JsonController@categories', 'as' => 'json.categories']);
Route::get('/json/box', ['uses' => 'JsonController@box', 'as' => 'json.box']); Route::get('/json/box/in', ['uses' => 'JsonController@boxIn', 'as' => 'json.box.in']);
Route::get('/json/box/out', ['uses' => 'JsonController@boxOut', 'as' => 'json.box.out']);
Route::get('/json/box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'json.box.paid']);
Route::get('/json/box/bills-paid', ['uses' => 'JsonController@boxBillsPaid', 'as' => 'json.box.unpaid']);
Route::get('/json/show-shared-reports', 'JsonController@showSharedReports'); Route::get('/json/show-shared-reports', 'JsonController@showSharedReports');
Route::get('/json/transaction-journals/{what}', 'JsonController@transactionJournals');
Route::get('/json/show-shared-reports/set', 'JsonController@setSharedReports'); Route::get('/json/show-shared-reports/set', 'JsonController@setSharedReports');
@@ -279,6 +269,7 @@ Route::group(
Route::post('/piggy-banks/destroy/{piggyBank}', ['uses' => 'PiggyBankController@destroy', 'as' => 'piggy-banks.destroy']); Route::post('/piggy-banks/destroy/{piggyBank}', ['uses' => 'PiggyBankController@destroy', 'as' => 'piggy-banks.destroy']);
Route::post('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@postAdd', 'as' => 'piggy-banks.add']); # add money Route::post('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@postAdd', 'as' => 'piggy-banks.add']); # add money
Route::post('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@postRemove', 'as' => 'piggy-banks.remove']); # remove money. Route::post('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@postRemove', 'as' => 'piggy-banks.remove']); # remove money.
Route::post('/piggy-banks/sort', ['uses' => 'PiggyBankController@order', 'as' => 'piggy-banks.order']);
/** /**
* Preferences Controller * Preferences Controller
@@ -311,19 +302,6 @@ Route::group(
Route::get('/reminder/act/{reminder}', ['uses' => 'ReminderController@act', 'as' => 'reminders.act']); Route::get('/reminder/act/{reminder}', ['uses' => 'ReminderController@act', 'as' => 'reminders.act']);
Route::get('/reminder/{reminder}', ['uses' => 'ReminderController@show', 'as' => 'reminders.show']); Route::get('/reminder/{reminder}', ['uses' => 'ReminderController@show', 'as' => 'reminders.show']);
/**
* Repeated Expenses Controller
*/
Route::get('/repeated-expenses', ['uses' => 'RepeatedExpenseController@index', 'as' => 'repeated.index']);
Route::get('/repeated-expenses/create', ['uses' => 'RepeatedExpenseController@create', 'as' => 'repeated.create']);
Route::get('/repeated-expenses/edit/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@edit', 'as' => 'repeated.edit']);
Route::get('/repeated-expenses/delete/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@delete', 'as' => 'repeated.delete']);
Route::get('/repeated-expenses/show/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@show', 'as' => 'repeated.show']);
Route::post('/repeated-expense/store', ['uses' => 'RepeatedExpenseController@store', 'as' => 'repeated.store']);
Route::post('/repeated-expense/update/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@update', 'as' => 'repeated.update']);
Route::post('/repeated-expense/destroy/{repeatedExpense}', ['uses' => 'RepeatedExpenseController@destroy', 'as' => 'repeated.destroy']);
/** /**
* Report Controller * Report Controller
*/ */
@@ -365,6 +343,7 @@ Route::group(
); );
Route::post('/transaction/update/{tj}', ['uses' => 'TransactionController@update', 'as' => 'transactions.update']); Route::post('/transaction/update/{tj}', ['uses' => 'TransactionController@update', 'as' => 'transactions.update']);
Route::post('/transaction/destroy/{tj}', ['uses' => 'TransactionController@destroy', 'as' => 'transactions.destroy']); Route::post('/transaction/destroy/{tj}', ['uses' => 'TransactionController@destroy', 'as' => 'transactions.destroy']);
Route::post('/transaction/reorder', ['uses' => 'TransactionController@reorder', 'as' => 'transactions.reorder']);
/** /**
* Auth\Auth Controller * Auth\Auth Controller

View File

@@ -1,10 +1,13 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use App;
use Crypt;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Query\JoinClause;
use Watson\Validating\ValidatingTrait; use Watson\Validating\ValidatingTrait;
use Crypt;
/** /**
* Class Account * Class Account
* *
@@ -14,47 +17,47 @@ class Account extends Model
{ {
use SoftDeletes, ValidatingTrait; use SoftDeletes, ValidatingTrait;
protected $fillable = ['user_id', 'account_type_id', 'name', 'active', 'virtual_balance'];
protected $rules protected $rules
= [ = [
'user_id' => 'required|exists:users,id', 'user_id' => 'required|exists:users,id',
'account_type_id' => 'required|exists:account_types,id', 'account_type_id' => 'required|exists:account_types,id',
'name' => 'required|between:1,1024|uniqueForUser:accounts,name', 'name' => 'required|between:1,1024|uniqueAccountForUser',
'active' => 'required|boolean' 'active' => 'required|boolean'
]; ];
protected $fillable = ['user_id', 'account_type_id', 'name', 'active'];
/** /**
* @param $fieldName * @param array $fields
* *
* @return string|null * @return Account|null
*/ */
public function getMeta($fieldName) public static function firstOrCreateEncrypted(array $fields)
{ {
foreach ($this->accountMeta as $meta) { // everything but the name:
if ($meta->name == $fieldName) { $query = Account::orderBy('id');
return $meta->data; foreach ($fields as $name => $value) {
if ($name != 'name') {
$query->where($name, $value);
} }
} }
$set = $query->get(['accounts.*']);
/** @var Account $account */
foreach ($set as $account) {
if ($account->name == $fields['name']) {
return $account;
}
}
// create it!
$account = Account::create($fields);
if (is_null($account->id)) {
// could not create account:
App::abort(500, 'Could not create new account with data: ' . json_encode($fields).' because ' . json_encode($account->getErrors()));
return null;
} }
/** return $account;
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if ($this->encrypted) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
} }
/** /**
@@ -81,6 +84,48 @@ class Account extends Model
return ['created_at', 'updated_at', 'deleted_at']; return ['created_at', 'updated_at', 'deleted_at'];
} }
/**
* @param $fieldName
*
* @return string|null
*/
public function getMeta($fieldName)
{
foreach ($this->accountMeta as $meta) {
if ($meta->name == $fieldName) {
return $meta->data;
}
}
return null;
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBanks()
{
return $this->hasMany('FireflyIII\Models\PiggyBank');
}
/** /**
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param array $types * @param array $types
@@ -94,6 +139,31 @@ class Account extends Model
$query->whereIn('account_types.type', $types); $query->whereIn('account_types.type', $types);
} }
/**
* @param EloquentBuilder $query
* @param string $name
* @param string $value
*/
public function scopeHasMetaValue(EloquentBuilder $query, $name, $value)
{
$joinName = str_replace('.', '_', $name);
$query->leftJoin(
'account_meta as ' . $joinName, function (JoinClause $join) use ($joinName, $name) {
$join->on($joinName . '.account_id', '=', 'accounts.id')->where($joinName . '.name', '=', $name);
}
);
$query->where($joinName . '.data', json_encode($value));
}
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */
@@ -110,12 +180,4 @@ class Account extends Model
return $this->belongsTo('FireflyIII\User'); return $this->belongsTo('FireflyIII\User');
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function piggyBanks()
{
return $this->hasMany('FireflyIII\Models\PiggyBank');
}
} }

View File

@@ -1,5 +1,6 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/** /**
@@ -10,7 +11,8 @@ use Illuminate\Database\Eloquent\Model;
class Bill extends Model class Bill extends Model
{ {
protected $fillable = ['name', 'match', 'amount_min','user_id', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active',]; protected $fillable
= ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active',];
/** /**
* @return array * @return array
@@ -20,6 +22,58 @@ class Bill extends Model
return ['created_at', 'updated_at', 'date']; return ['created_at', 'updated_at', 'date'];
} }
/**
* @param $value
*
* @return string
*/
public function getMatchAttribute($value)
{
if (intval($this->match_encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->name_encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/**
* @param $value
*/
public function setMatchAttribute($value)
{
$this->attributes['match'] = Crypt::encrypt($value);
$this->attributes['match_encrypted'] = true;
}
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['name_encrypted'] = true;
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */

View File

@@ -1,5 +1,6 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
@@ -31,6 +32,23 @@ class Budget extends Model
return ['created_at', 'updated_at', 'deleted_at']; return ['created_at', 'updated_at', 'deleted_at'];
} }
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/ */
@@ -39,6 +57,15 @@ class Budget extends Model
return $this->hasManyThrough('FireflyIII\Models\LimitRepetition', 'FireflyIII\Models\BudgetLimit', 'budget_id'); return $this->hasManyThrough('FireflyIII\Models\LimitRepetition', 'FireflyIII\Models\BudgetLimit', 'budget_id');
} }
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/ */

View File

@@ -1,8 +1,9 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Crypt;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use App;
/** /**
* Class Category * Class Category
* *
@@ -30,6 +31,39 @@ class Category extends Model
return $this->belongsToMany('FireflyIII\Models\TransactionJournal', 'category_transaction_journal', 'category_id'); return $this->belongsToMany('FireflyIII\Models\TransactionJournal', 'category_transaction_journal', 'category_id');
} }
/**
* @param array $fields
*
* @return Account|null
*/
public static function firstOrCreateEncrypted(array $fields)
{
// everything but the name:
$query = Category::orderBy('id');
foreach ($fields as $name => $value) {
if ($name != 'name') {
$query->where($name, $value);
}
}
$set = $query->get(['categories.*']);
/** @var Category $category */
foreach ($set as $category) {
if ($category->name == $fields['name']) {
return $category;
}
}
// create it!
$category = Category::create($fields);
if (is_null($category->id)) {
// could not create account:
App::abort(500, 'Could not create new category with data: ' . json_encode($fields));
}
return $category;
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/ */
@@ -38,4 +72,30 @@ class Category extends Model
return $this->belongsTo('FireflyIII\User'); return $this->belongsTo('FireflyIII\User');
} }
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
} }

View File

@@ -12,6 +12,8 @@ class Component extends Model
{ {
use SoftDeletes; use SoftDeletes;
protected $fillable = ['user_id', 'name', 'class'];
/** /**
* @return array * @return array
*/ */

View File

@@ -1,5 +1,7 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Auth;
use DB;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/** /**
@@ -26,15 +28,20 @@ class LimitRepetition extends Model
return ['created_at', 'updated_at', 'startdate', 'enddate']; return ['created_at', 'updated_at', 'startdate', 'enddate'];
} }
/**
* @return float
*/
public function spentInRepetition() public function spentInRepetition()
{ {
$sum = \DB::table('transactions') $sum = DB::table('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budget_transaction_journal.budget_id') ->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') ->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('transaction_journals.date', '>=', $this->startdate->format('Y-m-d')) ->where('transaction_journals.date', '>=', $this->startdate->format('Y-m-d'))
->where('transaction_journals.date', '<=', $this->enddate->format('Y-m-d')) ->where('transaction_journals.date', '<=', $this->enddate->format('Y-m-d'))
->where('transaction_journals.user_id', Auth::user()->id)
->whereNull('transactions.deleted_at')
->where('transactions.amount', '>', 0) ->where('transactions.amount', '>', 0)
->where('limit_repetitions.id', '=', $this->id) ->where('limit_repetitions.id', '=', $this->id)
->sum('transactions.amount'); ->sum('transactions.amount');

View File

@@ -1,7 +1,6 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Carbon\Carbon; use Crypt;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
@@ -14,7 +13,8 @@ class PiggyBank extends Model
{ {
use SoftDeletes; use SoftDeletes;
protected $fillable = ['repeats', 'name', 'account_id','rep_every', 'rep_times', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder','remind_me']; protected $fillable
= ['name', 'account_id', 'reminder_skip', 'targetamount', 'startdate', 'targetdate', 'reminder', 'remind_me'];
/** /**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
@@ -24,15 +24,6 @@ class PiggyBank extends Model
return $this->belongsTo('FireflyIII\Models\Account'); return $this->belongsTo('FireflyIII\Models\Account');
} }
/**
* @param $value
*
* @return int
*/
public function getRemindMeAttribute($value) {
return intval($value) == 1;
}
/** /**
* Grabs the PiggyBankRepetition that's currently relevant / active * Grabs the PiggyBankRepetition that's currently relevant / active
* *
@@ -40,50 +31,14 @@ class PiggyBank extends Model
*/ */
public function currentRelevantRep() public function currentRelevantRep()
{ {
if ($this->currentRep) { if (!is_null($this->currentRep)) {
return $this->currentRep; return $this->currentRep;
} }
if ($this->repeats == 0) { // repeating piggy banks are no longer supported.
$rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']); $rep = $this->piggyBankRepetitions()->first(['piggy_bank_repetitions.*']);
$this->currentRep = $rep; $this->currentRep = $rep;
return $rep; return $rep;
} else {
$query = $this->piggyBankRepetitions()->where(
function (EloquentBuilder $q) {
$q->where(
function (EloquentBuilder $q) {
$q->where(
function (EloquentBuilder $q) {
$today = new Carbon;
$q->whereNull('startdate');
$q->orWhere('startdate', '<=', $today->format('Y-m-d 00:00:00'));
}
)->where(
function (EloquentBuilder $q) {
$today = new Carbon;
$q->whereNull('targetdate');
$q->orWhere('targetdate', '>=', $today->format('Y-m-d 00:00:00'));
}
);
}
)->orWhere(
function (EloquentBuilder $q) {
$today = new Carbon;
$q->where('startdate', '>=', $today->format('Y-m-d 00:00:00'));
$q->where('targetdate', '>=', $today->format('Y-m-d 00:00:00'));
}
);
}
)->orderBy('startdate', 'ASC');
$result = $query->first(['piggy_bank_repetitions.*']);
$this->currentRep = $result;
return $result;
}
} }
@@ -104,6 +59,16 @@ class PiggyBank extends Model
return ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate']; return ['created_at', 'updated_at', 'deleted_at', 'startdate', 'targetdate'];
} }
/**
* @param $value
*
* @return int
*/
public function getRemindMeAttribute($value)
{
return intval($value) == 1;
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */
@@ -119,4 +84,30 @@ class PiggyBank extends Model
{ {
return $this->morphMany('FireflyIII\Models\Reminder', 'remindersable'); return $this->morphMany('FireflyIII\Models\Reminder', 'remindersable');
} }
/**
* @param $value
*/
public function setNameAttribute($value)
{
$this->attributes['name'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @param $value
*
* @return string
*/
public function getNameAttribute($value)
{
if (intval($this->encrypted) == 1) {
return Crypt::decrypt($value);
}
// @codeCoverageIgnoreStart
return $value;
// @codeCoverageIgnoreEnd
}
} }

View File

@@ -1,8 +1,9 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
/** /**
* Class PiggyBankRepetition * Class PiggyBankRepetition
* *
@@ -27,6 +28,18 @@ class PiggyBankRepetition extends Model
return $this->belongsTo('FireflyIII\Models\PiggyBank'); return $this->belongsTo('FireflyIII\Models\PiggyBank');
} }
/**
* @param EloquentBuilder $query
* @param Carbon $start
* @param Carbon $target
*
* @return $this
*/
public function scopeOnDates(EloquentBuilder $query, Carbon $start, Carbon $target)
{
return $query->where('startdate', $start->format('Y-m-d'))->where('targetdate', $target->format('Y-m-d'));
}
/** /**
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Carbon $date * @param Carbon $date
@@ -36,16 +49,18 @@ class PiggyBankRepetition extends Model
public function scopeRelevantOnDate(EloquentBuilder $query, Carbon $date) public function scopeRelevantOnDate(EloquentBuilder $query, Carbon $date)
{ {
return $query->where( return $query->where(
function($q) use ($date) { function (EloquentBuilder $q) use ($date) {
$q->where('startdate', '>=', $date->format('Y-m-d 00:00:00')); $q->where('startdate', '<=', $date->format('Y-m-d 00:00:00'));
$q->orWhereNull('startdate'); $q->orWhereNull('startdate');
}) }
)
->where(
function (EloquentBuilder $q) use ($date) {
->where(function($q) use ($date) { $q->where('targetdate', '>=', $date->format('Y-m-d 00:00:00'));
$q->where('targetdate', '<=', $date->format('Y-m-d 00:00:00'));
$q->orWhereNull('targetdate'); $q->orWhereNull('targetdate');
}); }
);
} }
} }

View File

@@ -1,6 +1,7 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Carbon\Carbon; use Carbon\Carbon;
use Crypt;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@@ -40,6 +41,10 @@ class Reminder extends Model
*/ */
public function getMetadataAttribute($value) public function getMetadataAttribute($value)
{ {
if (intval($this->encrypted) == 1) {
return json_decode(Crypt::decrypt($value));
}
return json_decode($value); return json_decode($value);
} }
@@ -86,7 +91,8 @@ class Reminder extends Model
*/ */
public function setMetadataAttribute($value) public function setMetadataAttribute($value)
{ {
$this->attributes['metadata'] = json_encode($value); $this->attributes['encrypted'] = true;
$this->attributes['metadata'] = Crypt::encrypt(json_encode($value));
} }
/** /**

View File

@@ -1,5 +1,7 @@
<?php namespace FireflyIII\Models; <?php namespace FireflyIII\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Watson\Validating\ValidatingTrait; use Watson\Validating\ValidatingTrait;
@@ -30,6 +32,28 @@ class Transaction extends Model
return $this->belongsTo('FireflyIII\Models\Account'); return $this->belongsTo('FireflyIII\Models\Account');
} }
/**
* @param EloquentBuilder $query
* @param Carbon $date
*
* @return mixed
*/
public function scopeAfter(EloquentBuilder $query, Carbon $date)
{
return $query->where('transaction_journals.date', '>=', $date->format('Y-m-d 00:00:00'));
}
/**
* @param EloquentBuilder $query
* @param Carbon $date
*
* @return mixed
*/
public function scopeBefore(EloquentBuilder $query, Carbon $date)
{
return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00'));
}
/** /**
* @return array * @return array
*/ */

View File

@@ -55,6 +55,19 @@ class TransactionJournal extends Model
return $this->belongsToMany('FireflyIII\Models\Category'); return $this->belongsToMany('FireflyIII\Models\Category');
} }
/**
* @return float
*/
public function getAmountAttribute()
{
/** @var Transaction $t */
foreach ($this->transactions as $t) {
if ($t->amount > 0) {
return floatval($t->amount);
}
}
}
/** /**
* @return array * @return array
*/ */

View File

@@ -1,6 +1,5 @@
<?php namespace FireflyIII\Providers; <?php namespace FireflyIII\Providers;
use App;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
@@ -14,6 +13,7 @@ use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Log; use Log;
use FireflyIII\Models\Reminder;
/** /**
* Class EventServiceProvider * Class EventServiceProvider
@@ -62,6 +62,15 @@ class EventServiceProvider extends ServiceProvider
); );
PiggyBank::deleting(
function (PiggyBank $piggyBank) {
$reminders = $piggyBank->reminders()->get();
/** @var Reminder $reminder */
foreach ($reminders as $reminder) {
$reminder->delete();
}
}
);
Account::deleted( Account::deleted(
function (Account $account) { function (Account $account) {
@@ -74,6 +83,8 @@ class EventServiceProvider extends ServiceProvider
} }
); );
// move this routine to a filter
// in case of repeated piggy banks and/or other problems.
PiggyBank::created( PiggyBank::created(
function (PiggyBank $piggyBank) { function (PiggyBank $piggyBank) {
$repetition = new PiggyBankRepetition; $repetition = new PiggyBankRepetition;

View File

@@ -62,9 +62,11 @@ class FireflyServiceProvider extends ServiceProvider
$this->app->bind('FireflyIII\Repositories\Journal\JournalRepositoryInterface', 'FireflyIII\Repositories\Journal\JournalRepository'); $this->app->bind('FireflyIII\Repositories\Journal\JournalRepositoryInterface', 'FireflyIII\Repositories\Journal\JournalRepository');
$this->app->bind('FireflyIII\Repositories\Bill\BillRepositoryInterface', 'FireflyIII\Repositories\Bill\BillRepository'); $this->app->bind('FireflyIII\Repositories\Bill\BillRepositoryInterface', 'FireflyIII\Repositories\Bill\BillRepository');
$this->app->bind('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', 'FireflyIII\Repositories\PiggyBank\PiggyBankRepository'); $this->app->bind('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', 'FireflyIII\Repositories\PiggyBank\PiggyBankRepository');
$this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository');
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search'); $this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
$this->app->bind('FireflyIII\Helpers\Help\HelpInterface', 'FireflyIII\Helpers\Help\Help');
$this->app->bind('FireflyIII\Helpers\Reminders\ReminderHelperInterface', 'FireflyIII\Helpers\Reminders\ReminderHelper'); $this->app->bind('FireflyIII\Helpers\Reminders\ReminderHelperInterface', 'FireflyIII\Helpers\Reminders\ReminderHelper');
$this->app->bind('FireflyIII\Helpers\Report\ReportHelperInterface', 'FireflyIII\Helpers\Report\ReportHelper'); $this->app->bind('FireflyIII\Helpers\Report\ReportHelperInterface', 'FireflyIII\Helpers\Report\ReportHelper');
$this->app->bind('FireflyIII\Helpers\Report\ReportQueryInterface', 'FireflyIII\Helpers\Report\ReportQuery'); $this->app->bind('FireflyIII\Helpers\Report\ReportQueryInterface', 'FireflyIII\Helpers\Report\ReportQuery');

View File

@@ -1,7 +1,6 @@
<?php namespace FireflyIII\Providers; <?php namespace FireflyIII\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Routing\Router; use Illuminate\Routing\Router;
/** /**
@@ -32,22 +31,6 @@ class RouteServiceProvider extends ServiceProvider
{ {
parent::boot($router); parent::boot($router);
$router->before(
function (Request $request) {
// put IP in session if not already there.
$reminders = [];
if ($request->user()) {
//Filter::setSessionDateRange();
//Reminders::updateReminders();
//Steam::removeEmptyBudgetLimits();
//$reminders = Reminders::getReminders();
}
// View::share('reminders', $reminders);
}
);
} }
/** /**

View File

@@ -6,6 +6,7 @@ use App;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Config; use Config;
use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
@@ -14,10 +15,12 @@ use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Session; use Session;
use Steam;
/** /**
* Class AccountRepository * Class AccountRepository
@@ -28,11 +31,13 @@ class AccountRepository implements AccountRepositoryInterface
{ {
/** /**
* @param array $types
*
* @return int * @return int
*/ */
public function countAssetAccounts() public function countAccounts(array $types)
{ {
return Auth::user()->accounts()->accountTypeIn(['Asset account', 'Default account'])->count(); return Auth::user()->accounts()->accountTypeIn($types)->count();
} }
/** /**
@@ -47,6 +52,52 @@ class AccountRepository implements AccountRepositoryInterface
return true; return true;
} }
/**
* @param array $types
*
* @return Collection
*/
public function getAccounts(array $types)
{
$result = Auth::user()->accounts()->with(
['accountmeta' => function (HasMany $query) {
$query->where('name', 'accountRole');
}]
)->accountTypeIn($types)->orderBy('accounts.name', 'ASC')->get(['accounts.*'])->sortBy('name');
return $result;
}
/**
* @return Collection
*/
public function getCreditCards()
{
return Auth::user()->accounts()
->hasMetaValue('accountRole', 'ccAsset')
->hasMetaValue('ccType', 'monthlyFull')
->get(
[
'accounts.*',
'ccType.data as ccType',
'accountRole.data as accountRole'
]
);
}
/**
* @param TransactionJournal $journal
* @param Account $account
*
* @return Transaction
*/
public function getFirstTransaction(TransactionJournal $journal, Account $account)
{
return $journal->transactions()->where('account_id', $account->id)->first();
}
/** /**
* @param Preference $preference * @param Preference $preference
* *
@@ -64,6 +115,10 @@ class AccountRepository implements AccountRepositoryInterface
} }
/** /**
* This method is used on the front page where (in turn) its viewed journals-tiny.php which (in turn)
* is almost the only place where formatJournal is used. Aka, we can use some custom querying to get some specific.
* fields using left joins.
*
* @param Account $account * @param Account $account
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
@@ -74,22 +129,24 @@ class AccountRepository implements AccountRepositoryInterface
{ {
return Auth::user() return Auth::user()
->transactionjournals() ->transactionjournals()
->with(['transactions', 'transactioncurrency', 'transactiontype']) ->with(['transactions'])
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')->where('accounts.id', $account->id) ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')->where('accounts.id', $account->id)
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transaction_journals.transaction_currency_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->before($end) ->before($end)
->after($start) ->after($start)
->orderBy('transaction_journals.date', 'DESC') ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC') ->orderBy('transaction_journals.id', 'DESC')
->take(10) ->take(10)
->get(['transaction_journals.*']); ->get(['transaction_journals.*', 'transaction_currencies.symbol', 'transaction_types.type']);
} }
/** /**
* @param Account $account * @param Account $account
* @param int $page * @param int $page
* *
* @return mixed * @return LengthAwarePaginator
*/ */
public function getJournals(Account $account, $page) public function getJournals(Account $account, $page)
{ {
@@ -99,7 +156,9 @@ class AccountRepository implements AccountRepositoryInterface
->withRelevantData() ->withRelevantData()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id) ->where('transactions.account_id', $account->id)
->orderBy('date', 'DESC'); ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC');
$query->before(Session::get('end', Carbon::now()->endOfMonth())); $query->before(Session::get('end', Carbon::now()->endOfMonth()));
$query->after(Session::get('start', Carbon::now()->startOfMonth())); $query->after(Session::get('start', Carbon::now()->startOfMonth()));
@@ -109,9 +168,139 @@ class AccountRepository implements AccountRepositoryInterface
return $paginator; return $paginator;
//return Paginator::make($items, $count, 50);
}
/**
* @param Account $account
*
* @return Carbon|null
*/
public function getLastActivity(Account $account)
{
$lastTransaction = $account->transactions()->leftJoin(
'transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->orderBy('transaction_journals.date', 'DESC')->first(['transactions.*', 'transaction_journals.date']);
if ($lastTransaction) {
return $lastTransaction->transactionjournal->date;
}
return null;
}
/**
* Get the accounts of a user that have piggy banks connected to them.
*
* @return Collection
*/
public function getPiggyBankAccounts()
{
$ids = [];
$start = clone Session::get('start', new Carbon);
$end = clone Session::get('end', new Carbon);
$accountIds = DB::table('piggy_banks')->distinct()->get(['piggy_banks.account_id']);
$accounts = new Collection;
foreach ($accountIds as $id) {
$ids[] = intval($id->account_id);
}
$ids = array_unique($ids);
if (count($ids) > 0) {
$accounts = Auth::user()->accounts()->whereIn('id', $ids)->get();
}
$accounts->each(
function (Account $account) use ($start, $end) {
$account->startBalance = Steam::balance($account, $start, true);
$account->endBalance = Steam::balance($account, $end, true);
$account->piggyBalance = 0;
/** @var PiggyBank $piggyBank */
foreach ($account->piggyBanks as $piggyBank) {
$account->piggyBalance += $piggyBank->currentRelevantRep()->currentamount;
}
// sum of piggy bank amounts on this account:
// diff between endBalance and piggyBalance.
// then, percentage.
$difference = $account->endBalance - $account->piggyBalance;
$account->difference = $difference;
$account->percentage = $difference != 0 ? round((($difference / $account->endBalance) * 100)) : 100;
}
);
return $accounts;
}
/**
* Get savings accounts and the balance difference in the period.
*
* @return Collection
*/
public function getSavingsAccounts()
{
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', 'accountRole')
->where('account_meta.data', '"savingAsset"')
->get(['accounts.*']);
$start = clone Session::get('start', new Carbon);
$end = clone Session::get('end', new Carbon);
$accounts->each(
function (Account $account) use ($start, $end) {
$account->startBalance = Steam::balance($account, $start);
$account->endBalance = Steam::balance($account, $end);
// diff (negative when lost, positive when gained)
$diff = $account->endBalance - $account->startBalance;
if ($diff < 0 && $account->startBalance > 0) {
// percentage lost compared to start.
$pct = (($diff * -1) / $account->startBalance) * 100;
} else {
if ($diff >= 0 && $account->startBalance > 0) {
$pct = ($diff / $account->startBalance) * 100;
} else {
$pct = 100;
}
}
$pct = $pct > 100 ? 100 : $pct;
$account->difference = $diff;
$account->percentage = round($pct);
}
);
return $accounts;
}
/**
* Get all transfers TO this account in this range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransfersInRange(Account $account, Carbon $start, Carbon $end)
{
return TransactionJournal::whereIn(
'id', function ($q) use ($account, $start, $end) {
$q->select('transaction_journals.id')
->from('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transactions.account_id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transactions.amount', '>', 0)
->where('transaction_types.type', 'Transfer');
}
)->get();
} }
/** /**
@@ -121,7 +310,7 @@ class AccountRepository implements AccountRepositoryInterface
*/ */
public function leftOnAccount(Account $account) public function leftOnAccount(Account $account)
{ {
$balance = \Steam::balance($account); $balance = \Steam::balance($account, null, true);
/** @var PiggyBank $p */ /** @var PiggyBank $p */
foreach ($account->piggybanks()->get() as $p) { foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount; $balance -= $p->currentRelevantRep()->currentamount;
@@ -151,8 +340,8 @@ class AccountRepository implements AccountRepositoryInterface
*/ */
public function store(array $data) public function store(array $data)
{ {
$newAccount = $this->_store($data); $newAccount = $this->storeAccount($data);
$this->_storeMetadata($newAccount, $data); $this->storeMetadata($newAccount, $data);
// continue with the opposing account: // continue with the opposing account:
@@ -161,11 +350,12 @@ class AccountRepository implements AccountRepositoryInterface
$opposingData = [ $opposingData = [
'user' => $data['user'], 'user' => $data['user'],
'accountType' => $type, 'accountType' => $type,
'virtual_balance' => $data['virtualBalance'],
'name' => $data['name'] . ' initial balance', 'name' => $data['name'] . ' initial balance',
'active' => false, 'active' => false,
]; ];
$opposing = $this->_store($opposingData); $opposing = $this->storeAccount($opposingData);
$this->_storeInitialBalance($newAccount, $opposing, $data); $this->storeInitialBalance($newAccount, $opposing, $data);
} }
@@ -173,6 +363,14 @@ class AccountRepository implements AccountRepositoryInterface
} }
/**
* @return float
*/
public function sumOfEverything()
{
return floatval(Auth::user()->transactions()->sum('amount'));
}
/** /**
* @param Account $account * @param Account $account
* @param array $data * @param array $data
@@ -182,10 +380,11 @@ class AccountRepository implements AccountRepositoryInterface
// update the account: // update the account:
$account->name = $data['name']; $account->name = $data['name'];
$account->active = $data['active'] == '1' ? true : false; $account->active = $data['active'] == '1' ? true : false;
$account->virtual_balance = $data['virtualBalance'];
$account->save(); $account->save();
// update meta data: // update meta data:
$this->_updateMetadata($account, $data); $this->updateMetadata($account, $data);
$openingBalance = $this->openingBalanceTransaction($account); $openingBalance = $this->openingBalanceTransaction($account);
@@ -194,7 +393,7 @@ class AccountRepository implements AccountRepositoryInterface
// if opening balance, do an update: // if opening balance, do an update:
if ($openingBalance) { if ($openingBalance) {
// update existing opening balance. // update existing opening balance.
$this->_updateInitialBalance($account, $openingBalance, $data); $this->updateInitialBalance($account, $openingBalance, $data);
} else { } else {
// create new opening balance. // create new opening balance.
$type = $data['openingBalance'] < 0 ? 'expense' : 'revenue'; $type = $data['openingBalance'] < 0 ? 'expense' : 'revenue';
@@ -204,8 +403,8 @@ class AccountRepository implements AccountRepositoryInterface
'name' => $data['name'] . ' initial balance', 'name' => $data['name'] . ' initial balance',
'active' => false, 'active' => false,
]; ];
$opposing = $this->_store($opposingData); $opposing = $this->storeAccount($opposingData);
$this->_storeInitialBalance($account, $opposing, $data); $this->storeInitialBalance($account, $opposing, $data);
} }
} else { } else {
@@ -224,7 +423,7 @@ class AccountRepository implements AccountRepositoryInterface
* *
* @return Account * @return Account
*/ */
protected function _store(array $data) protected function storeAccount(array $data)
{ {
$type = Config::get('firefly.accountTypeByIdentifier.' . $data['accountType']); $type = Config::get('firefly.accountTypeByIdentifier.' . $data['accountType']);
$accountType = AccountType::whereType($type)->first(); $accountType = AccountType::whereType($type)->first();
@@ -236,12 +435,14 @@ class AccountRepository implements AccountRepositoryInterface
'active' => $data['active'] === true ? true : false, 'active' => $data['active'] === true ? true : false,
] ]
); );
if (!$newAccount->isValid()) { if (!$newAccount->isValid()) {
// does the account already exist? // does the account already exist?
$existingAccount = Account::where('user_id', $data['user'])->where('account_type_id', $accountType->id)->where('name', $data['name'])->first(); $existingAccount = Account::where('user_id', $data['user'])->where('account_type_id', $accountType->id)->where('name', $data['name'])->first();
if (!$existingAccount) { if (!$existingAccount) {
Log::error('Account create error: ' . $newAccount->getErrors()->toJson()); Log::error('Account create error: ' . $newAccount->getErrors()->toJson());
var_dump($newAccount->getErrors()->toArray()); App::abort(500);
} }
$newAccount = $existingAccount; $newAccount = $existingAccount;
} }
@@ -254,21 +455,25 @@ class AccountRepository implements AccountRepositoryInterface
* @param Account $account * @param Account $account
* @param array $data * @param array $data
*/ */
protected function _storeMetadata(Account $account, array $data) protected function storeMetadata(Account $account, array $data)
{ {
$validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType'];
foreach ($validFields as $field) {
if (isset($data[$field])) {
$metaData = new AccountMeta( $metaData = new AccountMeta(
[ [
'account_id' => $account->id, 'account_id' => $account->id,
'name' => 'accountRole', 'name' => $field,
'data' => $data['accountRole'] 'data' => $data[$field]
] ]
); );
if (!$metaData->isValid()) {
App::abort(500);
}
$metaData->save(); $metaData->save();
} }
}
}
/** /**
* @param Account $account * @param Account $account
* @param Account $opposing * @param Account $opposing
@@ -276,7 +481,7 @@ class AccountRepository implements AccountRepositoryInterface
* *
* @return TransactionJournal * @return TransactionJournal
*/ */
protected function _storeInitialBalance(Account $account, Account $opposing, array $data) protected function storeInitialBalance(Account $account, Account $opposing, array $data)
{ {
$type = $data['openingBalance'] < 0 ? 'Withdrawal' : 'Deposit'; $type = $data['openingBalance'] < 0 ? 'Withdrawal' : 'Deposit';
$transactionType = TransactionType::whereType($type)->first(); $transactionType = TransactionType::whereType($type)->first();
@@ -345,33 +550,30 @@ class AccountRepository implements AccountRepositoryInterface
* @param Account $account * @param Account $account
* @param array $data * @param array $data
*/ */
protected function _updateMetadata(Account $account, array $data) protected function updateMetadata(Account $account, array $data)
{ {
$metaEntries = $account->accountMeta()->get(); $validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType'];
$updated = false;
/** @var AccountMeta $entry */ foreach ($validFields as $field) {
foreach ($metaEntries as $entry) { $entry = $account->accountMeta()->where('name', $field)->first();
if ($entry->name == 'accountRole') {
$entry->data = $data['accountRole']; // update if new data is present:
$updated = true; if ($entry && isset($data[$field])) {
$entry->data = $data[$field];
$entry->save(); $entry->save();
} }
} // no entry but data present?
if (!$entry && isset($data[$field])) {
if ($updated === false) {
$metaData = new AccountMeta( $metaData = new AccountMeta(
[ [
'account_id' => $account->id, 'account_id' => $account->id,
'name' => 'accountRole', 'name' => $field,
'data' => $data['accountRole'] 'data' => $data[$field]
] ]
); );
if (!$metaData->isValid()) {
App::abort(500);
}
$metaData->save(); $metaData->save();
} }
}
} }
@@ -382,7 +584,7 @@ class AccountRepository implements AccountRepositoryInterface
* *
* @return TransactionJournal * @return TransactionJournal
*/ */
protected function _updateInitialBalance(Account $account, TransactionJournal $journal, array $data) protected function updateInitialBalance(Account $account, TransactionJournal $journal, array $data)
{ {
$journal->date = $data['openingBalanceDate']; $journal->date = $data['openingBalanceDate'];

View File

@@ -2,11 +2,14 @@
namespace FireflyIII\Repositories\Account; namespace FireflyIII\Repositories\Account;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Preference;
use Illuminate\Support\Collection;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
/** /**
* Interface AccountRepositoryInterface * Interface AccountRepositoryInterface
* *
@@ -14,6 +17,13 @@ use Carbon\Carbon;
*/ */
interface AccountRepositoryInterface interface AccountRepositoryInterface
{ {
/**
* @param array $types
*
* @return int
*/
public function countAccounts(array $types);
/** /**
* @param Account $account * @param Account $account
* *
@@ -22,9 +32,43 @@ interface AccountRepositoryInterface
public function destroy(Account $account); public function destroy(Account $account);
/** /**
* @return int * @param array $types
*
* @return mixed
*/ */
public function countAssetAccounts(); public function getAccounts(array $types);
/**
* @param TransactionJournal $journal
* @param Account $account
*
* @return Transaction
*/
public function getFirstTransaction(TransactionJournal $journal, Account $account);
/**
* @return Collection
*/
public function getCreditCards();
/**
* Get the accounts of a user that have piggy banks connected to them.
*
* @return Collection
*/
public function getPiggyBankAccounts();
/**
* Get all transfers TO this account in this range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransfersInRange(Account $account, Carbon $start, Carbon $end);
/** /**
* @param Preference $preference * @param Preference $preference
@@ -46,10 +90,36 @@ interface AccountRepositoryInterface
* @param Account $account * @param Account $account
* @param string $range * @param string $range
* *
* @return mixed * @return LengthAwarePaginator
*/ */
public function getJournals(Account $account, $page); public function getJournals(Account $account, $page);
/**
* @param Account $account
*
* @return Carbon|null
*/
public function getLastActivity(Account $account);
/**
* @return float
*/
public function sumOfEverything();
/**
* Get savings accounts and the balance difference in the period.
*
* @return Collection
*/
public function getSavingsAccounts();
/**
* @param Account $account
*
* @return float
*/
public function leftOnAccount(Account $account);
/** /**
* @param Account $account * @param Account $account
* *
@@ -71,11 +141,4 @@ interface AccountRepositoryInterface
* @return Account * @return Account
*/ */
public function update(Account $account, array $data); public function update(Account $account, array $data);
/**
* @param Account $account
*
* @return float
*/
public function leftOnAccount(Account $account);
} }

View File

@@ -2,9 +2,15 @@
namespace FireflyIII\Repositories\Bill; namespace FireflyIII\Repositories\Bill;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Log; use Log;
use Navigation; use Navigation;
@@ -15,6 +21,121 @@ use Navigation;
*/ */
class BillRepository implements BillRepositoryInterface class BillRepository implements BillRepositoryInterface
{ {
/**
* Create a fake bill to help the chart controller.
*
* @param string $description
* @param Carbon $date
* @param float $amount
*
* @return Bill
*/
public function createFakeBill($description, Carbon $date, $amount)
{
$bill = new Bill;
$bill->name = $description;
$bill->match = $description;
$bill->amount_min = $amount;
$bill->amount_max = $amount;
$bill->date = $date;
$bill->repeat_freq = 'monthly';
$bill->skip = 0;
$bill->automatch = false;
$bill->active = false;
return $bill;
}
/**
* @param Bill $bill
*
* @return mixed
*/
public function destroy(Bill $bill)
{
return $bill->delete();
}
/**
* @return Collection
*/
public function getActiveBills()
{
/** @var Collection $set */
$set = Auth::user()->bills()->orderBy('name', 'ASC')->where('active', 1)->get()->sortBy('name');
return $set;
}
/**
* @return Collection
*/
public function getBills()
{
/** @var Collection $set */
$set = Auth::user()->bills()->orderBy('name', 'ASC')->get()->sortBy('name');
return $set;
}
/**
* @param Bill $bill
*
* @return Collection
*/
public function getJournals(Bill $bill)
{
return $bill->transactionjournals()->withRelevantData()
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.amount', '>', 0);
}
)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*', 'transactions.amount']);
}
/**
* Get all journals that were recorded on this bill between these dates.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getJournalsInRange(Bill $bill, Carbon $start, Carbon $end)
{
return $bill->transactionjournals()->before($end)->after($start)->get();
}
/**
* @param Bill $bill
*
* @return Collection
*/
public function getPossiblyRelatedJournals(Bill $bill)
{
$set = \DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max)->get(
['transaction_journal_id']
);
$ids = [];
/** @var Transaction $entry */
foreach ($set as $entry) {
$ids[] = intval($entry->transaction_journal_id);
}
$journals = new Collection;
if (count($ids) > 0) {
$journals = Auth::user()->transactionjournals()->whereIn('id', $ids)->get();
}
return $journals;
}
/** /**
* Every bill repeats itself weekly, monthly or yearly (or whatever). This method takes a date-range (usually the view-range of Firefly itself) * Every bill repeats itself weekly, monthly or yearly (or whatever). This method takes a date-range (usually the view-range of Firefly itself)
* and returns date ranges that fall within the given range; those ranges are the bills expected. When a bill is due on the 14th of the month and * and returns date ranges that fall within the given range; those ranges are the bills expected. When a bill is due on the 14th of the month and
@@ -51,16 +172,26 @@ class BillRepository implements BillRepositoryInterface
foreach ($billStarts as $dateEntry) { foreach ($billStarts as $dateEntry) {
if ($dateEntry['end'] > $start && $dateEntry['start'] < $end) { if ($dateEntry['end'] > $start && $dateEntry['start'] < $end) {
// count transactions for bill in this range (not relevant yet!): // count transactions for bill in this range (not relevant yet!):
// $count = $bill->transactionjournals()->before($dateEntry['end'])->after($dateEntry['start'])->count();
// if ($count == 0) {
$validRanges[] = $dateEntry; $validRanges[] = $dateEntry;
// }
} }
} }
return $validRanges; return $validRanges;
// echo $bill->name; }
// var_dump($validRanges);
/**
* @param Bill $bill
*
* @return Carbon|null
*/
public function lastFoundMatch(Bill $bill)
{
$last = $bill->transactionjournals()->orderBy('date', 'DESC')->first();
if ($last) {
return $last->date;
}
return null;
} }
/** /**
@@ -184,6 +315,12 @@ class BillRepository implements BillRepositoryInterface
Log::debug('TOTAL match is true!'); Log::debug('TOTAL match is true!');
$journal->bill()->associate($bill); $journal->bill()->associate($bill);
$journal->save(); $journal->save();
} else {
if ((!$wordMatch || !$amountMatch) && $bill->id == $journal->bill_id) {
// if no match, but bill used to match, remove it:
$journal->bill_id = null;
$journal->save();
}
} }
} }

View File

@@ -5,20 +5,68 @@ namespace FireflyIII\Repositories\Bill;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
/** /**
* Interface BillRepositoryInterface * Interface BillRepositoryInterface
* *
* @package FireflyIII\Repositories\Bill * @package FireflyIII\Repositories\Bill
*/ */
interface BillRepositoryInterface { interface BillRepositoryInterface
{
/**
* Create a fake bill to help the chart controller.
*
* @param string $description
* @param Carbon $date
* @param float $amount
*
* @return Bill
*/
public function createFakeBill($description, Carbon $date, $amount);
/** /**
* @param Bill $bill * @param Bill $bill
* *
* @return Carbon|null * @return mixed
*/ */
public function nextExpectedMatch(Bill $bill); public function destroy(Bill $bill);
/**
* @return Collection
*/
public function getActiveBills();
/**
* @return Collection
*/
public function getBills();
/**
* @param Bill $bill
*
* @return Collection
*/
public function getJournals(Bill $bill);
/**
* Get all journals that were recorded on this bill between these dates.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getJournalsInRange(Bill $bill, Carbon $start, Carbon $end);
/**
* @param Bill $bill
*
* @return Collection
*/
public function getPossiblyRelatedJournals(Bill $bill);
/** /**
* Every bill repeats itself weekly, monthly or yearly (or whatever). This method takes a date-range (usually the view-range of Firefly itself) * Every bill repeats itself weekly, monthly or yearly (or whatever). This method takes a date-range (usually the view-range of Firefly itself)
@@ -33,6 +81,29 @@ interface BillRepositoryInterface {
*/ */
public function getRanges(Bill $bill, Carbon $start, Carbon $end); public function getRanges(Bill $bill, Carbon $start, Carbon $end);
/**
* @param Bill $bill
*
* @return Carbon|null
*/
public function lastFoundMatch(Bill $bill);
/**
* @param Bill $bill
*
* @return Carbon|null
*/
public function nextExpectedMatch(Bill $bill);
/**
* @param Bill $bill
* @param TransactionJournal $journal
*
* @return bool
*/
public function scan(Bill $bill, TransactionJournal $journal);
/** /**
* @param array $data * @param array $data
* *
@@ -48,12 +119,4 @@ interface BillRepositoryInterface {
*/ */
public function update(Bill $bill, array $data); public function update(Bill $bill, array $data);
/**
* @param Bill $bill
* @param TransactionJournal $journal
*
* @return bool
*/
public function scan(Bill $bill, TransactionJournal $journal);
} }

View File

@@ -2,11 +2,14 @@
namespace FireflyIII\Repositories\Budget; namespace FireflyIII\Repositories\Budget;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
/** /**
* Class BudgetRepository * Class BudgetRepository
@@ -16,6 +19,31 @@ use Illuminate\Pagination\LengthAwarePaginator;
class BudgetRepository implements BudgetRepositoryInterface class BudgetRepository implements BudgetRepositoryInterface
{ {
/**
* @return void
*/
public function cleanupBudgets()
{
$limits = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')->get(['budget_limits.*']);
// loop budget limits:
$found = [];
/** @var BudgetLimit $limit */
foreach ($limits as $limit) {
$key = $limit->budget_id . '-' . $limit->startdate;
if (isset($found[$key])) {
$limit->delete();
} else {
$found[$key] = true;
}
unset($key);
}
// delete limits with amount 0:
BudgetLimit::where('amount', 0)->delete();
}
/** /**
* @param Budget $budget * @param Budget $budget
* *
@@ -28,6 +56,101 @@ class BudgetRepository implements BudgetRepositoryInterface
return true; return true;
} }
/**
* @param Budget $budget
* @param Carbon $date
*
* @return float
*/
public function expensesOnDay(Budget $budget, Carbon $date)
{
return floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($date)->sum('amount'));
}
/**
* @return Collection
*/
public function getActiveBudgets()
{
$budgets = Auth::user()->budgets()->where('active', 1)->get();
$budgets->sortBy('name');
return $budgets;
}
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getBudgetLimitRepetitions(Budget $budget, Carbon $start, Carbon $end)
{
/** @var Collection $repetitions */
return LimitRepetition::
leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00'))
->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->get(['limit_repetitions.*']);
}
/**
* @param Budget $budget
*
* @return Collection
*/
public function getBudgetLimits(Budget $budget)
{
return $budget->budgetLimits()->orderBy('startdate', 'DESC')->get();
}
/**
* @return Collection
*/
public function getBudgets()
{
$budgets = Auth::user()->budgets()->get();
$budgets->sortBy('name');
return $budgets;
}
/**
* @param Budget $budget
* @param Carbon $date
*
* @return LimitRepetition|null
*/
public function getCurrentRepetition(Budget $budget, Carbon $date)
{
return $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']);
}
/**
* @param Budget $budget
*
* @return Carbon
*/
public function getFirstBudgetLimitDate(Budget $budget)
{
$limit = $budget->budgetlimits()->orderBy('startdate', 'ASC')->first();
if ($limit) {
return $limit->startdate;
}
return Carbon::now()->startOfYear();
}
/**
* @return Collection
*/
public function getInactiveBudgets()
{
return Auth::user()->budgets()->where('active', 0)->get();
}
/** /**
* Returns all the transaction journals for a limit, possibly limited by a limit repetition. * Returns all the transaction journals for a limit, possibly limited by a limit repetition.
* *
@@ -42,7 +165,10 @@ class BudgetRepository implements BudgetRepositoryInterface
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $take : 0; $offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $take : 0;
$setQuery = $budget->transactionJournals()->withRelevantData()->take($take)->offset($offset)->orderBy('date', 'DESC'); $setQuery = $budget->transactionJournals()->withRelevantData()->take($take)->offset($offset)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC');
$countQuery = $budget->transactionJournals(); $countQuery = $budget->transactionJournals();
@@ -54,9 +180,95 @@ class BudgetRepository implements BudgetRepositoryInterface
$set = $setQuery->get(['transaction_journals.*']); $set = $setQuery->get(['transaction_journals.*']);
$count = $countQuery->count(); $count = $countQuery->count();
return new LengthAwarePaginator($set, $count, $take, $offset); return new LengthAwarePaginator($set, $count, $take, $offset);
} }
/**
* @param Budget $budget
*
* @return Carbon
*/
public function getLastBudgetLimitDate(Budget $budget)
{
$limit = $budget->budgetlimits()->orderBy('startdate', 'DESC')->first();
if ($limit) {
return $limit->startdate;
}
return Carbon::now()->startOfYear();
}
/**
* @param Budget $budget
* @param Carbon $date
*
* @return float|null
*/
public function getLimitAmountOnDate(Budget $budget, Carbon $date)
{
$repetition = LimitRepetition::leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('limit_repetitions.startdate', $date->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->first(['limit_repetitions.*']);
if ($repetition) {
return floatval($repetition->amount);
}
return null;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getWithoutBudget(Carbon $start, Carbon $end)
{
return Auth::user()
->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('budget_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*']);
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getWithoutBudgetSum(Carbon $start, Carbon $end)
{
$noBudgetSet = Auth::user()
->transactionjournals()
->whereNotIn(
'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) {
$query
->select('transaction_journals.id')
->from('transaction_journals')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'))
->whereNotNull('budget_transaction_journal.budget_id');
}
)
->before($end)
->after($start)
->lessThan(0)
->transactionTypes(['Withdrawal'])
->get();
return floatval($noBudgetSet->sum('amount')) * -1;
}
/** /**
* @param Budget $budget * @param Budget $budget
* @param Carbon $date * @param Carbon $date
@@ -91,6 +303,18 @@ class BudgetRepository implements BudgetRepositoryInterface
return $newBudget; return $newBudget;
} }
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function sumBudgetExpensesInPeriod(Budget $budget, $start, $end)
{
return floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
}
/** /**
* @param Budget $budget * @param Budget $budget
* @param array $data * @param array $data
@@ -101,6 +325,7 @@ class BudgetRepository implements BudgetRepositoryInterface
{ {
// update the account: // update the account:
$budget->name = $data['name']; $budget->name = $data['name'];
$budget->active = $data['active'];
$budget->save(); $budget->save();
return $budget; return $budget;
@@ -115,10 +340,12 @@ class BudgetRepository implements BudgetRepositoryInterface
*/ */
public function updateLimitAmount(Budget $budget, Carbon $date, $amount) public function updateLimitAmount(Budget $budget, Carbon $date, $amount)
{ {
// there should be a budget limit for this startdate:
/** @var BudgetLimit $limit */ /** @var BudgetLimit $limit */
$limit = $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']); $limit = $budget->budgetlimits()->where('budget_limits.startdate', $date)->first(['budget_limits.*']);
if (!$limit) { if (!$limit) {
// create one! // if not, create one!
$limit = new BudgetLimit; $limit = new BudgetLimit;
$limit->budget()->associate($budget); $limit->budget()->associate($budget);
$limit->startdate = $date; $limit->startdate = $date;
@@ -126,6 +353,10 @@ class BudgetRepository implements BudgetRepositoryInterface
$limit->repeat_freq = 'monthly'; $limit->repeat_freq = 'monthly';
$limit->repeats = 0; $limit->repeats = 0;
$limit->save(); $limit->save();
// likewise, there should be a limit repetition to match the end date
// (which is always the end of the month) but that is caught by an event.
} else { } else {
if ($amount > 0) { if ($amount > 0) {
$limit->amount = $amount; $limit->amount = $amount;

View File

@@ -5,6 +5,7 @@ namespace FireflyIII\Repositories\Budget;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use Illuminate\Support\Collection;
/** /**
* Interface BudgetRepositoryInterface * Interface BudgetRepositoryInterface
@@ -13,6 +14,11 @@ use FireflyIII\Models\LimitRepetition;
*/ */
interface BudgetRepositoryInterface interface BudgetRepositoryInterface
{ {
/**
* @return void
*/
public function cleanupBudgets();
/** /**
* @param Budget $budget * @param Budget $budget
* *
@@ -26,31 +32,53 @@ interface BudgetRepositoryInterface
* *
* @return float * @return float
*/ */
public function spentInMonth(Budget $budget, Carbon $date); public function expensesOnDay(Budget $budget, Carbon $date);
/**
* @return Collection
*/
public function getActiveBudgets();
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getBudgetLimitRepetitions(Budget $budget, Carbon $start, Carbon $end);
/**
* @param Budget $budget
*
* @return Collection
*/
public function getBudgetLimits(Budget $budget);
/**
* @return Collection
*/
public function getBudgets();
/** /**
* @param Budget $budget * @param Budget $budget
* @param Carbon $date * @param Carbon $date
* @param $amount
* *
* @return mixed * @return LimitRepetition|null
*/ */
public function updateLimitAmount(Budget $budget, Carbon $date, $amount); public function getCurrentRepetition(Budget $budget, Carbon $date);
/**
* @param array $data
*
* @return Budget
*/
public function store(array $data);
/** /**
* @param Budget $budget * @param Budget $budget
* @param array $data
* *
* @return Budget * @return Carbon
*/ */
public function update(Budget $budget, array $data); public function getFirstBudgetLimitDate(Budget $budget);
/**
* @return Collection
*/
public function getInactiveBudgets();
/** /**
* Returns all the transaction journals for a limit, possibly limited by a limit repetition. * Returns all the transaction journals for a limit, possibly limited by a limit repetition.
@@ -63,4 +91,76 @@ interface BudgetRepositoryInterface
*/ */
public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50); public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50);
/**
* @param Budget $budget
*
* @return Carbon
*/
public function getLastBudgetLimitDate(Budget $budget);
/**
* @param Budget $budget
* @param Carbon $date
*
* @return float
*/
public function getLimitAmountOnDate(Budget $budget, Carbon $date);
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getWithoutBudget(Carbon $start, Carbon $end);
/**
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getWithoutBudgetSum(Carbon $start, Carbon $end);
/**
* @param Budget $budget
* @param Carbon $date
*
* @return float
*/
public function spentInMonth(Budget $budget, Carbon $date);
/**
* @param array $data
*
* @return Budget
*/
public function store(array $data);
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function sumBudgetExpensesInPeriod(Budget $budget, $start, $end);
/**
* @param Budget $budget
* @param array $data
*
* @return Budget
*/
public function update(Budget $budget, array $data);
/**
* @param Budget $budget
* @param Carbon $date
* @param $amount
*
* @return mixed
*/
public function updateLimitAmount(Budget $budget, Carbon $date, $amount);
} }

View File

@@ -2,7 +2,13 @@
namespace FireflyIII\Repositories\Category; namespace FireflyIII\Repositories\Category;
use Auth;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/** /**
* Class CategoryRepository * Class CategoryRepository
@@ -12,6 +18,17 @@ use FireflyIII\Models\Category;
class CategoryRepository implements CategoryRepositoryInterface class CategoryRepository implements CategoryRepositoryInterface
{ {
/**
* @param Category $category
*
* @return int
*/
public function countJournals(Category $category)
{
return $category->transactionJournals()->count();
}
/** /**
* @param Category $category * @param Category $category
* *
@@ -24,6 +41,150 @@ class CategoryRepository implements CategoryRepositoryInterface
return true; return true;
} }
/**
* @return Collection
*/
public function getCategories()
{
/** @var Collection $set */
$set = Auth::user()->categories()->orderBy('name', 'ASC')->get();
$set->sortBy(
function (Category $category) {
return $category->name;
}
);
return $set;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getCategoriesAndExpenses($start, $end)
{
return TransactionJournal::
where('transaction_journals.user_id', Auth::user()->id)
->leftJoin(
'transactions',
function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->before($end)
->where('categories.user_id', Auth::user()->id)
->after($start)
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('sum', 'DESC')
->get(['categories.id', 'categories.encrypted', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
}
/**
* @param Category $category
*
* @return Carbon
*/
public function getFirstActivityDate(Category $category)
{
/** @var TransactionJournal $first */
$first = $category->transactionjournals()->orderBy('date', 'ASC')->first();
if ($first) {
return $first->date;
}
return new Carbon;
}
/**
* @param Category $category
* @param int $page
*
* @return Collection
*/
public function getJournals(Category $category, $page)
{
$offset = $page > 0 ? $page * 50 : 0;
return $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(
['transaction_journals.*']
);
}
/**
* @param Category $category
*
* @return Carbon|null
*/
public function getLatestActivity(Category $category)
{
$latest = $category->transactionjournals()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->first();
if ($latest) {
return $latest->date;
}
return null;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getWithoutCategory(Carbon $start, Carbon $end)
{
return Auth::user()
->transactionjournals()
->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('category_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*']);
}
/**
* @param Category $category
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end)
{
return floatval($category->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
}
/**
* @param Category $category
* @param Carbon $date
*
* @return float
*/
public function spentOnDaySum(Category $category, Carbon $date)
{
return floatval($category->transactionjournals()->onDate($date)->lessThan(0)->sum('amount')) * -1;
}
/** /**
* @param array $data * @param array $data
@@ -57,5 +218,4 @@ class CategoryRepository implements CategoryRepositoryInterface
return $category; return $category;
} }
} }

View File

@@ -2,7 +2,9 @@
namespace FireflyIII\Repositories\Category; namespace FireflyIII\Repositories\Category;
use Carbon\Carbon;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use Illuminate\Support\Collection;
/** /**
* Interface CategoryRepositoryInterface * Interface CategoryRepositoryInterface
@@ -11,6 +13,13 @@ use FireflyIII\Models\Category;
*/ */
interface CategoryRepositoryInterface interface CategoryRepositoryInterface
{ {
/**
* @param Category $category
*
* @return int
*/
public function countJournals(Category $category);
/** /**
* @param Category $category * @param Category $category
* *
@@ -18,6 +27,66 @@ interface CategoryRepositoryInterface
*/ */
public function destroy(Category $category); public function destroy(Category $category);
/**
* @return Collection
*/
public function getCategories();
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getCategoriesAndExpenses($start, $end);
/**
* @param Category $category
*
* @return Carbon
*/
public function getFirstActivityDate(Category $category);
/**
* @param Category $category
* @param int $page
*
* @return Collection
*/
public function getJournals(Category $category, $page);
/**
* @param Category $category
*
* @return Carbon|null
*/
public function getLatestActivity(Category $category);
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getWithoutCategory(Carbon $start, Carbon $end);
/**
* @param Category $category
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end);
/**
* @param Category $category
* @param Carbon $date
*
* @return float
*/
public function spentOnDaySum(Category $category, Carbon $date);
/** /**
* @param array $data * @param array $data
* *

View File

@@ -0,0 +1,84 @@
<?php
namespace FireflyIII\Repositories\Currency;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use Illuminate\Support\Collection;
/**
* Class CurrencyRepository
*
* @package FireflyIII\Repositories\Currency
*/
class CurrencyRepository implements CurrencyRepositoryInterface
{
/**
* @param TransactionCurrency $currency
*
* @return int
*/
public function countJournals(TransactionCurrency $currency)
{
return $currency->transactionJournals()->count();
}
/**
* @return Collection
*/
public function get()
{
return TransactionCurrency::get();
}
/**
* @param Preference $preference
*
* @return TransactionCurrency
*/
public function getCurrencyByPreference(Preference $preference)
{
$preferred = TransactionCurrency::whereCode($preference->data)->first();
if (is_null($preferred)) {
$preferred = TransactionCurrency::first();
}
return $preferred;
}
/**
* @param array $data
*
* @return TransactionCurrency
*/
public function store(array $data)
{
$currency = TransactionCurrency::create(
[
'name' => $data['name'],
'code' => $data['code'],
'symbol' => $data['symbol'],
]
);
return $currency;
}
/**
* @param TransactionCurrency $currency
* @param array $data
*
* @return TransactionCurrency
*/
public function update(TransactionCurrency $currency, array $data)
{
$currency->code = $data['code'];
$currency->symbol = $data['symbol'];
$currency->name = $data['name'];
$currency->save();
return $currency;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace FireflyIII\Repositories\Currency;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use Illuminate\Support\Collection;
/**
* Interface CurrencyRepositoryInterface
*
* @package FireflyIII\Repositories\Currency
*/
interface CurrencyRepositoryInterface
{
/**
* @param TransactionCurrency $currency
*
* @return int
*/
public function countJournals(TransactionCurrency $currency);
/**
* @return Collection
*/
public function get();
/**
* @param Preference $preference
*
* @return TransactionCurrency
*/
public function getCurrencyByPreference(Preference $preference);
/**
* @param array $data
*
* @return TransactionCurrency
*/
public function store(array $data);
/**
* @param TransactionCurrency $currency
* @param array $data
*
* @return TransactionCurrency
*/
public function update(TransactionCurrency $currency, array $data);
}

View File

@@ -2,6 +2,7 @@
namespace FireflyIII\Repositories\Journal; namespace FireflyIII\Repositories\Journal;
use App;
use Auth; use Auth;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
@@ -11,6 +12,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
/** /**
* Class JournalRepository * Class JournalRepository
@@ -20,6 +22,16 @@ use Illuminate\Support\Collection;
class JournalRepository implements JournalRepositoryInterface class JournalRepository implements JournalRepositoryInterface
{ {
/**
* Get users first transaction journal
*
* @return TransactionJournal
*/
public function first()
{
return Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
}
/** /**
* *
* Get the account_id, which is the asset account that paid for the transaction. * Get the account_id, which is the asset account that paid for the transaction.
@@ -50,6 +62,26 @@ class JournalRepository implements JournalRepositoryInterface
return $journal->transactions()->first()->account_id; return $journal->transactions()->first()->account_id;
} }
/**
* @param TransactionType $dbType
*
* @return Collection
*/
public function getJournalsOfType(TransactionType $dbType)
{
return Auth::user()->transactionjournals()->where('transaction_type_id', $dbType->id)->orderBy('id', 'DESC')->take(50)->get();
}
/**
* @param $type
*
* @return TransactionType
*/
public function getTransactionType($type)
{
return TransactionType::whereType($type)->first();
}
/** /**
* @param string $query * @param string $query
* @param TransactionJournal $journal * @param TransactionJournal $journal
@@ -128,7 +160,7 @@ class JournalRepository implements JournalRepositoryInterface
// store or get category // store or get category
if (strlen($data['category']) > 0) { if (strlen($data['category']) > 0) {
$category = Category::firstOrCreate(['name' => $data['category'], 'user_id' => $data['user']]); $category = Category::firstOrCreateEncrypted(['name' => $data['category'], 'user_id' => $data['user']]);
$journal->categories()->save($category); $journal->categories()->save($category);
} }
@@ -139,41 +171,7 @@ class JournalRepository implements JournalRepositoryInterface
} }
// store accounts (depends on type) // store accounts (depends on type)
switch ($transactionType->type) { list($from, $to) = $this->storeAccounts($transactionType, $data);
case 'Withdrawal':
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Deposit':
$to = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Transfer':
$from = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']);
break;
}
// store accompanying transactions. // store accompanying transactions.
Transaction::create( // first transaction. Transaction::create( // first transaction.
@@ -198,7 +196,6 @@ class JournalRepository implements JournalRepositoryInterface
} }
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param array $data * @param array $data
@@ -216,7 +213,7 @@ class JournalRepository implements JournalRepositoryInterface
// unlink all categories, recreate them: // unlink all categories, recreate them:
$journal->categories()->detach(); $journal->categories()->detach();
if (strlen($data['category']) > 0) { if (strlen($data['category']) > 0) {
$category = Category::firstOrCreate(['name' => $data['category'], 'user_id' => $data['user']]); $category = Category::firstOrCreateEncrypted(['name' => $data['category'], 'user_id' => $data['user']]);
$journal->categories()->save($category); $journal->categories()->save($category);
} }
@@ -228,41 +225,7 @@ class JournalRepository implements JournalRepositoryInterface
} }
// store accounts (depends on type) // store accounts (depends on type)
switch ($journal->transactionType->type) { list($from, $to) = $this->storeAccounts($journal->transactionType, $data);
case 'Withdrawal':
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Deposit':
$to = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreate(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreate(['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]);
}
break;
case 'Transfer':
$from = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']);
break;
}
// update the from and to transaction. // update the from and to transaction.
/** @var Transaction $transaction */ /** @var Transaction $transaction */
@@ -286,4 +249,64 @@ class JournalRepository implements JournalRepositoryInterface
return $journal; return $journal;
} }
/**
* @param TransactionType $type
* @param array $data
*
* @return array
*/
protected function storeAccounts(TransactionType $type, array $data)
{
$from = null;
$to = null;
switch ($type->type) {
case 'Withdrawal':
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
break;
case 'Deposit':
$to = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
break;
case 'Transfer':
$from = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']);
break;
}
if (is_null($to->id)) {
Log::error('"to"-account is null, so we cannot continue!');
App::abort(500, '"to"-account is null, so we cannot continue!');
}
if (is_null($from->id)) {
Log::error('"from"-account is null, so we cannot continue!');
App::abort(500, '"from"-account is null, so we cannot continue!');
}
return [$from, $to];
}
} }

View File

@@ -2,7 +2,9 @@
namespace FireflyIII\Repositories\Journal; namespace FireflyIII\Repositories\Journal;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@@ -12,6 +14,13 @@ use Illuminate\Support\Collection;
*/ */
interface JournalRepositoryInterface interface JournalRepositoryInterface
{ {
/**
* Get users first transaction journal
*
* @return TransactionJournal
*/
public function first();
/** /**
* *
* Get the account_id, which is the asset account that paid for the transaction. * Get the account_id, which is the asset account that paid for the transaction.
@@ -22,6 +31,13 @@ interface JournalRepositoryInterface
*/ */
public function getAssetAccount(TransactionJournal $journal); public function getAssetAccount(TransactionJournal $journal);
/**
* @param TransactionType $dbType
*
* @return Collection
*/
public function getJournalsOfType(TransactionType $dbType);
/** /**
* @param string $query * @param string $query
* @param TransactionJournal $journal * @param TransactionJournal $journal
@@ -30,6 +46,13 @@ interface JournalRepositoryInterface
*/ */
public function searchRelated($query, TransactionJournal $journal); public function searchRelated($query, TransactionJournal $journal);
/**
* @param $type
*
* @return TransactionType
*/
public function getTransactionType($type);
/** /**
* @param array $data * @param array $data
* *

View File

@@ -2,12 +2,10 @@
namespace FireflyIII\Repositories\PiggyBank; namespace FireflyIII\Repositories\PiggyBank;
use Amount;
use Auth; use Auth;
use Carbon\Carbon; use DB;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation; use Navigation;
@@ -19,6 +17,7 @@ use Navigation;
class PiggyBankRepository implements PiggyBankRepositoryInterface class PiggyBankRepository implements PiggyBankRepositoryInterface
{ {
/** /**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind. * @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
* *
@@ -88,6 +87,50 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return $part; return $part;
} }
/**
* @param PiggyBank $piggyBank
*
* @return Collection
*/
public function getEventSummarySet(PiggyBank $piggyBank)
{
return DB::table('piggy_bank_events')->where('piggy_bank_id', $piggyBank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]);
}
/**
* Set all piggy banks to order 0.
*
* @return void
*/
public function reset()
{
DB::table('piggy_banks')
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.id')
->where('accounts.user_id', Auth::user()->id)
->update(['order' => 0, 'piggy_banks.updated_at' => DB::Raw('NOW()')]);
}
/**
*
* set id of piggy bank.
*
* @param int $id
* @param int $order
*
* @return void
*/
public function setOrder($id, $order)
{
$piggyBank = PiggyBank::leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('accounts.user_id', Auth::user()->id)
->where('piggy_banks.id', $id)->first(['piggy_banks.*']);
if ($piggyBank) {
$piggyBank->order = $order;
$piggyBank->save();
}
}
/** /**
* @param array $data * @param array $data
* *
@@ -123,14 +166,10 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
$piggyBank->targetdate = $data['targetdate']; $piggyBank->targetdate = $data['targetdate'];
$piggyBank->reminder = $data['reminder']; $piggyBank->reminder = $data['reminder'];
$piggyBank->startdate = $data['startdate']; $piggyBank->startdate = $data['startdate'];
$piggyBank->rep_length = isset($data['rep_length']) ? $data['rep_length'] : null;
$piggyBank->rep_every = isset($data['rep_every']) ? $data['rep_every'] : null;
$piggyBank->rep_times = isset($data['rep_times']) ? $data['rep_times'] : null;
$piggyBank->remind_me = isset($data['remind_me']) && $data['remind_me'] == '1' ? 1 : 0; $piggyBank->remind_me = isset($data['remind_me']) && $data['remind_me'] == '1' ? 1 : 0;
$piggyBank->save(); $piggyBank->save();
return $piggyBank; return $piggyBank;
} }
} }

View File

@@ -4,9 +4,7 @@ namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Carbon\Carbon;
/** /**
* Interface PiggyBankRepositoryInterface * Interface PiggyBankRepositoryInterface
@@ -16,7 +14,6 @@ use Carbon\Carbon;
interface PiggyBankRepositoryInterface interface PiggyBankRepositoryInterface
{ {
/** /**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind. * @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
* *
@@ -37,6 +34,30 @@ interface PiggyBankRepositoryInterface
*/ */
public function createPiggyBankPart(array $data); public function createPiggyBankPart(array $data);
/**
* @param PiggyBank $piggyBank
*
* @return Collection
*/
public function getEventSummarySet(PiggyBank $piggyBank);
/**
* Set all piggy banks to order 0.
*
* @return void
*/
public function reset();
/**
*
* set id of piggy bank.
*
* @param int $id
* @param int $order
*
* @return void
*/
public function setOrder($id, $order);
/** /**

View File

@@ -2,10 +2,9 @@
namespace FireflyIII\Repositories\PiggyBank; namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Models\Reminder;
use FireflyIII\Models\PiggyBankRepetition;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder;
/** /**
* Class PiggyBankPart * Class PiggyBankPart

View File

@@ -5,6 +5,8 @@ namespace FireflyIII\Support;
use Cache; use Cache;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use Preferences as Prefs; use Preferences as Prefs;
/** /**
@@ -30,19 +32,27 @@ class Amount
} }
/** /**
* @param Transaction|\Transaction $transaction
* @param bool $coloured
*
* @return string * @return string
*/ */
public function formatTransaction(Transaction $transaction, $coloured = true) public function getCurrencySymbol()
{ {
$symbol = $transaction->transactionJournal->transactionCurrency->symbol; if (defined('FFCURRENCYSYMBOL')) {
$amount = floatval($transaction->amount); return FFCURRENCYSYMBOL;
}
if (\Cache::has('FFCURRENCYSYMBOL')) {
define('FFCURRENCYSYMBOL', \Cache::get('FFCURRENCYSYMBOL'));
return $this->formatWithSymbol($symbol, $amount, $coloured); return FFCURRENCYSYMBOL;
}
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
\Cache::forever('FFCURRENCYSYMBOL', $currency->symbol);
define('FFCURRENCYSYMBOL', $currency->symbol);
return $currency->symbol;
} }
/** /**
@@ -73,29 +83,64 @@ class Amount
return $symbol . ' ' . $string; return $symbol . ' ' . $string;
} }
/** /**
* @return string * @return string
*
* @param TransactionJournal $journal
*/ */
public function getCurrencySymbol() public function formatJournal(TransactionJournal $journal, $coloured = true)
{ {
if (defined('FFCURRENCYSYMBOL')) { $showPositive = true;
return FFCURRENCYSYMBOL; if (is_null($journal->symbol)) {
$symbol = $journal->transactionCurrency->symbol;
} else {
$symbol = $journal->symbol;
} }
if (\Cache::has('FFCURRENCYSYMBOL')) { $amount = 0;
define('FFCURRENCYSYMBOL', \Cache::get('FFCURRENCYSYMBOL'));
return FFCURRENCYSYMBOL; if (is_null($journal->type)) {
$type = $journal->transactionType->type;
} else {
$type = $journal->type;
} }
$currencyPreference = Prefs::get('currencyPreference', 'EUR'); if ($type == 'Withdrawal') {
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first(); $showPositive = false;
}
\Cache::forever('FFCURRENCYSYMBOL', $currency->symbol); foreach ($journal->transactions as $t) {
if (floatval($t->amount) > 0 && $showPositive === true) {
$amount = floatval($t->amount);
break;
}
if (floatval($t->amount) < 0 && $showPositive === false) {
$amount = floatval($t->amount);
}
}
define('FFCURRENCYSYMBOL', $currency->symbol); return $this->formatWithSymbol($symbol, $amount, $coloured);
}
return $currency->symbol; /**
* @param Transaction $transaction
* @param bool $coloured
*
* @return string
*/
public function formatTransaction(Transaction $transaction, $coloured = true)
{
$symbol = $transaction->transactionJournal->transactionCurrency->symbol;
$amount = floatval($transaction->amount);
return $this->formatWithSymbol($symbol, $amount, $coloured);
}
/**
* @return Collection
*/
public function getAllCurrencies()
{
return TransactionCurrency::orderBy('code', 'ASC')->get();
} }
/** /**

View File

@@ -2,13 +2,13 @@
namespace FireflyIII\Support; namespace FireflyIII\Support;
use Amount as Amt;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag; use Illuminate\Support\MessageBag;
use Input; use Input;
use Session; use Session;
use View; use View;
use Amount as Amt;
/** /**
* Class ExpandedForm * Class ExpandedForm
@@ -34,7 +34,7 @@ class ExpandedForm
$options['step'] = 'any'; $options['step'] = 'any';
$options['min'] = '0.01'; $options['min'] = '0.01';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency(); $defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = TransactionCurrency::orderBy('code', 'ASC')->get(); $currencies = Amt::getAllCurrencies();
$html = View::make('form.amount', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); $html = View::make('form.amount', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html; return $html;
@@ -62,8 +62,12 @@ class ExpandedForm
'account_id' => 'Asset account', 'account_id' => 'Asset account',
'budget_id' => 'Budget', 'budget_id' => 'Budget',
'openingBalance' => 'Opening balance', 'openingBalance' => 'Opening balance',
'virtualBalance' => 'Virtual balance',
'targetamount' => 'Target amount',
'accountRole' => 'Account role', 'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date', 'openingBalanceDate' => 'Opening balance date',
'ccType' => 'Credit card payment plan',
'ccMonthlyPaymentDate' => 'Credit card monthly payment date',
'piggy_bank_id' => 'Piggy bank']; 'piggy_bank_id' => 'Piggy bank'];
@@ -96,24 +100,14 @@ class ExpandedForm
public function getHolderClasses($name) public function getHolderClasses($name)
{ {
/* /*
* Get errors, warnings and successes from session: * Get errors from session:
*/ */
/** @var MessageBag $errors */ /** @var MessageBag $errors */
$errors = Session::get('errors'); $errors = Session::get('errors');
/** @var MessageBag $successes */
$successes = Session::get('successes');
switch (true) {
case (!is_null($errors) && $errors->has($name)):
$classes = 'form-group has-error has-feedback';
break;
case (!is_null($successes) && $successes->has($name)):
$classes = 'form-group has-success has-feedback';
break;
default:
$classes = 'form-group'; $classes = 'form-group';
break;
if (!is_null($errors) && $errors->has($name)) {
$classes = 'form-group has-error has-feedback';
} }
return $classes; return $classes;
@@ -153,7 +147,7 @@ class ExpandedForm
$value = $this->fillFieldValue($name, $value); $value = $this->fillFieldValue($name, $value);
$options['step'] = 'any'; $options['step'] = 'any';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency(); $defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
$currencies = TransactionCurrency::orderBy('code', 'ASC')->get(); $currencies = Amt::getAllCurrencies();
$html = View::make('form.balance', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); $html = View::make('form.balance', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html; return $html;
@@ -200,6 +194,24 @@ class ExpandedForm
return $html; return $html;
} }
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
*/
public function month($name, $value = null, array $options = [])
{
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$html = View::make('form.month', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/** /**
* @param $name * @param $name
* @param null $value * @param null $value

View File

@@ -344,6 +344,48 @@ class Navigation
throw new FireflyException('Cannot do startOfPeriod for $repeat_freq ' . $repeatFreq); throw new FireflyException('Cannot do startOfPeriod for $repeat_freq ' . $repeatFreq);
} }
/**
* @param Carbon $theDate
* @param $repeatFreq
* @param int $subtract
*
* @return Carbon
* @throws FireflyException
*/
public function subtractPeriod(Carbon $theDate, $repeatFreq, $subtract = 1)
{
$date = clone $theDate;
$functionMap = [
'daily' => 'subDays',
'week' => 'subWeeks',
'weekly' => 'subWeeks',
'month' => 'subMonths',
'monthly' => 'subMonths',
'year' => 'subYears',
'yearly' => 'subYears',
];
$modifierMap = [
'quarter' => 3,
'quarterly' => 3,
'half-year' => 6,
];
if (isset($functionMap[$repeatFreq])) {
$function = $functionMap[$repeatFreq];
$date->$function($subtract);
return $date;
}
if (isset($modifierMap[$repeatFreq])) {
$subtract = $subtract * $modifierMap[$repeatFreq];
$date->subMonths($subtract);
return $date;
}
throw new FireflyException('Cannot do subtractPeriod for $repeat_freq ' . $repeatFreq);
}
/** /**
* @param $range * @param $range
* @param Carbon $start * @param Carbon $start
@@ -414,47 +456,5 @@ class Navigation
throw new FireflyException('updateStartDate cannot handle $range ' . $range); throw new FireflyException('updateStartDate cannot handle $range ' . $range);
} }
/**
* @param Carbon $theDate
* @param $repeatFreq
* @param int $subtract
*
* @return Carbon
* @throws FireflyException
*/
public function subtractPeriod(Carbon $theDate, $repeatFreq, $subtract = 1)
{
$date = clone $theDate;
$functionMap = [
'daily' => 'subDays',
'week' => 'subWeeks',
'weekly' => 'subWeeks',
'month' => 'subMonths',
'monthly' => 'subMonths',
'year' => 'subYears',
'yearly' => 'subYears',
];
$modifierMap = [
'quarter' => 3,
'quarterly' => 3,
'half-year' => 6,
];
if (isset($functionMap[$repeatFreq])) {
$function = $functionMap[$repeatFreq];
$date->$function($subtract);
return $date;
}
if (isset($modifierMap[$repeatFreq])) {
$subtract = $subtract * $modifierMap[$repeatFreq];
$date->subMonths($subtract);
return $date;
}
throw new FireflyException('Cannot do subtractPeriod for $repeat_freq ' . $repeatFreq);
}
} }

View File

@@ -16,7 +16,7 @@ class Preferences
* @param $name * @param $name
* @param null $default * @param null $default
* *
* @return null|\Preference * @return null|Preference
*/ */
public function get($name, $default = null) public function get($name, $default = null)
{ {
@@ -37,7 +37,7 @@ class Preferences
* @param $name * @param $name
* @param $value * @param $value
* *
* @return \Preference * @return Preference
*/ */
public function set($name, $value) public function set($name, $value)
{ {

View File

@@ -3,11 +3,12 @@
namespace FireflyIII\Support\Search; namespace FireflyIII\Support\Search;
use Illuminate\Support\Collection; use Auth;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Collection;
/** /**
* Class Search * Class Search
@@ -23,7 +24,7 @@ class Search implements SearchInterface
*/ */
public function searchAccounts(array $words) public function searchAccounts(array $words)
{ {
return \Auth::user()->accounts()->with('accounttype')->where( return Auth::user()->accounts()->with('accounttype')->where(
function (EloquentBuilder $q) use ($words) { function (EloquentBuilder $q) use ($words) {
foreach ($words as $word) { foreach ($words as $word) {
$q->orWhere('name', 'LIKE', '%' . e($word) . '%'); $q->orWhere('name', 'LIKE', '%' . e($word) . '%');
@@ -40,7 +41,7 @@ class Search implements SearchInterface
public function searchBudgets(array $words) public function searchBudgets(array $words)
{ {
/** @var Collection $set */ /** @var Collection $set */
$set = \Auth::user()->budgets()->get(); $set = Auth::user()->budgets()->get();
$newSet = $set->filter( $newSet = $set->filter(
function (Budget $b) use ($words) { function (Budget $b) use ($words) {
$found = 0; $found = 0;
@@ -65,7 +66,7 @@ class Search implements SearchInterface
public function searchCategories(array $words) public function searchCategories(array $words)
{ {
/** @var Collection $set */ /** @var Collection $set */
$set = \Auth::user()->categories()->get(); $set = Auth::user()->categories()->get();
$newSet = $set->filter( $newSet = $set->filter(
function (Category $c) use ($words) { function (Category $c) use ($words) {
$found = 0; $found = 0;
@@ -101,12 +102,39 @@ class Search implements SearchInterface
*/ */
public function searchTransactions(array $words) public function searchTransactions(array $words)
{ {
return \Auth::user()->transactionjournals()->withRelevantData()->where( // decrypted transaction journals:
$decrypted = Auth::user()->transactionjournals()->withRelevantData()->where('encrypted', 0)->where(
function (EloquentBuilder $q) use ($words) { function (EloquentBuilder $q) use ($words) {
foreach ($words as $word) { foreach ($words as $word) {
$q->orWhere('description', 'LIKE', '%' . e($word) . '%'); $q->orWhere('description', 'LIKE', '%' . e($word) . '%');
} }
} }
)->get(); )->get();
// encrypted
$all = Auth::user()->transactionjournals()->withRelevantData()->where('encrypted', 1)->get();
$set = $all->filter(
function (TransactionJournal $journal) use ($words) {
foreach ($words as $word) {
$haystack = strtolower($journal->description);
$word = strtolower($word);
if (!(strpos($haystack, $word) === false)) {
return $journal;
}
}
}
);
$filtered = $set->merge($decrypted);
$filtered->sortBy(
function (TransactionJournal $journal) {
return intval($journal->date->format('U'));
}
);
$filtered = $filtered->reverse();
return $filtered;
} }
} }

View File

@@ -9,7 +9,8 @@ use Illuminate\Support\Collection;
* *
* @package FireflyIII\Support\Search * @package FireflyIII\Support\Search
*/ */
interface SearchInterface { interface SearchInterface
{
/** /**
* @param array $words * @param array $words
* *

View File

@@ -19,21 +19,74 @@ class Steam
* *
* @param Account $account * @param Account $account
* @param Carbon $date * @param Carbon $date
* @param bool $ignoreVirtualBalance
* *
* @return float * @return float
*/ */
public function balance(Account $account, Carbon $date = null) public function balance(Account $account, Carbon $date = null, $ignoreVirtualBalance = false)
{ {
$date = is_null($date) ? Carbon::now() : $date; $date = is_null($date) ? Carbon::now() : $date;
// find the first known transaction on this account:
//
$firstDateObject = $account
->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'ASC')->first(['transaction_journals.date']);
$firstDate = is_null($firstDateObject) ? clone $date : new Carbon($firstDateObject->date);
$date = $date < $firstDate ? $firstDate : $date;
/**
*select * from transactions
* left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
* order by date ASC
*/
$balance = floatval( $balance = floatval(
$account->transactions()->leftJoin( $account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
)->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount') )->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount')
); );
if (!$ignoreVirtualBalance) {
$balance += floatval($account->virtual_balance);
}
return $balance; return $balance;
} }
/**
* Only return the top X entries, group the rest by amount
* and described as 'Others'. id = 0 as well
*
* @param array $array
* @param int $limit
*
* @return array
*/
public function limitArray(array $array, $limit = 10)
{
$others = [
'name' => 'Others',
'queryAmount' => 0
];
$return = [];
$count = 0;
foreach ($array as $id => $entry) {
if ($count < ($limit - 1)) {
$return[$id] = $entry;
} else {
$others['queryAmount'] += $entry['queryAmount'];
}
$count++;
}
$return[0] = $others;
return $return;
}
/** /**
* Turns a collection into an array. Needs the field 'id' for the key, * Turns a collection into an array. Needs the field 'id' for the key,
* and saves only 'name' and 'amount' as a sub array. * and saves only 'name' and 'amount' as a sub array.
@@ -50,11 +103,15 @@ class Steam
$id = intval($entry->id); $id = intval($entry->id);
if (isset($array[$id])) { if (isset($array[$id])) {
$array[$id]['amount'] += floatval($entry->amount); $array[$id]['amount'] += floatval($entry->amount);
$array[$id]['queryAmount'] += floatval($entry->queryAmount);
$array[$id]['spent'] += floatval($entry->spent); $array[$id]['spent'] += floatval($entry->spent);
$array[$id]['encrypted'] = intval($entry->encrypted);
} else { } else {
$array[$id] = [ $array[$id] = [
'amount' => floatval($entry->amount), 'amount' => floatval($entry->amount),
'queryAmount' => floatval($entry->queryAmount),
'spent' => floatval($entry->spent), 'spent' => floatval($entry->spent),
'encrypted' => intval($entry->encrypted),
'name' => $entry->name 'name' => $entry->name
]; ];
} }
@@ -76,7 +133,7 @@ class Steam
foreach ($two as $id => $value) { foreach ($two as $id => $value) {
// $otherId also exists in $one: // $otherId also exists in $one:
if (isset($one[$id])) { if (isset($one[$id])) {
$one[$id]['amount'] += $value['amount']; $one[$id]['queryAmount'] += $value['queryAmount'];
$one[$id]['spent'] += $value['spent']; $one[$id]['spent'] += $value['spent'];
} else { } else {
$one[$id] = $value; $one[$id] = $value;
@@ -86,83 +143,6 @@ class Steam
return $one; return $one;
} }
/**
* Sort an array where all 'amount' keys are positive floats.
*
* @param array $array
*
* @return array
*/
public function sortArray(array $array)
{
uasort(
$array, function ($left, $right) {
if ($left['amount'] == $right['amount']) {
return 0;
}
return ($left['amount'] < $right['amount']) ? 1 : -1;
}
);
return $array;
}
/**
* Only return the top X entries, group the rest by amount
* and described as 'Others'. id = 0 as well
*
* @param array $array
* @param int $limit
*
* @return array
*/
public function limitArray(array $array, $limit = 10)
{
$others = [
'name' => 'Others',
'amount' => 0
];
$return = [];
$count = 0;
foreach ($array as $id => $entry) {
if ($count < ($limit - 1)) {
$return[$id] = $entry;
} else {
$others['amount'] += $entry['amount'];
}
$count++;
}
$return[0] = $others;
return $return;
}
/**
* Sort an array where all 'amount' keys are negative floats.
*
* @param array $array
*
* @return array
*/
public function sortNegativeArray(array $array)
{
uasort(
$array, function ($left, $right) {
if ($left['amount'] == $right['amount']) {
return 0;
}
return ($left['amount'] < $right['amount']) ? -1 : 1;
}
);
return $array;
}
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* @param PiggyBankRepetition $repetition * @param PiggyBankRepetition $repetition
@@ -181,4 +161,49 @@ class Steam
} }
} }
/**
* Sort an array where all 'amount' keys are positive floats.
*
* @param array $array
*
* @return array
*/
public function sortArray(array $array)
{
uasort(
$array, function ($left, $right) {
if ($left['queryAmount'] == $right['queryAmount']) {
return 0;
}
return ($left['queryAmount'] < $right['queryAmount']) ? 1 : -1;
}
);
return $array;
}
/**
* Sort an array where all 'amount' keys are negative floats.
*
* @param array $array
*
* @return array
*/
public function sortNegativeArray(array $array)
{
uasort(
$array, function ($left, $right) {
if ($left['queryAmount'] == $right['queryAmount']) {
return 0;
}
return ($left['queryAmount'] < $right['queryAmount']) ? -1 : 1;
}
);
return $array;
}
} }

View File

@@ -75,6 +75,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return $this->hasManyThrough('FireflyIII\Models\PiggyBank', 'FireflyIII\Models\Account'); return $this->hasManyThrough('FireflyIII\Models\PiggyBank', 'FireflyIII\Models\Account');
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
public function transactions()
{
return $this->hasManyThrough('FireflyIII\Models\Transaction', 'FireflyIII\Models\TransactionJournal');
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */

View File

@@ -4,8 +4,14 @@ namespace FireflyIII\Validation;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Config;
use Crypt;
use DB; use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
use Log;
use Navigation; use Navigation;
/** /**
@@ -25,6 +31,7 @@ class FireflyValidator extends Validator
*/ */
public function validateBelongsToUser($attribute, $value, $parameters) public function validateBelongsToUser($attribute, $value, $parameters)
{ {
$count = DB::table($parameters[0])->where('user_id', Auth::user()->id)->where('id', $value)->count(); $count = DB::table($parameters[0])->where('user_id', Auth::user()->id)->where('id', $value)->count();
if ($count == 1) { if ($count == 1) {
return true; return true;
@@ -34,6 +41,13 @@ class FireflyValidator extends Validator
} }
/**
* @param $attribute
* @param $value
* @param $parameters
*
* @return bool
*/
public function validatePiggyBankReminder($attribute, $value, $parameters) public function validatePiggyBankReminder($attribute, $value, $parameters)
{ {
$array = $this->data; $array = $this->data;
@@ -56,9 +70,78 @@ class FireflyValidator extends Validator
if ($nextReminder > $targetDate) { if ($nextReminder > $targetDate) {
return false; return false;
} }
return true; return true;
} }
/**
* @param $attribute
* @param $value
* @param $parameters
*
* @return bool
*/
public function validateUniqueAccountForUser($attribute, $value, $parameters)
{
$type = null;
/**
* Switch on different cases on which this method can respond:
*/
$hasWhat = isset($this->data['what']);
$hasAccountTypeId = isset($this->data['account_type_id']) && isset($this->data['name']);
$hasAccountId = isset($this->data['id']);
$ignoreId = 0;
if ($hasWhat) {
$search = Config::get('firefly.accountTypeByIdentifier.' . $this->data['what']);
$type = AccountType::whereType($search)->first();
// this field can be used to find the exact type, and continue.
}
if ($hasAccountTypeId) {
$type = AccountType::find($this->data['account_type_id']);
}
if ($hasAccountId) {
/** @var Account $account */
$account = Account::find($this->data['id']);
$ignoreId = intval($this->data['id']);
$type = AccountType::find($account->account_type_id);
unset($account);
}
/**
* Try to decrypt data just in case:
*/
try {
$value = Crypt::decrypt($value);
} catch (DecryptException $e) {
}
if (is_null($type)) {
Log::error('Could not determine type of account to validate.');
return false;
}
// get all accounts with this type, and find the name.
$userId = Auth::check() ? Auth::user()->id : 0;
$set = Account::where('account_type_id', $type->id)->where('id', '!=', $ignoreId)->where('user_id', $userId)->get();
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->name == $value) {
return false;
}
}
return true;
}
/** /**
* @param $attribute * @param $attribute
* @param $value * @param $value
@@ -69,6 +152,7 @@ class FireflyValidator extends Validator
public function validateUniqueForUser($attribute, $value, $parameters) public function validateUniqueForUser($attribute, $value, $parameters)
{ {
$query = DB::table($parameters[0])->where($parameters[1], $value); $query = DB::table($parameters[0])->where($parameters[1], $value);
$query->where('user_id', Auth::user()->id);
if (isset($paramers[2])) { if (isset($paramers[2])) {
$query->where('id', '!=', $parameters[2]); $query->where('id', '!=', $parameters[2]);
} }
@@ -80,5 +164,75 @@ class FireflyValidator extends Validator
return false; return false;
} }
/**
* Validate an object and its unicity. Checks for encryption / encrypted values as well.
*
* parameter 0: the table
* parameter 1: the field
* parameter 2: the encrypted / not encrypted boolean. Defaults to "encrypted".
* parameter 3: an id to ignore (when editing)
*
* @param $attribute
* @param $value
* @param $parameters
*
* @return bool
*/
public function validateUniqueObjectForUser($attribute, $value, $parameters)
{
$table = $parameters[0];
$field = $parameters[1];
$encrypted = isset($parameters[2]) ? $parameters[2] : 'encrypted';
$exclude = isset($parameters[3]) ? $parameters[3] : null;
$query = DB::table($table)->where('user_id', Auth::user()->id);
if (!is_null($exclude)) {
$query->where('id', '!=', $exclude);
}
$set = $query->get();
foreach ($set as $entry) {
$isEncrypted = intval($entry->$encrypted) == 1 ? true : false;
$checkValue = $isEncrypted ? Crypt::decrypt($entry->$field) : $entry->$field;
if ($checkValue == $value) {
return false;
}
}
return true;
}
/**
* @param $attribute
* @param $value
* @param $parameters
*
* @return bool
*/
public function validateUniquePiggyBankForUser($attribute, $value, $parameters)
{
$exclude = isset($parameters[0]) ? $parameters[0] : null;
$query = DB::table('piggy_banks');
$query->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id');
$query->where('accounts.user_id', Auth::user()->id);
if (!is_null($exclude)) {
$query->where('piggy_banks.id', '!=', $exclude);
}
$set = $query->get(['piggy_banks.*']);
foreach ($set as $entry) {
$isEncrypted = intval($entry->encrypted) == 1 ? true : false;
$checkValue = $isEncrypted ? Crypt::decrypt($entry->name) : $entry->name;
if ($checkValue == $value) {
return false;
}
}
return true;
}
} }

View File

@@ -11,8 +11,6 @@
| |
*/ */
use FireflyIII\Validation\FireflyValidator;
$app = new Illuminate\Foundation\Application( $app = new Illuminate\Foundation\Application(
realpath(__DIR__ . '/../') realpath(__DIR__ . '/../')
); );

View File

@@ -22,8 +22,8 @@
"require": { "require": {
"laravel/framework": "5.0.*", "laravel/framework": "5.0.*",
"davejamesmiller/laravel-breadcrumbs": "~3.0", "davejamesmiller/laravel-breadcrumbs": "~3.0",
"grumpydictator/gchart": "dev-master", "grumpydictator/gchart": "~1",
"watson/validating": "dev-master", "watson/validating": "~1.0",
"doctrine/dbal": "~2.5", "doctrine/dbal": "~2.5",
"illuminate/html": "~5.0", "illuminate/html": "~5.0",
"league/commonmark": "0.7.*" "league/commonmark": "0.7.*"
@@ -33,7 +33,11 @@
"barryvdh/laravel-ide-helper": "~2.0", "barryvdh/laravel-ide-helper": "~2.0",
"phpunit/phpunit": "~4.0", "phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1", "phpspec/phpspec": "~2.1",
"satooshi/php-coveralls": "0.6.1" "satooshi/php-coveralls": "0.6.1",
"mockery/mockery": "0.9.*",
"league/factory-muffin": "~2.1"
}, },
"autoload": { "autoload": {
"classmap": [ "classmap": [

686
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -136,8 +136,6 @@ return [
'Illuminate\Validation\ValidationServiceProvider', 'Illuminate\Validation\ValidationServiceProvider',
'Illuminate\View\ViewServiceProvider', 'Illuminate\View\ViewServiceProvider',
'Illuminate\Html\HtmlServiceProvider', 'Illuminate\Html\HtmlServiceProvider',
'Barryvdh\Debugbar\ServiceProvider',
'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
'DaveJamesMiller\Breadcrumbs\ServiceProvider', 'DaveJamesMiller\Breadcrumbs\ServiceProvider',
/* /*

View File

@@ -48,7 +48,7 @@ return [
'sqlite' => [ 'sqlite' => [
'driver' => 'sqlite', 'driver' => 'sqlite',
'database' => realpath(__DIR__ . '/../tests/_data/db.sqlite'), 'database' => ':memory:',
'prefix' => '', 'prefix' => '',
], ],

View File

@@ -19,7 +19,9 @@ return [
'accountRoles' => [ 'accountRoles' => [
'defaultAsset' => 'Default asset account', 'defaultAsset' => 'Default asset account',
'sharedAsset' => 'Shared asset account' 'sharedAsset' => 'Shared asset account',
'savingAsset' => 'Savings account',
'ccAsset' => 'Credit card',
], ],
'range_to_text' => [ 'range_to_text' => [
@@ -30,6 +32,9 @@ return [
'6M' => 'half year', '6M' => 'half year',
'custom' => '(custom)' 'custom' => '(custom)'
], ],
'ccTypes' => [
'monthlyFull' => 'Full payment every month'
],
'range_to_name' => [ 'range_to_name' => [
'1D' => 'one day', '1D' => 'one day',
'1W' => 'one week', '1W' => 'one week',
@@ -74,7 +79,7 @@ return [
[ [
'asset' => 'Asset account', 'asset' => 'Asset account',
'expense' => 'Expense account', 'expense' => 'Expense account',
'revenue' => 'Revenue account', 'revenue' => 'Revenue account'
], ],
'shortNamesByFullName' => 'shortNamesByFullName' =>
[ [

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