Compare commits

...

679 Commits

Author SHA1 Message Date
James Cole
8a3b7d7c1a Merge branch 'hotfix/3.7.2.3' 2016-02-06 04:37:29 +01:00
James Cole
87f14617cc Code fix for #172. 2016-02-06 04:37:18 +01:00
James Cole
9f24f765ea Fix in Javascript for #172. 2016-02-06 04:35:51 +01:00
James Cole
48c802e5cc New version. 2016-02-06 04:34:06 +01:00
James Cole
9c0893fa8c Merge branch 'hotfix/3.7.2.2' 2016-02-05 13:22:02 +01:00
James Cole
0dfb97c5f7 Fix #171 2016-02-05 13:21:56 +01:00
James Cole
4b4384b1a8 Merge branch 'hotfix/3.7.2.1' 2016-02-05 13:16:21 +01:00
James Cole
9b10984d81 Fix for bug #171 2016-02-05 13:16:12 +01:00
James Cole
10a2a4cf5b Merge branch 'hotfix/3.7.2' 2016-02-05 07:40:38 +01:00
James Cole
d48e4c66b2 I have no idea what I'm doing 2016-02-05 07:40:15 +01:00
James Cole
d357142075 Merge branch 'release/3.7.1' 2016-02-05 07:34:34 +01:00
James Cole
2e7d339d7e Fix for bug #170, caught thanks to @boerbiet 2016-02-05 07:34:25 +01:00
James Cole
6303b172b1 Merge branch 'release/3.7.0' into develop 2016-02-04 07:21:29 +01:00
James Cole
636ae193a7 Merge branch 'release/3.7.0' 2016-02-04 07:21:28 +01:00
James Cole
aff8493204 Fixed bug where download would not be pushed as download. 2016-02-03 11:46:28 +01:00
James Cole
8f2d4494d5 Fixed a bug in the beforeDraw() routine. 2016-01-31 14:02:52 +01:00
James Cole
7a2bb4eb96 Removed some old CSS. Might just fix #114 2016-01-30 10:00:52 +01:00
James Cole
4916c06797 Added new dates to transaction journal, see #122 2016-01-30 09:52:05 +01:00
James Cole
a818ab0942 Fixes #110 2016-01-30 09:41:48 +01:00
James Cole
9aa89393c0 Forgot some translations. 2016-01-30 08:57:25 +01:00
James Cole
216b304fe1 Updated composer.lock. 2016-01-30 08:47:37 +01:00
James Cole
cf8a687b4d Added some new lines to files. 2016-01-30 08:42:04 +01:00
James Cole
d1d6ba4114 Updated read me file. 2016-01-30 08:38:40 +01:00
James Cole
a5ac713310 Importer will now fire custom rules. 2016-01-30 07:36:22 +01:00
James Cole
6fa8c33672 Fixed some bugs in test data. 2016-01-30 07:36:11 +01:00
James Cole
dd2b019d3c Moved test data around. 2016-01-30 07:36:02 +01:00
James Cole
3a12ad192f Move more javascript around. 2016-01-29 18:39:50 +01:00
James Cole
d669b75352 This view does not use JS. 2016-01-29 18:33:01 +01:00
James Cole
746d50d459 Some javascript related cleaning up. 2016-01-29 17:53:49 +01:00
James Cole
f741552d91 Some javascript code cleanup. Should make some pages lighter. 2016-01-29 17:49:02 +01:00
James Cole
c38e4b86b4 I noticed a weird page jump in the piggy bank section so it seemed a good moment to clean up some javascript. 2016-01-29 17:31:07 +01:00
James Cole
b044d85e90 Expand report index to include both "normal" year and fiscal year. 2016-01-29 17:14:23 +01:00
James Cole
24d111f026 New text. 2016-01-29 17:14:02 +01:00
James Cole
f50058e3c2 Renamed fiscal year start and end variable. 2016-01-29 17:13:54 +01:00
James Cole
ce9445168c Move the start and end date for the fiscal date ranges to fiscal-start and fiscal-end respectively. 2016-01-29 17:09:23 +01:00
James Cole
27b8b4b35a Use the fiscal helper's interface instead of the fiscal helper itself. 2016-01-29 17:08:06 +01:00
James Cole
9ccb67db8a Charts "columnChart" and "stackedColumnChart" now support beforeDraw() which will check the data to see if the chart should be drawn. This cleans up the front page for new users and empty months. 2016-01-29 13:35:08 +01:00
James Cole
3d82afd4e6 Because the "options" parameter is used nowhere I've removed it to be used for something else. 2016-01-29 13:25:04 +01:00
James Cole
5f9cb61160 Some stuff moving around in test data generation. 2016-01-29 13:24:33 +01:00
James Cole
7f54b70c24 Make fiscal year quick link optional. 2016-01-29 07:47:18 +01:00
James Cole
a156dce281 Some minor code cleanup. 2016-01-29 07:35:14 +01:00
James Cole
c0b0e58720 Removed duplicated code. 2016-01-29 07:33:49 +01:00
James Cole
170ffbae04 Removed duplicate code. 2016-01-29 07:29:21 +01:00
James Cole
6755ec7eb0 Some code cleanup. 2016-01-29 07:17:50 +01:00
James Cole
c8bc9a096a Some other clean up. 2016-01-29 07:08:17 +01:00
James Cole
dc7f5a562b Clean up some migrations. 2016-01-29 07:08:04 +01:00
James Cole
3b5b51578d Merge pull request #153 from webenhanced/feature/current-fiscal-year-quicklink-report
Quicklink fiscal year report feature. As per github issue #131.
2016-01-29 06:45:36 +01:00
Graham Miller
41188a1bd6 Quicklink fiscal year report feature. As per github issue #131.
The reason for instantiating the helper at the beginning of the static function routeBinder
is I am hoping that the object will only be created once.
2016-01-29 09:11:36 +10:00
James Cole
d9bafe34eb Renamed some stuff, courtesy of sensiolabs. 2016-01-28 22:06:16 +01:00
James Cole
43dbba5378 Implemented wrong interface. 2016-01-28 21:59:40 +01:00
James Cole
168ed5ac56 Renamed some stuff, courtesy of sensiolabs. 2016-01-28 21:50:20 +01:00
James Cole
e5b4a55d8e Some code cleanup things courtesy of SensioLabs. 2016-01-28 21:33:45 +01:00
James Cole
167c057e8a Removed executable rights. 2016-01-28 21:22:26 +01:00
James Cole
71bf054ab1 Removed TODO items in favour of issues, or actual fixes. 2016-01-28 21:05:26 +01:00
James Cole
111fcd77c4 Code style stuff for @webenhanced 2016-01-28 16:05:39 +01:00
James Cole
287c1c4ffa Some code cleanup. 2016-01-28 16:03:49 +01:00
James Cole
3d69dc786d Some minor code cleanup. 2016-01-27 21:52:21 +01:00
James Cole
57a3f20c13 Moved more code around. 2016-01-27 21:35:59 +01:00
James Cole
eab2c57594 Moved another method. 2016-01-27 21:18:51 +01:00
James Cole
b019962f34 Moved a method. 2016-01-27 20:54:14 +01:00
James Cole
5c59c819b6 Moved method to new helper. 2016-01-27 20:48:35 +01:00
James Cole
93b97b8d72 Refer to new account helper. 2016-01-27 20:46:38 +01:00
James Cole
f1f922031a Refer to new account helper 2016-01-27 20:45:49 +01:00
James Cole
390cace775 New account report helper to make the report helper a lot lighter. 2016-01-27 20:45:05 +01:00
James Cole
28fdad9426 A set of small fixes, courtesy of scrutinizer-ci 2016-01-27 19:35:00 +01:00
James Cole
9155c13e08 A set of small fixes, courtesy of scrutinizer-ci 2016-01-27 18:31:44 +01:00
James Cole
e8776d44c5 Merge pull request #149 from webenhanced/feature/custom-fiscal-years
Feature - custom fiscal years
2016-01-27 17:57:13 +01:00
Graham Miller
5ee8d04800 Added modifications to reports enabling the custom fiscal year changes. 2016-01-27 13:38:34 +10:00
Graham Miller
eb31934fb7 Add a new helper to handle fiscal issues.
Used initially to provide fiscal year support to the reports.
2016-01-27 11:54:04 +10:00
Graham Miller
000f86d318 Added fiscal year start date stored in 'm-d' format to preferences.
Displays YYYY-MM-DD for current year to get input.
2016-01-27 11:31:33 +10:00
Graham Miller
d9b3ebc82f Add preferences option to enable or disable the custom fiscal year handling.
Stored in DB as 0 or 1 and converted:-
- twig expression in view (expandedform needs true boolean)
- checkbox true/false converted to integer in set function in controller

Oh and I worked out how to localize the label ... took me a while but ended up so simple.

This is stage 1 of the overall custom fiscal year extension for Firefly.
2016-01-27 11:28:14 +10:00
Graham Miller
91b3ca047a Merge branch 'develop' of https://github.com/JC5/firefly-iii.git 2016-01-27 10:35:33 +10:00
James Cole
08131e42af Implemented some new tests. 2016-01-24 20:38:58 +01:00
James Cole
a013553a6c More cleanup. 2016-01-24 18:11:57 +01:00
James Cole
7b2fe8eb4a Test code cleanup. 2016-01-24 18:05:04 +01:00
James Cole
610f782054 Translations. 2016-01-24 16:51:01 +01:00
James Cole
94700f4064 Fixed some things in the rule controllers. 2016-01-24 16:50:55 +01:00
James Cole
0f12ebb31c Expanded tests. 2016-01-24 16:25:03 +01:00
James Cole
00a8a9ac0e Removed parameter rule trigger / rule action since they weren't being used. 2016-01-24 16:12:59 +01:00
James Cole
92616c6ae3 New test data with required role. 2016-01-24 16:05:14 +01:00
James Cole
97db618cd8 Some code cleanup and reordering 2016-01-24 15:58:16 +01:00
James Cole
2832d308f1 Optimize scripts. 2016-01-24 15:55:48 +01:00
James Cole
a7ecdf715f New translations. 2016-01-24 15:54:54 +01:00
James Cole
935dc3ff9f Some new tests 2016-01-23 16:48:34 +01:00
James Cole
99d14e8cbe This prevents constraint errors when the specified category has already been set. 2016-01-23 16:48:15 +01:00
James Cole
664fde2344 Some forgotten translations. 2016-01-23 09:10:22 +01:00
James Cole
27c45eface Updated test env. 2016-01-23 08:51:34 +01:00
James Cole
f83bc3c8b3 Updated tests. 2016-01-23 08:48:36 +01:00
James Cole
0d5efb8d27 Better mocking of objects. 2016-01-23 06:59:22 +01:00
James Cole
bf9c1c1875 Fixed some tests so they will not be skipped. 2016-01-23 06:54:29 +01:00
James Cole
049d866f62 Unskipped some tests. 2016-01-22 23:00:32 +01:00
James Cole
d672f0c2ad Better scripts (accept arguments) 2016-01-22 22:39:51 +01:00
James Cole
7b040e8583 Some changes to test coverage. 2016-01-22 21:08:04 +01:00
James Cole
e1cf285272 Various updates in test data and test configuration. 2016-01-22 20:47:48 +01:00
James Cole
53f7f13362 New env file. 2016-01-22 20:44:25 +01:00
James Cole
f710677cdc Also check for CSV delimiter presence. 2016-01-22 20:43:44 +01:00
James Cole
b5fbc8b632 test data 2016-01-22 20:13:55 +01:00
James Cole
1fdc0a196c Test data. 2016-01-22 20:13:27 +01:00
James Cole
559f429f5e Merge pull request #148 from roberthorlings/bugfix/rulegrouprepository
Added binding for RuleGroupRepositoryInterface
2016-01-22 19:30:37 +01:00
James Cole
ae4b198d3d Merge pull request #147 from roberthorlings/develop
Fixed a bug in storing date changes for the opening balance
2016-01-22 19:30:02 +01:00
Robert Horlings
e088ecbbad Added binding for RuleGroupRepositoryInterface to enable storing a rulegroup 2016-01-22 16:01:33 +01:00
Robert Horlings
f6b7bd5b44 Fixed a bug in storing date changes for the opening balance of an account 2016-01-22 15:44:23 +01:00
James Cole
f5a21f64c0 More magic words. 2016-01-22 10:10:51 +01:00
James Cole
f5cbed7c0c Added magic words "currentMonthEnd" and "currentMonthStart". 2016-01-22 07:54:15 +01:00
James Cole
59fff8928b Simplified some methods. 2016-01-22 07:35:28 +01:00
James Cole
8743b49a17 Some fixes for code climate. 2016-01-22 07:27:49 +01:00
James Cole
19dc91937e Remove double badge. 2016-01-22 07:22:34 +01:00
James Cole
4846cb7b14 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop 2016-01-21 13:56:30 +01:00
James Cole
54e0ac816b Updated readme. [skip ci] 2016-01-21 13:56:08 +01:00
James Cole
9ce960b3d7 There are no converters that need the role of the current field. 2016-01-21 09:52:45 +01:00
James Cole
cc9d1c4bfd Some unrelated code cleanup. 2016-01-20 21:24:48 +01:00
James Cole
d211555beb Only fire post-processing specifix in the import routine. See issue #145 2016-01-20 21:24:27 +01:00
James Cole
9350557d10 Properly extend the current specifix to indicate they're all post-processors. See issue #145 2016-01-20 21:24:08 +01:00
James Cole
ea6c54cad0 Expand specifix to have a property that determines whether they fire before anything happens, or after. Could be expanded to allow for even more fine-grained control. See issue #145 2016-01-20 21:23:39 +01:00
James Cole
81287602d7 Updated tests. 2016-01-20 16:15:23 +01:00
James Cole
441ba79ec7 Some tests 2016-01-20 15:27:53 +01:00
James Cole
c9e4a09da6 Sort by alphabet. 2016-01-20 15:23:36 +01:00
James Cole
c84f4e2bc0 Sort by alphabet. 2016-01-20 15:21:27 +01:00
James Cole
6938f8ab89 I just realised this still said "watch movie". 2016-01-20 15:14:31 +01:00
James Cole
4fdd2c851f Fix registration. 2016-01-20 14:23:50 +01:00
James Cole
78b56712fc More new tests. 2016-01-20 10:47:29 +01:00
James Cole
6dcdb0f9a0 Committed wrong .gitignore file. 2016-01-20 10:14:27 +01:00
James Cole
cfab837b96 Create upload directory. 2016-01-20 10:14:05 +01:00
James Cole
b8461cdeb6 Touch empty attachment. 2016-01-20 10:13:44 +01:00
James Cole
e07d65ef6b Create default upload directory. 2016-01-20 10:13:33 +01:00
James Cole
80cbc35c89 More test data, more tests. 2016-01-20 09:31:24 +01:00
James Cole
2faf84780f Implemented some tests. 2016-01-20 09:15:33 +01:00
James Cole
ebdb1a8836 No more session flash. 2016-01-20 08:25:49 +01:00
James Cole
2dfa51652a Basic set of tests (incomplete). 2016-01-20 06:20:09 +01:00
James Cole
1cf1ea230f Updated read me. 2016-01-19 19:59:32 +01:00
James Cole
86e127ebff Fixed password reset routine. 2016-01-19 18:32:09 +01:00
James Cole
a313265785 Different URL for password reset. 2016-01-19 18:14:15 +01:00
James Cole
37693ad08d Fixed mail config. 2016-01-19 18:14:03 +01:00
James Cole
e8180d252b Wrong URL for login form. 2016-01-19 18:10:30 +01:00
James Cole
9957c95c68 Re-order auth controller. 2016-01-19 18:10:07 +01:00
James Cole
3354d53a48 Reset phpunit xml. 2016-01-19 18:09:53 +01:00
James Cole
ddaa53b940 Updated test code. 2016-01-19 16:55:53 +01:00
James Cole
c2e04a11bf Updated run scripts for tests. 2016-01-19 16:55:02 +01:00
James Cole
00d059b8df More tests. 2016-01-19 14:37:44 +01:00
James Cole
f3fff6f1c5 Code rearrangement. 2016-01-19 13:59:54 +01:00
James Cole
9360eb6a70 First account controller test. 2016-01-19 13:53:55 +01:00
James Cole
26fb03e6c8 Reformatted code according to scrutinizer-ci. 2016-01-19 09:11:15 +01:00
James Cole
eed3d021d9 Small cleanup. 2016-01-19 08:48:38 +01:00
James Cole
48728e8418 Merge pull request #146 from roberthorlings/feature/abn-amro-import-specifix
ABN AMRO Specifix
2016-01-19 08:36:06 +01:00
Robert Horlings
f39f25760a Added messages for new specifix 2016-01-18 15:43:30 +01:00
Robert Horlings
10ea60daaf Improved SEPA description parsing 2016-01-18 15:42:56 +01:00
Robert Horlings
f2fa5e140b Merge remote-tracking branch 'origin/develop' into feature/abn-amro-import-specifix 2016-01-18 15:35:08 +01:00
James Cole
be5ff35b13 Reversed the removal of the validating trait since @roberthorlings discovered it's actually still being used. 2016-01-18 13:15:11 +01:00
Robert Horlings
bd5d73d1e6 Removed transaction type from the description 2016-01-18 12:37:06 +01:00
Robert Horlings
cc712b0c75 Updated message identifier for unknown opposing account 2016-01-18 12:31:45 +01:00
Robert Horlings
a5ac84e1b9 Merge remote-tracking branch 'origin/develop' into feature/abn-amro-import-specifix 2016-01-18 09:09:35 +01:00
Robert Horlings
cbe1f762ca Fixing a bug with expense account being set to unknown 2016-01-18 09:09:30 +01:00
James Cole
e6db49c20c Some tests. 2016-01-17 15:48:18 +01:00
Robert Horlings
4909fcc8b4 Moved parsing of amount with decimal separator to Converter object 2016-01-17 15:37:49 +01:00
James Cole
68cdfd00b0 Update test things. 2016-01-17 15:28:01 +01:00
Robert Horlings
323c16ebe1 Implemented additional ABNAMRO description formats 2016-01-17 15:25:47 +01:00
James Cole
c20a38b7b0 Ignore file for coverage logs. 2016-01-17 08:38:19 +01:00
James Cole
20993342a2 Better wording. 2016-01-17 07:55:23 +01:00
James Cole
1b3198c143 New: upgrade instructions when installing or upgrading composer packages. 2016-01-17 07:50:09 +01:00
James Cole
173d0290ea New composer stuff. 2016-01-17 07:49:49 +01:00
James Cole
ae4cd8da12 This should fix the travis builds. 2016-01-17 07:30:14 +01:00
James Cole
a494398332 Actually correct database name for once. 2016-01-17 07:24:06 +01:00
James Cole
c82bdd1f5a New gitignore. 2016-01-17 07:22:36 +01:00
James Cole
e6ac3ccfad Fix travis configuration. 2016-01-17 07:19:44 +01:00
James Cole
b13b58a2b2 More test data and an actual test. 2016-01-17 07:18:35 +01:00
James Cole
2b6790f83f Fixed data seeder. 2016-01-17 07:07:22 +01:00
James Cole
fe30850568 Fixed some database migration stuff. 2016-01-16 21:32:36 +01:00
James Cole
493c71b89e Fix test data seeder. 2016-01-16 09:16:26 +01:00
James Cole
deaebc373c Some new code for testing. 2016-01-16 09:15:24 +01:00
James Cole
e834489206 Some cleaning up courtesy of PHPStorm. 2016-01-16 07:14:36 +01:00
James Cole
1b316e462e Also fire rules when updating. 2016-01-16 06:34:50 +01:00
James Cole
7bf75128a8 Some cleaning up courtesy of PHPStorm. 2016-01-15 23:12:52 +01:00
James Cole
3857e8d49f Remove old code. 2016-01-15 22:44:25 +01:00
James Cole
b16149b842 Wrong array name. 2016-01-15 22:44:17 +01:00
James Cole
32ed8a4d8d Removed some unnecessary methods. 2016-01-15 22:41:26 +01:00
James Cole
f949d2476b Move from getDates to array dates. 2016-01-15 22:32:21 +01:00
James Cole
0620830b10 Move from getDates to array dates. 2016-01-15 22:23:01 +01:00
James Cole
7ee25693aa Removed stuff no longer used. 2016-01-15 22:20:38 +01:00
James Cole
7cb86add64 New phpdoc. 2016-01-15 22:13:38 +01:00
James Cole
fbc9720f7a Clean up some report methods. 2016-01-15 21:50:57 +01:00
James Cole
ffdd37ddd5 More code cleanup. 2016-01-15 20:57:26 +01:00
James Cole
51062bc80b Some cleaning up and suppressing. 2016-01-15 20:48:06 +01:00
James Cole
31533b5ef8 No need to check for opening balance. 2016-01-15 19:48:35 +01:00
James Cole
3649b595df Will no longer recognise transactions of type "opening balance". 2016-01-15 19:48:09 +01:00
James Cole
83b7c9aa32 Fixed a bug in editing an opening balance 2016-01-15 19:45:34 +01:00
James Cole
4e3c59a2da Some cleaning up courtesy of PHPStorm. 2016-01-15 19:37:09 +01:00
James Cole
dcbfe90cf7 Some code cleanup courtesy of PHPStorm. 2016-01-15 18:21:59 +01:00
James Cole
f69be86c74 Some code cleanup courtesy of PHPStorm. 2016-01-15 17:53:54 +01:00
James Cole
820722f44e Some code cleanup courtesy of PHPStorm. 2016-01-15 17:38:09 +01:00
Robert Horlings
2ff806dedc Initial version of ABN AMRO import specifix with amount correction and SEPA description parser 2016-01-15 16:48:09 +01:00
James Cole
a755badd5f Use correct parameter. 2016-01-15 16:33:54 +01:00
James Cole
4df1e5393b Also updated all translations. 2016-01-15 16:33:03 +01:00
James Cole
8c55bd179f Moved the option right under the file selection. 2016-01-15 16:27:17 +01:00
James Cole
ccfc5ece66 Put the form options in the translation files. 2016-01-15 16:27:01 +01:00
James Cole
6c12f1bc86 Some refactoring, courtesy of PHPStorm. 2016-01-15 16:26:49 +01:00
James Cole
88839e9610 For more clarity in the code, moved the array of options to the controller itself. 2016-01-15 16:26:24 +01:00
James Cole
4f6a733238 For more clarity in the code, moved the array of options to the controller itself. 2016-01-15 16:26:19 +01:00
James Cole
dc9083a764 Also forget delimiter. 2016-01-15 16:26:02 +01:00
James Cole
6f231b840b Merge pull request #144 from roberthorlings/feature/import-csv-delimiter
Option to choose CSV delimiter when importing data
2016-01-15 16:19:53 +01:00
Robert Horlings
61a703e605 Implemented option to choose field delimiter in CSV import 2016-01-15 15:24:07 +01:00
Robert Horlings
8e0e9734a5 Updated gitignore with eclipse files and storage directory 2016-01-15 15:07:42 +01:00
James Cole
2afb455bac New translations. 2016-01-15 13:40:39 +01:00
James Cole
01792f91e2 Code cleanup 2016-01-15 13:13:33 +01:00
James Cole
651dff0750 Shorter method names. 2016-01-15 13:13:21 +01:00
James Cole
9cbfbd41dc Shorter method names. 2016-01-15 13:10:34 +01:00
James Cole
168d6f403c Shorter method names. 2016-01-15 13:09:27 +01:00
James Cole
41f200e630 Some code cleanup. 2016-01-15 13:08:25 +01:00
James Cole
0809cfdc6d Move methods around 2016-01-15 13:06:17 +01:00
James Cole
466d739da8 Start with new controller for rule groups. 2016-01-15 11:28:27 +01:00
James Cole
daf65cb387 Split rule and rule group repositories. 2016-01-15 11:27:27 +01:00
James Cole
2605f60983 Refactor. Split rules and rule groups. 2016-01-15 11:16:41 +01:00
James Cole
fcf6cdb134 Better order and display. 2016-01-15 10:52:35 +01:00
James Cole
209258b507 Fixed various bugs. 2016-01-15 09:25:32 +01:00
James Cole
f80bc214f9 Edit rules. 2016-01-15 08:45:39 +01:00
James Cole
f76990bb9b Twig methods. 2016-01-15 08:13:19 +01:00
James Cole
eef68c9b31 New extension for rules. 2016-01-15 08:10:22 +01:00
James Cole
006c2ae186 Refer to dedicated methods instead of variables. 2016-01-15 08:06:33 +01:00
James Cole
8b66ff6afe First edit-rule code. 2016-01-15 08:04:57 +01:00
James Cole
4cc6d57e6e Stop processing other rules if asked. 2016-01-15 06:43:00 +01:00
James Cole
af1c6b22bb Translation. 2016-01-15 06:35:53 +01:00
James Cole
5958990ed5 Stop processing. 2016-01-15 06:35:31 +01:00
James Cole
cd4cbdc197 Accidentally used a php 7 thing. 2016-01-15 06:25:09 +01:00
James Cole
1f538be16e More code for rules. 2016-01-14 21:34:17 +01:00
James Cole
9703439a4c Some more rule things before I merge a change to the CSV importer. 2016-01-14 19:20:02 +01:00
James Cole
42203ba872 Attempt at validating. 2016-01-14 18:57:52 +01:00
James Cole
cd60b852a1 This isn't really working. 2016-01-14 18:09:20 +01:00
James Cole
5b1d9e1a0d Start work on adding a rule. 2016-01-14 16:41:15 +01:00
James Cole
e02657a7c7 New font awesome 2016-01-14 13:12:20 +01:00
James Cole
c352eb0c74 First attempt at create rule form. 2016-01-14 12:37:49 +01:00
James Cole
81b9d5da09 User can now re-order triggers and actions. 2016-01-14 11:27:15 +01:00
James Cole
b9b0413510 More resources for rules. 2016-01-14 10:33:24 +01:00
James Cole
97770da619 Re-order rule groups. 2016-01-14 09:53:59 +01:00
James Cole
521623797e Move rule groups. 2016-01-14 09:49:12 +01:00
James Cole
15d3414443 Can order rules. 2016-01-14 09:38:48 +01:00
James Cole
245f06c93a Fix test, expand index. 2016-01-14 09:17:23 +01:00
James Cole
33899f0e2f New test data, new translations. 2016-01-14 09:08:44 +01:00
James Cole
4697fbdeef Routine to delete rules. Should include routine to move rules to other group. 2016-01-13 21:44:26 +01:00
James Cole
06174d6afb Edit rule group. 2016-01-13 18:50:15 +01:00
James Cole
b2bb16c1e0 Updated composer file. 2016-01-13 18:35:09 +01:00
James Cole
27aae279e6 More code for rules. 2016-01-13 18:34:56 +01:00
James Cole
e9ee93beb7 Expand view and translations. 2016-01-13 16:51:55 +01:00
James Cole
20941dedd3 Also fix rule group. 2016-01-13 16:08:05 +01:00
James Cole
5ac88623ed Prep stuff for routes and actions. 2016-01-13 16:05:39 +01:00
James Cole
768508dd4b First attempt at interface. 2016-01-13 15:59:45 +01:00
James Cole
668633e764 All actions and triggers. 2016-01-13 15:10:49 +01:00
James Cole
3dbb1f034d Fix the last triggers. 2016-01-13 14:51:49 +01:00
James Cole
1270e5d15c More valid triggers. 2016-01-13 14:37:19 +01:00
James Cole
afec8480fb More working triggers. 2016-01-13 14:12:46 +01:00
James Cole
8720511046 From account contains trigger. 2016-01-13 14:05:26 +01:00
James Cole
a7a00ecf40 More rule possibilities 2016-01-13 14:02:22 +01:00
James Cole
88bbafd3e8 If you get the destination account attribute before the model is saved, the cache breaks. 2016-01-13 14:01:40 +01:00
James Cole
ae3258b449 If you get the source account attribute before the model is saved, the cache breaks. 2016-01-13 14:01:09 +01:00
James Cole
cf4d7cfeef Grab rules properly. 2016-01-13 09:08:13 +01:00
James Cole
46ee2a0568 Can now automatically handle some rules. No user interface, yet. 2016-01-13 08:14:14 +01:00
James Cole
ce250c85fc Rename class. 2016-01-13 07:48:43 +01:00
James Cole
1087b9f5df Can now trigger. 2016-01-13 07:47:26 +01:00
James Cole
0dc74d9d14 Rules will now fire for a store-action. And fail. 2016-01-13 07:30:11 +01:00
James Cole
98b272383f Clean up triggers, add some new ones. 2016-01-13 07:16:29 +01:00
James Cole
e722daafd0 Removed old codeception file. 2016-01-13 05:54:42 +01:00
James Cole
670ab059d1 Simply log presence. 2016-01-13 05:37:25 +01:00
James Cole
09f826ceba First attempt at executing groups and rules. 2016-01-12 21:41:45 +01:00
James Cole
df5e3c9be9 Include handlers. 2016-01-12 21:41:19 +01:00
James Cole
31c336b3b1 New test data. 2016-01-12 21:40:31 +01:00
James Cole
b3a419b2f3 New events. 2016-01-12 21:38:12 +01:00
James Cole
11f63fa6ce Rename models for clarity 2016-01-12 21:38:05 +01:00
James Cole
4b35059140 Add connection to user. 2016-01-12 21:37:48 +01:00
James Cole
7836feba63 New trigger. 2016-01-12 21:10:16 +01:00
James Cole
1e54366f1f Update test data. 2016-01-12 21:09:51 +01:00
James Cole
d039bb0e03 Update database. 2016-01-12 21:09:38 +01:00
James Cole
166d32f073 Add actions 2016-01-12 21:09:28 +01:00
James Cole
321890992e Cleanup php doc. 2016-01-12 21:09:19 +01:00
James Cole
d644403d8a Add php doc to rule controller. 2016-01-12 20:56:53 +01:00
James Cole
44e76f9518 Cleanup rule migration. 2016-01-12 20:56:43 +01:00
James Cole
f9703eca4c Removed double configuration. 2016-01-12 20:56:36 +01:00
James Cole
48551e8bf5 Merge branch 'feature/rules' into develop 2016-01-12 20:39:49 +01:00
James Cole
74b538805b Expand config. 2016-01-12 20:38:19 +01:00
James Cole
35f493beff Move domain stuff to configuration. 2016-01-12 20:37:47 +01:00
James Cole
06d11c9133 New stuff for the rule thing. 2016-01-12 20:36:47 +01:00
James Cole
da7eb615db Added php doc. 2016-01-11 21:42:51 +01:00
James Cole
5da5024ad3 New models. 2016-01-11 21:28:29 +01:00
James Cole
c1346d4c86 Migration for rules. 2016-01-11 21:10:11 +01:00
James Cole
c38a4a15ff View 2016-01-11 20:42:24 +01:00
James Cole
bdec94ead6 Sidebar entry 2016-01-11 20:42:20 +01:00
James Cole
69bebf202f New translation 2016-01-11 20:42:15 +01:00
James Cole
38895afea6 New migration (empty) 2016-01-11 20:42:08 +01:00
James Cole
70ed4188b6 New controller for rules. 2016-01-11 20:41:57 +01:00
James Cole
d889094863 New route for rules index. 2016-01-11 20:41:50 +01:00
James Cole
c96eb8753e New bread crumb for rules controller. 2016-01-11 20:41:43 +01:00
James Cole
5a7607f6c6 More consistent monetary formatting. 2016-01-10 18:02:24 +01:00
James Cole
71bb88529a Also run php 7 tests. 2016-01-10 14:42:51 +01:00
James Cole
b26164a168 More test data. 2016-01-10 12:16:34 +01:00
James Cole
19444551e4 Get withdrawals only. 2016-01-10 12:16:25 +01:00
James Cole
e0b2a6e627 Translations. 2016-01-09 19:39:38 +01:00
James Cole
2973765866 New translations. 2016-01-09 19:37:21 +01:00
James Cole
2a08a25064 Fixed a translation. 2016-01-09 19:29:52 +01:00
James Cole
a55e291908 Removed money_format. 2016-01-09 18:02:36 +01:00
James Cole
2003d37a9a Fix for php 5.6 thing. 2016-01-09 16:10:12 +01:00
James Cole
29145bf6cf More bindings. 2016-01-09 16:09:26 +01:00
James Cole
caa1ff120a Built more binders. 2016-01-09 15:53:11 +01:00
James Cole
ef4e964c94 Fixed budget list binder. 2016-01-09 15:47:03 +01:00
James Cole
1f263f60a7 New binder for category lists. 2016-01-09 15:45:21 +01:00
James Cole
5fc7cafcbe All new binders. 2016-01-09 15:44:07 +01:00
James Cole
397c4926eb Update cookie policy 2016-01-09 15:39:41 +01:00
James Cole
a14544398b New middleware 2016-01-09 15:39:34 +01:00
James Cole
d439dceac1 New middleware. 2016-01-09 15:39:02 +01:00
James Cole
af29b31ea8 Remove "created by" message. 2016-01-09 15:38:48 +01:00
James Cole
b311ef70bc Point to website. 2016-01-09 14:34:28 +01:00
James Cole
9f9d744406 Cleared read me. [skip-ci] 2016-01-09 14:33:21 +01:00
James Cole
e094871bc9 Merge branch 'release/3.6.1' into develop
Conflicts:
	composer.lock
