Compare commits

..

296 Commits
3.1.1 ... 3.1.6

Author SHA1 Message Date
James Cole
ca85cab6fb Merge branch 'release/3.1.6' 2014-12-06 22:29:17 +01:00
James Cole
742479bb01 Update for coveralls. 2014-12-06 22:12:02 +01:00
James Cole
68e54a9297 Include coveralls. 2014-12-06 22:04:29 +01:00
James Cole
056d83eda4 Fixed the codeception command line 2014-12-06 21:59:03 +01:00
James Cole
42ec55d0db Upgrade to php 5.6 2014-12-06 21:53:09 +01:00
James Cole
c89bd89d9a Include codeception in travis. 2014-12-06 21:52:39 +01:00
James Cole
31a0be5bb4 All kinds of cleanup. 2014-12-06 21:48:23 +01:00
James Cole
07610ae8fb All kinds of new stuff for Codeception, which isn't working at ALL 2014-12-06 19:47:43 +01:00
James Cole
dbc95dd878 Cleanup. 2014-12-06 17:53:25 +01:00
James Cole
792e8a9947 Start with code cleanup. 2014-12-06 17:45:29 +01:00
James Cole
d774cde109 Add soft deletes. 2014-12-06 17:37:05 +01:00
James Cole
cc111d14b0 Lots of cleanup and formatting. 2014-12-06 17:34:39 +01:00
James Cole
bb3ba42ce2 Replaced Ardent with another package. 2014-12-06 12:12:55 +01:00
James Cole
f4ecf2d1aa Some new report data. Also, the report page now uses in excess of 3000 queries. Lol 2014-12-06 09:16:54 +01:00
James Cole
1997666196 Update comment to match methods. 2014-12-05 22:50:26 +01:00
James Cole
3aa9057c5f Small fixes. 2014-12-05 22:06:33 +01:00
James Cole
834f8382e9 Added some todo's. 2014-12-05 21:45:48 +01:00
James Cole
357638a26c Also do some account meta on the create screen. 2014-12-05 21:44:01 +01:00
James Cole
919a0c01e4 Expand views and JS. 2014-12-05 21:39:48 +01:00
James Cole
6ff618e388 Expand models. 2014-12-05 21:39:34 +01:00
James Cole
ac765b7e4c Expand model to get meta data 2014-12-05 21:39:26 +01:00
James Cole
2134e87c31 Method to unrelate transactions. 2014-12-05 21:39:16 +01:00
James Cole
0d37288129 Expand account controller with meta data (edit / update routine). 2014-12-05 21:39:04 +01:00
James Cole
7cbd41137d New configuration. 2014-12-05 21:38:45 +01:00
James Cole
ba43d7063f New route to unrelate transactions. 2014-12-05 21:38:36 +01:00
James Cole
c9b2e29ba0 Manage related transactions. 2014-12-04 20:38:45 +01:00
James Cole
4720519aef Clean up models, fixed a bug. 2014-12-01 06:09:27 +01:00
James Cole
d7b0106e7d Expand code for reports. 2014-12-01 05:57:03 +01:00
James Cole
0a2cbaa047 Some new code, transaction groups among them. 2014-11-30 14:52:17 +01:00
James Cole
3e5f615ffc Expand bread crumbs to include opening balances. 2014-11-28 17:29:05 +01:00
James Cole
00dc73e6d9 Added all bread crumbs. 2014-11-28 16:35:11 +01:00
James Cole
aa5b9a1727 More bread crumbs. 2014-11-28 16:15:30 +01:00
James Cole
f63a287a6c More bread crumbs. 2014-11-28 16:09:18 +01:00
James Cole
b9d4c8dcd6 More bread crumbs. 2014-11-28 16:04:06 +01:00
James Cole
94433f1714 Bread crumbs for categories. 2014-11-28 15:58:54 +01:00
James Cole
a4dd4358b4 Small fix for budget bread crumbs. 2014-11-28 15:53:50 +01:00
James Cole
4e79b43395 More bread crumbs. 2014-11-28 15:52:52 +01:00
James Cole
6bb54bfa85 More bread crumbs. 2014-11-28 15:46:44 +01:00
James Cole
0df6c3a8dc Some work on bread crumbs and reports. 2014-11-28 15:41:32 +01:00
James Cole
dc25086eab Updated composer.json 2014-11-28 14:55:59 +01:00
James Cole
3299188edf New report 2014-11-28 14:41:58 +01:00
James Cole
f6afb46f6f Some new help functions, some cleanup. 2014-11-28 14:12:16 +01:00
James Cole
98993cfa9b Updated hasManyThrough 2014-11-28 07:40:59 +01:00
James Cole
5a920d5efd Fix the view for accounts. 2014-11-28 07:40:04 +01:00
James Cole
935276af88 Updated accounts so actions will trigger cache flush. 2014-11-27 16:20:16 +01:00
James Cole
5a505c8469 Show everything on demand. 2014-11-27 15:42:07 +01:00
James Cole
2c2abe8b8e Fixed a bug where editing an expense account wasn't actually possible. 2014-11-27 15:37:31 +01:00
James Cole
638099d989 Fixed a bug where editing an expense account wasn't actually possible. 2014-11-27 15:36:37 +01:00
James Cole
0f32f6be4c Fixed a bug where editing an expense account wasn't actually possible. 2014-11-27 15:35:51 +01:00
James Cole
1f7c98bdcf Installed codeception. 2014-11-26 21:31:12 +01:00
James Cole
72068a4b78 Fixed the preselected piggy bank 2014-11-26 21:11:13 +01:00
James Cole
c8038e0774 A test to see if transfers and transactions and what-not can be related somehow. 2014-11-26 21:07:14 +01:00
James Cole
6b39beecb4 Expanded the report thing. 2014-11-26 20:46:21 +01:00
James Cole
b1ba64db12 New and updated views. 2014-11-26 17:20:43 +01:00
James Cole
ad53832766 Fixed a ORM thing. 2014-11-26 17:20:32 +01:00
James Cole
d5bffc8ed7 Removed debug messages. 2014-11-26 17:20:23 +01:00
James Cole
69b36ddd1d New methods. 2014-11-26 17:20:18 +01:00
James Cole
9f926394a6 Some new reports. 2014-11-26 17:20:05 +01:00
James Cole
1e7ecbdf9d Removed debug messages. 2014-11-26 17:19:59 +01:00
James Cole
beb8a461cf Some new reports. 2014-11-26 17:19:50 +01:00
James Cole
e235a57e2f Fixed a bug where the paginator for transactions would skip pages. 2014-11-26 09:43:21 +01:00
James Cole
114b27079e Added a routine that will cache stuff that costs a lot of queries. 2014-11-25 22:04:50 +01:00
James Cole
6e19bc01f5 Because this function generates a lot of queries, i've disabled it for now. 2014-11-25 21:15:49 +01:00
James Cole
f9dfdeafb3 Fixed a bug where deposits and withdrawals might show the wrong accounts when editing. 2014-11-25 21:09:52 +01:00
James Cole
f05d626e38 Mostly cleanup and bug fixes. 2014-11-25 21:04:00 +01:00
James Cole
918041258e Removed cleanup method. 2014-11-25 20:47:33 +01:00
James Cole
a5b13aa67f A very simple tool to do some cleanup. 2014-11-25 20:44:46 +01:00
James Cole
1383cbd4d5 Fixed a bug where a null transaction would be used anyway. 2014-11-25 20:22:32 +01:00
James Cole
e9b7e82aea Some visual updates to repeated expenses. 2014-11-24 23:02:08 +01:00
James Cole
fd6e7fc1ab Fixed a bug where the associated total amount for a bar would be too high. 2014-11-24 18:08:20 +01:00
James Cole
743deb4227 More stuff for repeated expenses. 2014-11-24 18:06:21 +01:00
James Cole
b051278d2e Expand repeated expenses. 2014-11-24 17:01:37 +01:00
James Cole
bfda4bc199 Lots of new stuff. 2014-11-24 10:12:34 +01:00
James Cole
4456ef2326 Some expansion on the index of repeated expenses, and the first reference to the show page. 2014-11-23 06:29:29 +01:00
James Cole
7336367eff Duh. Bug fixed. 2014-11-22 23:38:58 +01:00
James Cole
9cb6c7697e Some debug information. 2014-11-22 23:38:08 +01:00
James Cole
cd44f51072 Add some floatvals() just in case. 2014-11-22 23:37:03 +01:00
James Cole
d8976379b1 Some debug information. 2014-11-22 23:35:39 +01:00
James Cole
8aa847c718 Expanded the view. 2014-11-22 23:34:38 +01:00
James Cole
6691b238f7 New routes for repeated expenses. 2014-11-22 23:32:34 +01:00
James Cole
cca2758138 New views for the repeated expenses. 2014-11-22 23:32:24 +01:00
James Cole
4a74e68e31 Cleanup and expansion to accomodate repeated expenses. 2014-11-22 23:32:10 +01:00
James Cole
3d3842b9d6 Reinstated "periodshow" and some other stuff. 2014-11-22 23:31:55 +01:00
James Cole
9d889d05e4 Expanded Events to check on repeated expenses. 2014-11-22 23:31:40 +01:00
James Cole
9f920bcfe3 Expanded the transaction controller to show repeated expenses 2014-11-22 23:31:25 +01:00
James Cole
1bb49fa496 Expanded the repeated expenses. 2014-11-22 23:31:06 +01:00
James Cole
6b1d8d3aaa Added the possibility to save per quarter. 2014-11-22 23:30:58 +01:00
James Cole
c6b6ed7fa8 Removed a bunch of methods and code that wasn't used. Also added a repository for repeated expenses. 2014-11-22 23:30:45 +01:00
James Cole
34454261d2 First view for the return of the repeated expenses. 2014-11-22 17:17:28 +01:00
James Cole
ddf9f52737 Mar cleanup. 2014-11-22 08:21:10 +01:00
James Cole
10b969a074 New cleanup command for artisan 2014-11-22 08:17:37 +01:00
James Cole
5df0634380 Cleanup. 2014-11-22 07:34:08 +01:00
James Cole
f0f965421c Implemented some todo's. 2014-11-22 07:30:46 +01:00
James Cole
6381408fba Remove some often used long calls with shorter ones. 2014-11-21 19:33:09 +01:00
James Cole
43e738cb44 Remove todo 2014-11-21 19:19:06 +01:00
James Cole
6d4303aa3f Clone Carbon. 2014-11-21 19:18:53 +01:00
James Cole
13c2db5378 Removed some todo's 2014-11-21 19:18:44 +01:00
James Cole
36f6bda525 Fixed a namespace bug. 2014-11-21 11:21:48 +01:00
James Cole
78886e7b1f Updated git ignore. 2014-11-21 11:18:02 +01:00
James Cole
3dce194930 Use facades. 2014-11-21 11:12:22 +01:00
James Cole
ec776bb6eb Add some hasManyThrough references 2014-11-21 11:11:52 +01:00
James Cole
243d942a6e Remove unused library 2014-11-21 11:11:14 +01:00
James Cole
ea6aba62c4 Remove unused code. 2014-11-21 11:11:03 +01:00
James Cole
73743721b1 Fixed a bug where a non-matching journal would keep its recurring transaction. 2014-11-20 11:36:53 +01:00
James Cole
61eb5b341d Update piggy banks repetitions when the piggy bank is edited. 2014-11-20 11:19:35 +01:00
James Cole
d758f72393 Better visualisation in the budget charts. 2014-11-20 11:15:00 +01:00
James Cole
1b1367f4c2 Make sure the progress bar has a minimal width to improve readability. 2014-11-20 11:10:42 +01:00
James Cole
07388dd58a Ability to dismiss reminders, added some logic to pre fill transaction form. 2014-11-20 07:51:01 +01:00
James Cole
359c71ef2f Fixed a bug where the reminders would be created inactive. 2014-11-20 07:41:48 +01:00
James Cole
93d4d3df1d Wrong column names. 2014-11-20 07:41:30 +01:00
James Cole
d3a7596be2 A fix for a bug that would prevent both the category and the budget to be updated. 2014-11-20 07:12:41 +01:00
James Cole
114d3812cc Some logging. 2014-11-19 21:22:21 +01:00
James Cole
b2d4dcfbf1 Also catch transaction journal updates. 2014-11-19 21:19:13 +01:00
James Cole
0baf8f6d18 First attempt at testing the home controller. 2014-11-19 21:18:29 +01:00
James Cole
2f8e3a0707 Display bug in recurring transactions. 2014-11-19 21:18:16 +01:00
James Cole
d7a4bf22c6 Scan transaction journals for recurring transactions on store. 2014-11-19 21:18:06 +01:00
James Cole
0c2f9d22b9 With no category, the transaction journal store procedure would fail. 2014-11-19 21:17:21 +01:00
James Cole
e75c5aac49 A fix for the phpunit coverage. 2014-11-19 21:16:48 +01:00
James Cole
77e5024f54 Fixed a display bug. I commented this out, but no idea why. 2014-11-19 21:16:23 +01:00
James Cole
7329c0b200 All of this should contain one working test. 2014-11-18 10:33:38 +01:00
James Cole
0afe3c48a1 Fixed some things so coveralls will fire. 2014-11-18 10:18:50 +01:00
James Cole
1ab5e923bc Removed codeception. 2014-11-18 10:11:31 +01:00
James Cole
90b3bd77e7 Empty tests. 2014-11-18 10:11:18 +01:00
James Cole
9c60443f97 Clean up and added some TODO's. 2014-11-18 09:47:50 +01:00
James Cole
058e5602a3 For consistency, renamed some events. 2014-11-18 09:41:43 +01:00
James Cole
eebac2a66d Some formatting, cleanup, and a new chart. 2014-11-18 09:37:54 +01:00
James Cole
7e8f5c9548 Expanded the selection of reminders. 2014-11-18 01:53:52 +01:00
James Cole
d34b49bd48 Fixed a bug where the end date would be incorrectly calculated. 2014-11-18 01:52:08 +01:00
James Cole
ccffae287d Fixed a bug where the end date would be incorrectly calculated. 2014-11-18 01:51:15 +01:00
James Cole
ad69011ac5 Support 'month' as well as 'monthly'. 2014-11-17 23:09:52 +01:00
James Cole
f8ea0f971d This should fix reminders! 2014-11-17 23:08:36 +01:00
James Cole
9918410954 Implemented a proper way of generating reminders (we hope). 2014-11-17 22:32:55 +01:00
James Cole
4f4e6fac16 Fixed a bug where limits and limit repetitions would not be updated and/or created because of double post calls and missing events. 2014-11-17 21:50:11 +01:00
James Cole
15e99bd672 Removed some old code, added todo's. 2014-11-17 20:55:31 +01:00
James Cole
696e9a6fde Some general cleaning up. 2014-11-17 20:45:55 +01:00
James Cole
36cbb3d71f Added some code for reminders but most of its commented out. 2014-11-17 20:18:14 +01:00
Sander Dorigo
f69598c6aa Lots of work on the reminders. 2014-11-17 16:14:28 +01:00
Sander Dorigo
5fc31f3c1e First attempt at generating and showing reminders. 2014-11-17 10:10:57 +01:00
Sander Dorigo
6581ee0ee0 Fixed some more menu stuff. 2014-11-17 09:40:04 +01:00
James Cole
69e7501d47 Start with cleaning up the menu. 2014-11-17 08:20:05 +01:00
James Cole
314abbea8b Code cleanup. 2014-11-17 07:33:18 +01:00
James Cole
82c9a75578 Expand piggy banks 2014-11-16 22:55:34 +01:00
James Cole
651101912c Route for chart. 2014-11-16 20:05:03 +01:00
James Cole
fb0d463040 Chart for piggy banks 2014-11-16 20:03:53 +01:00
James Cole
8c254554eb Display bug for piggy banks with no reminders. 2014-11-16 10:36:23 +01:00
James Cole
2d9c89375a New chart and lots of stuff for piggy banks 2014-11-16 10:31:19 +01:00
James Cole
61aba29df7 More stuffs. Too lazy to explain. 2014-11-15 11:36:27 +01:00
James Cole
8c949e6190 Events that keep track of piggy bank money add/remove 2014-11-15 09:32:25 +01:00
James Cole
6eb9188690 Cleaned up preferences view. 2014-11-15 09:23:07 +01:00
James Cole
6832f2ebd0 Lots of code cleanup. 2014-11-15 07:46:01 +01:00
James Cole
3e02b50ea1 All kinds of todo items because otherwise I'd forget them. 2014-11-14 19:58:01 +01:00
James Cole
7b7743c03e Fixed some bugs 2014-11-14 19:33:50 +01:00
James Cole
de20563275 Removed all event triggers. 2014-11-14 19:33:40 +01:00
James Cole
9e720c3a38 Some view fixes. 2014-11-14 14:33:41 +01:00
James Cole
54685c1f5f Some new lists for recurring transactions. 2014-11-14 12:54:49 +01:00
James Cole
eb8f8fa935 Expanded on categories. 2014-11-14 11:56:45 +01:00
James Cole
9adbbd872c New chart for budgets. 2014-11-14 11:43:08 +01:00
James Cole
4bd38f97a2 Whoops. But not to worry, changed it already. 2014-11-14 10:41:35 +01:00
James Cole
78ab1e200a Remove transactions now works. 2014-11-14 10:39:34 +01:00
James Cole
11280e473d Catch broken journals. 2014-11-14 10:37:19 +01:00
James Cole
f9750a64f8 Fixed transactions lists. 2014-11-14 10:28:18 +01:00
James Cole
ac2ab65471 Got up to categories with the new tables. 2014-11-14 10:17:12 +01:00
James Cole
0530c0402c First full transaction list (again) and removed some google table references. 2014-11-14 09:34:53 +01:00
James Cole
a58a560bbb Fixed the tables (again) for account and for index. 2014-11-14 09:07:13 +01:00
James Cole
96ab112b22 Removed most, if not all, references to Google Charts (the tables part). 2014-11-14 08:52:58 +01:00
James Cole
b388dcc7d4 First attempt at generating proper paging google tables. However, somehow they are kind of messed up, so I'm probably going to drop this. 2014-11-14 08:40:16 +01:00
James Cole
f511a25c94 Added the input when creating new transactions. 2014-11-13 21:27:50 +01:00
James Cole
58d8b6f95b Fixed the charts, added some todo items. 2014-11-13 21:11:00 +01:00
James Cole
953d68c3a2 Build some transaction handling. 2014-11-13 17:01:09 +01:00
James Cole
9e2f7af59b New route for table. 2014-11-13 16:14:07 +01:00
James Cole
840dfa6696 Expanded recurring transactions. 2014-11-13 16:13:32 +01:00
James Cole
4a20c008ff Implementing recurring transactions. 2014-11-13 11:17:39 +01:00
James Cole
981ffe4194 Added a show piggybank view, removed some others. 2014-11-13 07:50:55 +01:00
James Cole
2597633b0e New migration and code cleanup. 2014-11-13 07:25:47 +01:00
James Cole
71d174d765 Code clean up and reformat. 2014-11-12 22:37:44 +01:00
James Cole
4aa9a04516 Code clean up and reformat. 2014-11-12 22:37:09 +01:00
James Cole
258d6a1688 Code clean up and reformat. 2014-11-12 22:36:02 +01:00
James Cole
2e2c12d6a4 General clean up. 2014-11-12 22:21:48 +01:00
James Cole
44d189d7d3 Fixed a bug where the transaction list was a giant mess. 2014-11-12 21:19:31 +01:00
James Cole
ab508a3d9e Recreated the JSON controller to fix auto-complete forms. 2014-11-12 20:52:34 +01:00
James Cole
4e166c7d2e This should fix the transactions again. 2014-11-12 20:46:55 +01:00
Sander Dorigo
7f175a4870 Some more work on the transactions. 2014-11-12 15:34:32 +01:00
Sander Dorigo
0a627f6f9e More work done for the transaction controller. 2014-11-12 15:22:01 +01:00
Sander Dorigo
d34cc65984 Start cleaning up transactions controller. 2014-11-12 14:38:32 +01:00
Sander Dorigo
78d034d366 There's a giant mix brewing between "old" code, bad code and not implemented exceptions. I suspect the next change will be to cut out all old stuff, throw a lot of NotImplementedExceptions and get going. 2014-11-12 10:54:53 +01:00
James Cole
638fa9005f Excluded more files from the "old" libraries and included new ones instead. This should greatly clean up the code base. 2014-11-12 07:31:48 +01:00
James Cole
5cb9907bf8 Various changes to support the removed old code. 2014-11-11 21:09:56 +01:00
Sander Dorigo
f08fcc36fb Half-way through with some cleaning up. 2014-11-11 18:16:59 +01:00
Sander Dorigo
d231cd9f61 Added the homestead configuration because it's not really secret. 2014-11-11 07:35:00 +01:00
Sander Dorigo
86a586f866 Ignore homestead configuration. 2014-11-11 07:26:16 +01:00
Sander Dorigo
9d4cba1620 Fixed some initial startup bugs when working with Homestead. 2014-11-11 07:20:52 +01:00
Sander Dorigo
e9afd55e9d Updated the default user seeder. 2014-11-10 22:36:25 +01:00
Sander Dorigo
9fa326f630 First attempt at unifying code for categories and budgets, which are basically the same thing. 2014-11-10 21:55:22 +01:00
Sander Dorigo
af9473c126 Fixed some bugs in various controllers and started rebuilding the category controller. 2014-11-10 19:03:03 +01:00
Sander Dorigo
cb08df0770 I broke some stuff but it's fixed again. 2014-11-10 18:39:50 +01:00
Sander Dorigo
d49ca2eb11 Merge branch 'master' of https://github.com/JC5/firefly-iii 2014-11-10 18:39:06 +01:00
Sander Dorigo
a3f8841ec3 I have no idea what's happening! 2014-11-10 18:38:58 +01:00
Sander Dorigo
fd678c286d Some cleanup. 2014-11-10 18:37:25 +01:00
Sander Dorigo
2a3f9b621b Removed unused imports. 2014-11-10 18:36:25 +01:00
Sander Dorigo
359e1b3943 Cleanup all models and migrations. 2014-11-10 18:33:00 +01:00
Sander Dorigo
754336b3cf Merge branch 'develop' of https://github.com/JC5/firefly-iii 2014-11-10 11:40:16 +01:00
James Cole
4918f1c4cb Merge pull request #31 from sonikarc/develop
[Fixes #21] Change the majority of View::share to View::make()->with()
2014-11-10 10:37:49 +01:00
Sander Dorigo
5d5e308942 Updated read me. 2014-11-10 07:55:52 +01:00
Stewart Malik
6a18f81cec Merge branch 'develop' of https://github.com/sonikarc/firefly-iii into develop 2014-11-09 16:01:20 +00:00
Stewart Malik
1ff135d172 [Fixes #21] Change the majority of View::share to View::make()->with() 2014-11-10 02:27:19 +10:30
Sander Dorigo
e666e5e9e3 Clean up composer.json 2014-11-09 11:32:18 +01:00
Sander Dorigo
9874e77ddf Remove ignore instructions. 2014-11-09 11:30:00 +01:00
Sander Dorigo
27e3ec693a New screenshot. 2014-11-09 11:20:54 +01:00
Sander Dorigo
6bb1415ad7 Updated readme file. 2014-11-09 11:10:57 +01:00
Sander Dorigo
83594e6f1f Merge branch 'release/3.1.5' 2014-11-09 11:09:38 +01:00
Sander Dorigo
0f6008705c Added a screenshot 2014-11-09 11:08:53 +01:00
Sander Dorigo
c58b653bb7 Updated read me file. 2014-11-09 11:01:57 +01:00
Sander Dorigo
f69b6f9b4e New chart for budget-overview. 2014-11-09 08:42:09 +01:00
Sander Dorigo
7750b06476 Added a "spent"-bar in budgets. 2014-11-09 08:02:47 +01:00
Sander Dorigo
873384a34b Built the 'show'-view for budgets. 2014-11-08 19:11:51 +01:00
Sander Dorigo
ac299e7279 More rounding. 2014-11-08 11:38:50 +01:00
Sander Dorigo
7895d7f5d0 Format amount. 2014-11-08 11:37:55 +01:00
Sander Dorigo
fe05d218fc Allow piggy bank edit/update. 2014-11-08 11:36:20 +01:00
Sander Dorigo
8196313ac0 Optimized queries. 2014-11-08 10:16:12 +01:00
Sander Dorigo
6d8f84654f Also add stuff not in budgets. 2014-11-08 09:15:03 +01:00
Sander Dorigo
ab4f34a96b Extended reports 2014-11-07 22:06:30 +01:00
Sander Dorigo
139d985904 All kinds of fixes and things. I should really start organizing. 2014-11-07 11:18:06 +01:00
Sander Dorigo
44705f0e18 Some bugfixes and cleanup. 2014-11-06 20:33:37 +01:00
Sander Dorigo
ddea7d696a Delete and update routines. 2014-11-06 07:38:15 +01:00
Sander Dorigo
f814f45e36 Test fix for budgeting. 2014-11-05 21:37:24 +01:00
Sander Dorigo
f7117d47c2 Whoops, bugfix. 2014-11-05 21:23:23 +01:00
Sander Dorigo
01b0a1058d Form fix. 2014-11-05 21:22:31 +01:00
Sander Dorigo
21f362c7b9 Lots of stuff for budgets, accounts and others! 2014-11-05 19:57:56 +01:00
Sander Dorigo
aaab7f8e0e Some fixes for budgets 2014-11-04 20:37:00 +01:00
Sander Dorigo
09e1f68c69 Font fix. 2014-11-02 21:26:41 +01:00
Sander Dorigo
03729aa5ae Chart cleanup 2014-11-02 21:24:50 +01:00
Sander Dorigo
ef39f31ea1 First reports. 2014-11-02 18:46:01 +01:00
Sander Dorigo
0f1437dd6a Delete piggy banks. 2014-11-02 16:47:01 +01:00
Sander Dorigo
03aac2f744 Implemented method stub. 2014-11-02 14:59:09 +01:00
Sander Dorigo
2f8b10e82c All kinds of new code, especially for the piggy banks. 2014-11-02 14:58:12 +01:00
Sander Dorigo
3231effd20 Cleanup and fix everything related to piggy banks. 2014-10-31 07:32:43 +01:00
Sander Dorigo
f7722c1189 Clean up piggy bank controller. 2014-10-30 19:26:52 +01:00
Sander Dorigo
70c2450ac4 Clean up the routes. 2014-10-30 19:26:43 +01:00
Sander Dorigo
2d5b0d0f99 Moved some stuff around. 2014-10-30 19:26:28 +01:00
Sander Dorigo
f0c0002a6d Some cleanup 2014-10-30 18:24:10 +01:00
Sander Dorigo
dd9f08d4fa New code for charts. 2014-10-30 18:12:27 +01:00
Sander Dorigo
de2e384225 Merge branch 'feature/google-charts' into develop
Conflicts:
	app/views/accounts/show.blade.php
2014-10-30 18:09:03 +01:00
Sander Dorigo
ffcd1fde0f Even more charts and tables. 2014-10-30 18:06:29 +01:00
Sander Dorigo
d5e1da5948 Some more stats on the piggy banks. 2014-10-29 12:39:15 +01:00
Sander Dorigo
ad479a5c7f Added some placeholders. 2014-10-29 12:33:19 +01:00
Sander Dorigo
0707603b63 Add buttons and a placeholder. 2014-10-29 12:26:26 +01:00
Sander Dorigo
2f9c383004 All new stufs! 2014-10-29 10:30:52 +01:00
Sander Dorigo
8ad0d7af93 Update views and CSS 2014-10-28 16:29:31 +01:00
Sander Dorigo
9b4391c0bf Initial set of code required for GCharts. 2014-10-28 16:29:24 +01:00
Sander Dorigo
da7802a0a4 New controller, updated composer. 2014-10-28 16:15:16 +01:00
Sander Dorigo
9c69949e8c Added edit and new buttons. 2014-10-28 15:59:10 +01:00
Sander Dorigo
b1d7a9451a Cleaner piggybank view. 2014-10-28 11:25:22 +01:00
Sander Dorigo
004488d453 New todo items. 2014-10-28 11:10:40 +01:00
Sander Dorigo
fc91372dd0 Merge branch 'feature/account-cleanup' into develop 2014-10-28 09:33:54 +01:00
Sander Dorigo
5970a9dc91 Merge branch 'master' of https://github.com/JC5/firefly-iii into feature/account-cleanup 2014-10-28 09:32:22 +01:00
Sander Dorigo
264cac4f9b Merge branch 'develop' of https://github.com/JC5/firefly-iii into feature/account-cleanup 2014-10-28 09:32:16 +01:00
Sander Dorigo
633328a965 Removed references to sankey. 2014-10-28 09:29:47 +01:00
Sander Dorigo
4d4b62a766 Add todo-text 2014-10-28 09:28:14 +01:00
Sander Dorigo
aeb2c7deeb Remove function. 2014-10-28 09:27:46 +01:00
Sander Dorigo
c323942d92 Add placeholder. 2014-10-28 09:27:36 +01:00
Sander Dorigo
a0afa25145 Cleanup and prep-work for new charts (will be a new feature). 2014-10-28 09:25:29 +01:00
Sander Dorigo
4533b46436 Updated menu logic 2014-10-28 06:00:41 +01:00
Sander Dorigo
e5f8db78f9 New views and layouts for the account controller. 2014-10-28 05:59:14 +01:00
Sander Dorigo
899f61671f All new library set for the account controller (and others). 2014-10-28 05:58:48 +01:00
Sander Dorigo
d84d88cc10 Completely revamped the account controller. 2014-10-28 05:58:32 +01:00
Sander Dorigo
97e7ac4052 New routes for accounts. 2014-10-28 05:58:10 +01:00
Sander Dorigo
ba4ffa44d2 Fixed a spelling error. 2014-10-27 21:28:58 +01:00
Sander Dorigo
07caeccf68 Code formatting and a reference to a new form element. 2014-10-27 21:28:30 +01:00
Sander Dorigo
d54832f61f Code formatting. 2014-10-27 21:28:15 +01:00
Sander Dorigo
b212753633 A new form field. 2014-10-27 21:27:45 +01:00
James Cole
f38d80cbf5 Update composer.json 2014-10-23 16:42:11 +02:00
James Cole
8bea1acd8e Update composer.json 2014-10-23 16:42:01 +02:00
Sander Dorigo
42458ce11d Merge branch 'release/3.1.4' 2014-10-14 07:32:00 +02:00
Sander Dorigo
aceb683d07 Remove chart when nothing to show. 2014-10-14 07:27:06 +02:00
Sander Dorigo
b7517b49ed Expand match to include expense account. 2014-10-14 07:24:59 +02:00
Sander Dorigo
849b711b79 Recycle code instead of copy pasting. 2014-10-14 07:24:41 +02:00
Sander Dorigo
25585b28c7 Removed "date in the future" demand. 2014-10-14 07:24:27 +02:00
Sander Dorigo
073da8fb2a Small layout changes. 2014-10-13 20:47:36 +02:00
Sander Dorigo
a787ff3f3c Merge branch 'release/3.1.3' 2014-10-13 19:13:44 +02:00
Sander Dorigo
733b6d7eb7 Expand the chart. 2014-10-13 18:50:37 +02:00
Sander Dorigo
36d8dee853 Building a chart for the recurring transactions. 2014-10-13 17:54:20 +02:00
Sander Dorigo
65a2e07d24 Cleaned out most of the "reminders" code. 2014-10-12 09:45:57 +02:00
Sander Dorigo
7c97c558ab No reference to reminders. 2014-10-12 09:41:10 +02:00
Sander Dorigo
a6bb61050c Remove piggy bank reminders. 2014-10-12 09:41:00 +02:00
Sander Dorigo
b184aa2315 No trigger for recurring transactions. 2014-10-12 09:39:40 +02:00
Sander Dorigo
e4595333e7 Do not destroy reminders since there aren't any. 2014-10-12 09:39:29 +02:00
Sander Dorigo
41dd139bde Trigger no longer fires or creates reminders for piggy banks. 2014-10-12 09:37:31 +02:00
Sander Dorigo
c577dd302a Removed unused reminder methods. 2014-10-12 09:36:24 +02:00
Sander Dorigo
0ab87de78b Removed unused routes. 2014-10-12 09:34:47 +02:00
Sander Dorigo
8a22509b41 Removed a no longer used view. 2014-10-12 09:34:30 +02:00
Sander Dorigo
b024c18441 Cleaned out the reminder controller. 2014-10-12 09:34:10 +02:00
Sander Dorigo
d9ac681a68 Bug fixes in recurring transaction matching. 2014-10-12 09:31:25 +02:00
Sander Dorigo
637a5579ec Small cleanup. 2014-10-12 09:29:19 +02:00
Sander Dorigo
4794156e80 Merge branch 'master' into develop 2014-10-12 08:20:31 +02:00
Sander Dorigo
5f4db7874c New view and what-not for feature. 2014-10-12 08:19:18 +02:00
Sander Dorigo
b4ea1839a5 Fixed some bugs in the recurring transaction match. 2014-10-12 08:03:35 +02:00
319 changed files with 19960 additions and 10315 deletions

View File

@@ -1 +1,2 @@
src_dir: .
coverage_clover: tests/_output/coverage.xml

4
.gitignore vendored
View File

@@ -14,3 +14,7 @@ index.html*
app/storage/firefly-export*
.vagrant
firefly-iii-import-*.json
tests/_output/*
testing.sqlite
c3.php

View File

@@ -2,12 +2,14 @@ language: php
php:
- 5.5
- 5.4
- 5.6
- hhvm
install:
- composer install
after_script:
- php vendor/bin/coveralls
script:
- php vendor/bin/codecept run
after_script:
- php vendor/bin/coveralls

View File

@@ -1,4 +1,4 @@
firefly-iii
Firefly III
===========
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii)
@@ -9,6 +9,8 @@ firefly-iii
[![Latest Unstable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/unstable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![License](https://poser.pugx.org/grumpydictator/firefly-iii/license.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
Firefly II is a tool to help you manage your finances. Please read the full description [in the wiki](https://github.com/JC5/firefly-iii/wiki/full-description).
Firefly Mark III is a new version of Firefly built upon best practices and lessons learned
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.
@@ -28,7 +30,6 @@ Everything is organised:
- Clear views that should show you how you're doing;
- Easy navigation through your records;
- Browse back and forth to see previous months or even years;
- Lots of help text in case you don't get it;
- Lots of charts because we all love them.
## Changes
@@ -36,6 +37,7 @@ Everything is organised:
Firefly III will feature, but does not feature yet:
- Financial reporting showing you how well you are doing;
- Lots of help text in case you don't get it;
- More control over other resources outside of personal finance
- Accounts shared with a partner (household accounts)
- Debts
@@ -51,6 +53,16 @@ Some stuff has been removed:
- 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
![Index](http://i.imgur.com/TkZNIer.png)
![Accounts](http://i.imgur.com/YE8WavP.png)
![Budgets](http://i.imgur.com/Go0M6Nd.png)
![Reports](http://i.imgur.com/EnEIyQI.png)
## Current state
I have the basics up and running. Test coverage is currently non-existent.

View File

@@ -1,7 +1,363 @@
<?php
use Carbon\Carbon;
use DaveJamesMiller\Breadcrumbs\Generator;
use FireflyIII\Exception\FireflyException;
/*
* Back home.
*/
Breadcrumbs::register('home', function($breadcrumbs) {
$breadcrumbs->push('Home', route('index'));
});
Breadcrumbs::register(
'home',
function (Generator $breadcrumbs) {
$breadcrumbs->push('Home', route('index'));
}
);
// accounts
Breadcrumbs::register(
'accounts.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
$breadcrumbs->push(ucfirst($what) . ' accounts', route('accounts.index', $what));
}
);
Breadcrumbs::register(
'accounts.show', function (Generator $breadcrumbs, \Account $account) {
switch ($account->accountType->type) {
default:
throw new FireflyException('Cannot handle account type "' . e($account->accountType->type) . '"');
break;
case 'Default account':
case 'Asset account':
$what = 'asset';
break;
case 'Expense account':
case 'Beneficiary account':
$what = 'expense';
break;
case 'Revenue account':
$what = 'revenue';
break;
}
$breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push($account->name, route('accounts.show', $account->id));
}
);
Breadcrumbs::register(
'accounts.delete', function (Generator $breadcrumbs, \Account $account) {
$breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Delete ' . $account->name, route('accounts.delete', $account->id));
}
);
Breadcrumbs::register(
'accounts.edit', function (Generator $breadcrumbs, \Account $account) {
$breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Edit ' . $account->name, route('accounts.edit', $account->id));
}
);
// budgets.
Breadcrumbs::register(
'budgets.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Budgets', route('budgets.index'));
}
);
Breadcrumbs::register(
'budgets.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push('Create new budget', route('budgets.create'));
}
);
Breadcrumbs::register(
'budgets.edit', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Edit ' . $budget->name, route('budgets.edit', $budget->id));
}
);
Breadcrumbs::register(
'budgets.delete', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Delete ' . $budget->name, route('budgets.delete', $budget->id));
}
);
Breadcrumbs::register(
'budgets.show', function (Generator $breadcrumbs, Budget $budget, LimitRepetition $repetition = null) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push($budget->name, route('budgets.show', $budget->id));
if (!is_null($repetition)) {
$breadcrumbs->push(
DateKit::periodShow($repetition->startdate, $repetition->limit->repeat_freq), route('budgets.show', $budget->id, $repetition->id)
);
}
}
);
// categories
Breadcrumbs::register(
'categories.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Categories', route('categories.index'));
}
);
Breadcrumbs::register(
'categories.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push('Create new category', route('categories.create'));
}
);
Breadcrumbs::register(
'categories.edit', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Edit ' . $category->name, route('categories.edit', $category->id));
}
);
Breadcrumbs::register(
'categories.delete', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Delete ' . $category->name, route('categories.delete', $category->id));
}
);
Breadcrumbs::register(
'categories.show', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push($category->name, route('categories.show', $category->id));
}
);
// piggy banks
Breadcrumbs::register(
'piggybanks.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Piggy banks', route('piggybanks.index'));
}
);
Breadcrumbs::register(
'piggybanks.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('piggybanks.index');
$breadcrumbs->push('Create new piggy bank', route('piggybanks.create'));
}
);
Breadcrumbs::register(
'piggybanks.edit', function (Generator $breadcrumbs, Piggybank $piggybank) {
$breadcrumbs->parent('piggybanks.show', $piggybank);
$breadcrumbs->push('Edit ' . $piggybank->name, route('piggybanks.edit', $piggybank->id));
}
);
Breadcrumbs::register(
'piggybanks.delete', function (Generator $breadcrumbs, Piggybank $piggybank) {
$breadcrumbs->parent('piggybanks.show', $piggybank);
$breadcrumbs->push('Delete ' . $piggybank->name, route('piggybanks.delete', $piggybank->id));
}
);
Breadcrumbs::register(
'piggybanks.show', function (Generator $breadcrumbs, Piggybank $piggybank) {
$breadcrumbs->parent('piggybanks.index');
$breadcrumbs->push($piggybank->name, route('piggybanks.show', $piggybank->id));
}
);
// preferences
Breadcrumbs::register(
'preferences', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Preferences', route('preferences'));
}
);
// profile
Breadcrumbs::register(
'profile', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Profile', route('profile'));
}
);
Breadcrumbs::register(
'change-password', function (Generator $breadcrumbs) {
$breadcrumbs->parent('profile');
$breadcrumbs->push('Change your password', route('change-password'));
}
);
// recurring transactions
Breadcrumbs::register(
'recurring.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Recurring transactions', route('recurring.index'));
}
);
Breadcrumbs::register(
'recurring.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('recurring.index');
$breadcrumbs->push('Create new recurring transaction', route('recurring.create'));
}
);
Breadcrumbs::register(
'recurring.edit', function (Generator $breadcrumbs, RecurringTransaction $recurring) {
$breadcrumbs->parent('recurring.show', $recurring);
$breadcrumbs->push('Edit ' . $recurring->name, route('recurring.edit', $recurring->id));
}
);
Breadcrumbs::register(
'recurring.delete', function (Generator $breadcrumbs, RecurringTransaction $recurring) {
$breadcrumbs->parent('recurring.show', $recurring);
$breadcrumbs->push('Delete ' . $recurring->name, route('recurring.delete', $recurring->id));
}
);
Breadcrumbs::register(
'recurring.show', function (Generator $breadcrumbs, RecurringTransaction $recurring) {
$breadcrumbs->parent('recurring.index');
$breadcrumbs->push($recurring->name, route('recurring.show', $recurring->id));
}
);
// reminders
Breadcrumbs::register(
'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Reminder #' . $reminder->id, route('reminders.show', $reminder->id));
}
);
// 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 ' . $piggybank->name, route('repeated.edit', $piggybank->id));
}
);
Breadcrumbs::register(
'repeated.delete', function (Generator $breadcrumbs, Piggybank $piggybank) {
$breadcrumbs->parent('repeated.show', $piggybank);
$breadcrumbs->push('Delete ' . $piggybank->name, route('repeated.delete', $piggybank->id));
}
);
Breadcrumbs::register(
'repeated.show', function (Generator $breadcrumbs, Piggybank $piggybank) {
$breadcrumbs->parent('repeated.index');
$breadcrumbs->push($piggybank->name, route('repeated.show', $piggybank->id));
}
);
// reports
Breadcrumbs::register(
'reports.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Reports', route('reports.index'));
}
);
Breadcrumbs::register(
'reports.year', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push($date->format('Y'), route('reports.year', $date->format('Y')));
}
);
Breadcrumbs::register(
'reports.budgets', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push('Budgets in ' . $date->format('F Y'), route('reports.budgets', $date->format('Y')));
}
);
Breadcrumbs::register(
'reports.unbalanced', function (Generator $breadcrumbs, Carbon $date) {
$breadcrumbs->parent('reports.index');
$breadcrumbs->push('Unbalanced transactions in ' . $date->format('F Y'), route('reports.unbalanced', $date->format('Y')));
}
);
// search
Breadcrumbs::register(
'search', function (Generator $breadcrumbs, $query) {
$breadcrumbs->parent('home');
$breadcrumbs->push('Search for "' . e($query) . '"', route('search'));
}
);
// transactions
Breadcrumbs::register(
'transactions.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
switch ($what) {
case 'expenses':
case 'withdrawal':
$subTitle = 'Expenses';
break;
case 'revenue':
case 'deposit':
$subTitle = 'Revenue, income and deposits';
break;
case 'transfer':
case 'transfers':
$subTitle = 'Transfers';
break;
case 'opening balance':
$subTitle = 'Opening balances';
break;
default:
throw new FireflyException('Cannot handle $what "'.e($what).'" in bread crumbs');
}
$breadcrumbs->push($subTitle, route('transactions.index', $what));
}
);
Breadcrumbs::register(
'transactions.create', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('transactions.index', $what);
$breadcrumbs->push('Create new ' . $what, route('transactions.create', $what));
}
);
Breadcrumbs::register(
'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Edit ' . $journal->description, route('transactions.edit', $journal ->id));
}
);
Breadcrumbs::register(
'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Delete ' . $journal->description, route('transactions.delete', $journal->id));
}
);
Breadcrumbs::register(
'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type));
$breadcrumbs->push($journal->description, route('transactions.show', $journal->id));
}
);