2016-01-09 14:31:52 +01:00
James Cole
dfc95cee45 Merge branch 'release/3.6.1' 2016-01-09 14:31:25 +01:00
James Cole
fdca234721 New composer lock file. 2016-01-09 14:31:17 +01:00
James Cole
cf5cc626d7 Important bug fix. 2016-01-09 14:27:35 +01:00
James Cole
358d9aac7d New version. 2016-01-09 14:27:17 +01:00
James Cole
681bc580c4 Removed GA beacon. 2016-01-09 12:54:43 +01:00
James Cole
4a2768f8d1 Reinstated test files. 2016-01-09 09:56:41 +01:00
James Cole
05f8773fa0 Fix user model. 2016-01-09 08:51:49 +01:00
James Cole
5e5fdfdd51 Move database reference. 2016-01-09 08:47:54 +01:00
James Cole
5e744390c0 Remove PHP requirement. 2016-01-09 08:45:27 +01:00
James Cole
cb5fa401cb Try to get travis working again. 2016-01-09 08:36:50 +01:00
James Cole
2980860377 Did some code cleanup. Comments and headers mostly. 2016-01-09 08:20:55 +01:00
James Cole
9ff0b282f3 With the growing popularity and frankly, quality of this software, I've decided to connect my actual name to it in the form of a license. Soon, every file will have a short reference to this license. 2016-01-09 07:57:42 +01:00
James Cole
4bc1c032bd Removed middle ware. 2016-01-09 07:48:45 +01:00
James Cole
9fcb00f10b Fixed some date range problems. 2016-01-09 07:46:11 +01:00
James Cole
723e461559 Fix logout. 2016-01-09 07:38:18 +01:00
James Cole
9b24e6d448 Remove explanation text. 2016-01-09 07:32:23 +01:00
James Cole
e622774775 Move to better spot 2016-01-08 20:48:34 +01:00
James Cole
84ce9bc94b Move locale code. 2016-01-08 20:47:35 +01:00
James Cole
a3a1bc30b1 Convert to number. 2016-01-08 20:43:46 +01:00
James Cole
b4c9a7698e Also call parent constructor. 2016-01-08 20:40:48 +01:00
James Cole
8b2d7fc32f Need to call parent constructor. 2016-01-08 19:13:51 +01:00
James Cole
90e66cbd94 Remove session guard. 2016-01-08 18:51:10 +01:00
James Cole
fd9a7080ea Move authentication around. 2016-01-08 18:29:47 +01:00
James Cole
c0fad106f0 Temp fix for redirect loop. 2016-01-08 16:38:20 +01:00
James Cole
78c8243184 Not the problem. 2016-01-08 16:36:41 +01:00
James Cole
780abecd53 Worked the last time.. 2016-01-08 16:35:44 +01:00
James Cole
ea6896816d Fix domain check. 2016-01-08 16:34:52 +01:00
James Cole
95a456860a Fix another URL. 2016-01-08 16:33:27 +01:00
James Cole
b1b2fda155 No 5.5 test. 2016-01-08 16:32:51 +01:00
James Cole
5847f534c3 Fix register route. 2016-01-08 16:32:31 +01:00
James Cole
e8e8163fa7 Allow registering by default. 2016-01-08 16:31:27 +01:00
James Cole
6a21d82dcf Debug class does not belong in production. 2016-01-08 16:29:14 +01:00
James Cole
d6b47656bc Updated various files after upgrade to laravel 5.2 2016-01-08 16:09:51 +01:00
James Cole
8c37ef3a95 Updated various files after upgrade to laravel 5.2 2016-01-08 16:02:15 +01:00
James Cole
35deed1d10 Update http related classes after upgrade to Laravel 5.2 2016-01-08 16:01:21 +01:00
James Cole
ba32a665f1 Update middleware after upgrade to Laravel 5.2 2016-01-08 16:00:57 +01:00
James Cole
bbd19be554 Update requests after upgrade to Laravel 5.2 2016-01-08 16:00:28 +01:00
James Cole
c360cc6db6 Update jobs after upgrade to Laravel 5.2 2016-01-08 16:00:16 +01:00
James Cole
7e4b9af315 Updated providers after upgrade to Laravel 5.2 2016-01-08 16:00:07 +01:00
James Cole
9b03e6b124 Updated config after upgrade to Laravel 5.2 2016-01-08 15:59:30 +01:00
James Cole
013e16e15f Updated config after upgrade to Laravel 5.2 2016-01-08 15:59:21 +01:00
James Cole
180ec52798 Various new files after upgrade to Laravel 5.2 2016-01-08 15:56:35 +01:00
James Cole
c7b47b4453 Fix view after upgrade to Laravel 5.2 2016-01-08 15:55:22 +01:00
James Cole
4b00db7662 Removed test code after upgrading to laravel 5.2 (will have to reinstate). 2016-01-08 14:30:19 +01:00
James Cole
78a7b995d2 Updated artisan after upgrading to laravel 5.2 2016-01-08 14:29:12 +01:00
James Cole
f7c50a123a Updated config file after upgrading to laravel 5.2 2016-01-08 14:28:59 +01:00
James Cole
9617d17aca Updated app file after upgrading to laravel 5.2 2016-01-08 14:28:15 +01:00
James Cole
d9884ddf73 Removed test code after upgrading to laravel 5.2 2016-01-08 14:28:04 +01:00
James Cole
8157f0a958 Updated htaccess file after upgrading to laraval 5.2 2016-01-08 14:27:35 +01:00
James Cole
b7580a5f83 Updated example env files after upgrading to laraval 5.2 2016-01-08 14:27:27 +01:00
James Cole
66703b30b3 Updated git files after laravel 5.2 upgrade 2016-01-08 14:27:05 +01:00
James Cole
53677e3c64 Moved views. 2016-01-08 13:35:27 +01:00
James Cole
feef6a1756 Merge branch 'release/3.6.0' into develop 2016-01-07 10:22:18 +01:00
James Cole
5f299b895b Merge branch 'release/3.6.0' 2016-01-07 10:22:17 +01:00
James Cole
4e1bb5fbac Update read me, composer 2016-01-07 10:21:57 +01:00
James Cole
47ccc513ad New version. 2016-01-07 10:11:37 +01:00
James Cole
cce1a01936 Update composer.lock 2016-01-07 10:10:52 +01:00
James Cole
6f2b1a6a76 The Sendgrid cron does not belong in Firefly. 2016-01-06 15:20:21 +01:00
James Cole
8526907f50 Expand multi-year report. 2016-01-05 21:23:58 +01:00
James Cole
bc192a8e54 Merge pull request #138 from leander091/develop
String is a reserved class name in php 7.
2016-01-03 20:25:34 +01:00
leander091
9ff6f8fc52 String is a reserved class name in php7 changed to Str according to the upstream project 2016-01-03 15:52:12 +01:00
James Cole
6573bd6b4b Code cleanup according to PHPStorm. 2016-01-02 19:33:44 +01:00
James Cole
9dc3f614af Localised date 2016-01-02 16:59:36 +01:00
James Cole
3888b8cceb Code cleanup according to PHPStorm. 2016-01-02 16:57:31 +01:00
James Cole
294df4a2b3 Simplified some code. 2016-01-02 16:32:08 +01:00
James Cole
265dd37212 Cleanup and add various warnings. 2016-01-02 16:31:14 +01:00
James Cole
eb7c79ad27 Can be written to. 2016-01-02 09:47:01 +01:00
James Cole
de111c7100 Fix broken route. 2016-01-01 21:59:19 +01:00
James Cole
e892c9a824 Followed up on some inspections. 2016-01-01 21:49:27 +01:00
James Cole
5eb0e18cae Cleaning up 2016-01-01 21:15:03 +01:00
James Cole
27cabb398e More queries filtered. 2016-01-01 21:07:15 +01:00
James Cole
64dbb14241 Method no longer used. 2016-01-01 20:05:14 +01:00
James Cole
bb4e2be9eb Method no longer used. 2016-01-01 20:04:44 +01:00
James Cole
7d1de0da17 Method no longer used. 2016-01-01 20:02:01 +01:00
James Cole
bf16c9a42b Method no longer used. 2016-01-01 20:01:07 +01:00
James Cole
1a7b1ce499 Method no longer used. 2016-01-01 20:00:20 +01:00
James Cole
efc9bc71a7 Method no longer used. 2016-01-01 19:58:31 +01:00
James Cole
fc5b315af0 Method no longer used. 2016-01-01 19:58:05 +01:00
James Cole
7a4a78628d Method no longer used. 2016-01-01 19:57:23 +01:00
James Cole
d16fb30a62 Method no longer used. 2016-01-01 19:56:23 +01:00
James Cole
2d177e660e Method no longer used. 2016-01-01 19:55:47 +01:00
James Cole
2f131dc170 Method no longer used. 2016-01-01 19:55:00 +01:00
James Cole
94810e371a Enable bill report. 2016-01-01 19:52:55 +01:00
James Cole
59731878f6 Month report optimised. 2016-01-01 19:46:12 +01:00
James Cole
54ede8aa18 Code cleanup. 2016-01-01 13:54:23 +01:00
James Cole
b415b6b043 Some code cleanup. 2016-01-01 12:41:00 +01:00
James Cole
70c922cdc5 Code cleanup. 2016-01-01 11:32:08 +01:00
James Cole
068fc32cb2 Some query cleaning up. 2015-12-31 20:12:49 +01:00
James Cole
3dcdacc3b8 Cleared lots of queries. In some cases, from 1400 back to 300. And those 300 have a different cause which is next. 2015-12-31 17:46:34 +01:00
James Cole
a6594358d8 Use a lot less queries 2015-12-31 17:20:54 +01:00
James Cole
f98921da46 Optimised summary chart. 2015-12-31 08:36:01 +01:00
James Cole
25747fbcf2 And optimised another chart. Amounts are slightly different. Will investigate 2015-12-31 08:31:28 +01:00
James Cole
aac5c2b13c Optimised another chart. 2015-12-31 08:26:04 +01:00
James Cole
cc810a5b6f Renamed a chart to be more consistent with the others. 2015-12-31 07:54:11 +01:00
James Cole
1b3592d959 Optimise chart. 2015-12-31 07:49:19 +01:00
James Cole
d75614e9a7 Optimised two charts, cleaned up some code. 2015-12-30 16:42:09 +01:00
James Cole
08703e282f Fix array. 2015-12-30 09:30:06 +01:00
James Cole
2904baf44e From 1100+ queries to a steady 6. 2015-12-30 09:17:29 +01:00
James Cole
f99e46bf75 Removed for-loop in favour of "pluck()" aka: RTFM. 2015-12-30 09:17:14 +01:00
James Cole
9f87890ead Removed for-loop in favour of "pluck()" aka: RTFM. 2015-12-30 09:17:05 +01:00
James Cole
638184cf66 Removed for-loop in favour of "pluck()" aka: RTFM. 2015-12-30 09:16:58 +01:00
James Cole
03babfe75c Removed for-loop in favour of "pluck()" aka: RTFM. 2015-12-30 09:16:53 +01:00
James Cole
238ed3c788 Rename a method. 2015-12-30 08:25:38 +01:00
James Cole
6a9d931ba3 More code cleanup. 2015-12-30 08:21:11 +01:00
James Cole
a3d2a9e00b Some cleaning up, and I hope simplification. 2015-12-30 08:15:04 +01:00
James Cole
39b88e8207 Added an alias to make methods more readable. 2015-12-30 08:00:52 +01:00
James Cole
449c6dfde5 Bind new class. 2015-12-29 22:51:31 +01:00
James Cole
7cc47ca0b1 Some cleaning up. 2015-12-29 22:48:55 +01:00
James Cole
95f4a83f41 Split category repository into two repositories. One for database calls for single categories, the other pertaining all categories. 2015-12-29 22:44:13 +01:00
James Cole
35154dc7a3 Another chart optimised 2015-12-29 18:55:30 +01:00
James Cole
0fd0d7d080 Less queries for category frontpage chart. 2015-12-29 10:08:40 +01:00
James Cole
658265c938 Optimised whole budget chart to use less queries. 2015-12-29 08:45:43 +01:00
James Cole
38fe9e7e1c Optimised chart. 2015-12-29 08:27:13 +01:00
James Cole
77056dcf8d Cleanup. 2015-12-29 08:27:05 +01:00
James Cole
026683a8e1 Made reportType camelCase. 2015-12-28 20:04:54 +01:00
James Cole
6ab6dd6ac3 First attempt at optimised query for multi-year budget chart. 2015-12-28 19:56:28 +01:00
James Cole
83de3482ce Optimised budget year chart. 2015-12-28 17:57:03 +01:00
James Cole
919a35aed3 Merge branch 'release/3.5.6.1' 2015-12-28 16:53:45 +01:00
James Cole
ad3defb071 Merge branch 'release/3.5.6.1' into develop 2015-12-28 16:53:45 +01:00
James Cole
9c929ecd1b New release. 2015-12-28 16:53:30 +01:00
James Cole
f79c9f7cf1 Cleanup. 2015-12-28 16:52:28 +01:00
James Cole
8e75c345d9 Found a bug that requires a new release of FF3 to fix. 2015-12-28 16:52:21 +01:00
James Cole
44886d9aad Merge branch 'release/3.5.6' into develop 2015-12-28 08:07:44 +01:00
James Cole
c2d444347d Merge branch 'release/3.5.6' 2015-12-28 08:07:43 +01:00
James Cole
5cb497596d New release. 2015-12-28 08:07:26 +01:00
James Cole
1857469d2f Removed unused parameters 2015-12-28 08:00:42 +01:00
James Cole
ea71b4843d Formatting fix in chart. 2015-12-28 07:58:40 +01:00
James Cole
97727e2e3d Some code cleanup courtesy of phpstorm. 2015-12-28 07:55:09 +01:00
James Cole
f81e7da8bb Code cleanup. 2015-12-28 07:49:27 +01:00
James Cole
8e827bf83b Removed parameter. 2015-12-28 07:43:57 +01:00
James Cole
9e1fa284ca Update php doc. 2015-12-28 07:43:05 +01:00
James Cole
3bf800be6e null check. 2015-12-28 07:41:44 +01:00
James Cole
635b9f9dba instance check. 2015-12-28 07:39:48 +01:00
James Cole
52a0d7cf7b Clean up code. 2015-12-28 07:38:02 +01:00
James Cole
a34516932b Rename include advised by scrutinizer. 2015-12-28 07:35:09 +01:00
James Cole
929a2a30a2 Fix TODO. 2015-12-28 07:31:48 +01:00
James Cole
ffa88eeb08 Made deleted piggy banks for piggy bank events visible. 2015-12-28 07:27:16 +01:00
James Cole
51b45b4ed4 Code cleanup. 2015-12-28 07:12:47 +01:00
James Cole
f263844793 Fix a bug where you cannot edit transactions. Will warrant a new release of FF. 2015-12-28 07:12:12 +01:00
James Cole
18c46df9aa Fix negative amounts and chart names. 2015-12-27 21:26:44 +01:00
James Cole
15846e157b From 200+ queries back to ~17. 2015-12-27 21:17:04 +01:00
James Cole
bc59f2db0d Optimised queries. 2015-12-27 20:07:49 +01:00
James Cole
cd2be8c1a4 Activate caching. 2015-12-27 19:51:20 +01:00
James Cole
f958115c50 Update composer file. 2015-12-27 17:57:32 +01:00
James Cole
e7d677bfb6 Add rounding, so the number will be a float. 2015-12-27 17:34:31 +01:00
James Cole
3e80ffc52b Huge change to bills and paid/unpaid/cc boxes. 2015-12-27 17:29:41 +01:00
James Cole
d0c7a5c076 Optimised query. 2015-12-27 09:44:12 +01:00
James Cole
f3f4e6b354 Stops date from skipping ahead slowly. 2015-12-27 09:40:28 +01:00
James Cole
5a45b25614 Merge branch 'hotfix/chart-fix' 2015-12-27 09:35:41 +01:00
James Cole
0b5ee1edfc Merge branch 'hotfix/chart-fix' into develop 2015-12-27 09:35:41 +01:00
James Cole
da3dc599f9 Fix chart call. 2015-12-27 09:35:24 +01:00
James Cole
f013b435ab Merge branch 'release/3.5.5' 2015-12-27 08:58:36 +01:00
James Cole
5f6975a113 Merge branch 'release/3.5.5' into develop 2015-12-27 08:58:36 +01:00
James Cole
c5dee29e4b New version. 2015-12-27 08:58:25 +01:00
James Cole
633ee02f13 Remove old Google references 2015-12-27 08:57:51 +01:00
James Cole
6b750c909a Fix forgotten call in bill repository. 2015-12-27 08:39:41 +01:00
James Cole
5f8b6640a9 A lot less queries thanks to efficient query. 2015-12-27 08:39:29 +01:00
James Cole
dd42d8437c Removed code for unused chart. 2015-12-27 08:12:46 +01:00
James Cole
67a178591d Some query optimisations. 2015-12-27 07:59:00 +01:00
James Cole
f5e5659c1f Code cleanup. 2015-12-26 09:40:24 +01:00
James Cole
8b0f0fb615 Optimise queries. 2015-12-26 09:39:35 +01:00
James Cole
209116e766 Query optimisations. 2015-12-26 09:21:45 +01:00
James Cole
79392ab656 Add caching to various queries and lists. 2015-12-26 08:44:34 +01:00
James Cole
3ca1207231 #135 2015-12-26 08:24:41 +01:00
James Cole
cec1b147f2 #135 2015-12-26 08:23:52 +01:00
James Cole
46cfcfa3e7 Update admin template. 2015-12-26 08:22:48 +01:00
James Cole
b833e8dfa2 #135 2015-12-26 08:16:30 +01:00
James Cole
77b843efd8 #135 2015-12-26 08:15:22 +01:00
James Cole
db72ad7c60 Issue #135 2015-12-26 08:12:51 +01:00
James Cole
eadc630fcb #135 2015-12-26 08:12:44 +01:00
James Cole
170c1793cc #135 2015-12-26 08:06:34 +01:00
James Cole
9f7c6c2d0c Extra cache. 2015-12-25 17:11:55 +01:00
James Cole
72d054c55c Add support for virtual balance currency, even though it cannot be stored yet. 2015-12-25 17:10:04 +01:00
James Cole
524edfe7c2 Better formatting (will take currency into account). 2015-12-25 16:40:27 +01:00
James Cole
c25c5623d2 Fixed the currency dropdown when multiple fields present on single page. 2015-12-25 16:38:53 +01:00
James Cole
4f38b77ef6 Better caching. 2015-12-25 09:34:37 +01:00
James Cole
5862803434 This saves some queries. 2015-12-25 09:34:23 +01:00
James Cole
5b3beded39 I can't believe I left this here all this time. 2015-12-25 07:58:19 +01:00
James Cole
c61fb7a598 Marked some unused stuff as deprecated. 2015-12-25 07:52:56 +01:00
James Cole
33d9148029 Make sure charts are cached. 2015-12-25 07:43:34 +01:00
James Cole
63969f5a33 Same routine but for money spent on accounts. 2015-12-25 07:42:00 +01:00
James Cole
edde18aeef Remove old chart. 2015-12-25 07:32:56 +01:00
James Cole
657116d361 Display new chart. 2015-12-25 07:32:03 +01:00
James Cole
e16269daa8 Collect data for new chart. 2015-12-25 07:31:54 +01:00
James Cole
c07591ff5c New method, earnedForAccounts 2015-12-25 07:31:43 +01:00
James Cole
75a478ad54 New chart, earned in period. 2015-12-25 07:31:29 +01:00
James Cole
8dae8b1a7f More code. Forgot to push. 2015-12-24 16:59:38 +01:00
James Cole
15fd8cf486 Completed the renaming of some methods. 2015-12-24 10:27:45 +01:00
James Cole
55333156ac Better cache control for some charts. 2015-12-24 10:14:01 +01:00
James Cole
8cdcba3231 Original fix in place. #133 2015-12-24 09:50:28 +01:00
James Cole
8bab9e84e2 Should not have edited that code. #133 2015-12-24 09:50:16 +01:00
James Cole
2faae83912 Include empty budgets. #133 2015-12-24 09:47:44 +01:00
James Cole
5a61a11a61 Attempt to fix bug #133 2015-12-24 09:45:21 +01:00
James Cole
a6d71988f2 Replaced some language calls. 2015-12-24 08:35:08 +01:00
James Cole
7069e242ae Removed useless entry. 2015-12-24 08:20:59 +01:00
James Cole
56ee830558 Moved locale information from the language to the translation files. 2015-12-24 08:20:47 +01:00
James Cole
6dd12729e6 Small disclaimer in readme. 2015-12-24 08:20:19 +01:00
James Cole
14a48303cb Cleanup. 2015-12-23 11:32:50 +01:00
James Cole
72cf6c9c0f Removed old route. 2015-12-23 11:32:41 +01:00
James Cole
144ee6b8ca Updated read me. 2015-12-23 10:38:42 +01:00
James Cole
8967d86da6 Updated language files. 2015-12-23 09:34:23 +01:00
James Cole
18c6edbb5d Update language files. 2015-12-23 09:09:51 +01:00
James Cole
53de3c4717 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Fix a bug where the frontpage would not honor transaction order.
  Fix a bug where the report page would mess up the session dates.