77
app/commands/Cleanup.php Normal file
View File

@@ -0,0 +1,77 @@
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
class Cleanup extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clean caches, regenerate some stuff.';
/**
* The console command name.
*
* @var string
*/
protected $name = 'firefly:cleanup';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function fire()
{
$this->info('Start!');
Artisan::call('clear-compiled');
$this->info('Cleared compiled...');
Artisan::call('ide-helper:generate');
$this->info('IDE helper, done...');
Artisan::call('ide-helper:models', ['write']);
$this->info('IDE models, done...');
Artisan::call('optimize');
$this->info('Optimized...');
Artisan::call('dump-autoload');
$this->info('Done!');
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [
// ['example', InputArgument::REQUIRED, 'An example argument.'],
];
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
// ['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
];
}
}

View File

@@ -1,3 +1,4 @@
local/
laptop/
vagrant/
vagrant/
production/

View File

@@ -39,11 +39,9 @@ return [
'Illuminate\Workbench\WorkbenchServiceProvider',
'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
'Barryvdh\Debugbar\ServiceProvider',
'Firefly\Storage\StorageServiceProvider',
'Firefly\Helper\HelperServiceProvider',
'Firefly\Validation\ValidationServiceProvider',
'FireflyIII\FF3ServiceProvider',
'DaveJamesMiller\Breadcrumbs\ServiceProvider',
'TwigBridge\ServiceProvider'
'Grumpydictator\Gchart\GchartServiceProvider',
],
'manifest' => storage_path() . '/meta',
'aliases' => [
@@ -86,8 +84,7 @@ return [
'URL' => 'Illuminate\Support\Facades\URL',
'Validator' => 'Illuminate\Support\Facades\Validator',
'View' => 'Illuminate\Support\Facades\View',
'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade',
'Twig' => 'TwigBridge\Facade\Twig',
'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade'
],

View File

@@ -1,11 +1,15 @@
<?php
use Carbon\Carbon;
return [
'index_periods' => ['1D', '1W', '1M', '3M', '6M','1Y', 'custom'],
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
'piggybank_periods' => ['day', 'week', 'month', 'year'],
'periods_to_text' => [
'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'],
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
'piggybank_periods' => [
'week' => 'Week',
'month' => 'Month',
'quarter' => 'Quarter',
'year' => 'Year'
],
'periods_to_text' => [
'weekly' => 'A week',
'monthly' => 'A month',
'quarterly' => 'A quarter',
@@ -13,7 +17,12 @@ return [
'yearly' => 'A year',
],
'range_to_text' => [
'accountRoles' => [
'default' => 'Default expense account',
'sharedExpense' => 'Shared expense account'
],
'range_to_text' => [
'1D' => 'day',
'1W' => 'week',
'1M' => 'month',
@@ -21,15 +30,15 @@ return [
'6M' => 'half year',
'custom' => '(custom)'
],
'range_to_name' => [
'1D' => 'one day',
'1W' => 'one week',
'1M' => 'one month',
'3M' => 'three months',
'6M' => 'six months',
'1Y' => 'one year',
'range_to_name' => [
'1D' => 'one day',
'1W' => 'one week',
'1M' => 'one month',
'3M' => 'three months',
'6M' => 'six months',
'1Y' => 'one year',
],
'range_to_repeat_freq' => [
'range_to_repeat_freq' => [
'1D' => 'weekly',
'1W' => 'weekly',
'1M' => 'monthly',

View File

@@ -0,0 +1,15 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => true,
'log_level' => 'debug',
];

View File

@@ -0,0 +1,12 @@
<?php
return [
'driver' => 'file',
'path' => storage_path() . '/cache',
'connection' => null,
'table' => 'cache',
'memcached' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 100],
],
'prefix' => 'laravel',
];

View File

@@ -0,0 +1,47 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'homestead',
'username' => 'homestead',
'password' => 'secret',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
],
'pgsql' => [
'driver' => 'pgsql',
'host' => 'localhost',
'database' => 'homestead',
'username' => 'homestead',
'password' => 'secret',
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
],
],
];

View File

@@ -0,0 +1,13 @@
<?php
return [
'driver' => 'smtp',
'host' => 'smtp.gmail.com',
'port' => 587,
'from' => ['address' => '@gmail.com', 'name' => 'Firefly III'],
'encryption' => 'tls',
'username' => '@gmail.com',
'password' => '',
'sendmail' => '/usr/sbin/sendmail -bs',
'pretend' => false,
];

View File

@@ -2,14 +2,14 @@
use Illuminate\Support\Facades\Config;
return array(
return [
'enabled' => Config::get('app.debug'),
'enabled' => Config::get('app.debug'),
'storage' => array(
'storage' => [
'enabled' => true,
'path' => storage_path() . '/debugbar',
),
'path' => storage_path() . '/debugbar',
],
/*
|--------------------------------------------------------------------------
@@ -37,8 +37,8 @@ return array(
|
*/
'capture_ajax' => true,
'capture_ajax' => true,
/*
|--------------------------------------------------------------------------
| Capture Console Commands
@@ -59,7 +59,7 @@ return array(
|
*/
'collectors' => array(
'collectors' => [
'phpinfo' => true, // Php version
'messages' => true, // Messages
'time' => true, // Time Datalogger
@@ -78,7 +78,7 @@ return array(
'files' => false, // Show the included files
'config' => false, // Display config settings
'auth' => false, // Display Laravel authentication status
),
],
/*
|--------------------------------------------------------------------------
@@ -89,27 +89,27 @@ return array(
|
*/
'options' => array(
'auth' => array(
'options' => [
'auth' => [
'show_name' => false, // Also show the users name/email in the debugbar
),
'db' => array(
'with_params' => true, // Render SQL with the parameters substituted
'timeline' => false, // Add the queries to the timeline
),
'mail' => array(
],
'db' => [
'with_params' => true, // Render SQL with the parameters substituted
'timeline' => false, // Add the queries to the timeline
],
'mail' => [
'full_log' => false
),
'views' => array(
],
'views' => [
'data' => false, //Note: Can slow down the application, because the data can be quite large..
),
'route' => array(
],
'route' => [
'label' => true // show complete route on bar
),
'logs' => array(
],
'logs' => [
'file' => null
),
),
],
],
/*
|--------------------------------------------------------------------------
@@ -122,6 +122,6 @@ return array(
|
*/
'inject' => true,
'inject' => true,
);
];

View File

@@ -1,6 +1,6 @@
<?php
return array(
return [
/*
|--------------------------------------------------------------------------
@@ -11,7 +11,7 @@ return array(
|
*/
'filename' => '_ide_helper.php',
'filename' => '_ide_helper.php',
/*
|--------------------------------------------------------------------------
@@ -25,9 +25,9 @@ return array(
'include_helpers' => false,
'helper_files' => array(
base_path().'/vendor/laravel/framework/src/Illuminate/Support/helpers.php',
),
'helper_files' => [
base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php',
],
/*
|--------------------------------------------------------------------------
@@ -39,9 +39,9 @@ return array(
|
*/
'model_locations' => array(
'model_locations' => [
'app/models',
),
],
/*
@@ -53,14 +53,14 @@ return array(
|
*/
'extra' => array(
'Artisan' => array('Illuminate\Foundation\Artisan'),
'Eloquent' => array('Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'),
'Session' => array('Illuminate\Session\Store'),
),
'extra' => [
'Artisan' => ['Illuminate\Foundation\Artisan'],
'Eloquent' => ['Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'],
'Session' => ['Illuminate\Session\Store'],
],
'magic' => array(
'Log' => array(
'magic' => [
'Log' => [
'debug' => 'Monolog\Logger::addDebug',
'info' => 'Monolog\Logger::addInfo',
'notice' => 'Monolog\Logger::addNotice',
@@ -69,7 +69,8 @@ return array(
'critical' => 'Monolog\Logger::addCritical',
'alert' => 'Monolog\Logger::addAlert',
'emergency' => 'Monolog\Logger::addEmergency',
)
)
]
]
);
];

View File

@@ -1,5 +1,5 @@
<?php
return array(
return [
'view' => 'laravel-breadcrumbs::bootstrap3',
);
];

View File

@@ -0,0 +1,2 @@
<?php
return ['log_level' => 'debug',];

View File

@@ -1,6 +1,6 @@
<?php
return array(
return [
/*
|--------------------------------------------------------------------------
@@ -17,4 +17,4 @@ return array(
'driver' => 'array',
);
];

View File

@@ -4,8 +4,9 @@ return [
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => ':memory:',
'database' => 'tests/_data/testing.sqlite',
'prefix' => ''
],
]
]
];

View File

@@ -0,0 +1,13 @@
<?php
return [
'driver' => 'smtp',
'host' => '',
'port' => 587,
'from' => ['address' => '', 'name' => 'Firefly III'],
'encryption' => 'tls',
'username' => '',
'password' => '',
'sendmail' => '/usr/sbin/sendmail -bs',
'pretend' => true,
];

View File

@@ -1,6 +1,6 @@
<?php
return array(
return [
/*
|--------------------------------------------------------------------------
@@ -18,4 +18,4 @@ return array(
'driver' => 'array',
);
];

View File

@@ -1,6 +1,6 @@
<?php
return [
'paths' => array(__DIR__ . '/../views'),
'paths' => [__DIR__ . '/../views'],
'pagination' => 'pagination::slider-3',
];

View File

@@ -1,94 +1,44 @@
<?php
use Firefly\Helper\Controllers\AccountInterface as AI;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
* Class AccountController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class AccountController extends \BaseController
class AccountController extends BaseController
{
protected $_repository;
protected $_accounts;
/**
* @param ARI $repository
* @param AI $accounts
*
*/
public function __construct(ARI $repository, AI $accounts)
public function __construct()
{
$this->_accounts = $accounts;
$this->_repository = $repository;
View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', 'Accounts');
}
/**
* @param $what
*
* @return \Illuminate\View\View
*/
public function create($what)
{
switch ($what) {
case 'asset':
View::share('subTitleIcon', 'fa-money');
$subTitleIcon = 'fa-money';
break;
case 'expense':
View::share('subTitleIcon', 'fa-shopping-cart');
$subTitleIcon = 'fa-shopping-cart';
break;
case 'revenue':
View::share('subTitleIcon', 'fa-download');
$subTitleIcon = 'fa-download';
break;
}
$subTitle = 'Create a new ' . $what . ' account';
return View::make('accounts.create')->with('subTitle', 'Create a new ' . $what . ' account')->with(
'what', $what
);
}
/**
* @return $this
*/
public function asset()
{
View::share('subTitleIcon', 'fa-money');
$accounts = $this->_repository->getOfTypes(['Asset account', 'Default account']);
return View::make('accounts.asset')->with('subTitle', 'Asset accounts')->with(
'accounts', $accounts
);
}
/**
* @return $this
*/
public function expense()
{
View::share('subTitleIcon', 'fa-shopping-cart');
$accounts = $this->_repository->getOfTypes(['Expense account', 'Beneficiary account']);
return View::make('accounts.expense')->with('subTitle', 'Expense accounts')->with(
'accounts', $accounts
);
}
/**
* @return $this
*/
public function revenue()
{
View::share('subTitleIcon', 'fa-download');
$accounts = $this->_repository->getOfTypes(['Revenue account']);
return View::make('accounts.revenue')->with('subTitle', 'Revenue accounts')->with(
'accounts', $accounts
);
return View::make('accounts.create', compact('subTitleIcon', 'what', 'subTitle'));
}
/**
@@ -98,10 +48,9 @@ class AccountController extends \BaseController
*/
public function delete(Account $account)
{
return View::make('accounts.delete')->with('account', $account)
->with(
'subTitle', 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'
);
$subTitle = 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"';
return View::make('accounts.delete', compact('account', 'subTitle'));
}
/**
@@ -111,24 +60,29 @@ class AccountController extends \BaseController
*/
public function destroy(Account $account)
{
$type = $account->accountType->type;
$this->_repository->destroy($account);
Session::flash('success', 'The account was deleted.');
$name = $account->name;
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
$acct->destroy($account);
$return = 'asset';
switch ($type) {
case 'Asset account':
case 'Default account':
return Redirect::route('accounts.asset');
break;
case 'Expense account':
case 'Beneficiary account':
return Redirect::route('accounts.expense');
$return = 'expense';
break;
case 'Revenue account':
return Redirect::route('accounts.revenue');
$return = 'revenue';
break;
}
Session::flash('success', 'The ' . $return . ' account "' . e($name) . '" was deleted.');
return Redirect::route('accounts.index', $return);
}
/**
@@ -138,137 +92,249 @@ class AccountController extends \BaseController
*/
public function edit(Account $account)
{
$prefilled = [];
switch ($account->accountType->type) {
case 'Asset account':
case 'Default account':
View::share('subTitleIcon', 'fa-money');
$subTitleIcon = 'fa-money';
$prefilled['account_role'] = $account->getMeta('accountRole');
break;
case 'Expense account':
case 'Beneficiary account':
View::share('subTitleIcon', 'fa-shopping-cart');
$subTitleIcon = 'fa-shopping-cart';
break;
case 'Revenue account':
View::share('subTitleIcon', 'fa-download');
$subTitleIcon = 'fa-download';
break;
}
$openingBalance = $this->_accounts->openingBalanceTransaction($account);
return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance)
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
->with('subTitle', 'Edit ' . strtolower($account->accountType->type) . ' "' . $account->name . '"');
$openingBalance = $acct->openingBalanceTransaction($account);
Session::forget('prefilled');
if (!is_null($openingBalance)) {
$prefilled['openingbalancedate'] = $openingBalance->date->format('Y-m-d');
$prefilled['openingbalance'] = floatval($openingBalance->transactions()->where('account_id', $account->id)->first()->amount);
}
Session::flash('prefilled', $prefilled);
return View::make('accounts.edit', compact('account', 'openingBalance', 'subTitleIcon'))->with(
'subTitle', 'Edit ' . strtolower(
$account->accountType->type
) . ' "' . $account->name . '"'
);
}
/**
* @return $this
* @param string $what
*
* @return View
* @throws FireflyException
*/
public function index()
public function index($what = 'default')
{
return View::make('error')->with('message', 'This view has been disabled');
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
switch ($what) {
default:
throw new FireflyException('Cannot handle account type "' . e($what) . '".');
break;
case 'asset':
$subTitleIcon = 'fa-money';
$subTitle = 'Asset accounts';
$accounts = $acct->getAssetAccounts();
break;
case 'expense':
$subTitleIcon = 'fa-shopping-cart';
$subTitle = 'Expense accounts';
$accounts = $acct->getExpenseAccounts();
break;
case 'revenue':
$subTitleIcon = 'fa-download';
$subTitle = 'Revenue accounts';
$accounts = $acct->getRevenueAccounts();
break;
}
$accounts->each(
function (Account $account) {
if (Cache::has('account.' . $account->id . '.lastActivityDate')) {
$account->lastActionDate = Cache::get('account.' . $account->id . '.lastActivityDate');
} else {
$transaction = $account->transactions()->orderBy('updated_at', 'DESC')->first();
if (is_null($transaction)) {
$account->lastActionDate = null;
Cache::forever('account.' . $account->id . '.lastActivityDate', 0);
} else {
$account->lastActionDate = $transaction->updated_at;
Cache::forever('account.' . $account->id . '.lastActivityDate', $transaction->updated_at);
}
}
}
);
return View::make('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts'));
}
/**
* @param Account $account
* @param string $view
*
* @return $this
*/
public function show(Account $account)
public function show(Account $account, $view = 'session')
{
switch ($account->accountType->type) {
case 'Asset account':
case 'Default account':
View::share('subTitleIcon', 'fa-money');
$subTitleIcon = 'fa-money';
$what = 'asset';
break;
case 'Expense account':
case 'Beneficiary account':
View::share('subTitleIcon', 'fa-shopping-cart');
$subTitleIcon = 'fa-shopping-cart';
$what = 'expense';
break;
case 'Revenue account':
View::share('subTitleIcon', 'fa-download');
$subTitleIcon = 'fa-download';
$what = 'revenue';
break;
}
// get a paginated view of all transactions for this account:
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
switch ($view) {
default:
case 'session':
$journals = $acct->getTransactionJournals($account, 50);
break;
case 'all':
$journals = $acct->getAllTransactionJournals($account, 50);
break;
}
$data = $this->_accounts->show($account, 40);
return View::make('accounts.show')->with('account', $account)->with('show', $data)->with(
'subTitle',
'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'
return View::make('accounts.show', compact('account', 'what', 'view', 'subTitleIcon', 'journals'))->with('account', $account)->with(
'subTitle', 'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'
);
}
/**
* @return $this|\Illuminate\Http\RedirectResponse
* @throws FireflyException
*/
public function store()
{
$data = Input::all();
$data['what'] = isset($data['what']) && $data['what'] != '' ? $data['what'] : 'asset';
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
switch ($data['what']) {
switch ($data['post_submit_action']) {
default:
case 'asset':
$data['account_type'] = 'Asset account';
break;
case 'expense':
$data['account_type'] = 'Expense account';
break;
case 'revenue':
$data['account_type'] = 'Revenue account';
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'return_to_edit':
case 'store':
$messages = $acct->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save account: ' . $messages['errors']->first());
}
$account = $this->_repository->store($data);
return Redirect::route('accounts.create', $data['what'])->withInput()->withErrors($messages['errors']);
}
// store!
$acct->store($data);
Session::flash('success', 'New account stored!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('accounts.create', $data['what']);
} else {
return Redirect::route('accounts.index', $data['what']);
}
break;
case 'validate_only':
$messageBags = $acct->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
if ($account->validate()) {
// saved! return to wherever.
Session::flash('success', 'Account "' . $account->name . '" created!');
if (intval(Input::get('create')) === 1) {
return Redirect::route('accounts.create', $data['what'])->withInput();
} else {
return Redirect::route('accounts.' . e($data['what']));
}
} else {
// did not save, return with error:
Session::flash('error', 'Could not save the new account: ' . $account->errors()->first());
return Redirect::route('accounts.create', $data['what'])->withErrors($account->errors())->withInput();
break;
}
}
/**
* @param Account $account
*
* @return $this|\Illuminate\Http\RedirectResponse
* @return $this
* @throws FireflyException
*/
public function update(Account $account)
{
/** @var \Account $account */
$account = $this->_repository->update($account, Input::all());
if ($account->validate()) {
Session::flash('success', 'Account "' . $account->name . '" updated.');
switch ($account->accountType->type) {
case 'Asset account':
case 'Default account':
return Redirect::route('accounts.asset');
break;
case 'Expense account':
case 'Beneficiary account':
return Redirect::route('accounts.expense');
break;
case 'Revenue account':
return Redirect::route('accounts.revenue');
break;
}
} else {
Session::flash('error', 'Could not update account: ' . $account->errors()->first());
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
$data = Input::except('_token');
return Redirect::route('accounts.edit', $account->id)->withInput()->withErrors($account->errors());
switch ($account->accountType->type) {
default:
throw new FireflyException('Cannot handle account type "' . e($account->accountType->type) . '"');
break;
case 'Default account':
case 'Asset account':
$data['what'] = 'asset';
break;
case 'Expense account':
case 'Beneficiary account':
$data['what'] = 'expense';
break;
case 'Revenue account':
$data['what'] = 'revenue';
break;
}
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'create_another':
case 'update':
$messages = $acct->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save account: ' . $messages['errors']->first());
return Redirect::route('accounts.edit', $account->id)->withInput()->withErrors($messages['errors']);
}
// store!
$acct->update($account, $data);
Session::flash('success', 'Account updated!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('accounts.edit', $account->id);
} else {
return Redirect::route('accounts.index', $data['what']);
}
case 'validate_only':
$messageBags = $acct->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('accounts.edit', $account->id)->withInput();
break;
}
}

View File

@@ -1,75 +1,79 @@
<?php
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Firefly\Helper\Controllers\BudgetInterface as BI;
use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
* Class BudgetController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*
*/
class BudgetController extends BaseController
{
protected $_budgets;
protected $_repository;
/**
* @param BI $budgets
* @param BRI $repository
*
*/
public function __construct(BI $budgets, BRI $repository)
public function __construct()
{
$this->_budgets = $budgets;
$this->_repository = $repository;
View::share('title', 'Budgets');
View::share('mainTitleIcon', 'fa-tasks');
}
public function nobudget($view = 'session') {
switch($view) {
default:
throw new FireflyException('Cannot show transactions without a budget for view "'.$view.'".');
break;
case 'session':
$start = Session::get('start');
$end = Session::get('end');
break;
/**
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
* @throws Exception
*/
public function amount(Budget $budget)
{
$amount = intval(Input::get('amount'));
$date = Session::get('start');
/** @var \Limit $limit */
$limit = $budget->limits()->where('startdate', $date->format('Y-m-d'))->first();
if (!$limit) {
// create one!
$limit = new Limit;
$limit->budget()->associate($budget);
$limit->startdate = $date;
$limit->amount = $amount;
$limit->repeat_freq = 'monthly';
$limit->repeats = 0;
$limit->save();
/*
* A newly stored limit also created a limit repetition.
*/
Event::fire('limits.store', [$limit]);
} else {
if ($amount > 0) {
$limit->amount = $amount;
$limit->save();
/*
* An updated limit also updates the associated limit repetitions.
*/
Event::fire('limits.update', [$limit]);
} else {
$limit->delete();
}
}
// try to find the limit repetition for this limit:
$repetition = $limit->limitrepetitions()->first();
if ($repetition) {
return Response::json(['name' => $budget->name, 'repetition' => $repetition->id]);
} else {
return Response::json(['name' => $budget->name, 'repetition' => null]);
}
// Add expenses that have no budget:
$set = \Auth::user()->transactionjournals()->whereNotIn(
'transaction_journals.id', function ($query) use ($start, $end) {
$query->select('transaction_journals.id')->from('transaction_journals')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('components.class', 'Budget');
}
)->before($end)->after($start)->get();
return View::make('budgets.nobudget')
->with('view', $view)
->with('transactions',$set)
->with('subTitle', 'Transactions without a budget');
}
/**
* @return $this|\Illuminate\View\View
* @return $this
*/
public function create()
{
$periods = \Config::get('firefly.periods_to_text');
return View::make('budgets.create')->with('periods', $periods)->with('subTitle', 'Create a new budget');
return View::make('budgets.create')->with('subTitle', 'Create a new budget');
}
/**
@@ -79,8 +83,7 @@ class BudgetController extends BaseController
*/
public function delete(Budget $budget)
{
return View::make('budgets.delete')->with('budget', $budget)
->with('subTitle', 'Delete budget "' . $budget->name . '"');
return View::make('budgets.delete')->with('budget', $budget)->with('subTitle', 'Delete budget "' . $budget->name . '"');
}
/**
@@ -90,16 +93,13 @@ class BudgetController extends BaseController
*/
public function destroy(Budget $budget)
{
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
// remove budget
Event::fire('budgets.destroy', [$budget]); // just before deletion.
$this->_repository->destroy($budget);
$repos->destroy($budget);
Session::flash('success', 'The budget was deleted.');
// redirect:
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
}
return Redirect::route('budgets.index.budget');
return Redirect::route('budgets.index');
}
@@ -110,142 +110,222 @@ class BudgetController extends BaseController
*/
public function edit(Budget $budget)
{
return View::make('budgets.edit')->with('budget', $budget)
->with('subTitle', 'Edit budget "' . $budget->name . '"');
Session::flash('prefilled', ['name' => $budget->name]);
}
/**
* @return $this|\Illuminate\View\View
*/
public function indexByBudget()
{
View::share('subTitleIcon', 'fa-folder-open');
$budgets = $this->_repository->get();
return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', new Carbon)
->with('subTitle', 'Grouped by budget');
return View::make('budgets.edit')->with('budget', $budget)->with('subTitle', 'Edit budget "' . $budget->name . '"');
}
/**
* @return $this
*/
public function indexByDate()
public function index()
{
View::share('subTitleIcon', 'fa-calendar');
// get a list of dates by getting all repetitions:
$set = $this->_repository->get();
$budgets = $this->_budgets->organizeByDate($set);
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
return View::make('budgets.indexByDate')->with('budgets', $budgets)
->with('subTitle', 'Grouped by date');
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$budgets = $repos->get();
// get the limits for the current month.
$date = \Session::get('start');
$spent = 0;
/** @var \Budget $budget */
foreach ($budgets as $budget) {
}
$budget->spent = $repos->spentInMonth($budget, $date);
$spent += $budget->spent;
$budget->pct = 0;
$budget->limit = 0;
/**
* Three use cases for this view:
*
* - Show everything.
* - Show a specific repetition.
* - Show everything shows NO repetition.
*
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return int
*/
public function show(Budget $budget, \LimitRepetition $repetition = null)
{
$useSessionDates = Input::get('useSession') == 'true' ? true : false;
$view = null;
$title = null;
\Log::debug('Is envelope true? ' . (Input::get('noenvelope') == 'true'));
switch (true) {
case (!is_null($repetition)):
$data = $this->_budgets->organizeRepetition($repetition);
$view = 1;
$title = $budget->name . ', ' . $repetition->periodShow() . ', ' . mf(
$repetition->limit->amount,
false
);
break;
case (Input::get('noenvelope') == 'true'):
$data = $this->_budgets->outsideRepetitions($budget);
$view = 2;
$title = $budget->name . ', transactions outside an envelope';
break;
default:
$data = $this->_budgets->organizeRepetitions($budget, $useSessionDates);
$view = $useSessionDates ? 3 : 4;
$title = $useSessionDates ? $budget->name . ' in session period' : $budget->name;
break;
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
/** @var \LimitRepetition $repetition */
foreach ($limit->limitrepetitions as $repetition) {
if ($repetition->startdate == $date) {
$budget->currentRep = $repetition;
$budget->limit = floatval($repetition->amount);
if ($budget->limit > $budget->spent) {
// not overspent:
$budget->pct = 30;
} else {
$budget->pct = 50;
}
}
}
}
}
return View::make('budgets.show')
->with('budget', $budget)
->with('repetitions', $data)
->with('view', $view)
->with('highlight', Input::get('highlight'))
->with('useSessionDates', $useSessionDates)
->with('subTitle', 'Overview for ' . $title);
$budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000);
$amount = floatval($budgetAmount->data);
$overspent = $spent > $amount;
if ($overspent) {
// overspent on total amount
$spentPCT = ceil($amount / $spent * 100);
} else {
// not overspent on total amount.
$spentPCT = ceil($spent / $amount * 100);
}
return View::make('budgets.index', compact('budgets', 'spent', 'spentPCT', 'overspent'))->with('budgetAmount', $budgetAmount);
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function postUpdateIncome()
{
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
$date = Session::get('start');
$value = intval(Input::get('amount'));
$preferences->set('budgetIncomeTotal' . $date->format('FY'), $value);
return Redirect::route('budgets.index');
}
/**
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Illuminate\View\View
*/
public function show(Budget $budget, LimitRepetition $repetition = null)
{
if (!is_null($repetition) && $repetition->limit->budget->id != $budget->id) {
App::abort(500);
}
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
if (is_null($repetition)) {
// get all other repetitions:
$limits = $budget->limits()->orderBy('startdate', 'DESC')->get();
// get all transaction journals for this budget.
$journals = $repos->getTransactionJournals($budget, 50);
$subTitle = $budget->name;
} else {
// get nothing? i dunno
$limits = [$repetition->limit];
// get all transaction journals for this budget and limit repetition.
$subTitle = $budget->name . ' in ' . $repetition->startdate->format('F Y');
$journals = $repos->getTransactionJournalsInRepetition($budget, $repetition, 50);
}
$hideBudget = true;
return View::make('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle', 'hideBudget'));
}
/**
* @return $this
* @throws FireflyException
*/
public function store()
{
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$data = Input::except('_token');
$budget = $this->_repository->store(Input::all());
if ($budget->validate()) {
Event::fire('budgets.store', [$budget]);
Session::flash('success', 'Budget created!');
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save budget: ' . $messages['errors']->first());
if (Input::get('create') == '1') {
return Redirect::route('budgets.create', ['from' => Input::get('from')]);
}
return Redirect::route('budgets.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New budget stored!');
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
} else {
return Redirect::route('budgets.index.budget');
}
} else {
Session::flash('error', 'Could not save the new budget');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('budgets.create');
} else {
return Redirect::route('budgets.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('budgets.create')->withInput()->withErrors($budget->errors());
return Redirect::route('budgets.create')->withInput();
break;
}
}
/**
* @param Budget $budget
*
* @return $this|\Illuminate\Http\RedirectResponse
* @return $this
* @throws FireflyException
*/
public function update(Budget $budget)
{
$budget = $this->_repository->update($budget, Input::all());
if ($budget->validate()) {
Event::fire('budgets.update', [$budget]);
Session::flash('success', 'Budget "' . $budget->name . '" updated.');
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
} else {
return Redirect::route('budgets.index.budget');
}
} else {
Session::flash('error', 'Could not update budget: ' . $budget->errors()->first());
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
$data = Input::except('_token');
return Redirect::route('budgets.edit', $budget->id)->withInput()->withErrors($budget->errors());
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'return_to_edit':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save budget: ' . $messages['errors']->first());
return Redirect::route('budgets.edit', $budget->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($budget, $data);
Session::flash('success', 'Budget updated!');
if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('budgets.edit', $budget->id);
} else {
return Redirect::route('budgets.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('budgets.edit', $budget->id)->withInput();
break;
}
}
/**
* @return $this
*/
public function updateIncome()
{
$date = Session::get('start');
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
$budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000);
}
return View::make('budgets.income')->with('amount', $budgetAmount)->with('date', $date);
}
}

View File

@@ -1,27 +1,18 @@
<?php
use Firefly\Helper\Controllers\CategoryInterface as CI;
use Firefly\Storage\Category\CategoryRepositoryInterface as CRI;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
* Class CategoryController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class CategoryController extends BaseController
{
protected $_repository;
protected $_category;
/**
* @param CRI $repository
* @param CI $category
*
*/
public function __construct(CRI $repository, CI $category)
public function __construct()
{
$this->_repository = $repository;
$this->_category = $category;
View::share('title','Categories');
View::share('title', 'Categories');
View::share('mainTitleIcon', 'fa-bar-chart');
}
@@ -40,8 +31,7 @@ class CategoryController extends BaseController
*/
public function delete(Category $category)
{
return View::make('categories.delete')->with('category', $category)
->with('subTitle', 'Delete category "' . $category->name . '"');
return View::make('categories.delete')->with('category', $category)->with('subTitle', 'Delete category "' . $category->name . '"');
}
/**
@@ -51,8 +41,12 @@ class CategoryController extends BaseController
*/
public function destroy(Category $category)
{
$this->_repository->destroy($category);
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$repos->destroy($category);
Session::flash('success', 'The category was deleted.');
return Redirect::route('categories.index');
}
@@ -63,8 +57,7 @@ class CategoryController extends BaseController
*/
public function edit(Category $category)
{
return View::make('categories.edit')->with('category', $category)
->with('subTitle', 'Edit category "' . $category->name . '"');
return View::make('categories.edit')->with('category', $category)->with('subTitle', 'Edit category "' . $category->name . '"');
}
/**
@@ -72,10 +65,11 @@ class CategoryController extends BaseController
*/
public function index()
{
$categories = $this->_repository->get();
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$categories = $repos->get();
return View::make('categories.index')->with('categories', $categories)
->with('subTitle', 'All your categories');
return View::make('categories.index', compact('categories'));
}
/**
@@ -85,54 +79,106 @@ class CategoryController extends BaseController
*/
public function show(Category $category)
{
$start = \Session::get('start');
$end = \Session::get('end');
$hideCategory = true;
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$journals = $this->_category->journalsInRange($category, $start, $end);
$journals = $repos->getTransactionJournals($category, 50);
return View::make('categories.show')->with('category', $category)->with('journals', $journals)->with(
'highlight', Input::get('highlight')
)->with('subTitle', 'Overview for category "' . $category->name . '"');
return View::make('categories.show', compact('category', 'journals', 'hideCategory'));
}
/**
* @return $this|\Illuminate\Http\RedirectResponse
* @return $this
* @throws FireflyException
*/
public function store()
{
$category = $this->_repository->store(Input::all());
if ($category->validate()) {
Session::flash('success', 'Category "' . $category->name . '" created!');
$data = Input::all();
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
if (Input::get('create') == '1') {
return Redirect::route('categories.create');
}
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save category: ' . $messages['errors']->first());
return Redirect::route('categories.index');
} else {
Session::flash('error', 'Could not save the new category!');
return Redirect::route('categories.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New category stored!');
return Redirect::route('categories.create')->withInput();
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('categories.create')->withInput();
} else {
return Redirect::route('categories.index');
}
break;
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('categories.create')->withInput();
break;
}
}
/**
* @param Category $category
*
* @return $this|\Illuminate\Http\RedirectResponse
* @return $this
* @throws FireflyException
*/
public function update(Category $category)
{
$category = $this->_repository->update($category, Input::all());
if ($category->validate()) {
Session::flash('success', 'Category "' . $category->name . '" updated.');
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$data = Input::except('_token');
return Redirect::route('categories.index');
} else {
Session::flash('success', 'Could not update category "' . $category->name . '".');
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'return_to_edit':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save category: ' . $messages['errors']->first());
return Redirect::route('categories.edit')->withErrors($category->errors())->withInput();
return Redirect::route('categories.edit', $category->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($category, $data);
Session::flash('success', 'Category updated!');
if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('categories.edit', $category->id);
} else {
return Redirect::route('categories.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('categories.edit', $category->id)->withInput();
break;
}

View File

@@ -1,485 +0,0 @@
<?php
use Carbon\Carbon;
use Firefly\Helper\Controllers\ChartInterface;
use Firefly\Storage\Account\AccountRepositoryInterface;
/**
* Class ChartController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ChartController extends BaseController
{
protected $_chart;
protected $_accounts;
/**
* @param ChartInterface $chart
* @param AccountRepositoryInterface $accounts
*/
public function __construct(ChartInterface $chart, AccountRepositoryInterface $accounts)
{
$this->_chart = $chart;
$this->_accounts = $accounts;
}
/**
* This method takes a budget, all limits and all their repetitions and displays three numbers per repetition:
* the amount of money in the repetition (represented as "an envelope"), the amount spent and the spent percentage.
*
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetDefault(\Budget $budget)
{
$expense = [];
$left = [];
$envelope = [];
// get all limit repetitions for this budget.
/** @var \Limit $limit */
foreach ($budget->limits as $limit) {
/** @var \LimitRepetition $rep */
foreach ($limit->limitrepetitions as $rep) {
// get the amount of money spent in this period on this budget.
$spentInRep = $rep->amount - $rep->leftInRepetition();
$pct = round((floatval($spentInRep) / floatval($limit->amount)) * 100, 2);
$name = $rep->periodShow();
$envelope[] = [$name, floatval($limit->amount)];
$expense[] = [$name, floatval($spentInRep)];
$left[] = [$name, $pct];
}
}
$return = [
'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' => 'All envelopes',
'series' => [
[
'type' => 'line',
'yAxis' => 1,
'name' => 'Amount in envelope',
'data' => $envelope
],
[
'type' => 'column',
'name' => 'Expenses in envelope',
'data' => $expense
],
[
'type' => 'line',
'yAxis' => 1,
'name' => 'Spent percentage for envelope',
'data' => $left
]
]
];
return Response::json($return);
}
/**
* This method takes a single limit repetition (so a single "envelope") and displays the amount of money spent
* per day and subsequently how much money is left.
*
* @param LimitRepetition $rep
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetLimit(\LimitRepetition $rep)
{
$budget = $rep->limit->budget;
$current = clone $rep->startdate;
$expense = [];
$leftInLimit = [];
$currentLeftInLimit = floatval($rep->limit->amount);
while ($current <= $rep->enddate) {
$spent = $this->_chart->spentOnDay($budget, $current);
$spent = floatval($spent) == 0 ? null : floatval($spent);
$entry = [$current->timestamp * 1000, $spent];
$expense[] = $entry;
$currentLeftInLimit = $currentLeftInLimit - $spent;
$leftInLimit[] = [$current->timestamp * 1000, $currentLeftInLimit];
$current->addDay();
}
$return = [
'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' =>
'Between ' . $rep->startdate->format('M jS, Y') . ' and ' . $rep->enddate->format('M jS, Y'),
'series' => [
[
'type' => 'column',
'name' => 'Expenses per day',
'yAxis' => 1,
'data' => $expense
],
[
'type' => 'line',
'name' => 'Left in envelope',
'data' => $leftInLimit
]
]
];
return Response::json($return);
}
/**
* This method takes a budget and gets all transactions in it which haven't got an envelope (limit).
*
* Usually this means that very old and unorganized or very NEW transactions get displayed; there was never an
* envelope or it hasn't been created (yet).
*
*
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetNoLimits(\Budget $budget)
{
/*
* Firefly can go about this two ways. Either it finds all transactions which definitely are IN an envelope
* and exclude them or it searches for transactions outside of the range of any of the envelopes there are.
*
* Since either is kinda shitty Firefly uses the first one because it's easier to build.
*/
$inRepetitions = $this->_chart->allJournalsInBudgetEnvelope($budget);
/*
* With this set of id's, Firefly can search for all journals NOT in that set.
* BUT they have to be in the budget (duh).
*/
$set = $this->_chart->journalsNotInSet($budget, $inRepetitions);
/*
* Next step: get all transactions for those journals.
*/
$transactions = $this->_chart->transactionsByJournals($set);
/*
* this set builds the chart:
*/
$expense = [];
foreach ($transactions as $t) {
$date = new Carbon($t->date);
$expense[] = [$date->timestamp * 1000, floatval($t->aggregate)];
}
$return = [
'chart_title' => 'Overview for ' . $budget->name,
'subtitle' => 'Not organized by an envelope',
'series' => [
[
'type' => 'column',
'name' => 'Expenses per day',
'data' => $expense
]
]
];
return Response::json($return);
}
/**
* This method gets all transactions within a budget within the period set by the current session
* start and end date. It also includes any envelopes which might exist within this period.
*
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetSession(\Budget $budget)
{
$series = [];
$end = clone Session::get('end');
$start = clone Session::get('start');
/*
* Expenses per day in the session's period. That's easy.
*/
$expense = [];
$current = clone Session::get('start');
while ($current <= $end) {
$spent = $this->_chart->spentOnDay($budget, $current);
$spent = floatval($spent) == 0 ? null : floatval($spent);
$expense[] = [$current->timestamp * 1000, $spent];
$current->addDay();
}
$series[] = [
'type' => 'column',
'name' => 'Expenses per day',
'data' => $expense
];
unset($expense, $spent, $current);
/*
* Find all limit repetitions (for this budget) between start and end. This is
* quite a complex query.
*/
$reps = $this->_chart->limitsInRange($budget, $start, $end);
/*
* For each limitrepetition Firefly creates a serie that contains the amount left in
* the limitrepetition for its entire date-range. Entries are only actually included when they
* fall into the charts date range.
*
* So example: the user has a session date from Jan 15 to Jan 30. The limitrepetition
* starts at 1 Jan until 1 Feb.
*
* Firefly loops from 1 Jan to 1 Feb but only includes Jan 15 / Jan 30.
* But it does keep count of the amount outside of these dates because otherwise the line might be wrong.
*/
/** @var \LimitRepetition $repetition */
foreach ($reps as $repetition) {
$limitAmount = $repetition->limit->amount;
// create a serie for the repetition.
$currentSerie = [
'type' => 'spline',
'id' => 'rep-' . $repetition->id,
'yAxis' => 1,
'name' => 'Envelope #' . $repetition->id . ' in ' . $repetition->periodShow(),
'data' => []
];
$current = clone $repetition->startdate;
while ($current <= $repetition->enddate) {
if ($current >= $start && $current <= $end) {
// spent on limit:
$spentSoFar = $this->_chart->spentOnLimitRepetitionBetweenDates(
$repetition, $repetition->startdate, $current
);
$leftInLimit = floatval($limitAmount) - floatval($spentSoFar);
$currentSerie['data'][] = [$current->timestamp * 1000, $leftInLimit];
}
$current->addDay();
}
// do something here.
$series[] = $currentSerie;
}
$return = [
'chart_title' => 'Overview for budget ' . $budget->name,
'subtitle' =>
'Between ' . Session::get('start')->format('M jS, Y') . ' and ' . Session::get('end')->format(
'M jS, Y'
),
'series' => $series
];
return Response::json($return);
}
/**
* @param Category $category
*
* @return \Illuminate\Http\JsonResponse
*/
public function categoryShowChart(Category $category)
{
$start = Session::get('start');
$end = Session::get('end');
$range = Session::get('range');
$serie = $this->_chart->categoryShowChart($category, $range, $start, $end);
$data = [
'chart_title' => $category->name,
'subtitle' => '<a href="' . route('categories.show', [$category->id]) . '">View more</a>',
'series' => $serie
];
return Response::json($data);
}
/**
* @param Account $account
*
* @return mixed
*/
public function homeAccount(Account $account = null)
{
// get preferences and accounts (if necessary):
$start = Session::get('start');
$end = Session::get('end');
if (is_null($account)) {
// get, depending on preferences:
/** @var \Firefly\Helper\Preferences\PreferencesHelperInterface $prefs */
$prefs = \App::make('Firefly\Helper\Preferences\PreferencesHelperInterface');
$pref = $prefs->get('frontpageAccounts', []);
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $acct */
$acct = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accounts = $acct->getByIds($pref->data);
} else {
$accounts = [$account];
}
// loop and get array data.
$url = count($accounts) == 1 && is_array($accounts)
? '<a href="' . route('accounts.show', [$account->id]) . '">View more</a>'
:
'<a href="' . route('accounts.index') . '">View more</a>';
$data = [
'chart_title' => count($accounts) == 1 ? $accounts[0]->name : 'All accounts',
'subtitle' => $url,
'series' => []
];
foreach ($accounts as $account) {
$data['series'][] = $this->_chart->account($account, $start, $end);
}
return Response::json($data);
}
/**
* @param $name
* @param $day
* @param $month
* @param $year
*
* @return $this
*/
public function homeAccountInfo($name, $day, $month, $year)
{
$account = $this->_accounts->findByName($name);
$date = Carbon::createFromDate($year, $month, $day);
$result = $this->_chart->accountDailySummary($account, $date);
return View::make('charts.info')->with('rows', $result['rows'])->with('sum', $result['sum'])->with(
'account', $account
);
}
/**
* @return \Illuminate\Http\JsonResponse
*/
public function homeBudgets()
{
$start = Session::get('start');
$end = Session::get('end');
$data = [
'labels' => [],
'series' => [
[
'name' => 'Limit',
'data' => []
],
[
'name' => 'Spent',
'data' => []
],
]
];
// Get all budgets.
$budgets = \Auth::user()->budgets()->orderBy('name', 'ASC')->get();
$budgetIds = [];
/** @var \Budget $budget */
foreach ($budgets as $budget) {
$budgetIds[] = $budget->id;
// Does the budget have a limit starting on $start?
$rep = \LimitRepetition::
leftJoin('limits', 'limit_repetitions.limit_id', '=', 'limits.id')->leftJoin(
'components', 'limits.component_id', '=', 'components.id'
)->where('limit_repetitions.startdate', $start->format('Y-m-d'))->where(
'components.id', $budget->id
)->first(['limit_repetitions.*']);
if (is_null($rep)) {
$limit = 0.0;
$id = null;
$parameter = 'useSession=true';
} else {
$limit = floatval($rep->amount);
$id = $rep->id;
$parameter = '';
}
// Date range to check for expenses made?
if (is_null($rep)) {
// use the session start and end for our search query
$expenseStart = Session::get('start');
$expenseEnd = Session::get('end');
} else {
// use the limit's start and end for our search query
$expenseStart = $rep->startdate;
$expenseEnd = $rep->enddate;
}
// How much have we spent on this budget?
$expenses = floatval($budget->transactionjournals()->before($expenseEnd)->after($expenseStart)->lessThan(0)->sum('amount')) * -1;
// Append to chart:
if ($limit > 0 || $expenses > 0) {
$data['labels'][] = $budget->name;
$data['series'][0]['data'][] = [
'y' => $limit,
'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter
];
$data['series'][1]['data'][] = [
'y' => $expenses,
'url' => route('budgets.show', [$budget->id, $id]) . '?' . $parameter
];
}
}
// Add expenses that have no budget:
$set = \Auth::user()->transactionjournals()->whereNotIn(
'transaction_journals.id', function ($query) use ($start, $end) {
$query->select('transaction_journals.id')->from('transaction_journals')
->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('components.class', 'Budget');
}
)->before($end)->after($start)->lessThan(0)->transactionTypes(['Withdrawal'])->sum('amount');
// This can be debugged by using get(['transaction_journals.*','transactions.amount']);
$data['labels'][] = 'No budget';
$data['series'][0]['data'][] = [
'y' => 0,
'url' => route('budgets.nobudget','session')
];
$data['series'][1]['data'][] = [
'y' => floatval($set) * -1,
'url' => route('budgets.nobudget','session')
];
return Response::json($data);
}
/**
* @return \Illuminate\Http\JsonResponse
*/
public function homeCategories()
{
$start = Session::get('start');
$end = Session::get('end');
return Response::json($this->_chart->categories($start, $end));
}
}

View File

@@ -0,0 +1,757 @@
<?php
use Carbon\Carbon;
/**
* Class GoogleChartController
*/
class GoogleChartController extends BaseController
{
/**
* @param Account $account
* @param string $view
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountBalanceChart(Account $account, $view = 'session')
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Day of month', 'date');
$chart->addColumn('Balance for ' . $account->name, 'number');
/*
* Loop the date, then loop the accounts, then add balance.
*/
switch ($view) {
default:
case 'session':
$start = Session::get('start');
$end = Session::get('end');
break;
case 'all':
$first = $account->transactionjournals()->orderBy('date', 'DESC')->first();
$last = $account->transactionjournals()->orderBy('date', 'ASC')->first();
if (is_null($first)) {
$start = Session::get('start');
} else {
$start = clone $first->date;
}
if (is_null($last)) {
$end = Session::get('end');
} else {
$end = clone $last->date;
}
break;
}
$current = clone $start;
while ($end >= $current) {
$row = [clone $current];
if ($current > Carbon::now()) {
$row[] = null;
} else {
$row[] = Steam::balance($account, $current);
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Account $account
* @param string $view
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountSankeyInChart(Account $account, $view = 'session')
{
// collect all relevant entries.
$set = [];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('From', 'string');
$chart->addColumn('To', 'string', 'domain');
$chart->addColumn('Weight', 'number');
switch ($view) {
default:
case 'session':
$start = Session::get('start');
$end = Session::get('end');
break;
case 'all':
$first = $account->transactionjournals()->orderBy('date', 'DESC')->first();
$last = $account->transactionjournals()->orderBy('date', 'ASC')->first();
if (is_null($first)) {
$start = Session::get('start');
} else {
$start = clone $first->date;
}
if (is_null($last)) {
$end = Session::get('end');
} else {
$end = clone $last->date;
}
break;
}
$transactions = $account->transactions()->with(
['transactionjournal', 'transactionjournal.transactions' => function ($q) {
$q->where('amount', '<', 0);
}, 'transactionjournal.budgets', 'transactionjournal.transactiontype', 'transactionjournal.categories']
)->before($end)->after($start)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$amount = floatval($transaction->amount);
$type = $transaction->transactionJournal->transactionType->type;
if ($amount > 0 && $type != 'Transfer') {
$otherAccount = $transaction->transactionJournal->transactions[0]->account->name;
$categoryName = isset($transaction->transactionJournal->categories[0]) ? $transaction->transactionJournal->categories[0]->name : '(no cat)';
$set[] = [$otherAccount, $categoryName, $amount];
$set[] = [$categoryName, $account->name, $amount];
}
}
// loop the set, group everything together:
$grouped = [];
foreach ($set as $entry) {
$key = $entry[0] . $entry[1];
if (isset($grouped[$key])) {
$grouped[$key][2] += $entry[2];
} else {
$grouped[$key] = $entry;
}
}
// add rows to the chart:
foreach ($grouped as $entry) {
$chart->addRow($entry[0], $entry[1], $entry[2]);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Account $account
* @param string $view
*
* @return \Illuminate\Http\JsonResponse
*/
public function accountSankeyOutChart(Account $account, $view = 'session')
{
// collect all relevant entries.
$set = [];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('From', 'string');
$chart->addColumn('To', 'string', 'domain');
$chart->addColumn('Weight', 'number');
$transactions = $account->transactions()->with(
['transactionjournal', 'transactionjournal.transactions', 'transactionjournal.budgets', 'transactionjournal.transactiontype',
'transactionjournal.categories']
)->before(Session::get('end'))->after(
Session::get('start')
)->get();
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$amount = floatval($transaction->amount);
$type = $transaction->transactionJournal->transactionType->type;
if ($amount < 0 && $type != 'Transfer') {
// from account to a budget (if present).
$budgetName = isset($transaction->transactionJournal->budgets[0]) ? $transaction->transactionJournal->budgets[0]->name : '(no budget)';
$set[] = [$account->name, $budgetName, $amount * -1];
// from budget to category.
$categoryName = isset($transaction->transactionJournal->categories[0]) ? ' ' . $transaction->transactionJournal->categories[0]->name
: '(no cat)';
$set[] = [$budgetName, $categoryName, $amount * -1];
}
}
// loop the set, group everything together:
$grouped = [];
foreach ($set as $entry) {
$key = $entry[0] . $entry[1];
if (isset($grouped[$key])) {
$grouped[$key][2] += $entry[2];
} else {
$grouped[$key] = $entry;
}
}
// add rows to the chart:
foreach ($grouped as $entry) {
$chart->addRow($entry[0], $entry[1], $entry[2]);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* This method renders the b
*/
public function allAccountsBalanceChart()
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Day of the month', 'date');
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$pref = $preferences->get('frontpageAccounts', []);
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
if (count($pref->data) > 0) {
$accounts = $acct->getByIds($pref->data);
} else {
$accounts = $acct->getAssetAccounts();
}
/*
* Add a column for each account.
*/
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn('Balance for ' . $account->name, 'number');
}
/*
* Loop the date, then loop the accounts, then add balance.
*/
$start = Session::get('start');
$end = Session::get('end');
$current = clone $start;
while ($end >= $current) {
$row = [clone $current];
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @return \Illuminate\Http\JsonResponse
*/
public function allBudgetsHomeChart()
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Budget', 'string');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
/** @var \FireflyIII\Database\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget');
$budgets = $bdt->get();
/*
* Loop budgets:
*/
/** @var Budget $budget */
foreach ($budgets as $budget) {
/*
* Is there a repetition starting on this particular date? We can use that.
*/
/** @var \LimitRepetition $repetition */
$repetition = $bdt->repetitionOnStartingOnDate($budget, Session::get('start'));
/*
* If there is, use it. Otherwise, forget it.
*/
if (is_null($repetition)) {
// use the session start and end for our search query
$searchStart = Session::get('start');
$searchEnd = Session::get('end');
// the limit is zero:
$limit = 0;
} else {
// use the limit's start and end for our search query
$searchStart = $repetition->startdate;
$searchEnd = $repetition->enddate;
// the limit is the repetitions limit:
$limit = floatval($repetition->amount);
}
/*
* No matter the result of the search for the repetition, get all the transactions associated
* with the budget, and sum up the expenses made.
*/
$expenses = floatval($budget->transactionjournals()->before($searchEnd)->after($searchStart)->lessThan(0)->sum('amount')) * -1;
if ($expenses > 0) {
$chart->addRow($budget->name, $limit, $expenses);
}
}
/*
* Finally, get all transactions WITHOUT a budget and add those as well.
* (yes this method is oddly specific).
*/
$noBudgetSet = $bdt->transactionsWithoutBudgetInDateRange(Session::get('start'), Session::get('end'));
$sum = $noBudgetSet->sum('amount') * -1;
$chart->addRow('No budget', 0, $sum);
$chart->generate();
return Response::json($chart->getData());
}
/**
* @return \Illuminate\Http\JsonResponse
*/
public function allCategoriesHomeChart()
{
$data = [];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Category', 'string');
$chart->addColumn('Spent', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
/*
* Get the journals:
*/
$journals = $tj->getInDateRange(Session::get('start'), Session::get('end'));
/** @var \TransactionJournal $journal */
foreach ($journals as $journal) {
if ($journal->transactionType->type == 'Withdrawal') {
$amount = $journal->getAmount();
$category = $journal->categories()->first();
if (!is_null($category)) {
if (isset($data[$category->name])) {
$data[$category->name] += $amount;
} else {
$data[$category->name] = $amount;
}
}
}
}
arsort($data);
foreach ($data as $key => $entry) {
$chart->addRow($key, $entry);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetLimitSpending(\Budget $budget, \LimitRepetition $repetition)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Day', 'date');
$chart->addColumn('Left', 'number');
$amount = $repetition->amount;
while ($start <= $end) {
/*
* Sum of expenses on this day:
*/
$sum = floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($start)->sum('amount'));
$amount += $sum;
$chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetsReportChart($year)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
/** @var \FireflyIII\Database\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget');
$budgets = $bdt->get();
$chart->addColumn('Month', 'date');
/** @var \Budget $budget */
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
$chart->addColumn('No budget', 'number');
/*
* Loop budgets this year.
*/
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$row = [clone $start];
foreach ($budgets as $budget) {
$row[] = $bdt->spentInMonth($budget, $start);
}
/*
* Without a budget:
*/
$endOfMonth = clone $start;
$endOfMonth->endOfMonth();
$set = $bdt->transactionsWithoutBudgetInDateRange($start, $endOfMonth);
$row[] = floatval($set->sum('amount')) * -1;
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Component $component
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function componentsAndSpending(Component $component, $year)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
if ($component->class == 'Budget') {
/** @var \FireflyIII\Database\Budget $repos */
$repos = App::make('FireflyIII\Database\Budget');
} else {
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$spent = $repos->spentInMonth($component, $start);
$repetition = $repos->repetitionOnStartingOnDate($component, $start);
if ($repetition) {
$budgeted = floatval($repetition->amount);
} else {
$budgeted = null;
}
$chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param Piggybank $piggybank
*
* @return \Illuminate\Http\JsonResponse
*/
public function piggyBankHistory(\Piggybank $piggybank)
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Date', 'date');
$chart->addColumn('Balance', 'number');
$set = \DB::table('piggybank_events')->where('piggybank_id', $piggybank->id)->groupBy('date')->get(['date', DB::Raw('SUM(`amount`) AS `sum`')]);
foreach ($set as $entry) {
$chart->addRow(new Carbon($entry->date), floatval($entry->sum));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param RecurringTransaction $recurring
*
* @return \Illuminate\Http\JsonResponse
*/
public function recurringOverview(RecurringTransaction $recurring)
{
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Date', 'date');
$chart->addColumn('Max amount', 'number');
$chart->addColumn('Min amount', 'number');
$chart->addColumn('Current entry', 'number');
// get first transaction or today for start:
$first = $recurring->transactionjournals()->orderBy('date', 'ASC')->first();
if ($first) {
$start = $first->date;
} else {
$start = new Carbon;
}
$end = new Carbon;
while ($start <= $end) {
$result = $recurring->transactionjournals()->before($end)->after($start)->first();
if ($result) {
$amount = $result->getAmount();
} else {
$amount = 0;
}
unset($result);
$chart->addRow(clone $start, $recurring->amount_max, $recurring->amount_min, $amount);
$start = DateKit::addPeriod($start, $recurring->repeat_freq, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @return \Illuminate\Http\JsonResponse
* @throws \FireflyIII\Exception\FireflyException
*/
public function recurringTransactionsOverview()
{
/*
* Set of paid transaction journals.
* Set of unpaid recurring transactions.
*/
$paid = ['items' => [], 'amount' => 0];
$unpaid = ['items' => [], 'amount' => 0];
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
/** @var \FireflyIII\Database\Recurring $rcr */
$rcr = App::make('FireflyIII\Database\Recurring');
$recurring = $rcr->get();
/** @var \RecurringTransaction $entry */
foreach ($recurring as $entry) {
/*
* Start another loop starting at the $date.
*/
$start = clone $entry->date;
$end = Carbon::now();
/*
* The jump we make depends on the $repeat_freq
*/
$current = clone $start;
while ($current <= $end) {
/*
* Get end of period for $current:
*/
$currentEnd = DateKit::endOfPeriod($current, $entry->repeat_freq);
/*
* In the current session range?
*/
if (\Session::get('end') >= $current and $currentEnd >= \Session::get('start')) {
/*
* Lets see if we've already spent money on this recurring transaction (it hath recurred).
*/
/** @var TransactionJournal $set */
$journal = $rcr->getJournalForRecurringInRange($entry, $current, $currentEnd);
if (is_null($journal)) {
$unpaid['items'][] = $entry->name;
$unpaid['amount'] += (($entry->amount_max + $entry->amount_min) / 2);
} else {
$amount = $journal->getAmount();
$paid['items'][] = $journal->description;
$paid['amount'] += $amount;
}
}
/*
* Add some time for the next loop!
*/
$current = DateKit::addPeriod($current, $entry->repeat_freq, intval($entry->skip));
}
}
/** @var \RecurringTransaction $entry */
$chart->addRow('Unpaid: ' . join(', ', $unpaid['items']), $unpaid['amount']);
$chart->addRow('Paid: ' . join(', ', $paid['items']), $paid['amount']);
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInExp($year)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
// total income:
$income = $tj->getSumOfIncomesByMonth($start);
$expense = $tj->getSumOfExpensesByMonth($start);
$chart->addRow(clone $start, $income, $expense);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInExpSum($year)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
$end = clone $start;
$end->endOfYear();
$income = 0;
$expense = 0;
$count = 0;
while ($start < $end) {
// total income:
$income += $tj->getSumOfIncomesByMonth($start);
$expense += $tj->getSumOfExpensesByMonth($start);
$count++;
$start->addMonth();
}
$chart->addRow('Sum', $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow('Average', ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Class HelpController
*/
class HelpController extends BaseController
{
/**
* @param $route
*
* @return \Illuminate\Http\JsonResponse
*/
public function show($route)
{
// no valid route
if (!Route::has($route)) {
$helpText = '<p>There is no help for this route!</p>';
$helpTitle = 'Help';
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
// content in cache
if (Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text')) {
$helpText = Cache::get('help.' . $route . '.text');
$helpTitle = Cache::get('help.' . $route . '.title');
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
// get the help-content from Github:
$URL = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
try {
$content = file_get_contents($URL);
} catch (ErrorException $e) {
$content = '<p>There is no help for this route.</p>';
}
if (strlen($content) > 0) {
$helpText = \Michelf\Markdown::defaultTransform($content);
$helpTitle = $route;
Cache::put('help.' . $route . '.text', $helpText, 10080); // a week.
Cache::put('help.' . $route . '.title', $helpTitle, 10080);
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
$helpText = '<p>There is no help for this route!</p>';
$helpTitle = 'Help';
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
}

View File

@@ -1,80 +1,11 @@
<?php
use Firefly\Helper\Preferences\PreferencesHelperInterface as PHI;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI;
use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI;
/**
* Class HomeController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class HomeController extends BaseController
{
protected $_accounts;
protected $_preferences;
protected $_journal;
protected $_reminders;
/**
* @param ARI $accounts
* @param PHI $preferences
* @param TJRI $journal
* @param RRI $reminders
*/
public function __construct(ARI $accounts, PHI $preferences, TJRI $journal, RRI $reminders)
{
$this->_accounts = $accounts;
$this->_preferences = $preferences;
$this->_journal = $journal;
$this->_reminders = $reminders;
}
public function jobDev()
{
$fullName = storage_path() . DIRECTORY_SEPARATOR . 'firefly-export-2014-07-23.json';
\Log::notice('Pushed start job.');
Queue::push('Firefly\Queue\Import@start', ['file' => $fullName, 'user' => 1]);
}
/*
*
*/
public function sessionPrev()
{
/** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface');
$toolkit->prev();
return Redirect::back();
//return Redirect::route('index');
}
/*
*
*/
public function sessionNext()
{
/** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface');
$toolkit->next();
return Redirect::back();
//return Redirect::route('index');
}
public function rangeJump($range)
{
$viewRange = $this->_preferences->get('viewRange', '1M');
$valid = ['1D', '1W', '1M', '3M', '6M', '1Y',];
if(in_array($range,$valid)) {
$this->_preferences->set('viewRange', $range);
Session::forget('range');
}
return Redirect::back();
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
@@ -90,34 +21,81 @@ class HomeController extends BaseController
*/
public function index()
{
Event::fire('limits.check');
Event::fire('piggybanks.check');
Event::fire('recurring.check');
// count, maybe Firefly needs some introducing text to show:
$count = $this->_accounts->count();
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\TransactionJournal $jrnls */
$jrnls = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
$count = $acct->countAssetAccounts();
$start = Session::get('start');
$end = Session::get('end');
// get the preference for the home accounts to show:
$frontpage = $this->_preferences->get('frontpageAccounts', []);
$frontpage = $preferences->get('frontpageAccounts', []);
if ($frontpage->data == []) {
$accounts = $this->_accounts->getActiveDefault();
$accounts = $acct->getAssetAccounts();
} else {
$accounts = $this->_accounts->getByIds($frontpage->data);
$accounts = $acct->getByIds($frontpage->data);
}
$transactions = [];
foreach ($accounts as $account) {
$set = $this->_journal->getByAccountInDateRange($account, 10, $start, $end);
$set = $jrnls->getInDateRangeAccount($account, 10, $start, $end);
if (count($set) > 0) {
$transactions[] = [$set, $account];
}
}
// build the home screen:
return View::make('index')->with('count', $count)->with('transactions', $transactions)->with('title', 'Firefly')
->with('subTitle', 'What\'s playing?')->with('mainTitleIcon', 'fa-fire');
return View::make('index')->with('count', $count)->with('transactions', $transactions)->with('title', 'Firefly')->with('subTitle', 'What\'s playing?')
->with('mainTitleIcon', 'fa-fire');
}
/**
* @param $range
*
* @return \Illuminate\Http\RedirectResponse
*/
public function rangeJump($range)
{
$valid = ['1D', '1W', '1M', '3M', '6M', '1Y',];
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
if (in_array($range, $valid)) {
$preferences->set('viewRange', $range);
Session::forget('range');
}
return Redirect::back();
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function sessionNext()
{
Navigation::next();
return Redirect::back();
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function sessionPrev()
{
Navigation::prev();
return Redirect::back();
}
}

View File

@@ -1,25 +1,11 @@
<?php
use Firefly\Helper\Controllers\JsonInterface as JI;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Builder;
/**
* Class JsonController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class JsonController extends BaseController
{
/** @var \Firefly\Helper\Controllers\JsonInterface $helper */
protected $helper;
public function __construct(JI $helper)
{
$this->helper = $helper;
}
/**
* Returns a list of categories.
@@ -28,8 +14,8 @@ class JsonController extends BaseController
*/
public function categories()
{
/** @var \Firefly\Storage\Category\EloquentCategoryRepository $categories */
$categories = App::make('Firefly\Storage\Category\CategoryRepositoryInterface');
/** @var \FireflyIII\Database\Category $categories */
$categories = App::make('FireflyIII\Database\Category');
$list = $categories->get();
$return = [];
foreach ($list as $entry) {
@@ -48,9 +34,9 @@ class JsonController extends BaseController
*/
public function expenseAccounts()
{
/** @var \Firefly\Storage\Account\EloquentAccountRepository $accounts */
$accounts = App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$list = $accounts->getOfTypes(['Expense account', 'Beneficiary account']);
/** @var \FireflyIII\Database\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account');
$list = $accounts->getExpenseAccounts();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
@@ -61,99 +47,13 @@ class JsonController extends BaseController
}
/**
* Returns a list of transactions, expenses only, using the given parameters.
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenses()
{
/*
* Gets most parameters from the Input::all() array:
*/
$parameters = $this->helper->dataTableParameters();
/*
* Add some more parameters to fine tune the query:
*/
$parameters['transactionTypes'] = ['Withdrawal'];
$parameters['amount'] = 'negative';
/*
* Get the query:
*/
$query = $this->helper->journalQuery($parameters);
/*
* Build result set:
*/
$resultSet = $this->helper->journalDataset($parameters, $query);
/*
* Build return data:
*/
return Response::json($resultSet);
}
/**
*
*/
public function recurringjournals(RecurringTransaction $recurringTransaction)
{
$parameters = $this->helper->dataTableParameters();
$parameters['transactionTypes'] = ['Withdrawal'];
$parameters['amount'] = 'negative';
$query = $this->helper->journalQuery($parameters);
$query->where('recurring_transaction_id', $recurringTransaction->id);
$resultSet = $this->helper->journalDataset($parameters, $query);
/*
* Build return data:
*/
return Response::json($resultSet);
}
public function recurring()
{
$parameters = $this->helper->dataTableParameters();
$query = $this->helper->recurringTransactionsQuery($parameters);
$resultSet = $this->helper->recurringTransactionsDataset($parameters, $query);
return Response::json($resultSet);
}
/**
* @return \Illuminate\Http\JsonResponse|string
*/
public function revenue()
{
$parameters = $this->helper->dataTableParameters();
$parameters['transactionTypes'] = ['Deposit'];
$parameters['amount'] = 'positive';
$query = $this->helper->journalQuery($parameters);
$resultSet = $this->helper->journalDataset($parameters, $query);
/*
* Build return data:
*/
return Response::json($resultSet);
}
/**
* Returns a JSON list of all revenue accounts.
*
* @return \Illuminate\Http\JsonResponse
*/
public function revenueAccounts()
{
/** @var \Firefly\Storage\Account\EloquentAccountRepository $accounts */
$accounts = App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$list = $accounts->getOfTypes(['Revenue account']);
/** @var \FireflyIII\Database\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account');
$list = $accounts->getRevenueAccounts();
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
@@ -162,25 +62,4 @@ class JsonController extends BaseController
return Response::json($return);
}
/**
* Returns a list of all transfers.
*
* @return \Illuminate\Http\JsonResponse
*/
public function transfers()
{
$parameters = $this->helper->dataTableParameters();
$parameters['transactionTypes'] = ['Transfer'];
$parameters['amount'] = 'positive';
$query = $this->helper->journalQuery($parameters);
$resultSet = $this->helper->journalDataset($parameters, $query);
/*
* Build return data:
*/
return Response::json($resultSet);
}
}
}

View File

@@ -1,162 +0,0 @@
<?php
use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI;
use Firefly\Storage\Limit\LimitRepositoryInterface as LRI;
/**
* Class LimitController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class LimitController extends BaseController
{
protected $_budgets;
protected $_limits;
/**
* @param BRI $budgets
* @param LRI $limits
*/
public function __construct(BRI $budgets, LRI $limits)
{
$this->_budgets = $budgets;
$this->_limits = $limits;
View::share('title','Envelopes');
View::share('mainTitleIcon', 'fa-tasks');
}
/**
* @param Budget $budget
*
* @return $this
*/
public function create(\Budget $budget = null)
{
$periods = \Config::get('firefly.periods_to_text');
$prefilled = [
'startdate' => \Input::get('startdate') ? : date('Y-m-d'),
'repeat_freq' => \Input::get('repeat_freq') ? : 'monthly',
'budget_id' => $budget ? $budget->id : null
];
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
$budgets = $toolkit->makeSelectList($this->_budgets->get());
return View::make('limits.create')->with('budgets', $budgets)->with(
'periods', $periods
)->with('prefilled', $prefilled)->with('subTitle','New envelope');
}
/**
* @param Limit $limit
*
* @return $this
*/
public function delete(\Limit $limit)
{
return View::make('limits.delete')->with('limit', $limit)->with('subTitle','Delete envelope');
}
/**
* @param Limit $limit
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(\Limit $limit)
{
Event::fire('limits.destroy', [$limit]); // before
$success = $this->_limits->destroy($limit);
if ($success) {
Session::flash('success', 'The envelope was deleted.');
} else {
Session::flash('error', 'Could not delete the envelope. Check the logs to be sure.');
}
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
} else {
return Redirect::route('budgets.index.budget');
}
}
/**
* @param Limit $limit
*
* @return $this
*/
public function edit(Limit $limit)
{
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
$budgets = $toolkit->makeSelectList($this->_budgets->get());
$periods = \Config::get('firefly.periods_to_text');
return View::make('limits.edit')->with('limit', $limit)->with('budgets', $budgets)->with(
'periods', $periods
)->with('subTitle','Edit envelope');
}
/**
* @param Budget $budget
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function store(Budget $budget = null)
{
// find a limit with these properties, as Firefly might already have one:
$limit = $this->_limits->store(Input::all());
if ($limit->validate()) {
Session::flash('success', 'Envelope created!');
Event::fire('limits.store', [$limit]);
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
} else {
return Redirect::route('budgets.index.budget');
}
} else {
Session::flash('error', 'Could not save new envelope.');
$budgetId = $budget ? $budget->id : null;
$parameters = [$budgetId, 'from' => Input::get('from')];
return Redirect::route('budgets.limits.create', $parameters)->withInput()
->withErrors($limit->errors());
}
}
/**
* @param Limit $limit
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function update(\Limit $limit)
{
$limit = $this->_limits->update($limit, Input::all());
if ($limit->validate()) {
Event::fire('limits.update', [$limit]);
Session::flash('success', 'Limit saved!');
if (Input::get('from') == 'date') {
return Redirect::route('budgets.index');
} else {
return Redirect::route('budgets.index.budget');
}
} else {
Session::flash('error', 'Could not save new limit: ' . $limit->errors()->first());
return Redirect::route('budgets.limits.edit', [$limit->id, 'from' => Input::get('from')])->withInput()
->withErrors($limit->errors());
}
}
}

View File

@@ -1,52 +0,0 @@
<?php
/**
* Class MigrateController
*/
class MigrateController extends BaseController
{
/**
* @return $this
*/
public function index()
{
return View::make('migrate.index')->with('index', 'Migration')->with('title','Migrate')->
with('subTitle','From Firefly II to Firefly III');
}
/**
*
*/
public function upload()
{
if (Input::hasFile('file') && Input::file('file')->isValid()) {
$path = storage_path();
$fileName = 'firefly-iii-import-' . date('Y-m-d-H-i') . '.json';
$fullName = $path . DIRECTORY_SEPARATOR . $fileName;
if (Input::file('file')->move($path, $fileName)) {
// so now Firefly pushes something in a queue and does something with it! Yay!
\Log::debug('Pushed a job to start the import.');
Queue::push('Firefly\Queue\Import@start', ['file' => $fullName, 'user' => \Auth::user()->id]);
if (Config::get('queue.default') == 'sync') {
Session::flash('success', 'Your data has been imported!');
} else {
Session::flash(
'success',
'The import job has been queued. Please be patient. Data will appear slowly. Please be patient.'
);
}
return Redirect::route('index');
}
Session::flash('error', 'Could not save file to storage.');
return Redirect::route('migrate.index');
} else {
Session::flash('error', 'Please upload a file.');
return Redirect::route('migrate.index');
}
}
}

View File

@@ -1,106 +1,73 @@
<?php
use Firefly\Exception\FireflyException;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
use Firefly\Storage\Piggybank\PiggybankRepositoryInterface as PRI;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
* Class PiggybankController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.TooManyMethods)
*
*/
class PiggybankController extends BaseController
{
protected $_accounts;
protected $_repository;
/**
* @param PRI $repository
* @param ARI $accounts
*
*/
public function __construct(PRI $repository, ARI $accounts)
public function __construct()
{
$this->_repository = $repository;
$this->_accounts = $accounts;
}
/**
* @param Piggybank $piggyBank
* Add money to piggy bank
*
* @param Piggybank $piggybank
*
* @return $this
*/
public function addMoney(Piggybank $piggyBank)
public function add(Piggybank $piggybank)
{
$what = 'add';
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
$maxRemove = null;
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
return View::make('piggybanks.modifyAmount')->with('what', $what)->with('maxAdd', $maxAdd)->with(
'maxRemove', $maxRemove
)->with('piggybank', $piggyBank);
$leftOnAccount = $repos->leftOnAccount($piggybank->account);
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
$leftToSave = $piggybank->targetamount - $savedSoFar;
$amount = min($leftOnAccount, $leftToSave);
return View::make('piggybanks.add', compact('piggybank'))->with('maxAmount', $amount);
}
/**
* @return $this
* @return mixed
*/
public function createPiggybank()
public function create()
{
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
$periods = Config::get('firefly.piggybank_periods');
$periods = Config::get('firefly.piggybank_periods');
$list = $this->_accounts->getActiveDefault();
$accounts = $toolkit->makeSelectList($list);
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
View::share('title', 'Piggy banks');
View::share('subTitle', 'Create new');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
return View::make('piggybanks.create-piggybank')->with('accounts', $accounts)
->with('periods', $periods);
return View::make('piggybanks.create', compact('accounts', 'periods'))->with('title', 'Piggy banks')->with('mainTitleIcon', 'fa-sort-amount-asc')->with(
'subTitle', 'Create new piggy bank'
)->with('subTitleIcon', 'fa-plus');
}
/**
* @return $this
*/
public function createRepeated()
{
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
$periods = Config::get('firefly.piggybank_periods');
$list = $this->_accounts->getActiveDefault();
$accounts = $toolkit->makeSelectList($list);
View::share('title', 'Repeated expenses');
View::share('subTitle', 'Create new');
View::share('mainTitleIcon', 'fa-rotate-right');
return View::make('piggybanks.create-repeated')->with('accounts', $accounts)->with('periods', $periods);
}
/**
* @param Piggybank $piggyBank
* @param Piggybank $piggybank
*
* @return $this
*/
public function delete(Piggybank $piggyBank)
public function delete(Piggybank $piggybank)
{
View::share('subTitle', 'Delete "' . $piggyBank->name . '"');
if ($piggyBank->repeats == 1) {
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-right');
} else {
View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
}
return View::make('piggybanks.delete')->with('piggybank', $piggyBank);
return View::make('piggybanks.delete')->with('piggybank', $piggybank)->with('subTitle', 'Delete "' . $piggybank->name . '"')->with(
'title', 'Piggy banks'
)->with('mainTitleIcon', 'fa-sort-amount-asc');
}
/**
@@ -110,293 +77,281 @@ class PiggybankController extends BaseController
*/
public function destroy(Piggybank $piggyBank)
{
Event::fire('piggybanks.destroy', [$piggyBank]);
if ($piggyBank->repeats == 1) {
$route = 'piggybanks.index.repeated';
$message = 'Repeated expense';
} else {
$route = 'piggybanks.index.piggybanks';
$message = 'Piggybank';
}
$this->_repository->destroy($piggyBank);
/** @var \FireflyIII\Database\Piggybank $acct */
$repos = App::make('FireflyIII\Database\Piggybank');
$repos->destroy($piggyBank);
Session::flash('success', 'Piggy bank deleted.');
Session::flash('success', $message . ' deleted.');
return Redirect::route($route);
return Redirect::route('piggybanks.index');
}
/**
* @param Piggybank $piggyBank
* @param Piggybank $piggybank
*
* @return $this
*/
public function edit(Piggybank $piggyBank)
public function edit(Piggybank $piggybank)
{
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
$list = $this->_accounts->getActiveDefault();
$accounts = $toolkit->makeSelectList($list);
$periods = Config::get('firefly.piggybank_periods');
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
$periods = Config::get('firefly.piggybank_periods');
View::share('subTitle', 'Edit "' . $piggyBank->name . '"');
if ($piggyBank->repeats == 1) {
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-left');
return View::make('piggybanks.edit-repeated')->with('piggybank', $piggyBank)->with('accounts', $accounts)
->with('periods', $periods);
} else {
// piggy bank.
View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
return View::make('piggybanks.edit-piggybank')->with('piggybank', $piggyBank)->with('accounts', $accounts)
->with('periods', $periods);
}
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
/*
* Flash some data to fill the form.
*/
$prefilled = ['name' => $piggybank->name,
'account_id' => $piggybank->account_id,
'targetamount' => $piggybank->targetamount,
'targetdate' => !is_null($piggybank->targetdate) ? $piggybank->targetdate->format('Y-m-d') : null,
'reminder' => $piggybank->reminder,
'remind_me' => intval($piggybank->remind_me) == 1 || !is_null($piggybank->reminder) ? true : false
];
Session::flash('prefilled', $prefilled);
return View::make('piggybanks.edit', compact('piggybank', 'accounts', 'periods', 'prefilled'))->with('title', 'Piggybanks')->with(
'mainTitleIcon', 'fa-sort-amount-asc'
)->with('subTitle', 'Edit piggy bank "' . e($piggybank->name) . '"')->with('subTitleIcon', 'fa-pencil');
}
/**
* @param Piggybank $piggyBank
* @return $this
*/
public function index()
{
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
/** @var Collection $piggybanks */
$piggybanks = $repos->get();
$accounts = [];
/** @var Piggybank $piggybank */
foreach ($piggybanks as $piggybank) {
$piggybank->savedSoFar = floatval($piggybank->currentRelevantRep()->currentamount);
$piggybank->percentage = intval($piggybank->savedSoFar / $piggybank->targetamount * 100);
$piggybank->leftToSave = $piggybank->targetamount - $piggybank->savedSoFar;
/*
* Fill account information:
*/
$account = $piggybank->account;
if (!isset($accounts[$account->id])) {
$accounts[$account->id] = ['name' => $account->name, 'balance' => Steam::balance($account),
'leftForPiggybanks' => $repos->leftOnAccount($account), 'sumOfSaved' => $piggybank->savedSoFar,
'sumOfTargets' => floatval($piggybank->targetamount), 'leftToSave' => $piggybank->leftToSave];
} else {
$accounts[$account->id]['sumOfSaved'] += $piggybank->savedSoFar;
$accounts[$account->id]['sumOfTargets'] += floatval($piggybank->targetamount);
$accounts[$account->id]['leftToSave'] += $piggybank->leftToSave;
}
}
return View::make('piggybanks.index', compact('piggybanks', 'accounts'))->with('title', 'Piggy banks')->with('mainTitleIcon', 'fa-sort-amount-asc');
}
/**
* POST add money to piggy bank
*
* @param Piggybank $piggybank
*
* @return \Illuminate\Http\RedirectResponse
* @throws Firefly\Exception\FireflyException
*/
public function modMoney(Piggybank $piggyBank)
public function postAdd(Piggybank $piggybank)
{
$amount = round(floatval(Input::get('amount')), 2);
/** @var \FireflyIII\Database\Piggybank $acct */
$repos = App::make('FireflyIII\Database\Piggybank');
$leftOnAccount = $repos->leftOnAccount($piggybank->account);
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
$leftToSave = $piggybank->targetamount - $savedSoFar;
$maxAmount = round(min($leftOnAccount, $leftToSave), 2);
if ($amount <= $maxAmount) {
$repetition = $piggybank->currentRelevantRep();
$repetition->currentamount += $amount;
$repetition->save();
/*
* Create event!
*/
Event::fire('piggybank.addMoney', [$piggybank, $amount]); // new and used.
Session::flash('success', 'Added ' . mf($amount, false) . ' to "' . e($piggybank->name) . '".');
} else {
Session::flash('error', 'Could not add ' . mf($amount, false) . ' to "' . e($piggybank->name) . '".');
}
return Redirect::route('piggybanks.index');
}
/**
* @param Piggybank $piggybank
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postRemove(Piggybank $piggybank)
{
$amount = floatval(Input::get('amount'));
switch (Input::get('what')) {
$savedSoFar = $piggybank->currentRelevantRep()->currentamount;
if ($amount <= $savedSoFar) {
$repetition = $piggybank->currentRelevantRep();
$repetition->currentamount -= $amount;
$repetition->save();
/*
* Create event!
*/
Event::fire('piggybank.removeMoney', [$piggybank, $amount]); // new and used.
Session::flash('success', 'Removed ' . mf($amount, false) . ' from "' . e($piggybank->name) . '".');
} else {
Session::flash('error', 'Could not remove ' . mf($amount, false) . ' from "' . e($piggybank->name) . '".');
}
return Redirect::route('piggybanks.index');
}
/**
* @param Piggybank $piggybank
*
* @return \Illuminate\View\View
*/
public function remove(Piggybank $piggybank)
{
return View::make('piggybanks.remove', compact('piggybank'));
}
/**
* @param Piggybank $piggybank
*
* @return $this
*/
public function show(Piggybank $piggybank)
{
$events = $piggybank->piggybankevents()->orderBy('date', 'DESC')->orderBy('id', 'DESC')->get();
/*
* Number of reminders:
*/
$amountPerReminder = $piggybank->amountPerReminder();
$remindersCount = $piggybank->countFutureReminders();
return View::make('piggybanks.show', compact('amountPerReminder', 'remindersCount', 'piggybank', 'events'))->with('title', 'Piggy banks')->with(
'mainTitleIcon', 'fa-sort-amount-asc'
)->with(
'subTitle', $piggybank->name
);
}
/**
*
*/
public function store()
{
$data = Input::all();
$data['repeats'] = 0;
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
switch ($data['post_submit_action']) {
default:
throw new FireflyException('No such action');
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'add':
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
if (round($amount, 2) <= round(min($maxAdd, $piggyBank->targetamount), 2)) {
Session::flash('success', 'Amount updated!');
$this->_repository->modifyAmount($piggyBank, $amount);
Event::fire('piggybanks.modifyAmountAdd', [$piggyBank, $amount]);
case 'create_another':
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save piggy bank: ' . $messages['errors']->first());
return Redirect::route('piggybanks.create')->withInput()->withErrors($messages['errors']);
}
// store!
$piggyBank = $repos->store($data);
/*
* Create the relevant repetition per Event.
*/
Event::fire('piggybank.store', [$piggyBank]); // new and used.
Session::flash('success', 'New piggy bank stored!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('piggybanks.create')->withInput();
} else {
Session::flash('warning', 'Could not!');
return Redirect::route('piggybanks.index');
}
break;
case 'remove':
$rep = $piggyBank->currentRelevantRep();
$maxRemove = $rep->currentamount;
if (round($amount, 2) <= round($maxRemove, 2)) {
Session::flash('success', 'Amount updated!');
$this->_repository->modifyAmount($piggyBank, ($amount * -1));
Event::fire('piggybanks.modifyAmountRemove', [$piggyBank, ($amount * -1)]);
} else {
Session::flash('warning', 'Could not!');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('piggybanks.create')->withInput();
break;
}
if($piggyBank->repeats == 1) {
$route = 'piggybanks.index.repeated';
} else {
$route = 'piggybanks.index.piggybanks';
}
return Redirect::route($route);
}
/**
* @return $this
*/
public function piggybanks()
{
$countRepeating = $this->_repository->countRepeating();
$countNonRepeating = $this->_repository->countNonrepeating();
$piggybanks = $this->_repository->get();
// get the accounts with each piggy bank and check their balance; Fireflyy might needs to
// show the user a correction.
$accounts = [];
/** @var \Piggybank $piggybank */
foreach ($piggybanks as $piggybank) {
$account = $piggybank->account;
$id = $account->id;
if (!isset($accounts[$id])) {
$account->leftOnAccount = $this->_repository->leftOnAccount($account);
$accounts[$id] = ['account' => $account, 'left' => $this->_repository->leftOnAccount($account)];
}
}
View::share('title', 'Piggy banks');
View::share('subTitle', 'Save for big expenses');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
return View::make('piggybanks.index')->with('piggybanks', $piggybanks)
->with('countRepeating', $countRepeating)
->with('countNonRepeating', $countNonRepeating)
->with('accounts', $accounts);
}
/**
* @param Piggybank $piggyBank
*
* @return $this
*/
public function removeMoney(Piggybank $piggyBank)
{
$what = 'remove';
$maxAdd = $this->_repository->leftOnAccount($piggyBank->account);
$maxRemove = $piggyBank->currentRelevantRep()->currentamount;
return View::make('piggybanks.modifyAmount')->with('what', $what)->with('maxAdd', $maxAdd)->with(
'maxRemove', $maxRemove
)->with('piggybank', $piggyBank);
}
/**
* @return $this
*/
public function repeated()
{
$countRepeating = $this->_repository->countRepeating();
$countNonRepeating = $this->_repository->countNonrepeating();
$piggybanks = $this->_repository->get();
// get the accounts with each piggy bank and check their balance; Fireflyy might needs to
// show the user a correction.
$accounts = [];
/** @var \Piggybank $piggybank */
foreach ($piggybanks as $piggybank) {
$account = $piggybank->account;
$id = $account->id;
if (!isset($accounts[$id])) {
$account->leftOnAccount = $this->_repository->leftOnAccount($account);
$accounts[$id] = ['account' => $account, 'left' => $this->_repository->leftOnAccount($account)];
}
}
View::share('title', 'Repeated expenses');
View::share('subTitle', 'Save for returning bills');
View::share('mainTitleIcon', 'fa-rotate-left');
return View::make('piggybanks.index')->with('piggybanks', $piggybanks)
->with('countRepeating', $countRepeating)
->with('countNonRepeating', $countNonRepeating)
->with('accounts', $accounts);
}
/**
*
*/
public function show(Piggybank $piggyBank)
{
$leftOnAccount = $this->_repository->leftOnAccount($piggyBank->account);
$balance = $piggyBank->account->balance();
View::share('subTitle', $piggyBank->name);
if ($piggyBank->repeats == 1) {
// repeated expense.
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-left');
} else {
// piggy bank.
View::share('title', 'Piggy banks');
View::share('mainTitleIcon', 'fa-sort-amount-asc');
}
return View::make('piggybanks.show')->with('piggyBank', $piggyBank)->with('leftOnAccount', $leftOnAccount)
->with('balance', $balance);
}
/**
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function storePiggybank()
{
$data = Input::all();
unset($data['_token']);
// extend the data array with the settings needed to create a piggy bank:
$data['repeats'] = 0;
$data['rep_times'] = 1;
$data['rep_every'] = 1;
$data['order'] = 0;
$piggyBank = $this->_repository->store($data);
if (!is_null($piggyBank->id)) {
Session::flash('success', 'New piggy bank "' . $piggyBank->name . '" created!');
Event::fire('piggybanks.store', [$piggyBank]);
return Redirect::route('piggybanks.index.piggybanks');
} else {
Session::flash('error', 'Could not save piggy bank: ' . $piggyBank->errors()->first());
return Redirect::route('piggybanks.create.piggybank')->withInput()->withErrors($piggyBank->errors());
}
}
/**
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function storeRepeated()
{
$data = Input::all();
unset($data['_token']);
// extend the data array with the settings needed to create a repeated:
$data['repeats'] = 1;
$data['order'] = 0;
$piggyBank = $this->_repository->store($data);
if ($piggyBank->id) {
Session::flash('success', 'New piggy bank "' . $piggyBank->name . '" created!');
Event::fire('piggybanks.store', [$piggyBank]);
return Redirect::route('piggybanks.index.repeated');
} else {
Session::flash('error', 'Could not save piggy bank: ' . $piggyBank->errors()->first());
return Redirect::route('piggybanks.create.repeated')->withInput()->withErrors($piggyBank->errors());
}
}
/**
* @param Piggybank $piggyBank
*
* @return $this|\Illuminate\Http\RedirectResponse
* @throws FireflyException
*/
public function update(Piggybank $piggyBank)
{
$piggyBank = $this->_repository->update($piggyBank, Input::all());
if ($piggyBank->validate()) {
if ($piggyBank->repeats == 1) {
$route = 'piggybanks.index.repeated';
$message = 'Repeated expense';
} else {
$route = 'piggybanks.index.piggybanks';
$message = 'Piggy bank';
}
/** @var \FireflyIII\Database\Piggybank $repos */
$repos = App::make('FireflyIII\Database\Piggybank');
$data = Input::except('_token');
Session::flash('success', $message . ' "' . $piggyBank->name . '" updated.');
Event::fire('piggybanks.update', [$piggyBank]);
switch (Input::get('post_submit_action')) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'return_to_edit':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save piggy bank: ' . $messages['errors']->first());
return Redirect::route($route);
} else {
Session::flash('error', 'Could not update piggy bank: ' . $piggyBank->errors()->first());
return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($piggyBank, $data);
Event::fire('piggybank.update', [$piggyBank]); // new and used.
Session::flash('success', 'Piggy bank updated!');
return Redirect::route('piggybanks.edit', $piggyBank->id)->withErrors($piggyBank->errors())->withInput();
if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('piggybanks.edit', $piggyBank->id);
} else {
return Redirect::route('piggybanks.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('piggybanks.edit', $piggyBank->id)->withInput();
break;
}

View File

@@ -1,29 +1,19 @@
<?php
use Firefly\Helper\Preferences\PreferencesHelperInterface as PHI;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
/**
* Class PreferencesController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class PreferencesController extends BaseController
{
protected $_accounts;
protected $_preferences;
/**
* @param ARI $accounts
* @param PHI $preferences
*
*/
public function __construct(ARI $accounts, PHI $preferences)
public function __construct()
{
$this->_accounts = $accounts;
$this->_preferences = $preferences;
View::share('title','Preferences');
View::share('mainTitleIcon','fa-gear');
View::share('title', 'Preferences');
View::share('mainTitleIcon', 'fa-gear');
}
/**
@@ -31,16 +21,18 @@ class PreferencesController extends BaseController
*/
public function index()
{
$accounts = $this->_accounts->getDefault();
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
$viewRange = $this->_preferences->get('viewRange', '1M');
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$accounts = $acct->getAssetAccounts();
$viewRange = $preferences->get('viewRange', '1M');
$viewRangeValue = $viewRange->data;
$frontpage = $preferences->get('frontpageAccounts', []);
// pref:
$frontpage = $this->_preferences->get('frontpageAccounts', []);
return View::make('preferences.index')->with('accounts', $accounts)->with('frontpageAccounts', $frontpage)
->with('viewRange', $viewRangeValue);
return View::make('preferences.index')->with('accounts', $accounts)->with('frontpageAccounts', $frontpage)->with('viewRange', $viewRangeValue);
}
/**
@@ -49,15 +41,18 @@ class PreferencesController extends BaseController
public function postIndex()
{
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
// frontpage accounts
$frontpageAccounts = [];
foreach (Input::get('frontpageAccounts') as $id) {
$frontpageAccounts[] = intval($id);
}
$this->_preferences->set('frontpageAccounts', $frontpageAccounts);
$preferences->set('frontpageAccounts', $frontpageAccounts);
// view range:
$this->_preferences->set('viewRange', Input::get('viewRange'));
$preferences->set('viewRange', Input::get('viewRange'));
// forget session values:
Session::forget('start');
Session::forget('end');

View File

@@ -1,7 +1,5 @@
<?php
use Firefly\Storage\User\UserRepositoryInterface as URI;
/**
* Class ProfileController
*/
@@ -9,11 +7,13 @@ class ProfileController extends BaseController
{
/**
* @param URI $user
* @return \Illuminate\View\View
*/
public function __construct(URI $user)
public function changePassword()
{
$this->user = $user;
return View::make('profile.change-password')->with('title', Auth::user()->email)->with('subTitle', 'Change your password')->with(
'mainTitleIcon', 'fa-user'
);
}
/**
@@ -22,21 +22,7 @@ class ProfileController extends BaseController
*/
public function index()
{
View::share('title','Profile');
View::share('subTitle',Auth::user()->email);
View::share('mainTitleIcon','fa-user');
return View::make('profile.index');
}
/**
* @return \Illuminate\View\View
*/
public function changePassword()
{
View::share('title',Auth::user()->email);
View::share('subTitle','Change your password');
View::share('mainTitleIcon','fa-user');
return View::make('profile.change-password');
return View::make('profile.index')->with('title', 'Profile')->with('subTitle', Auth::user()->email)->with('mainTitleIcon', 'fa-user');
}
/**
@@ -70,8 +56,9 @@ class ProfileController extends BaseController
}
// update the user with the new password.
/** @noinspection PhpParamsInspection */
$this->user->updatePassword(Auth::user(), Input::get('new1'));
/** @var \FireflyIII\Database\User $repository */
$repository = \App::make('FireflyIII\Database\User');
$repository->updatePassword(Auth::user(), Input::get('new1'));
Session::flash('success', 'Password changed!');

View File

@@ -1,27 +1,18 @@
<?php
use Firefly\Exception\FireflyException;
use Firefly\Storage\RecurringTransaction\RecurringTransactionRepositoryInterface as RTR;
use Firefly\Helper\Controllers\RecurringInterface as RI;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
* Class RecurringController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class RecurringController extends BaseController
{
protected $_repository;
protected $_helper;
/**
* @param RTR $repository
* @param RI $helper
*
*/
public function __construct(RTR $repository, RI $helper)
public function __construct()
{
$this->_repository = $repository;
$this->_helper = $helper;
View::share('title', 'Recurring transactions');
View::share('mainTitleIcon', 'fa-rotate-right');
@@ -32,10 +23,9 @@ class RecurringController extends BaseController
*/
public function create()
{
View::share('subTitle', 'Create new');
$periods = \Config::get('firefly.periods_to_text');
return View::make('recurring.create')->with('periods', $periods);
return View::make('recurring.create')->with('periods', $periods)->with('subTitle', 'Create new');
}
/**
@@ -45,8 +35,9 @@ class RecurringController extends BaseController
*/
public function delete(RecurringTransaction $recurringTransaction)
{
View::share('subTitle', 'Delete "' . $recurringTransaction->name . '"');
return View::make('recurring.delete')->with('recurringTransaction', $recurringTransaction);
return View::make('recurring.delete')->with('recurringTransaction', $recurringTransaction)->with(
'subTitle', 'Delete "' . $recurringTransaction->name . '"'
);
}
/**
@@ -57,7 +48,11 @@ class RecurringController extends BaseController
public function destroy(RecurringTransaction $recurringTransaction)
{
//Event::fire('recurring.destroy', [$recurringTransaction]);
$result = $this->_repository->destroy($recurringTransaction);
/** @var \FireflyIII\Database\Recurring $repository */
$repository = App::make('FireflyIII\Database\Recurring');
$result = $repository->destroy($recurringTransaction);
if ($result === true) {
Session::flash('success', 'The recurring transaction was deleted.');
} else {
@@ -77,10 +72,8 @@ class RecurringController extends BaseController
{
$periods = \Config::get('firefly.periods_to_text');
View::share('subTitle', 'Edit "' . $recurringTransaction->name . '"');
return View::make('recurring.edit')->with('periods', $periods)->with(
'recurringTransaction', $recurringTransaction
return View::make('recurring.edit')->with('periods', $periods)->with('recurringTransaction', $recurringTransaction)->with(
'subTitle', 'Edit "' . $recurringTransaction->name . '"'
);
}
@@ -89,125 +82,144 @@ class RecurringController extends BaseController
*/
public function index()
{
return View::make('recurring.index');
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
$recurring = $repos->get();
return View::make('recurring.index', compact('recurring'));
}
/**
* @param RecurringTransaction $recurringTransaction
*
* @return mixed
*/
public function rescan(RecurringTransaction $recurringTransaction)
{
if (intval($recurringTransaction->active) == 0) {
Session::flash('warning', 'Inactive recurring transactions cannot be scanned.');
return Redirect::back();
}
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
$repos->scanEverything($recurringTransaction);
Session::flash('success', 'Rescanned everything.');
return Redirect::back();
}
/**
* @param RecurringTransaction $recurringTransaction
*
* @return mixed
*/
public function show(RecurringTransaction $recurringTransaction)
{
View::share('subTitle', $recurringTransaction->name);
return View::make('recurring.show')->with('recurring', $recurringTransaction);
$journals = $recurringTransaction->transactionjournals()->withRelevantData()->orderBy('date', 'DESC')->get();
$hideRecurring = true;
return View::make('recurring.show', compact('journals', 'hideRecurring', 'finalDate'))->with('recurring', $recurringTransaction)->with(
'subTitle', $recurringTransaction->name
);
}
/**
* @return $this
* @throws FireflyException
*/
public function store()
{
$data = Input::except(['_token', 'post_submit_action']);
switch (Input::get('post_submit_action')) {
$data = Input::except('_token');
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.');
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'store':
case 'create_another':
/*
* Try to store:
*/
$messageBag = $this->_repository->store($data);
case 'store':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save recurring transaction: ' . $messages['errors']->first());
/*
* Failure!
*/
if ($messageBag->count() > 0) {
Session::flash('error', 'Could not save recurring transaction: ' . $messageBag->first());
return Redirect::route('recurring.create')->withInput()->withErrors($messageBag);
return Redirect::route('recurring.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repos->store($data);
Session::flash('success', 'New recurring transaction stored!');
/*
* Success!
*/
Session::flash('success', 'Recurring transaction "' . e(Input::get('name')) . '" saved!');
/*
* Redirect to original location or back to the form.
*/
if (Input::get('post_submit_action') == 'create_another') {
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('recurring.create')->withInput();
} else {
return Redirect::route('recurring.index');
}
break;
case 'validate_only':
$messageBags = $this->_helper->validate($data);
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('recurring.create')->withInput();
break;
}
}
/**
* @param RecurringTransaction $recurringTransaction
*
* @return $this
* @throws FireflyException
*/
public function update(RecurringTransaction $recurringTransaction)
{
$data = Input::except(['_token', 'post_submit_action']);
/** @var \FireflyIII\Database\Recurring $repos */
$repos = App::make('FireflyIII\Database\Recurring');
$data = Input::except('_token');
switch (Input::get('post_submit_action')) {
case 'update':
case 'return_to_edit':
$messageBag = $this->_repository->update($recurringTransaction, $data);
if ($messageBag->count() == 0) {
// has been saved, return to index:
Session::flash('success', 'Recurring transaction updated!');
if (Input::get('post_submit_action') == 'return_to_edit') {
return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput();
} else {
return Redirect::route('recurring.index');
}
} else {
Session::flash('error', 'Could not update recurring transaction: ' . $messageBag->first());
return Redirect::route('transactions.edit', $recurringTransaction->id)->withInput()
->withErrors($messageBag);
}
default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'validate_only':
$data = Input::all();
$data['id'] = $recurringTransaction->id;
$messageBags = $this->_helper->validate($data);
case 'create_another':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save recurring transaction: ' . $messages['errors']->first());
return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($recurringTransaction, $data);
Session::flash('success', 'Recurring transaction updated!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('recurring.edit', $recurringTransaction->id);
} else {
return Redirect::route('recurring.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput();
break;
// update
default:
throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.');
return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput();
break;
}
// /** @var \RecurringTransaction $recurringTransaction */
// $recurringTransaction = $this->_repository->update($recurringTransaction, Input::all());
// if ($recurringTransaction->errors()->count() == 0) {
// Session::flash('success', 'The recurring transaction has been updated.');
// //Event::fire('recurring.update', [$recurringTransaction]);
//
// return Redirect::route('recurring.index');
// } else {
// Session::flash(
// 'error', 'Could not update the recurring transaction: ' . $recurringTransaction->errors()->first()
// );
//
// return Redirect::route('recurring.edit', $recurringTransaction->id)->withInput()->withErrors(
// $recurringTransaction->errors()
// );
// }
}
}

View File

@@ -1,97 +1,96 @@
<?php
use Carbon\Carbon;
use Firefly\Storage\Reminder\ReminderRepositoryInterface as RRI;
use FireflyIII\Exception\FireflyException;
/**
* Class ReminderController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/
class ReminderController extends BaseController
{
protected $_repository;
/**
* @param RRI $repository
*
*/
public function __construct(RRI $repository)
public function __construct()
{
$this->_repository = $repository;
View::share('title', 'Reminders');
View::share('mainTitleIcon', 'fa-lightbulb-o');
}
/**
* @param Reminder $reminder
*
* @return \Illuminate\Http\JsonResponse
* @return \Illuminate\Http\RedirectResponse
* @throws FireflyException
*/
public function dismiss(\Reminder $reminder)
public function act(Reminder $reminder)
{
$reminder = $this->_repository->deactivate($reminder);
return Response::json($reminder->id);
switch (get_class($reminder->remindersable)) {
default:
throw new FireflyException('Cannot act on reminder for ' . get_class($reminder->remindersable));
break;
break;
case 'Piggybank':
$amount = Reminders::amountForReminder($reminder);
$prefilled = [
'amount' => round($amount, 2),
'description' => 'Money for ' . $reminder->remindersable->name,
'piggybank_id' => $reminder->remindersable_id,
'account_to_id' => $reminder->remindersable->account_id
];
Session::flash('prefilled', $prefilled);
return Redirect::route('transactions.create', 'transfer');
break;
}
}
/**
* Returns the reminders currently active for the modal dialog.
* @param Reminder $reminder
*
* @return \Illuminate\Http\RedirectResponse
*/
public function modalDialog()
public function dismiss(Reminder $reminder)
{
$today = new Carbon;
$reminders = $this->_repository->getPiggybankReminders();
$reminder->active = 0;
$reminder->save();
Session::flash('success', 'Reminder dismissed');
/** @var \Reminder $reminder */
foreach ($reminders as $index => $reminder) {
if (\Session::has('dismissal-' . $reminder->id)) {
$time = \Session::get('dismissal-' . $reminder->id);
if ($time >= $today) {
unset($reminders[$index]);
}
return Redirect::route('index');
}
}
/**
* @param Reminder $reminder
*
* @return \Illuminate\Http\RedirectResponse
*/
public function notnow(Reminder $reminder)
{
$reminder->active = 0;
$reminder->notnow = 1;
$reminder->save();
Session::flash('success', 'Reminder dismissed');
return Redirect::route('index');
}
/**
* @param Reminder $reminder
*
* @return \Illuminate\View\View
*/
public function show(Reminder $reminder)
{
$amount = null;
if (get_class($reminder->remindersable) == 'Piggybank') {
$amount = Reminders::amountForReminder($reminder);
}
return View::make('reminders.popup')->with('reminders', $reminders);
return View::make('reminders.show', compact('reminder', 'amount'));
}
/**
* @param Reminder $reminder
*
* @return \Illuminate\Http\JsonResponse
*/
public function postpone(\Reminder $reminder)
{
$now = new Carbon;
$now->addDay();
Session::put('dismissal-' . $reminder->id, $now);
return Response::json($reminder->id);
}
/**
* @param Reminder $reminder
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function redirect(\Reminder $reminder)
{
if ($reminder instanceof PiggybankReminder) {
// fields to prefill:
$parameters = [
'account_to_id' => $reminder->piggybank->account->id,
'amount' => round($reminder->amountToSave(), 2),
'description' => 'Money for ' . $reminder->piggybank->name,
'piggybank_id' => $reminder->piggybank->id,
'reminder_id' => $reminder->id
];
return Redirect::to(
route('transactions.create', ['what' => 'transfer']) . '?' . http_build_query($parameters)
);
}
return View::make('error')->with('message', 'No such reminder.');
}
}
}

View File

@@ -0,0 +1,135 @@
<?php
use Carbon\Carbon;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\MessageBag;
/**
* Class RepeatedExpenseController
*/
class RepeatedExpenseController extends BaseController
{
/**
*
*/
public function __construct()
{
View::share('title', 'Repeated expenses');
View::share('mainTitleIcon', 'fa-rotate-left');
}
/**
* @return $this
*/
public function create()
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
$periods = Config::get('firefly.piggybank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
return View::make('repeatedexpense.create', compact('accounts', 'periods'))->with('subTitle', 'Create new repeated expense')->with(
'subTitleIcon', 'fa-plus'
);
}
/**
* @return \Illuminate\View\View
*/
public function index()
{
$subTitle = 'Overview';
/** @var \FireflyIII\Database\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\RepeatedExpense');
$expenses = $repository->get();
$expenses->each(
function (Piggybank $piggyBank) use ($repository) {
$piggyBank->currentRelevantRep();
}
);
return View::make('repeatedexpense.index', compact('expenses', 'subTitle'));
}
/**
* @param Piggybank $piggyBank
*
* @return \Illuminate\View\View
*/
public function show(Piggybank $piggyBank)
{
$subTitle = $piggyBank->name;
$today = Carbon::now();
/** @var \FireflyIII\Database\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\RepeatedExpense');
$repetitions = $piggyBank->piggybankrepetitions()->get();
$repetitions->each(
function (PiggybankRepetition $repetition) use ($repository) {
$repository->calculateParts($repetition);
}
);
return View::make('repeatedexpense.show', compact('repetitions', 'piggyBank', 'today', 'subTitle'));
}
/**
* @return $this
* @throws FireflyException
*/
public function store()
{
$data = Input::all();
$data['repeats'] = 1;
/** @var \FireflyIII\Database\RepeatedExpense $repository */
$repository = App::make('FireflyIII\Database\RepeatedExpense');
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
case 'store':
$messages = $repository->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save repeated expense: ' . $messages['errors']->first());
return Redirect::route('repeated.create')->withInput()->withErrors($messages['errors']);
}
// store!
$repeated = $repository->store($data);
/*
* Create the relevant repetition per Event.
*/
Event::fire('piggybank.store', [$repeated]); // new and used.
Session::flash('success', 'New repeated expense stored!');
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('repeated.create')->withInput();
} else {
return Redirect::route('repeated.index');
}
break;
case 'validate_only':
$messageBags = $repository->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('repeated.create')->withInput();
break;
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
use Carbon\Carbon;
/**
* Class ReportController
@@ -6,13 +7,304 @@
class ReportController extends BaseController
{
/**
* @param $year
* @param $month
*
* @return \Illuminate\View\View
*/
public function budgets($year, $month)
{
try {
$start = new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
App::abort(500);
}
$end = clone $start;
$title = 'Reports';
$subTitle = 'Budgets in ' . $start->format('F Y');
$mainTitleIcon = 'fa-line-chart';
$subTitleIcon = 'fa-bar-chart';
$end->endOfMonth();
// get a list of all budgets and expenses.
/** @var \FireflyIII\Database\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget');
/** @var \FireflyIII\Database\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account');
$budgets = $budgetRepository->get();
// calculate some stuff:
$budgets->each(
function (Budget $budget) use ($start, $end, $budgetRepository) {
$limitRepetitions = $budget->limitrepetitions()->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d'))->where(
'enddate', '<=', $end->format(
'Y-m-d'
)
)->get();
$repInfo = [];
/** @var LimitRepetition $repetition */
foreach ($limitRepetitions as $repetition) {
$spent = $budgetRepository->spentInPeriod($budget, $start, $end);
if ($spent > floatval($repetition->amount)) {
// overspent!
$overspent = true;
$pct = floatval($repetition->amount) / $spent * 100;
} else {
$overspent = false;
$pct = $spent / floatval($repetition->amount) * 100;
}
$pctDisplay = $spent / floatval($repetition->amount) * 100;
$repInfo[] = [
'date' => DateKit::periodShow($repetition->startdate, $repetition->limit->repeat_freq),
'spent' => $spent,
'budgeted' => floatval($repetition->amount),
'left' => floatval($repetition->amount) - $spent,
'pct' => ceil($pct),
'pct_display' => ceil($pctDisplay),
'overspent' => $overspent,
];
}
$budget->repInfo = $repInfo;
}
);
$accounts = $accountRepository->getAssetAccounts();
$accounts->each(
function (Account $account) use ($start, $end, $accountRepository) {
$journals = $accountRepository->getTransactionJournalsInRange($account, $start, $end);
$budgets = [];
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$budgetId = isset($journal->budgets[0]) ? $journal->budgets[0]->id : 0;
$budgetName = isset($journal->budgets[0]) ? $journal->budgets[0]->name : '(no budget)';
if (!isset($budgets[$budgetId])) {
$arr = [
'budget_id' => $budgetId,
'budget_name' => $budgetName,
'spent' => floatval($journal->getAmount()),
'budgeted' => 0,
];
$budgets[$budgetId] = $arr;
} else {
$budgets[$budgetId]['spent'] += floatval($journal->getAmount());
}
}
foreach ($budgets as $budgetId => $budget) {
$budgets[$budgetId]['left'] = $budget['budgeted'] - $budget['spent'];
}
$account->budgetInfo = $budgets;
}
);
return View::make('reports.budgets', compact('start', 'end', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon', 'budgets', 'accounts'));
}
/**
*
*/
public function index()
{
return View::make('reports.index')->with('title','Reports')->with('mainTitleIcon','fa-line-chart');
/** @var \FireflyIII\Database\TransactionJournal $journals */
$journals = App::make('FireflyIII\Database\TransactionJournal');
/** @var TransactionJournal $journal */
$journal = $journals->first();
if (is_null($journal)) {
$date = Carbon::now();
} else {
$date = clone $journal->date;
}
$years = [];
$months = [];
while ($date <= Carbon::now()) {
$years[] = $date->format('Y');
$date->addYear();
}
// months
if (is_null($journal)) {
$date = Carbon::now();
} else {
$date = clone $journal->date;
}
while ($date <= Carbon::now()) {
$months[] = [
'formatted' => $date->format('F Y'),
'month' => intval($date->format('m')),
'year' => intval($date->format('Y')),
];
$date->addMonth();
}
return View::make('reports.index', compact('years', 'months'))->with('title', 'Reports')->with('mainTitleIcon', 'fa-line-chart');
}
/**
* @param $year
* @param $month
*
* @return \Illuminate\View\View
*/
public function unbalanced($year, $month)
{
try {
$date = new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
App::abort(500);
}
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$title = 'Reports';
$subTitle = 'Unbalanced transactions in ' . $start->format('F Y');
$mainTitleIcon = 'fa-line-chart';
$subTitleIcon = 'fa-bar-chart';
$end->endOfMonth();
/** @var \FireflyIII\Database\TransactionJournal $journalRepository */
$journalRepository = App::make('FireflyIII\Database\TransactionJournal');
/*
* Get all journals from this month:
*/
$journals = $journalRepository->getInDateRange($start, $end);
/*
* Filter withdrawals:
*/
$withdrawals = $journals->filter(
function (TransactionJournal $journal) {
if ($journal->transactionType->type == 'Withdrawal' && count($journal->budgets) == 0) {
// count groups related to balance.
if ($journal->transactiongroups()->where('relation', 'balance')->count() == 0) {
return $journal;
}
}
return null;
}
);
/*
* Filter deposits.
*/
$deposits = $journals->filter(
function (TransactionJournal $journal) {
if ($journal->transactionType->type == 'Deposit' && count($journal->budgets) == 0) {
// count groups related to balance.
if ($journal->transactiongroups()->where('relation', 'balance')->count() == 0) {
return $journal;
}
}
return null;
}
);
/*
* Filter transfers (not yet used)
*/
// $transfers = $journals->filter(
// function (TransactionJournal $journal) {
// if ($journal->transactionType->type == 'Transfer') {
// return $journal;
// }
// }
// );
$journals = $withdrawals->merge($deposits);
return View::make('reports.unbalanced', compact('start', 'end', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon', 'journals'));
}
/**
* @param $year
*
* @return $this
*/
public function year($year)
{
Config::set('app.debug', false);
try {
$date = new Carbon('01-01-' . $year);
} catch (Exception $e) {
App::abort(500);
}
$date = new Carbon('01-01-' . $year);
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = App::make('FireflyIII\Database\TransactionJournal');
/** @var \FireflyIII\Database\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\Report $reportRepository */
$reportRepository = App::make('FireflyIII\Database\Report');
$accounts = $accountRepository->getAssetAccounts();
// get some sums going
$summary = [];
/** @var \Account $account */
$accounts->each(
function (\Account $account) {
if ($account->getMeta('accountRole') == 'sharedExpense') {
$account->sharedExpense = true;
} else {
$account->sharedExpense = false;
}
}
);
$end = clone $date;
$end->endOfYear();
while ($date < $end) {
$month = $date->format('F');
$income = 0;
$incomeShared = 0;
$expense = 0;
$expenseShared = 0;
foreach ($accounts as $account) {
if ($account->sharedExpense === true) {
$incomeShared += $reportRepository->getIncomeByMonth($account, $date);
$expenseShared += $reportRepository->getExpenseByMonth($account, $date);
} else {
$income += $reportRepository->getIncomeByMonth($account, $date);
$expense += $reportRepository->getExpenseByMonth($account, $date);
}
}
$summary[] = [
'month' => $month,
'income' => $income,
'expense' => $expense,
'incomeShared' => $incomeShared,
'expenseShared' => $expenseShared,
];
$date->addMonth();
}
// draw some charts etc.
return View::make('reports.year', compact('summary', 'date'))->with('title', 'Reports')->with('mainTitleIcon', 'fa-line-chart')->with('subTitle', $year)
->with(
'subTitleIcon', 'fa-bar-chart'
)->with('year', $year);
}
}

View File

@@ -1,50 +1,38 @@
<?php
use Firefly\Helper\Controllers\SearchInterface as SI;
/**
* Class SearchController
*/
class SearchController extends BaseController
{
protected $_helper;
public function __construct(SI $helper)
{
$this->_helper = $helper;
}
/**
* Results always come in the form of an array [results, count, fullCount]
*/
public function index()
{
/** @var \FireflyIII\Search\Search $searcher */
$searcher = App::make('FireflyIII\Search\Search');
$subTitle = null;
$rawQuery = null;
$result = [];
$result = [];
if (!is_null(Input::get('q'))) {
$rawQuery = trim(Input::get('q'));
$words = explode(' ', $rawQuery);
$subTitle = 'Results for "' . e($rawQuery) . '"';
$transactions = $this->_helper->searchTransactions($words);
$accounts = $this->_helper->searchAccounts($words);
$categories = $this->_helper->searchCategories($words);
$budgets = $this->_helper->searchBudgets($words);
$tags = $this->_helper->searchTags($words);
$result = [
'transactions' => $transactions,
'accounts' => $accounts,
'categories' => $categories,
'budgets' => $budgets,
'tags' => $tags
];
$transactions = $searcher->searchTransactions($words);
$accounts = $searcher->searchAccounts($words);
$categories = $searcher->searchCategories($words);
$budgets = $searcher->searchBudgets($words);
$tags = $searcher->searchTags($words);
$result = ['transactions' => $transactions, 'accounts' => $accounts, 'categories' => $categories, 'budgets' => $budgets, 'tags' => $tags];
}
return View::make('search.index')->with('title', 'Search')->with('subTitle', $subTitle)->with(
'mainTitleIcon', 'fa-search'
)->with('query', $rawQuery)->with('result',$result);
)->with('query', $rawQuery)->with('result', $result);
}
}

View File

@@ -1,37 +1,67 @@
<?php
use Firefly\Exception\FireflyException;
use Firefly\Helper\Controllers\TransactionInterface as TI;
use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI;
use FireflyIII\Exception\FireflyException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
* Class TransactionController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*
*/
class TransactionController extends BaseController
{
protected $_helper;
protected $_repository;
/**
* Construct a new transaction controller with two of the most often used helpers.
*
* @param TJRI $repository
* @param TI $helper
*/
public function __construct(TJRI $repository, TI $helper)
public function __construct()
{
$this->_repository = $repository;
$this->_helper = $helper;
View::share('title', 'Transactions');
View::share('mainTitleIcon', 'fa-repeat');
}
/**
*
* TODO this needs cleaning up and thinking over.
*
* @param TransactionJournal $journal
*
* @return array|\Illuminate\Http\JsonResponse
*/
public function alreadyRelated(TransactionJournal $journal)
{
$ids = [];
/** @var TransactionGroup $group */
foreach ($journal->transactiongroups()->get() as $group) {
/** @var TransactionJournal $jrnl */
foreach ($group->transactionjournals()->get() as $jrnl) {
if ($jrnl->id != $journal->id) {
$ids[] = $jrnl->id;
}
}
}
$unique = array_unique($ids);
if (count($ids) > 0) {
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
$set = $repository->getByIds($ids);
$set->each(
function (TransactionJournal $journal) {
$journal->amount = mf($journal->getAmount());
}
);
return Response::json($set->toArray());
} else {
return (new Collection)->toArray();
}
}
/**
* Shows the view helping the user to create a new transaction journal.
*
@@ -44,28 +74,31 @@ class TransactionController extends BaseController
/*
* The repositories we need:
*/
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */
$accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface');
/** @var \FireflyIII\Database\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account');
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */
$budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
/** @var \FireflyIII\Database\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget');
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */
$piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
/** @var \FireflyIII\Database\Piggybank $piggyRepository */
$piggyRepository = App::make('FireflyIII\Database\Piggybank');
// get asset accounts with names and id's.
$assetAccounts = $toolkit->makeSelectList($accountRepository->getActiveDefault());
/** @var \FireflyIII\Database\RepeatedExpense $repRepository */
$repRepository = App::make('FireflyIII\Database\RepeatedExpense');
// get asset accounts with names and id's .
$assetAccounts = FFForm::makeSelectList($accountRepository->getAssetAccounts());
// get budgets as a select list.
$budgets = $toolkit->makeSelectList($budgetRepository->get());
$budgets = FFForm::makeSelectList($budgetRepository->get());
$budgets[0] = '(no budget)';
// get the piggy banks.
$piggies = $toolkit->makeSelectList($piggyRepository->get());
$list = $piggyRepository->get()->merge($repRepository->get());
$piggies = FFForm::makeSelectList($list);
$piggies[0] = '(no piggy bank)';
asort($piggies);
/*
* respond to a possible given values in the URL.
@@ -79,9 +112,8 @@ class TransactionController extends BaseController
}
Session::put('prefilled', $prefilled);
return View::make('transactions.create')->with('accounts', $assetAccounts)->with('budgets', $budgets)->with(
'what', $what
)->with('piggies', $piggies)->with('subTitle', 'Add a new ' . $what);
return View::make('transactions.create')->with('accounts', $assetAccounts)->with('budgets', $budgets)->with('what', $what)->with('piggies', $piggies)
->with('subTitle', 'Add a new ' . $what);
}
/**
@@ -102,7 +134,6 @@ class TransactionController extends BaseController
}
/**
* @param TransactionJournal $transactionJournal
*
@@ -111,19 +142,56 @@ class TransactionController extends BaseController
public function destroy(TransactionJournal $transactionJournal)
{
$type = $transactionJournal->transactionType->type;
$transactionJournal->delete();
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
$repository->destroy($transactionJournal);
$return = 'withdrawal';
switch ($type) {
case 'Withdrawal':
return Redirect::route('transactions.expenses');
break;
case 'Deposit':
return Redirect::route('transactions.revenue');
$return = 'deposit';
break;
case 'Transfer':
return Redirect::route('transactions.transfers');
$return = 'transfers';
break;
}
return Redirect::route('transactions.index', $return);
}
/**
* TODO this needs cleaning up and thinking over.
*
* @return \Illuminate\Http\JsonResponse
*/
public function doRelate()
{
$id = intval(Input::get('id'));
$sister = intval(Input::get('relateTo'));
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
$journal = $repository->find($id);
$sis = $repository->find($sister);
if ($journal && $sis) {
$group = new TransactionGroup;
$group->relation = 'balance';
$group->user_id = $repository->getUser()->id;
$group->save();
$group->transactionjournals()->save($journal);
$group->transactionjournals()->save($sis);
return Response::json(true);
}
return Response::json(false);
}
/**
@@ -138,26 +206,25 @@ class TransactionController extends BaseController
/*
* All the repositories we need:
*/
/** @var \Firefly\Helper\Toolkit\Toolkit $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\Toolkit');
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */
$accountRepository = App::make('Firefly\Storage\Account\AccountRepositoryInterface');
/** @var \FireflyIII\Database\Account $accountRepository */
$accountRepository = App::make('FireflyIII\Database\Account');
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */
$budgetRepository = App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
/** @var \FireflyIII\Database\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget');
/** @var \FireflyIII\Database\Piggybank $piggyRepository */
$piggyRepository = App::make('FireflyIII\Database\Piggybank');
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */
$piggyRepository = App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
// type is useful for display:
$what = strtolower($journal->transactiontype->type);
// get asset accounts with names and id's.
$accounts = $toolkit->makeSelectList($accountRepository->getActiveDefault());
$accounts = FFForm::makeSelectList($accountRepository->getAssetAccounts());
// get budgets as a select list.
$budgets = $toolkit->makeSelectList($budgetRepository->get());
$budgets = FFForm::makeSelectList($budgetRepository->get());
$budgets[0] = '(no budget)';
/*
@@ -165,8 +232,8 @@ class TransactionController extends BaseController
* of the transactions in the journal has this field, it should all fill in nicely.
*/
// get the piggy banks.
$piggies = $toolkit->makeSelectList($piggyRepository->get());
$piggies[0] = '(no piggy bank)';
$piggies = FFForm::makeSelectList($piggyRepository->get());
$piggies[0] = '(no piggy bank)';
$piggyBankId = 0;
foreach ($journal->transactions as $t) {
if (!is_null($t->piggybank_id)) {
@@ -198,29 +265,61 @@ class TransactionController extends BaseController
*/
switch ($what) {
case 'withdrawal':
$prefilled['account_id'] = $journal->transactions[0]->account->id;
$prefilled['expense_account'] = $journal->transactions[1]->account->name;
$prefilled['amount'] = floatval($journal->transactions[1]->amount);
if (floatval($journal->transactions[0]->amount) < 0) {
// transactions[0] is the asset account that paid for the withdrawal.
$prefilled['account_id'] = $journal->transactions[0]->account->id;
$prefilled['expense_account'] = $journal->transactions[1]->account->name;
$prefilled['amount'] = floatval($journal->transactions[1]->amount);
} else {
// transactions[1] is the asset account that paid for the withdrawal.
$prefilled['account_id'] = $journal->transactions[1]->account->id;
$prefilled['expense_account'] = $journal->transactions[0]->account->name;
$prefilled['amount'] = floatval($journal->transactions[0]->amount);
}
$budget = $journal->budgets()->first();
if (!is_null($budget)) {
$prefilled['budget_id'] = $budget->id;
}
break;
case 'deposit':
$prefilled['account_id'] = $journal->transactions[1]->account->id;
$prefilled['revenue_account'] = $journal->transactions[0]->account->name;
$prefilled['amount'] = floatval($journal->transactions[1]->amount);
if (floatval($journal->transactions[0]->amount) < 0) {
// transactions[0] contains the account the money came from.
$prefilled['account_id'] = $journal->transactions[1]->account->id;
$prefilled['revenue_account'] = $journal->transactions[0]->account->name;
$prefilled['amount'] = floatval($journal->transactions[1]->amount);
} else {
// transactions[1] contains the account the money came from.
$prefilled['account_id'] = $journal->transactions[0]->account->id;
$prefilled['revenue_account'] = $journal->transactions[1]->account->name;
$prefilled['amount'] = floatval($journal->transactions[0]->amount);
}
break;
case 'transfer':
$prefilled['account_from_id'] = $journal->transactions[1]->account->id;
$prefilled['account_to_id'] = $journal->transactions[0]->account->id;
$prefilled['amount'] = floatval($journal->transactions[1]->amount);
if (floatval($journal->transactions[0]->amount) < 0) {
// zero = from account.
$prefilled['account_from_id'] = $journal->transactions[0]->account->id;
$prefilled['account_to_id'] = $journal->transactions[1]->account->id;
$prefilled['amount'] = floatval($journal->transactions[1]->amount);
} else {
// one = from account
$prefilled['account_from_id'] = $journal->transactions[1]->account->id;
$prefilled['account_to_id'] = $journal->transactions[0]->account->id;
$prefilled['amount'] = floatval($journal->transactions[0]->amount);
}
if ($journal->piggybankevents()->count() > 0) {
$prefilled['piggybank_id'] = $journal->piggybankevents()->first()->piggybank_id;
}
break;
}
/*
* Show the view.
*/
return View::make('transactions.edit')->with('journal', $journal)->with('accounts', $accounts)->with(
'what', $what
)->with('budgets', $budgets)->with('data', $prefilled)->with('piggies', $piggies)->with(
@@ -229,23 +328,85 @@ class TransactionController extends BaseController
}
/**
* @param $what
*
* @return $this
*/
public function expenses()
public function index($what)
{
return View::make('transactions.list')->with('subTitle', 'Expenses')->with(
'subTitleIcon', 'fa-long-arrow-left'
)->with('what', 'expenses');
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
switch ($what) {
case 'expenses':
case 'withdrawal':
$subTitleIcon = 'fa-long-arrow-left';
$subTitle = 'Expenses';
$journals = $repository->getWithdrawalsPaginated(50);
break;
case 'revenue':
case 'deposit':
$subTitleIcon = 'fa-long-arrow-right';
$subTitle = 'Revenue, income and deposits';
$journals = $repository->getDepositsPaginated(50);
break;
case 'transfer':
case 'transfers':
$subTitleIcon = 'fa-arrows-h';
$subTitle = 'Transfers';
$journals = $repository->getTransfersPaginated(50);
break;
}
return View::make('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals'));
}
/**
* @return $this
* @param TransactionJournal $journal
*
* @return \Illuminate\View\View
*/
public function revenue()
public function relate(TransactionJournal $journal)
{
return View::make('transactions.list')->with('subTitle', 'Revenue')->with(
'subTitleIcon', 'fa-long-arrow-right'
)->with('what', 'revenue');
$groups = $journal->transactiongroups()->get();
$members = new Collection;
/** @var TransactionGroup $group */
foreach ($groups as $group) {
/** @var TransactionJournal $jrnl */
foreach ($group->transactionjournals()->get() as $jrnl) {
if ($jrnl->id != $journal->id) {
$members->push($jrnl);
}
}
}
return View::make('transactions.relate', compact('journal', 'members'));
}
/**
* TODO this needs cleaning up and thinking over.
*
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\JsonResponse
*/
public function relatedSearch(TransactionJournal $journal)
{
$search = e(trim(Input::get('searchValue')));
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
$result = $repository->searchRelated($search, $journal);
$result->each(
function (TransactionJournal $j) {
$j->amount = mf($j->getAmount());
}
);
return Response::json($result->toArray());
}
/**
@@ -255,7 +416,30 @@ class TransactionController extends BaseController
*/
public function show(TransactionJournal $journal)
{
return View::make('transactions.show')->with('journal', $journal)->with(
$journal->transactions->each(
function (\Transaction $t) use ($journal) {
$t->before = floatval(
$t->account->transactions()->leftJoin(
'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.id', '!=', $journal->id)->sum('transactions.amount')
);
$t->after = $t->before + $t->amount;
}
);
$members = new Collection;
/** @var TransactionGroup $group */
foreach ($journal->transactiongroups()->get() as $group) {
/** @var TransactionJournal $jrnl */
foreach ($group->transactionjournals()->get() as $jrnl) {
if ($jrnl->id != $journal->id) {
$members->push($jrnl);
}
}
}
return View::make('transactions.show', compact('journal', 'members'))->with(
'subTitle', $journal->transactionType->type . ' "' . $journal->description . '"'
);
}
@@ -268,62 +452,92 @@ class TransactionController extends BaseController
*/
public function store($what)
{
/*
* Collect data to process:
*/
$data = Input::except(['_token']);
$data['what'] = $what;
$data = Input::except('_token');
$data['what'] = $what;
$data['currency'] = 'EUR';
switch (Input::get('post_submit_action')) {
case 'store':
/** @var \FireflyIII\Database\TransactionJournal $repository */
$repository = App::make('FireflyIII\Database\TransactionJournal');
switch ($data['post_submit_action']) {
default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break;
case 'create_another':
/*
* Try to store:
*/
$messageBag = $this->_helper->store($data);
case 'store':
$messages = $repository->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save transaction: ' . $messages['errors']->first());
return Redirect::route('transactions.create', $what)->withInput()->withErrors($messages['errors']);
}
// store!
$journal = $repository->store($data);
Session::flash('success', 'New transaction stored!');
/*
* Failure!
* Trigger a search for the related (if selected)
* piggy bank and store an event.
*/
if ($messageBag->count() > 0) {
Session::flash('error', 'Could not save transaction: ' . $messageBag->first());
return Redirect::route('transactions.create', [$what])->withInput()->withErrors($messageBag);
$piggyID = null;
if (!is_null(Input::get('piggybank_id')) && intval(Input::get('piggybank_id')) > 0) {
$piggyID = intval(Input::get('piggybank_id'));
}
Event::fire('transactionJournal.store', [$journal, $piggyID]); // new and used.
/*
* Also trigger on both transactions.
*/
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
Event::fire('transaction.store', [$transaction]);
}
/*
* Success!
*/
Session::flash('success', 'Transaction "' . e(Input::get('description')) . '" saved!');
/*
* Redirect to original location or back to the form.
*/
if (Input::get('post_submit_action') == 'create_another') {
if ($data['post_submit_action'] == 'create_another') {
return Redirect::route('transactions.create', $what)->withInput();
} else {
return Redirect::route('transactions.index.' . $what);
return Redirect::route('transactions.index', $what);
}
break;
case 'validate_only':
$messageBags = $this->_helper->validate($data);
$messageBags = $repository->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('transactions.create', [$what])->withInput();
break;
default:
throw new FireflyException('Method ' . Input::get('post_submit_action') . ' not implemented yet.');
return Redirect::route('transactions.create', $what)->withInput();
break;
}
}
public function transfers()
/**
* TODO this needs cleaning up and thinking over.
*
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\JsonResponse
* @throws Exception
*/
public function unrelate(TransactionJournal $journal)
{
return View::make('transactions.list')->with('subTitle', 'Transfers')->with(
'subTitleIcon', 'fa-arrows-h'
)->with('what', 'transfers');
$groups = $journal->transactiongroups()->get();
$relatedTo = intval(Input::get('relation'));
/** @var TransactionGroup $group */
foreach ($groups as $group) {
foreach ($group->transactionjournals()->get() as $jrnl) {
if ($jrnl->id == $relatedTo) {
// remove from group:
$group->transactionjournals()->detach($relatedTo);
}
}
if ($group->transactionjournals()->count() == 1) {
$group->delete();
}
}
return Response::json(true);
}
@@ -334,38 +548,52 @@ class TransactionController extends BaseController
*/
public function update(TransactionJournal $journal)
{
/** @var \FireflyIII\Database\TransactionJournal $repos */
$repos = App::make('FireflyIII\Database\TransactionJournal');
$data = Input::except('_token');
$data['currency'] = 'EUR';
$data['what'] = strtolower($journal->transactionType->type);
switch (Input::get('post_submit_action')) {
case 'update':
case 'return_to_edit':
$what = strtolower($journal->transactionType->type);
$messageBag = $this->_helper->update($journal, Input::all());
$messageBag = $repos->update($journal, $data);
if ($messageBag->count() == 0) {
// has been saved, return to index:
Session::flash('success', 'Transaction updated!');
Event::fire('journals.update', [$journal]);
Event::fire('transactionJournal.update', [$journal]); // new and used.
/*
* Also trigger on both transactions.
*/
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
Event::fire('transaction.update', [$transaction]);
}
if (Input::get('post_submit_action') == 'return_to_edit') {
return Redirect::route('transactions.edit', $journal->id)->withInput();
} else {
return Redirect::route('transactions.index.' . $what);
return Redirect::route('transactions.index', $data['what']);
}
} else {
Session::flash('error', 'Could not update transaction: ' . $journal->errors()->first());
Session::flash('error', 'Could not update transaction: ' . $journal->getErrors()->first());
return Redirect::route('transactions.edit', $journal->id)->withInput()->withErrors(
$journal->errors()
$journal->getErrors()
);
}
break;
case 'validate_only':
$data = Input::all();
$data['what'] = strtolower($journal->transactionType->type);
$messageBags = $this->_helper->validate($data);
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('transactions.edit', $journal->id)->withInput();
break;
default:
@@ -376,4 +604,4 @@ class TransactionController extends BaseController
}
}
}

View File

@@ -1,8 +1,5 @@
<?php
use Firefly\Helper\Email\EmailHelperInterface as EHI;
use Firefly\Storage\User\UserRepositoryInterface as URI;
/**
* Class UserController
*/
@@ -11,15 +8,9 @@ class UserController extends BaseController
/**
* Constructor.
*
* @param URI $user
* @param EHI $email
*/
public function __construct(URI $user, EHI $email)
public function __construct()
{
$this->user = $user;
$this->email = $email;
}
/**
@@ -32,6 +23,18 @@ class UserController extends BaseController
return View::make('user.login');
}
/**
* Logout user.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function logout()
{
Auth::logout();
Session::flush();
return Redirect::route('index');
}
/**
* Login.
@@ -41,10 +44,7 @@ class UserController extends BaseController
public function postLogin()
{
$rememberMe = Input::get('remember_me') == '1';
$data = [
'email' => Input::get('email'),
'password' => Input::get('password')
];
$data = ['email' => Input::get('email'), 'password' => Input::get('password')];
$result = Auth::attempt($data, $rememberMe);
if ($result) {
return Redirect::route('index');
@@ -55,20 +55,6 @@ class UserController extends BaseController
return View::make('user.login');
}
/**
* If allowed, show the register form.
*
* @return $this|\Illuminate\View\View
*/
public function register()
{
if (Config::get('auth.allow_register') !== true) {
return View::make('error')->with('message', 'Not possible');
}
return View::make('user.register');
}
/**
* If allowed, register the user.
*
@@ -84,32 +70,77 @@ class UserController extends BaseController
if (Config::get('auth.allow_register') !== true) {
return View::make('error')->with('message', 'Not possible');
}
$user = $this->user->register(Input::all());
/** @var \FireflyIII\Database\User $repository */
$repository = App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */
$email = App::make('FireflyIII\Shared\Mail\RegistrationInterface');
$user = $repository->register(Input::all());
//$user = $this->user->register(Input::all());
if ($user) {
if (Config::get('auth.verify_mail') === true) {
$this->email->sendVerificationMail($user);
$email->sendVerificationMail($user);
return View::make('user.verification-pending');
}
$this->email->sendPasswordMail($user);
$email->sendPasswordMail($user);
return View::make('user.registered');
}
return View::make('user.register');
}
/**
* Logout user.
* If need to verify, send new reset code.
* Otherwise, send new password.
*
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\View\View
*/
public function logout()
public function postRemindme()
{
Auth::logout();
Session::flush();
return Redirect::route('index');
/** @var \FireflyIII\Database\User $repository */
$repository = App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */
$email = App::make('FireflyIII\Shared\Mail\RegistrationInterface');
$user = $repository->findByEmail(Input::get('email'));
if (!$user) {
Session::flash('error', 'No good!');
return View::make('user.remindme');
}
if (Config::get('auth.verify_reset') === true) {
$email->sendResetVerification($user);
return View::make('user.verification-pending');
}
$email->sendPasswordMail($user);
return View::make('user.registered');
}
/**
* If allowed, show the register form.
*
* @return $this|\Illuminate\View\View
*/
public function register()
{
if (Config::get('auth.allow_register') !== true) {
return View::make('error')->with('message', 'Not possible');
}
return View::make('user.register');
}
/**
@@ -122,31 +153,6 @@ class UserController extends BaseController
return View::make('user.remindme');
}
/**
* If need to verify, send new reset code.
* Otherwise, send new password.
*
* @return \Illuminate\View\View
*/
public function postRemindme()
{
$user = $this->user->findByEmail(Input::get('email'));
if (!$user) {
Session::flash('error', 'No good!');
return View::make('user.remindme');
}
if (Config::get('auth.verify_reset') === true) {
$this->email->sendResetVerification($user);
return View::make('user.verification-pending');
}
$this->email->sendPasswordMail($user);
return View::make('user.registered');
}
/**
* Send a user a password based on his reset code.
*
@@ -156,9 +162,16 @@ class UserController extends BaseController
*/
public function reset($reset)
{
$user = $this->user->findByReset($reset);
/** @var \FireflyIII\Database\User $repository */
$repository = App::make('FireflyIII\Database\User');
/** @var \FireflyIII\Shared\Mail\RegistrationInterface $email */
$email = App::make('FireflyIII\Shared\Mail\RegistrationInterface');
$user = $repository->findByReset($reset);
if ($user) {
$this->email->sendPasswordMail($user);
$email->sendPasswordMail($user);
return View::make('user.registered');
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateUsersTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('users');
}
/**
* Run the migrations.
*
@@ -33,14 +43,4 @@ class CreateUsersTable extends Migration
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('users');
}
}

View File

@@ -29,14 +29,14 @@ class CreateAccountTypesTable extends Migration
public function up()
{
Schema::create(
'account_types', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('type', 50);
$table->boolean('editable');
'account_types', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('type', 50);
$table->boolean('editable');
$table->unique('type');
}
$table->unique('type');
}
);
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateAccountsTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('accounts');
}
/**
* Run the migrations.
*
@@ -22,34 +32,21 @@ class CreateAccountsTable extends Migration
'accounts', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->integer('user_id')->unsigned();
$table->integer('account_type_id')->unsigned();
$table->string('name', 100);
$table->boolean('active');
// connect accounts to users
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
// connect accounts to account_types
$table->foreign('account_type_id')
->references('id')->on('account_types')
->onDelete('cascade');
$table->foreign('account_type_id')->references('id')->on('account_types')->onDelete('cascade');
$table->unique(['user_id', 'account_type_id', 'name']);
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('accounts');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateComponentsTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('components');
}
/**
* Run the migrations.
*
@@ -22,14 +32,13 @@ class CreateComponentsTable extends Migration
'components', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('name', 50);
$table->integer('user_id')->unsigned();
$table->string('class', 20);
// connect components to users
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unique(['user_id', 'class', 'name']);
}
@@ -37,14 +46,4 @@ class CreateComponentsTable extends Migration
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('components');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreatePiggybanksTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('piggybanks');
}
/**
* Run the migrations.
*
@@ -28,31 +38,20 @@ class CreatePiggybanksTable extends Migration
$table->date('startdate')->nullable();
$table->date('targetdate')->nullable();
$table->boolean('repeats');
$table->enum('rep_length', ['day', 'week', 'month', 'year'])->nullable();
$table->enum('rep_length', ['day', 'week', 'quarter', 'month', 'year'])->nullable();
$table->smallInteger('rep_every')->unsigned();
$table->smallInteger('rep_times')->unsigned()->nullable();
$table->enum('reminder', ['day', 'week', 'month', 'year'])->nullable();
$table->enum('reminder', ['day', 'week', 'quarter', 'month', 'year'])->nullable();
$table->smallInteger('reminder_skip')->unsigned();
$table->boolean('remind_me');
$table->integer('order')->unsigned();
// connect account to piggybank.
$table->foreign('account_id')
->references('id')->on('accounts')
->onDelete('cascade');
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->unique(['account_id', 'name']);
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('piggybanks');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateTransactionCurrenciesTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('transaction_currencies');
}
/**
* Run the migrations.
*
@@ -22,19 +32,10 @@ class CreateTransactionCurrenciesTable extends Migration
'transaction_currencies', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('code', 3);
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('transaction_currencies');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateTransactionTypesTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('transaction_types');
}
/**
* Run the migrations.
*
@@ -22,19 +32,10 @@ class CreateTransactionTypesTable extends Migration
'transaction_types', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('type', 50);
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('transaction_types');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateRecurringTransactionsTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('recurring_transactions');
}
/**
* Run the migrations.
*
@@ -41,14 +51,4 @@ class CreateRecurringTransactionsTable extends Migration
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('recurring_transactions');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateTransactionJournalsTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('transaction_journals');
}
/**
* Run the migrations.
*
@@ -22,6 +32,7 @@ class CreateTransactionJournalsTable extends Migration
'transaction_journals', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->integer('user_id')->unsigned();
$table->integer('transaction_type_id')->unsigned();
$table->integer('recurring_transaction_id')->unsigned()->nullable();
@@ -31,36 +42,18 @@ class CreateTransactionJournalsTable extends Migration
$table->date('date');
// connect transaction journals to transaction types
$table->foreign('transaction_type_id')
->references('id')->on('transaction_types')
->onDelete('cascade');
$table->foreign('transaction_type_id')->references('id')->on('transaction_types')->onDelete('cascade');
// connect transaction journals to recurring transactions
$table->foreign('recurring_transaction_id')
->references('id')->on('recurring_transactions')
->onDelete('set null');
$table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('set null');
// connect transaction journals to transaction currencies
$table->foreign('transaction_currency_id')
->references('id')->on('transaction_currencies')
->onDelete('cascade');
$table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade');
// connect users
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('transaction_journals');
}
}

View File

@@ -32,6 +32,7 @@ class CreateTransactionsTable extends Migration
'transactions', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->integer('account_id')->unsigned();
$table->integer('piggybank_id')->nullable()->unsigned();
$table->integer('transaction_journal_id')->unsigned();
@@ -39,19 +40,13 @@ class CreateTransactionsTable extends Migration
$table->decimal('amount', 10, 2);
// connect transactions to transaction journals
$table->foreign('transaction_journal_id')
->references('id')->on('transaction_journals')
->onDelete('cascade');
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
// connect piggy banks
$table->foreign('piggybank_id')
->references('id')->on('piggybanks')
->onDelete('set null');
$table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('set null');
// connect account id:
$table->foreign('account_id')
->references('id')->on('accounts')
->onDelete('cascade');
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
}
);

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateComponentTransactionTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('component_transaction');
}
/**
* Run the migrations.
*
@@ -25,26 +35,12 @@ class CreateComponentTransactionTable extends Migration
$table->integer('transaction_id')->unsigned();
// connect to components
$table->foreign('component_id')
->references('id')->on('components')
->onDelete('cascade');
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
// connect to transactions
$table->foreign('transaction_id')
->references('id')->on('transactions')
->onDelete('cascade');
$table->foreign('transaction_id')->references('id')->on('transactions')->onDelete('cascade');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('component_transaction');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateComponentTransactionJournalTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('component_transaction_journal');
}
/**
* Run the migrations.
*
@@ -25,26 +35,12 @@ class CreateComponentTransactionJournalTable extends Migration
$table->integer('transaction_journal_id')->unsigned();
// link components with component_id
$table->foreign('component_id')
->references('id')->on('components')
->onDelete('cascade');
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
// link transaction journals with transaction_journal_id
$table->foreign('transaction_journal_id')
->references('id')->on('transaction_journals')
->onDelete('cascade');
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('cascade');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('component_transaction_journal');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreatePreferencesTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('preferences');
}
/**
* Run the migrations.
*
@@ -27,21 +37,9 @@ class CreatePreferencesTable extends Migration
$table->text('data');
// connect preferences to users
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('preferences');
}
}

View File

@@ -10,6 +10,16 @@ use Illuminate\Database\Migrations\Migration;
class CreateSessionTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('sessions');
}
/**
* Run the migrations.
*
@@ -26,14 +36,4 @@ class CreateSessionTable extends Migration
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('sessions');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateLimitsTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('limits');
}
/**
* Run the migrations.
*
@@ -31,21 +41,9 @@ class CreateLimitsTable extends Migration
$table->unique(['component_id', 'startdate', 'repeat_freq']);
// connect component
$table->foreign('component_id')
->references('id')->on('components')
->onDelete('cascade');
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('limits');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreateLimitRepeatTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('limit_repetitions');
}
/**
* Run the migrations.
*
@@ -30,21 +40,9 @@ class CreateLimitRepeatTable extends Migration
$table->unique(['limit_id', 'startdate', 'enddate']);
// connect limit
$table->foreign('limit_id')
->references('id')->on('limits')
->onDelete('cascade');
$table->foreign('limit_id')->references('id')->on('limits')->onDelete('cascade');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('limit_repetitions');
}
}

View File

@@ -11,6 +11,16 @@ use Illuminate\Database\Schema\Blueprint;
class RecurringTransactionsToComponents extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('component_recurring_transaction');
}
/**
* Run the migrations.
*
@@ -26,26 +36,12 @@ class RecurringTransactionsToComponents extends Migration
$table->boolean('optional');
// link components with component_id
$table->foreign('component_id')
->references('id')->on('components')
->onDelete('cascade');
$table->foreign('component_id')->references('id')->on('components')->onDelete('cascade');
// link transaction journals with transaction_journal_id
$table->foreign('recurring_transaction_id')
->references('id')->on('recurring_transactions')
->onDelete('cascade');
$table->foreign('recurring_transaction_id')->references('id')->on('recurring_transactions')->onDelete('cascade');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('component_recurring_transaction');
}
}

View File

@@ -6,6 +6,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreatePiggyInstance extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('piggybank_repetitions');
}
/**
* Run the migrations.
*
@@ -25,21 +35,9 @@ class CreatePiggyInstance extends Migration
$table->unique(['piggybank_id', 'startdate', 'targetdate']);
// connect instance to piggybank.
$table->foreign('piggybank_id')
->references('id')->on('piggybanks')
->onDelete('cascade');
$table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('piggybank_repetitions');
}
}

View File

@@ -6,6 +6,16 @@ use Illuminate\Database\Schema\Blueprint;
class CreatePiggyEvents extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('piggybank_events');
}
/**
* Run the migrations.
*
@@ -22,21 +32,9 @@ class CreatePiggyEvents extends Migration
$table->decimal('amount', 10, 2);
// connect instance to piggybank.
$table->foreign('piggybank_id')
->references('id')->on('piggybanks')
->onDelete('cascade');
$table->foreign('piggybank_id')->references('id')->on('piggybanks')->onDelete('cascade');
}
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('piggybank_events');
}
}

View File

@@ -27,30 +27,13 @@ class CreateRemindersTable extends Migration
'reminders', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('class', 40);
$table->integer('piggybank_id')->unsigned()->nullable();
$table->integer('recurring_transaction_id')->unsigned()->nullable();
$table->integer('user_id')->unsigned();
$table->date('startdate');
$table->date('enddate')->nullable();
$table->boolean('active');
// connect reminders to piggy banks.
$table->foreign('piggybank_id')
->references('id')->on('piggybanks')
->onDelete('set null');
// connect reminders to recurring transactions.
$table->foreign('recurring_transaction_id')
->references('id')->on('recurring_transactions')
->onDelete('set null');
// connect reminders to users
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
}
);
}

View File

@@ -1,41 +1,41 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateImportmapsTable extends Migration {
class CreateImportmapsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('importmaps', function(Blueprint $table)
{
$table->increments('id');
$table->timestamps();
$table->integer('user_id')->unsigned();
$table->string('file',500);
$table->integer('totaljobs')->unsigned();
$table->integer('jobsdone')->unsigned();
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('importmaps');
}
// connect maps to users
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
});
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(
'importmaps', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('user_id')->unsigned();
$table->string('file', 500);
$table->integer('totaljobs')->unsigned();
$table->integer('jobsdone')->unsigned();
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('importmaps');
}
// connect maps to users
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
}
);
}
}

View File

@@ -1,42 +1,41 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateImportentriesTable extends Migration {
class CreateImportentriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('importentries', function(Blueprint $table)
{
$table->increments('id');
$table->timestamps();
$table->string('class',200);
$table->integer('importmap_id')->unsigned();
$table->integer('old')->unsigned();
$table->integer('new')->unsigned();
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('importentries');
}
// map import entries to import map.
// connect accounts to account_types
$table->foreign('importmap_id')
->references('id')->on('importmaps')
->onDelete('cascade');
});
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(
'importentries', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('class', 200);
$table->integer('importmap_id')->unsigned();
$table->integer('old')->unsigned();
$table->integer('new')->unsigned();
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('importentries');
}
// connect import map.
$table->foreign('importmap_id')->references('id')->on('importmaps')->onDelete('cascade');
}
);
}
}

View File

@@ -23,13 +23,15 @@ class CreateFailedJobsTable extends Migration
*/
public function up()
{
Schema::create('failed_jobs', function (Blueprint $table) {
$table->increments('id');
$table->text('connection');
$table->text('queue');
$table->text('payload');
$table->timestamp('failed_at');
});
Schema::create(
'failed_jobs', function (Blueprint $table) {
$table->increments('id');
$table->text('connection');
$table->text('queue');
$table->text('payload');
$table->timestamp('failed_at');
}
);
}
}

View File

@@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateAccountMeta extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
Schema::drop('account_meta');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//
Schema::create(
'account_meta', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('account_id')->unsigned();
$table->string('name');
$table->text('data');
}
);
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class ExtendPiggybankEvents extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table(
'piggybank_events', function (Blueprint $table) {
$table->integer('transaction_journal_id')->unsigned()->nullable();
$table->foreign('transaction_journal_id')->references('id')->on('transaction_journals')->onDelete('set null');
}
);
}
}

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class EventTableAdditions1 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// remove some fields:
Schema::table(
'reminders', function (Blueprint $table) {
$table->boolean('notnow')->default(0);
$table->integer('remindersable_id')->unsigned()->nullable();
$table->string('remindersable_type')->nullable();
}
);
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
class CacheTable extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('cache');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(
'cache', function ($table) {
$table->string('key')->unique();
$table->text('value');
$table->integer('expiration');
}
);
}
}

View File

@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class Transactiongroups extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('transaction_groups');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(
'transaction_groups', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->integer('user_id')->unsigned();
$table->enum('relation', ['balance']);
// connect reminders to users
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
}
);
}
}

View File

@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class Transactiongroupsjoin extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
Schema::drop('transaction_group_transaction_journal');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(
'transaction_group_transaction_journal', function (Blueprint $table) {
$table->increments('id');
$table->integer('transaction_group_id')->unsigned();
$table->integer('transaction_journal_id')->unsigned();
$table->foreign('transaction_group_id', 'tr_grp_id')->references('id')->on('transaction_groups')->onDelete('cascade');
$table->foreign('transaction_journal_id', 'tr_trj_id')->references('id')->on('transaction_journals')->onDelete('cascade');
$table->unique(['transaction_group_id', 'transaction_journal_id'], 'tt_joined');
}
);
}
}

View File

@@ -19,6 +19,7 @@ class DatabaseSeeder extends Seeder
$this->call('TransactionCurrencySeeder');
$this->call('TransactionTypeSeeder');
$this->call('DefaultUserSeeder');
$this->call('TestContentSeeder');
}
}

View File

@@ -8,16 +8,15 @@ class DefaultUserSeeder extends Seeder
public function run()
{
DB::table('users')->delete();
if (App::environment() == 'testing') {
User::create(
[
'email' => 's@nder.be',
'password' => 'sander',
'reset' => null,
'remember_token' => null,
'migrated' => 0
]
);
User::create(
['email' => 'thegrumpydictator@gmail.com', 'password' => 'james', 'reset' => null, 'remember_token' => null, 'migrated' => 0]
);
User::create(
['email' => 'acceptance@example.com', 'password' => 'acceptance', 'reset' => null, 'remember_token' => null, 'migrated' => 0]
);
}
}

View File

@@ -0,0 +1,176 @@
<?php
use Carbon\Carbon;
class TestContentSeeder extends Seeder
{
public function run()
{
if (App::environment() == 'testing') {
$assetType = AccountType::whereType('Asset account')->first();
$expenseType = AccountType::whereType('Expense account')->first();
$revenueType = AccountType::whereType('Revenue account')->first();
$ibType = AccountType::whereType('Initial balance account')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
$obType = TransactionType::whereType('Opening balance')->first();
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$transfer = TransactionType::whereType('Transfer')->first();
$deposit = TransactionType::whereType('Deposit')->first();
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
if ($user) {
// create two asset accounts.
$checking = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Checking account', 'active' => 1]);
$savings = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Savings account', 'active' => 1]);
// create two budgets:
$groceriesBudget = Budget::create(['user_id' => $user->id, 'name' => 'Groceries']);
$billsBudget = Budget::create(['user_id' => $user->id, 'name' => 'Bills']);
// create two categories:
$dailyGroceries = Category::create(['user_id' => $user->id, 'name' => 'Daily groceries']);
$lunch = Category::create(['user_id' => $user->id, 'name' => 'Lunch']);
$house = Category::create(['user_id' => $user->id, 'name' => 'House']);
// create some expense accounts.
$ah = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Albert Heijn', 'active' => 1]);
$plus = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'PLUS', 'active' => 1]);
$vitens = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Vitens', 'active' => 1]);
$greenchoice = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Greenchoice', 'active' => 1]);
$portaal = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Portaal', 'active' => 1]);
$store = Account::create(['user_id' => $user->id, 'account_type_id' => $expenseType->id, 'name' => 'Buy More', 'active' => 1]);
// create three revenue accounts.
$employer = Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'Employer', 'active' => 1]);
$taxes = Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'IRS', 'active' => 1]);
$job = Account::create(['user_id' => $user->id, 'account_type_id' => $revenueType->id, 'name' => 'Job', 'active' => 1]);
// put money in the two accounts (initial balance)
$ibChecking = Account::create(
['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Checking account initial balance', 'active' => 0]
);
$ibSavings = Account::create(
['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Savings account initial balance', 'active' => 0]
);
$this->createTransaction($ibChecking, $checking, 4000, $obType, 'Initial Balance for Checking account', '2014-01-01');
$this->createTransaction($ibSavings, $savings, 10000, $obType, 'Initial Balance for Savings account', '2014-01-01');
// create some expenses and incomes and what-not (for every month):
$start = new Carbon('2014-01-01');
$end = Carbon::now()->startOfMonth()->subDay();
while ($start <= $end) {
$this->createTransaction(
$checking, $portaal, 500, $withdrawal, 'Rent for ' . $start->format('F Y'), $start->format('Y-m-') . '01', $billsBudget, $house
);
$this->createTransaction(
$checking, $vitens, 12, $withdrawal, 'Water for ' . $start->format('F Y'), $start->format('Y-m-') . '02', $billsBudget, $house
);
$this->createTransaction(
$checking, $greenchoice, 110, $withdrawal, 'Power for ' . $start->format('F Y'), $start->format('Y-m-') . '02', $billsBudget, $house
);
// spend on groceries
$groceriesStart = clone $start;
for ($i = 0; $i < 13; $i++) {
$amt = rand(100, 300) / 10;
$lunchAmount = rand(30, 60) / 10;
$this->createTransaction(
$checking, $plus, $lunchAmount, $withdrawal, 'Lunch', $groceriesStart->format('Y-m-d'), $groceriesBudget, $lunch
);
$groceriesStart->addDay();
if (intval($groceriesStart->format('d')) % 2 == 0) {
$this->createTransaction(
$checking, $ah, $amt, $withdrawal, 'Groceries', $groceriesStart->format('Y-m-d'), $groceriesBudget, $dailyGroceries
);
}
$groceriesStart->addDay();
}
// get income:
$this->createTransaction($employer, $checking, rand(1400, 1600), $deposit, 'Salary', $start->format('Y-m-') . '23');
// pay taxes:
$this->createTransaction($checking, $taxes, rand(50, 70), $withdrawal, 'Taxes in ' . $start->format('F Y'), $start->format('Y-m-') . '27');
// some other stuff.
$start->addMonth();
}
// create some big expenses, move some money around.
$this->createTransaction($savings, $checking, 1259, $transfer, 'Money for new PC', $end->format('Y-m') . '-11');
$this->createTransaction($checking, $store, 1259, $withdrawal, 'New PC', $end->format('Y-m') . '-12');
// create two budgets
// create two categories
// create
}
}
}
/**
* @param Account $from
* @param Account $to
* @param $amount
* @param TransactionType $type
* @param $description
* @param $date
*
* @return TransactionJournal
*/
public function createTransaction(
Account $from, Account $to, $amount, TransactionType $type, $description, $date, Budget $budget = null, Category $category = null
) {
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
/** @var TransactionJournal $journal */
$journal = TransactionJournal::create(
[
'user_id' => $user->id,
'transaction_type_id' => $type->id,
'transaction_currency_id' => $euro->id,
'description' => $description,
'completed' => 1,
'date' => $date
]
);
Transaction::create(
[
'account_id' => $from->id,
'transaction_journal_id' => $journal->id,
'amount' => $amount * -1
]
);
Transaction::create(
[
'account_id' => $to->id,
'transaction_journal_id' => $journal->id,
'amount' => $amount
]
);
if (!is_null($budget)) {
$journal->budgets()->save($budget);
}
if (!is_null($category)) {
$journal->categories()->save($category);
}
return $journal;
}
}

View File

@@ -4,13 +4,14 @@
App::before(
function ($request) {
$reminders = [];
if (Auth::check()) {
/** @var \Firefly\Helper\Toolkit\ToolkitInterface $toolkit */
$toolkit = App::make('Firefly\Helper\Toolkit\ToolkitInterface');
$toolkit->getDateRange();
$toolkit->checkImportJobs();
Filter::setSessionDateRange();
Reminders::updateReminders();
$reminders = Reminders::getReminders();
}
View::share('reminders', $reminders);
}
);
@@ -67,6 +68,7 @@ Route::filter(
if (Auth::check()) {
return Redirect::to('/');
}
return null;
}
);

View File

@@ -1,6 +1,6 @@
<?php
return array(
return [
/*
|--------------------------------------------------------------------------
@@ -17,4 +17,4 @@ return array(
'next' => 'Next &raquo;',
);
];

View File

@@ -1,24 +1,24 @@
<?php
return array(
return [
/*
|--------------------------------------------------------------------------
| Password Reminder Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
/*
|--------------------------------------------------------------------------
| Password Reminder Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
"password" => "Passwords must be at least six characters and match the confirmation.",
"password" => "Passwords must be at least six characters and match the confirmation.",
"user" => "We can't find a user with that e-mail address.",
"user" => "We can't find a user with that e-mail address.",
"token" => "This password reset token is invalid.",
"token" => "This password reset token is invalid.",
"sent" => "Password reminder sent!",
"sent" => "Password reminder sent!",
);
];

View File

@@ -21,12 +21,12 @@ return [
"alpha_num" => "The :attribute may only contain letters and numbers.",
"array" => "The :attribute must be an array.",
"before" => "The :attribute must be a date before :date.",
"between" => array(
"between" => [
"numeric" => "The :attribute must be between :min and :max.",
"file" => "The :attribute must be between :min and :max kilobytes.",
"string" => "The :attribute must be between :min and :max characters.",
"array" => "The :attribute must have between :min and :max items.",
),
],
"confirmed" => "The :attribute confirmation does not match.",
"date" => "The :attribute is not a valid date.",
"date_format" => "The :attribute does not match the format :format.",
@@ -39,19 +39,19 @@ return [
"in" => "The selected :attribute is invalid.",
"integer" => "The :attribute must be an integer.",
"ip" => "The :attribute must be a valid IP address.",
"max" => array(
"max" => [
"numeric" => "The :attribute may not be greater than :max.",
"file" => "The :attribute may not be greater than :max kilobytes.",
"string" => "The :attribute may not be greater than :max characters.",
"array" => "The :attribute may not have more than :max items.",
),
],
"mimes" => "The :attribute must be a file of type: :values.",
"min" => array(
"min" => [
"numeric" => "The :attribute must be at least :min.",
"file" => "The :attribute must be at least :min kilobytes.",
"string" => "The :attribute must be at least :min characters.",
"array" => "The :attribute must have at least :min items.",
),
],
"not_in" => "The selected :attribute is invalid.",
"numeric" => "The :attribute must be a number.",
"regex" => "The :attribute format is invalid.",
@@ -62,12 +62,12 @@ return [
"required_without" => "The :attribute field is required when :values is not present.",
"required_without_all" => "The :attribute field is required when none of :values are present.",
"same" => "The :attribute and :other must match.",
"size" => array(
"size" => [
"numeric" => "The :attribute must be :size.",
"file" => "The :attribute must be :size kilobytes.",
"string" => "The :attribute must be :size characters.",
"array" => "The :attribute must contain :size items.",
),
],
"unique" => "The :attribute has already been taken.",
"url" => "The :attribute format is invalid.",
@@ -82,11 +82,11 @@ return [
|
*/
'custom' => array(
'attribute-name' => array(
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
),
),
],
],
'alphabasic' => 'The :attribute field must consist of basic alphanumeric characters.',
/*
@@ -100,6 +100,6 @@ return [
|
*/
'attributes' => array(),
'attributes' => [],
];

View File

@@ -10,9 +10,10 @@ class Form
{
/**
* @param $name
* @param null $value
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -29,18 +30,42 @@ class Form
return self::ffInput('checkbox', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffAmount($name, $value = null, array $options = [])
{
$options['step'] = 'any';
$options['min'] = '0.01';
$options['min'] = '0.01';
return self::ffInput('amount', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffBalance($name, $value = null, array $options = [])
{
$options['step'] = 'any';
return self::ffInput('amount', $name, $value, $options);
}
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -50,9 +75,10 @@ class Form
}
/**
* @param $name
* @param null $value
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -63,10 +89,11 @@ class Form
}
/**
* @param $name
* @param $name
* @param array $list
* @param null $selected
* @param null $selected
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -76,9 +103,10 @@ class Form
}
/**
* @param $name
* @param null $value
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
@@ -88,16 +116,25 @@ class Form
}
public static function label($name)
/**
* @param $name
* @param $options
*
* @return string
*/
public static function label($name, $options)
{
if (isset($options['label'])) {
return $options['label'];
}
$labels = [
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from',
'account_to_id' => 'Account to',
'account_id' => 'Asset account'
'account_to_id' => 'Account to',
'account_id' => 'Asset account'
];
return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name));
@@ -151,28 +188,29 @@ class Form
case 'create':
$return = '<div class="form-group"><label for="return_to_form" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action','create_another', $previousValue == 'create_another');
$return .= \Form::radio('post_submit_action', 'create_another', $previousValue == 'create_another');
$return .= 'After storing, return here to create another one.</label></div></div></div>';
break;
case 'update':
$return = '<div class="form-group"><label for="return_to_edit" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action','return_to_edit', $previousValue == 'return_to_edit');
$return .= \Form::radio('post_submit_action', 'return_to_edit', $previousValue == 'return_to_edit');
$return .= 'After updating, return here.</label></div></div></div>';
break;
default:
throw new FireflyException('Cannot create ffOptionsList for option (store+return) ' . $type);
break;
}
return $store.$validate.$return;
return $store . $validate . $return;
}
/**
* @param $type
* @param $name
* @param null $value
* @param $type
* @param $name
* @param null $value
* @param array $options
* @param array $list
*
* @return string
* @throws FireflyException
*/
@@ -181,10 +219,10 @@ class Form
/*
* add some defaults to this method:
*/
$options['class'] = 'form-control';
$options['id'] = 'ffInput_' . $name;
$options['class'] = 'form-control';
$options['id'] = 'ffInput_' . $name;
$options['autocomplete'] = 'off';
$label = self::label($name);
$label = self::label($name, $options);
/*
* Make label and placeholder look nice.
*/
@@ -193,9 +231,9 @@ class Form
/*
* Get prefilled value:
*/
if(\Session::has('prefilled')) {
if (\Session::has('prefilled')) {
$prefilled = \Session::get('prefilled');
$value = isset($prefilled[$name]) && is_null($value) ? $prefilled[$name] : $value;
$value = isset($prefilled[$name]) && is_null($value) ? $prefilled[$name] : $value;
}
/*

View File

@@ -12,14 +12,17 @@ class Account implements AccountInterface
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account)
{
return \TransactionJournal::withRelevantData()
->accountIs($account)
->leftJoin('transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id')
->leftJoin(
'transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id'
)
->where('transaction_types.type', 'Opening balance')
->first(['transaction_journals.*']);
}
@@ -36,7 +39,7 @@ class Account implements AccountInterface
* For now, Firefly simply warns the user of this.
*
* @param \Account $account
* @param $perPage
* @param $perPage
*
* @return array|mixed
* @throws \Firefly\Exception\FireflyException
@@ -110,17 +113,25 @@ class Account implements AccountInterface
// statistics (transactions)
$trIn = floatval(\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount'));
$trOut = floatval(\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount'));
$trIn = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
);
$trOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
);
$trDiff = $trIn + $trOut;
// statistics (transfers)
$trfIn = floatval(\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount'));
$trfOut = floatval(\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount'));
$trfIn = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount')
);
$trfOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount')
);
$trfDiff = $trfIn + $trfOut;
$stats['period'] = [

View File

@@ -1,11 +1,4 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 27/09/14
* Time: 07:39
*/
namespace Firefly\Helper\Controllers;
use LaravelBook\Ardent\Builder;
@@ -355,7 +348,7 @@ class Json implements JsonInterface
* Loop set and create entries to return.
*/
foreach ($set as $entry) {
$data['data'][] = [
$set = [
'name' => ['name' => $entry->name, 'url' => route('recurring.show', $entry->id)],
'match' => explode(' ', $entry->match),
@@ -370,6 +363,11 @@ class Json implements JsonInterface
'delete' => route('recurring.delete', $entry->id)
]
];
if (intval($entry->skip) > 0) {
$set['repeat_freq'] = $entry->repeat_freq . ' (skip ' . $entry->skip . ')';
}
$data['data'][] = $set;
}
return $data;

View File

@@ -107,9 +107,6 @@ class Toolkit implements ToolkitInterface
}
}
/**
* @return mixed
*/
protected function _getRange()
{
if (!is_null(\Session::get('range'))) {
@@ -175,6 +172,9 @@ class Toolkit implements ToolkitInterface
{
$end = clone $start;
switch ($range) {
default:
throw new FireflyException('_updateEndDate cannot handle $range ' . $range);
break;
case '1D':
$end->endOfDay();
break;
@@ -197,9 +197,7 @@ class Toolkit implements ToolkitInterface
case '1Y':
$end->endOfYear();
break;
default:
throw new FireflyException('_updateEndDate cannot handle $range ' . $range);
break;
}
return $end;
@@ -241,6 +239,9 @@ class Toolkit implements ToolkitInterface
protected function _previous($range, Carbon $date)
{
switch ($range) {
default:
throw new FireflyException('Cannot do _previous() on ' . $range);
break;
case '1D':
$date->startOfDay()->subDay();
break;
@@ -264,9 +265,7 @@ class Toolkit implements ToolkitInterface
case '1Y':
$date->startOfYear()->subYear();
break;
default:
throw new FireflyException('Cannot do _previous() on ' . $range);
break;
}
return $date;
}
@@ -379,4 +378,127 @@ class Toolkit implements ToolkitInterface
}
return $selectList;
}
/**
* @param string $start
* @param string $end
* @param int $steps
*/
public function colorRange($start, $end, $steps = 5)
{
if (strlen($start) != 6) {
throw new FireflyException('Start, ' . e($start) . ' should be a six character HTML colour.');
}
if (strlen($end) != 6) {
throw new FireflyException('End, ' . e($end) . ' should be a six character HTML colour.');
}
if ($steps < 1) {
throw new FireflyException('Steps must be > 1');
}
$start = '#' . $start;
$end = '#' . $end;
/*
* Split html colours.
*/
list($rs, $gs, $bs) = sscanf($start, "#%02x%02x%02x");
list($re, $ge, $be) = sscanf($end, "#%02x%02x%02x");
$stepr = ($re - $rs) / $steps;
$stepg = ($ge - $gs) / $steps;
$stepb = ($be - $bs) / $steps;
$return = [];
for ($i = 0; $i <= $steps; $i++) {
$cr = $rs + ($stepr * $i);
$cg = $gs + ($stepg * $i);
$cb = $bs + ($stepb * $i);
$return[] = $this->rgb2html($cr, $cg, $cb);
}
return $return;
}
protected function rgb2html($r, $g = -1, $b = -1)
{
$r = dechex($r < 0 ? 0 : ($r > 255 ? 255 : $r));
$g = dechex($g < 0 ? 0 : ($g > 255 ? 255 : $g));
$b = dechex($b < 0 ? 0 : ($b > 255 ? 255 : $b));
$color = (strlen($r) < 2 ? '0' : '') . $r;
$color .= (strlen($g) < 2 ? '0' : '') . $g;
$color .= (strlen($b) < 2 ? '0' : '') . $b;
return '#' . $color;
}
/**
* @param Carbon $currentEnd
* @param $repeatFreq
* @throws FireflyException
*/
public function endOfPeriod(Carbon $currentEnd, $repeatFreq)
{
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$currentEnd->addDay();
break;
case 'weekly':
$currentEnd->addWeek()->subDay();
break;
case 'monthly':
$currentEnd->addMonth()->subDay();
break;
case 'quarterly':
$currentEnd->addMonths(3)->subDay();
break;
case 'half-year':
$currentEnd->addMonths(6)->subDay();
break;
case 'yearly':
$currentEnd->addYear()->subDay();
break;
}
}
/**
* @param Carbon $date
* @param $repeatFreq
* @param $skip
* @return Carbon
* @throws FireflyException
*/
public function addPeriod(Carbon $date, $repeatFreq, $skip)
{
$add = ($skip + 1);
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do getFunctionForRepeatFreq for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$date->addDays($add);
break;
case 'weekly':
$date->addWeeks($add);
break;
case 'monthly':
$date->addMonths($add);
break;
case 'quarterly':
$months = $add * 3;
$date->addMonths($months);
break;
case 'half-year':
$months = $add * 6;
$date->addMonths($months);
break;
case 'yearly':
$date->addYears($add);
break;
}
return $date;
}
}

View File

@@ -2,6 +2,7 @@
namespace Firefly\Helper\Toolkit;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
@@ -21,7 +22,7 @@ interface ToolkitInterface
* Takes any collection and tries to make a sensible select list compatible array of it.
*
* @param Collection $set
* @param null $titleField
* @param null $titleField
*
* @return mixed
*/
@@ -33,4 +34,19 @@ interface ToolkitInterface
public function checkImportJobs();
/**
* @param string $start
* @param string $end
* @param int $steps
*/
public function colorRange($start, $end, $steps = 5);
/**
* @param Carbon $date
* @param $repeatFreq
* @param $skip
* @return Carbon
*/
public function addPeriod(Carbon $date, $repeatFreq, $skip);
}

View File

@@ -68,7 +68,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
/**
*
* TODO
*
* Also delete: initial balance, initial balance account, and transactions
*/

View File

@@ -175,12 +175,6 @@ class EloquentRecurringTransactionRepository implements RecurringTransactionRepo
return $messageBag;
}
if ($recurringTransaction->date < Carbon::now()) {
$messageBag->add('date', 'Must be in the future.');
return $messageBag;
}
if ($recurringTransaction->validate()) {
$recurringTransaction->save();
} else {

View File

@@ -31,17 +31,4 @@ class EloquentReminderRepository implements ReminderRepositoryInterface
{
$this->_user = \Auth::user();
}
/**
* @param \Reminder $reminder
*
* @return mixed|void
*/
public function deactivate(\Reminder $reminder)
{
$reminder->active = 0;
$reminder->save();
return $reminder;
}
}

View File

@@ -10,14 +10,6 @@ namespace Firefly\Storage\Reminder;
interface ReminderRepositoryInterface
{
/**
* @param \Reminder $reminder
*
* @return mixed
*/
public function deactivate(\Reminder $reminder);
/**
* @param \User $user
* @return mixed

View File

@@ -5,6 +5,7 @@ namespace Firefly\Storage\TransactionJournal;
use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
@@ -24,6 +25,15 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
$this->_user = \Auth::user();
}
/**
* Get them ALL
*
* @return Collection
*/
public function get() {
return $this->_user->transactionjournals()->with('transactions')->get();
}
/**
* @param Job $job
* @param array $payload

View File

@@ -4,6 +4,7 @@ namespace Firefly\Storage\TransactionJournal;
use Carbon\Carbon;
use Illuminate\Queue\Jobs\Job;
use Illuminate\Support\Collection;
/**
* Interface TransactionJournalRepositoryInterface
@@ -20,6 +21,13 @@ interface TransactionJournalRepositoryInterface
*/
public function importTransaction(Job $job, array $payload);
/**
* Get them ALL
*
* @return Collection
*/
public function get();
/**
* @param Job $job
* @param array $payload

View File

@@ -46,9 +46,9 @@ class EloquentBudgetTrigger
*/
public function subscribe(Dispatcher $events)
{
$events->listen('budgets.destroy', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@destroy');
$events->listen('budgets.store', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@store');
$events->listen('budgets.update', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@update');
// $events->listen('budgets.destroy', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@destroy');
// $events->listen('budgets.store', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@store');
// $events->listen('budgets.update', 'Firefly\Trigger\Budgets\EloquentBudgetTrigger@update');
}

View File

@@ -23,44 +23,19 @@ class EloquentJournalTrigger
/*
* Grab all recurring events.
*/
$set = $journal->user()->first()->recurringtransactions()->get();
$set = $journal->user()->first()->recurringtransactions()->get();
$result = [];
/*
* Prep vars
*/
$description = strtolower($journal->description);
$result = [0 => 0];
/** @var \RecurringTransaction $recurring */
foreach ($set as $recurring) {
$matches = explode(' ', $recurring->match);
/*
* Count the number of matches.
*/
$count = 0;
foreach ($matches as $word) {
if (!(strpos($description, $word) === false)) {
$count++;
\Log::debug('Recurring transaction #' . $recurring->id . ': word "' . $word . '" found in "' . $description . '".');
}
}
$result[$recurring->id] = $count;
\Event::fire('recurring.rescan', [$recurring, $journal]);
}
/*
* The one with the highest value is the winrar!
*/
$index = array_search(max($result), $result);
/*
* Find the recurring transaction:
*/
if (count($result[$index]) > 0) {
$winner = $journal->user()->first()->recurringtransactions()->find($index);
if ($winner) {
$journal->recurringTransaction()->associate($winner);
$journal->save();
}
}
return true;
}
@@ -70,8 +45,8 @@ class EloquentJournalTrigger
*/
public function subscribe(Dispatcher $events)
{
$events->listen('journals.store', 'Firefly\Trigger\Journals\EloquentJournalTrigger@store');
$events->listen('journals.update', 'Firefly\Trigger\Journals\EloquentJournalTrigger@update');
// $events->listen('journals.store', 'Firefly\Trigger\Journals\EloquentJournalTrigger@store');
// $events->listen('journals.update', 'Firefly\Trigger\Journals\EloquentJournalTrigger@update');
}

View File

@@ -107,11 +107,11 @@ class EloquentLimitTrigger
public function subscribe(Dispatcher $events)
{
//$events->listen('budgets.change', 'Firefly\Trigger\Limits\EloquentLimitTrigger@updateLimitRepetitions');
$events->listen('limits.destroy', 'Firefly\Trigger\Limits\EloquentLimitTrigger@destroy');
$events->listen('limits.store', 'Firefly\Trigger\Limits\EloquentLimitTrigger@store');
$events->listen('limits.update', 'Firefly\Trigger\Limits\EloquentLimitTrigger@update');
$events->listen('limits.check', 'Firefly\Trigger\Limits\EloquentLimitTrigger@checkRepeatingLimits');
$events->listen('limits.repetition', 'Firefly\Trigger\Limits\EloquentLimitTrigger@madeRepetition');
// $events->listen('limits.destroy', 'Firefly\Trigger\Limits\EloquentLimitTrigger@destroy');
// $events->listen('limits.store', 'Firefly\Trigger\Limits\EloquentLimitTrigger@store');
// $events->listen('limits.update', 'Firefly\Trigger\Limits\EloquentLimitTrigger@update');
// $events->listen('limits.check', 'Firefly\Trigger\Limits\EloquentLimitTrigger@checkRepeatingLimits');
// $events->listen('limits.repetition', 'Firefly\Trigger\Limits\EloquentLimitTrigger@madeRepetition');
//\Event::fire('limits.repetition', [$repetition]);
}
@@ -126,9 +126,6 @@ class EloquentLimitTrigger
// remove and recreate limit repetitions.
// if limit is not repeating, simply update the repetition to match the limit,
// even though deleting everything is easier.
foreach ($limit->limitrepetitions()->get() as $l) {
$l->delete();
}
$limit->createRepetition($limit->startdate);
return true;

View File

@@ -107,176 +107,6 @@ class EloquentPiggybankTrigger
return true;
}
/**
* Whenever a repetition is made, the decision is there to make reminders for it. Or not.
* Some combinations are "invalid" or impossible and will never trigger reminders. Others do.
*
* The numbers below refer to a small list I made in a text-file (it no longer exists) which contained the eight
* binary combinations that can be made of three properties each piggy bank has (among others):
*
* - Whether or not it has a start date.
* - Whether or not it has an end date.
* - Whether or not the piggy bank repeats itself.
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param \PiggybankRepetition $repetition
*
* @return null
*/
public function createdRepetition(\PiggybankRepetition $repetition)
{
\Log::debug('TRIGGER on createdRepetition() for repetition #' . $repetition->id);
$piggyBank = $repetition->piggybank;
// first, exclude all combinations that will not generate (valid) reminders
// no reminders needed (duh)
if (is_null(($piggyBank->reminder))) {
\Log::debug('No reminders because no reminder needed.');
return null;
}
// no start, no target, no repeat (#1):
if (is_null($piggyBank->startdate) && is_null($piggyBank->targetdate) && $piggyBank->repeats == 0) {
\Log::debug('No reminders because no start, no target, no repeat (#1)');
return null;
}
// no start, but repeats (#5):
if (is_null($piggyBank->startdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no start, but repeats (#5)');
return null;
}
// no start, no end, but repeats (#6)
if (is_null($piggyBank->startdate) && is_null($piggyBank->targetdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no start, no end, but repeats (#6)');
return null;
}
// no end, but repeats (#7)
if (is_null($piggyBank->targetdate) && $piggyBank->repeats == 1) {
\Log::debug('No reminders because no end, but repeats (#7)');
return null;
}
\Log::debug('Will continue...');
/*
* #2, #3, #4 and #8 are valid combo's.
*
* We add two years to the end when the repetition has no target date; we "pretend" there is a target date.
*
*/
if (is_null($repetition->targetdate)) {
$end = new Carbon;
$end->addYears(2);
} else {
$end = $repetition->targetdate;
}
/*
* If there is no start date, the start dat becomes right now.
*/
if (is_null($repetition->startdate)) {
$start = new Carbon;
} else {
$start = $repetition->startdate;
}
/*
* Firefly checks every period X between $start and $end and if necessary creates a reminder. Firefly
* only creates reminders if the $current date is after today. Piggy banks may have their start in the past.
*
* This loop will jump a month when the reminder is set monthly, a week when it's set weekly, etcetera.
*/
$current = $start;
$today = new Carbon;
$today->startOfDay();
while ($current <= $end) {
\Log::debug('Looping reminder dates; now at ' . $current);
/*
* Piggy bank reminders start X days before the actual date of the event.
*/
$reminderStart = clone $current;
switch ($piggyBank->reminder) {
case 'day':
$reminderStart->subDay();
break;
case 'week':
$reminderStart->subDays(4);
break;
case 'month':
$reminderStart->subDays(21);
break;
case 'year':
$reminderStart->subMonths(9);
break;
}
/*
* If the date is past today we create a reminder, otherwise we don't. The end date is the date
* the reminder is due; after that it is invalid.
*/
if ($current >= $today) {
$reminder = new \PiggybankReminder;
$reminder->piggybank()->associate($piggyBank);
$reminder->user()->associate(\Auth::user());
$reminder->startdate = $reminderStart;
$reminder->enddate = $current;
$reminder->active = 1;
\Log::debug('Will create a reminder. Is it valid?');
\Log::debug($reminder->validate());
try {
$reminder->save();
} catch (QueryException $e) {
\Log::error('Could not save reminder: ' . $e->getMessage());
}
} else {
\Log::debug('Current is before today, will not make a reminder.');
}
/*
* Here Firefly jumps ahead to the next reminder period.
*/
switch ($piggyBank->reminder) {
case 'day':
$current->addDays($piggyBank->reminder_skip);
break;
case 'week':
$current->addWeeks($piggyBank->reminder_skip);
break;
case 'month':
$current->addMonths($piggyBank->reminder_skip);
break;
case 'year':
$current->addYears($piggyBank->reminder_skip);
break;
}
}
}
/**
* @param \Piggybank $piggyBank
*
* @return bool
*/
public function destroy(\Piggybank $piggyBank)
{
$reminders = $piggyBank->piggybankreminders()->get();
/** @var \PiggybankReminder $reminder */
foreach ($reminders as $reminder) {
$reminder->delete();
}
return true;
}
/**
* @param \Piggybank $piggyBank
* @param $amount
@@ -335,30 +165,14 @@ class EloquentPiggybankTrigger
*/
public function subscribe(Dispatcher $events)
{
$events->listen('piggybanks.destroy', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@destroy');
$events->listen(
'piggybanks.modifyAmountAdd', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@modifyAmountAdd'
);
$events->listen(
'piggybanks.modifyAmountRemove', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@modifyAmountRemove'
);
$events->listen('piggybanks.store', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@store');
$events->listen('piggybanks.update', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@update');
$events->listen(
'piggybanks.createRelatedTransfer',
'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@createRelatedTransfer'
);
$events->listen(
'piggybanks.updateRelatedTransfer',
'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@updateRelatedTransfer'
);
$events->listen(
'piggybanks.check', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@checkRepeatingPiggies'
);
// $events->listen('piggybanks.modifyAmountAdd', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@modifyAmountAdd');
// $events->listen('piggybanks.modifyAmountRemove', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@modifyAmountRemove');
// $events->listen('piggybanks.store', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@store');
// $events->listen('piggybanks.update', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@update');
// $events->listen('piggybanks.createRelatedTransfer', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@createRelatedTransfer');
// $events->listen('piggybanks.updateRelatedTransfer', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@updateRelatedTransfer');
// $events->listen('piggybanks.storepiggybanks.check', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@checkRepeatingPiggies');
$events->listen(
'piggybanks.repetition', 'Firefly\Trigger\Piggybanks\EloquentPiggybankTrigger@createdRepetition'
);
}
/**

View File

@@ -3,6 +3,7 @@
namespace Firefly\Trigger\Recurring;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Events\Dispatcher;
/**
@@ -28,8 +29,67 @@ class EloquentRecurringTrigger
}
public function createReminders()
/**
* @param \RecurringTransaction $recurring
* @param \TransactionJournal $journal
*/
public function rescan(\RecurringTransaction $recurring, \TransactionJournal $journal)
{
/*
* Match words.
*/
$wordMatch = false;
$matches = explode(' ', $recurring->match);
$description = strtolower($journal->description);
/*
* Attach expense account to description for more narrow matching.
*/
$transactions = $journal->transactions()->get();
/** @var \Transaction $transaction */
foreach ($transactions as $transaction) {
/** @var \Account $account */
$account = $transaction->account()->first();
/** @var \AccountType $type */
$type = $account->accountType()->first();
if ($type->type == 'Expense account' || $type->type == 'Beneficiary account') {
$description .= ' ' . strtolower($account->name);
}
}
$count = 0;
foreach ($matches as $word) {
if (!(strpos($description, strtolower($word)) === false)) {
$count++;
}
}
if ($count >= count($matches)) {
$wordMatch = true;
}
/*
* Match amount.
*/
$amountMatch = false;
if (count($transactions) > 1) {
$amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount));
$min = floatval($recurring->amount_min);
$max = floatval($recurring->amount_max);
if ($amount >= $min && $amount <= $max) {
$amountMatch = true;
}
}
/*
* If both, update!
*/
if ($wordMatch && $amountMatch) {
$journal->recurringTransaction()->associate($recurring);
$journal->save();
}
}
/**
@@ -39,10 +99,7 @@ class EloquentRecurringTrigger
*/
public function subscribe(Dispatcher $events)
{
// $events->listen('recurring.destroy', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@destroy');
// $events->listen('recurring.store', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@store');
// $events->listen('recurring.update', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@update');
// $events->listen('recurring.check', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@createReminders');
//$events->listen('recurring.rescan', 'Firefly\Trigger\Recurring\EloquentRecurringTrigger@rescan');
}
/**

View File

@@ -0,0 +1,167 @@
<?php
namespace FireflyIII\Collection;
use Carbon\Carbon;
class PiggybankPart
{
/** @var float */
public $amountPerBar;
/** @var float */
public $currentamount;
/** @var float */
public $cumulativeAmount;
/** @var \Reminder */
public $reminder;
/** @var \PiggybankRepetition */
public $repetition;
/** @var Carbon */
public $startdate;
/** @var Carbon */
public $targetdate;
/**
* @return \Reminder
*/
public function getReminder()
{
return $this->reminder;
}
/**
* @param \Reminder $reminder
*/
public function setReminder($reminder)
{
$this->reminder = $reminder;
}
/**
* @return \PiggybankRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param \PiggybankRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
/**
* @return Carbon
*/
public function getStartdate()
{
return $this->startdate;
}
/**
* @param Carbon $startdate
*/
public function setStartdate($startdate)
{
$this->startdate = $startdate;
}
/**
* @return Carbon
*/
public function getTargetdate()
{
return $this->targetdate;
}
/**
* @param Carbon $targetdate
*/
public function setTargetdate($targetdate)
{
$this->targetdate = $targetdate;
}
public function hasReminder()
{
return !is_null($this->reminder);
}
public function percentage()
{
if ($this->getCurrentamount() < $this->getCumulativeAmount()) {
$pct = 0;
// calculate halway point?
if ($this->getCumulativeAmount() - $this->getCurrentamount() < $this->getAmountPerBar()) {
$left = $this->getCurrentamount() % $this->getAmountPerBar();
$pct = round($left / $this->getAmountPerBar() * 100);
}
return $pct;
} else {
return 100;
}
}
/**
* @return float
*/
public function getCurrentamount()
{
return $this->currentamount;
}
/**
* @param float $currentamount
*/
public function setCurrentamount($currentamount)
{
$this->currentamount = $currentamount;
}
/**
* @return float
*/
public function getAmountPerBar()
{
return $this->amountPerBar;
}
/**
* @param float $amountPerBar
*/
public function setAmountPerBar($amountPerBar)
{
$this->amountPerBar = $amountPerBar;
}
/**
* @return float
*/
public function getCumulativeAmount()
{
return $this->cumulativeAmount;
}
/**
* @param float $cumulativeAmount
*/
public function setCumulativeAmount($cumulativeAmount)
{
$this->cumulativeAmount = $cumulativeAmount;
}
}

View File

@@ -0,0 +1,597 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use FireflyIII\Database\Ifaces\AccountInterface;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
* Class Account
*
* @package FireflyIII\Database
*/
class Account implements CUD, CommonDatabaseCalls, AccountInterface
{
use SwitchUser;
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param array $types
*
* @return int
*/
public function countAccountsByType(array $types)
{
return $this->getUser()->accounts()->accountTypeIn($types)->count();
}
/**
* @return int
*/
public function countAssetAccounts()
{
return $this->countAccountsByType(['Default account', 'Asset account']);
}
/**
* @return int
*/
public function countExpenseAccounts()
{
return $this->countAccountsByType(['Expense account', 'Beneficiary account']);
}
/**
* Counts the number of total revenue accounts. Useful for DataTables.
*
* @return int
*/
public function countRevenueAccounts()
{
return $this->countAccountsByType(['Revenue account']);
}
/**
* @param \Account $account
*
* @return \Account|null
*/
public function findInitialBalanceAccount(\Account $account)
{
/** @var \FireflyIII\Database\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType');
$accountType = $acctType->findByWhat('initial');
return $this->getUser()->accounts()->where('account_type_id', $accountType->id)->where('name', 'LIKE', $account->name . '%')->first();
}
/**
* @param array $types
*
* @return Collection
*/
public function getAccountsByType(array $types)
{
/*
* Basic query:
*/
$query = $this->getUser()->accounts()->accountTypeIn($types)->withMeta();
/*
* Without an opening balance, the rest of these queries will fail.
*/
$query->leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id');
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
/*
* Not used, but useful for the balance within a certain month / year.
*/
$query->where(
function ($q) {
$q->where('transaction_journals.date', '<=', Carbon::now()->format('Y-m-d'));
$q->orWhereNull('transaction_journals.date');
}
);
$query->groupBy('accounts.id');
/*
* If present, process parameters for sorting:
*/
$query->orderBy('name', 'ASC');
return $query->get(['accounts.*', \DB::Raw('SUM(`transactions`.`amount`) as `balance`')]);
}
/**
* Get all asset accounts. Optional JSON based parameters.
*
* @param array $parameters
*
* @return Collection
*/
public function getAssetAccounts()
{
$list = $this->getAccountsByType(['Default account', 'Asset account']);
$list->each(
function (\Account $account) {
/** @var \AccountMeta $entry */
foreach ($account->accountmeta as $entry) {
if ($entry->name == 'accountRole') {
$account->accountRole = \Config::get('firefly.accountRoles.' . $entry->data);
}
}
if (!isset($account->accountRole)) {
$account->accountRole = 'Default expense account';
}
}
);
return $list;
}
/**
* @return Collection
*/
public function getExpenseAccounts()
{
return $this->getAccountsByType(['Expense account', 'Beneficiary account']);
}
/**
* Get all revenue accounts.
*
* @return Collection
*/
public function getRevenueAccounts()
{
return $this->getAccountsByType(['Revenue account']);
}
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account)
{
return \TransactionJournal::withRelevantData()->accountIs($account)->leftJoin(
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'
)->where('transaction_types.type', 'Opening balance')->first(['transaction_journals.*']);
}
/**
* @param \Account $account
* @param array $data
*
* @return bool
*/
public function storeInitialBalance(\Account $account, array $data)
{
$opposingData = ['name' => $account->name . ' Initial Balance', 'active' => 0, 'what' => 'initial'];
$opposingAccount = $this->store($opposingData);
/*
* Create a journal from opposing to account or vice versa.
*/
$balance = floatval($data['openingbalance']);
$date = new Carbon($data['openingbalancedate']);
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = \App::make('FireflyIII\Database\TransactionJournal');
if ($balance < 0) {
// first transaction draws money from the new account to the opposing
$from = $account;
$to = $opposingAccount;
} else {
// first transaction puts money into account
$from = $opposingAccount;
$to = $account;
}
// data for transaction journal:
$balance = $balance < 0 ? $balance * -1 : $balance;
$opening = ['what' => 'opening', 'currency' => 'EUR', 'amount' => $balance, 'from' => $from, 'to' => $to, 'date' => $date,
'description' => 'Opening balance for new account ' . $account->name,];
$validation = $tj->validate($opening);
if ($validation['errors']->count() == 0) {
$tj->store($opening);
return true;
} else {
var_dump($validation['errors']);
exit;
}
}
/**
* @param \Eloquent $model
*
* @return bool
*/
public function destroy(\Eloquent $model)
{
// delete journals:
$journals = \TransactionJournal::whereIn(
'id', function ($query) use ($model) {
$query->select('transaction_journal_id')
->from('transactions')->whereIn(
'account_id', function ($query) use ($model) {
$query
->select('id')
->from('accounts')
->where(
function ($q) use ($model) {
$q->where('id', $model->id);
$q->orWhere(
function ($q) use ($model) {
$q->where('accounts.name', 'LIKE', '%' . $model->name . '%');
// TODO magic number!
$q->where('accounts.account_type_id', 3);
$q->where('accounts.active', 0);
}
);
}
)->where('accounts.user_id', $this->getUser()->id);
}
)->get();
}
)->delete();
/*
* Trigger deletion:
*/
\Event::fire('account.destroy', [$model]);
// delete accounts:
\Account::where(
function ($q) use ($model) {
$q->where('id', $model->id);
$q->orWhere(
function ($q) use ($model) {
$q->where('accounts.name', 'LIKE', '%' . $model->name . '%');
// TODO magic number!
$q->where('accounts.account_type_id', 3);
$q->where('accounts.active', 0);
}
);
})->delete();
return true;
}
/**
* @param array $data
*
* @return \Eloquent
*/
public function store(array $data)
{
/*
* Find account type.
*/
/** @var \FireflyIII\Database\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType');
$accountType = $acctType->findByWhat($data['what']);
$data['user_id'] = $this->getUser()->id;
$data['account_type_id'] = $accountType->id;
$data['active'] = isset($data['active']) && $data['active'] === '1' ? 1 : 0;
$data = array_except($data, ['_token', 'what']);
$account = new \Account($data);
if (!$account->isValid()) {
var_dump($account->getErrors()->all());
exit;
}
$account->save();
if (isset($data['openingbalance']) && floatval($data['openingbalance']) != 0) {
$this->storeInitialBalance($account, $data);
}
// TODO this needs cleaning up and thinking over.
switch ($account->accountType->type) {
case 'Asset account':
case 'Default account':
$account->updateMeta('accountRole', $data['account_role']);
break;
}
/* Tell transaction journal to store a new one.*/
\Event::fire('account.store', [$account]);
return $account;
}
/**
* @param \Eloquent $model
* @param array $data
*
* @return bool
*/
public function update(\Eloquent $model, array $data)
{
$model->name = $data['name'];
$model->active = isset($data['active']) ? intval($data['active']) : 0;
// TODO this needs cleaning up and thinking over.
switch ($model->accountType->type) {
case 'Asset account':
case 'Default account':
$model->updateMeta('accountRole', $data['account_role']);
break;
}
$model->save();
if (isset($data['openingbalance']) && isset($data['openingbalancedate']) && strlen($data['openingbalancedate']) > 0) {
$openingBalance = $this->openingBalanceTransaction($model);
// TODO this needs cleaning up and thinking over.
if (is_null($openingBalance)) {
$this->storeInitialBalance($model, $data);
} else {
$openingBalance->date = new Carbon($data['openingbalancedate']);
$openingBalance->save();
$amount = floatval($data['openingbalance']);
/** @var \Transaction $transaction */
foreach ($openingBalance->transactions as $transaction) {
if ($transaction->account_id == $model->id) {
$transaction->amount = $amount;
} else {
$transaction->amount = $amount * -1;
}
$transaction->save();
}
}
}
\Event::fire('account.update', [$model]);
return true;
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
/*
* Name validation:
*/
if (!isset($model['name'])) {
$errors->add('name', 'Name is mandatory');
}
if (isset($model['name']) && strlen($model['name']) == 0) {
$errors->add('name', 'Name is too short');
}
if (isset($model['name']) && strlen($model['name']) > 100) {
$errors->add('name', 'Name is too long');
}
$validator = \Validator::make([$model], \Account::$rules);
if ($validator->invalid()) {
$errors->merge($errors);
}
if (isset($model['account_role']) && !in_array($model['account_role'], array_keys(\Config::get('firefly.accountRoles')))) {
$errors->add('account_role', 'Invalid account role');
} else {
$successes->add('account_role', 'OK');
}
/*
* type validation.
*/
if (!isset($model['what'])) {
$errors->add('name', 'Internal error: need to know type of account!');
}
/*
* Opening balance and opening balance date.
*/
if (isset($model['what']) && $model['what'] == 'asset') {
if (isset($model['openingbalance']) && strlen($model['openingbalance']) > 0 && !is_numeric($model['openingbalance'])) {
$errors->add('openingbalance', 'This is not a number.');
}
if (isset($model['openingbalancedate']) && strlen($model['openingbalancedate']) > 0) {
try {
new Carbon($model['openingbalancedate']);
} catch (\Exception $e) {
$errors->add('openingbalancedate', 'This date is invalid.');
}
}
}
if (!$errors->has('name')) {
$successes->add('name', 'OK');
}
if (!$errors->has('openingbalance')) {
$successes->add('openingbalance', 'OK');
}
if (!$errors->has('openingbalancedate')) {
$successes->add('openingbalancedate', 'OK');
}
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return \Eloquent
*/
public function find($id)
{
return $this->getUser()->accounts()->find($id);
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
throw new NotImplementedException;
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
throw new NotImplementedException;
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
return $this->getUser()->accounts()->whereIn('id', $ids)->get();
}
public function firstExpenseAccountOrCreate($name)
{
/** @var \FireflyIII\Database\AccountType $accountTypeRepos */
$accountTypeRepos = \App::make('FireflyIII\Database\AccountType');
$accountType = $accountTypeRepos->findByWhat('expense');
// if name is "", find cash account:
if (strlen($name) == 0) {
$cashAccountType = $accountTypeRepos->findByWhat('cash');
// find or create cash account:
return \Account::firstOrCreate(
['name' => 'Cash account', 'account_type_id' => $cashAccountType->id, 'active' => 1, 'user_id' => $this->getUser()->id,]
);
}
$data = ['user_id' => $this->getUser()->id, 'account_type_id' => $accountType->id, 'name' => $name, 'active' => 1];
return \Account::firstOrCreate($data);
}
public function firstRevenueAccountOrCreate($name)
{
/** @var \FireflyIII\Database\AccountType $accountTypeRepos */
$accountTypeRepos = \App::make('FireflyIII\Database\AccountType');
$accountType = $accountTypeRepos->findByWhat('revenue');
$data = ['user_id' => $this->getUser()->id, 'account_type_id' => $accountType->id, 'name' => $name, 'active' => 1];
return \Account::firstOrCreate($data);
}
public function getAllTransactionJournals(\Account $account, $limit = 50)
{
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0;
$set = $this->getUser()->transactionJournals()->withRelevantData()->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->where('transactions.account_id', $account->id)->take($limit)->offset($offset)->orderBy('date', 'DESC')->get(
['transaction_journals.*']
);
$count = $this->getUser()->transactionJournals()->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->orderBy('date', 'DESC')->where('transactions.account_id', $account->id)->count();
$items = [];
foreach ($set as $entry) {
$items[] = $entry;
}
return \Paginator::make($items, $count, $limit);
}
public function getTransactionJournals(\Account $account, $limit = 50)
{
$start = \Session::get('start');
$end = \Session::get('end');
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0;
$set = $this->getUser()->transactionJournals()->withRelevantData()->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->where('transactions.account_id', $account->id)->take($limit)->offset($offset)->before($end)->after($start)->orderBy('date', 'DESC')->get(
['transaction_journals.*']
);
$count = $this->getUser()->transactionJournals()->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->before($end)->after($start)->orderBy('date', 'DESC')->where('transactions.account_id', $account->id)->count();
$items = [];
foreach ($set as $entry) {
$items[] = $entry;
}
return \Paginator::make($items, $count, $limit);
}
/**
* @param \Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Pagination\Paginator
*/
public function getTransactionJournalsInRange(\Account $account, Carbon $start, Carbon $end)
{
$set = $this->getUser()->transactionJournals()->transactionTypes(['Withdrawal'])->withRelevantData()->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->where('transactions.account_id', $account->id)->before($end)->after($start)->orderBy('date', 'DESC')->get(
['transaction_journals.*']
);
return $set;
}
}

View File

@@ -0,0 +1,135 @@
<?php
namespace FireflyIII\Database;
use FireflyIII\Exception\FireflyException;
use FireflyIII\Database\Ifaces\AccountTypeInterface;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\Collection;
/**
* Class AccountType
*
* @package FireflyIII\Database
*/
class AccountType implements CUD, CommonDatabaseCalls
{
/**
* @param \Eloquent $model
*
* @return bool
*/
public function destroy(\Eloquent $model)
{
// TODO: Implement destroy() method.
throw new NotImplementedException;
}
/**
* @param array $data
*
* @return \Eloquent
*/
public function store(array $data)
{
// TODO: Implement store() method.
throw new NotImplementedException;
}
/**
* @param \Eloquent $model
* @param array $data
*
* @return bool
*/
public function update(\Eloquent $model, array $data)
{
// TODO: Implement update() method.
throw new NotImplementedException;
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
// TODO: Implement validate() method.
throw new NotImplementedException;
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return \Eloquent
*/
public function find($id)
{
// TODO: Implement find() method.
throw new NotImplementedException;
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue
*
* @param $what
*
* @return \AccountType|null
* @throws FireflyException
*/
public function findByWhat($what)
{
switch ($what) {
case 'expense':
return \AccountType::whereType('Expense account')->first();
break;
case 'asset':
return \AccountType::whereType('Asset account')->first();
break;
case 'revenue':
return \AccountType::whereType('Revenue account')->first();
break;
case 'cash':
return \AccountType::whereType('Cash account')->first();
break;
case 'initial':
return \AccountType::whereType('Initial balance account')->first();
break;
default:
throw new FireflyException('Cannot find account type described as "' . e($what) . '".');
break;
}
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
throw new NotImplementedException;
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
throw new NotImplementedException;
}
}

View File

@@ -0,0 +1,266 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use FireflyIII\Database\Ifaces\BudgetInterface;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
* Class Budget
*
* @package FireflyIII\Database
*/
class Budget implements CUD, CommonDatabaseCalls, BudgetInterface
{
use SwitchUser;
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param \Eloquent $model
*
* @return bool
*/
public function destroy(\Eloquent $model)
{
$model->delete();
return true;
}
/**
* @param array $data
*
* @return \Eloquent
*/
public function store(array $data)
{
$data['user_id'] = $this->getUser()->id;
$budget = new \Budget($data);
$budget->class = 'Budget';
if (!$budget->isValid()) {
var_dump($budget->getErrors()->all());
exit;
}
$budget->save();
return $budget;
}
/**
* @param \Eloquent $model
* @param array $data
*
* @return bool
*/
public function update(\Eloquent $model, array $data)
{
$model->name = $data['name'];
if (!$model->isValid()) {
var_dump($model->getErrors()->all());
exit;
}
$model->save();
return true;
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
if (isset($model['name'])) {
if (strlen($model['name']) < 1) {
$errors->add('name', 'Name is too short');
}
if (strlen($model['name']) > 200) {
$errors->add('name', 'Name is too long');
}
} else {
$errors->add('name', 'Name is mandatory');
}
$validator = \Validator::make($model, \Component::$rules);
if ($validator->invalid()) {
$errors->merge($validator->errors());
}
if (!$errors->has('name')) {
$successes->add('name', 'OK');
}
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return \Eloquent
*/
public function find($id)
{
return $this->getUser()->budgets()->find($id);
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
throw new NotImplementedException;
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
$budgets = $this->getUser()->budgets()->get();
return $budgets;
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
throw new NotImplementedException;
}
public function getTransactionJournals(\Budget $budget, $limit = 50)
{
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0;
$set = $budget->transactionJournals()->withRelevantData()->take($limit)->offset($offset)->orderBy('date', 'DESC')->get(['transaction_journals.*']);
$count = $budget->transactionJournals()->count();
$items = [];
foreach ($set as $entry) {
$items[] = $entry;
}
return \Paginator::make($items, $count, $limit);
}
public function getTransactionJournalsInRepetition(\Budget $budget, \LimitRepetition $repetition, $limit = 50)
{
$start = $repetition->startdate;
$end = $repetition->enddate;
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0;
$set = $budget->transactionJournals()->withRelevantData()->before($end)->after($start)->take($limit)->offset($offset)->orderBy('date', 'DESC')->get(
['transaction_journals.*']
);
$count = $budget->transactionJournals()->before($end)->after($start)->count();
$items = [];
foreach ($set as $entry) {
$items[] = $entry;
}
return \Paginator::make($items, $count, $limit);
}
/**
* @param \Budget $budget
* @param Carbon $date
*
* @return \LimitRepetition|null
*/
public function repetitionOnStartingOnDate(\Budget $budget, Carbon $date)
{
return \LimitRepetition::
leftJoin('limits', 'limit_repetitions.limit_id', '=', 'limits.id')->leftJoin(
'components', 'limits.component_id', '=', 'components.id'
)->where('limit_repetitions.startdate', $date->format('Y-m-d'))->where(
'components.id', $budget->id
)->first(['limit_repetitions.*']);
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function transactionsWithoutBudgetInDateRange(Carbon $start, Carbon $end)
{
// Add expenses that have no budget:
return $this->getUser()->transactionjournals()->whereNotIn(
'transaction_journals.id', function ($query) use ($start, $end) {
$query->select('transaction_journals.id')->from('transaction_journals')->leftJoin(
'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id')->where(
'transaction_journals.date', '>=', $start->format('Y-m-d')
)->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->where('components.class', 'Budget');
}
)->before($end)->after($start)->lessThan(0)->transactionTypes(['Withdrawal'])->get();
}
/**
* @param \Budget $budget
* @param Carbon $date
*
* @return float
*/
public function spentInMonth(\Budget $budget, Carbon $date)
{
$end = clone $date;
$date->startOfMonth();
$end->endOfMonth();
$sum = floatval($budget->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1;
return $sum;
}
/**
* @param \Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function spentInPeriod(\Budget $budget, Carbon $start, Carbon $end)
{
$sum = floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
return $sum;
}
}

View File

@@ -0,0 +1,213 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use FireflyIII\Database\Ifaces\CategoryInterface;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
/**
* Class Category
*
* @package FireflyIII\Database
*/
class Category implements CUD, CommonDatabaseCalls
{
use SwitchUser;
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param \Eloquent $model
*
* @return bool
*/
public function destroy(\Eloquent $model)
{
$model->delete();
return true;
}
/**
* @param array $data
*
* @return \Eloquent
*/
public function store(array $data)
{
$category = new \Category;
$category->name = $data['name'];
$category->class = 'Category';
$category->user()->associate($this->getUser());
if (!$category->isValid()) {
var_dump($category->getErrors());
exit();
}
$category->save();
return $category;
}
/**
* @param \Eloquent $model
* @param array $data
*
* @return bool
*/
public function update(\Eloquent $model, array $data)
{
$model->name = $data['name'];
if (!$model->isValid()) {
var_dump($model->getErrors()->all());
exit;
}
$model->save();
return true;
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
if (isset($model['name'])) {
if (strlen($model['name']) < 1) {
$errors->add('name', 'Name is too short');
}
if (strlen($model['name']) > 200) {
$errors->add('name', 'Name is too long');
}
} else {
$errors->add('name', 'Name is mandatory');
}
$validator = \Validator::make($model, \Component::$rules);
if ($validator->invalid()) {
$errors->merge($validator->getErrors());
}
if (!$errors->has('name')) {
$successes->add('name', 'OK');
}
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return \Eloquent
*/
public function find($id)
{
// TODO: Implement find() method.
throw new NotImplementedException;
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
throw new NotImplementedException;
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
return $this->getUser()->categories()->orderBy('name', 'ASC')->get();
}
/**
* @param array $ids
*
* @return Collection
*/
public function getByIds(array $ids)
{
// TODO: Implement getByIds() method.
throw new NotImplementedException;
}
public function firstOrCreate($name)
{
return \Category::firstOrCreate(['user_id' => $this->getUser()->id, 'name' => $name]);
}
public function getTransactionJournals(\Category $category, $limit = 50)
{
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0;
$set = $category->transactionJournals()->withRelevantData()->take($limit)->offset($offset)->orderBy('date', 'DESC')->get(['transaction_journals.*']);
$count = $category->transactionJournals()->count();
$items = [];
foreach ($set as $entry) {
$items[] = $entry;
}
return \Paginator::make($items, $count, $limit);
}
/**
* @param \Category $budget
* @param Carbon $date
*
* @return null
*/
public function repetitionOnStartingOnDate(\Category $category, Carbon $date)
{
return null;
}
/**
* @param \Category $category
* @param Carbon $date
*
* @return float
*/
public function spentInMonth(\Category $category, Carbon $date)
{
$end = clone $date;
$date->startOfMonth();
$end->endOfMonth();
$sum = floatval($category->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1;
return $sum;
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace FireflyIII\Database\Ifaces;
use Illuminate\Support\Collection;
/**
* Interface AccountInterface
*
* @package FireflyIII\Database
*/
interface AccountInterface
{
/**
* Counts the number of accounts found with the included types.
*
* @param array $types
*
* @return int
*/
public function countAccountsByType(array $types);
/**
* Counts the number of total asset accounts. Useful for DataTables.
*
* @return int
*/
public function countAssetAccounts();
/**
* Counts the number of total expense accounts. Useful for DataTables.
*
* @return int
*/
public function countExpenseAccounts();
/**
* Counts the number of total revenue accounts. Useful for DataTables.
*
* @return int
*/
public function countRevenueAccounts();
/**
* @param \Account $account
*
* @return \Account|null
*/
public function findInitialBalanceAccount(\Account $account);
/**
* Get all accounts of the selected types. Is also capable of handling DataTables' parameters.
*
* @param array $types
*
* @return Collection
*/
public function getAccountsByType(array $types);
/**
* Get all asset accounts. The parameters are optional and are provided by the DataTables plugin.
*
* @return Collection
*/
public function getAssetAccounts();
/**
* @return Collection
*/
public function getExpenseAccounts();
/**
* Get all revenue accounts.
*
* @return Collection
*/
public function getRevenueAccounts();
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account);
/**
* @param \Account $account
* @param array $data
*
* @return bool
*/
public function storeInitialBalance(\Account $account, array $data);
}

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