2015-12-22 20:46:52 +01:00
James Cole
ad577e4e81 Expand language files. 2015-12-22 20:46:16 +01:00
James Cole
44811a3e7c Fix a bug where the frontpage would not honor transaction order. 2015-12-21 11:30:58 +01:00
James Cole
1ab3f05b3a Fix a bug where the report page would mess up the session dates. 2015-12-21 10:25:57 +01:00
James Cole
5e76488ae7 Better localisation in charts. 2015-12-20 08:40:58 +01:00
James Cole
32771fe7e1 Add Português do Brasil 2015-12-20 08:20:50 +01:00
James Cole
9b40cc6881 Fix locale setting for Carbon. 2015-12-20 08:19:26 +01:00
James Cole
2e35260bbb Add some translations. 2015-12-20 07:34:10 +01:00
James Cole
a067704277 Move stuff around. 2015-12-20 07:34:01 +01:00
James Cole
de281818ac Add language 2015-12-19 21:16:09 +01:00
James Cole
c49bfad38d Move languages. 2015-12-19 20:54:59 +01:00
James Cole
c1ba591b26 Rename languages. 2015-12-19 20:54:27 +01:00
James Cole
719af38a61 Cleanup. 2015-12-18 18:42:56 +01:00
James Cole
ac61dfae6b File reformatting. 2015-12-18 16:38:50 +01:00
James Cole
813fb679a7 File reformatting. 2015-12-18 16:37:45 +01:00
James Cole
e7562781f7 File reformatting. 2015-12-18 16:37:27 +01:00
James Cole
56d36b7f53 Remove Google references. 2015-12-18 16:37:02 +01:00
James Cole
53b3f7f821 Merge branch 'release/3.5.4' 2015-12-18 08:13:28 +01:00
James Cole
08a53156bd Merge branch 'release/3.5.4' into develop 2015-12-18 08:13:28 +01:00
James Cole
8985cd6309 New composer for 3.5.4. 2015-12-18 08:13:19 +01:00
James Cole
3833da7410 Add something about security. 2015-12-18 08:11:05 +01:00
James Cole
4210cd10db Cleanup. 2015-12-18 08:10:41 +01:00
James Cole
a7bd1c6892 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  First attempt cleaning up chart money formatting.
2015-12-18 07:32:03 +01:00
James Cole
52b0111afa Expanded text in register dialog. 2015-12-18 07:31:49 +01:00
James Cole
7921d128e4 Cleanup routine that checks for blocked domains. 2015-12-18 07:31:36 +01:00
James Cole
d7e838701a Blocked domains now in .env file. 2015-12-18 07:31:14 +01:00
James Cole
289bcb22aa First attempt cleaning up chart money formatting. 2015-12-17 15:03:47 +01:00
James Cole
3fe57b7983 Report remembers budgets and categories. 2015-12-16 16:19:15 +01:00
James Cole
32e92c2a16 Committed some dev stuff. 2015-12-16 13:10:49 +01:00
James Cole
1b3d208540 First attempt at functional category chart. 2015-12-16 13:08:26 +01:00
James Cole
6a8bf0aa62 Fixed the "undefined" error. 2015-12-16 12:13:01 +01:00
James Cole
56715556ed Second attempt. 2015-12-16 10:54:56 +01:00
James Cole
838330b909 First attempt at multi-year budget chart. 2015-12-16 10:17:15 +01:00
James Cole
69553b138b First calculations for multi-year budget chart. 2015-12-15 12:52:42 +01:00
James Cole
36d7a02994 Some refactoring. 2015-12-15 12:46:40 +01:00
James Cole
301528e2d2 Quick links. 2015-12-15 12:38:18 +01:00
James Cole
0303b45707 First code for multi year budget chart. 2015-12-15 12:37:55 +01:00
James Cole
ba722e8ed5 Expanded error message. 2015-12-15 08:19:16 +01:00
James Cole
289e5a5442 Add new blocked domain. 2015-12-15 08:19:07 +01:00
James Cole
fdad96e2bc Replaced route. 2015-12-14 21:14:34 +01:00
James Cole
af994e4dae Included first multi-year chart. 2015-12-14 21:12:10 +01:00
James Cole
006d68e279 Expand chart generation. 2015-12-14 21:11:57 +01:00
James Cole
29dc122ad3 New charts (slight variations of previous charts) 2015-12-14 21:11:26 +01:00
James Cole
cf4a8c6204 New translations. 2015-12-14 21:11:12 +01:00
James Cole
3c73fe92bf Lower threshold. 2015-12-14 20:58:23 +01:00
James Cole
6637590797 Empty placeholder for multi-year report. 2015-12-14 20:54:19 +01:00
James Cole
b8bab11acd Fix bread crumbs, clean up routes. 2015-12-14 20:45:12 +01:00
James Cole
a2f600feac Nice clean code. 2015-12-14 20:37:38 +01:00
James Cole
80dd62ef0a Refer to correct translations. 2015-12-14 20:34:08 +01:00
James Cole
827b1c9cd8 Fix some translations. 2015-12-14 20:33:57 +01:00
James Cole
2e4fcf803d Fix JS references. 2015-12-14 20:33:50 +01:00
James Cole
d00d95fc6f Cleanup JS 2015-12-14 20:33:10 +01:00
James Cole
3e3ab9bd25 Move some JS around. 2015-12-14 20:25:48 +01:00
James Cole
6eecc7722d Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Fix for left unbalanced field in report.
2015-12-14 20:22:47 +01:00
James Cole
ada4aaf69a This will generate buttloads of test data. 2015-12-14 20:21:24 +01:00
James Cole
93244c1f78 Fix for left unbalanced field in report. 2015-12-14 12:35:52 +01:00
James Cole
be056cea6b Update some queries. 2015-12-13 20:41:35 +01:00
James Cole
659ca8be14 Update some queries. 2015-12-13 20:40:41 +01:00
James Cole
ea9af8366d Update some queries. 2015-12-13 20:39:26 +01:00
James Cole
80edd47d36 First attempts at building a multi-year report. 2015-12-13 17:31:25 +01:00
James Cole
d7746b3649 Support multi-year, not implemented yet. 2015-12-13 10:18:25 +01:00
James Cole
c4c4fbc34c Refactor 2015-12-13 10:05:13 +01:00
James Cole
59f57c96e9 Refactor names. 2015-12-13 09:41:22 +01:00
James Cole
a2f852fecf Clean up code. 2015-12-13 09:35:58 +01:00
James Cole
ad114ed329 Remove unused methods. 2015-12-13 09:30:02 +01:00
James Cole
c4c3d0f07f Some refactoring. 2015-12-13 09:01:17 +01:00
James Cole
6cf8102de5 Update version for coming release. 2015-12-12 22:32:19 +01:00
James Cole
e7e4aa2218 Update composer.lock. 2015-12-12 22:32:04 +01:00
James Cole
6d84f4b6c1 Update screenshots. 2015-12-12 22:31:34 +01:00
James Cole
ce3e9ffd11 Better cache control. 2015-12-12 21:20:20 +01:00
James Cole
3ada260e0e This will keep history. 2015-12-12 20:59:29 +01:00
James Cole
8fdd0cb795 Fixed some yearly charts. 2015-12-12 20:56:07 +01:00
James Cole
913e05a2e6 Really, reversed. 2015-12-12 20:20:18 +01:00
James Cole
fa1f703ef6 Some negative sums were failing regarding transfers. 2015-12-12 20:19:40 +01:00
James Cole
4004c53e1b Fix negative amount thing. 2015-12-12 20:16:30 +01:00
James Cole
4838670649 Transfer fix. 2015-12-12 20:13:07 +01:00
James Cole
a985e09282 Fix query. 2015-12-12 20:10:52 +01:00
James Cole
9bd1503cb4 Expand query to catch all expenses. 2015-12-12 20:07:33 +01:00
James Cole
a2ccbf7844 Jump to year report if the period is too long. 2015-12-12 19:04:30 +01:00
James Cole
61bbe8a905 Don't need this file. 2015-12-12 17:51:40 +01:00
James Cole
59bc5d22d1 Clean up some urls 2015-12-12 17:51:07 +01:00
James Cole
1423d5b314 Expand query, let's see what happens. 2015-12-12 12:36:36 +01:00
James Cole
152d0eb1d0 Cleanup and translations. 2015-12-12 12:29:54 +01:00
James Cole
6426d1df06 Fix the report chart. 2015-12-12 10:41:51 +01:00
James Cole
9284eb3fe9 First attempt at account specific bill report. 2015-12-12 10:33:19 +01:00
James Cole
afdae8bc1e Fix a sum for #129 2015-12-12 08:26:12 +01:00
James Cole
2a7085e593 Experimental budget lines. #129 2015-12-12 08:25:08 +01:00
James Cole
2408fb3ed4 Experimental budget lines. #129 2015-12-12 08:24:17 +01:00
James Cole
8316afb176 Experimental budget lines. #129 2015-12-12 08:21:46 +01:00
James Cole
e59fd098a3 Spent amount / withdrawals are negative, #129. 2015-12-12 08:15:14 +01:00
James Cole
e044199693 Spent amount / withdrawals are negative, #129. 2015-12-12 08:14:17 +01:00
James Cole
8f8e29fc22 Fix a sum. 2015-12-12 08:12:27 +01:00
James Cole
8de5384158 Spent amount / withdrawals are negative, #129. 2015-12-12 08:11:30 +01:00
James Cole
216c659335 Spent amount / withdrawals are negative, #129. 2015-12-12 08:07:25 +01:00
James Cole
041ca8a5d3 Amount reversal for #129 2015-12-12 08:05:10 +01:00
James Cole
fe4f1b306d Fix a sum for #129 2015-12-11 18:52:21 +01:00
James Cole
a0972d99fb Made a negative amount a positive as per #129 2015-12-11 18:49:07 +01:00
James Cole
e332bfef7c First attempt at budgets (split by account). 2015-12-11 18:45:39 +01:00
James Cole
cba5e226d8 Fix display of amount. 2015-12-11 18:36:19 +01:00
James Cole
5aff0c4943 Some fixes for #129 2015-12-11 18:35:49 +01:00
James Cole
cb49c00f4d Fix another JS error 2015-12-11 18:33:47 +01:00
James Cole
e26d797d57 Fix JS error. 2015-12-11 18:32:57 +01:00
James Cole
938581527e Fix sorting. 2015-12-11 18:31:15 +01:00
James Cole
c38ae09735 Negative expenses, as per #129 2015-12-11 18:30:28 +01:00
James Cole
28c3cfe084 Fix display of amount. See issue #129 2015-12-11 18:15:37 +01:00
James Cole
4a2823bcba Reverse sort. 2015-12-11 18:05:07 +01:00
James Cole
18eba02026 Expanded report for categories. 2015-12-11 18:03:13 +01:00
James Cole
d4690ce580 Fix date in budget report 2015-12-11 17:54:52 +01:00
James Cole
a785c450b1 First attempt at including a budget report. 2015-12-11 17:53:17 +01:00
James Cole
7480dc4a19 Fix a query. 2015-12-11 16:39:33 +01:00
James Cole
ad01891a67 First attempt at including expense report. 2015-12-11 16:36:40 +01:00
James Cole
67fe35d564 Small query fix. 2015-12-11 11:32:22 +01:00
James Cole
7f19b6957a Expanded new report a bit. Mainly copy/paste work. Will have to see how it pans out. 2015-12-11 09:39:17 +01:00
James Cole
0a54caf202 Tweak more translations. 2015-12-11 08:48:07 +01:00
James Cole
4b4c1c7f8f Moved some translations to see if they will still be picked up by Laravel. 2015-12-11 08:40:45 +01:00
James Cole
d071f3947e Merge pull request #127 from tonicospinelli/demeter-law
applying Demeter law for Transaction Type. Looking good!
2015-12-11 08:36:03 +01:00
Antonio Spinelli
b3d99cd210 apply demeter law for transaction type calls
- adds contants for transaction type names
- demeter law = never speaks with strangers
2015-12-10 16:53:48 -02:00
James Cole
90e696f82c Period is a month 2015-12-07 14:42:28 +01:00
James Cole
958fcd1cfa Report IP address 2015-12-07 14:41:04 +01:00
James Cole
8f57c7dcb3 Some fixes to amounts. 2015-12-06 13:17:00 +01:00
James Cole
77262f52a4 First functional view of default report. 2015-12-06 13:11:43 +01:00
James Cole
16bfbc8a12 Some JS to process the report form beforehand. 2015-12-06 08:42:04 +01:00
James Cole
1fd375b875 Better redirect after logout. 2015-12-05 17:45:33 +01:00
James Cole
46131ad39d Updated view for new reports. 2015-12-04 06:57:08 +01:00
James Cole
0b5c5b2ae9 Some mediocre Javascript for report thing. 2015-12-04 06:56:59 +01:00
James Cole
55be174037 Method to find an account. 2015-12-04 06:56:45 +01:00
James Cole
a17b7025f1 New function to build URL report. 2015-12-04 06:56:35 +01:00
James Cole
170cf7fd77 New routes for new reports. 2015-12-04 06:56:03 +01:00
James Cole
23cdb4d326 Expand month list for new reports. 2015-12-04 06:55:54 +01:00
James Cole
cbbe529572 Show bill if one is connected. 2015-12-04 06:16:19 +01:00
James Cole
0b382426e9 First experimental report generator / choice thing. 2015-12-03 14:52:10 +01:00
James Cole
1cbbf9baa4 Added a missing translation. 2015-12-03 11:46:05 +01:00
James Cole
8d41ff7b79 Prev should be next. Duh. 2015-12-03 11:41:06 +01:00
James Cole
e3b6057bf8 Catch Swift exceptions and do a log only (instead of crashing) because the email message isn't actually critical. 2015-12-03 11:30:43 +01:00
James Cole
66a4042cad Updated composer file. 2015-12-03 11:25:12 +01:00
James Cole
56c08d8302 Can block certain domains from registering, such as ten-minute-mail services. Two example domains provided in configuration. 2015-12-03 11:17:48 +01:00
James Cole
d4e759754d Make password reset impossible for blocked users. 2015-12-02 13:28:11 +01:00
James Cole
a96e171cbf Update composer. 2015-12-02 13:26:58 +01:00
James Cole
bd4a8c8397 Fixed "under" column 2015-12-02 09:03:34 +01:00
James Cole
04f71b3b43 Removed old code. 2015-12-02 09:01:40 +01:00
James Cole
d124de51db Shouldn't be like this? 2015-12-02 09:00:28 +01:00
James Cole
d87d12a0f5 Better query. See if works. 2015-12-02 08:58:40 +01:00
James Cole
f2b08346d0 Log. 2015-12-02 08:50:28 +01:00
James Cole
d3682a6727 Another fix in reports. 2015-12-02 08:46:03 +01:00
James Cole
371bbd9508 Some cosmetic fixes to reports 2015-12-02 08:44:23 +01:00
James Cole
a8a28f442f Also show "zero" amounts. 2015-12-02 08:38:57 +01:00
James Cole
65ddd8a736 One 'equals' sign too many! 2015-12-02 08:37:35 +01:00
James Cole
8bb27de233 More report subtleties. 2015-12-02 08:37:08 +01:00
James Cole
37e2f097ba To make budget report more clear, add spent amount to "spent" column. 2015-12-02 08:35:15 +01:00
James Cole
1966d87ce6 Small formatting fixes in reports. 2015-12-02 08:33:22 +01:00
James Cole
7b8c86e1e3 Only get active piggy banks. 2015-12-02 08:25:38 +01:00
unknown
de634da513 Only get active savings accounts. 2015-12-02 08:22:25 +01:00
James Cole
96836e2d6c Update read me. 2015-11-28 16:04:30 +01:00
James Cole
8a9d576f61 Allow change to default currency. issue #121 2015-11-22 11:30:06 +01:00
James Cole
791d12fbb4 Fix for issue #123 2015-11-22 11:25:15 +01:00
James Cole
d1329be2fa Bill scan routine should not grab transfers and income. 2015-11-20 20:13:10 +01:00
James Cole
3ed6561702 Updated all packages. 2015-11-13 07:16:23 +01:00
James Cole
7a0587f433 Merge branch 'release/3.5.3' 2015-11-13 07:01:29 +01:00
James Cole
0fe682bfe6 Merge branch 'release/3.5.3' into develop 2015-11-13 07:01:29 +01:00
James Cole
0f685e8789 New version. 2015-11-13 07:01:14 +01:00
James Cole
420771c233 New translations. See issue #117 2015-11-13 07:00:30 +01:00
James Cole
5e3e9271ca Added encryption to new password. See issue #118 2015-11-13 06:59:08 +01:00
James Cole
1e603c0833 Merge pull request #113 from RonaldvanMeer/develop
Removed duplicated controlbar item
2015-11-01 08:22:33 +01:00
James Cole
03e1673e92 Fix login message. 2015-11-01 08:06:51 +01:00
James Cole
9f992f003d Fix redirect loop. 2015-11-01 08:03:41 +01:00
James Cole
f50244a41f Merge branch 'release/3.5.2' into develop 2015-10-30 07:23:43 +01:00
RonaldvanMeer
baf9ebab15 Update control-bar.twig 2015-10-27 12:31:21 +01:00
RonaldvanMeer
fb2fa54480 Merge pull request #1 from RonaldvanMeer/master
Duplicate controlbar item
2015-10-26 22:45:38 +01:00
564 changed files with 28472 additions and 15309 deletions

View File

@@ -3,6 +3,7 @@ languages:
JavaScript: true
PHP: true
exclude_paths:
- "gulpfile.js"
- "public/packages/maximebf/php-debugbar/debugbar.js"
- "public/packages/maximebf/php-debugbar/widgets.js"
- "public/packages/maximebf/php-debugbar/openhandler.js"

27
.env.example Normal file → Executable file
View File

@@ -2,6 +2,7 @@ APP_ENV=production
APP_DEBUG=false
APP_KEY=SomeRandomStringOf32CharsExactly
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=homestead
@@ -10,15 +11,27 @@ DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
DEFAULT_CURRENCY=EUR
DEFAULT_LANGUAGE=en_US
REDIS_HOST=localhost
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_FROM=enter_your_email_here
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
SHOW_INCOMPLETE_TRANSLATIONS=false
EMAIL_SMTP=
EMAIL_DRIVER=smtp
EMAIL_USERNAME=
EMAIL_PASSWORD=
ANALYTICS_ID=
EMAIL_PRETEND=false
RUNCLEANUP=true
SITE_OWNER=mail@example.com
SENDGRID_USERNAME=
SENDGRID_PASSWORD=
BLOCKED_DOMAINS=

31
.env.testing Normal file → Executable file
View File

@@ -1,6 +1,7 @@
APP_ENV=testing
APP_DEBUG=true
APP_KEY=SomeRandomString
APP_KEY=SomeRandomStringOf32CharsExactly
DB_CONNECTION=sqlite
DB_HOST=localhost
@@ -10,9 +11,27 @@ DB_PASSWORD=secret
CACHE_DRIVER=array
SESSION_DRIVER=array
QUEUE_DRIVER=array
EMAIL_SMTP=
EMAIL_USERNAME=
EMAIL_PASSWORD=
ANALYTICS_ID=ABC
EMAIL_PRETEND=true
DEFAULT_CURRENCY=EUR
DEFAULT_LANGUAGE=en_US
REDIS_HOST=localhost
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=log
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_FROM=your_address_here@example.com
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
SHOW_INCOMPLETE_TRANSLATIONS=false
ANALYTICS_ID=abcde
RUNCLEANUP=false
SITE_OWNER=your_address_here@example.com
BLOCKED_DOMAINS=

2
.gitattributes vendored Normal file → Executable file
View File

@@ -1 +1,3 @@
* text=auto
*.css linguist-vendored
*.less linguist-vendored

45
.gitignore vendored
View File

@@ -1,36 +1,17 @@
/bootstrap/compiled.php
/vendor
composer.phar
Thumbs.db
.idea/
tests/_output/*
_ide_helper.php
/build/logs/clover.xml
index.html*
app/storage/firefly-export*
.vagrant
firefly-iii-import-*.json
tests/_output/*
testing.sqlite
_ide_helper_models.php
clean.sqlite
tests/acceptance/AcceptanceTester.php
tests/functional/FunctionalTester.php
tests/unit/UnitTester.php
pi.php
tests/_data/db.sqlite
tests/_data/dump.sql
db.sqlite_snapshot
c3.php
db.sqlite-journal
tests/_output/*
/node_modules
Homestead.yaml
Homestead.json
.env
clover.xml
node_modules/
addNewLines.php
_ide_helper.php
_ide_helper_models.php
.phpstorm.meta.php
.env.backup
.env.local
tests/_output/*
tests/_output/*
storage/
# Eclipse project files
.buildpath
.project
.settings/
.env.local

View File

@@ -3,13 +3,20 @@ sudo: false
php:
- 5.5
- 5.6
- 7
install:
- composer selfupdate
- composer install --no-dev
- composer update
- php artisan env
- mv -v .env.testing .env
- php artisan env
- touch storage/upload/at-1.data
- touch storage/upload/at-2.data
- touch storage/database/testing.db
- php artisan migrate --seed
script:
- phpunit

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright (C) 2016 Sander Dorigo
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

109
README.md
View File

@@ -2,115 +2,26 @@
[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![Total Downloads](https://poser.pugx.org/grumpydictator/firefly-iii/downloads)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master)
[![Build Status](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/build.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/build-status/master)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
[![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
## About
"Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared
household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money.
_Firefly is a system you'll have install yourself on webhosting of your choosing._
Personal financial management is pretty difficult, and everybody has their own approach to it. Some people
make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase
their current cashflow. There are tons of ways to save and earn money.
Personal financial management is pretty difficult, and everybody has their own approach to it. Some people make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase their current cashflow. There are tons of ways to save and earn money.
Firefly works on the principle that if you know where you're money is going, you can stop it from going there.
To get to know Firefly, and to see if it fits you, check out these resources:
#### Some advantages of using Firefly
- The screenshots below on this very page.
- The featurelist below, also on this very page.
- The [full description](https://github.com/JC5/firefly-iii/wiki/full-description), which will tell you how Firefly works,
and the philosophy behind it.
- Firefly can import any CSV file, so migrating from other systems is easy.
- Firefly runs on your own server, so you are fully in control of your data. Remember, there is no such thing as "the cloud", its just somebody elses computer!
- Firefly has lots of features without becoming fancy or bloated.
- If you feel you're missing something you can just ask me and I'll add it!
#### About the name (should you care)
It's III, or 3, because [version 2](https://github.com/JC5/Firefly) and version 1 (not online) preceded it. It has been growing steadily ever since.
## Current features
- [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system);
- You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management;
- You can manage different types of accounts;
- [Asset](https://en.wikipedia.org/wiki/Asset) accounts
- Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household))
- Saving accounts
- Credit cards
- It's possible to create, change and manage money using _[budgets](https://en.wikipedia.org/wiki/Envelope_system)_;
- Organize transactions using categories;
- Save towards a goal using [piggy banks](https://en.wikipedia.org/wiki/Piggy_bank);
- Predict and anticipate [bills](https://en.wikipedia.org/wiki/Invoice);
- View income / expense [reports](https://en.wikipedia.org/wiki/Financial_statement);
- Organize expenses using tags;
- Lots of help text in case you don't get it.
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 charts because we all love them;
- Financial reporting showing you how well you are doing.
## Screenshots
_Please note that everything in these screenshots is fictional and may not be realistic._
![Index](https://i.nder.be/c6hz06d3)
![Accounts](https://i.nder.be/gzxxyz6n)
![Budgets](https://i.nder.be/hhu3krqk)
![Reports 1](https://i.nder.be/cc3yspf6)
![Reports 2](https://i.nder.be/h6fp7xkb)
![Bills](https://i.nder.be/c30zkcpv)
![Piggy banks](https://i.nder.be/g20k0mdq)
## Running and installing
If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable)
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site. Accounts on the demo sites will stop working after one week.
## Credits
Firefly III uses the following libraries and tools:
* The AdminLTE template by [Almsaseed Studio](https://almsaeedstudio.com/)
* The [Google charts](https://developers.google.com/chart/) library.
* [Chart.js](http://www.chartjs.org/)
* [Bootstrap](http://getbootstrap.com/)
* [Laravel](http://laravel.com/)
* [Twig](http://twig.sensiolabs.org/)
* For development, some of the excellent tools made by [Barry van den Heuvel](https://github.com/barryvdh)
* [Bootstrap sortable](https://github.com/drvic10k/bootstrap-sortable) by [Matúš Brliť](https://github.com/drvic10k).
* [Date range picker](https://github.com/dangrossman/bootstrap-daterangepicker/) by [Dan Grossman](https://github.com/dangrossman)
* The [real favicon generator](http://realfavicongenerator.net/)
* Various other open source components (see [composer.json](https://github.com/JC5/firefly-iii/blob/master/composer.json))
## Current state
Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. Translations are a work in progress.
Questions, ideas, bugs or other things to contribute? [Let me know](https://github.com/JC5/firefly-iii/issues/new)!
If you like this tool, feel free to [donate me some beer money](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2ZMV952UUSCLU&lc=NL&item_name=Development%20of%20Firefly&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted).
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
[![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
![GA](https://ga-beacon.appspot.com/UA-58172398-6/firefly-iii/readme)
Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://jc5.github.io/firefly-iii/).

View File

@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<ruleset name="JamesStandard">
<rule ref="Zend">
<exclude name="Zend.NamingConventions.ValidVariableName" />
<exclude name="PEAR.WhiteSpace.ScopeClosingBrace" />
<!--<exclude name="PEAR.Whitespace.ScopeIndent"/>-->
<exclude name="PEAR.WhiteSpace.ScopeClosingBrace"/>
<exclude name="Generic.Formatting.MultipleStatementAlignment.Incorrect" />
<exclude name="PEAR.Functions.FunctionCallSignature" />
</rule>
<!--
Here we change two messages from the same sniff. Note how the
codes are slightly different because the sniff developer has
defined both a MaxExceeded message and a TooLong message. In the
case of this sniff, one is used for warnings and one is used
for errors.
-->
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="160"/>
<property name="absoluteLineLimit" value="160"/>
</properties>
</rule>
</ruleset>

322
_development/codestyle.xml Normal file
View File

@@ -0,0 +1,322 @@
<code_scheme name="Use This One">
<option name="RIGHT_MARGIN" value="160" />
<CoffeeScriptCodeStyleSettings>
<option name="SPACE_BEFORE_PROPERTY_COLON" value="true" />
</CoffeeScriptCodeStyleSettings>
<JSCodeStyleSettings>
<option name="ALIGN_MULTILINE_VAR_DECLARATION" value="true" />
</JSCodeStyleSettings>
<PHPCodeStyleSettings>
<option name="ALIGN_KEY_VALUE_PAIRS" value="true" />
<option name="ALIGN_PHPDOC_PARAM_NAMES" value="true" />
<option name="ALIGN_PHPDOC_COMMENTS" value="true" />
<option name="ALIGN_ASSIGNMENTS" value="true" />
<option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" />
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
<option name="PHPDOC_WRAP_LONG_LINES" value="true" />
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
<option name="LOWER_CASE_NULL_CONST" value="true" />
<option name="BLANK_LINE_BEFORE_RETURN_STATEMENT" value="true" />
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
<option name="ALIGN_CLASS_CONSTANTS" value="true" />
<option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" />
</PHPCodeStyleSettings>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="CoffeeScript">
<option name="KEEP_LINE_BREAKS" value="false" />
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
</codeStyleSettings>
<codeStyleSettings language="PHP">
<option name="RIGHT_MARGIN" value="160" />
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
<option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="FOR_STATEMENT_LPAREN_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_RPAREN_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<arrangement>
<tokens>
<token id="modifiers" name="modifiers">
<rules>
<rule>
<match>
<AND>
<PUBLIC>true</PUBLIC>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
<rule>
<match>
<AND>
<PROTECTED>true</PROTECTED>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
<rule>
<match>
<AND>
<PRIVATE>true</PRIVATE>
<STATIC>true</STATIC>
</AND>
</match>
</rule>
<rule>
<match>
<PUBLIC>true</PUBLIC>
</match>
<order>BY_NAME</order>
</rule>
<rule>
<match>
<PROTECTED>true</PROTECTED>
</match>
<order>BY_NAME</order>
</rule>
<rule>
<match>
<PRIVATE>true</PRIVATE>
</match>
<order>BY_NAME</order>
</rule>
</rules>
</token>
<token id="visibility" name="visibility">
<rules>
<rule>
<match>
<PUBLIC>true</PUBLIC>
</match>
</rule>
<rule>
<match>
<PROTECTED>true</PROTECTED>
</match>
</rule>
<rule>
<match>
<PRIVATE>true</PRIVATE>
</match>
</rule>
</rules>
</token>
</tokens>
<groups>
<group>
<type>GETTERS_AND_SETTERS</type>
<order>KEEP</order>
</group>
</groups>
<rules>
<section>
<rule>
<match>
<CONST />
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PUBLIC />
<STATIC />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PROTECTED />
<STATIC />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PRIVATE />
<STATIC />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PUBLIC />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PROTECTED />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<FIELD />
<PRIVATE />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<CONSTRUCTOR />
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PUBLIC />
<STATIC />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PROTECTED />
<STATIC />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PRIVATE />
<STATIC />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PUBLIC />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PROTECTED />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<METHOD />
<PRIVATE />
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<TRAIT />
</match>
</rule>
</section>
<section>
<rule>
<match>
<INTERFACE />
</match>
</rule>
</section>
<section>
<rule>
<match>
<CLASS />
</match>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="pcsg-generated-ruleset"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/
</description>
<rule ref="rulesets/codesize.xml/CyclomaticComplexity">
<properties>
<property name="reportLevel" value="5"/>
</properties>
</rule>
<rule ref="rulesets/codesize.xml/NPathComplexity">
<properties>
<property name="minimum" value="128"/>
</properties>
</rule>
<rule ref="rulesets/codesize.xml/ExcessiveMethodLength">
<properties>
<property name="minimum" value="40"/>
</properties>
</rule>
<rule ref="rulesets/codesize.xml/ExcessiveParameterList">
<properties>
<property name="minimum" value="5"/>
</properties>
</rule>
<!-- Import rule set and exclude rules -->
<rule ref="rulesets/controversial.xml">
<exclude name="CamelCasePropertyName" />
</rule>
</ruleset>

1
_development/readme.txt Normal file
View File

@@ -0,0 +1 @@
These are some of the files I use for code formatting, PHPMD and PHPCS.

View File

@@ -1,12 +0,0 @@
<?php namespace FireflyIII\Commands;
/**
* Class Command
*
* @codeCoverageIgnore
* @package FireflyIII\Commands
*/
abstract class Command
{
}

View File

@@ -0,0 +1,67 @@
<?php
namespace FireflyIII\Console\Commands;
use Config;
use Illuminate\Console\Command;
/**
* Class UpgradeFireflyInstructions
*
* @package FireflyIII\Console\Commands
*/
class UpgradeFireflyInstructions extends Command
{
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly:upgrade-instructions';
/**
* Create a new command instance.
*
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
//
$version = Config::get('firefly.version');
$config = Config::get('upgrade.text');
$text = isset($config[$version]) ? $config[$version] : null;
$this->line('+------------------------------------------------------------------------------+');
$this->line('');
if (is_null($text)) {
$this->line('Thank you for installing Firefly III, v' . $version);
$this->line('If you are upgrading from a previous version,');
$this->info('there are no extra upgrade instructions.');
$this->line('Firefly III should be ready for use.');
} else {
$this->line('Thank you for installing Firefly III, v' . $version);
$this->line('If you are upgrading from a previous version,');
$this->line('please follow these upgrade instructions carefully:');
$this->info(wordwrap($text));
}
$this->line('');
$this->line('+------------------------------------------------------------------------------+');
}
}

View File

@@ -1,17 +1,25 @@
<?php namespace FireflyIII\Console;
<?php
/**
* Kernel.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Console;
use FireflyIII\Console\Commands\UpgradeFireflyInstructions;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
/**
* Class Kernel
*
* @codeCoverageIgnore
* @package FireflyIII\Console
*/
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
@@ -19,6 +27,7 @@ class Kernel extends ConsoleKernel
*/
protected $commands
= [
UpgradeFireflyInstructions::class,
];
/**
@@ -26,12 +35,11 @@ class Kernel extends ConsoleKernel
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameters)
*/
protected function schedule(Schedule $schedule)
{
}
}

View File

@@ -1,14 +1,13 @@
<?php namespace FireflyIII\Events;
<?php
namespace FireflyIII\Events;
/**
* Class Event
*
* @codeCoverageIgnore
* @package FireflyIII\Events
*/
abstract class Event
{
//
}

View File

@@ -1,25 +0,0 @@
<?php namespace FireflyIII\Events;
use Illuminate\Queue\SerializesModels;
/**
* Class JournalDeleted
*
* @codeCoverageIgnore
* @package FireflyIII\Events
*/
class JournalDeleted extends Event
{
use SerializesModels;
/**
* Create a new event instance.
*
*/
public function __construct()
{
//
}
}

View File

@@ -1,15 +1,24 @@
<?php namespace FireflyIII\Events;
<?php
/**
* TransactionJournalStored.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Events;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels;
/**
* Class JournalCreated
* Class TransactionJournalStored
*
* @codeCoverageIgnore
* @package FireflyIII\Events
*/
class JournalCreated extends Event
class TransactionJournalStored extends Event
{
use SerializesModels;

View File

@@ -4,12 +4,12 @@ use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels;
/**
* Class JournalSaved
* Class TransactionJournalUpdated
*
* @codeCoverageIgnore
* @package FireflyIII\Events
*/
class JournalSaved extends Event
class TransactionJournalUpdated extends Event
{
use SerializesModels;

View File

@@ -1,18 +1,20 @@
<?php namespace FireflyIII\Exceptions;
<?php
namespace FireflyIII\Exceptions;
use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpException;
/**
* Class Handler
*
* @codeCoverageIgnore
* @package FireflyIII\Exceptions
*/
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
@@ -20,40 +22,35 @@ class Handler extends ExceptionHandler
*/
protected $dontReport
= [
'Symfony\Component\HttpKernel\Exception\HttpException'
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
];
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @SuppressWarnings(PHPMD.ShortVariable)
* @param \Exception $exception
*
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
public function render($request, Exception $exception)
{
if ($e instanceof HttpException) {
return $this->renderHttpException($e);
} else {
return parent::render($request, $e);
}
return parent::render($request, $exception);
}
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
* @SuppressWarnings(PHPMD.ShortVariable)
*
* @param \Exception $e
* @param Exception $exception
*
* @return void
*/
public function report(Exception $e)
public function report(Exception $exception)
{
parent::report($e);
parent::report($exception);
}
}

View File

@@ -1,4 +1,11 @@
<?php
/**
* AccountChartGeneratorInterface.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Generator\Chart\Account;
@@ -7,11 +14,11 @@ use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
/**
* Interface AccountChartGenerator
* Interface AccountChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Account
*/
interface AccountChartGenerator
interface AccountChartGeneratorInterface
{
/**
@@ -21,7 +28,7 @@ interface AccountChartGenerator
*
* @return array
*/
public function all(Collection $accounts, Carbon $start, Carbon $end);
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end);
/**
* @param Collection $accounts
@@ -40,13 +47,4 @@ interface AccountChartGenerator
* @return array
*/
public function single(Account $account, Carbon $start, Carbon $end);
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end);
}

View File

@@ -3,10 +3,8 @@
namespace FireflyIII\Generator\Chart\Account;
use Carbon\Carbon;
use Config;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
use Preferences;
use Steam;
/**
@@ -14,24 +12,9 @@ use Steam;
*
* @package FireflyIII\Generator\Chart\Account
*/
class ChartJsAccountChartGenerator implements AccountChartGenerator
class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface
{
/**
* @codeCoverageIgnore
*
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function all(Collection $accounts, Carbon $start, Carbon $end)
{
return $this->frontpage($accounts, $start, $end);
}
/**
* @param Collection $accounts
* @param Carbon $start
@@ -42,10 +25,10 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end)
{
$data = [
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.spent'),
'data' => []]]];
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.spent'),
'data' => []]]];
bcscale(2);
$start->subDay();
@@ -79,22 +62,6 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
return $data;
}
/**
* @param $array
* @param $entryId
*
* @return string
*/
protected function isInArray($array, $entryId)
{
if (isset($array[$entryId])) {
return $array[$entryId];
}
return '0';
}
/**
* @param Collection $accounts
* @param Carbon $start
@@ -105,21 +72,16 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
public function frontpage(Collection $accounts, Carbon $start, Carbon $end)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.monthAndDay.' . $language);
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
$current = clone $start;
$format = (string)trans('config.month_and_day');
$data = ['count' => 0, 'labels' => [], 'datasets' => [],];
$current = clone $start;
while ($current <= $end) {
$data['labels'][] = $current->formatLocalized($format);
$current->addDay();
}
foreach ($accounts as $account) {
$set = [
$set = [
'label' => $account->name,
'fillColor' => 'rgba(220,220,220,0.2)',
'strokeColor' => 'rgba(220,220,220,1)',
@@ -129,9 +91,15 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
'pointHighlightStroke' => 'rgba(220,220,220,1)',
'data' => [],
];
$current = clone $start;
$current = clone $start;
$range = Steam::balanceInRange($account, $start, clone $end);
$previous = round(array_values($range)[0], 2);
while ($current <= $end) {
$set['data'][] = Steam::balance($account, $current);
$format = $current->format('Y-m-d');
$balance = isset($range[$format]) ? round($range[$format], 2) : $previous;
$set['data'][] = $balance;
$previous = $balance;
$current->addDay();
}
$data['datasets'][] = $set;
@@ -151,25 +119,29 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
public function single(Account $account, Carbon $start, Carbon $end)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.monthAndDay.' . $language);
$format = (string)trans('config.month_and_day');
$data = [
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => $account->name,
'data' => []
]
'data' => [],
],
],
];
$current = clone $start;
$range = Steam::balanceInRange($account, $start, $end);
$current = clone $start;
$previous = array_values($range)[0];
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
$balance = isset($range[$theDate]) ? $range[$theDate] : $previous;
$data['labels'][] = $current->formatLocalized($format);
$data['datasets'][0]['data'][] = Steam::balance($account, $current);
$data['datasets'][0]['data'][] = $balance;
$previous = $balance;
$current->addDay();
}
@@ -191,4 +163,19 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
return array_unique($ids);
}
/**
* @param $array
* @param $entryId
*
* @return string
*/
protected function isInArray($array, $entryId)
{
if (isset($array[$entryId])) {
return $array[$entryId];
}
return '0';
}
}

View File

@@ -1,4 +1,12 @@
<?php
/**
* BillChartGeneratorInterface.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Generator\Chart\Bill;
@@ -6,20 +14,20 @@ use FireflyIII\Models\Bill;
use Illuminate\Support\Collection;
/**
* Interface BillChartGenerator
* Interface BillChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Bill
*/
interface BillChartGenerator
interface BillChartGeneratorInterface
{
/**
* @param Collection $paid
* @param Collection $unpaid
* @param string $paid
* @param string $unpaid
*
* @return array
*/
public function frontpage(Collection $paid, Collection $unpaid);
public function frontpage($paid, $unpaid);
/**
* @param Bill $bill

View File

@@ -1,62 +1,48 @@
<?php
/**
* ChartJsBillChartGenerator.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Generator\Chart\Bill;
use Config;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use Preferences;
/**
* Class ChartJsBillChartGenerator
*
* @package FireflyIII\Generator\Chart\Bill
*/
class ChartJsBillChartGenerator implements BillChartGenerator
class ChartJsBillChartGenerator implements BillChartGeneratorInterface
{
/**
* @param Collection $paid
* @param Collection $unpaid
* @param string $paid
* @param string $unpaid
*
* @return array
*/
public function frontpage(Collection $paid, Collection $unpaid)
public function frontpage($paid, $unpaid)
{
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
bcscale(2);
/** @var TransactionJournal $entry */
foreach ($paid as $entry) { // loop paid and create single entry:
$paidDescriptions[] = $entry->description;
$paidAmount = bcadd($paidAmount, $entry->amount_positive);
}
/** @var Bill $entry */
foreach ($unpaid as $entry) { // loop unpaid:
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = bcdiv(bcadd($entry[0]->amount_max, $entry[0]->amount_min), 2);
$unpaidDescriptions[] = $description;
$unpaidAmount = bcadd($unpaidAmount, $amount);
unset($amount, $description);
}
$data = [
[
'value' => $unpaidAmount,
'value' => round($unpaid, 2),
'color' => 'rgba(53, 124, 165,0.7)',
'highlight' => 'rgba(53, 124, 165,0.9)',
'label' => trans('firefly.unpaid'),
],
[
'value' => $paidAmount,
'value' => round($paid * -1, 2), // paid is negative, must be positive.
'color' => 'rgba(0, 141, 76, 0.7)',
'highlight' => 'rgba(0, 141, 76, 0.9)',
'label' => trans('firefly.paid'),
]
],
];
return $data;
@@ -70,28 +56,24 @@ class ChartJsBillChartGenerator implements BillChartGenerator
*/
public function single(Bill $bill, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$data = [
$format = (string)trans('config.month');
$data = [
'count' => 3,
'labels' => [],
'datasets' => [],
];
// dataset: max amount
// dataset: min amount
// dataset: actual amount
$minAmount = [];
$maxAmount = [];
$actualAmount = [];
/** @var TransactionJournal $entry */
foreach ($entries as $entry) {
$data['labels'][] = $entry->date->formatLocalized($format);
$minAmount[] = round($bill->amount_min, 2);
$maxAmount[] = round($bill->amount_max, 2);
$actualAmount[] = round(($entry->amount * -1), 2);
/*
* journalAmount has been collected in BillRepository::getJournals
*/
$actualAmount[] = round(($entry->journalAmount * -1), 2);
}
$data['datasets'][] = [

View File

@@ -1,15 +1,22 @@
<?php
/**
* BudgetChartGeneratorInterface.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Generator\Chart\Budget;
use Illuminate\Support\Collection;
/**
* Interface BudgetChartGenerator
* Interface BudgetChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Budget
*/
interface BudgetChartGenerator
interface BudgetChartGeneratorInterface
{
/**
* @param Collection $entries
@@ -32,6 +39,13 @@ interface BudgetChartGenerator
*/
public function frontpage(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries);
/**
* @param Collection $budgets
* @param Collection $entries

View File

@@ -12,7 +12,7 @@ use Preferences;
*
* @package FireflyIII\Generator\Chart\Budget
*/
class ChartJsBudgetChartGenerator implements BudgetChartGenerator
class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface
{
/**
@@ -24,7 +24,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
public function budget(Collection $entries, $dateFormat = 'month')
{
// language:
$language = Preferences::get('language', 'en')->data;
$language = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'))->data;
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
$data = [
@@ -33,7 +33,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
[
'label' => 'Amount',
'data' => [],
]
],
],
];
@@ -68,24 +68,24 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
*/
public function frontpage(Collection $entries)
{
$data = [
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
// dataset: left
// dataset: spent
// dataset: overspent
$left = [];
$spent = [];
$overspent = [];
foreach ($entries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$data['labels'][] = $entry[0];
$left[] = round($entry[1], 2);
$spent[] = round($entry[2], 2);
$overspent[] = round($entry[3], 2);
$filtered = $entries->filter(
function ($entry) {
return ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0);
}
);
foreach ($filtered as $entry) {
$data['labels'][] = $entry[0];
$left[] = round($entry[1], 2);
$spent[] = round($entry[2] * -1, 2); // spent is coming in negative, must be positive
$overspent[] = round($entry[3] * -1, 2); // same
}
$data['datasets'][] = [
@@ -106,6 +106,40 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries)
{
// dataset:
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
// get labels from one of the budgets (assuming there's at least one):
$first = $entries->first();
$keys = array_keys($first['budgeted']);
foreach ($keys as $year) {
$data['labels'][] = strval($year);
}
// then, loop all entries and create datasets:
foreach ($entries as $entry) {
$name = $entry['name'];
$spent = $entry['spent'];
$budgeted = $entry['budgeted'];
$data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)];
$data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)];
}
$data['count'] = count($data['datasets']);
return $data;
}
/**
* @param Collection $budgets
* @param Collection $entries
@@ -115,8 +149,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
public function year(Collection $budgets, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = (string)trans('config.month');
$data = [
'labels' => [],

View File

@@ -1,15 +1,22 @@
<?php
/**
* CategoryChartGeneratorInterface.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Generator\Chart\Category;
use Illuminate\Support\Collection;
/**
* Interface CategoryChartGenerator
* Interface CategoryChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Category
*/
interface CategoryChartGenerator
interface CategoryChartGeneratorInterface
{
/**
@@ -19,6 +26,14 @@ interface CategoryChartGenerator
*/
public function all(Collection $entries);
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInPeriod(Collection $categories, Collection $entries);
/**
* @param Collection $entries
*
@@ -26,6 +41,13 @@ interface CategoryChartGenerator
*/
public function frontpage(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries);
/**
* @param Collection $entries
*
@@ -33,20 +55,11 @@ interface CategoryChartGenerator
*/
public function period(Collection $entries);
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function spentInYear(Collection $categories, Collection $entries);
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInYear(Collection $categories, Collection $entries);
public function spentInPeriod(Collection $categories, Collection $entries);
}

View File

@@ -2,9 +2,7 @@
namespace FireflyIII\Generator\Chart\Category;
use Config;
use Illuminate\Support\Collection;
use Preferences;
/**
@@ -12,7 +10,7 @@ use Preferences;
*
* @package FireflyIII\Generator\Chart\Category
*/
class ChartJsCategoryChartGenerator implements CategoryChartGenerator
class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface
{
/**
@@ -30,12 +28,12 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
'datasets' => [
[
'label' => trans('firefly.spent'),
'data' => []
'data' => [],
],
[
'label' => trans('firefly.earned'),
'data' => []
]
'data' => [],
],
],
];
@@ -51,58 +49,17 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries)
{
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.spent'),
'data' => []
]
],
];
foreach ($entries as $entry) {
if ($entry['sum'] != 0) {
$data['labels'][] = $entry['name'];
$data['datasets'][0]['data'][] = round(($entry['sum'] * -1), 2);
}
}
return $data;
}
/**
* @codeCoverageIgnore
*
* @param Collection $entries
*
* @return array
*/
public function period(Collection $entries)
{
return $this->all($entries);
}
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function spentInYear(Collection $categories, Collection $entries)
public function earnedInPeriod(Collection $categories, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = (string)trans('config.month');
$data = [
'count' => 0,
@@ -125,18 +82,95 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
}
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries)
{
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.spent'),
'data' => [],
],
],
];
foreach ($entries as $entry) {
if ($entry->spent != 0) {
$data['labels'][] = $entry->name;
$data['datasets'][0]['data'][] = round(($entry->spent * -1), 2);
}
}
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries)
{
// dataset:
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
// get labels from one of the categories (assuming there's at least one):
$first = $entries->first();
$keys = array_keys($first['spent']);
foreach ($keys as $year) {
$data['labels'][] = strval($year);
}
// then, loop all entries and create datasets:
foreach ($entries as $entry) {
$name = $entry['name'];
$spent = $entry['spent'];
$earned = $entry['earned'];
if (array_sum(array_values($spent)) != 0) {
$data['datasets'][] = ['label' => 'Spent in category ' . $name, 'data' => array_values($spent)];
}
if (array_sum(array_values($earned)) != 0) {
$data['datasets'][] = ['label' => 'Earned in category ' . $name, 'data' => array_values($earned)];
}
}
$data['count'] = count($data['datasets']);
return $data;
}
/**
* @codeCoverageIgnore
*
* @param Collection $entries
*
* @return array
*/
public function period(Collection $entries)
{
return $this->all($entries);
}
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInYear(Collection $categories, Collection $entries)
public function spentInPeriod(Collection $categories, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = (string)trans('config.month');
$data = [
'count' => 0,

View File

@@ -3,9 +3,7 @@
namespace FireflyIII\Generator\Chart\PiggyBank;
use Carbon\Carbon;
use Config;
use Illuminate\Support\Collection;
use Preferences;
/**
@@ -13,7 +11,7 @@ use Preferences;
*
* @package FireflyIII\Generator\Chart\PiggyBank
*/
class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
class ChartJsPiggyBankChartGenerator implements PiggyBankChartGeneratorInterface
{
/**
@@ -25,8 +23,7 @@ class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.monthAndDay.' . $language);
$format = (string)trans('config.month_and_day');
$data = [
'count' => 1,
@@ -34,8 +31,8 @@ class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
'datasets' => [
[
'label' => 'Diff',
'data' => []
]
'data' => [],
],
],
];
$sum = '0';

View File

@@ -1,20 +0,0 @@
<?php
namespace FireflyIII\Generator\Chart\PiggyBank;
use Illuminate\Support\Collection;
/**
* Interface PiggyBankChartGenerator
*
* @package FireflyIII\Generator\Chart\PiggyBank
*/
interface PiggyBankChartGenerator
{
/**
* @param Collection $set
*
* @return array
*/
public function history(Collection $set);
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* PiggyBankChartGenerator.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Generator\Chart\PiggyBank;
use Illuminate\Support\Collection;
/**
* Interface PiggyBankChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\PiggyBank
*/
interface PiggyBankChartGeneratorInterface
{
/**
* @param Collection $set
*
* @return array
*/
public function history(Collection $set);
}

View File

@@ -2,18 +2,80 @@
namespace FireflyIII\Generator\Chart\Report;
use Config;
use Illuminate\Support\Collection;
use Preferences;
/**
* Class ChartJsReportChartGenerator
*
* @package FireflyIII\Generator\Chart\Report
*/
class ChartJsReportChartGenerator implements ReportChartGenerator
class ChartJsReportChartGenerator implements ReportChartGeneratorInterface
{
/**
* Same as above but other translations.
*
* @param Collection $entries
*
* @return array
*/
public function multiYearInOut(Collection $entries)
{
$data = [
'count' => 2,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.income'),
'data' => [],
],
[
'label' => trans('firefly.expenses'),
'data' => [],
],
],
];
foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized('%Y');
$data['datasets'][0]['data'][] = round($entry[1], 2);
$data['datasets'][1]['data'][] = round($entry[2], 2);
}
return $data;
}
/**
* @param string $income
* @param string $expense
* @param int $count
*
* @return array
*/
public function multiYearInOutSummarized($income, $expense, $count)
{
$data = [
'count' => 2,
'labels' => [trans('firefly.sum_of_years'), trans('firefly.average_of_years')],
'datasets' => [
[
'label' => trans('firefly.income'),
'data' => [],
],
[
'label' => trans('firefly.expenses'),
'data' => [],
],
],
];
$data['datasets'][0]['data'][] = round($income, 2);
$data['datasets'][1]['data'][] = round($expense, 2);
$data['datasets'][0]['data'][] = round(($income / $count), 2);
$data['datasets'][1]['data'][] = round(($expense / $count), 2);
return $data;
}
/**
* @param Collection $entries
*
@@ -22,8 +84,7 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
public function yearInOut(Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = (string)trans('config.month');
$data = [
'count' => 2,
@@ -31,12 +92,12 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
'datasets' => [
[
'label' => trans('firefly.income'),
'data' => []
'data' => [],
],
[
'label' => trans('firefly.expenses'),
'data' => []
]
'data' => [],
],
],
];
@@ -65,18 +126,18 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
'datasets' => [
[
'label' => trans('firefly.income'),
'data' => []
'data' => [],
],
[
'label' => trans('firefly.expenses'),
'data' => []
]
'data' => [],
],
],
];
$data['datasets'][0]['data'][] = round($income, 2);
$data['datasets'][1]['data'][] = round( $expense, 2);
$data['datasets'][1]['data'][] = round($expense, 2);
$data['datasets'][0]['data'][] = round(($income / $count), 2);
$data['datasets'][1]['data'][] = round(( $expense / $count), 2);
$data['datasets'][1]['data'][] = round(($expense / $count), 2);
return $data;
}

View File

@@ -1,31 +0,0 @@
<?php
namespace FireflyIII\Generator\Chart\Report;
use Illuminate\Support\Collection;
/**
* Interface ReportChartGenerator
*
* @package FireflyIII\Generator\Chart\Report
*/
interface ReportChartGenerator
{
/**
* @param Collection $entries
*
* @return array
*/
public function yearInOut(Collection $entries);
/**
* @param string $income
* @param string $expense
* @param int $count
*
* @return array
*/
public function yearInOutSummarized($income, $expense, $count);
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* ReportChartGenerator.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Generator\Chart\Report;
use Illuminate\Support\Collection;
/**
* Interface ReportChartGeneratorInterface
*
* @package FireflyIII\Generator\Chart\Report
*/
interface ReportChartGeneratorInterface
{
/**
* @param Collection $entries
*
* @return array
*/
public function multiYearInOut(Collection $entries);
/**
* @param string $income
* @param string $expense
* @param int $count
*
* @return array
*/
public function multiYearInOutSummarized($income, $expense, $count);
/**
* @param Collection $entries
*
* @return array
*/
public function yearInOut(Collection $entries);
/**
* @param string $income
* @param string $expense
* @param int $count
*
* @return array
*/
public function yearInOutSummarized($income, $expense, $count);
}

View File

@@ -1,7 +1,7 @@
<?php namespace FireflyIII\Handlers\Events;
use Auth;
use FireflyIII\Events\JournalCreated;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\TransactionJournal;
@@ -26,15 +26,13 @@ class ConnectJournalToPiggyBank
}
/**
* Handle the event when journal is saved.
* Connect a new transaction journal to any related piggy banks.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param JournalCreated $event
* @param TransactionJournalStored $event
*
* @return boolean
*/
public function handle(JournalCreated $event)
public function handle(TransactionJournalStored $event)
{
/** @var TransactionJournal $journal */
$journal = $event->journal;
@@ -44,12 +42,12 @@ class ConnectJournalToPiggyBank
$piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank)) {
return false;
return true;
}
// update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) {
return false;
return true;
}
bcscale(2);

View File

@@ -0,0 +1,77 @@
<?php
/**
* FireRulesForStore.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
use Illuminate\Support\Facades\Auth;
use Log;
/**
* Class FireRulesForStore
*
* @package FireflyIII\Handlers\Events
*/
class FireRulesForStore
{
/**
* Create the event handler.
*
* @codeCoverageIgnore
*
*/
public function __construct()
{
//
}
/**
* Connect a new transaction journal to any related piggy banks.
*
* @param TransactionJournalStored $event
*
* @return boolean
*/
public function handle(TransactionJournalStored $event)
{
// get all the user's rule groups, with the rules, order by 'order'.
/** @var User $user */
$user = Auth::user();
$groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
Log::debug('Now processing group "' . $group->title . '".');
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
Log::debug('Now handling rule #' . $rule->id . ' (' . $rule->title . ')');
$processor = new Processor($rule, $event->journal);
// get some return out of this?
$processor->handle();
if ($rule->stop_processing) {
break;
}
}
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* FireRulesForUpdate.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Handlers\Events;
use Auth;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
use Log;
/**
* Class FireRulesForUpdate
*
* @package FireflyIII\Handlers\Events
*/
class FireRulesForUpdate
{
/**
* Create the event handler.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param TransactionJournalUpdated $event
*
* @return void
*/
public function handle(TransactionJournalUpdated $event)
{
// get all the user's rule groups, with the rules, order by 'order'.
/** @var User $user */
$user = Auth::user();
$groups = $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get();
//
/** @var RuleGroup $group */
foreach ($groups as $group) {
Log::debug('Now processing group "' . $group->title . '".');
$rules = $group->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'update-journal')
->where('rules.active', 1)
->get(['rules.*']);
/** @var Rule $rule */
foreach ($rules as $rule) {
Log::debug('Now handling rule #' . $rule->id . ' (' . $rule->title . ')');
$processor = new Processor($rule, $event->journal);
// get some return out of this?
$processor->handle();
if ($rule->stop_processing) {
break;
}
}
}
}
}

View File

@@ -1,52 +0,0 @@
<?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalSaved;
use Log;
/**
* Class RescanJournal
*
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events
*/
class RescanJournal
{
/**
* Create the event handler.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param JournalSaved $event
*
* @return void
*/
public function handle(JournalSaved $event)
{
$journal = $event->journal;
Log::debug('Triggered saved event for journal #' . $journal->id . ' (' . $journal->description . ')');
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
$repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$list = $journal->user->bills()->where('active', 1)->where('automatch', 1)->get();
Log::debug('Found ' . $list->count() . ' bills to check.');
/** @var \FireflyIII\Models\Bill $bill */
foreach ($list as $bill) {
Log::debug('Now calling bill #' . $bill->id . ' (' . $bill->name . ')');
$repository->scan($bill, $journal);
}
Log::debug('Done!');
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* RescanJournalAfterStore.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Support\Events\BillScanner;
use Log;
/**
* Class RescanJournal
*
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events
*/
class ScanForBillsAfterStore
{
/**
* Create the event handler.
*
*/
public function __construct()
{
//
}
/**
* Scan a transaction journal for possible links to bills, right after storing.
*
* @param TransactionJournalStored $event
*
* @return void
*/
public function handle(TransactionJournalStored $event)
{
$journal = $event->journal;
BillScanner::scan($journal);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* ScanForBillsAfterUpdate.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Support\Events\BillScanner;
use Log;
/**
* Class RescanJournal
*
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events
*/
class ScanForBillsAfterUpdate
{
/**
* Create the event handler.
*
*/
public function __construct()
{
//
}
/**
* Scan a transaction journal for possibly related bills after it has been updated.
*
* @param TransactionJournalUpdated $event
*
* @return void
*/
public function handle(TransactionJournalUpdated $event)
{
$journal = $event->journal;
BillScanner::scan($journal);
}
}

View File

@@ -1,6 +1,6 @@
<?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalSaved;
use FireflyIII\Events\TransactionJournalUpdated;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
@@ -25,11 +25,11 @@ class UpdateJournalConnection
/**
* Handle the event.
*
* @param JournalSaved $event
* @param TransactionJournalUpdated $event
*
* @return void
*/
public function handle(JournalSaved $event)
public function handle(TransactionJournalUpdated $event)
{
$journal = $event->journal;

View File

@@ -19,14 +19,14 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
class AttachmentHelper implements AttachmentHelperInterface
{
/** @var int */
protected $maxUploadSize;
/** @var array */
protected $allowedMimes;
/** @var MessageBag */
public $errors;
/** @var MessageBag */
public $messages;
/** @var array */
protected $allowedMimes;
/** @var int */
protected $maxUploadSize;
/**
*
@@ -51,6 +51,22 @@ class AttachmentHelper implements AttachmentHelperInterface
return $path;
}
/**
* @return MessageBag
*/
public function getErrors()
{
return $this->errors;
}
/**
* @return MessageBag
*/
public function getMessages()
{
return $this->messages;
}
/**
* @param Model $model
*
@@ -67,7 +83,9 @@ class AttachmentHelper implements AttachmentHelperInterface
}
}
} else {
$this->processFile($files, $model);
if (!is_null($files)) {
$this->processFile($files, $model);
}
}
return true;
@@ -87,7 +105,7 @@ class AttachmentHelper implements AttachmentHelperInterface
$count = Auth::user()->attachments()->where('md5', $md5)->where('attachable_id', $model->id)->where('attachable_type', $class)->count();
if ($count > 0) {
$msg = trans('validation.file_already_attached', ['name' => $name]);
$msg = (string)trans('validation.file_already_attached', ['name' => $name]);
$this->errors->add('attachments', $msg);
return true;
@@ -96,27 +114,6 @@ class AttachmentHelper implements AttachmentHelperInterface
return false;
}
/**
* @param UploadedFile $file
* @param Model $model
*
* @return bool
*/
protected function validateUpload(UploadedFile $file, Model $model)
{
if (!$this->validMime($file)) {
return false;
}
if (!$this->validSize($file)) {
return false;
}
if ($this->hasFile($file, $model)) {
return false;
}
return true;
}
/**
* @param UploadedFile $file
* @param Model $model
@@ -154,7 +151,7 @@ class AttachmentHelper implements AttachmentHelperInterface
$attachment->save();
$name = e($file->getClientOriginalName()); // add message:
$msg = trans('validation.file_attached', ['name' => $name]);
$msg = (string)trans('validation.file_attached', ['name' => $name]);
$this->messages->add('attachments', $msg);
// return it.
@@ -174,7 +171,7 @@ class AttachmentHelper implements AttachmentHelperInterface
$name = e($file->getClientOriginalName());
if (!in_array($mime, $this->allowedMimes)) {
$msg = trans('validation.file_invalid_mime', ['name' => $name, 'mime' => $mime]);
$msg = (string)trans('validation.file_invalid_mime', ['name' => $name, 'mime' => $mime]);
$this->errors->add('attachments', $msg);
return false;
@@ -193,7 +190,7 @@ class AttachmentHelper implements AttachmentHelperInterface
$size = $file->getSize();
$name = e($file->getClientOriginalName());
if ($size > $this->maxUploadSize) {
$msg = trans('validation.file_too_large', ['name' => $name]);
$msg = (string)trans('validation.file_too_large', ['name' => $name]);
$this->errors->add('attachments', $msg);
return false;
@@ -203,19 +200,24 @@ class AttachmentHelper implements AttachmentHelperInterface
}
/**
* @return MessageBag
* @param UploadedFile $file
* @param Model $model
*
* @return bool
*/
public function getErrors()
protected function validateUpload(UploadedFile $file, Model $model)
{
return $this->errors;
}
if (!$this->validMime($file)) {
return false;
}
if (!$this->validSize($file)) {
return false;
}
if ($this->hasFile($file, $model)) {
return false;
}
/**
* @return MessageBag
*/
public function getMessages()
{
return $this->messages;
return true;
}

View File

@@ -15,11 +15,11 @@ interface AttachmentHelperInterface
{
/**
* @param Model $model
* @param Attachment $attachment
*
* @return bool
* @return mixed
*/
public function saveAttachmentsForModel(Model $model);
public function getAttachmentLocation(Attachment $attachment);
/**
* @return MessageBag
@@ -32,10 +32,10 @@ interface AttachmentHelperInterface
public function getMessages();
/**
* @param Attachment $attachment
* @param Model $model
*
* @return mixed
* @return bool
*/
public function getAttachmentLocation(Attachment $attachment);
public function saveAttachmentsForModel(Model $model);
}

View File

@@ -3,7 +3,6 @@
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Support\Collection;
/**
@@ -26,9 +25,6 @@ class BalanceLine
/** @var BudgetModel */
protected $budget;
/** @var LimitRepetition */
protected $repetition;
protected $role = self::ROLE_DEFAULTROLE;
/**
@@ -48,24 +44,19 @@ class BalanceLine
}
/**
* @return string
* @return Collection
*/
public function getTitle()
public function getBalanceEntries()
{
if ($this->getBudget() instanceof BudgetModel) {
return $this->getBudget()->name;
}
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
return trans('firefly.noBudget');
}
if ($this->getRole() == self::ROLE_TAGROLE) {
return trans('firefly.coveredWithTags');
}
if ($this->getRole() == self::ROLE_DIFFROLE) {
return trans('firefly.leftUnbalanced');
}
return $this->balanceEntries;
}
return '';
/**
* @param Collection $balanceEntries
*/
public function setBalanceEntries($balanceEntries)
{
$this->balanceEntries = $balanceEntries;
}
/**
@@ -100,6 +91,27 @@ class BalanceLine
$this->role = $role;
}
/**
* @return string
*/
public function getTitle()
{
if ($this->getBudget() instanceof BudgetModel) {
return $this->getBudget()->name;
}
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
return trans('firefly.noBudget');
}
if ($this->getRole() == self::ROLE_TAGROLE) {
return trans('firefly.coveredWithTags');
}
if ($this->getRole() == self::ROLE_DIFFROLE) {
return trans('firefly.leftUnbalanced');
}
return '';
}
/**
* If a BalanceLine has a budget/repetition, each BalanceEntry in this BalanceLine
* should have a "spent" value, which is the amount of money that has been spent
@@ -110,7 +122,7 @@ class BalanceLine
*/
public function leftOfRepetition()
{
$start = $this->getRepetition() ? $this->getRepetition()->amount : 0;
$start = isset($this->budget->amount) ? $this->budget->amount : 0;
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$start += $balanceEntry->getSpent();
@@ -118,56 +130,4 @@ class BalanceLine
return $start;
}
/**
* @return LimitRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param LimitRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
/**
* @return Collection
*/
public function getBalanceEntries()
{
return $this->balanceEntries;
}
/**
* @param Collection $balanceEntries
*/
public function setBalanceEntries($balanceEntries)
{
$this->balanceEntries = $balanceEntries;
}
/**
* If the BalanceEntries for a BalanceLine have a "left" value, the amount
* of money left in the entire BalanceLine is returned here:
*
* @return float
*/
public function sumOfLeft()
{
$sum = '0';
bcscale(2);
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$sum = bcadd($sum, $balanceEntry->getLeft());
}
return $sum;
}
}

View File

@@ -37,6 +37,7 @@ class Category
// spent is minus zero for an expense report:
if ($category->spent < 0) {
$this->categories->push($category);
$this->addTotal($category->spent);
}
}
@@ -55,7 +56,7 @@ class Category
*/
public function getCategories()
{
$set = $this->categories->sortByDesc(
$set = $this->categories->sortBy(
function (CategoryModel $category) {
return $category->spent;
}

View File

@@ -2,6 +2,7 @@
namespace FireflyIII\Helpers\Collection;
use Crypt;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use stdClass;
@@ -33,18 +34,24 @@ class Expense
*/
public function addOrCreateExpense(TransactionJournal $entry)
{
bcscale(2);
$accountId = $entry->account_id;
$amount = strval(round($entry->journalAmount, 2));
if (bccomp('0', $amount) === -1) {
$amount = bcmul($amount, '-1');
}
if (!$this->expenses->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = strval(round($entry->amount_positive, 2));
$newObject->name = $entry->name;
$newObject->amount = $amount;
$newObject->name = Crypt::decrypt($entry->account_name);
$newObject->count = 1;
$newObject->id = $accountId;
$this->expenses->put($accountId, $newObject);
} else {
bcscale(2);
$existing = $this->expenses->get($accountId);
$existing->amount = bcadd($existing->amount, $entry->amount_positive);
$existing->amount = bcadd($existing->amount, $amount);
$existing->count++;
$this->expenses->put($accountId, $existing);
}
@@ -55,8 +62,18 @@ class Expense
*/
public function addToTotal($add)
{
$add = strval(round($add, 2));
bcscale(2);
$add = strval(round($add, 2));
if (bccomp('0', $add) === -1) {
$add = bcmul($add, '-1');
}
// if amount is positive, the original transaction
// was a transfer. But since this is an expense report,
// that amount must be negative.
$this->total = bcadd($this->total, $add);
}
@@ -65,7 +82,7 @@ class Expense
*/
public function getExpenses()
{
$set = $this->expenses->sortByDesc(
$set = $this->expenses->sortBy(
function (stdClass $object) {
return $object->amount;
}

View File

@@ -2,6 +2,7 @@
namespace FireflyIII\Helpers\Collection;
use Crypt;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use stdClass;
@@ -38,15 +39,15 @@ class Income
$accountId = $entry->account_id;
if (!$this->incomes->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = strval(round($entry->amount_positive, 2));
$newObject->name = $entry->name;
$newObject->amount = strval(round($entry->journalAmount, 2));
$newObject->name = Crypt::decrypt($entry->account_name);
$newObject->count = 1;
$newObject->id = $accountId;
$this->incomes->put($accountId, $newObject);
} else {
bcscale(2);
$existing = $this->incomes->get($accountId);
$existing->amount = bcadd($existing->amount, $entry->amount_positive);
$existing->amount = bcadd($existing->amount, $entry->journalAmount);
$existing->count++;
$this->incomes->put($accountId, $existing);
}

View File

@@ -2,8 +2,6 @@
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Account;
/**
* Class Amount
*
@@ -13,7 +11,7 @@ class Amount extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
* @return string|int
*/
public function convert()
{

View File

@@ -0,0 +1,28 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class AmountComma
*
* Parses the input as the amount with a comma as decimal separator
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AmountComma extends BasicConverter implements ConverterInterface
{
/**
* @return float|int
*/
public function convert()
{
$value = str_replace(',', '.', $this->value);
if (is_numeric($value)) {
return floatval($value);
}
return 0;
}
}

View File

@@ -18,8 +18,6 @@ class BasicConverter
/** @var array */
protected $mapped;
/** @var string */
protected $role;
/** @var string */
protected $value;
/**
@@ -86,22 +84,6 @@ class BasicConverter
$this->mapped = $mapped;
}
/**
* @return string
*/
public function getRole()
{
return $this->role;
}
/**
* @param string $role
*/
public function setRole($role)
{
$this->role = $role;
}
/**
* @return string
*/

View File

@@ -5,7 +5,7 @@ use Auth;
use FireflyIII\Models\Budget;
/**
* Class AccountId
* Class BudgetId
*
* @package FireflyIII\Helpers\Csv\Converter
*/

View File

@@ -24,7 +24,7 @@ class CategoryName extends BasicConverter implements ConverterInterface
$category = Category::firstOrCreateEncrypted(
[
'name' => $this->value,
'user_id' => Auth::user()->id
'user_id' => Auth::user()->id,
]
);
}

View File

@@ -36,11 +36,6 @@ interface ConverterInterface
*/
public function setMapped($mapped);
/**
* @param string $role
*/
public function setRole($role);
/**
* @param string $value
*/

View File

@@ -13,7 +13,7 @@ class CurrencyCode extends BasicConverter implements ConverterInterface
{
/**
* @return mixed|static
* @return TransactionCurrency
*/
public function convert()
{

View File

@@ -13,7 +13,7 @@ class CurrencyId extends BasicConverter implements ConverterInterface
{
/**
* @return mixed|static
* @return TransactionCurrency
*/
public function convert()
{

View File

@@ -13,7 +13,7 @@ class CurrencyName extends BasicConverter implements ConverterInterface
{
/**
* @return mixed|static
* @return TransactionCurrency
*/
public function convert()
{

View File

@@ -13,7 +13,7 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface
{
/**
* @return mixed|static
* @return TransactionCurrency
*/
public function convert()
{

View File

@@ -18,6 +18,7 @@ class Date extends BasicConverter implements ConverterInterface
/**
* @return Carbon
* @throws FireflyException
*/
public function convert()
{

View File

@@ -12,7 +12,7 @@ class Description extends BasicConverter implements ConverterInterface
/**
* @return mixed
* @return string
*/
public function convert()
{

View File

@@ -2,8 +2,6 @@
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Account;
/**
* Class Amount
*
@@ -13,7 +11,7 @@ class Ignore extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
* @return null
*/
public function convert()
{

View File

@@ -6,7 +6,7 @@ use Auth;
use FireflyIII\Models\Account;
/**
* Class OpposingName
* Class OpposingAccountId
*
* @package FireflyIII\Helpers\Csv\Converter
*/

View File

@@ -6,7 +6,7 @@ use Auth;
use FireflyIII\Models\Account;
/**
* Class OpposingName
* Class OpposingAccountName
*
* @package FireflyIII\Helpers\Csv\Converter
*/

View File

@@ -13,7 +13,7 @@ class RabobankDebetCredit extends BasicConverter implements ConverterInterface
/**
* @return mixed
* @return int
*/
public function convert()
{

View File

@@ -3,7 +3,6 @@
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Tag;
use Illuminate\Support\Collection;
@@ -16,7 +15,7 @@ class TagsComma extends BasicConverter implements ConverterInterface
{
/**
* @return Bill
* @return Collection
*/
public function convert()
{

View File

@@ -3,7 +3,6 @@
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Tag;
use Illuminate\Support\Collection;
@@ -16,7 +15,7 @@ class TagsSpace extends BasicConverter implements ConverterInterface
{
/**
* @return Bill
* @return Collection
*/
public function convert()
{

View File

@@ -1,5 +1,4 @@
<?php
namespace FireflyIII\Helpers\Csv;
use Crypt;
@@ -16,14 +15,16 @@ class Data
/** @var string */
protected $csvFileContent;
/** @var string */
protected $csvFileLocation;
/** @var string */
protected $dateFormat;
/** @var string */
protected $delimiter;
/** @var bool */
protected $hasHeaders;
/** @var int */
protected $importAccount = 0;
/** @var array */
protected $map = [];
/** @var array */
@@ -32,15 +33,10 @@ class Data
protected $reader;
/** @var array */
protected $roles = [];
/** @var array */
protected $specifix = [];
/** @var int */
protected $importAccount = 0;
/**
*
*/
public function __construct()
{
@@ -52,6 +48,234 @@ class Data
$this->sessionMapped();
$this->sessionSpecifix();
$this->sessionImportAccount();
$this->sessionDelimiter();
}
/**
*
* @return string
*/
public function getCsvFileContent()
{
return $this->csvFileContent;
}
/**
*
* @param string $csvFileContent
*/
public function setCsvFileContent($csvFileContent)
{
$this->csvFileContent = $csvFileContent;
}
/**
*
* @return string
*/
public function getCsvFileLocation()
{
return $this->csvFileLocation;
}
/**
*
* @param string $csvFileLocation
*/
public function setCsvFileLocation($csvFileLocation)
{
Session::put('csv-file', $csvFileLocation);
$this->csvFileLocation = $csvFileLocation;
}
/**
*
* @return string
*/
public function getDateFormat()
{
return $this->dateFormat;
}
/**
*
* @param mixed $dateFormat
*/
public function setDateFormat($dateFormat)
{
Session::put('csv-date-format', $dateFormat);
$this->dateFormat = $dateFormat;
}
/**
*
* @return string
*/
public function getDelimiter()
{
return $this->delimiter;
}
/**
*
* @param string $delimiter
*/
public function setDelimiter($delimiter)
{
Session::put('csv-delimiter', $delimiter);
$this->delimiter = $delimiter;
}
/**
*
* @return array
*/
public function getMap()
{
return $this->map;
}
/**
*
* @param array $map
*/
public function setMap(array $map)
{
Session::put('csv-map', $map);
$this->map = $map;
}
/**
*
* @return array
*/
public function getMapped()
{
return $this->mapped;
}
/**
*
* @param array $mapped
*/
public function setMapped(array $mapped)
{
Session::put('csv-mapped', $mapped);
$this->mapped = $mapped;
}
/**
*
* @return Reader
*/
public function getReader()
{
if (strlen($this->csvFileContent) === 0) {
$this->loadCsvFile();
}
if (is_null($this->reader)) {
$this->reader = Reader::createFromString($this->getCsvFileContent());
$this->reader->setDelimiter($this->delimiter);
}
return $this->reader;
}
/**
*
* @return array
*/
public function getRoles()
{
return $this->roles;
}
/**
*
* @param array $roles
*/
public function setRoles(array $roles)
{
Session::put('csv-roles', $roles);
$this->roles = $roles;
}
/**
*
* @return array
*/
public function getSpecifix()
{
return is_array($this->specifix) ? $this->specifix : [];
}
/**
*
* @param array $specifix
*/
public function setSpecifix(array $specifix)
{
Session::put('csv-specifix', $specifix);
$this->specifix = $specifix;
}
/**
*
* @return bool
*/
public function hasHeaders()
{
return $this->hasHeaders;
}
/**
*
* @param bool $hasHeaders
*/
public function setHasHeaders($hasHeaders)
{
Session::put('csv-has-headers', $hasHeaders);
$this->hasHeaders = $hasHeaders;
}
/**
*
* @param int $importAccount
*/
public function setImportAccount($importAccount)
{
Session::put('csv-import-account', $importAccount);
$this->importAccount = $importAccount;
}
protected function loadCsvFile()
{
$file = $this->getCsvFileLocation();
$content = file_get_contents($file);
$contentDecrypted = Crypt::decrypt($content);
$this->setCsvFileContent($contentDecrypted);
}
protected function sessionCsvFileLocation()
{
if (Session::has('csv-file')) {
$this->csvFileLocation = (string)Session::get('csv-file');
}
}
protected function sessionDateFormat()
{
if (Session::has('csv-date-format')) {
$this->dateFormat = (string)Session::get('csv-date-format');
}
}
protected function sessionDelimiter()
{
if (Session::has('csv-delimiter')) {
$this->delimiter = Session::get('csv-delimiter');
}
}
protected function sessionHasHeaders()
@@ -68,20 +292,6 @@ class Data
}
}
protected function sessionDateFormat()
{
if (Session::has('csv-date-format')) {
$this->dateFormat = (string)Session::get('csv-date-format');
}
}
protected function sessionCsvFileLocation()
{
if (Session::has('csv-file')) {
$this->csvFileLocation = (string)Session::get('csv-file');
}
}
protected function sessionMap()
{
if (Session::has('csv-map')) {
@@ -89,13 +299,6 @@ class Data
}
}
protected function sessionRoles()
{
if (Session::has('csv-roles')) {
$this->roles = (array)Session::get('csv-roles');
}
}
protected function sessionMapped()
{
if (Session::has('csv-mapped')) {
@@ -103,181 +306,17 @@ class Data
}
}
protected function sessionRoles()
{
if (Session::has('csv-roles')) {
$this->roles = (array)Session::get('csv-roles');
}
}
protected function sessionSpecifix()
{
if (Session::has('csv-specifix')) {
$this->specifix = (array)Session::get('csv-specifix');
}
}
/**
* @return string
*/
public function getDateFormat()
{
return $this->dateFormat;
}
/**
* @param mixed $dateFormat
*/
public function setDateFormat($dateFormat)
{
Session::put('csv-date-format', $dateFormat);
$this->dateFormat = $dateFormat;
}
/**
* @param int $importAccount
*/
public function setImportAccount($importAccount)
{
Session::put('csv-import-account', $importAccount);
$this->importAccount = $importAccount;
}
/**
* @return bool
*/
public function hasHeaders()
{
return $this->hasHeaders;
}
/**
* @param bool $hasHeaders
*/
public function setHasHeaders($hasHeaders)
{
Session::put('csv-has-headers', $hasHeaders);
$this->hasHeaders = $hasHeaders;
}
/**
* @return array
*/
public function getMap()
{
return $this->map;
}
/**
* @param array $map
*/
public function setMap(array $map)
{
Session::put('csv-map', $map);
$this->map = $map;
}
/**
* @return array
*/
public function getMapped()
{
return $this->mapped;
}
/**
* @param array $mapped
*/
public function setMapped(array $mapped)
{
Session::put('csv-mapped', $mapped);
$this->mapped = $mapped;
}
/**
* @return Reader
*/
public function getReader()
{
if (strlen($this->csvFileContent) === 0) {
$this->loadCsvFile();
}
if (is_null($this->reader)) {
$this->reader = Reader::createFromString($this->getCsvFileContent());
}
return $this->reader;
}
protected function loadCsvFile()
{
$file = $this->getCsvFileLocation();
$content = file_get_contents($file);
$contentDecrypted = Crypt::decrypt($content);
$this->setCsvFileContent($contentDecrypted);
}
/**
* @return string
*/
public function getCsvFileLocation()
{
return $this->csvFileLocation;
}
/**
* @param string $csvFileLocation
*/
public function setCsvFileLocation($csvFileLocation)
{
Session::put('csv-file', $csvFileLocation);
$this->csvFileLocation = $csvFileLocation;
}
/**
* @return string
*/
public function getCsvFileContent()
{
return $this->csvFileContent;
}
/**
* @param string $csvFileContent
*/
public function setCsvFileContent($csvFileContent)
{
$this->csvFileContent = $csvFileContent;
}
/**
* @return array
*/
public function getRoles()
{
return $this->roles;
}
/**
* @param array $roles
*/
public function setRoles(array $roles)
{
Session::put('csv-roles', $roles);
$this->roles = $roles;
}
/**
* @return array
*/
public function getSpecifix()
{
return is_array($this->specifix) ? $this->specifix : [];
}
/**
* @param array $specifix
*/
public function setSpecifix($specifix)
{
Session::put('csv-specifix', $specifix);
$this->specifix = $specifix;
}
}

View File

@@ -9,9 +9,14 @@ use FireflyIII\Helpers\Csv\Converter\ConverterInterface;
use FireflyIII\Helpers\Csv\PostProcessing\PostProcessorInterface;
use FireflyIII\Helpers\Csv\Specifix\SpecifixInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Rules\Processor;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use Log;
@@ -34,6 +39,8 @@ class Importer
protected $importRow;
/** @var int */
protected $imported = 0;
/** @var Collection */
protected $journals;
/** @var array */
protected $map;
/** @var array */
@@ -45,9 +52,6 @@ class Importer
/** @var array */
protected $specifix = [];
/** @var Collection */
protected $journals;
/**
* Used by CsvController.
*
@@ -68,6 +72,14 @@ class Importer
return $this->imported;
}
/**
* @return Collection
*/
public function getJournals()
{
return $this->journals;
}
/**
* Used by CsvController
*
@@ -79,14 +91,13 @@ class Importer
}
/**
* @return Collection
* @return array
*/
public function getJournals()
public function getSpecifix()
{
return $this->journals;
return is_array($this->specifix) ? $this->specifix : [];
}
/**
* @throws FireflyException
*/
@@ -115,139 +126,19 @@ class Importer
Log::debug('---');
}
}
}
/**
* @param int $index
*
* @return bool
*/
protected function parseRow($index)
{
return (($this->data->hasHeaders() && $index >= 1) || !$this->data->hasHeaders());
}
/**
* @param $row
*
* @throws FireflyException
* @return string|bool
*/
protected function importRow($row)
{
$data = $this->getFiller(); // These fields are necessary to create a new transaction journal. Some are optional
foreach ($row as $index => $value) {
$role = isset($this->roles[$index]) ? $this->roles[$index] : '_ignore';
$class = Config::get('csv.roles.' . $role . '.converter');
$field = Config::get('csv.roles.' . $role . '.field');
Log::debug('Column #' . $index . ' (role: ' . $role . ') : converter ' . $class . ' stores its data into field ' . $field . ':');
/** @var ConverterInterface $converter */
$converter = app('FireflyIII\Helpers\Csv\Converter\\' . $class);
$converter->setData($data); // the complete array so far.
$converter->setField($field);
$converter->setIndex($index);
$converter->setMapped($this->mapped);
$converter->setValue($value);
$converter->setRole($role);
$data[$field] = $converter->convert();
}
// move to class vars.
$this->importData = $data;
$this->importRow = $row;
unset($data, $row);
// post processing and validating.
$this->postProcess();
$result = $this->validateData();
if (!($result === true)) {
return $result; // return error.
}
$journal = $this->createTransactionJournal();
return $journal;
}
/**
* @return array
*/
protected function getFiller()
{
$filler = [];
foreach (Config::get('csv.roles') as $role) {
if (isset($role['field'])) {
$fieldName = $role['field'];
$filler[$fieldName] = null;
}
}
// some extra's:
$filler['bill-id'] = null;
$filler['opposing-account-object'] = null;
$filler['asset-account-object'] = null;
$filler['amount-modifier'] = '1';
return $filler;
// once all journals have been imported (or not)
// fire the rules.
$this->fireRules();
}
/**
* Row denotes the original data.
*
* @return void
* @param Data $data
*/
protected function postProcess()
public function setData($data)
{
// do bank specific fixes (must be enabled but now all of them.
foreach ($this->getSpecifix() as $className) {
/** @var SpecifixInterface $specifix */
$specifix = app('FireflyIII\Helpers\Csv\Specifix\\' . $className);
$specifix->setData($this->importData);
$specifix->setRow($this->importRow);
Log::debug('Now post-process specifix named ' . $className . ':');
$this->importData = $specifix->fix();
}
$set = Config::get('csv.post_processors');
foreach ($set as $className) {
/** @var PostProcessorInterface $postProcessor */
$postProcessor = app('FireflyIII\Helpers\Csv\PostProcessing\\' . $className);
$postProcessor->setData($this->importData);
Log::debug('Now post-process processor named ' . $className . ':');
$this->importData = $postProcessor->process();
}
}
/**
* @return array
*/
public function getSpecifix()
{
return is_array($this->specifix) ? $this->specifix : [];
}
/**
*
* @return bool|string
*/
protected function validateData()
{
if (is_null($this->importData['date']) && is_null($this->importData['date-rent'])) {
return 'No date value for this row.';
}
if (is_null($this->importData['opposing-account-object'])) {
return 'Opposing account is null';
}
if (!($this->importData['asset-account-object'] instanceof Account)) {
return 'No asset account to import into.';
}
return true;
$this->data = $data;
}
/**
@@ -296,15 +187,15 @@ class Importer
// some debug info:
$journalId = $journal->id;
$type = $journal->transactionType->type;
$type = $journal->getTransactionType();
/** @var Account $asset */
$asset = $this->importData['asset-account-object'];
/** @var Account $opposing */
$opposing = $this->importData['opposing-account-object'];
Log::info('Created journal #' . $journalId . ' of type ' . $type . '!');
Log::info('Asset account ****** (#' . $asset->id . ') lost/gained: ' . $this->importData['amount']);
Log::info($opposing->accountType->type . ' ****** (#' . $opposing->id . ') lost/gained: ' . bcmul($this->importData['amount'], -1));
Log::info('Asset account #' . $asset->id . ' lost/gained: ' . $this->importData['amount']);
Log::info($opposing->accountType->type . ' #' . $opposing->id . ' lost/gained: ' . bcmul($this->importData['amount'], -1));
return $journal;
}
@@ -314,18 +205,104 @@ class Importer
*/
protected function getTransactionType()
{
$transactionType = TransactionType::where('type', 'Deposit')->first();
$transactionType = TransactionType::where('type', TransactionType::DEPOSIT)->first();
if ($this->importData['amount'] < 0) {
$transactionType = TransactionType::where('type', 'Withdrawal')->first();
$transactionType = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
}
if (in_array($this->importData['opposing-account-object']->accountType->type, ['Asset account', 'Default account'])) {
$transactionType = TransactionType::where('type', 'Transfer')->first();
$transactionType = TransactionType::where('type', TransactionType::TRANSFER)->first();
}
return $transactionType;
}
/**
* @param $row
*
* @throws FireflyException
* @return string|bool
*/
protected function importRow($row)
{
$data = $this->getFiller(); // These fields are necessary to create a new transaction journal. Some are optional
foreach ($row as $index => $value) {
$role = isset($this->roles[$index]) ? $this->roles[$index] : '_ignore';
$class = Config::get('csv.roles.' . $role . '.converter');
$field = Config::get('csv.roles.' . $role . '.field');
Log::debug('Column #' . $index . ' (role: ' . $role . ') : converter ' . $class . ' stores its data into field ' . $field . ':');
// here would be the place where preprocessors would fire.
/** @var ConverterInterface $converter */
$converter = app('FireflyIII\Helpers\Csv\Converter\\' . $class);
$converter->setData($data); // the complete array so far.
$converter->setField($field);
$converter->setIndex($index);
$converter->setMapped($this->mapped);
$converter->setValue($value);
$data[$field] = $converter->convert();
}
// move to class vars.
$this->importData = $data;
$this->importRow = $row;
unset($data, $row);
// post processing and validating.
$this->postProcess();
$result = $this->validateData();
if (!($result === true)) {
return $result; // return error.
}
$journal = $this->createTransactionJournal();
return $journal;
}
/**
* @param int $index
*
* @return bool
*/
protected function parseRow($index)
{
return (($this->data->hasHeaders() && $index >= 1) || !$this->data->hasHeaders());
}
/**
* Row denotes the original data.
*
* @return void
*/
protected function postProcess()
{
// do bank specific fixes (must be enabled but now all of them.
foreach ($this->getSpecifix() as $className) {
/** @var SpecifixInterface $specifix */
$specifix = app('FireflyIII\Helpers\Csv\Specifix\\' . $className);
if ($specifix->getProcessorType() == SpecifixInterface::POST_PROCESSOR) {
$specifix->setData($this->importData);
$specifix->setRow($this->importRow);
Log::debug('Now post-process specifix named ' . $className . ':');
$this->importData = $specifix->fix();
}
}
$set = Config::get('csv.post_processors');
foreach ($set as $className) {
/** @var PostProcessorInterface $postProcessor */
$postProcessor = app('FireflyIII\Helpers\Csv\PostProcessing\\' . $className);
$postProcessor->setData($this->importData);
Log::debug('Now post-process processor named ' . $className . ':');
$this->importData = $postProcessor->process();
}
}
/**
* @param TransactionJournal $journal
*/
@@ -361,11 +338,94 @@ class Importer
}
/**
* @param Data $data
*
* @return bool|string
*/
public function setData($data)
protected function validateData()
{
$this->data = $data;
if (is_null($this->importData['date']) && is_null($this->importData['date-rent'])) {
return 'No date value for this row.';
}
if (is_null($this->importData['opposing-account-object'])) {
return 'Opposing account is null';
}
if (!($this->importData['asset-account-object'] instanceof Account)) {
return 'No asset account to import into.';
}
return true;
}
/**
* @param Collection $groups
* @param TransactionJournal $journal
*/
private function fireRule(Collection $groups, TransactionJournal $journal)
{
/** @var RuleGroup $group */
foreach ($groups as $group) {
/** @var Rule $rule */
foreach ($group->rules as $rule) {
$processor = new Processor($rule, $journal);
$processor->handle();
if ($rule->stop_processing) {
break;
}
}
}
}
private function fireRules()
{
// get all users rules.
/** @var User $user */
$user = Auth::user();
$groups = $user
->ruleGroups()
->where('rule_groups.active', 1)
->orderBy('order', 'ASC')
->with(
[
'rules' => function (HasMany $q) {
$q->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->orderBy('rules.order', 'ASC');
},
]
)
->get();
/** @var TransactionJournal $journal */
foreach ($this->journals as $journal) {
$this->fireRule($groups, $journal);
}
}
/**
* @return array
*/
private function getFiller()
{
$filler = [];
foreach (Config::get('csv.roles') as $role) {
if (isset($role['field'])) {
$fieldName = $role['field'];
$filler[$fieldName] = null;
}
}
// some extra's:
$filler['bill-id'] = null;
$filler['opposing-account-object'] = null;
$filler['asset-account-object'] = null;
$filler['amount-modifier'] = '1';
return $filler;
}
}

View File

@@ -24,7 +24,7 @@ class Currency implements PostProcessorInterface
// fix currency
if (is_null($this->data['currency'])) {
$currencyPreference = Preferences::get('currencyPreference', 'EUR');
$currencyPreference = Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR'));
$this->data['currency'] = TransactionCurrency::whereCode($currencyPreference->data)->first();
}

View File

@@ -1,10 +1,4 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 05/07/15
* Time: 19:20
*/
namespace FireflyIII\Helpers\Csv\PostProcessing;

View File

@@ -0,0 +1,185 @@
<?php
namespace FireflyIII\Helpers\Csv\Specifix;
use Log;
/**
* Parses the description from txt files for ABN AMRO bank accounts.
*
* Based on the logic as described in the following Gist:
* https://gist.github.com/vDorst/68d555a6a90f62fec004
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class AbnAmroDescription extends Specifix implements SpecifixInterface
{
/** @var array */
protected $data;
/** @var array */
protected $row;
/**
* AbnAmroDescription constructor.
*/
public function __construct()
{
$this->setProcessorType(self::POST_PROCESSOR);
}
/**
* @return array
*/
public function fix()
{
// Try to parse the description in known formats.
$parsed = $this->parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription();
// If the description could not be parsed, specify an unknown opposing
// account, as an opposing account is required
if (!$parsed) {
$this->data['opposing-account-name'] = trans('firefly.unknown');
}
return $this->data;
}
/**
* @param array $data
*/
public function setData($data)
{
$this->data = $data;
}
/**
* @param array $row
*/
public function setRow($row)
{
$this->row = $row;
}
/**
* Parses the current description with costs from ABN AMRO itself
*
* @return boolean true if the description is GEA/BEA-format, false otherwise
*/
protected function parseABNAMRODescription()
{
// See if the current description is formatted in ABN AMRO format
if (preg_match('/ABN AMRO.{24} (.*)/', $this->data['description'], $matches)) {
Log::debug('AbnAmroSpecifix: Description is structured as costs from ABN AMRO itself.');
$this->data['opposing-account-name'] = 'ABN AMRO';
$this->data['description'] = $matches[1];
return true;
}
return false;
}
/**
* Parses the current description in GEA/BEA format
*
* @return boolean true if the description is GEA/BEAformat, false otherwise
*/
protected function parseGEABEADescription()
{
// See if the current description is formatted in GEA/BEA format
if (preg_match('/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/', $this->data['description'], $matches)) {
Log::debug('AbnAmroSpecifix: Description is structured as GEA or BEA format.');
// description and opposing account will be the same.
$this->data['opposing-account-name'] = $matches[4];
$this->data['description'] = $matches[4];
return true;
}
return false;
}
/**
* Parses the current description in SEPA format
*
* @return boolean true if the description is SEPA format, false otherwise
*/
protected function parseSepaDescription()
{
// See if the current description is formatted as a SEPA plain description
if (preg_match('/^SEPA(.{28})/', $this->data['description'], $matches)) {
Log::debug('AbnAmroSpecifix: Description is structured as SEPA plain description.');
// SEPA plain descriptions contain several key-value pairs, split by a colon
preg_match_all('/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9._#-]+(?=\s))/', $this->data['description'], $matches, PREG_SET_ORDER);
if (is_array($matches)) {
foreach ($matches as $match) {
$key = $match[1];
$value = trim($match[2]);
switch (strtoupper($key)) {
case 'OMSCHRIJVING':
$this->data['description'] = $value;
break;
case 'NAAM':
$this->data['opposing-account-name'] = $value;
break;
case 'IBAN':
$this->data['opposing-account-iban'] = $value;
break;
default:
// Ignore the rest
}
}
}
return true;
}
return false;
}
/**
* Parses the current description in TRTP format
*
* @return boolean true if the description is TRTP format, false otherwise
*/
protected function parseTRTPDescription()
{
// See if the current description is formatted in TRTP format
if (preg_match_all('!\/([A-Z]{3,4})\/([^/]*)!', $this->data['description'], $matches, PREG_SET_ORDER)) {
Log::debug('AbnAmroSpecifix: Description is structured as TRTP format.');
if (is_array($matches)) {
foreach ($matches as $match) {
$key = $match[1];
$value = trim($match[2]);
switch (strtoupper($key)) {
case 'NAME':
$this->data['opposing-account-name'] = $value;
break;
case 'REMI':
$this->data['description'] = $value;
break;
case 'IBAN':
$this->data['opposing-account-iban'] = $value;
break;
default:
// Ignore the rest
}
}
}
return true;
}
return false;
}
}

View File

@@ -7,7 +7,7 @@ namespace FireflyIII\Helpers\Csv\Specifix;
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class Dummy
class Dummy extends Specifix implements SpecifixInterface
{
/** @var array */
protected $data;
@@ -15,6 +15,13 @@ class Dummy
/** @var array */
protected $row;
/**
* Dummy constructor.
*/
public function __construct()
{
$this->setProcessorType(self::POST_PROCESSOR);
}
/**
* @return array

View File

@@ -9,7 +9,7 @@ use Log;
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class RabobankDescription
class RabobankDescription extends Specifix implements SpecifixInterface
{
/** @var array */
protected $data;
@@ -17,6 +17,14 @@ class RabobankDescription
/** @var array */
protected $row;
/**
* RabobankDescription constructor.
*/
public function __construct()
{
$this->setProcessorType(self::POST_PROCESSOR);
}
/**
* @return array
@@ -29,23 +37,6 @@ class RabobankDescription
}
/**
* Fixes Rabobank specific thing.
*/
protected function rabobankFixEmptyOpposing()
{
Log::debug('RaboSpecifix: Opposing account name is "******".');
if (is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) == 0) {
Log::debug('RaboSpecifix: opp-name is zero length, changed to: "******"');
$this->data['opposing-account-name'] = $this->row[10];
Log::debug('Description was: "******".');
$this->data['description'] = trim(str_replace($this->row[10], '', $this->data['description']));
Log::debug('Description is now: "******".');
}
}
/**
* @param array $data
*/
@@ -62,5 +53,22 @@ class RabobankDescription
$this->row = $row;
}
/**
* Fixes Rabobank specific thing.
*/
protected function rabobankFixEmptyOpposing()
{
Log::debug('RaboSpecifix: Opposing account name is "******".');
if (is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) == 0) {
Log::debug('RaboSpecifix: opp-name is zero length, changed to: "******"');
$this->data['opposing-account-name'] = $this->row[10];
Log::debug('Description was: "******".');
$this->data['description'] = trim(str_replace($this->row[10], '', $this->data['description']));
Log::debug('Description is now: "******".');
}
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* Specifix.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Helpers\Csv\Specifix;
/**
* Class Specifix
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class Specifix
{
/** @var int */
protected $processorType;
/**
* @return int
*/
public function getProcessorType()
{
return $this->processorType;
}
/**
* @param $processorType
*
* @return $this
*/
public function setProcessorType($processorType)
{
$this->processorType = $processorType;
return $this;
}
}

View File

@@ -8,16 +8,30 @@ namespace FireflyIII\Helpers\Csv\Specifix;
*/
interface SpecifixInterface
{
const PRE_PROCESSOR = 1;
const POST_PROCESSOR = 2;
/**
* Implement bank and locale related fixes.
*/
public function fix();
/**
* @return int
*/
public function getProcessorType();
/**
* @param array $data
*/
public function setData($data);
/**
* @param int $processorType
*
* @return $this
*/
public function setProcessorType($processorType);
/**
* @param array $row
*/

View File

@@ -63,7 +63,8 @@ class Wizard implements WizardInterface
if (is_array($map)) {
foreach ($map as $index => $field) {
$keys = array_keys($map);
foreach ($keys as $index) {
if (isset($roles[$index])) {
$name = $roles[$index];
if ($configRoles[$name]['mappable']) {
@@ -167,17 +168,6 @@ class Wizard implements WizardInterface
}
/**
* @param bool $hasHeaders
* @param int $index
*
* @return bool
*/
protected function useRow($hasHeaders, $index)
{
return ($hasHeaders && $index > 1) || !$hasHeaders;
}
/**
* @param array $array
*
@@ -191,4 +181,15 @@ class Wizard implements WizardInterface
return $array;
}
/**
* @param bool $hasHeaders
* @param int $index
*
* @return bool
*/
protected function useRow($hasHeaders, $index)
{
return ($hasHeaders && $index > 1) || !$hasHeaders;
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace FireflyIII\Helpers;
use Carbon\Carbon;
use Preferences;
/**
* Class FiscalHelper
*
* @package FireflyIII\Helpers
*/
class FiscalHelper implements FiscalHelperInterface
{
/** @var bool */
protected $useCustomFiscalYear;
/**
* FiscalHelper constructor.
*
* @codeCoverageIgnore
*
*/
public function __construct()
{
if (Preferences::get('customFiscalYear', 0)->data) {
$this->useCustomFiscalYear = true;
} else {
$this->useCustomFiscalYear = false;
}
}
/**
* @param Carbon $date
*
* @return Carbon date object
*/
public function startOfFiscalYear(Carbon $date)
{
// get start mm-dd. Then create a start date in the year passed.
$startDate = clone $date;
if ($this->useCustomFiscalYear === true) {
$prefStartStr = Preferences::get('fiscalYearStart', '01-01')->data;
list($mth, $day) = explode('-', $prefStartStr);
$startDate->month(intval($mth))->day(intval($day));
// if start date is after passed date, sub 1 year.
if ($startDate > $date) {
$startDate->subYear();
}
} else {
$startDate->startOfYear();
}
return $startDate;
}
/**
* @param Carbon $date
*
* @return Carbon date object
*/
public function endOfFiscalYear(Carbon $date)
{
// get start of fiscal year for passed date
$endDate = $this->startOfFiscalYear($date);
if ($this->useCustomFiscalYear === true) {
// add 1 year and sub 1 day
$endDate->addYear();
$endDate->subDay();
} else {
$endDate->endOfYear();
}
return $endDate;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FireflyIII\Helpers;
use Carbon\Carbon;
/**
* Interface FiscalHelperInterface
*
* @package FireflyIII\Helpers
*/
interface FiscalHelperInterface
{
/**
* This method produces a clone of the Carbon date object passed, checks preferences
* and calculates the first day of the fiscal year.
*
* @param Carbon $date
*
* @return Carbon date object
*/
public function startOfFiscalYear(Carbon $date);
/**
* This method produces a clone of the Carbon date object passed, checks preferences
* and calculates the last day of the fiscal year.
*
* @param Carbon $date
*
* @return Carbon date object
*/
public function endOfFiscalYear(Carbon $date);
}

View File

@@ -0,0 +1,113 @@
<?php
/**
* AccountReportHelper.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use DB;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
/**
* Class AccountReportHelper
*
* @package FireflyIII\Helpers\Report
*/
class AccountReportHelper implements AccountReportHelperInterface
{
/**
* This method generates a full report for the given period on all
* given accounts
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return AccountCollection
*/
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts)
{
$startAmount = '0';
$endAmount = '0';
$diff = '0';
$ids = $accounts->pluck('id')->toArray();
$yesterday = clone $start;
$yesterday->subDay();
bcscale(2);
// get balances for start.
$startSet = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereIn('accounts.id', $ids)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->where('transaction_journals.date', '<=', $yesterday->format('Y-m-d'))
->groupBy('accounts.id')
->get(['accounts.id', DB::Raw('SUM(`transactions`.`amount`) as `balance`')]);
// and end:
$endSet = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereIn('accounts.id', $ids)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->groupBy('accounts.id')
->get(['accounts.id', DB::Raw('SUM(`transactions`.`amount`) as `balance`')]);
$accounts->each(
function (Account $account) use ($startSet, $endSet) {
/**
* The balance for today always incorporates transactions
* made on today. So to get todays "start" balance, we sub one
* day.
*/
//
$currentStart = $startSet->filter(
function (Account $entry) use ($account) {
return $account->id == $entry->id;
}
);
if ($currentStart->first()) {
$account->startBalance = $currentStart->first()->balance;
}
$currentEnd = $endSet->filter(
function (Account $entry) use ($account) {
return $account->id == $entry->id;
}
);
if ($currentEnd->first()) {
$account->endBalance = $currentEnd->first()->balance;
}
}
);
// summarize:
foreach ($accounts as $account) {
$startAmount = bcadd($startAmount, $account->startBalance);
$endAmount = bcadd($endAmount, $account->endBalance);
$diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance));
}
$object = new AccountCollection;
$object->setStart($startAmount);
$object->setEnd($endAmount);
$object->setDifference($diff);
$object->setAccounts($accounts);
return $object;
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* AccountReportHelperInterface.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use Illuminate\Support\Collection;
/**
* Interface AccountReportHelperInterface
*
* @package FireflyIII\Helpers\Report
*/
interface AccountReportHelperInterface
{
/**
* This method generates a full report for the given period on all
* given accounts
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return AccountCollection
*/
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts);
}

View File

@@ -0,0 +1,241 @@
<?php
/**
* BalanceReportHelper.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\BalanceEntry;
use FireflyIII\Helpers\Collection\BalanceHeader;
use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
/**
* Class BalanceReportHelper
*
* @package FireflyIII\Helpers\Report
*/
class BalanceReportHelper implements BalanceReportHelperInterface
{
/** @var BudgetRepositoryInterface */
protected $budgetRepository;
/** @var TagRepositoryInterface */
protected $tagRepository;
/**
* ReportHelper constructor.
*
* @codeCoverageIgnore
*
* @param BudgetRepositoryInterface $budgetRepository
* @param TagRepositoryInterface $tagRepository
*/
public function __construct(BudgetRepositoryInterface $budgetRepository, TagRepositoryInterface $tagRepository)
{
$this->budgetRepository = $budgetRepository;
$this->tagRepository = $tagRepository;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts)
{
$balance = new Balance;
// build a balance header:
$header = new BalanceHeader;
$budgets = $this->budgetRepository->getBudgetsAndLimitsInRange($start, $end);
$spentData = $this->budgetRepository->spentPerBudgetPerAccount($budgets, $accounts, $start, $end);
foreach ($accounts as $account) {
$header->addAccount($account);
}
/** @var BudgetModel $budget */
foreach ($budgets as $budget) {
$balance->addBalanceLine($this->createBalanceLine($budget, $accounts, $spentData));
}
$balance->addBalanceLine($this->createEmptyBalanceLine($accounts, $spentData));
$balance->addBalanceLine($this->createTagsBalanceLine($accounts, $start, $end));
$balance->addBalanceLine($this->createDifferenceBalanceLine($accounts, $spentData, $start, $end));
$balance->setBalanceHeader($header);
return $balance;
}
/**
* @param Budget $budget
* @param Collection $accounts
* @param Collection $spentData
*
* @return BalanceLine
*/
private function createBalanceLine(BudgetModel $budget, Collection $accounts, Collection $spentData)
{
$line = new BalanceLine;
$line->setBudget($budget);
// loop accounts:
foreach ($accounts as $account) {
$balanceEntry = new BalanceEntry;
$balanceEntry->setAccount($account);
// get spent:
$entry = $spentData->filter(
function (TransactionJournal $model) use ($budget, $account) {
return $model->account_id == $account->id && $model->budget_id == $budget->id;
}
);
$spent = 0;
if (!is_null($entry->first())) {
$spent = $entry->first()->spent;
}
$balanceEntry->setSpent($spent);
$line->addBalanceEntry($balanceEntry);
}
return $line;
}
/**
* @param Collection $accounts
* @param Collection $spentData
* @param Carbon $start
* @param Carbon $end
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @return BalanceLine
*/
private function createDifferenceBalanceLine(Collection $accounts, Collection $spentData, Carbon $start, Carbon $end)
{
$diff = new BalanceLine;
$tagsLeft = $this->tagRepository->allCoveredByBalancingActs($accounts, $start, $end);
$diff->setRole(BalanceLine::ROLE_DIFFROLE);
foreach ($accounts as $account) {
$entry = $spentData->filter(
function (TransactionJournal $model) use ($account) {
return $model->account_id == $account->id && is_null($model->budget_id);
}
);
$spent = 0;
if (!is_null($entry->first())) {
$spent = $entry->first()->spent;
}
$leftEntry = $tagsLeft->filter(
function (Tag $tag) use ($account) {
return $tag->account_id == $account->id;
}
);
$left = 0;
if (!is_null($leftEntry->first())) {
$left = $leftEntry->first()->sum;
}
bcscale(2);
$diffValue = bcadd($spent, $left);
// difference:
$diffEntry = new BalanceEntry;
$diffEntry->setAccount($account);
$diffEntry->setSpent($diffValue);
$diff->addBalanceEntry($diffEntry);
}
return $diff;
}
/**
* @param Collection $accounts
* @param Collection $spentData
*
* @return BalanceLine
*/
private function createEmptyBalanceLine(Collection $accounts, Collection $spentData)
{
$empty = new BalanceLine;
foreach ($accounts as $account) {
$entry = $spentData->filter(
function (TransactionJournal $model) use ($account) {
return $model->account_id == $account->id && is_null($model->budget_id);
}
);
$spent = 0;
if (!is_null($entry->first())) {
$spent = $entry->first()->spent;
}
// budget
$budgetEntry = new BalanceEntry;
$budgetEntry->setAccount($account);
$budgetEntry->setSpent($spent);
$empty->addBalanceEntry($budgetEntry);
}
return $empty;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return BalanceLine
*/
private function createTagsBalanceLine(Collection $accounts, Carbon $start, Carbon $end)
{
$tags = new BalanceLine;
$tagsLeft = $this->tagRepository->allCoveredByBalancingActs($accounts, $start, $end);
$tags->setRole(BalanceLine::ROLE_TAGROLE);
foreach ($accounts as $account) {
$leftEntry = $tagsLeft->filter(
function (Tag $tag) use ($account) {
return $tag->account_id == $account->id;
}
);
$left = 0;
if (!is_null($leftEntry->first())) {
$left = $leftEntry->first()->sum;
}
bcscale(2);
// balanced by tags
$tagEntry = new BalanceEntry;
$tagEntry->setAccount($account);
$tagEntry->setLeft($left);
$tags->addBalanceEntry($tagEntry);
}
return $tags;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* BalanceReportHelperInterface.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Balance;
use Illuminate\Support\Collection;
/**
* Interface BalanceReportHelperInterface
*
* @package FireflyIII\Helpers\Report
*/
interface BalanceReportHelperInterface
{
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts);
}

View File

@@ -0,0 +1,133 @@
<?php
/**
* BudgetReportHelper.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\BudgetLine;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Support\Collection;
/**
* Class BudgetReportHelper
*
* @package FireflyIII\Helpers\Report
*/
class BudgetReportHelper implements BudgetReportHelperInterface
{
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return BudgetCollection
*/
public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts)
{
$object = new BudgetCollection;
/** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$set = $repository->getBudgets();
$allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end);
$allTotalSpent = $repository->spentAllPerDayForAccounts($accounts, $start, $end);
bcscale(2);
foreach ($set as $budget) {
$repetitions = $allRepetitions->filter(
function (LimitRepetition $rep) use ($budget) {
return $rep->budget_id == $budget->id;
}
);
$totalSpent = isset($allTotalSpent[$budget->id]) ? $allTotalSpent[$budget->id] : [];
// no repetition(s) for this budget:
if ($repetitions->count() == 0) {
$spent = array_sum($totalSpent);
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setOverspent($spent);
$object->addOverspent($spent);
$object->addBudgetLine($budgetLine);
continue;
}
// one or more repetitions for budget:
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setRepetition($repetition);
$expenses = $this->getSumOfRange($start, $end, $totalSpent);
// 200 en -100 is 100, vergeleken met 0 === 1
// 200 en -200 is 0, vergeleken met 0 === 0
// 200 en -300 is -100, vergeleken met 0 === -1
$left = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? bcadd($repetition->amount, $expenses) : 0;
$spent = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? $expenses : '0';
$overspent = bccomp(bcadd($repetition->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $repetition->amount);
$budgetLine->setLeft($left);
$budgetLine->setSpent($expenses);
$budgetLine->setOverspent($overspent);
$budgetLine->setBudgeted($repetition->amount);
$object->addBudgeted($repetition->amount);
$object->addSpent($spent);
$object->addLeft($left);
$object->addOverspent($overspent);
$object->addBudgetLine($budgetLine);
}
}
// stuff outside of budgets:
$noBudget = $repository->getWithoutBudgetSum($start, $end);
$budgetLine = new BudgetLine;
$budgetLine->setOverspent($noBudget);
$budgetLine->setSpent($noBudget);
$object->addOverspent($noBudget);
$object->addBudgetLine($budgetLine);
return $object;
}
/**
* Take the array as returned by SingleCategoryRepositoryInterface::spentPerDay and SingleCategoryRepositoryInterface::earnedByDay
* and sum up everything in the array in the given range.
*
* @param Carbon $start
* @param Carbon $end
* @param array $array
*
* @return string
*/
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
{
bcscale(2);
$sum = '0';
$currentStart = clone $start; // to not mess with the original one
$currentEnd = clone $end; // to not mess with the original one
while ($currentStart <= $currentEnd) {
$date = $currentStart->format('Y-m-d');
if (isset($array[$date])) {
$sum = bcadd($sum, $array[$date]);
}
$currentStart->addDay();
}
return $sum;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* BudgetReportHelperInterface.php
* Copyright (C) 2016 Sander Dorigo
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use Illuminate\Support\Collection;
/**
* Interface BudgetReportHelperInterface
*
* @package FireflyIII\Helpers\Report
*/
interface BudgetReportHelperInterface
{
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return BudgetCollection
*/
public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts);
}

View File

@@ -3,22 +3,17 @@
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\BalanceEntry;
use FireflyIII\Helpers\Collection\BalanceHeader;
use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Helpers\Collection\Bill as BillCollection;
use FireflyIII\Helpers\Collection\BillLine;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\BudgetLine;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use FireflyIII\Models\Account;
use FireflyIII\Helpers\FiscalHelperInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
/**
* Class ReportHelper
@@ -28,187 +23,47 @@ use FireflyIII\Models\LimitRepetition;
class ReportHelper implements ReportHelperInterface
{
/** @var BudgetRepositoryInterface */
protected $budgetRepository;
/** @var ReportQueryInterface */
protected $query;
/** @var TagRepositoryInterface */
protected $tagRepository;
/**
* ReportHelper constructor.
*
* @codeCoverageIgnore
*
* @param ReportQueryInterface $query
*
* @param ReportQueryInterface $query
* @param BudgetRepositoryInterface $budgetRepository
* @param TagRepositoryInterface $tagRepository
*/
public function __construct(ReportQueryInterface $query)
public function __construct(ReportQueryInterface $query, BudgetRepositoryInterface $budgetRepository, TagRepositoryInterface $tagRepository)
{
$this->query = $query;
}
/**
* This method generates a full report for the given period on all
* the users asset and cash accounts.
*
* @param Carbon $date
* @param Carbon $end
* @param $shared
*
* @return AccountCollection
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared)
{
$accounts = $this->query->getAllAccounts($date, $end, $shared);
$start = '0';
$end = '0';
$diff = '0';
bcscale(2);
// remove cash account, if any:
$accounts = $accounts->filter(
function (Account $account) {
if ($account->accountType->type != 'Cash account') {
return $account;
}
return null;
}
);
// summarize:
foreach ($accounts as $account) {
$start = bcadd($start, $account->startBalance);
$end = bcadd($end, $account->endBalance);
$diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance));
}
$object = new AccountCollection;
$object->setStart($start);
$object->setEnd($end);
$object->setDifference($diff);
$object->setAccounts($accounts);
return $object;
}
/**
*
* The balance report contains a Balance object which in turn contains:
*
* A BalanceHeader object which contains all relevant user asset accounts for the report.
*
* A number of BalanceLine objects, which hold:
* - A budget
* - A number of BalanceEntry objects.
*
* The BalanceEntry object holds:
* - The same budget (again)
* - A user asset account as mentioned in the BalanceHeader
* - The amount of money spent on the budget by the user asset account
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, $shared)
{
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$tagRepository = app('FireflyIII\Repositories\Tag\TagRepositoryInterface');
$balance = new Balance;
// build a balance header:
$header = new BalanceHeader;
$accounts = $this->query->getAllAccounts($start, $end, $shared);
$budgets = $repository->getBudgets();
foreach ($accounts as $account) {
$header->addAccount($account);
}
/** @var BudgetModel $budget */
foreach ($budgets as $budget) {
$line = new BalanceLine;
$line->setBudget($budget);
// get budget amount for current period:
$rep = $repository->getCurrentRepetition($budget, $start, $end);
$line->setRepetition($rep);
// loop accounts:
foreach ($accounts as $account) {
$balanceEntry = new BalanceEntry;
$balanceEntry->setAccount($account);
// get spent:
$spent = $this->query->spentInBudgetCorrected($account, $budget, $start, $end); // I think shared is irrelevant.
$balanceEntry->setSpent($spent);
$line->addBalanceEntry($balanceEntry);
}
// add line to balance:
$balance->addBalanceLine($line);
}
// then a new line for without budget.
// and one for the tags:
$empty = new BalanceLine;
$tags = new BalanceLine;
$diffLine = new BalanceLine;
$tags->setRole(BalanceLine::ROLE_TAGROLE);
$diffLine->setRole(BalanceLine::ROLE_DIFFROLE);
foreach ($accounts as $account) {
$spent = $this->query->spentNoBudget($account, $start, $end);
$left = $tagRepository->coveredByBalancingActs($account, $start, $end);
bcscale(2);
$diff = bcsub($spent, $left);
// budget
$budgetEntry = new BalanceEntry;
$budgetEntry->setAccount($account);
$budgetEntry->setSpent($spent);
$empty->addBalanceEntry($budgetEntry);
// balanced by tags
$tagEntry = new BalanceEntry;
$tagEntry->setAccount($account);
$tagEntry->setLeft($left);
$tags->addBalanceEntry($tagEntry);
// difference:
$diffEntry = new BalanceEntry;
$diffEntry->setAccount($account);
$diffEntry->setSpent($diff);
$diffLine->addBalanceEntry($diffEntry);
}
$balance->addBalanceLine($empty);
$balance->addBalanceLine($tags);
$balance->addBalanceLine($diffLine);
$balance->setBalanceHeader($header);
return $balance;
$this->query = $query;
$this->budgetRepository = $budgetRepository;
$this->tagRepository = $tagRepository;
}
/**
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @param Carbon $start
* @param Carbon $end
* Excludes bills which have not had a payment on the mentioned accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return BillCollection
*/
public function getBillReport(Carbon $start, Carbon $end)
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts)
{
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
$repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$bills = $repository->getBills();
$bills = $repository->getBillsForAccounts($accounts);
$journals = $repository->getAllJournalsInRange($bills, $start, $end);
$collection = new BillCollection;
/** @var Bill $bill */
@@ -221,16 +76,17 @@ class ReportHelper implements ReportHelperInterface
// is hit in period?
bcscale(2);
$set = $repository->getJournalsInRange($bill, $start, $end);
if ($set->count() == 0) {
$billLine->setHit(false);
} else {
$billLine->setHit(true);
$amount = '0';
foreach ($set as $entry) {
$amount = bcadd($amount, $entry->amount);
$entry = $journals->filter(
function (TransactionJournal $journal) use ($bill) {
return $journal->bill_id == $bill->id;
}
$billLine->setAmount($amount);
);
if (!is_null($entry->first())) {
$billLine->setAmount($entry->first()->journalAmount);
$billLine->setHit(true);
} else {
$billLine->setHit(false);
}
$collection->addBill($billLine);
@@ -238,120 +94,49 @@ class ReportHelper implements ReportHelperInterface
}
return $collection;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return BudgetCollection
*/
public function getBudgetReport(Carbon $start, Carbon $end, $shared)
{
$object = new BudgetCollection;
/** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$set = $repository->getBudgets();
bcscale(2);
foreach ($set as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
// no repetition(s) for this budget:
if ($repetitions->count() == 0) {
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setOverspent($spent);
$object->addOverspent($spent);
$object->addBudgetLine($budgetLine);
continue;
}
// one or more repetitions for budget:
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setRepetition($repetition);
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, $shared);
$expenses = $expenses * -1;
$left = $expenses < $repetition->amount ? bcsub($repetition->amount, $expenses) : 0;
$spent = $expenses > $repetition->amount ? 0 : $expenses;
$overspent = $expenses > $repetition->amount ? bcsub($expenses, $repetition->amount) : 0;
$budgetLine->setLeft($left);
$budgetLine->setSpent($spent);
$budgetLine->setOverspent($overspent);
$budgetLine->setBudgeted($repetition->amount);
$object->addBudgeted($repetition->amount);
$object->addSpent($spent);
$object->addLeft($left);
$object->addOverspent($overspent);
$object->addBudgetLine($budgetLine);
}
}
// stuff outside of budgets:
$noBudget = $repository->getWithoutBudgetSum($start, $end);
$budgetLine = new BudgetLine;
$budgetLine->setOverspent($noBudget);
$object->addOverspent($noBudget);
$object->addBudgetLine($budgetLine);
return $object;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared)
public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts)
{
$object = new CategoryCollection;
/**
* GET CATEGORIES:
*/
/** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */
$repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
$set = $repository->getCategories();
$set = $repository->spentForAccountsPerMonth($accounts, $start, $end);
foreach ($set as $category) {
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
$category->spent = $spent;
$object->addCategory($category);
$object->addTotal($spent);
}
return $object;
}
/**
* Get a full report on the users expenses during the period.
* Get a full report on the users expenses during the period for a list of accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Expense
*/
public function getExpenseReport($start, $end, $shared)
public function getExpenseReport($start, $end, Collection $accounts)
{
$object = new Expense;
$set = $this->query->expenseInPeriodCorrected($start, $end, $shared);
$set = $this->query->expense($accounts, $start, $end);
foreach ($set as $entry) {
$object->addToTotal($entry->amount_positive);
$object->addToTotal($entry->journalAmount); // can be positive, if it's a transfer
$object->addOrCreateExpense($entry);
}
@@ -359,20 +144,21 @@ class ReportHelper implements ReportHelperInterface
}
/**
* Get a full report on the users incomes during the period.
* Get a full report on the users incomes during the period for the given accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Income
*/
public function getIncomeReport($start, $end, $shared)
public function getIncomeReport($start, $end, Collection $accounts)
{
$object = new Income;
$set = $this->query->incomeInPeriodCorrected($start, $end, $shared);
$set = $this->query->income($accounts, $start, $end);
foreach ($set as $entry) {
$object->addToTotal($entry->amount_positive);
$object->addToTotal($entry->journalAmount);
$object->addOrCreateIncome($entry);
}
@@ -386,20 +172,72 @@ class ReportHelper implements ReportHelperInterface
*/
public function listOfMonths(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
/** @var FiscalHelperInterface $fiscalHelper */
$fiscalHelper = app('FireflyIII\Helpers\FiscalHelperInterface');
$start = clone $date;
$start->startOfMonth();
$end = Carbon::now();
$end->endOfMonth();
$months = [];
while ($start <= $end) {
$year = $start->year;
$months[$year][] = [
// current year:
$year = $fiscalHelper->endOfFiscalYear($start)->year;
if (!isset($months[$year])) {
$months[$year] = [
'fiscal_start' => $fiscalHelper->startOfFiscalYear($start)->format('Y-m-d'),
'fiscal_end' => $fiscalHelper->endOfFiscalYear($start)->format('Y-m-d'),
'start' => Carbon::createFromDate($year, 1, 1)->format('Y-m-d'),
'end' => Carbon::createFromDate($year, 12, 31)->format('Y-m-d'),
'months' => [],
];
}
$currentEnd = clone $start;
$currentEnd->endOfMonth();
$months[$year]['months'][] = [
'formatted' => $start->formatLocalized('%B %Y'),
'start' => $start->format('Y-m-d'),
'end' => $currentEnd->format('Y-m-d'),
'month' => $start->month,
'year' => $year,
];
$start->addMonth();
// to make the hop to the next month properly:
$start = clone $currentEnd;
$start->addDay();
}
return $months;
}
/**
* Take the array as returned by SingleCategoryRepositoryInterface::spentPerDay and SingleCategoryRepositoryInterface::earnedByDay
* and sum up everything in the array in the given range.
*
* @param Carbon $start
* @param Carbon $end
* @param array $array
*
* @return string
*/
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
{
bcscale(2);
$sum = '0';
$currentStart = clone $start; // to not mess with the original one
$currentEnd = clone $end; // to not mess with the original one
while ($currentStart <= $currentEnd) {
$date = $currentStart->format('Y-m-d');
if (isset($array[$date])) {
$sum = bcadd($sum, $array[$date]);
}
$currentStart->addDay();
}
return $sum;
}
}

View File

@@ -3,13 +3,11 @@
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\Bill as BillCollection;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use Illuminate\Support\Collection;
/**
* Interface ReportHelperInterface
@@ -19,77 +17,50 @@ use FireflyIII\Helpers\Collection\Income;
interface ReportHelperInterface
{
/**
* This method generates a full report for the given period on all
* the users asset and cash accounts.
*
* @param Carbon $date
* @param Carbon $end
* @param boolean $shared
*
* @return AccountCollection
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared);
/**
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @param Carbon $start
* @param Carbon $end
* Excludes bills which have not had a payment on the mentioned accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return BillCollection
*/
public function getBillReport(Carbon $start, Carbon $end);
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return BudgetCollection
*/
public function getBudgetReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared);
public function getCategoryReport(Carbon $start, Carbon $end, Collection $accounts);
/**
* Get a full report on the users expenses during the period.
* Get a full report on the users expenses during the period for a list of accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Expense
*/
public function getExpenseReport($start, $end, $shared);
public function getExpenseReport($start, $end, Collection $accounts);
/**
* Get a full report on the users incomes during the period.
* Get a full report on the users incomes during the period for the given accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Income
*/
public function getIncomeReport($start, $end, $shared);
public function getIncomeReport($start, $end, Collection $accounts);
/**
* @param Carbon $date

View File

@@ -4,14 +4,10 @@ namespace FireflyIII\Helpers\Report;
use Auth;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder;
use DB;
use FireflyIII\Models\TransactionType;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Steam;
/**
* Class ReportQuery
@@ -20,260 +16,163 @@ use Steam;
*/
class ReportQuery implements ReportQueryInterface
{
/**
* See ReportQueryInterface::incomeInPeriodCorrected.
* Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers)
* grouped by month like so: "2015-01" => '123.45'
*
* This method's length is caused mainly by the query build stuff. Therefor:
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
public function earnedPerMonth(Collection $accounts, Carbon $start, Carbon $end)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($includeShared === false) {
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) { // only get withdrawals not from a shared account
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) { // and transfers from a shared account.
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
}
);
} else {
$query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine.
$ids = $accounts->pluck('id')->toArray();
$query = Auth::user()->transactionjournals()
->leftJoin(
'transactions AS t_from', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0);
}
)
->leftJoin(
'transactions AS t_to', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0);
}
)
->whereIn('t_to.account_id', $ids)
->whereNotIn('t_from.account_id', $ids)
->after($start)
->before($end)
->transactionTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE])
->groupBy('dateFormatted')
->get(
[
DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'),
DB::Raw('SUM(`t_to`.`amount`) AS `sum`'),
]
);
$array = [];
foreach ($query as $result) {
$array[$result->dateFormatted] = $result->sum;
}
$query->orderBy('transaction_journals.date');
$data = $query->get( // get everything
['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted']
);
$data->each(
function (TransactionJournal $journal) {
if (intval($journal->account_encrypted) == 1) {
$journal->name = Crypt::decrypt($journal->name);
}
}
);
return $data;
return $array;
}
/**
* Get a users accounts combined with various meta-data related to the start and end date.
* This method returns all the "out" transaction journals for the given account and given period. The amount
* is stored in "journalAmount".
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false)
public function expense(Collection $accounts, Carbon $start, Carbon $end)
{
$query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')
->accountTypeIn(['Default account', 'Asset account', 'Cash account']);
if ($includeShared === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where(
function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedAsset"');
$query->orWhereNull('account_meta.data');
}
);
}
$set = $query->get(['accounts.*']);
$set->each(
function (Account $account) use ($start, $end) {
/**
* The balance for today always incorporates transactions
* made on today. So to get todays "start" balance, we sub one
* day.
*/
$yesterday = clone $start;
$yesterday->subDay();
/** @noinspection PhpParamsInspection */
$account->startBalance = Steam::balance($account, $yesterday);
$account->endBalance = Steam::balance($account, $end);
}
);
$ids = $accounts->pluck('id')->toArray();
$set = Auth::user()->transactionjournals()
->leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts', 't_to.account_id', '=', 'accounts.id')
->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE])
->before($end)
->after($start)
->whereIn('t_from.account_id', $ids)
->whereNotIn('t_to.account_id', $ids)
->get(['transaction_journals.*', 't_from.amount as journalAmount', 'accounts.id as account_id', 'accounts.name as account_name']);
return $set;
}
/**
* This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results
* will simply list the transaction journals only. This should allow any follow up counting to be accurate with
* regards to tags.
* This method returns all the "in" transaction journals for the given account and given period. The amount
* is stored in "journalAmount".
*
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
public function income(Collection $accounts, Carbon $start, Carbon $end)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($includeShared === false) {
// only get deposits not to a shared account
// and transfers to a shared account.
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedAsset"');
$q->where('acm_to.data','!=','"sharedAsset"');
}
);
}
);
} else {
// any deposit is fine.
$query->where('transaction_types.type', 'Deposit');
$ids = $accounts->pluck('id')->toArray();
$set = Auth::user()->transactionjournals()
->leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts', 't_from.account_id', '=', 'accounts.id')
->transactionTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE])
->before($end)
->after($start)
->whereIn('t_to.account_id', $ids)
->whereNotIn('t_from.account_id', $ids)
->get(['transaction_journals.*', 't_to.amount as journalAmount', 'accounts.id as account_id', 'accounts.name as account_name']);
return $set;
}
/**
* Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers)
* grouped by month like so: "2015-01" => '123.45'
*
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function spentPerMonth(Collection $accounts, Carbon $start, Carbon $end)
{
$ids = $accounts->pluck('id')->toArray();
$query = Auth::user()->transactionjournals()
->leftJoin(
'transactions AS t_from', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0);
}
)
->leftJoin(
'transactions AS t_to', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0);
}
)
->whereIn('t_from.account_id', $ids)
->whereNotIn('t_to.account_id', $ids)
->after($start)
->before($end)
->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE])
->groupBy('dateFormatted')
->get(
[
DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'),
DB::Raw('SUM(`t_from`.`amount`) AS `sum`'),
]
);
$array = [];
foreach ($query as $result) {
$array[$result->dateFormatted] = $result->sum;
}
$query->orderBy('transaction_journals.date');
// get everything
$data = $query->get(
['transaction_journals.*', 'transaction_types.type', 'ac_from.name as name', 'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted']
);
return $array;
$data->each(
function (TransactionJournal $journal) {
if (intval($journal->account_encrypted) == 1) {
$journal->name = Crypt::decrypt($journal->name);
}
}
);
$data = $data->filter(
function (TransactionJournal $journal) {
if ($journal->amount != 0) {
return $journal;
}
return null;
}
);
return $data;
}
/**
* Covers tags
*
* @param Account $account
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end)
{
bcscale(2);
return bcmul(
Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->transactionTypes(['Withdrawal'])
->where('transactions.account_id', $account->id)
->before($end)
->after($start)
->where('budget_transaction_journal.budget_id', $budget->id)
->get(['transaction_journals.*'])->sum('amount'), -1
);
}
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function spentNoBudget(Account $account, Carbon $start, Carbon $end)
{
return
Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->transactionTypes(['Withdrawal'])
->where('transactions.account_id', $account->id)
->before($end)
->after($start)
->whereNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*'])->sum('amount');
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Builder
*/
protected function queryJournalsWithTransactions(Carbon $start, Carbon $end)
{
$query = TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id');
$query->before($end)->after($start)->where('transaction_journals.user_id', Auth::user()->id);
return $query;
}
}

View File

@@ -3,8 +3,6 @@
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use Illuminate\Support\Collection;
/**
@@ -16,65 +14,52 @@ interface ReportQueryInterface
{
/**
* See ReportQueryInterface::incomeInPeriodCorrected
* Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers)
* grouped by month like so: "2015-01" => '123.45'
*
* This method returns all "expense" journals in a certain period, which are both transfers to a shared account
* and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false);
public function earnedPerMonth(Collection $accounts, Carbon $start, Carbon $end);
/**
* Get a users accounts combined with various meta-data related to the start and end date.
* This method returns all the "out" transaction journals for the given account and given period. The amount
* is stored in "journalAmount".
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false);
public function expense(Collection $accounts, Carbon $start, Carbon $end);
/**
* This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results
* will simply list the transaction journals only. This should allow any follow up counting to be accurate with
* regards to tags.
* This method returns all the "in" transaction journals for the given account and given period. The amount
* is stored in "journalAmount".
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false);
public function income(Collection $accounts, Carbon $start, Carbon $end);
/**
* Covers tags as well.
* Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers)
* grouped by month like so: "2015-01" => '123.45'
*
* @param Account $account
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return float
* @return array
*/
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end);
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function spentNoBudget(Account $account, Carbon $start, Carbon $end);
public function spentPerMonth(Collection $accounts, Carbon $start, Carbon $end);
}

View File

@@ -6,7 +6,7 @@ use Config;
use ExpandedForm;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use Input;
use Preferences;
use Session;
@@ -38,6 +38,8 @@ class AccountController extends Controller
*/
public function create($what = 'asset')
{
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$subTitle = trans('firefly.make_new_' . $what . '_account');
@@ -54,11 +56,12 @@ class AccountController extends Controller
}
/**
* @param ARI $repository
* @param Account $account
*
* @return \Illuminate\View\View
*/
public function delete(AccountRepositoryInterface $repository, Account $account)
public function delete(ARI $repository, Account $account)
{
$typeName = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]);
@@ -74,12 +77,12 @@ class AccountController extends Controller
}
/**
* @param AccountRepositoryInterface $repository
* @param Account $account
* @param ARI $repository
* @param Account $account
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(AccountRepositoryInterface $repository, Account $account)
public function destroy(ARI $repository, Account $account)
{
$type = $account->accountType->type;
$typeName = Config::get('firefly.shortNamesByFullName.' . $type);
@@ -95,12 +98,12 @@ class AccountController extends Controller
}
/**
* @param AccountRepositoryInterface $repository
* @param Account $account
* @param ARI $repository
* @param Account $account
*
* @return \Illuminate\View\View
*/
public function edit(AccountRepositoryInterface $repository, Account $account)
public function edit(ARI $repository, Account $account)
{
$what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type];
@@ -130,7 +133,7 @@ class AccountController extends Controller
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount,
'virtualBalance' => round($account->virtual_balance, 2)
'virtualBalance' => round($account->virtual_balance, 2),
];
Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'accounts');
@@ -140,40 +143,31 @@ class AccountController extends Controller
}
/**
* @param AccountRepositoryInterface $repository
* @param ARI $repository
* @param $what
*
* @return \Illuminate\View\View
*/
public function index(AccountRepositoryInterface $repository, $what)
public function index(ARI $repository, $what)
{
$subTitle = trans('firefly.' . $what . '_accounts');
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$types = Config::get('firefly.accountTypesByIdentifier.' . $what);
$accounts = $repository->getAccounts($types);
// last activity:
/**
* HERE WE ARE
*/
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = clone Session::get('end', Carbon::now()->endOfMonth());
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = clone Session::get('end', Carbon::now()->endOfMonth());
$start->subDay();
// start balances:
$ids = [];
foreach ($accounts as $account) {
$ids[] = $account->id;
}
$ids = $accounts->pluck('id')->toArray();
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$activities = Steam::getLastActivities($ids);
$accounts->each(
function (Account $account) use ($activities, $startBalances, $endBalances) {
$account->lastActivityDate = isset($activities[$account->id]) ? $activities[$account->id] : null;
$account->startBalance = isset($startBalances[$account->id]) ? $startBalances[$account->id] : null;
$account->endBalance = isset($endBalances[$account->id]) ? $endBalances[$account->id] : null;
$account->lastActivityDate = $this->isInArray($activities, $account->id);
$account->startBalance = $this->isInArray($startBalances, $account->id);
$account->endBalance = $this->isInArray($endBalances, $account->id);
}
);
@@ -181,12 +175,12 @@ class AccountController extends Controller
}
/**
* @param AccountRepositoryInterface $repository
* @param Account $account
* @param ARI $repository
* @param Account $account
*
* @return \Illuminate\View\View
*/
public function show(AccountRepositoryInterface $repository, Account $account)
public function show(ARI $repository, Account $account)
{
$page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page'));
$subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $account->accountType->type);
@@ -200,24 +194,25 @@ class AccountController extends Controller
}
/**
* @param AccountFormRequest $request
* @param AccountRepositoryInterface $repository
* @param AccountFormRequest $request
* @param ARI $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(AccountFormRequest $request, AccountRepositoryInterface $repository)
public function store(AccountFormRequest $request, ARI $repository)
{
$accountData = [
'name' => $request->input('name'),
'accountType' => $request->input('what'),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'virtualBalanceCurrency' => intval($request->input('amount_currency_id_virtualBalance')),
'active' => true,
'user' => Auth::user()->id,
'iban' => $request->input('iban'),
'accountRole' => $request->input('accountRole'),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
];
@@ -238,13 +233,13 @@ class AccountController extends Controller
}
/**
* @param AccountFormRequest $request
* @param AccountRepositoryInterface $repository
* @param Account $account
* @param AccountFormRequest $request
* @param ARI $repository
* @param Account $account
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(AccountFormRequest $request, AccountRepositoryInterface $repository, Account $account)
public function update(AccountFormRequest $request, ARI $repository, Account $account)
{
$accountData = [
@@ -256,12 +251,10 @@ class AccountController extends Controller
'virtualBalance' => round($request->input('virtualBalance'), 2),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
'ccType' => $request->input('ccType'),
'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'),
];
$repository->update($account, $accountData);
Session::flash('success', 'Account "' . $account->name . '" updated.');
@@ -279,4 +272,20 @@ class AccountController extends Controller
}
/**
* @param array $array
* @param $entryId
*
* @return null|mixed
*/
protected function isInArray(array $array, $entryId)
{
if (isset($array[$entryId])) {
return $array[$entryId];
}
return null;
}
}

View File

@@ -2,7 +2,6 @@
namespace FireflyIII\Http\Controllers;
use Crypt;
use File;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
@@ -25,7 +24,7 @@ class AttachmentController extends Controller
{
/**
* @codeCoverageIgnore
*
*/
public function __construct()
{
@@ -34,26 +33,6 @@ class AttachmentController extends Controller
View::share('title', trans('firefly.attachments'));
}
/**
* @param Attachment $attachment
*
* @return \Illuminate\View\View
*/
public function edit(Attachment $attachment)
{
$subTitleIcon = 'fa-pencil';
$subTitle = trans('firefly.edit_attachment', ['name' => $attachment->filename]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('attachments.edit.fromUpdate') !== true) {
Session::put('attachments.edit.url', URL::previous());
}
Session::forget('attachments.edit.fromUpdate');
return view('attachments.edit', compact('attachment', 'subTitleIcon', 'subTitle'));
}
/**
* @param Attachment $attachment
*
@@ -90,7 +69,10 @@ class AttachmentController extends Controller
}
/**
* @param Attachment $attachment
* @param Attachment $attachment
* @param AttachmentHelperInterface $helper
*
* @return string
*/
public function download(Attachment $attachment, AttachmentHelperInterface $helper)
{
@@ -100,23 +82,42 @@ class AttachmentController extends Controller
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . $quoted);
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . $attachment->size);
echo Crypt::decrypt(file_get_contents($file));
return response(Crypt::decrypt(file_get_contents($file)), 200)
->header('Content-Description', 'File Transfer')
->header('Content-Type', 'application/octet-stream')
->header('Content-Disposition', 'attachment; filename=' . $quoted)
->header('Content-Transfer-Encoding', 'binary')
->header('Connection', 'Keep-Alive')
->header('Expires', '0')
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->header('Pragma', 'public')
->header('Content-Length', $attachment->size);
} else {
abort(404);
}
}
/**
* @param Attachment $attachment
*
* @return \Illuminate\View\View
*/
public function edit(Attachment $attachment)
{
$subTitleIcon = 'fa-pencil';
$subTitle = trans('firefly.edit_attachment', ['name' => $attachment->filename]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('attachments.edit.fromUpdate') !== true) {
Session::put('attachments.edit.url', URL::previous());
}
Session::forget('attachments.edit.fromUpdate');
return view('attachments.edit', compact('attachment', 'subTitleIcon', 'subTitle'));
}
/**
* @param Attachment $attachment
*

View File

@@ -1,4 +1,6 @@
<?php namespace FireflyIII\Http\Controllers\Auth;
<?php
namespace FireflyIII\Http\Controllers\Auth;
use Auth;
use FireflyIII\Http\Controllers\Controller;
@@ -8,12 +10,14 @@ use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Illuminate\Support\Facades\Lang;
use Log;
use Mail;
use Request as Rq;
use Session;
use Twig;
use Validator;
/**
* Class AuthController
*
@@ -24,15 +28,20 @@ class AuthController extends Controller
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
/**
* Show the application registration form.
* Where to redirect users after login / registration.
*
* @return \Illuminate\Http\Response
* @var string
*/
public function getRegister()
{
$host = Rq::getHttpHost();
protected $redirectTo = '/home';
return view('auth.register', compact('host'));
/**
* Create a new authentication controller instance.
*
*/
public function __construct()
{
$this->middleware('guest', ['except' => 'logout']);
parent::__construct();
}
/**
@@ -42,17 +51,9 @@ class AuthController extends Controller
*
* @return \Illuminate\Http\Response
*/
public function postLogin(Request $request)
public function login(Request $request)
{
$this->validate(
$request, [
$this->loginUsername() => 'required', 'password' => 'required',
]
);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
$this->validate($request, [$this->loginUsername() => 'required', 'password' => 'required',]);
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
@@ -62,76 +63,40 @@ class AuthController extends Controller
$credentials = $this->getCredentials($request);
$credentials['blocked'] = 0; // most not be blocked.
if (Auth::attempt($credentials, $request->has('remember'))) {
if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
return $this->handleUserWasAuthenticated($request, $throttles);
}
// default error message:
$message = $this->getFailedLoginMessage();
// try to find a blocked user with this email address.
// check if user is blocked:
$message = '';
/** @var User $foundUser */
$foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first();
if (!is_null($foundUser)) {
// if it exists, show message:
$code = $foundUser->blocked_code;
$code = $foundUser->blocked_code;
if (strlen($code) == 0) {
$code = 'general_blocked';
}
$message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]);
}
// try
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
if ($throttles) {
$this->incrementLoginAttempts($request);
}
return redirect($this->loginPath())
->withInput($request->only($this->loginUsername(), 'remember'))
->withErrors(
[
$this->loginUsername() => $message,
]
);
}
public $redirectTo = '/';
/**
* Create a new authentication controller instance.
*
* @codeCoverageIgnore
*
*/
public function __construct()
{
parent::__construct();
$this->middleware('guest', ['except' => 'getLogout']);
}
/**
* Show the application login form.
*
* @codeCoverageIgnore
* @return \Illuminate\Http\Response
*
*/
public function getLogin()
{
return Twig::render('auth.login');
return $this->sendFailedLoginResponse($request, $message);
}
/**
* Handle a registration request for the application.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function postRegister(Request $request)
public function register(Request $request)
{
$validator = $this->validator($request->all());
@@ -139,25 +104,37 @@ class AuthController extends Controller
$this->throwValidationException(
$request, $validator
);
// @codeCoverageIgnoreStart
}
// @codeCoverageIgnoreEnd
$data = $request->all();
$data['password'] = bcrypt($data['password']);
Auth::login($this->create($data));
// is user email domain blocked?
if ($this->isBlockedDomain($data['email'])) {
$validator->getMessageBag()->add('email', (string)trans('validation.invalid_domain'));
$this->throwValidationException(
$request, $validator
);
}
Auth::login($this->create($request->all()));
// get the email address
if (Auth::user() instanceof User) {
$email = Auth::user()->email;
$address = route('index');
$email = Auth::user()->email;
$address = route('index');
$ipAddress = $request->ip();
// send email.
Mail::send(
['emails.registered-html', 'emails.registered'], ['address' => $address], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
try {
Mail::send(
['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
}
);
} catch (\Swift_TransportException $e) {
Log::error($e->getMessage());
}
);
// set flash message
Session::flash('success', 'You have registered successfully!');
@@ -176,25 +153,20 @@ class AuthController extends Controller
// @codeCoverageIgnoreStart
abort(500, 'Not a user!');
return redirect('/');
// @codeCoverageIgnoreEnd
return redirect($this->redirectPath());
}
/**
* Get a validator for an incoming registration request.
* Show the application registration form.
*
* @param array $data
*
* @return \Illuminate\Contracts\Validation\Validator
* @return \Illuminate\Http\Response
*/
public function validator(array $data)
public function showRegistrationForm()
{
return Validator::make(
$data, [
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]
);
$host = Rq::getHttpHost();
return view('auth.register', compact('host'));
}
/**
@@ -204,13 +176,102 @@ class AuthController extends Controller
*
* @return User
*/
public function create(array $data)
protected function create(array $data)
{
return User::create(
[
'email' => $data['email'],
'password' => $data['password'],
'password' => bcrypt($data['password']),
]
);
}
/**
* @return array
*/
protected function getBlockedDomains()
{
$set = explode(',', env('BLOCKED_DOMAINS', ''));
$domains = [];
foreach ($set as $entry) {
$domain = trim($entry);
if (strlen($domain) > 0) {
$domains[] = $domain;
}
}
return $domains;
}
/**
* Get the failed login message.
*
* @param $message
*
* @return string
*/
protected function getFailedLoginMessage($message)
{
if (strlen($message) > 0) {
return $message;
}
return Lang::has('auth.failed')
? Lang::get('auth.failed')
: 'These credentials do not match our records.';
}
/**
* @param $email
*
* @return bool
*/
protected function isBlockedDomain($email)
{
$parts = explode('@', $email);
$blocked = $this->getBlockedDomains();
if (isset($parts[1]) && in_array($parts[1], $blocked)) {
return true;
}
return false;
}
/**
* Get the failed login response instance.
*
* @param \Illuminate\Http\Request $request
*
* @param $message
*
* @return \Illuminate\Http\Response
*/
protected function sendFailedLoginResponse(Request $request, $message)
{
return redirect()->back()
->withInput($request->only($this->loginUsername(), 'remember'))
->withErrors(
[
$this->loginUsername() => $this->getFailedLoginMessage($message),
]
);
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
*
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make(
$data, [
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]
);
}
}

View File

@@ -1,17 +1,22 @@
<?php namespace FireflyIII\Http\Controllers\Auth;
<?php
namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\User;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Illuminate\Support\Facades\Password;
/**
* Class PasswordController
*
* @codeCoverageIgnore
* @package FireflyIII\Http\Controllers\Auth
*/
class PasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
@@ -25,14 +30,9 @@ class PasswordController extends Controller
use ResetsPasswords;
protected $redirectPath = '/';
/**
* Create a new password controller instance.
*
* @codeCoverageIgnore
*
*/
public function __construct()
{
@@ -41,4 +41,38 @@ class PasswordController extends Controller
$this->middleware('guest');
}
/**
* Send a reset link to the given user.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function sendResetLinkEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$user = User::whereEmail($request->get('email'))->first();
if (!is_null($user) && intval($user->blocked) === 1) {
$response = 'passwords.blocked';
} else {
$response = Password::sendResetLink(
$request->only('email'), function (Message $message) {
$message->subject($this->getEmailSubject());
}
);
}
switch ($response) {
case Password::RESET_LINK_SENT:
return $this->getSendResetLinkEmailSuccessResponse($response);
case Password::INVALID_USER:
case 'passwords.blocked':
default:
return $this->getSendResetLinkEmailFailureResponse($response);
}
}
}

View File

@@ -6,7 +6,9 @@ use Carbon\Carbon;
use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Support\Collection;
use Input;
use Navigation;
use Preferences;
@@ -19,7 +21,6 @@ use View;
* Class BudgetController
*
* @package FireflyIII\Http\Controllers
* @SuppressWarnings(PHPMD.TooManyMethods)
*/
class BudgetController extends Controller
{
@@ -67,7 +68,7 @@ class BudgetController extends Controller
Session::forget('budgets.create.fromStore');
Session::flash('gaEventCategory', 'budgets');
Session::flash('gaEventAction', 'create');
$subTitle = trans('firefly.create_new_budget');
$subTitle = (string)trans('firefly.create_new_budget');
return view('budgets.create', compact('subTitle'));
}
@@ -133,9 +134,11 @@ class BudgetController extends Controller
/**
* @param BudgetRepositoryInterface $repository
*
* @param ARI $accountRepository
*
* @return \Illuminate\View\View
*/
public function index(BudgetRepositoryInterface $repository)
public function index(BudgetRepositoryInterface $repository, ARI $accountRepository)
{
$budgets = $repository->getActiveBudgets();
$inactive = $repository->getInactiveBudgets();
@@ -147,6 +150,8 @@ class BudgetController extends Controller
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
$budgetIncomeTotal = Preferences::get($key, 1000)->data;
$period = Navigation::periodShow($start, $range);
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
bcscale(2);
/**
* Do some cleanup:
@@ -156,7 +161,7 @@ class BudgetController extends Controller
// loop the budgets:
/** @var Budget $budget */
foreach ($budgets as $budget) {
$budget->spent = $repository->balanceInPeriod($budget, $start, $end);
$budget->spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
$budget->currentRep = $repository->getCurrentRepetition($budget, $start, $end);
if ($budget->currentRep) {
$budgeted = bcadd($budgeted, $budget->currentRep->amount);
@@ -170,7 +175,7 @@ class BudgetController extends Controller
$defaultCurrency = Amount::getDefaultCurrency();
return view(
'budgets.index', compact('budgetMaximum','period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
'budgets.index', compact('budgetMaximum', 'period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
);
}
@@ -227,13 +232,26 @@ class BudgetController extends Controller
$journals = $repository->getJournals($budget, $repetition);
if (is_null($repetition->id)) {
$limits = $repository->getBudgetLimits($budget);
$start = $repository->firstActivity($budget);
$end = new Carbon;
$set = $budget->limitrepetitions()->orderBy('startdate', 'DESC')->get();
$subTitle = e($budget->name);
} else {
$limits = [$repetition->budgetLimit];
$start = $repetition->startdate;
$end = $repetition->enddate;
$set = new Collection([$repetition]);
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
}
$spentArray = $repository->spentPerDay($budget, $start, $end);
$limits = new Collection();
/** @var LimitRepetition $entry */
foreach ($set as $entry) {
$entry->spent = $this->getSumOfRange($entry->startdate, $entry->enddate, $spentArray);
$limits->push($entry);
}
$journals->setPath('/budgets/show/' . $budget->id);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
@@ -279,7 +297,7 @@ class BudgetController extends Controller
{
$budgetData = [
'name' => $request->input('name'),
'active' => intval($request->input('active')) == 1
'active' => intval($request->input('active')) == 1,
];
$repository->update($budget, $budgetData);

View File

@@ -4,7 +4,8 @@ use Auth;
use Carbon\Carbon;
use FireflyIII\Http\Requests\CategoryFormRequest;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
use FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface as SCRI;
use FireflyIII\Support\CacheProperties;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
@@ -68,12 +69,12 @@ class CategoryController extends Controller
}
/**
* @param CategoryRepositoryInterface $repository
* @param Category $category
* @param SCRI $repository
* @param Category $category
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(CategoryRepositoryInterface $repository, Category $category)
public function destroy(SCRI $repository, Category $category)
{
$name = $category->name;
@@ -107,17 +108,18 @@ class CategoryController extends Controller
}
/**
* @param CategoryRepositoryInterface $repository
* @param CRI $repository
* @param SCRI $singleRepository
*
* @return \Illuminate\View\View
*/
public function index(CategoryRepositoryInterface $repository)
public function index(CRI $repository, SCRI $singleRepository)
{
$categories = $repository->getCategories();
$categories = $repository->listCategories();
$categories->each(
function (Category $category) use ($repository) {
$category->lastActivity = $repository->getLatestActivity($category);
function (Category $category) use ($singleRepository) {
$category->lastActivity = $singleRepository->getLatestActivity($category);
}
);
@@ -125,15 +127,15 @@ class CategoryController extends Controller
}
/**
* @param CategoryRepositoryInterface $repository
* @param CRI $repository
*
* @return \Illuminate\View\View
*/
public function noCategory(CategoryRepositoryInterface $repository)
public function noCategory(CRI $repository)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->startOfMonth());
$list = $repository->getWithoutCategory($start, $end);
$list = $repository->listNoCategory($start, $end);
$subTitle = trans(
'firefly.without_category_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
@@ -143,37 +145,12 @@ class CategoryController extends Controller
}
/**
* @param CategoryRepositoryInterface $repository
* @param Category $category
* @param SCRI $repository
* @param Category $category
*
* @return \Illuminate\View\View
*/
public function showWithDate(CategoryRepositoryInterface $repository, Category $category, $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$subTitle = $category->name;
$hideCategory = true; // used in list.
$page = intval(Input::get('page'));
$set = $repository->getJournalsInRange($category, $page, $start, $end);
$count = $repository->countJournalsInRange($category, $start, $end);
$journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('categories/show/' . $category->id . '/' . $date);
return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
}
/**
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Illuminate\View\View
*/
public function show(CategoryRepositoryInterface $repository, Category $category)
public function show(SCRI $repository, Category $category)
{
$hideCategory = true; // used in list.
$page = intval(Input::get('page'));
@@ -198,6 +175,12 @@ class CategoryController extends Controller
$cache->addProperty($end);
$cache->addProperty('category-show');
$cache->addProperty($category->id);
// get all spent and earned data:
// get amount earned in period, grouped by day.
$spentArray = $repository->spentPerDay($category, $start, $end);
$earnedArray = $repository->earnedPerDay($category, $start, $end);
if ($cache->has()) {
$entries = $cache->get();
} else {
@@ -206,9 +189,9 @@ class CategoryController extends Controller
$end = Navigation::startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range);
// here do something.
$spent = $repository->spentInPeriod($category, $end, $currentEnd);
$earned = $repository->earnedInPeriod($category, $end, $currentEnd);
// get data from spentArray:
$spent = $this->getSumOfRange($end, $currentEnd, $spentArray);
$earned = $this->getSumOfRange($end, $currentEnd, $earnedArray);
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned]);
@@ -223,12 +206,39 @@ class CategoryController extends Controller
}
/**
* @param CategoryFormRequest $request
* @param CategoryRepositoryInterface $repository
* @param SCRI $repository
* @param Category $category
*
* @param $date
*
* @return \Illuminate\View\View
*/
public function showWithDate(SCRI $repository, Category $category, $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$subTitle = $category->name;
$hideCategory = true; // used in list.
$page = intval(Input::get('page'));
$set = $repository->getJournalsInRange($category, $page, $start, $end);
$count = $repository->countJournalsInRange($category, $start, $end);
$journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('categories/show/' . $category->id . '/' . $date);
return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon'));
}
/**
* @param CategoryFormRequest $request
* @param SCRI $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(CategoryFormRequest $request, CategoryRepositoryInterface $repository)
public function store(CategoryFormRequest $request, SCRI $repository)
{
$categoryData = [
'name' => $request->input('name'),
@@ -250,13 +260,13 @@ class CategoryController extends Controller
/**
* @param CategoryFormRequest $request
* @param CategoryRepositoryInterface $repository
* @param Category $category
* @param CategoryFormRequest $request
* @param SCRI $repository
* @param Category $category
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(CategoryFormRequest $request, CategoryRepositoryInterface $repository, Category $category)
public function update(CategoryFormRequest $request, SCRI $repository, Category $category)
{
$categoryData = [
'name' => $request->input('name'),

View File

@@ -5,7 +5,7 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Preferences;
@@ -20,7 +20,7 @@ use Session;
class AccountController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Account\AccountChartGenerator */
/** @var \FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface */
protected $generator;
/**
@@ -30,50 +30,37 @@ class AccountController extends Controller
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Account\AccountChartGenerator');
$this->generator = app('FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface');
}
/**
* Shows the balances for all the user's accounts.
* Shows the balances for a given set of dates and accounts.
*
* @param AccountRepositoryInterface $repository
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @param $year
* @param $month
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function all(AccountRepositoryInterface $repository, $year, $month, $shared = false)
public function report($reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('all');
$cache->addProperty('accounts');
$cache->addProperty('default');
$cache->addProperty($reportType);
$cache->addProperty($accounts);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
/** @var Collection $accounts */
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
if ($shared === false) {
/** @var Account $account */
foreach ($accounts as $index => $account) {
if ($account->getMeta('accountRole') == 'sharedAsset') {
$accounts->forget($index);
}
}
}
// make chart:
$data = $this->generator->all($accounts, $start, $end);
$data = $this->generator->frontpage($accounts, $start, $end);
$cache->store($data);
return Response::json($data);
@@ -82,11 +69,11 @@ class AccountController extends Controller
/**
* Shows the balances for all the user's expense accounts.
*
* @param AccountRepositoryInterface $repository
* @param ARI $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function expenseAccounts(AccountRepositoryInterface $repository)
public function expenseAccounts(ARI $repository)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = clone Session::get('end', Carbon::now()->endOfMonth());
@@ -112,11 +99,11 @@ class AccountController extends Controller
/**
* Shows the balances for all the user's frontpage accounts.
*
* @param AccountRepositoryInterface $repository
* @param ARI $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(AccountRepositoryInterface $repository)
public function frontpage(ARI $repository)
{
$frontPage = Preferences::get('frontPageAccounts', []);
$start = clone Session::get('start', Carbon::now()->startOfMonth());

View File

@@ -19,7 +19,7 @@ use Session;
class BillController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Bill\BillChartGenerator */
/** @var \FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface */
protected $generator;
/**
@@ -29,11 +29,11 @@ class BillController extends Controller
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Bill\BillChartGenerator');
$this->generator = app('FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface');
}
/**
* Shows all bills and whether or not theyve been paid this month (pie chart).
* Shows all bills and whether or not they've been paid this month (pie chart).
*
* @param BillRepositoryInterface $repository
*
@@ -41,28 +41,24 @@ class BillController extends Controller
*/
public function frontpage(BillRepositoryInterface $repository)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$cache = new CacheProperties(); // chart properties for cache:
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('bills');
$cache->addProperty('frontpage');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$paid = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
$unpaid = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
$creditCardDue = $repository->getCreditCardBill($start, $end);
if ($creditCardDue < 0) {
// expenses are negative (bill not yet paid),
$creditCardDue = bcmul($creditCardDue, '-1');
$unpaid = bcadd($unpaid, $creditCardDue);
} else {
// if more than zero, the bill has been paid: (transfer = positive).
// amount must be negative to be added to $paid:
$paid = bcadd($paid, $creditCardDue);
}
$set = $repository->getBillsForChart($start, $end);
// optionally expand this set with credit card data
$set = $repository->getCreditCardInfoForChart($set, $start, $end);
$paid = $set->get('paid');
$unpaid = $set->get('unpaid');
// build chart:
$data = $this->generator->frontpage($paid, $unpaid);
$cache->store($data);
return Response::json($data);
}

View File

@@ -6,6 +6,7 @@ use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
@@ -22,7 +23,7 @@ use Session;
class BudgetController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Budget\BudgetChartGenerator */
/** @var \FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface */
protected $generator;
/**
@@ -32,7 +33,88 @@ class BudgetController extends Controller
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator');
$this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface');
}
/**
*
* @param BudgetRepositoryInterface $repository
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Collection $budgets
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList) // need all parameters
*
* @return \Illuminate\Http\JsonResponse
*/
public function multiYear(BudgetRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets)
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($reportType);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty($budgets);
$cache->addProperty('multiYearBudget');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
/*
* Get the budgeted amounts for each budgets in each year.
*/
$budgetedSet = $repository->getBudgetedPerYear($budgets, $start, $end);
$budgetedArray = [];
/** @var Budget $entry */
foreach ($budgetedSet as $entry) {
$budgetedArray[$entry->id][$entry->dateFormatted] = $entry->budgeted;
}
$set = $repository->getBudgetsAndExpensesPerYear($budgets, $accounts, $start, $end);
$entries = new Collection;
// go by budget, not by year.
/** @var Budget $budget */
foreach ($budgets as $budget) {
$entry = ['name' => '', 'spent' => [], 'budgeted' => []];
$id = $budget->id;
$currentStart = clone $start;
while ($currentStart < $end) {
// fix the date:
$currentEnd = clone $currentStart;
$currentEnd->endOfYear();
// save to array:
$year = $currentStart->year;
$entry['name'] = $budget->name;
$spent = 0;
$budgeted = 0;
if (isset($set[$id]['entries'][$year])) {
$spent = $set[$id]['entries'][$year] * -1;
}
if (isset($budgetedArray[$id][$year])) {
$budgeted = round($budgetedArray[$id][$year], 2);
}
$entry['spent'][$year] = $spent;
$entry['budgeted'][$year] = $budgeted;
// jump to next year.
$currentStart = clone $currentEnd;
$currentStart->addDay();
}
$entries->push($entry);
}
// generate chart with data:
$data = $this->generator->multiYear($entries);
$cache->store($data);
return Response::json($data);
}
/**
@@ -48,9 +130,6 @@ class BudgetController extends Controller
$first = $repository->getFirstBudgetLimitDate($budget);
$range = Preferences::get('viewRange', '1M')->data;
$last = Session::get('end', new Carbon);
$final = clone $last;
$final->addYears(2);
$last = Navigation::endOfX($last, $range, $final);
// chart properties for cache:
$cache = new CacheProperties();
@@ -58,18 +137,29 @@ class BudgetController extends Controller
$cache->addProperty($last);
$cache->addProperty('budget');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$final = clone $last;
$final->addYears(2);
$last = Navigation::endOfX($last, $range, $final);
$entries = new Collection;
// get all expenses:
$set = $repository->getExpensesPerMonth($budget, $first, $last);
while ($first < $last) {
$end = Navigation::addPeriod($first, $range, 0);
$end->subDay();
$chartDate = clone $end;
$chartDate->startOfMonth();
$spent = $repository->balanceInPeriod($budget, $first, $end) * -1;
$entries->push([$chartDate, $spent]);
$monthFormatted = $first->format('Y-m');
$filtered = $set->filter(
function (Budget $obj) use ($monthFormatted) {
return $obj->dateFormatted == $monthFormatted;
}
);
$spent = is_null($filtered->first()) ? '0' : $filtered->first()->monthlyAmount;
$entries->push([$first, round(($spent * -1), 2)]);
$first = Navigation::addPeriod($first, $range, 0);
}
@@ -106,15 +196,24 @@ class BudgetController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore
}
$set = $repository->getExpensesPerDay($budget, $start, $end);
$entries = new Collection;
$amount = $repetition->amount;
// get sum (har har)!
while ($start <= $end) {
$formatted = $start->format('Y-m-d');
$filtered = $set->filter(
function (Budget $obj) use ($formatted) {
return $obj->date == $formatted;
}
);
$sum = is_null($filtered->first()) ? '0' : $filtered->first()->dailyAmount;
/*
* Sum of expenses on this day:
*/
$sum = $repository->expensesOnDayCorrected($budget, $start);
$amount = bcadd($amount, $sum);
$amount = round(bcadd($amount, $sum), 2);
$entries->push([clone $start, $amount]);
$start->addDay();
}
@@ -131,14 +230,14 @@ class BudgetController extends Controller
*
* @param BudgetRepositoryInterface $repository
*
* @param ARI $accountRepository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(BudgetRepositoryInterface $repository)
public function frontpage(BudgetRepositoryInterface $repository, ARI $accountRepository)
{
$budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$allEntries = new Collection;
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
// chart properties for cache:
$cache = new CacheProperties();
@@ -150,98 +249,103 @@ class BudgetController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore
}
$budgets = $repository->getBudgetsAndLimitsInRange($start, $end);
$allEntries = new Collection;
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
bcscale(2);
/** @var Budget $budget */
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->balanceInPeriod($budget, $start, $end, true) * -1;
$allEntries->push([$budget->name, 0, 0, $expenses, 0, 0]);
continue;
// we already have amount, startdate and enddate.
// if this "is" a limit repetition (as opposed to a budget without one entirely)
// depends on whether startdate and enddate are null.
$name = $budget->name;
if (is_null($budget->startdate) && is_null($budget->enddate)) {
$currentStart = clone $start;
$currentEnd = clone $end;
$expenses = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts);
$amount = 0;
$left = 0;
$spent = $expenses;
$overspent = 0;
} else {
$currentStart = clone $budget->startdate;
$currentEnd = clone $budget->enddate;
$expenses = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts);
$amount = $budget->amount;
// smaller than 1 means spent MORE than budget allows.
$left = bccomp(bcadd($budget->amount, $expenses), '0') < 1 ? 0 : bcadd($budget->amount, $expenses);
$spent = bccomp(bcadd($budget->amount, $expenses), '0') < 1 ? ($amount * -1) : $expenses;
$overspent = bccomp(bcadd($budget->amount, $expenses), '0') < 1 ? bcadd($budget->amount, $expenses) : 0;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, true) * -1;
// $left can be less than zero.
// $overspent can be more than zero ( = overspending)
$left = max(bcsub($repetition->amount, $expenses), 0); // limited at zero.
$overspent = max(bcsub($expenses, $repetition->amount), 0); // limited at zero.
$name = $budget->name;
// $spent is maxed to the repetition amount:
$spent = $expenses > $repetition->amount ? $repetition->amount : $expenses;
$allEntries->push([$name, $left, $spent, $overspent, $repetition->amount, $expenses]);
}
$allEntries->push([$name, $left, $spent, $overspent, $amount, $expenses]);
}
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end) * -1;
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push([trans('firefly.noBudget'), 0, 0, $noBudgetExpenses, 0, 0]);
$data = $this->generator->frontpage($allEntries);
$cache->store($data);
return Response::json($data);
}
/**
* Show a yearly overview for a budget.
*
* @param BudgetRepositoryInterface $repository
* @param $year
* @param bool $shared
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function year(BudgetRepositoryInterface $repository, $year, $shared = false)
public function year(BudgetRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$allBudgets = $repository->getBudgets();
$budgets = new Collection;
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($reportType);
$cache->addProperty($accounts);
$cache->addProperty('budget');
$cache->addProperty('year');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
// filter empty budgets:
$budgetInformation = $repository->getBudgetsAndExpensesPerMonth($accounts, $start, $end);
$budgets = new Collection;
$entries = new Collection;
foreach ($allBudgets as $budget) {
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
if ($spent != 0) {
$budgets->push($budget);
}
/** @var array $row */
foreach ($budgetInformation as $row) {
$budgets->push($row['budget']);
}
$entries = new Collection;
while ($start < $end) {
// month is the current end of the period:
$month = clone $start;
$month->endOfMonth();
$row = [clone $start];
$row = [clone $start];
$dateFormatted = $start->format('Y-m');
// each budget, fill the row:
foreach ($budgets as $budget) {
$spent = $repository->balanceInPeriod($budget, $start, $month, $shared);
$row[] = $spent * -1;
// each budget, check if there is an entry for this month:
/** @var array $row */
foreach ($budgetInformation as $budgetRow) {
$spent = 0; // nothing spent.
if (isset($budgetRow['entries'][$dateFormatted])) {
$spent = $budgetRow['entries'][$dateFormatted] * -1; // to fit array
}
$row[] = $spent;
}
$entries->push($row);
$start->endOfMonth()->addDay();
}
$data = $this->generator->year($allBudgets, $entries);
$data = $this->generator->year($budgets, $entries);
$cache->store($data);
return Response::json($data);

View File

@@ -6,13 +6,15 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
use FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface as SCRI;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
use Session;
use stdClass;
/**
* Class CategoryController
@@ -21,7 +23,7 @@ use Session;
*/
class CategoryController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Category\CategoryChartGenerator */
/** @var \FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface */
protected $generator;
/**
@@ -31,19 +33,21 @@ class CategoryController extends Controller
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Category\CategoryChartGenerator');
$this->generator = app('FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface');
}
/**
* Show an overview for a category for all time, per month/week/year.
*
* @param CategoryRepositoryInterface $repository
* @param Category $category
* @param SCRI $repository
* @param Category $category
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function all(CategoryRepositoryInterface $repository, Category $category)
public function all(SCRI $repository, Category $category)
{
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
@@ -51,8 +55,6 @@ class CategoryController extends Controller
$start = Navigation::startOfPeriod($start, $range);
$end = new Carbon;
$entries = new Collection;
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
@@ -62,36 +64,116 @@ class CategoryController extends Controller
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$spentArray = $repository->spentPerDay($category, $start, $end);
$earnedArray = $repository->earnedPerDay($category, $start, $end);
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriod($category, $start, $currentEnd);
$earned = $repository->earnedInPeriod($category, $start, $currentEnd);
$spent = $this->getSumOfRange($start, $currentEnd, $spentArray);
$earned = $this->getSumOfRange($start, $currentEnd, $earnedArray);
$date = Navigation::periodShow($start, $range);
$entries->push([clone $start, $date, $spent, $earned]);
$start = Navigation::addPeriod($start, $range, 0);
}
// limit the set to the last 40:
$entries = $entries->reverse();
$entries = $entries->slice(0, 48);
$entries = $entries->reverse();
$data = $this->generator->all($entries);
$data = $this->generator->all($entries);
$cache->store($data);
return Response::json($data);
}
/**
* @param SCRI $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function currentPeriod(SCRI $repository, Category $category)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$data = $this->makePeriodChart($repository, $category, $start, $end);
return Response::json($data);
}
/**
* Returns a chart of what has been earned in this period in each category
* grouped by month.
*
* @param CRI $repository
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList) // cant avoid it.
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5.
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // it's long but ok.
*
* @return \Illuminate\Http\JsonResponse
*/
public function earnedInPeriod(CRI $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($reportType);
$cache->addProperty($accounts);
$cache->addProperty('category');
$cache->addProperty('earned-in-period');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$set = $repository->earnedForAccountsPerMonth($accounts, $start, $end);
$categories = $set->unique('id')->sortBy(
function (Category $category) {
return $category->name;
}
);
$entries = new Collection;
while ($start < $end) { // filter the set:
$row = [clone $start];
$currentSet = $set->filter( // get possibly relevant entries from the big $set
function (Category $category) use ($start) {
return $category->dateFormatted == $start->format('Y-m');
}
);
/** @var Category $category */
foreach ($categories as $category) { // check for each category if its in the current set.
$entry = $currentSet->filter( // if its in there, use the value.
function (Category $cat) use ($category) {
return ($cat->id == $category->id);
}
)->first();
if (!is_null($entry)) {
$row[] = round($entry->earned, 2);
} else {
$row[] = 0;
}
}
$entries->push($row);
$start->addMonth();
}
$data = $this->generator->earnedInPeriod($categories, $entries);
$cache->store($data);
return $data;
}
/**
* Show this month's category overview.
*
* @param CategoryRepositoryInterface $repository
* @param CRI $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(CategoryRepositoryInterface $repository)
public function frontpage(CRI $repository)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
@@ -107,52 +189,225 @@ class CategoryController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore
}
$array = $repository->getCategoriesAndExpensesCorrected($start, $end);
// sort by callback:
uasort(
$array,
function ($left, $right) {
if ($left['sum'] == $right['sum']) {
return 0;
}
// get data for categories (and "no category"):
$set = $repository->spentForAccountsPerMonth(new Collection, $start, $end);
$outside = $repository->sumSpentNoCategory(new Collection, $start, $end);
return ($left['sum'] < $right['sum']) ? -1 : 1;
}
);
$set = new Collection($array);
// this is a "fake" entry for the "no category" entry.
$entry = new stdClass();
$entry->name = trans('firefly.no_category');
$entry->spent = $outside;
$set->push($entry);
$set = $set->sortBy('spent');
$data = $this->generator->frontpage($set);
$cache->store($data);
return Response::json($data);
}
/**
* @param CategoryRepositoryInterface $repository
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Collection $categories
*
* @return \Illuminate\Http\JsonResponse
*/
public function multiYear($reportType, Carbon $start, Carbon $end, Collection $accounts, Collection $categories)
{
/** @var CRI $repository */
$repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($reportType);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty('multiYearCategory');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
$set = $repository->listMultiYear($categories, $accounts, $start, $end);
/** @var Category $category */
foreach ($categories as $category) {
$entry = ['name' => '', 'spent' => [], 'earned' => []];
$currentStart = clone $start;
while ($currentStart < $end) {
// fix the date:
$year = $currentStart->year;
$currentEnd = clone $currentStart;
$currentEnd->endOfYear();
// get data:
if (is_null($category->id)) {
$name = trans('firefly.noCategory');
$spent = $repository->sumSpentNoCategory($accounts, $currentStart, $currentEnd);
$earned = $repository->sumEarnedNoCategory($accounts, $currentStart, $currentEnd);
} else {
// get from set:
$entrySpent = $set->filter(
function (Category $cat) use ($year, $category) {
return ($cat->type == 'Withdrawal' && $cat->dateFormatted == $year && $cat->id == $category->id);
}
)->first();
$entryEarned = $set->filter(
function (Category $cat) use ($year, $category) {
return ($cat->type == 'Deposit' && $cat->dateFormatted == $year && $cat->id == $category->id);
}
)->first();
$name = $category->name;
$spent = !is_null($entrySpent) ? $entrySpent->sum : 0;
$earned = !is_null($entryEarned) ? $entryEarned->sum : 0;
}
// save to array:
$entry['name'] = $name;
$entry['spent'][$year] = ($spent * -1);
$entry['earned'][$year] = $earned;
// jump to next year.
$currentStart = clone $currentEnd;
$currentStart->addDay();
}
$entries->push($entry);
}
// generate chart with data:
$data = $this->generator->multiYear($entries);
$cache->store($data);
return Response::json($data);
}
/**
* @param SCRI $repository
* @param Category $category
*
* @param $date
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function currentPeriod(CategoryRepositoryInterface $repository, Category $category)
public function specificPeriod(SCRI $repository, Category $category, $date)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$data = $this->makePeriodChart($repository, $category, $start, $end);
return Response::json($data);
}
/**
* Returns a chart of what has been spent in this period in each category
* grouped by month.
*
* @param CRI $repository
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList) // need all parameters
* @SuppressWarnings(PHPMD.ExcessuveMethodLength) // need the length
*
* @return \Illuminate\Http\JsonResponse
*/
public function spentInPeriod(CRI $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($reportType);
$cache->addProperty($accounts);
$cache->addProperty('category');
$cache->addProperty('spent-in-period');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$set = $repository->spentForAccountsPerMonth($accounts, $start, $end);
$categories = $set->unique('id')->sortBy(
function (Category $category) {
return $category->name;
}
);
$entries = new Collection;
while ($start < $end) { // filter the set:
$row = [clone $start];
$currentSet = $set->filter(// get possibly relevant entries from the big $set
function (Category $category) use ($start) {
return $category->dateFormatted == $start->format('Y-m');
}
);
/** @var Category $category */
foreach ($categories as $category) {// check for each category if its in the current set.
$entry = $currentSet->filter(// if its in there, use the value.
function (Category $cat) use ($category) {
return ($cat->id == $category->id);
}
)->first();
if (!is_null($entry)) {
$row[] = round(($entry->spent * -1), 2);
} else {
$row[] = 0;
}
}
$entries->push($row);
$start->addMonth();
}
$data = $this->generator->spentInPeriod($categories, $entries);
$cache->store($data);
return $data;
}
/**
* @param SCRI $repository
* @param Category $category
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function makePeriodChart(SCRI $repository, Category $category, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($category->id);
$cache->addProperty('category');
$cache->addProperty('currentPeriod');
$cache->addProperty('specific-period');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
return $cache->get(); // @codeCoverageIgnore
}
$entries = new Collection;
// get amount earned in period, grouped by day.
// get amount spent in period, grouped by day.
$spentArray = $repository->spentPerDay($category, $start, $end);
$earnedArray = $repository->earnedPerDay($category, $start, $end);
while ($start <= $end) {
$spent = $repository->spentOnDaySumCorrected($category, $start);
$earned = $repository->earnedOnDaySumCorrected($category, $start);
$str = $start->format('Y-m-d');
$spent = isset($spentArray[$str]) ? $spentArray[$str] : 0;
$earned = isset($earnedArray[$str]) ? $earnedArray[$str] : 0;
$date = Navigation::periodShow($start, '1D');
$entries->push([clone $start, $date, $spent, $earned]);
$start->addDay();
@@ -161,169 +416,8 @@ class CategoryController extends Controller
$data = $this->generator->period($entries);
$cache->store($data);
return Response::json($data);
return $data;
}
/**
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, $date)
{
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($category->id);
$cache->addProperty('category');
$cache->addProperty('specificPeriod');
$cache->addProperty($date);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
while ($start <= $end) {
$spent = $repository->spentOnDaySumCorrected($category, $start);
$earned = $repository->earnedOnDaySumCorrected($category, $start);
$theDate = Navigation::periodShow($start, '1D');
$entries->push([clone $start, $theDate, $spent, $earned]);
$start->addDay();
}
$data = $this->generator->period($entries);
$cache->store($data);
return Response::json($data);
}
/**
* This chart will only show expenses.
*
* @param CategoryRepositoryInterface $repository
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function spentInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category');
$cache->addProperty('spent-in-year');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$shared = $shared == 'shared' ? true : false;
$allCategories = $repository->getCategories();
$entries = new Collection;
$categories = $allCategories->filter(
function (Category $category) use ($repository, $start, $end, $shared) {
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
if ($spent < 0) {
return $category;
}
return null;
}
);
while ($start < $end) {
$month = clone $start; // month is the current end of the period
$month->endOfMonth();
$row = [clone $start]; // make a row:
foreach ($categories as $category) { // each budget, fill the row
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
if ($spent < 0) {
$row[] = $spent * -1;
} else {
$row[] = 0;
}
}
$entries->push($row);
$start->addMonth();
}
$data = $this->generator->spentInYear($categories, $entries);
$cache->store($data);
return Response::json($data);
}
/**
* This chart will only show income.
*
* @param CategoryRepositoryInterface $repository
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function earnedInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category');
$cache->addProperty('earned-in-year');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$shared = $shared == 'shared' ? true : false;
$allCategories = $repository->getCategories();
$allEntries = new Collection;
$categories = $allCategories->filter(
function (Category $category) use ($repository, $start, $end, $shared) {
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
if ($spent > 0) {
return $category;
}
return null;
}
);
while ($start < $end) {
$month = clone $start; // month is the current end of the period
$month->endOfMonth();
$row = [clone $start]; // make a row:
foreach ($categories as $category) { // each budget, fill the row
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
if ($spent > 0) {
$row[] = $spent;
} else {
$row[] = 0;
}
}
$allEntries->push($row);
$start->addMonth();
}
$data = $this->generator->earnedInYear($categories, $allEntries);
$cache->store($data);
return Response::json($data);
}
}

View File

@@ -18,7 +18,7 @@ use Response;
class PiggyBankController extends Controller
{
/** @var \FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator */
/** @var \FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGeneratorInterface */
protected $generator;
/**
@@ -28,7 +28,7 @@ class PiggyBankController extends Controller
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator');
$this->generator = app('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGeneratorInterface');
}
/**

View File

@@ -9,7 +9,6 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Response;
use Log;
/**
* Class ReportController
@@ -19,7 +18,7 @@ use Log;
class ReportController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Report\ReportChartGenerator */
/** @var \FireflyIII\Generator\Chart\Report\ReportChartGeneratorInterface */
protected $generator;
/**
@@ -29,7 +28,7 @@ class ReportController extends Controller
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Report\ReportChartGenerator');
$this->generator = app('FireflyIII\Generator\Chart\Report\ReportChartGeneratorInterface');
}
@@ -37,40 +36,41 @@ class ReportController extends Controller
* Summarizes all income and expenses, per month, for a given year.
*
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
* @SuppressWarnings(PHPMD.ExcessiveParameterList) // cant avoid it.
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInOut(ReportQueryInterface $query, $year, $shared = false)
public function yearInOut(ReportQueryInterface $query, $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
// get start and end of year
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('yearInOut');
$cache->addProperty($year);
$cache->addProperty($shared);
$cache->addProperty($start);
$cache->addProperty($reportType);
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$incomeSum = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
$expenseSum = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
// spent per month, and earned per month. For a specific set of accounts
// grouped by month
$spentArray = $query->spentPerMonth($accounts, $start, $end);
$earnedArray = $query->earnedPerMonth($accounts, $start, $end);
$entries->push([clone $start, $incomeSum, $expenseSum]);
$start->addMonth();
if ($start->diffInMonths($end) > 12) {
// data = method X
$data = $this->multiYearInOut($earnedArray, $spentArray, $start, $end);
} else {
// data = method Y
$data = $this->singleYearInOut($earnedArray, $spentArray, $start, $end);
}
$data = $this->generator->yearInOut($entries);
$cache->store($data);
return Response::json($data);
@@ -81,56 +81,170 @@ class ReportController extends Controller
* Summarizes all income and expenses for a given year. Gives a total and an average.
*
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
* @param $reportType
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function yearInOutSummarized(ReportQueryInterface $query, $year, $shared = false)
public function yearInOutSummarized(ReportQueryInterface $query, $reportType, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('yearInOutSummarized');
$cache->addProperty($year);
$cache->addProperty($shared);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($reportType);
$cache->addProperty($accounts);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
// spent per month, and earned per month. For a specific set of accounts
// grouped by month
$spentArray = $query->spentPerMonth($accounts, $start, $end);
$earnedArray = $query->earnedPerMonth($accounts, $start, $end);
if ($start->diffInMonths($end) > 12) {
// per year
$data = $this->multiYearInOutSummarized($earnedArray, $spentArray, $start, $end);
} else {
// per month!
$data = $this->singleYearInOutSummarized($earnedArray, $spentArray, $start, $end);
}
$cache->store($data);
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
return Response::json($data);
}
/**
* @param array $earned
* @param array $spent
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function singleYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end)
{
$income = '0';
$expense = '0';
$count = 0;
bcscale(2);
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$currentIncome = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
$currentExpense = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
Log::debug('Date ['.$month->format('M Y').']: income = ['.$income.' + '.$currentIncome.'], out = ['.$expense.' + '.$currentExpense.']');
$income = bcadd($income, $currentIncome);
$expense = bcadd($expense, $currentExpense);
$date = $start->format('Y-m');
$currentIncome = isset($earned[$date]) ? $earned[$date] : 0;
$currentExpense = isset($spent[$date]) ? ($spent[$date] * -1) : 0;
$income = bcadd($income, $currentIncome);
$expense = bcadd($expense, $currentExpense);
$count++;
$start->addMonth();
}
$data = $this->generator->yearInOutSummarized($income, $expense, $count);
$cache->store($data);
return Response::json($data);
return $data;
}
/**
* @param array $earned
* @param array $spent
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function multiYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end)
{
$income = '0';
$expense = '0';
$count = 0;
while ($start < $end) {
$currentIncome = $this->pluckFromArray($start->year, $earned);
$currentExpense = $this->pluckFromArray($start->year, $spent) * -1;
$income = bcadd($income, $currentIncome);
$expense = bcadd($expense, $currentExpense);
$count++;
$start->addYear();
}
$data = $this->generator->multiYearInOutSummarized($income, $expense, $count);
return $data;
}
/**
* @param array $earned
* @param array $spent
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function multiYearInOut(array $earned, array $spent, Carbon $start, Carbon $end)
{
$entries = new Collection;
while ($start < $end) {
$incomeSum = $this->pluckFromArray($start->year, $earned);
$expenseSum = $this->pluckFromArray($start->year, $spent) * -1;
$entries->push([clone $start, $incomeSum, $expenseSum]);
$start->addYear();
}
$data = $this->generator->multiYearInOut($entries);
return $data;
}
/**
* @param array $earned
* @param array $spent
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function singleYearInOut(array $earned, array $spent, Carbon $start, Carbon $end)
{
// per month? simply use each month.
$entries = new Collection;
while ($start < $end) {
// total income and total expenses:
$date = $start->format('Y-m');
$incomeSum = isset($earned[$date]) ? $earned[$date] : 0;
$expenseSum = isset($spent[$date]) ? ($spent[$date] * -1) : 0;
$entries->push([clone $start, $incomeSum, $expenseSum]);
$start->addMonth();
}
$data = $this->generator->yearInOut($entries);
return $data;
}
/**
* @param int $year
* @param array $set
*
* @return string
*/
protected function pluckFromArray($year, array $set)
{
bcscale(2);
$sum = '0';
foreach ($set as $date => $amount) {
if (substr($date, 0, 4) == $year) {
$sum = bcadd($sum, $amount);
}
}
return $sum;
}
}

View File

@@ -1,10 +1,15 @@
<?php namespace FireflyIII\Http\Controllers;
<?php
namespace FireflyIII\Http\Controllers;
use App;
use Auth;
use Config;
use Carbon\Carbon;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use NumberFormatter;
use Preferences;
use View;
@@ -13,10 +18,9 @@ use View;
*
* @package FireflyIII\Http\Controllers
*/
abstract class Controller extends BaseController
class Controller extends BaseController
{
use DispatchesJobs, ValidatesRequests;
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
/** @var string */
protected $monthAndDayFormat;
@@ -24,7 +28,7 @@ abstract class Controller extends BaseController
protected $monthFormat;
/**
* @codeCoverageIgnore
* Controller constructor.
*/
public function __construct()
{
@@ -34,14 +38,60 @@ abstract class Controller extends BaseController
View::share('hideTags', false);
if (Auth::check()) {
$pref = Preferences::get('language', 'en');
$pref = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'));
$lang = $pref->data;
$this->monthFormat = Config::get('firefly.month.' . $lang);
$this->monthAndDayFormat = Config::get('firefly.monthAndDay.' . $lang);
$this->monthFormat = (string)trans('config.month');
$this->monthAndDayFormat = (string)trans('config.month_and_day');
App::setLocale($lang);
Carbon::setLocale(substr($lang, 0, 2));
$locale = explode(',', trans('config.locale'));
$locale = array_map('trim', $locale);
setlocale(LC_TIME, $locale);
setlocale(LC_MONETARY, $locale);
// change localeconv to a new array:
$numberFormatter = numfmt_create($lang, NumberFormatter::CURRENCY);
$localeconv = [
'mon_decimal_point' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL)),
'mon_thousands_sep' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL)),
'frac_digits' => $numberFormatter->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS),
];
View::share('monthFormat', $this->monthFormat);
View::share('monthAndDayFormat', $this->monthAndDayFormat);
View::share('language', $lang);
View::share('localeconv', $localeconv);
}
}
/**
* Take the array as returned by SingleCategoryRepositoryInterface::spentPerDay and SingleCategoryRepositoryInterface::earnedByDay
* and sum up everything in the array in the given range.
*
* @param Carbon $start
* @param Carbon $end
* @param array $array
*
* @return string
*/
protected function getSumOfRange(Carbon $start, Carbon $end, array $array)
{
bcscale(2);
$sum = '0';
$currentStart = clone $start; // to not mess with the original one
$currentEnd = clone $end; // to not mess with the original one
while ($currentStart <= $currentEnd) {
$date = $currentStart->format('Y-m-d');
if (isset($array[$date])) {
$sum = bcadd($sum, $array[$date]);
}
$currentStart->addDay();
}
return $sum;
}
}

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