Compare commits

...

810 Commits
3.4.1 ... 3.5.6

Author SHA1 Message Date
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
James Cole
8b9607f9b5 Merge branch 'release/3.5.2' 2015-10-30 07:23:42 +01:00
James Cole
af107ad5e8 New release. 2015-10-30 07:23:33 +01:00
James Cole
8926d62165 Remake of: Merge pull request #111 from colinodell/patch-1
Loosen league/commonmark version
2015-10-30 07:22:39 +01:00
James Cole
45344ee347 Fixed a bug where budget chart would come up negative on overspent amounts. 2015-10-30 07:16:15 +01:00
RonaldvanMeer
baf9ebab15 Update control-bar.twig 2015-10-27 12:31:21 +01:00
James Cole
b7dab817f2 Merge pull request #111 from colinodell/patch-1
Loosen league/commonmark version
2015-10-27 12:17:25 +01:00
RonaldvanMeer
fb2fa54480 Merge pull request #1 from RonaldvanMeer/master
Duplicate controlbar item
2015-10-26 22:45:38 +01:00
James Cole
2c966a1234 Fixed ambiguous column. 2015-10-19 15:21:17 +02:00
James Cole
143ea69c0d Fixed the fix (fixeption). 2015-10-19 15:20:18 +02:00
James Cole
8a02ead013 Blind validator update. 2015-10-19 15:16:30 +02:00
James Cole
930d5ab941 Better fix. 2015-10-17 21:15:16 +02:00
James Cole
e54a56d3a8 Experimental chart fix. 2015-10-17 21:14:25 +02:00
James Cole
f5216c0d85 Fixed absolute uri 2015-10-05 20:30:57 +02:00
James Cole
ebc77540b9 Fix some reports. 2015-10-03 22:49:12 +02:00
James Cole
28d2583c10 Remove multiplication. 2015-10-03 22:34:25 +02:00
James Cole
b0988a7b00 Fix zero division. 2015-10-03 22:32:36 +02:00
James Cole
2c4920db2d Fix amount thing. 2015-10-03 22:31:55 +02:00
James Cole
8321663815 Some amount fixes. 2015-10-03 22:29:34 +02:00
James Cole
ac0280d460 Fixed some amounts. 2015-10-03 22:23:42 +02:00
James Cole
ba6f4268f0 Get correct fields. 2015-10-03 17:55:08 +02:00
James Cole
0bd18f94ac Fix ambiguous id. 2015-10-03 17:54:15 +02:00
James Cole
66fb63661f Less journals. 2015-10-03 17:53:13 +02:00
James Cole
d9b16beb0a Added some debug. 2015-10-03 17:51:13 +02:00
James Cole
6a01a9bdfd Fix bill scan. 2015-10-01 07:48:50 +02:00
James Cole
b81b34a706 Fixed amount in bill chart. 2015-10-01 07:46:47 +02:00
James Cole
3750a00b5f Fix amount, make it positive. 2015-09-30 19:43:02 +02:00
James Cole
05ddcc169d Fix amount thing. 2015-09-30 19:41:21 +02:00
James Cole
f571a5f1bd Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Fixed the other chart too.
  Trying to fix chart.
  New code. Debug.
2015-09-30 19:32:58 +02:00
James Cole
48b786adea Some fixes for amounts. 2015-09-30 19:31:21 +02:00
James Cole
d691fa9b4d Fixed the other chart too. 2015-09-29 09:29:28 +02:00
James Cole
da50f9e419 Trying to fix chart. 2015-09-29 09:28:16 +02:00
James Cole
103de5e18a New code. Debug. 2015-09-29 09:20:50 +02:00
James Cole
dac6efd98b Fix amount in box. 2015-09-28 21:45:11 +02:00
James Cole
46aa7f81b2 Fixed a method call. 2015-09-28 20:41:53 +02:00
James Cole
0683c7cd67 Fixed chart 2015-09-28 14:43:08 +02:00
James Cole
50d7aa7b6a Fix so chart will not display negatives. 2015-09-28 14:32:12 +02:00
James Cole
b781215d0a Date range fixes. 2015-09-27 17:45:01 +02:00
James Cole
866bc2f3bd Cleanup in budgets. 2015-09-27 17:44:49 +02:00
James Cole
49d4705014 Chart was negative. 2015-09-27 09:52:02 +02:00
James Cole
9163fcfccb Quicker amount thing. 2015-09-27 09:47:13 +02:00
James Cole
7e10641461 Organized some category charts. Still needs some translating 2015-09-26 07:18:12 +02:00
James Cole
cdc0e3cfd8 Some cleaning up. 2015-09-25 20:40:24 +02:00
James Cole
466e81d56a Fixed some sorting. 2015-09-25 20:25:22 +02:00
James Cole
5953f691d1 More should fit. 2015-09-25 20:15:38 +02:00
James Cole
e2790ca6c1 Try to limit the collection returned to the user. 2015-09-25 20:13:43 +02:00
James Cole
66dbd48b76 Fix something with the dates. 2015-09-25 19:36:03 +02:00
James Cole
73cfbbd2ba Fix some things so the date range has a better display. 2015-09-25 19:28:39 +02:00
James Cole
38bc38bf26 Add some period things. 2015-09-25 17:28:42 +02:00
James Cole
e12d13c838 Fixed a negative chart. 2015-09-25 16:43:34 +02:00
James Cole
fcc3af6136 Fixed something. 2015-09-25 16:38:36 +02:00
James Cole
491298e1cb Some fixes in titles and bread crumbs. 2015-09-25 16:33:45 +02:00
James Cole
72b5895217 Added a subtitle to the category view. 2015-09-25 16:25:17 +02:00
James Cole
0a8f4017bd Fix some chart things. Again. 2015-09-25 16:18:50 +02:00
James Cole
cb985f5897 Make sure the month chart can show the spent / earned. 2015-09-25 16:08:10 +02:00
James Cole
968ec0853f Fixed the chart. Forgot an argument. 2015-09-25 16:06:06 +02:00
James Cole
0bde72d3df Spent should not be negative for the chart. 2015-09-25 16:04:22 +02:00
James Cole
45293fbd42 Fixes for #109. 2015-09-25 16:00:14 +02:00
Colin O'Dell
72997065f0 Loosen league/commonmark version
See https://github.com/thephpleague/commonmark#versioning
2015-09-19 10:49:55 -04:00
James Cole
a838dc163d I need unit tests again. #109 2015-09-14 17:22:31 +02:00
James Cole
3d15a4ca6d I need unit tests again. #109 2015-09-14 17:20:20 +02:00
James Cole
51c7d4fb1b Properly show spent and earned. #109 2015-09-13 07:40:37 +02:00
James Cole
fa586dba7e Also include all types. 2015-09-13 07:35:53 +02:00
James Cole
8ad40389f2 Corrected the field used for bug #109 2015-09-13 07:34:58 +02:00
James Cole
b3333cc2d3 Experimental fix for issue #109. 2015-09-13 07:32:39 +02:00
James Cole
3699a7ba9a Some cleaning up. 2015-09-08 19:38:04 +02:00
James Cole
204e521ba4 Fixed sorting for contracts. 2015-09-08 19:36:54 +02:00
James Cole
c9bab3e5c3 Tag list show sum. 2015-08-29 08:43:18 +02:00
James Cole
3d00e20238 Fixed a translation. 2015-08-29 08:35:11 +02:00
James Cole
c3958ed3c4 Fixed some bugs that caused inconsistencies in the monthly reports. 2015-08-29 07:20:53 +02:00
James Cole
e5b88be5fa Fix a bug where an transfer to and from a shared asset would count as an expense/income. 2015-08-29 06:48:12 +02:00
James Cole
425552988a Fix month report so transfers from a shared account to another shared account do not count as income. 2015-08-28 06:19:50 +02:00
James Cole
a81dd8abe5 Send search engine spiders away, as suggested in issue #105 2015-08-24 20:11:04 +02:00
James Cole
bac8154a5b Merge branch 'release/3.5.1' 2015-08-23 07:43:02 +02:00
James Cole
737d15fa0e Merge branch 'release/3.5.1' into develop 2015-08-23 07:43:02 +02:00
James Cole
5f2317af7f New version 2015-08-23 07:42:32 +02:00
James Cole
2bd1f783e5 Cleaned up some translations. 2015-08-23 07:41:12 +02:00
James Cole
d6c0c9f963 Cleanup composer. 2015-08-23 07:10:02 +02:00
James Cole
21b6ad7a41 More checks in the bounce cron job. 2015-08-19 19:21:39 +02:00
James Cole
a65d609fdc Fixed bug #104 2015-08-16 21:18:12 +02:00
James Cole
04e676b936 Fix bug #103 2015-08-16 20:27:25 +02:00
James Cole
be8eaaffdf Some code cleanup. 2015-08-16 20:26:11 +02:00
James Cole
28c753523f Caught a nasty bug thanks to an alert Tweakers.net user. This fix will make the account-edit screen inconsistent for a select number of users. This will be detailed on the wiki. 2015-08-15 21:45:29 +02:00
James Cole
7fbd0b2ffc Fixed some inconsistencies in the delete-form. 2015-08-15 21:44:29 +02:00
James Cole
7ffb48a87a New translations 2015-08-15 21:42:45 +02:00
James Cole
d485270e1f Merge branch 'release/3.5.0' 2015-08-13 17:37:55 +02:00
James Cole
5fd688b266 Merge branch 'release/3.5.0' into develop 2015-08-13 17:37:55 +02:00
James Cole
3716668e0c Updated read me. 2015-08-13 17:37:45 +02:00
James Cole
ae7fd18c34 New composer file. 2015-08-13 17:35:48 +02:00
James Cole
4f59b1d32f No Google thing when no Analytics ID present. 2015-08-13 17:35:41 +02:00
James Cole
90cb3279df Better example env file. 2015-08-13 17:34:08 +02:00
James Cole
cf0c7ef6b2 New version. 2015-08-13 17:32:22 +02:00
James Cole
47c23781d9 Fixed password reset. 2015-08-13 17:32:15 +02:00
James Cole
e258c050f7 Merge branch 'release/3.4.11' 2015-08-10 20:13:38 +02:00
James Cole
57801b2f34 Merge branch 'release/3.4.11' into develop 2015-08-10 20:13:38 +02:00
James Cole
710e9c9423 new version. 2015-08-10 20:13:33 +02:00
James Cole
deefef83bd Added sum for the current period, see issue #99 2015-08-09 17:01:12 +02:00
James Cole
51e30aed66 Added a sum of the current page and the sum of the entire category, in reference to issue #99. 2015-08-09 16:56:38 +02:00
James Cole
8d109a3cfe Fixed a null pointer exception. 2015-08-09 13:54:58 +02:00
James Cole
3424e019b5 Removed animation, again [skip ci] 2015-08-06 16:39:53 +02:00
James Cole
c6b4bceb67 Animation test [skip ci] 2015-08-06 16:39:06 +02:00
James Cole
afb4155015 Remove animation thing. [skip ci] 2015-08-06 16:37:53 +02:00
James Cole
8d99baf38a Update charts.js 2015-08-05 09:02:33 +02:00
James Cole
b91cb60328 Fix translations [skip ci] 2015-08-02 09:01:13 +02:00
James Cole
c0d62237fc Made the date thing throw a FF error. 2015-08-02 08:53:34 +02:00
James Cole
223ea80860 Fixed some embarrassing spelling errors in the CSV importer. [skip ci] 2015-08-02 08:53:19 +02:00
James Cole
5a77bef494 Sort chart and code cleanup [skip ci] 2015-08-02 07:41:47 +02:00
James Cole
80c0efe821 Small rearrangement of front page boxes. [skip ci] 2015-08-02 07:35:09 +02:00
James Cole
8044d89557 Display correct amount [skip ci] 2015-08-02 07:08:47 +02:00
James Cole
4f0ed97410 Fixed a bug where the category list in a monthly report would be empty. 2015-08-02 07:04:43 +02:00
James Cole
af7952f204 Removed old references to Google [skip ci] 2015-08-01 07:22:48 +02:00
James Cole
d8dcae856b Remove log. 2015-08-01 07:12:34 +02:00
James Cole
7296796ed9 Fix chart. 2015-08-01 07:12:03 +02:00
James Cole
a2c2bb4948 Forgot a dot [skip ci] 2015-08-01 07:09:51 +02:00
James Cole
72ebfdc20e Debug log. 2015-08-01 07:09:12 +02:00
James Cole
16b95ea78a New chart. 2015-08-01 07:04:41 +02:00
James Cole
c04f08dfd8 Filter empty budgets. 2015-07-31 18:18:54 +02:00
James Cole
a30793e818 Fix chart. Related to #99 2015-07-31 18:14:24 +02:00
James Cole
e39e1eaf21 Included opening balance. 2015-07-31 18:10:55 +02:00
James Cole
ab22d2cbaa Fixed the overview chart for categories, so it will properly reflect income and expenses. See bug #99 2015-07-31 14:26:22 +02:00
James Cole
96ddbe7227 Reorganized the category charts in the year report to properly reflect income and expenses. Necessary to facilitate the changes needed for bug #99 2015-07-31 14:20:18 +02:00
James Cole
4d09235aef Update composer.lock 2015-07-31 14:17:49 +02:00
James Cole
136b8975e3 Sort piggy bank list. 2015-07-31 13:44:56 +02:00
James Cole
e21b1eca17 Remove script. 2015-07-31 07:31:05 +02:00
James Cole
244b90b1d4 Fix bug #98 2015-07-30 21:32:58 +02:00
James Cole
b318f3f940 Merge branch 'release/3.4.10' 2015-07-27 21:23:20 +02:00
James Cole
e211c9812e Fixed some math things. 2015-07-26 19:42:28 +02:00
James Cole
eef28d96f4 Code cleanup [skip ci] 2015-07-26 19:13:06 +02:00
James Cole
c8227e09ee id was ambiguous. 2015-07-26 19:10:31 +02:00
James Cole
3e05fd91d9 Lots of cleaning up. 2015-07-26 19:07:02 +02:00
James Cole
450baba56a Removed some dead code and fixed some other. 2015-07-26 17:03:05 +02:00
James Cole
17a8c4918c Code cleanup. 2015-07-26 15:51:07 +02:00
James Cole
0e2419d61a New translations [skip ci] 2015-07-26 09:44:31 +02:00
James Cole
79b1a2ca6d Gave cron controller a new line. [skip ci] 2015-07-26 07:42:34 +02:00
James Cole
2213c68155 Added a missing breadcrumb. 2015-07-26 07:41:10 +02:00
James Cole
2492b1fa96 Expanded the message a user may get when his credentials do not work. 2015-07-26 07:39:21 +02:00
James Cole
6c6598dac5 Lots of new translations. 2015-07-26 07:39:04 +02:00
James Cole
a137112e66 New read me. 2015-07-25 19:28:19 +02:00
James Cole
8642ae8180 New version. 2015-07-25 19:27:14 +02:00
James Cole
894c4dc5a7 New composer.lock. 2015-07-25 19:24:07 +02:00
James Cole
d96063ea6e Also expand interface. 2015-07-25 19:23:00 +02:00
James Cole
c3dc193f3e First attempt at new last activity thing. 2015-07-25 19:22:41 +02:00
James Cole
3c2952009e Some new stuff. 2015-07-25 19:04:39 +02:00
James Cole
f4ade470df Remove if statements. 2015-07-25 18:48:48 +02:00
James Cole
2e33b43389 CSS and invalid account warning. 2015-07-25 18:40:45 +02:00
James Cole
92799699bc Better attachment handling. 2015-07-25 18:33:19 +02:00
James Cole
7ab0508167 Some new translations. 2015-07-25 18:31:05 +02:00
James Cole
3c65c28936 Some translations. 2015-07-25 16:48:32 +02:00
James Cole
43892da07e may edit fields [skip ci] 2015-07-25 07:05:27 +02:00
James Cole
7c436920a4 Some formatting and translations. [skip ci] 2015-07-25 07:04:09 +02:00
James Cole
89d565e63b Check for block code. [skip ci] 2015-07-25 07:03:50 +02:00
James Cole
150b6fe5b6 Add block code [skip ci] 2015-07-25 07:03:42 +02:00
James Cole
0e77574c26 Also give block code. [skip ci] 2015-07-25 07:03:35 +02:00
James Cole
df23863443 Remove bounce error thing. 2015-07-24 22:08:38 +02:00
James Cole
581bf11b21 Fixed a translation [skip ci] 2015-07-24 13:34:22 +02:00
James Cole
d602d4b429 Add some debug logging. 2015-07-24 13:26:42 +02:00
James Cole
d1d4a52934 Catch empty send grid credentials. 2015-07-24 13:23:02 +02:00
James Cole
375d113769 Find users not already blocked only. 2015-07-24 13:20:09 +02:00
James Cole
9b83974bff Improve the cron controller. Force blocked users to logout. 2015-07-24 13:17:47 +02:00
James Cole
3c68c99bd5 Fixed some translations. 2015-07-24 09:03:40 +02:00
James Cole
ec4b37c596 Updated chart. 2015-07-24 08:36:49 +02:00
James Cole
ba9601d21c Better display for piggy bank events. 2015-07-24 08:34:30 +02:00
James Cole
50c13fd469 Clear cache. 2015-07-22 22:13:40 +02:00
James Cole
7af072b8fc Show message. 2015-07-22 19:35:39 +02:00
James Cole
faa128d41e Made a cron controller. 2015-07-22 19:09:17 +02:00
James Cole
868fe46932 Some more debug stuff. 2015-07-22 18:44:51 +02:00
James Cole
e9e4307ce5 Better debug. 2015-07-22 18:09:14 +02:00
James Cole
774d4844a9 Another try to fix csrf 2015-07-22 17:58:06 +02:00
James Cole
586c53e670 Remove CSRF check. 2015-07-22 17:52:55 +02:00
James Cole
68e073fbff A new controller that can be used in combination with SendGrid. 2015-07-22 17:50:02 +02:00
James Cole
8101dc37b1 New route for attachment preview. 2015-07-19 22:19:36 +02:00
James Cole
63f16c458d Small fixes for piggy banks data seed. 2015-07-19 22:19:26 +02:00
James Cole
821e007e95 If zero, other thing. 2015-07-19 18:39:06 +02:00
James Cole
1656a2f11a Experimenting with a preview for attachments. 2015-07-19 18:37:29 +02:00
James Cole
4dbc135dce Added max file size for uploads. 2015-07-19 14:30:20 +02:00
James Cole
fc886f6bc1 Seed some tags. 2015-07-19 13:46:41 +02:00
James Cole
f93e480466 Better notifications. 2015-07-19 13:46:34 +02:00
James Cole
fe807e23f8 Fixed sorting in tags. 2015-07-19 13:46:20 +02:00
James Cole
ecf61c31f1 Add new line to file. 2015-07-19 12:23:27 +02:00
James Cole
4feff18af5 Fix test data. 2015-07-19 12:21:51 +02:00
James Cole
a07c52e0d8 Fix some route names. 2015-07-19 12:21:38 +02:00
James Cole
7bb07d7f55 Add non-breaking space to fix issue #95. 2015-07-19 12:20:35 +02:00
James Cole
f12dfc8a14 Icons for attachments. 2015-07-19 11:47:56 +02:00
James Cole
be030f15c4 New composer.lock. 2015-07-19 09:57:26 +02:00
James Cole
f5fb6c063b Also delete attachments. 2015-07-19 09:53:58 +02:00
James Cole
fb722f06b9 Some added newlines. 2015-07-19 09:38:44 +02:00
James Cole
c0ea19e15e Test data no longer runs into the future. 2015-07-19 09:37:52 +02:00
James Cole
cdeb1ad87c Some model updates. 2015-07-19 09:37:37 +02:00
James Cole
0dbe4e94fa Allow to edit an attachment. 2015-07-19 09:37:28 +02:00
James Cole
b5e2e8aa1d Edit attachment page. 2015-07-18 23:51:51 +02:00
James Cole
9502010248 Some new routes. 2015-07-18 23:06:51 +02:00
James Cole
fea0557b47 Going to allow edit of attachment. 2015-07-18 22:49:27 +02:00
James Cole
ed4fcc9011 Some optimisation. 2015-07-18 22:17:31 +02:00
James Cole
ed12ea7cfb Check for double files and some code clean up. 2015-07-18 21:46:16 +02:00
James Cole
73e526645e Uncomment providers. 2015-07-18 21:33:52 +02:00
James Cole
72aeafb2b5 Some model code block updates 2015-07-18 21:33:10 +02:00
James Cole
cc1af60cb4 Basic attachment download function. 2015-07-18 21:32:31 +02:00
James Cole
359fab315f A fix in the model and a simple view for attachments. 2015-07-18 21:12:34 +02:00
James Cole
6a9574bab9 Allow jpeg and PDF. 2015-07-18 19:49:35 +02:00
James Cole
83d6158483 Basic upload working. 2015-07-18 09:49:59 +02:00
James Cole
63ef89b6cc Basic interface for upload. 2015-07-18 09:49:29 +02:00
James Cole
b0beab4cd3 Attachment model and database changes. 2015-07-18 09:49:19 +02:00
James Cole
a34782575f Fix form and upload thing. 2015-07-18 08:59:33 +02:00
James Cole
142bdc9430 Add attachment thing to upload form. 2015-07-17 21:45:58 +02:00
James Cole
14b79cb0a4 Fixed a bug where deposits and/or transfers would be assigned budgets if you had selected a budget at the withdrawal screen earlier. 2015-07-17 21:03:13 +02:00
James Cole
ce5beeaf2c Better compare for amounts because floatval can be inaccurate. 2015-07-17 17:35:04 +02:00
James Cole
31114a2ca5 Fixed a bug where tags would be recreated instead of "found". 2015-07-17 17:34:49 +02:00
James Cole
32528094ad Updated composer file. 2015-07-16 20:39:20 +02:00
James Cole
0a2a01c44c Code reformat. 2015-07-15 21:06:26 +02:00
James Cole
c1888dc3ac Merge branch 'release/3.4.9' 2015-07-15 21:02:36 +02:00
James Cole
4d76afbe01 Merge branch 'release/3.4.9' into develop 2015-07-15 21:02:36 +02:00
James Cole
76d7a97f93 New release with all changes so far. Change log coming soon. 2015-07-15 21:02:27 +02:00
James Cole
8b1366b20a Merge pull request #93 from RonaldvanMeer/master
fix issue #91
2015-07-15 21:00:29 +02:00
RonaldvanMeer
e0f9685578 Spacing fixes 2015-07-15 12:29:11 +02:00
RonaldvanMeer
5235657954 Fixing missing IBAN field on Create New User request 2015-07-15 12:25:09 +02:00
James Cole
a15fbc8094 Now committing to correct branch. 2015-07-14 22:48:34 +02:00
James Cole
546f1d9c50 Revert "Some login and session updates."
This reverts commit 74231f552a.
2015-07-14 22:45:14 +02:00
James Cole
74231f552a Some login and session updates. 2015-07-14 22:45:00 +02:00
James Cole
b250a10e3c Merge branch 'release/3.4.8'
Conflicts:
	resources/twig/auth/login.twig
	resources/twig/auth/password.twig
	resources/twig/auth/register.twig
2015-07-12 22:45:37 +02:00
James Cole
a9f1b31dd6 New version. 2015-07-12 22:43:43 +02:00
James Cole
7fe393acaf Updated composer.lock. 2015-07-12 22:43:01 +02:00
James Cole
04faba4db5 Fix URL things. 2015-07-12 22:37:05 +02:00
James Cole
91bba40c20 Cleanup code. 2015-07-12 17:59:13 +02:00
James Cole
79e39f7de8 Code cleanup. 2015-07-12 17:36:38 +02:00
James Cole
9c09353559 Some improvements in tour 2015-07-12 12:45:41 +02:00
James Cole
50752a5bfe Implemented a short tour. 2015-07-11 10:01:13 +02:00
James Cole
d59879db7d Add bootstrap tour references. 2015-07-11 08:37:21 +02:00
James Cole
aab125da27 Add bootstrap tour files. 2015-07-11 08:36:46 +02:00
James Cole
74fc731f96 Full Bootstrap. 2015-07-11 08:35:51 +02:00
James Cole
bd0050fec2 Fix balance display. 2015-07-10 20:59:20 +02:00
James Cole
aa5e313b92 Save transactions by moving them. 2015-07-10 20:48:45 +02:00
James Cole
e89d613b7e Removed confidential data from logging routine. [skip ci] 2015-07-10 20:25:17 +02:00
James Cole
8757929ead Make log [skip ci] 2015-07-10 20:19:31 +02:00
James Cole
e0a9b19802 Some new translations and what-not. 2015-07-10 20:17:17 +02:00
James Cole
308da6dc6e Clean up some lists [skip ci] 2015-07-10 07:39:59 +02:00
James Cole
b6960fb0e5 Small layout and translation fixes [skip ci] 2015-07-10 05:39:35 +02:00
James Cole
137208c3fd Typical. 2015-07-09 21:59:02 +02:00
James Cole
d7a9a62a1d Should fix some scrutinyizer problems. 2015-07-09 21:48:05 +02:00
James Cole
075315bdaa Added some model thingies so scrutinizer will stop whining. 2015-07-09 21:29:30 +02:00
James Cole
3948fcd614 Added newlines to files. 2015-07-09 21:26:40 +02:00
James Cole
8e61e129ab More debug, some bug fixes. 2015-07-09 19:36:14 +02:00
James Cole
20cffd0502 Fixed Rabo importer. 2015-07-09 19:23:49 +02:00
James Cole
5df09dab09 Put specifix in json. 2015-07-09 19:05:59 +02:00
James Cole
7446b911e5 Logging in Rabo specifix. 2015-07-09 19:03:39 +02:00
James Cole
f15267c1ab Better import feedback. 2015-07-09 18:38:15 +02:00
James Cole
9c9fc2b5dc Some fixes in csv importer. 2015-07-09 18:33:09 +02:00
James Cole
28f601b54b Import fix asset account. 2015-07-09 16:37:42 +02:00
James Cole
18b8a05014 Extra logging. 2015-07-09 16:07:05 +02:00
James Cole
910c995ed8 Log account id. [skip ci] 2015-07-09 16:03:47 +02:00
James Cole
498468aa2c Display error. [skip ci] 2015-07-09 15:39:41 +02:00
James Cole
637aebcb34 Bug fix for importer. [skip ci] 2015-07-09 15:36:56 +02:00
James Cole
9afd5cb277 Fix specific [skip ci] 2015-07-09 15:27:40 +02:00
James Cole
bc525e7272 Date format in config downloader. [skip ci] 2015-07-09 15:25:24 +02:00
James Cole
a80180780d Use log [skip ci] 2015-07-09 15:21:34 +02:00
James Cole
0d73086c37 More logging [skip ci] 2015-07-09 15:20:55 +02:00
James Cole
02ae39238d Forgot one iban key. 2015-07-09 14:04:01 +02:00
James Cole
43d6b51d42 Some cleaning up [skip ci] 2015-07-09 11:13:38 +02:00
James Cole
84566310de Cleaned up model comments. 2015-07-09 09:42:09 +02:00
James Cole
6a6ec9fbe4 Cleaned up some bill related code. 2015-07-09 09:41:54 +02:00
James Cole
84a7f825d7 Some small code optimisations. 2015-07-09 06:13:39 +02:00
James Cole
0372c1aaf1 Small fix to make account journal list sortable. [skip ci] 2015-07-08 20:18:51 +02:00
James Cole
c9fff197f7 Some improved sorting. 2015-07-08 13:11:51 +02:00
James Cole
6900392e43 Fixed some sorting. 2015-07-08 13:05:33 +02:00
James Cole
c00bcd78cc Some dependency clean up. 2015-07-07 19:09:45 +02:00
James Cole
b2b82124e6 Merge branch 'release/3.4.7' 2015-07-07 11:12:30 +02:00
James Cole
3de57c668f Merge branch 'release/3.4.7' into develop 2015-07-07 11:12:30 +02:00
James Cole
43669648ce New test data. 2015-07-07 11:10:13 +02:00
James Cole
3b73b416d5 New read me. 2015-07-07 11:10:07 +02:00
James Cole
5153591c8f New version. 2015-07-07 10:38:53 +02:00
James Cole
3172bc90da Some small fixes. 2015-07-07 10:05:11 +02:00
James Cole
76a1b2cd51 Improve some search methods. 2015-07-07 09:46:19 +02:00
James Cole
bdf7eee72f Clean up some code routines. 2015-07-07 01:07:19 +02:00
James Cole
2d4b148b2c Various optimisations. 2015-07-06 22:23:34 +02:00
James Cole
d67db74ca2 Optimized some code. 2015-07-06 22:12:35 +02:00
James Cole
516725456f Scrutiniser will now include duplicate code. 2015-07-06 21:57:43 +02:00
James Cole
001d72a484 Code cleanup. 2015-07-06 20:56:20 +02:00
James Cole
c555e28988 Add new bank specific fixes. 2015-07-06 20:21:55 +02:00
James Cole
af13d1943f Should fix the last issues. 2015-07-06 18:57:15 +02:00
James Cole
52df2edc8f Fixed the last issues. Now onto code complexity. 2015-07-06 18:48:17 +02:00
James Cole
cd08484a13 Code cleanup. 2015-07-06 18:13:57 +02:00
James Cole
f38d38f139 Math fixes. Not bugs. 2015-07-06 18:06:31 +02:00
James Cole
93b6c68938 Some math fixes. Not bugs. 2015-07-06 18:04:13 +02:00
James Cole
a4cc25175a Fixed some duplication. 2015-07-06 17:45:59 +02:00
James Cole
3fb14b4708 Code cleanup. 2015-07-06 16:52:18 +02:00
James Cole
6bdb6db330 Fixed some problems. 2015-07-06 16:42:19 +02:00
James Cole
d05c165ace These might be the final issues on scrutiniser, apart from code complexity. 2015-07-06 16:33:54 +02:00
James Cole
ab53cdb896 Cleanup redirect code. 2015-07-06 16:27:21 +02:00
James Cole
c1f142af78 Fix some redirect things. 2015-07-06 16:12:22 +02:00
James Cole
6e261abb73 Some code improvements. 2015-07-06 16:08:36 +02:00
James Cole
39af9e4414 More translations. 2015-07-06 10:39:44 +02:00
James Cole
5b50abb2c7 Bread crumbs and fine tuning. 2015-07-06 08:33:39 +02:00
James Cole
13bda0a264 Mainly layout improvements. 2015-07-06 08:14:19 +02:00
James Cole
1658c666ab Some changes. 2015-07-05 21:47:59 +02:00
James Cole
170aebfe54 Some code improvements. 2015-07-05 19:57:44 +02:00
James Cole
c4ef379d0e Cleanup post processing. 2015-07-05 19:31:58 +02:00
James Cole
18b038d8ff Fixed most importers. 2015-07-05 18:18:44 +02:00
James Cole
12ee5da872 Massively complex but working never the less. 2015-07-05 16:39:25 +02:00
James Cole
601f9f86bb Some more importers. 2015-07-05 15:16:44 +02:00
James Cole
d7329a5915 Expanded CSV configuration. 2015-07-05 14:37:36 +02:00
James Cole
9e7b730002 Somehow CSV reader got lost in translation. 2015-07-05 09:34:57 +02:00
James Cole
2d59d845bc Account iban thing fix [skip ci] 2015-07-05 09:19:51 +02:00
James Cole
c2645894e0 Allow update iban [skip ci] 2015-07-05 09:07:05 +02:00
James Cole
3751106317 Fix name in option list. 2015-07-05 09:05:51 +02:00
James Cole
60bb639351 Add IBAN to account list. 2015-07-05 08:52:35 +02:00
James Cole
74c50930bd Merge branch 'feature/csv-import' into develop
Conflicts:
	composer.json
	composer.lock
	resources/lang/en/form.php
	resources/lang/nl/form.php
2015-07-05 08:50:54 +02:00
James Cole
9105104303 Make CSV import a feature that can be turned on / off. 2015-07-05 08:47:16 +02:00
James Cole
540dde135e CSV importer now indicates the problems it has. 2015-07-05 08:45:05 +02:00
James Cole
f8936210cf Small fixes. 2015-07-05 07:18:48 +02:00
James Cole
1dc6d8de40 First working version, very beta. 2015-07-05 06:59:05 +02:00
James Cole
1069db3c13 Some new converters. 2015-07-05 06:26:34 +02:00
James Cole
65122f0144 Greatly expanded the CSV routine. 2015-07-05 06:18:02 +02:00
James Cole
d2c018f7da First (almost) functional CSV importer. 2015-07-04 07:53:37 +02:00
James Cole
46493c2af6 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Update model info.
  Accounts can now have IBAN.
  Add IBAN field.
  New read me.
2015-07-03 12:54:45 +02:00
James Cole
c303e03f76 Update model info. 2015-07-03 12:54:35 +02:00
James Cole
d8b65f62e7 Accounts can now have IBAN. 2015-07-03 12:54:35 +02:00
James Cole
854368a8f3 Add IBAN field. 2015-07-03 12:54:35 +02:00
James Cole
7653a34aea New read me. 2015-07-03 12:54:35 +02:00
James Cole
ee50b58e00 Update model info. 2015-07-03 12:52:03 +02:00
James Cole
1eb60ab100 Accounts can now have IBAN. 2015-07-03 12:51:14 +02:00
James Cole
4a20eef351 Add IBAN field. 2015-07-03 12:27:49 +02:00
James Cole
26c9b2c353 Initial process form. 2015-07-03 12:22:20 +02:00
James Cole
16374bce9b Fixed upload form, made a new form element, added some processing. 2015-07-03 11:52:51 +02:00
James Cole
86011d4ea2 Initial commit for CSV import. 2015-07-03 10:45:00 +02:00
James Cole
fcb8b02da9 Merge branch 'release/3.4.6.1' 2015-07-03 10:32:31 +02:00
James Cole
571165c2bb Merge branch 'release/3.4.6.1' into develop 2015-07-03 10:32:31 +02:00
James Cole
c842113610 New read me. 2015-07-03 10:32:16 +02:00
James Cole
974a8b3b70 Merge branch 'master' of https://github.com/JC5/firefly-iii
# By Richard Ebbers
# Via James Cole (2) and Richard Ebbers (1)
* 'master' of https://github.com/JC5/firefly-iii:
  updated authentication pages to use dynamic urls
  use relative path in login screen
2015-07-03 10:30:57 +02:00
Richard Ebbers
2e20c99ada updated authentication pages to use dynamic urls 2015-07-03 10:30:32 +02:00
Richard Ebbers
aa88ff6f2c use relative path in login screen 2015-07-03 10:30:32 +02:00
James Cole
5e6aa63d03 Merge pull request #86 from ebbz/master
use relative path in login screen improved
2015-07-03 10:29:35 +02:00
James Cole
ad6700c114 An example test. Will continue later. 2015-07-03 10:28:40 +02:00
Richard Ebbers
f08c6efb00 updated authentication pages to use dynamic urls 2015-07-03 09:37:59 +02:00
James Cole
cc807ec132 Remove database setup from single test. 2015-07-03 08:16:14 +02:00
James Cole
24e7c68243 Initial codeception installation. 2015-07-03 08:15:30 +02:00
James Cole
ab25edd37a Updated to include codeception. 2015-07-03 08:13:50 +02:00
James Cole
be47fde6c2 Removed all tests. 2015-07-03 07:38:55 +02:00
James Cole
1ffa8c5e72 Removed code coverage logic. 2015-07-03 07:33:43 +02:00
James Cole
d855ccb8a7 Removed help button for small layouts [skip ci] 2015-07-03 07:28:20 +02:00
James Cole
d88919474b Reversed codeception. 2015-07-03 07:27:01 +02:00
James Cole
e139664301 Revert "Some updates to unit tests."
This reverts commit 5adf5f6e3f.
2015-07-03 07:25:07 +02:00
James Cole
5adf5f6e3f Some updates to unit tests. 2015-07-03 07:24:34 +02:00
James Cole
aef2075c8e Cleanup test case. 2015-07-02 09:49:45 +02:00
James Cole
922b2962a3 Bootstrap codeception. 2015-07-02 09:45:21 +02:00
James Cole
b4a401700e Installed codeception. 2015-07-02 09:44:56 +02:00
James Cole
e9601bb9c1 Add codeception. 2015-07-02 09:36:43 +02:00
James Cole
58af3dc6ea Removed everything related to unit tests. Can restore later. 2015-07-02 08:51:53 +02:00
Richard Ebbers
074295df61 use relative path in login screen 2015-07-01 20:03:43 +02:00
James Cole
ec349b31c7 Fixed tests. 2015-07-01 16:41:59 +02:00
James Cole
5ae236e016 With tooltips. 2015-07-01 15:08:09 +02:00
James Cole
d7c5897aba Optimise bootstrap. [skip ci] 2015-07-01 15:04:56 +02:00
James Cole
5252e7efe7 Cleanup css [skip ci] 2015-07-01 15:02:27 +02:00
James Cole
fc7d65629a Reorder bill chart [skip ci] 2015-07-01 13:18:50 +02:00
James Cole
f28fdf8252 Fixed chart tooltip [skip ci] 2015-07-01 11:08:14 +02:00
James Cole
5d07c4a949 Add date format to category charts [skip ci] 2015-07-01 11:07:10 +02:00
James Cole
fdd9eaab4b Added a better date for some budget charts [skip ci] 2015-07-01 11:04:08 +02:00
James Cole
e0d863a46f Create bills in test data, sort them [skip ci] 2015-07-01 10:52:42 +02:00
James Cole
3aacb6f5f3 New packages, updated read me, new composer.lock 2015-06-29 16:42:38 +02:00
James Cole
428e331b3e Reinstated the help button. 2015-06-29 15:23:50 +02:00
James Cole
847e05e9a7 Small experimental fix [skip ci] 2015-06-29 12:34:31 +02:00
James Cole
087eb5dbe6 Merge branch 'release/3.4.6' into develop 2015-06-29 09:53:26 +02:00
James Cole
f15932b2ac Merge branch 'release/3.4.6' 2015-06-29 09:53:25 +02:00
James Cole
d3a4c3795d New version. 2015-06-29 09:53:10 +02:00
James Cole
b15d55e1d9 Ignore untestable stuff. 2015-06-29 09:23:39 +02:00
James Cole
4f5889cc5b New tests for navigation. 2015-06-29 09:14:39 +02:00
James Cole
bf2a104a4e Fixed some tests. 2015-06-29 07:59:06 +02:00
James Cole
0c6dd5cd16 Removed some dead code. 2015-06-29 07:22:51 +02:00
James Cole
5efb06a7aa Removed unused code. 2015-06-29 07:17:39 +02:00
James Cole
b13acef272 Removed some dead code. 2015-06-29 07:16:43 +02:00
James Cole
cfa67d6c0f Implemented google chart tests. 2015-06-28 21:33:39 +02:00
James Cole
e70444f19a Fixed the bug that would unintentionally not let you edit accounts without changing their name. Closed #83 2015-06-28 21:13:08 +02:00
James Cole
0258982e60 Implemented two google chart tests. 2015-06-28 20:56:04 +02:00
James Cole
70eed5cb5e New tests. 2015-06-28 18:00:11 +02:00
James Cole
a650fa51f7 Implemented bill chart JS test. 2015-06-28 16:41:12 +02:00
James Cole
cb205580d8 First test for chartJS. 2015-06-28 16:20:14 +02:00
James Cole
f9329aac00 Removed unnecessary html tags. [skip ci] 2015-06-28 13:09:20 +02:00
James Cole
745f4a7523 Small chart and layout fixes [skip ci] 2015-06-28 13:07:23 +02:00
James Cole
60254dafd7 Implemented a forgotten chart. 2015-06-28 12:41:58 +02:00
James Cole
a8d60388ba New (empty) tests. 2015-06-28 10:38:51 +02:00
James Cole
83ec60254c New tests. 2015-06-28 10:03:34 +02:00
James Cole
c15c45f765 Some small fixes. 2015-06-28 08:42:06 +02:00
James Cole
cbe52b5089 Optimised chart generation. 2015-06-28 08:33:23 +02:00
James Cole
e4e2921f3e Fixed some tests. 2015-06-28 08:24:12 +02:00
James Cole
4673170531 Restored some cache [skip ci] 2015-06-27 22:22:27 +02:00
James Cole
2c2ed26c38 Optimise some charts [skip ci] 2015-06-27 22:11:03 +02:00
James Cole
94be5244fe Final chart. First version. 2015-06-27 20:52:06 +02:00
James Cole
f137a08493 Implemented some more charts. 2015-06-27 20:39:50 +02:00
James Cole
48624d0a34 A new chart, single count. 2015-06-27 17:38:16 +02:00
James Cole
4cceb3ddaa Completed the index. 2015-06-27 17:32:52 +02:00
James Cole
f728395603 Added some more charts 2015-06-27 17:05:39 +02:00
James Cole
3e82d43807 Expand some code to generate chartJS charts. 2015-06-27 16:01:06 +02:00
James Cole
2194c4e0a9 Expand some layouts to accept chartJS charts. 2015-06-27 16:00:50 +02:00
James Cole
c581080f3f Added option for other charts (chartJS currently). 2015-06-27 12:21:04 +02:00
James Cole
f6b1ec27e5 Renamed google chart methods and data. 2015-06-27 12:04:53 +02:00
James Cole
368b183230 Fixed category chart [skip ci] 2015-06-27 11:46:14 +02:00
James Cole
9028ad36ad Moved charts to separate generators. 2015-06-27 11:44:18 +02:00
James Cole
6cc041cd39 Fixed the chart generator. 2015-06-27 08:38:27 +02:00
James Cole
63ff01e78d Outsourced a chart to a specialised Google chart thing. 2015-06-27 08:18:47 +02:00
James Cole
9e5484937e Code cleanup [skip ci] 2015-06-27 08:06:24 +02:00
James Cole
b8ed489b14 Updated the read me [skip ci] 2015-06-27 07:55:59 +02:00
James Cole
765152d04b Reimplemented GA code. [skip ci] 2015-06-26 05:19:11 +02:00
James Cole
14934367d8 Simplified account name for cash accounts. 2015-06-26 04:57:30 +02:00
James Cole
04164500c8 Optimised some layout things. 2015-06-25 17:06:20 +02:00
James Cole
5160f2c298 Some new tests and a bug fix for piggy banks. 2015-06-24 21:02:34 +02:00
James Cole
124c9303b9 This should fix the tests. 2015-06-23 22:13:13 +02:00
James Cole
cd27f0ad69 Some database and css fixes. [skip ci] 2015-06-23 21:14:21 +02:00
James Cole
a7555bcce3 Some layout and file updates. 2015-06-23 19:07:37 +02:00
James Cole
6b5c4fd3f4 Merge branch 'master' of https://github.com/JC5/firefly-iii into develop
* 'master' of https://github.com/JC5/firefly-iii:
  New images [skip ci]
2015-06-23 07:40:50 +02:00
James Cole
cc55e2acee Lets see if this fixes the database. 2015-06-23 07:38:48 +02:00
James Cole
1511f75a80 Fixed tests. Broke database. 2015-06-22 22:04:46 +02:00
James Cole
f01bbefc1f Fixed tests. 2015-06-22 21:45:32 +02:00
James Cole
1d1eb5ffa8 Fix migration thing. 2015-06-22 21:37:02 +02:00
James Cole
a465cb2191 Remove all references to reminders. 2015-06-22 21:31:12 +02:00
James Cole
42d13e02ef New images [skip ci] 2015-06-22 21:04:31 +02:00
James Cole
d00786c43f Merge branch 'release/3.4.5' into develop 2015-06-22 19:12:05 +02:00
James Cole
4b47f99829 Merge branch 'release/3.4.5' 2015-06-22 19:12:04 +02:00
James Cole
35aaf40003 Updated composer file. 2015-06-22 19:11:17 +02:00
James Cole
cc5b4a1e02 Actually fix version [skip ci] 2015-06-22 19:03:03 +02:00
James Cole
7079521e8c Fix version in develop. 2015-06-22 19:02:28 +02:00
James Cole
b5025560a5 Merge branch 'feature/admin-lte' into develop 2015-06-22 18:59:36 +02:00
James Cole
3f4bdd7f0e Remove unnecessary code. 2015-06-22 18:58:19 +02:00
James Cole
e94bb9b549 Fixed some html errors [skip ci] 2015-06-22 18:50:54 +02:00
James Cole
1ddaacbef5 Some updated templates [skip ci] 2015-06-22 17:55:37 +02:00
James Cole
e8b40518e0 Hide boxes [skip ci] 2015-06-22 17:23:02 +02:00
James Cole
0f88cbb41b Fixed some layouts [skip ci] 2015-06-22 07:24:44 +02:00
James Cole
780d137b76 Fixed tests. 2015-06-21 16:10:32 +02:00
James Cole
ad8a9717d1 Fixed some views. [skip ci] 2015-06-21 15:09:10 +02:00
James Cole
9d6ea6b2f6 Some view updates [skip ci] 2015-06-21 11:59:35 +02:00
James Cole
7559383089 Cleaned up lots of views. 2015-06-21 10:50:45 +02:00
James Cole
f84381c927 Fixed search [skip ci] 2015-06-21 05:58:05 +02:00
James Cole
cb0122a43f Different colour scheme. [skip ci] 2015-06-21 05:48:47 +02:00
James Cole
6776b20989 Cleaned up tag views [skip ci] 2015-06-21 05:48:07 +02:00
James Cole
e98d556022 Some new templates and a bug fix [skip ci] 2015-06-20 22:18:54 +02:00
James Cole
5bf18b69d7 Smaller css footprint [skip ci] 2015-06-20 21:57:54 +02:00
James Cole
ea17f045a7 Added language stuff [skip ci] 2015-06-20 21:57:03 +02:00
James Cole
526f565ea7 Updated stuff from the control list. 2015-06-20 21:55:55 +02:00
James Cole
4aff9d6e73 Some more stuff done. Layout mostly. 2015-06-20 18:22:33 +02:00
James Cole
bf516d4d21 Fix some things. 2015-06-20 09:54:01 +02:00
James Cole
ae92e409d9 First functional run of new layout. 2015-06-20 07:48:44 +02:00
James Cole
4d017dc8a9 Updated boxes and what-not. 2015-06-20 07:29:25 +02:00
James Cole
707f4e2965 First implementation of new template. 2015-06-19 20:59:14 +02:00
James Cole
1c3bffdc50 Trigger a build. 2015-06-19 08:38:21 +02:00
James Cole
e54ddcb8b0 Small fix in edit form. [skip ci] 2015-06-18 18:12:57 +02:00
James Cole
ddefb0debc Improved tag view [skip ci] 2015-06-18 18:05:06 +02:00
James Cole
92d8dde90d Fixed sort commands [skip ci] 2015-06-17 06:11:35 +02:00
James Cole
1bb0508ddf Fix balance report [skip ci] 2015-06-16 18:34:19 +02:00
James Cole
a280a326b9 Fix report [skip ci] 2015-06-16 18:32:34 +02:00
James Cole
683e9b7c2c Small bugs in transaction controller [skip ci] 2015-06-16 18:26:17 +02:00
James Cole
a44e5da421 Add save routine to each transaction. [skip ci] 2015-06-16 06:52:30 +02:00
James Cole
8cd2c90ad7 Fixed menu in mobile [skip ci] 2015-06-15 20:24:58 +02:00
James Cole
5e57a390a2 Fixed tests. 2015-06-15 19:25:54 +02:00
James Cole
620848272e Implement tag count and fix a count thing. 2015-06-14 21:27:04 +02:00
James Cole
1e86794416 Add tag count to journal for easier amount calculations [skip ci] 2015-06-14 11:52:07 +02:00
James Cole
e36717259b Put tags in test data [skip ci] 2015-06-14 11:51:47 +02:00
James Cole
75b9238b90 Code cleanup [skip ci] 2015-06-14 11:51:33 +02:00
James Cole
ce5b20027e Newlines [skip ci] 2015-06-14 11:22:41 +02:00
James Cole
0de1242c83 Some cleaning up [skip ci] 2015-06-14 08:22:02 +02:00
James Cole
8bd445ab19 Some code cleanup [skip ci] 2015-06-13 10:02:36 +02:00
James Cole
fdef0de163 Increased test coverage. 2015-06-13 08:17:38 +02:00
James Cole
b1b03a4325 Upgrade to laravel 5.1 2015-06-11 21:19:40 +02:00
James Cole
0587d96474 Fixed tests [skip ci] 2015-06-11 20:14:15 +02:00
James Cole
c2241567e4 New translations. [skip ci] 2015-06-11 18:32:31 +02:00
James Cole
7ac24ba418 Some new translations [skip ci] 2015-06-09 17:56:08 +02:00
James Cole
c933ffec66 Quick fix in budget edit. [skip ci] 2015-06-09 16:20:37 +02:00
James Cole
e587d934b1 Switch menu around [skip ci] 2015-06-09 15:47:10 +02:00
James Cole
f354e90656 Some small menu optimisations. [skip ci] 2015-06-09 15:46:13 +02:00
James Cole
1b0bc7ec6e Clean up language. [skip ci] 2015-06-08 21:36:54 +02:00
James Cole
ee1acb9c00 Merge branch 'hotfix/3.4.4.2' 2015-06-08 18:52:30 +02:00
James Cole
06862a2812 Merge branch 'hotfix/3.4.4.2' into develop 2015-06-08 18:52:30 +02:00
James Cole
5fa87e18db Fix tag bug. (again) 2015-06-08 18:51:45 +02:00
James Cole
77989e2720 Fix read me. 2015-06-08 18:50:48 +02:00
James Cole
3a1102fa4e New code for auth controller [skip ci] 2015-06-08 18:42:19 +02:00
James Cole
8a9974ce53 New menu and new boxes [skip ci] 2015-06-08 18:41:47 +02:00
James Cole
4be8f1ca03 New email messages [skip ci] 2015-06-08 18:41:36 +02:00
James Cole
1ec2970ee3 Merge branch 'hotfix/3.4.4.1' 2015-06-08 18:41:02 +02:00
James Cole
81b3a22606 Merge branch 'hotfix/3.4.4.1' into develop 2015-06-08 18:41:02 +02:00
James Cole
f81a475cc9 Fixed a bug where the source and destination account could be the same one. 2015-06-08 18:40:52 +02:00
Sander Dorigo
d7ee03d4f9 New HTML view. 2015-06-08 09:48:53 +02:00
Sander Dorigo
c1c06410c2 Add HTML view. 2015-06-08 09:47:02 +02:00
James Cole
657d16bb60 Merge branch 'release/3.4.4' 2015-06-07 18:24:40 +02:00
James Cole
e65a4c1010 Merge branch 'release/3.4.4' into develop 2015-06-07 18:24:40 +02:00
James Cole
e23d3f5661 Mark a new release. 2015-06-07 18:24:23 +02:00
James Cole
e13611f7af Stupid faker messes up again. 2015-06-07 16:58:49 +02:00
James Cole
596cd09489 With a little luck this will cover a lot. 2015-06-07 15:32:01 +02:00
James Cole
0be5b27d34 Fixed some test coverage. 2015-06-07 10:28:26 +02:00
James Cole
a27471ae55 Fixed test. [skip ci] 2015-06-07 09:30:24 +02:00
James Cole
e27e3622a8 Fixed translation [skip ci] 2015-06-07 09:10:44 +02:00
James Cole
e95273b72b Simplified (or tried to) some code. [skip ci] 2015-06-07 09:09:27 +02:00
James Cole
583d4f3249 Cleanup scan method. [skip ci] 2015-06-07 08:13:19 +02:00
James Cole
d6967c4516 Cleaning up the relevantTags thing [skip ci] 2015-06-07 07:48:53 +02:00
James Cole
40b3097374 Lots of cleanup and stuff. 2015-06-06 23:09:12 +02:00
James Cole
1a1f127993 Cleanup code. 2015-06-06 17:40:41 +02:00
James Cole
a0f34a7ce1 Fix tests. 2015-06-06 16:26:26 +02:00
James Cole
db020db34b Fix tests and fix coverage. 2015-06-06 15:36:12 +02:00
James Cole
681167bc1b Some code cleanup. 2015-06-05 19:02:23 +02:00
James Cole
40e49ffc37 Some bug fixes and cleanup. 2015-06-05 16:49:16 +02:00
James Cole
834b1afb38 Updated some code [skip ci] 2015-06-05 13:39:24 +02:00
James Cole
62d5a1da87 Cleaning up [skip ci] 2015-06-05 12:48:58 +02:00
James Cole
8d8308e557 Cleanup another validator [skip ci] 2015-06-05 12:34:45 +02:00
James Cole
e1aa63487a Optimized a validator. [skip ci] 2015-06-05 12:18:20 +02:00
Sander Dorigo
b7fbe110d4 Merge pull request #82 from JC5/scrutinizer-patch-5
Scrutinizer Auto-Fixes
2015-06-05 11:39:08 +02:00
Scrutinizer Auto-Fixer
58859eb35a Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-06-05 08:40:26 +00:00
James Cole
4b7e1ae1c6 Removed duplicate code. 2015-06-05 10:02:40 +02:00
James Cole
3a06a6ac07 Removed unnecessary code. 2015-06-05 09:51:52 +02:00
James Cole
db0f269dc8 Fix class reference. 2015-06-05 09:50:59 +02:00
James Cole
3cabe6ca5a Improved test coverage. 2015-06-05 08:39:23 +02:00
James Cole
d483005219 Expanded some tests. 2015-06-05 07:49:07 +02:00
James Cole
fea9bc4e7e Increased coverage and fixed a nasty bug. 2015-06-05 07:10:51 +02:00
James Cole
d579992c98 Slightly improved test coverage. 2015-06-04 23:08:44 +02:00
James Cole
ad1c61d959 Improved test coverage. 2015-06-04 21:35:36 +02:00
James Cole
bb1da31830 Increase test coverage. 2015-06-04 17:43:50 +02:00
James Cole
a50949e554 Cache preferences. 2015-06-03 22:11:50 +02:00
James Cole
14dce8a10b Optimized preferences. 2015-06-03 21:58:06 +02:00
James Cole
1240c8f685 Update models [skip ci] 2015-06-03 21:27:36 +02:00
James Cole
cc7c2e952c Code cleanup [skip ci] 2015-06-03 21:25:11 +02:00
James Cole
409ec2e086 Should fix tests. 2015-06-03 21:15:52 +02:00
James Cole
a7f6848e53 Lots of stuff gets cached now. 2015-06-03 18:22:47 +02:00
James Cole
4b0b79199d Some cleanup. 2015-06-03 17:32:50 +02:00
Sander Dorigo
d1d6c48d9b Update GA code 2015-06-03 11:27:47 +02:00
James Cole
21631780bb Fix some tests 2015-06-02 22:22:47 +02:00
James Cole
b935e32340 Fix amount in tag list [skip ci] 2015-06-02 22:07:38 +02:00
James Cole
72dd064932 Fix count in transaction search [skip ci] 2015-06-02 21:58:30 +02:00
James Cole
2e75446665 Fixed some JS bugs [skip ci] 2015-06-02 18:13:23 +02:00
James Cole
be17e4481e Cache boxes. 2015-06-02 18:05:42 +02:00
James Cole
616c849b1f Fixed tests. 2015-06-02 17:58:30 +02:00
James Cole
71947c097f Some experiments with a cache control thing. [skip ci] 2015-06-02 17:44:50 +02:00
James Cole
546787802d Some code cleanup [skip ci] 2015-06-02 17:14:03 +02:00
James Cole
294d0e388a Add cache to main chart. 2015-06-01 18:41:18 +02:00
James Cole
193a1b0325 Basic tutorial for new users. 2015-06-01 18:13:54 +02:00
James Cole
12743217a2 Merge branch 'release/3.4.3' into develop 2015-06-01 17:37:03 +02:00
James Cole
b252b9da66 Merge branch 'release/3.4.3' 2015-06-01 17:37:02 +02:00
James Cole
cdef9c3c7e New version. 2015-06-01 17:36:53 +02:00
James Cole
71dcebb744 New read me [skip ci] 2015-06-01 17:36:13 +02:00
James Cole
25f248c60a New read me [skip ci] 2015-06-01 17:35:48 +02:00
James Cole
d5cbc17831 Update read me. 2015-06-01 17:35:03 +02:00
James Cole
7a10217511 New read me and scrutiniser instructions. 2015-05-31 20:53:31 +02:00
James Cole
7559efab77 Update build thing. 2015-05-31 20:52:20 +02:00
James Cole
8254efbd03 Fix modal. 2015-05-31 20:24:06 +02:00
James Cole
4ae24225a5 Cleanup some modals. 2015-05-31 20:23:49 +02:00
James Cole
67d9154563 Added some new code to implement a permission scheme. 2015-05-28 06:43:07 +02:00
James Cole
ad0319c188 Some new translations [skip ci] 2015-05-28 06:11:39 +02:00
James Cole
eb650ea3ec Some code cleanup 2015-05-27 08:36:26 +02:00
James Cole
7eba33e805 Fixed some issues for scrutiniser [skip ci] 2015-05-27 07:58:54 +02:00
James Cole
e1cb9d387e Fixed chart and redirect 2015-05-27 07:51:33 +02:00
James Cole
2ace7c3ca0 Some code cleanup [skip ci] 2015-05-27 07:27:05 +02:00
James Cole
58014f0592 Extra translation [skip ci] 2015-05-27 06:57:02 +02:00
James Cole
1d4938bb09 Small fix in error handler. 2015-05-26 22:12:34 +02:00
James Cole
bbf4007c3e Fixed return type [skip ci] 2015-05-26 20:59:16 +02:00
Sander Dorigo
4d5124fb4c Merge pull request #81 from JC5/scrutinizer-patch-4
Scrutinizer Auto-Fixes
2015-05-26 20:58:11 +02:00
Scrutinizer Auto-Fixer
14a7cd05b1 Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-05-26 18:57:31 +00:00
James Cole
946be80eef Fix route urls. 2015-05-26 20:37:01 +02:00
James Cole
9ad8b1a980 Some optimisations. 2015-05-26 20:28:18 +02:00
James Cole
f733216fcb Trigger a build. 2015-05-26 20:18:21 +02:00
Sander Dorigo
ffc6139e21 Merge pull request #80 from JC5/scrutinizer-patch-3
Scrutinizer Auto-Fixes
2015-05-26 20:16:39 +02:00
Scrutinizer Auto-Fixer
571cac6644 Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-05-26 17:50:09 +00:00
James Cole
2738ac5a5c Did some cleanup [skip ci] 2015-05-26 19:48:49 +02:00
James Cole
7dfde51b84 Fixed tests. 2015-05-26 19:38:52 +02:00
James Cole
2d2f18e538 Code cleanup. 2015-05-26 12:19:11 +02:00
James Cole
3af0dd2e3b Some code cleanup [skip ci] 2015-05-26 12:08:46 +02:00
James Cole
349e077802 Added model data to models to aid scrutiniser. 2015-05-26 11:15:45 +02:00
James Cole
812aae358f Some code cleanup. 2015-05-26 08:17:58 +02:00
James Cole
c3c59d0627 Fixed some JS things [skip ci] 2015-05-26 07:50:38 +02:00
James Cole
89518b412d Merge branch 'release/3.4.2' into develop 2015-05-25 23:18:33 +02:00
James Cole
f43b026162 Merge branch 'release/3.4.2' 2015-05-25 23:18:32 +02:00
James Cole
b806c70f52 New read me. 2015-05-25 22:58:49 +02:00
James Cole
10bff3c0b8 Fix tests. 2015-05-25 22:56:00 +02:00
James Cole
50f71c4130 Merge branch 'release/3.4.1' into develop 2015-05-25 22:24:28 +02:00
602 changed files with 31790 additions and 47004 deletions

View File

@@ -1,3 +1 @@
src_dir: .
coverage_clover: build/logs/clover.xml
json_path: build/logs/coveralls-upload.json

View File

@@ -1,6 +1,7 @@
APP_ENV=production
APP_DEBUG=false
APP_KEY=SomeRandomString
APP_KEY=SomeRandomStringOf32CharsExactly
DB_CONNECTION=mysql
DB_HOST=localhost
@@ -11,10 +12,22 @@ DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file
DEFAULT_CURRENCY=EUR
DEFAULT_LANGUAGE=en_US
EMAIL_SMTP=
EMAIL_DRIVER=smtp
EMAIL_USERNAME=
EMAIL_PASSWORD=
ANALYTICS_ID=
EMAIL_PRETEND=false
RUNCLEANUP=true
SHOW_INCOMPLETE_TRANSLATIONS=false
ANALYTICS_ID=
RUNCLEANUP=true
SITE_OWNER=mail@example.com
SENDGRID_USERNAME=
SENDGRID_PASSWORD=
BLOCKED_DOMAINS=

5
.gitignore vendored
View File

@@ -5,7 +5,7 @@ Thumbs.db
.idea/
tests/_output/*
_ide_helper.php
/build/logs/clover.xml
/build/logs
index.html*
app/storage/firefly-export*
.vagrant
@@ -31,3 +31,6 @@ addNewLines.php
.phpstorm.meta.php
.env.backup
.env.local
tests/_output/*
tests/_output/*

3
.scrutinizer.yml Normal file
View File

@@ -0,0 +1,3 @@
# .scrutinizer.yml
tools:
external_code_coverage: false

View File

@@ -10,11 +10,9 @@ install:
- composer update
- php artisan env
- mv -v .env.testing .env
- touch storage/database/testing.db
- php artisan migrate --env=testing
- php artisan migrate --seed --env=testing
script:
- phpunit
after_script:
- php vendor/bin/coveralls
- CODECLIMATE_REPO_TOKEN=26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61 ./vendor/bin/test-reporter --stdout > codeclimate.json
- "curl -X POST -d @codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v0.1.1)' https://codeclimate.com/test_reports"

View File

@@ -1,13 +1,11 @@
# Firefly III
#### 3.4.1
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
[![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)
[![Coverage Status](https://coveralls.io/repos/JC5/firefly-iii/badge.svg?branch=master)](https://coveralls.io/r/JC5/firefly-iii?branch=master)
[![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)
[![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)
## 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
@@ -21,7 +19,6 @@ 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:
- The screenshots below on this very page.
@@ -30,7 +27,7 @@ To get to know Firefly, and to see if it fits you, check out these resources:
and the philosophy behind it.
#### About the name (if you care)
#### 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.
@@ -63,19 +60,19 @@ Everything is organised:
_Please note that everything in these screenshots is fictional and may not be realistic._
![Index](https://i.nder.be/c09vfw90)
![Index](https://i.nder.be/hmp5mhw5)
![Accounts](https://i.nder.be/hkn0vhcg)
![Accounts](https://i.nder.be/hf5k02g9)
![Budgets](https://i.nder.be/h2snx2mw)
![Budgets](https://i.nder.be/gzv635mz)
![Reports 1](https://i.nder.be/c9f8zy5c)
![Reports 1](https://i.nder.be/g0w698s3)
![Reports 2](https://i.nder.be/ghvs5png)
![Reports 2](https://i.nder.be/cr77nyxq)
![Bills](https://i.nder.be/h58kh00p)
![Bills](https://i.nder.be/c7sugsz5)
![Piggy banks](https://i.nder.be/hkud0h53)
![Piggy banks](https://i.nder.be/gy2nk0y4)
## Running and installing
@@ -84,14 +81,47 @@ If you're still interested please read [the installation guide](https://github.c
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.
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 month. It's a trial.
## Security
You should always run Firefly III on a site with TLS enabled (https://). Please note that although some parts of the
database are encrypted (transaction descriptions, names, etc.) some parts are _not_ (amounts, dates, etc). If you need
more security, you must enable transparent database encryption or a comparable technology. Please remember that this
is open source software under active development, and it is in no way guaranteed to be safe or secure.
## Translations
Firefly III is currently available in Dutch and English. Support for other languages is being worked on. I can use
your help. Checkout [Crowdin](https://crowdin.com/project/firefly-iii) for more information.
## 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. One of the things on the todo-list
is adding translations.
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)

View File

@@ -2,6 +2,7 @@
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpException;
/**
* Class Handler
@@ -33,7 +34,7 @@ class Handler extends ExceptionHandler
*/
public function render($request, Exception $e)
{
if ($this->isHttpException($e)) {
if ($e instanceof HttpException) {
return $this->renderHttpException($e);
} else {
return parent::render($request, $e);

View File

@@ -0,0 +1,43 @@
<?php
namespace FireflyIII\Generator\Chart\Account;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
/**
* Interface AccountChartGenerator
*
* @package FireflyIII\Generator\Chart\Account
*/
interface AccountChartGenerator
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function frontpage(Collection $accounts, Carbon $start, Carbon $end);
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @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

@@ -0,0 +1,187 @@
<?php
namespace FireflyIII\Generator\Chart\Account;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
use Steam;
/**
* Class ChartJsAccountChartGenerator
*
* @package FireflyIII\Generator\Chart\Account
*/
class ChartJsAccountChartGenerator implements AccountChartGenerator
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end)
{
$data = [
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.spent'),
'data' => []]]];
bcscale(2);
$start->subDay();
$ids = $this->getIdsFromCollection($accounts);
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$accounts->each(
function (Account $account) use ($startBalances, $endBalances) {
$id = $account->id;
$startBalance = $this->isInArray($startBalances, $id);
$endBalance = $this->isInArray($endBalances, $id);
$diff = bcsub($endBalance, $startBalance);
$account->difference = round($diff, 2);
}
);
$accounts = $accounts->sortByDesc(
function (Account $account) {
return $account->difference;
}
);
foreach ($accounts as $account) {
if ($account->difference > 0) {
$data['labels'][] = $account->name;
$data['datasets'][0]['data'][] = $account->difference;
}
}
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
* @param Carbon $end
*
* @return array
*/
public function frontpage(Collection $accounts, Carbon $start, Carbon $end)
{
// language:
$format = 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 = [
'label' => $account->name,
'fillColor' => 'rgba(220,220,220,0.2)',
'strokeColor' => 'rgba(220,220,220,1)',
'pointColor' => 'rgba(220,220,220,1)',
'pointStrokeColor' => '#fff',
'pointHighlightFill' => '#fff',
'pointHighlightStroke' => 'rgba(220,220,220,1)',
'data' => [],
];
$current = clone $start;
$range = Steam::balanceInRange($account, $start, clone $end);
$previous = array_values($range)[0];
while ($current <= $end) {
$format = $current->format('Y-m-d');
$balance = isset($range[$format]) ? $range[$format] : $previous;
$set['data'][] = $balance;
$previous = $balance;
$current->addDay();
}
$data['datasets'][] = $set;
}
$data['count'] = count($data['datasets']);
return $data;
}
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function single(Account $account, Carbon $start, Carbon $end)
{
// language:
$format = trans('config.month_and_day');
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => $account->name,
'data' => []
]
],
];
$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'][] = $balance;
$previous = $balance;
$current->addDay();
}
return $data;
}
/**
* @param Collection $collection
*
* @return array
*/
protected function getIdsFromCollection(Collection $collection)
{
$ids = [];
foreach ($collection as $entry) {
$ids[] = $entry->id;
}
return array_unique($ids);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace FireflyIII\Generator\Chart\Bill;
use FireflyIII\Models\Bill;
use Illuminate\Support\Collection;
/**
* Interface BillChartGenerator
*
* @package FireflyIII\Generator\Chart\Bill
*/
interface BillChartGenerator
{
/**
* @param string $paid
* @param string $unpaid
*
* @return array
*/
public function frontpage($paid, $unpaid);
/**
* @param Bill $bill
* @param Collection $entries
*
* @return array
*/
public function single(Bill $bill, Collection $entries);
}

View File

@@ -0,0 +1,96 @@
<?php
namespace FireflyIII\Generator\Chart\Bill;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
/**
* Class ChartJsBillChartGenerator
*
* @package FireflyIII\Generator\Chart\Bill
*/
class ChartJsBillChartGenerator implements BillChartGenerator
{
/**
* @param string $paid
* @param string $unpaid
*
* @return array
*/
public function frontpage($paid, $unpaid)
{
bcscale(2);
$data = [
[
'value' => round($unpaid, 2),
'color' => 'rgba(53, 124, 165,0.7)',
'highlight' => 'rgba(53, 124, 165,0.9)',
'label' => trans('firefly.unpaid'),
],
[
'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;
}
/**
* @param Bill $bill
* @param Collection $entries
*
* @return array
*/
public function single(Bill $bill, Collection $entries)
{
// language:
$format = 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);
/*
* journalAmount has been collected in BillRepository::getJournals
*/
$actualAmount[] = round(($entry->journalAmount * -1), 2);
}
$data['datasets'][] = [
'label' => trans('firefly.minAmount'),
'data' => $minAmount,
];
$data['datasets'][] = [
'label' => trans('firefly.billEntry'),
'data' => $actualAmount,
];
$data['datasets'][] = [
'label' => trans('firefly.maxAmount'),
'data' => $maxAmount,
];
$data['count'] = count($data['datasets']);
return $data;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace FireflyIII\Generator\Chart\Budget;
use Illuminate\Support\Collection;
/**
* Interface BudgetChartGenerator
*
* @package FireflyIII\Generator\Chart\Budget
*/
interface BudgetChartGenerator
{
/**
* @param Collection $entries
*
* @return array
*/
public function budget(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function budgetLimit(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries);
/**
* @param Collection $budgets
* @param Collection $entries
*
* @return array
*/
public function year(Collection $budgets, Collection $entries);
}

View File

@@ -0,0 +1,176 @@
<?php
namespace FireflyIII\Generator\Chart\Budget;
use Config;
use Illuminate\Support\Collection;
use Preferences;
/**
* Class ChartJsBudgetChartGenerator
*
* @package FireflyIII\Generator\Chart\Budget
*/
class ChartJsBudgetChartGenerator implements BudgetChartGenerator
{
/**
* @param Collection $entries
* @param string $dateFormat
*
* @return array
*/
public function budget(Collection $entries, $dateFormat = 'month')
{
// language:
$language = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'))->data;
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
$data = [
'labels' => [],
'datasets' => [
[
'label' => 'Amount',
'data' => [],
],
],
];
/** @var array $entry */
foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized($format);
$data['datasets'][0]['data'][] = $entry[1];
}
$data['count'] = count($data['datasets']);
return $data;
}
/**
* @codeCoverageIgnore
*
* @param Collection $entries
*
* @return array
*/
public function budgetLimit(Collection $entries)
{
return $this->budget($entries, 'monthAndDay');
}
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries)
{
$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] * -1, 2); // spent is coming in negative, must be positive
$overspent[] = round($entry[3] * -1, 2); // same
}
}
$data['datasets'][] = [
'label' => trans('firefly.left'),
'data' => $left,
];
$data['datasets'][] = [
'label' => trans('firefly.spent'),
'data' => $spent,
];
$data['datasets'][] = [
'label' => trans('firefly.overspent'),
'data' => $overspent,
];
$data['count'] = count($data['datasets']);
return $data;
}
/**
* @param Collection $budgets
* @param Collection $entries
*
* @return array
*/
public function year(Collection $budgets, Collection $entries)
{
// language:
$format = trans('config.month');
$data = [
'labels' => [],
'datasets' => [],
];
foreach ($budgets as $budget) {
$data['labels'][] = $budget->name;
}
/** @var array $entry */
foreach ($entries as $entry) {
$array = [
'label' => $entry[0]->formatLocalized($format),
'data' => [],
];
array_shift($entry);
$array['data'] = $entry;
$data['datasets'][] = $array;
}
$data['count'] = count($data['datasets']);
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();
foreach ($first['budgeted'] as $year => $noInterest) {
$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;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace FireflyIII\Generator\Chart\Category;
use Illuminate\Support\Collection;
/**
* Interface CategoryChartGenerator
*
* @package FireflyIII\Generator\Chart\Category
*/
interface CategoryChartGenerator
{
/**
* @param Collection $entries
*
* @return array
*/
public function all(Collection $entries);
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInPeriod(Collection $categories, Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function frontpage(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function period(Collection $entries);
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function spentInPeriod(Collection $categories, Collection $entries);
}

View File

@@ -0,0 +1,194 @@
<?php
namespace FireflyIII\Generator\Chart\Category;
use Illuminate\Support\Collection;
/**
* Class ChartJsCategoryChartGenerator
*
* @package FireflyIII\Generator\Chart\Category
*/
class ChartJsCategoryChartGenerator implements CategoryChartGenerator
{
/**
* @param Collection $entries
*
* @return array
*/
public function all(Collection $entries)
{
$data = [
'count' => 2,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.spent'),
'data' => []
],
[
'label' => trans('firefly.earned'),
'data' => []
]
],
];
foreach ($entries as $entry) {
$data['labels'][] = $entry[1];
$spent = round($entry[2], 2);
$earned = round($entry[3], 2);
$data['datasets'][0]['data'][] = $spent == 0 ? null : $spent * -1;
$data['datasets'][1]['data'][] = $earned == 0 ? null : $earned;
}
return $data;
}
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInPeriod(Collection $categories, Collection $entries)
{
// language:
$format = trans('config.month');
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
foreach ($categories as $category) {
$data['labels'][] = $category->name;
}
foreach ($entries as $entry) {
$date = $entry[0]->formatLocalized($format);
array_shift($entry);
$data['count']++;
$data['datasets'][] = ['label' => $date, 'data' => $entry];
}
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;
}
/**
* @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();
foreach ($first['spent'] as $year => $noInterest) {
$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 spentInPeriod(Collection $categories, Collection $entries)
{
// language:
$format = trans('config.month');
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
foreach ($categories as $category) {
$data['labels'][] = $category->name;
}
foreach ($entries as $entry) {
$date = $entry[0]->formatLocalized($format);
array_shift($entry);
$data['count']++;
$data['datasets'][] = ['label' => $date, 'data' => $entry];
}
return $data;
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace FireflyIII\Generator\Chart\PiggyBank;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Class ChartJsPiggyBankChartGenerator
*
* @package FireflyIII\Generator\Chart\PiggyBank
*/
class ChartJsPiggyBankChartGenerator implements PiggyBankChartGenerator
{
/**
* @param Collection $set
*
* @return array
*/
public function history(Collection $set)
{
// language:
$format = trans('config.month_and_day');
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => 'Diff',
'data' => []
]
],
];
$sum = '0';
bcscale(2);
foreach ($set as $entry) {
$date = new Carbon($entry->date);
$sum = bcadd($sum, $entry->sum);
$data['labels'][] = $date->formatLocalized($format);
$data['datasets'][0]['data'][] = round($sum, 2);
}
return $data;
}
}

View File

@@ -0,0 +1,20 @@
<?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,144 @@
<?php
namespace FireflyIII\Generator\Chart\Report;
use Illuminate\Support\Collection;
/**
* Class ChartJsReportChartGenerator
*
* @package FireflyIII\Generator\Chart\Report
*/
class ChartJsReportChartGenerator implements ReportChartGenerator
{
/**
* 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
*
* @return array
*/
public function yearInOut(Collection $entries)
{
// language:
$format = trans('config.month');
$data = [
'count' => 2,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.income'),
'data' => []
],
[
'label' => trans('firefly.expenses'),
'data' => []
]
],
];
foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized($format);
$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 yearInOutSummarized($income, $expense, $count)
{
$data = [
'count' => 2,
'labels' => [trans('firefly.sum_of_year'), trans('firefly.average_of_year')],
'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;
}
}

View File

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

@@ -4,7 +4,6 @@ use Auth;
use FireflyIII\Events\JournalCreated;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
/**
@@ -52,18 +51,16 @@ class ConnectJournalToPiggyBank
if (is_null($repetition)) {
return false;
}
bcscale(2);
$amount = $journal->amount;
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id == $piggyBank->account_id) {
if ($transaction->amount < 0) {
$amount = $transaction->amount * -1;
}
}
$amount = $journal->amount_positive;
// if piggy account matches source account, the amount is positive
if ($piggyBank->account_id == $journal->source_account->id) {
$amount = $amount * -1;
}
$repetition->currentamount += $amount;
$repetition->currentamount = bcadd($repetition->currentamount, $amount);
$repetition->save();
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]);

View File

@@ -1,6 +1,5 @@
<?php namespace FireflyIII\Handlers\Events;
use App;
use FireflyIII\Events\JournalSaved;
use Log;
@@ -36,7 +35,7 @@ class RescanJournal
Log::debug('Triggered saved event for journal #' . $journal->id . ' (' . $journal->description . ')');
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$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.');

View File

@@ -49,10 +49,12 @@ class UpdateJournalConnection
if (is_null($repetition)) {
return;
}
$amount = $journal->amount;
$diff = $amount - $event->amount;// update current repetition
bcscale(2);
$repetition->currentamount += $diff;
$amount = $journal->amount;
$diff = bcsub($amount, $event->amount); // update current repetition
$repetition->currentamount = bcadd($repetition->currentamount, $diff);
$repetition->save();

View File

@@ -0,0 +1,224 @@
<?php
namespace FireflyIII\Helpers\Attachments;
use Auth;
use Config;
use Crypt;
use FireflyIII\Models\Attachment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\MessageBag;
use Input;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Class AttachmentHelper
*
* @package FireflyIII\Helpers\Attachments
*/
class AttachmentHelper implements AttachmentHelperInterface
{
/** @var int */
protected $maxUploadSize;
/** @var array */
protected $allowedMimes;
/** @var MessageBag */
public $errors;
/** @var MessageBag */
public $messages;
/**
*
*/
public function __construct()
{
$this->maxUploadSize = Config::get('firefly.maxUploadSize');
$this->allowedMimes = Config::get('firefly.allowedMimes');
$this->errors = new MessageBag;
$this->messages = new MessageBag;
}
/**
* @param Attachment $attachment
*
* @return string
*/
public function getAttachmentLocation(Attachment $attachment)
{
$path = storage_path('upload') . DIRECTORY_SEPARATOR . 'at-' . $attachment->id . '.data';
return $path;
}
/**
* @param Model $model
*
* @return bool
*/
public function saveAttachmentsForModel(Model $model)
{
$files = Input::file('attachments');
if (is_array($files)) {
foreach ($files as $entry) {
if (!is_null($entry)) {
$this->processFile($entry, $model);
}
}
} else {
if (!is_null($files)) {
$this->processFile($files, $model);
}
}
return true;
}
/**
* @param UploadedFile $file
* @param Model $model
*
* @return bool
*/
protected function hasFile(UploadedFile $file, Model $model)
{
$md5 = md5_file($file->getRealPath());
$name = $file->getClientOriginalName();
$class = get_class($model);
$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]);
$this->errors->add('attachments', $msg);
return true;
}
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
*
* @return bool|Attachment
*/
protected function processFile(UploadedFile $file, Model $model)
{
$validation = $this->validateUpload($file, $model);
if ($validation === false) {
return false;
}
$attachment = new Attachment; // create Attachment object.
$attachment->user()->associate(Auth::user());
$attachment->attachable()->associate($model);
$attachment->md5 = md5_file($file->getRealPath());
$attachment->filename = $file->getClientOriginalName();
$attachment->mime = $file->getMimeType();
$attachment->size = $file->getSize();
$attachment->uploaded = 0;
$attachment->save();
$path = $file->getRealPath(); // encrypt and move file to storage.
$content = file_get_contents($path);
$encrypted = Crypt::encrypt($content);
// store it:
$upload = $this->getAttachmentLocation($attachment);
if (is_writable(dirname($upload))) {
file_put_contents($upload, $encrypted);
}
$attachment->uploaded = 1; // update attachment
$attachment->save();
$name = e($file->getClientOriginalName()); // add message:
$msg = trans('validation.file_attached', ['name' => $name]);
$this->messages->add('attachments', $msg);
// return it.
return $attachment;
}
/**
* @param UploadedFile $file
*
* @return bool
*/
protected function validMime(UploadedFile $file)
{
$mime = e($file->getMimeType());
$name = e($file->getClientOriginalName());
if (!in_array($mime, $this->allowedMimes)) {
$msg = trans('validation.file_invalid_mime', ['name' => $name, 'mime' => $mime]);
$this->errors->add('attachments', $msg);
return false;
}
return true;
}
/**
* @param UploadedFile $file
*
* @return bool
*/
protected function validSize(UploadedFile $file)
{
$size = $file->getSize();
$name = e($file->getClientOriginalName());
if ($size > $this->maxUploadSize) {
$msg = trans('validation.file_too_large', ['name' => $name]);
$this->errors->add('attachments', $msg);
return false;
}
return true;
}
/**
* @return MessageBag
*/
public function getErrors()
{
return $this->errors;
}
/**
* @return MessageBag
*/
public function getMessages()
{
return $this->messages;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace FireflyIII\Helpers\Attachments;
use FireflyIII\Models\Attachment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\MessageBag;
/**
* Interface AttachmentHelperInterface
*
* @package FireflyIII\Helpers\Attachments
*/
interface AttachmentHelperInterface
{
/**
* @param Model $model
*
* @return bool
*/
public function saveAttachmentsForModel(Model $model);
/**
* @return MessageBag
*/
public function getErrors();
/**
* @return MessageBag
*/
public function getMessages();
/**
* @param Attachment $attachment
*
* @return mixed
*/
public function getAttachmentLocation(Attachment $attachment);
}

View File

@@ -64,6 +64,8 @@ class BalanceLine
if ($this->getRole() == self::ROLE_DIFFROLE) {
return trans('firefly.leftUnbalanced');
}
return '';
}
/**
@@ -148,23 +150,4 @@ class BalanceLine
{
$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.0;
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$sum += $balanceEntry->getLeft();
}
return $sum;
}
}

View File

@@ -40,7 +40,7 @@ class Bill
*/
public function getBills()
{
$this->bills->sortBy(
$set = $this->bills->sortBy(
function (BillLine $bill) {
$active = intval($bill->getBill()->active) == 0 ? 1 : 0;
$name = $bill->getBill()->name;
@@ -50,7 +50,7 @@ class Bill
);
return $this->bills;
return $set;
}
}

View File

@@ -16,19 +16,19 @@ class BillLine
/** @var bool */
protected $active;
/** @var float */
/** @var string */
protected $amount;
/** @var BillModel */
protected $bill;
/** @var bool */
protected $hit;
/** @var float */
/** @var string */
protected $max;
/** @var float */
/** @var string */
protected $min;
/**
* @return float
* @return string
*/
public function getAmount()
{
@@ -36,7 +36,7 @@ class BillLine
}
/**
* @param float $amount
* @param string $amount
*/
public function setAmount($amount)
{
@@ -60,7 +60,7 @@ class BillLine
}
/**
* @return float
* @return string
*/
public function getMax()
{
@@ -68,7 +68,7 @@ class BillLine
}
/**
* @param float $max
* @param string $max
*/
public function setMax($max)
{
@@ -76,7 +76,7 @@ class BillLine
}
/**
* @return float
* @return string
*/
public function getMin()
{
@@ -84,7 +84,7 @@ class BillLine
}
/**
* @param float $min
* @param string $min
*/
public function setMin($min)
{

View File

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

View File

@@ -33,19 +33,24 @@ class Expense
*/
public function addOrCreateExpense(TransactionJournal $entry)
{
bcscale(2);
$accountId = $entry->account_id;
$amount = strval(round($entry->amount, 2));
if (bccomp('0', $amount) === -1) {
$amount = bcmul($amount, '-1');
}
if (!$this->expenses->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = strval(round($entry->amount, 2));
$newObject->amount = $amount;
$newObject->name = $entry->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);
$existing->amount = bcadd($existing->amount, $amount);
$existing->count++;
$this->expenses->put($accountId, $existing);
}
@@ -56,8 +61,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);
}
@@ -66,13 +81,13 @@ class Expense
*/
public function getExpenses()
{
$this->expenses->sortByDesc(
$set = $this->expenses->sortBy(
function (stdClass $object) {
return $object->amount;
}
);
return $this->expenses;
return $set;
}
/**

View File

@@ -18,7 +18,7 @@ class Income
/** @var Collection */
protected $incomes;
/** @var float */
/** @var string */
protected $total;
/**
@@ -38,7 +38,7 @@ class Income
$accountId = $entry->account_id;
if (!$this->incomes->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = strval(round($entry->amount, 2));
$newObject->amount = strval(round($entry->amount_positive, 2));
$newObject->name = $entry->name;
$newObject->count = 1;
$newObject->id = $accountId;
@@ -46,7 +46,7 @@ class Income
} else {
bcscale(2);
$existing = $this->incomes->get($accountId);
$existing->amount = bcadd($existing->amount, $entry->amount);
$existing->amount = bcadd($existing->amount, $entry->amount_positive);
$existing->count++;
$this->incomes->put($accountId, $existing);
}
@@ -67,17 +67,17 @@ class Income
*/
public function getIncomes()
{
$this->incomes->sortByDesc(
$set = $this->incomes->sortByDesc(
function (stdClass $object) {
return $object->amount;
}
);
return $this->incomes;
return $set;
}
/**
* @return float
* @return string
*/
public function getTotal()
{

View File

@@ -0,0 +1,38 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Account;
use Log;
/**
* Class AccountId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AccountId extends BasicConverter implements ConverterInterface
{
/**
* @return Account
*/
public function convert()
{
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
/** @var Account $account */
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
} else {
/** @var Account $account */
$account = Auth::user()->accounts()->find($this->value);
if (!is_null($account)) {
Log::debug('Found ' . $account->accountType->type . ' named "******" with ID: ' . $this->value . ' (not mapped) ');
}
}
return $account;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Account;
/**
* Class Amount
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Amount extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
*/
public function convert()
{
if (is_numeric($this->value)) {
return $this->value;
}
return 0;
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
/**
* Class AssetAccountIban
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AssetAccountIban extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
*/
public function convert()
{
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
return $account;
}
if (strlen($this->value) > 0) {
// find or create new account:
$account = $this->findAccount();
$accountType = AccountType::where('type', 'Asset account')->first();
if (is_null($account)) {
// create it if doesn't exist.
$account = Account::firstOrCreateEncrypted(
[
'name' => $this->value,
'iban' => $this->value,
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'active' => 1,
]
);
}
return $account;
}
return null;
}
/**
* @return Account|null
*/
protected function findAccount()
{
$set = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get(['accounts.*']);
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->iban == $this->value) {
return $entry;
}
}
return null;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
/**
* Class AssetAccountName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AssetAccountName extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
*/
public function convert()
{
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
return $account;
}
// find or create new account:
$accountType = AccountType::where('type', 'Asset account')->first();
$set = Auth::user()->accounts()->accountTypeIn(['Asset account', 'Default account'])->get();
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->name == $this->value) {
return $entry;
}
}
// create it if doesnt exist.
$account = Account::firstOrCreateEncrypted(
[
'name' => $this->value,
'iban' => '',
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'active' => 1,
]
);
return $account;
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class BasicConverter
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BasicConverter
{
/** @var array */
protected $data;
/** @var string */
protected $field;
/** @var int */
protected $index;
/** @var array */
protected $mapped;
/** @var string */
protected $role;
/** @var string */
protected $value;
/**
* @return array
*/
public function getData()
{
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @return string
*/
public function getField()
{
return $this->field;
}
/**
* @param string $field
*/
public function setField($field)
{
$this->field = $field;
}
/**
* @return int
*/
public function getIndex()
{
return $this->index;
}
/**
* @param int $index
*/
public function setIndex($index)
{
$this->index = $index;
}
/**
* @return array
*/
public function getMapped()
{
return $this->mapped;
}
/**
* @param array $mapped
*/
public function setMapped($mapped)
{
$this->mapped = $mapped;
}
/**
* @return string
*/
public function getRole()
{
return $this->role;
}
/**
* @param string $role
*/
public function setRole($role)
{
$this->role = $role;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* @param string $value
*/
public function setValue($value)
{
$this->value = $value;
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Bill;
/**
* Class BillId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BillId extends BasicConverter implements ConverterInterface
{
/**
* @return Bill
*/
public function convert()
{
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$bill = Auth::user()->bills()->find($this->mapped[$this->index][$this->value]);
} else {
$bill = Auth::user()->bills()->find($this->value);
}
return $bill;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Bill;
/**
* Class BillName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BillName extends BasicConverter implements ConverterInterface
{
/**
* @return Bill
*/
public function convert()
{
$bill = null;
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$bill = Auth::user()->bills()->find($this->mapped[$this->index][$this->value]);
} else {
$bills = Auth::user()->bills()->get();
/** @var Bill $bill */
foreach ($bills as $bill) {
if ($bill->name == $this->value) {
return $bill;
}
}
}
return $bill;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Budget;
/**
* Class AccountId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BudgetId extends BasicConverter implements ConverterInterface
{
/**
* @return Budget
*/
public function convert()
{
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$budget = Auth::user()->budgets()->find($this->mapped[$this->index][$this->value]);
} else {
$budget = Auth::user()->budgets()->find($this->value);
}
return $budget;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Budget;
/**
* Class BudgetName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BudgetName extends BasicConverter implements ConverterInterface
{
/**
* @return Budget
*/
public function convert()
{
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$budget = Auth::user()->budgets()->find($this->mapped[$this->index][$this->value]);
} else {
$budget = Budget::firstOrCreateEncrypted(
[
'name' => $this->value,
'user_id' => Auth::user()->id,
'active' => true,
]
);
}
return $budget;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Category;
/**
* Class CategoryId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CategoryId extends BasicConverter implements ConverterInterface
{
/**
* @return Category
*/
public function convert()
{
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$category = Auth::user()->categories()->find($this->mapped[$this->index][$this->value]);
} else {
$category = Auth::user()->categories()->find($this->value);
}
return $category;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Category;
/**
* Class CategoryName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CategoryName extends BasicConverter implements ConverterInterface
{
/**
* @return Category
*/
public function convert()
{
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$category = Auth::user()->categories()->find($this->mapped[$this->index][$this->value]);
} else {
$category = Category::firstOrCreateEncrypted(
[
'name' => $this->value,
'user_id' => Auth::user()->id
]
);
}
return $category;
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Interface ConverterInterface
*
* @package FireflyIII\Helpers\Csv\Converter
*/
interface ConverterInterface
{
/**
* @return mixed
*/
public function convert();
/**
* @param array $data
*/
public function setData(array $data);
/**
* @param string $field
*
*/
public function setField($field);
/**
* @param int $index
*/
public function setIndex($index);
/**
* @param array $mapped
*/
public function setMapped($mapped);
/**
* @param string $role
*/
public function setRole($role);
/**
* @param string $value
*/
public function setValue($value);
}

View File

@@ -0,0 +1,28 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\TransactionCurrency;
/**
* Class CurrencyCode
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CurrencyCode extends BasicConverter implements ConverterInterface
{
/**
* @return mixed|static
*/
public function convert()
{
if (isset($this->mapped[$this->index][$this->value])) {
$currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]);
} else {
$currency = TransactionCurrency::whereCode($this->value)->first();
}
return $currency;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\TransactionCurrency;
/**
* Class CurrencyId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CurrencyId extends BasicConverter implements ConverterInterface
{
/**
* @return mixed|static
*/
public function convert()
{
if (isset($this->mapped[$this->index][$this->value])) {
$currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]);
} else {
$currency = TransactionCurrency::find($this->value);
}
return $currency;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\TransactionCurrency;
/**
* Class CurrencyName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CurrencyName extends BasicConverter implements ConverterInterface
{
/**
* @return mixed|static
*/
public function convert()
{
if (isset($this->mapped[$this->index][$this->value])) {
$currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]);
} else {
$currency = TransactionCurrency::whereName($this->value)->first();
}
return $currency;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\TransactionCurrency;
/**
* Class CurrencySymbol
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CurrencySymbol extends BasicConverter implements ConverterInterface
{
/**
* @return mixed|static
*/
public function convert()
{
if (isset($this->mapped[$this->index][$this->value])) {
$currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]);
} else {
$currency = TransactionCurrency::whereSymbol($this->value)->first();
}
return $currency;
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use InvalidArgumentException;
use Log;
use Session;
/**
* Class Date
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Date extends BasicConverter implements ConverterInterface
{
/**
* @return static
* @throws FireflyException
*/
public function convert()
{
$format = Session::get('csv-date-format');
try {
$date = Carbon::createFromFormat($format, $this->value);
} catch (InvalidArgumentException $e) {
Log::error('Date conversion error: ' . $e->getMessage() . '. Value was "' . $this->value . '", format was "' . $format . '".');
$message = trans('firefly.csv_date_parse_error', ['format' => $format, 'value' => $this->value]);
throw new FireflyException($message);
}
return $date;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class Description
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Description extends BasicConverter implements ConverterInterface
{
/**
* @return mixed
*/
public function convert()
{
return trim($this->data['description'] . ' ' . $this->value);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Account;
/**
* Class Amount
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Ignore extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
*/
public function convert()
{
return null;
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Account;
use Log;
/**
* Class OpposingAccountIban
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class OpposingAccountIban extends BasicConverter implements ConverterInterface
{
/**
* If mapped, return account. Otherwise, only return the name itself.
*
* @return Account|string
*/
public function convert()
{
if (isset($this->mapped[$this->index][$this->value])) {
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
return $account;
} else {
if (strlen($this->value) > 0) {
$account = $this->findAccount();
if (!is_null($account)) {
return $account;
}
}
return $this->value;
}
}
/**
* @return Account|null
*/
protected function findAccount()
{
$set = Auth::user()->accounts()->get();
/** @var Account $account */
foreach ($set as $account) {
if ($account->iban == $this->value) {
Log::debug('OpposingAccountIban::convert found an Account (#' . $account->id . ': ******) with IBAN ******');
return $account;
}
}
return null;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Account;
/**
* Class OpposingName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class OpposingAccountId extends BasicConverter implements ConverterInterface
{
/**
* @return Account
*/
public function convert()
{
if (isset($this->mapped[$this->index][$this->value])) {
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
} else {
$account = Auth::user()->accounts()->find($this->value);
}
return $account;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Account;
/**
* Class OpposingName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class OpposingAccountName extends BasicConverter implements ConverterInterface
{
/**
* If mapped, return account. Otherwise, only return the name itself.
*
* @return Account|string
*/
public function convert()
{
if (isset($this->mapped[$this->index][$this->value])) {
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
return $account;
} else {
return $this->value;
}
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class RabobankDebetCredit
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class RabobankDebetCredit extends BasicConverter implements ConverterInterface
{
/**
* @return mixed
*/
public function convert()
{
if ($this->value == 'D') {
return -1;
}
return 1;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Tag;
use Illuminate\Support\Collection;
/**
* Class TagsComma
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class TagsComma extends BasicConverter implements ConverterInterface
{
/**
* @return Bill
*/
public function convert()
{
$tags = new Collection;
$strings = explode(',', $this->value);
foreach ($strings as $string) {
$tag = Tag::firstOrCreateEncrypted(
[
'tag' => $string,
'tagMode' => 'nothing',
'user_id' => Auth::user()->id,
]
);
$tags->push($tag);
}
$tags = $tags->merge($this->data['tags']);
return $tags;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Tag;
use Illuminate\Support\Collection;
/**
* Class TagsSpace
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class TagsSpace extends BasicConverter implements ConverterInterface
{
/**
* @return Bill
*/
public function convert()
{
$tags = new Collection;
$strings = explode(' ', $this->value);
foreach ($strings as $string) {
$tag = Tag::firstOrCreateEncrypted(
[
'tag' => $string,
'tagMode' => 'nothing',
'user_id' => Auth::user()->id,
]
);
$tags->push($tag);
}
$tags = $tags->merge($this->data['tags']);
return $tags;
}
}

283
app/Helpers/Csv/Data.php Normal file
View File

@@ -0,0 +1,283 @@
<?php
namespace FireflyIII\Helpers\Csv;
use Crypt;
use League\Csv\Reader;
use Session;
/**
* Class Data
*
* @package FireflyIII\Helpers\Csv
*/
class Data
{
/** @var string */
protected $csvFileContent;
/** @var string */
protected $csvFileLocation;
/** @var string */
protected $dateFormat;
/** @var bool */
protected $hasHeaders;
/** @var array */
protected $map = [];
/** @var array */
protected $mapped = [];
/** @var Reader */
protected $reader;
/** @var array */
protected $roles = [];
/** @var array */
protected $specifix = [];
/** @var int */
protected $importAccount = 0;
/**
*
*/
public function __construct()
{
$this->sessionHasHeaders();
$this->sessionDateFormat();
$this->sessionCsvFileLocation();
$this->sessionMap();
$this->sessionRoles();
$this->sessionMapped();
$this->sessionSpecifix();
$this->sessionImportAccount();
}
protected function sessionHasHeaders()
{
if (Session::has('csv-has-headers')) {
$this->hasHeaders = (bool)Session::get('csv-has-headers');
}
}
protected function sessionImportAccount()
{
if (Session::has('csv-import-account')) {
$this->importAccount = intval(Session::get('csv-import-account'));
}
}
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')) {
$this->map = (array)Session::get('csv-map');
}
}
protected function sessionRoles()
{
if (Session::has('csv-roles')) {
$this->roles = (array)Session::get('csv-roles');
}
}
protected function sessionMapped()
{
if (Session::has('csv-mapped')) {
$this->mapped = (array)Session::get('csv-mapped');
}
}
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

@@ -0,0 +1,371 @@
<?php
namespace FireflyIII\Helpers\Csv;
use Auth;
use Config;
use FireflyIII\Exceptions\FireflyException;
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\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use Log;
/**
* Class Importer
*
* @package FireflyIII\Helpers\Csv
*/
class Importer
{
/** @var Data */
protected $data;
/** @var array */
protected $errors;
/** @var array */
protected $importData;
/** @var array */
protected $importRow;
/** @var int */
protected $imported = 0;
/** @var array */
protected $map;
/** @var array */
protected $mapped;
/** @var array */
protected $roles;
/** @var int */
protected $rows = 0;
/** @var array */
protected $specifix = [];
/** @var Collection */
protected $journals;
/**
* Used by CsvController.
*
* @return array
*/
public function getErrors()
{
return $this->errors;
}
/**
* Used by CsvController
*
* @return int
*/
public function getImported()
{
return $this->imported;
}
/**
* Used by CsvController
*
* @return int
*/
public function getRows()
{
return $this->rows;
}
/**
* @return Collection
*/
public function getJournals()
{
return $this->journals;
}
/**
* @throws FireflyException
*/
public function run()
{
set_time_limit(0);
$this->journals = new Collection;
$this->map = $this->data->getMap();
$this->roles = $this->data->getRoles();
$this->mapped = $this->data->getMapped();
$this->specifix = $this->data->getSpecifix();
foreach ($this->data->getReader() as $index => $row) {
if ($this->parseRow($index)) {
Log::debug('--- Importing row ' . $index);
$this->rows++;
$result = $this->importRow($row);
if (!($result instanceof TransactionJournal)) {
Log::error('Caught error at row #' . $index . ': ' . $result);
$this->errors[$index] = $result;
} else {
$this->imported++;
$this->journals->push($result);
}
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;
}
/**
* 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);
$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;
}
/**
*
* @return TransactionJournal|string
*/
protected function createTransactionJournal()
{
bcscale(2);
$date = $this->importData['date'];
if (is_null($this->importData['date'])) {
$date = $this->importData['date-rent'];
}
$transactionType = $this->getTransactionType(); // defaults to deposit
$errors = new MessageBag;
$journal = TransactionJournal::create(
['user_id' => Auth::user()->id, 'transaction_type_id' => $transactionType->id, 'transaction_currency_id' => $this->importData['currency']->id,
'description' => $this->importData['description'], 'completed' => 0, 'date' => $date, 'bill_id' => $this->importData['bill-id'],]
);
if ($journal->getErrors()->count() == 0) {
// first transaction
$accountId = $this->importData['asset-account-object']->id; // create first transaction:
$amount = $this->importData['amount'];
$transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]);
$errors = $transaction->getErrors();
// second transaction
$accountId = $this->importData['opposing-account-object']->id; // create second transaction:
$amount = bcmul($this->importData['amount'], -1);
$transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]);
$errors = $transaction->getErrors()->merge($errors);
}
if ($errors->count() == 0) {
$journal->completed = 1;
$journal->save();
} else {
$text = join(',', $errors->all());
return $text;
}
$this->saveBudget($journal);
$this->saveCategory($journal);
$this->saveTags($journal);
// some debug info:
$journalId = $journal->id;
$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));
return $journal;
}
/**
* @return TransactionType
*/
protected function getTransactionType()
{
$transactionType = TransactionType::where('type', TransactionType::DEPOSIT)->first();
if ($this->importData['amount'] < 0) {
$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', TransactionType::TRANSFER)->first();
}
return $transactionType;
}
/**
* @param TransactionJournal $journal
*/
protected function saveBudget(TransactionJournal $journal)
{
// add budget:
if (!is_null($this->importData['budget'])) {
$journal->budgets()->save($this->importData['budget']);
}
}
/**
* @param TransactionJournal $journal
*/
protected function saveCategory(TransactionJournal $journal)
{
// add category:
if (!is_null($this->importData['category'])) {
$journal->categories()->save($this->importData['category']);
}
}
/**
* @param TransactionJournal $journal
*/
protected function saveTags(TransactionJournal $journal)
{
if (!is_null($this->importData['tags'])) {
foreach ($this->importData['tags'] as $tag) {
$journal->tags()->save($tag);
}
}
}
/**
* @param Data $data
*/
public function setData($data)
{
$this->data = $data;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Account;
/**
* Class AnyAccount
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class AnyAccount implements MapperInterface
{
/**
* @return array
*/
public function getMap()
{
$result = Auth::user()->accounts()->with('accountType')->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$list = [];
/** @var Account $account */
foreach ($result as $account) {
$list[$account->id] = $account->name . ' (' . $account->accountType->type . ')';
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Account;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* Class AssetAccount
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class AssetAccount implements MapperInterface
{
/**
* @return array
*/
public function getMap()
{
$result = Auth::user()->accounts()->with(
['accountmeta' => function (HasMany $query) {
$query->where('name', 'accountRole');
}]
)->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$list = [];
/** @var Account $account */
foreach ($result as $account) {
$name = $account->name;
if (strlen($account->iban) > 0) {
$name .= ' (' . $account->iban . ')';
}
$list[$account->id] = $name;
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Bill as BillModel;
/**
* Class Bill
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class Bill implements MapperInterface
{
/**
* @return array
*/
public function getMap()
{
$result = Auth::user()->bills()->get(['bills.*']);
$list = [];
/** @var BillModel $bill */
foreach ($result as $bill) {
$list[$bill->id] = $bill->name . ' [' . $bill->match . ']';
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Budget as BudgetModel;
/**
* Class Budget
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class Budget implements MapperInterface
{
/**
* @return array
*/
public function getMap()
{
$result = Auth::user()->budgets()->get(['budgets.*']);
$list = [];
/** @var BudgetModel $budget */
foreach ($result as $budget) {
$list[$budget->id] = $budget->name;
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Category as CategoryModel;
/**
* Class Category
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class Category implements MapperInterface
{
/**
* @return array
*/
public function getMap()
{
$result = Auth::user()->categories()->get(['categories.*']);
$list = [];
/** @var CategoryModel $category */
foreach ($result as $category) {
$list[$category->id] = $category->name;
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace FireflyIII\Helpers\Csv\Mapper;
/**
* Interface MapperInterface
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
interface MapperInterface
{
/**
* @return array
*/
public function getMap();
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Tag as TagModel;
/**
* Class Tag
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class Tag implements MapperInterface
{
/**
* @return array
*/
public function getMap()
{
$result = Auth::user()->budgets()->get(['tags.*']);
$list = [];
/** @var TagModel $tag */
foreach ($result as $tag) {
$list[$tag->id] = $tag->tag;
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace FireflyIII\Helpers\Csv\Mapper;
use FireflyIII\Models\TransactionCurrency as TC;
/**
* Class TransactionCurrency
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class TransactionCurrency implements MapperInterface
{
/**
* @return array
*/
public function getMap()
{
$currencies = TC::get();
$list = [];
foreach ($currencies as $currency) {
$list[$currency->id] = $currency->name . ' (' . $currency->code . ')';
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FireflyIII\Helpers\Csv\PostProcessing;
/**
* Class Amount
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class Amount implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process()
{
bcscale(2);
$this->data['amount'] = bcmul($this->data['amount'], $this->data['amount-modifier']);
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
}

View File

@@ -0,0 +1,185 @@
<?php
namespace FireflyIII\Helpers\Csv\PostProcessing;
use Auth;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use Log;
use Validator;
/**
* Class AssetAccount
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class AssetAccount implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process()
{
$result = $this->checkIdNameObject(); // has object in ID or Name?
if (!is_null($result)) {
return $result;
}
$result = $this->checkIbanString();
if (!is_null($result)) {
return $result;
}
$result = $this->checkNameString();
if (!is_null($result)) {
return $result;
}
return null;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @return array
*/
protected function checkIdNameObject()
{
if ($this->data['asset-account-id'] instanceof Account) { // first priority. try to find the account based on ID, if any
$this->data['asset-account-object'] = $this->data['asset-account-id'];
return $this->data;
}
if ($this->data['asset-account-iban'] instanceof Account) { // second: try to find the account based on IBAN, if any.
$this->data['asset-account-object'] = $this->data['asset-account-iban'];
return $this->data;
}
return null;
}
/**
* @return array|null
*/
protected function checkIbanString()
{
$rules = ['iban' => 'iban'];
$check = ['iban' => $this->data['asset-account-iban']];
$validator = Validator::make($check, $rules);
if (!$validator->fails()) {
$this->data['asset-account-object'] = $this->parseIbanString();
return $this->data;
}
return null;
}
/**
* @return Account|null
*/
protected function parseIbanString()
{
// create by name and/or iban.
$accounts = Auth::user()->accounts()->get();
foreach ($accounts as $entry) {
if ($entry->iban == $this->data['asset-account-iban']) {
return $entry;
}
}
$account = $this->createAccount();
return $account;
}
/**
* @return Account|null
*/
protected function createAccount()
{
$accountType = $this->getAccountType();
// create if not exists:
$name = is_string($this->data['asset-account-name']) && strlen($this->data['asset-account-name']) > 0 ? $this->data['asset-account-name']
: $this->data['asset-account-iban'];
$account = Account::firstOrCreateEncrypted(
[
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'name' => $name,
'iban' => $this->data['asset-account-iban'],
'active' => true,
]
);
return $account;
}
/**
*
* @return AccountType
*/
protected function getAccountType()
{
return AccountType::where('type', 'Asset account')->first();
}
/**
* @return array|null
*/
protected function checkNameString()
{
if ($this->data['asset-account-name'] instanceof Account) { // third: try to find account based on name, if any.
$this->data['asset-account-object'] = $this->data['asset-account-name'];
return $this->data;
}
if (is_string($this->data['asset-account-name'])) {
$this->data['asset-account-object'] = $this->parseNameString();
return $this->data;
}
return null;
}
/**
* @return Account|null
*/
protected function parseNameString()
{
$accountType = $this->getAccountType();
$accounts = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get();
foreach ($accounts as $entry) {
if ($entry->name == $this->data['asset-account-name']) {
Log::debug('Found an asset account with this name (#' . $entry->id . ': ******)');
return $entry;
}
}
// create if not exists:
$account = Account::firstOrCreateEncrypted(
[
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'name' => $this->data['asset-account-name'],
'iban' => '',
'active' => true,
]
);
return $account;
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace FireflyIII\Helpers\Csv\PostProcessing;
/**
* Class Bill
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class Bill implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process()
{
// get bill id.
if (!is_null($this->data['bill'])) {
$this->data['bill-id'] = $this->data['bill']->id;
}
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace FireflyIII\Helpers\Csv\PostProcessing;
use FireflyIII\Models\TransactionCurrency;
use Preferences;
/**
* Class Currency
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class Currency implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process()
{
// fix currency
if (is_null($this->data['currency'])) {
$currencyPreference = Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR'));
$this->data['currency'] = TransactionCurrency::whereCode($currencyPreference->data)->first();
}
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace FireflyIII\Helpers\Csv\PostProcessing;
/**
* Class Description
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class Description implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process()
{
$this->data['description'] = trim($this->data['description']);
if (strlen($this->data['description']) == 0) {
$this->data['description'] = trans('firefly.csv_empty_description');
}
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
}

View File

@@ -0,0 +1,210 @@
<?php
namespace FireflyIII\Helpers\Csv\PostProcessing;
use Auth;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use Log;
use Validator;
/**
* Class OpposingAccount
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class OpposingAccount implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process()
{
// three values:
// opposing-account-id, opposing-account-iban, opposing-account-name
$result = $this->checkIdNameObject();
if (!is_null($result)) {
return $result;
}
$result = $this->checkIbanString();
if (!is_null($result)) {
return $result;
}
$result = $this->checkNameString();
if (!is_null($result)) {
return $result;
}
return null;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @return array
*/
protected function checkIdNameObject()
{
if ($this->data['opposing-account-id'] instanceof Account) { // first priority. try to find the account based on ID, if any
Log::debug('OpposingAccountPostProcession: opposing-account-id is an Account.');
$this->data['opposing-account-object'] = $this->data['opposing-account-id'];
return $this->data;
}
if ($this->data['opposing-account-iban'] instanceof Account) { // second: try to find the account based on IBAN, if any.
Log::debug('OpposingAccountPostProcession: opposing-account-iban is an Account.');
$this->data['opposing-account-object'] = $this->data['opposing-account-iban'];
return $this->data;
}
return null;
}
/**
* @return array|null
*/
protected function checkIbanString()
{
$rules = ['iban' => 'iban'];
$iban = $this->data['opposing-account-iban'];
$check = ['iban' => $iban];
$validator = Validator::make($check, $rules);
if (is_string($iban) && strlen($iban) > 0 && !$validator->fails()) {
Log::debug('OpposingAccountPostProcession: opposing-account-iban is a string (******).');
$this->data['opposing-account-object'] = $this->parseIbanString();
return $this->data;
}
return null;
}
/**
* @return Account|null
*/
protected function parseIbanString()
{
// create by name and/or iban.
$accounts = Auth::user()->accounts()->get();
foreach ($accounts as $entry) {
if ($entry->iban == $this->data['opposing-account-iban']) {
Log::debug('OpposingAccountPostProcession: opposing-account-iban matches an Account.');
return $entry;
}
}
$account = $this->createAccount();
return $account;
}
/**
* @return Account|null
*/
protected function createAccount()
{
$accountType = $this->getAccountType();
// create if not exists:
$name = is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) > 0 ? $this->data['opposing-account-name']
: $this->data['opposing-account-iban'];
$account = Account::firstOrCreateEncrypted(
[
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'name' => $name,
'iban' => $this->data['opposing-account-iban'],
'active' => true,
]
);
Log::debug('OpposingAccountPostProcession: created a new account.');
return $account;
}
/**
*
* @return AccountType
*/
protected function getAccountType()
{
// opposing account type:
if ($this->data['amount'] < 0) {
// create expense account:
return AccountType::where('type', 'Expense account')->first();
} else {
// create revenue account:
return AccountType::where('type', 'Revenue account')->first();
}
}
/**
* @return array|null
*/
protected function checkNameString()
{
if ($this->data['opposing-account-name'] instanceof Account) { // third: try to find account based on name, if any.
Log::debug('OpposingAccountPostProcession: opposing-account-name is an Account.');
$this->data['opposing-account-object'] = $this->data['opposing-account-name'];
return $this->data;
}
if (is_string($this->data['opposing-account-name'])) {
$this->data['opposing-account-object'] = $this->parseNameString();
return $this->data;
}
return null;
}
/**
* @return Account|null
*/
protected function parseNameString()
{
$accountType = $this->getAccountType();
$accounts = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get();
foreach ($accounts as $entry) {
if ($entry->name == $this->data['opposing-account-name']) {
Log::debug('Found an account with this name (#' . $entry->id . ': ******)');
return $entry;
}
}
// create if not exists:
$account = Account::firstOrCreateEncrypted(
[
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'name' => $this->data['opposing-account-name'],
'iban' => '',
'active' => true,
]
);
return $account;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 05/07/15
* Time: 19:20
*/
namespace FireflyIII\Helpers\Csv\PostProcessing;
/**
* Interface PostProcessorInterface
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
interface PostProcessorInterface
{
/**
* @return array
*/
public function process();
/**
* @param array $data
*/
public function setData(array $data);
}

View File

@@ -0,0 +1,45 @@
<?php
namespace FireflyIII\Helpers\Csv\Specifix;
/**
* Class Dummy
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class Dummy
{
/** @var array */
protected $data;
/** @var array */
protected $row;
/**
* @return array
*/
public function fix()
{
return $this->data;
}
/**
* @param array $data
*/
public function setData($data)
{
$this->data = $data;
}
/**
* @param array $row
*/
public function setRow($row)
{
$this->row = $row;
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace FireflyIII\Helpers\Csv\Specifix;
use Log;
/**
* Class RabobankDescription
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class RabobankDescription
{
/** @var array */
protected $data;
/** @var array */
protected $row;
/**
* @return array
*/
public function fix()
{
$this->rabobankFixEmptyOpposing();
return $this->data;
}
/**
* 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
*/
public function setData($data)
{
$this->data = $data;
}
/**
* @param array $row
*/
public function setRow($row)
{
$this->row = $row;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace FireflyIII\Helpers\Csv\Specifix;
/**
* Interface SpecifixInterface
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
interface SpecifixInterface
{
/**
* Implement bank and locale related fixes.
*/
public function fix();
/**
* @param array $data
*/
public function setData($data);
/**
* @param array $row
*/
public function setRow($row);
}

194
app/Helpers/Csv/Wizard.php Normal file
View File

@@ -0,0 +1,194 @@
<?php
namespace FireflyIII\Helpers\Csv;
use Auth;
use Config;
use Crypt;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Csv\Mapper\MapperInterface;
use League\Csv\Reader;
use Log;
use ReflectionException;
use Session;
/**
* Class Wizard
*
* @package FireflyIII\Helpers\Csv
*/
class Wizard implements WizardInterface
{
/**
* @param Reader $reader
* @param array $map
* @param bool $hasHeaders
*
* @return array
*/
public function getMappableValues($reader, array $map, $hasHeaders)
{
$values = [];
/*
* Loop over the CSV and collect mappable data:
*/
$keys = array_keys($map);
foreach ($reader as $index => $row) {
if ($this->useRow($hasHeaders, $index)) {
// collect all map values
foreach ($keys as $column) {
$values[$column][] = $row[$column];
}
}
}
/*
* Make each one unique.
*/
$values = $this->uniqueRecursive($values);
return $values;
}
/**
* @param array $roles
* @param mixed $map
*
* @return array
*/
public function processSelectedMapping(array $roles, $map)
{
$configRoles = Config::get('csv.roles');
$maps = [];
if (is_array($map)) {
foreach ($map as $index => $field) {
if (isset($roles[$index])) {
$name = $roles[$index];
if ($configRoles[$name]['mappable']) {
$maps[$index] = $name;
}
}
}
}
return $maps;
}
/**
* @param mixed $input
*
* @return array
*/
public function processSelectedRoles($input)
{
$roles = [];
/*
* Store all rows for each column:
*/
if (is_array($input)) {
foreach ($input as $index => $role) {
if ($role != '_ignore') {
$roles[$index] = $role;
}
}
}
return $roles;
}
/**
* @param array $fields
*
* @return bool
*/
public function sessionHasValues(array $fields)
{
foreach ($fields as $field) {
if (!Session::has($field)) {
Log::error('Session is missing field: ' . $field);
return false;
}
}
return true;
}
/**
* @param array $map
*
* @return array
* @throws FireflyException
*/
public function showOptions(array $map)
{
$options = [];
foreach ($map as $index => $columnRole) {
$mapper = Config::get('csv.roles.' . $columnRole . '.mapper');
if (is_null($mapper)) {
throw new FireflyException('Cannot map field of type "' . $columnRole . '".');
}
$class = 'FireflyIII\Helpers\Csv\Mapper\\' . $mapper;
try {
/** @var MapperInterface $mapObject */
$mapObject = app($class);
} catch (ReflectionException $e) {
throw new FireflyException('Column "' . $columnRole . '" cannot be mapped because class ' . $mapper . ' does not exist.');
}
$set = $mapObject->getMap();
$options[$index] = $set;
}
return $options;
}
/**
* @param $path
*
* @return string
*/
public function storeCsvFile($path)
{
$time = str_replace(' ', '-', microtime());
$fileName = 'csv-upload-' . Auth::user()->id . '-' . $time . '.csv.encrypted';
$fullPath = storage_path('upload') . DIRECTORY_SEPARATOR . $fileName;
$content = file_get_contents($path);
$contentEncrypted = Crypt::encrypt($content);
file_put_contents($fullPath, $contentEncrypted);
return $fullPath;
}
/**
* @param bool $hasHeaders
* @param int $index
*
* @return bool
*/
protected function useRow($hasHeaders, $index)
{
return ($hasHeaders && $index > 1) || !$hasHeaders;
}
/**
* @param array $array
*
* @return array
*/
protected function uniqueRecursive(array $array)
{
foreach ($array as $column => $found) {
$array[$column] = array_unique($found);
}
return $array;
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace FireflyIII\Helpers\Csv;
use League\Csv\Reader;
/**
* Interface WizardInterface
*
* @package FireflyIII\Helpers\Csv
*/
interface WizardInterface
{
/**
* @param Reader $reader
* @param array $map
* @param bool $hasHeaders
*
* @return array
*/
public function getMappableValues($reader, array $map, $hasHeaders);
/**
* @param array $roles
* @param mixed $map
*
* @return array
*/
public function processSelectedMapping(array $roles, $map);
/**
* @param mixed $input
*
* @return array
*/
public function processSelectedRoles($input);
/**
* @param array $fields
*
* @return bool
*/
public function sessionHasValues(array $fields);
/**
* @param array $map
*
* @return array
*/
public function showOptions(array $map);
/**
* @param $path
*
* @return string
*/
public function storeCsvFile($path);
}

View File

@@ -37,10 +37,12 @@ class Help implements HelpInterface
*/
public function getFromGithub($route)
{
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/en/' . e($route) . '.md';
$content = [
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/en/' . e($route) . '.md';
$routeIndex = str_replace('.', '-', $route);
$title = trans('help.' . $routeIndex);
$content = [
'text' => '<p>There is no help for this route!</p>',
'title' => $route,
'title' => $title,
];
try {
$content['text'] = file_get_contents($uri);
@@ -69,6 +71,18 @@ class Help implements HelpInterface
return Route::has($route);
}
/**
* @codeCoverageIgnore
*
* @param $route
*
* @return bool
*/
public function inCache($route)
{
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
}
/**
* @codeCoverageIgnore
*
@@ -82,16 +96,4 @@ class Help implements HelpInterface
Cache::put('help.' . $route . '.text', $content['text'], 10080); // a week.
Cache::put('help.' . $route . '.title', $content['title'], 10080);
}
/**
* @codeCoverageIgnore
*
* @param $route
*
* @return bool
*/
public function inCache($route)
{
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
}
}

View File

@@ -1,176 +0,0 @@
<?php
namespace FireflyIII\Helpers\Reminders;
use Amount;
use Auth;
use Carbon\Carbon;
use Config;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder;
use Navigation;
use Preferences;
/**
* Class ReminderHelper
*
* @package FireflyIII\Helpers\Reminders
*/
class ReminderHelper implements ReminderHelperInterface
{
/**
* @param PiggyBank $piggyBank
* @param Carbon $start
* @param Carbon $end
*
* @return Reminder
*/
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end)
{
$reminder = Auth::user()->reminders()->where('remindersable_id', $piggyBank->id)->onDates($start, $end)->first();
if (is_null($reminder)) {
if (!is_null($piggyBank->targetdate)) {
// get ranges again, but now for the start date
$ranges = $this->getReminderRanges($piggyBank, $start);
$currentRep = $piggyBank->currentRelevantRep();
$left = $piggyBank->targetamount - $currentRep->currentamount;
$perReminder = count($ranges) == 0 ? $left : $left / count($ranges);
} else {
$perReminder = null;
$ranges = [];
$left = 0;
}
$metaData = [
'perReminder' => $perReminder,
'rangesCount' => count($ranges),
'ranges' => $ranges,
'leftToSave' => $left,
];
$reminder = new Reminder;
$reminder->user()->associate(Auth::user());
$reminder->startdate = $start;
$reminder->enddate = $end;
$reminder->active = true;
$reminder->metadata = $metaData;
$reminder->notnow = false;
$reminder->remindersable()->associate($piggyBank);
$reminder->save();
return $reminder;
} else {
return $reminder;
}
}
/**
* Create all reminders for a piggy bank for a given date.
*
* @param PiggyBank $piggyBank
*
* @param Carbon $date
*
* @return mixed
*/
public function createReminders(PiggyBank $piggyBank, Carbon $date)
{
$ranges = $this->getReminderRanges($piggyBank);
foreach ($ranges as $range) {
if ($date < $range['end'] && $date > $range['start']) {
// create a reminder here!
$this->createReminder($piggyBank, $range['start'], $range['end']);
// stop looping, we're done.
break;
}
}
}
/**
* This routine will return an array consisting of two dates which indicate the start
* and end date for each reminder that this piggy bank will have, if the piggy bank has
* any reminders. For example:
*
* [12 mar - 15 mar]
* [15 mar - 18 mar]
*
* etcetera.
*
* Array is filled with tiny arrays with Carbon objects in them.
*
* @param PiggyBank $piggyBank
* @param Carbon $date ;
*
* @return array
*/
public function getReminderRanges(PiggyBank $piggyBank, Carbon $date = null)
{
$ranges = [];
if (is_null($date)) {
$date = new Carbon;
}
if ($piggyBank->remind_me === false) {
return $ranges;
}
if (!is_null($piggyBank->targetdate)) {
// count back until now.
$start = $piggyBank->targetdate;
$end = $piggyBank->startdate;
while ($start > $end) {
$currentEnd = clone $start;
$start = Navigation::subtractPeriod($start, $piggyBank->reminder, 1);
$currentStart = clone $start;
$ranges[] = ['start' => clone $currentStart, 'end' => clone $currentEnd];
}
} else {
$start = clone $piggyBank->startdate;
while ($start < $date) {
$currentStart = clone $start;
$start = Navigation::addPeriod($start, $piggyBank->reminder, 0);
$currentEnd = clone $start;
$ranges[] = ['start' => clone $currentStart, 'end' => clone $currentEnd];
}
}
return $ranges;
}
/**
* Takes a reminder, finds the piggy bank and tells you what to do now.
* Aka how much money to put in.
*
*
* @param Reminder $reminder
*
* @return string
*/
public function getReminderText(Reminder $reminder)
{
/** @var PiggyBank $piggyBank */
$piggyBank = $reminder->remindersable;
if (is_null($piggyBank)) {
return trans('firefly.piggy_bank_not_exists');
}
if (is_null($piggyBank->targetdate)) {
return trans('firefly.add_any_amount_to_piggy', ['amount' => Amount::format($piggyBank->targetamount)]);
}
$lang = Preferences::get('language', 'en')->data;
return trans(
'firefly.add_set_amount_to_piggy',
[
'amount' => Amount::format($reminder->metadata->perReminder),
'date' => $piggyBank->targetdate->formatLocalized(Config::get('firefly.monthAndDay.' . $lang))
]
);
}
}

View File

@@ -1,63 +0,0 @@
<?php
namespace FireflyIII\Helpers\Reminders;
use Carbon\Carbon;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Reminder;
/**
* Interface ReminderHelperInterface
*
* @package FireflyIII\Helpers\Reminders
*/
interface ReminderHelperInterface
{
/**
* Takes a reminder, finds the piggy bank and tells you what to do now.
* Aka how much money to put in.
*
* @param Reminder $reminder
*
* @return string
*/
public function getReminderText(Reminder $reminder);
/**
* This routine will return an array consisting of two dates which indicate the start
* and end date for each reminder that this piggy bank will have, if the piggy bank has
* any reminders. For example:
*
* [12 mar - 15 mar]
* [15 mar - 18 mar]
*
* etcetera.
*
* Array is filled with tiny arrays with Carbon objects in them.
*
* @param PiggyBank $piggyBank
*
* @return array
*/
public function getReminderRanges(PiggyBank $piggyBank);
/**
* @param PiggyBank $piggyBank
* @param Carbon $start
* @param Carbon $end
*
* @return Reminder
*/
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end);
/**
* Create all reminders for a piggy bank for a given date.
*
* @param PiggyBank $piggyBank
*
* @param Carbon $date
*
* @return mixed
*/
public function createReminders(PiggyBank $piggyBank, Carbon $date);
}

View File

@@ -2,7 +2,6 @@
namespace FireflyIII\Helpers\Report;
use App;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use FireflyIII\Helpers\Collection\Balance;
@@ -20,6 +19,8 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Support\Collection;
use Steam;
/**
* Class ReportHelper
@@ -44,45 +45,113 @@ class ReportHelper implements ReportHelperInterface
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return CategoryCollection
*/
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();
foreach ($set as $category) {
$spent = $repository->balanceInPeriod($category, $start, $end, $accounts);
$category->spent = $spent;
$object->addCategory($category);
}
return $object;
}
/**
* @param Carbon $date
*
* @return array
*/
public function listOfMonths(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$months = [];
while ($start <= $end) {
$year = $start->year;
if (!isset($months[$year])) {
$months[$year] = [
'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();
}
return $months;
}
/**
* This method generates a full report for the given period on all
* the users asset and cash accounts.
* given accounts
*
* @param Carbon $date
* @param Carbon $end
* @param $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Account
* @return AccountCollection
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared)
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts)
{
$startAmount = '0';
$endAmount = '0';
$diff = '0';
bcscale(2);
$accounts->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();
$accounts = $this->query->getAllAccounts($date, $end, $shared);
$start = 0;
$end = 0;
$diff = 0;
// remove cash account, if any:
$accounts = $accounts->filter(
function (Account $account) {
if ($account->accountType->type != 'Cash account') {
return $account;
}
} // @codeCoverageIgnore
/** @noinspection PhpParamsInspection */
$account->startBalance = Steam::balance($account, $yesterday);
$account->endBalance = Steam::balance($account, $end);
}
);
// summarize:
foreach ($accounts as $account) {
$start += $account->startBalance;
$end += $account->endBalance;
$diff += ($account->endBalance - $account->startBalance);
$startAmount = bcadd($startAmount, $account->startBalance);
$endAmount = bcadd($endAmount, $account->endBalance);
$diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance));
}
$object = new AccountCollection;
$object->setStart($start);
$object->setEnd($end);
$object->setStart($startAmount);
$object->setEnd($endAmount);
$object->setDifference($diff);
$object->setAccounts($accounts);
@@ -90,37 +159,136 @@ class ReportHelper implements ReportHelperInterface
}
/**
* Get a full report on the users incomes during the period for the given accounts.
*
* The balance report contains a Balance object which in turn contains:
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* A BalanceHeader object which contains all relevant user asset accounts for the report.
* @return Income
*/
public function getIncomeReport($start, $end, Collection $accounts)
{
$object = new Income;
$set = $this->query->incomeInPeriod($start, $end, $accounts);
foreach ($set as $entry) {
$object->addToTotal($entry->amount_positive);
$object->addOrCreateIncome($entry);
}
return $object;
}
/**
* Get a full report on the users expenses during the period for a list of accounts.
*
* A number of BalanceLine objects, which hold:
* - A budget
* - A number of BalanceEntry objects.
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* 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
* @return Expense
*/
public function getExpenseReport($start, $end, Collection $accounts)
{
$object = new Expense;
$set = $this->query->expenseInPeriod($start, $end, $accounts);
foreach ($set as $entry) {
$object->addToTotal($entry->amount); // can be positive, if it's a transfer
$object->addOrCreateExpense($entry);
}
return $object;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @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();
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, $accounts);
$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, $start, $end, $accounts);
// 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($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);
$budgetLine->setSpent($noBudget);
$object->addOverspent($noBudget);
$object->addBudgetLine($budgetLine);
return $object;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, $shared)
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts)
{
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface');
$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();
$header = new BalanceHeader;
$budgets = $repository->getBudgets();
foreach ($accounts as $account) {
$header->addAccount($account);
}
@@ -131,7 +299,8 @@ class ReportHelper implements ReportHelperInterface
$line->setBudget($budget);
// get budget amount for current period:
$rep = $repository->getCurrentRepetition($budget, $start);
$rep = $repository->getCurrentRepetition($budget, $start, $end);
// could be null?
$line->setRepetition($rep);
// loop accounts:
@@ -140,7 +309,7 @@ class ReportHelper implements ReportHelperInterface
$balanceEntry->setAccount($account);
// get spent:
$spent = $this->query->spentInBudgetCorrected($account, $budget, $start, $end); // I think shared is irrelevant.
$spent = $this->query->spentInBudget($account, $budget, $start, $end); // I think shared is irrelevant.
$balanceEntry->setSpent($spent);
$line->addBalanceEntry($balanceEntry);
@@ -151,6 +320,7 @@ class ReportHelper implements ReportHelperInterface
// then a new line for without budget.
// and one for the tags:
// and one for "left unbalanced".
$empty = new BalanceLine;
$tags = new BalanceLine;
$diffLine = new BalanceLine;
@@ -161,7 +331,8 @@ class ReportHelper implements ReportHelperInterface
foreach ($accounts as $account) {
$spent = $this->query->spentNoBudget($account, $start, $end);
$left = $tagRepository->coveredByBalancingActs($account, $start, $end);
$diff = $spent + $left;
bcscale(2);
$diff = bcadd($spent, $left);
// budget
$budgetEntry = new BalanceEntry;
@@ -196,17 +367,19 @@ class ReportHelper implements ReportHelperInterface
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* 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, $shared)
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts)
{
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$bills = $repository->getBills();
$repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$bills = $repository->getBillsForAccounts($accounts);
$collection = new BillCollection;
/** @var Bill $bill */
@@ -214,18 +387,19 @@ class ReportHelper implements ReportHelperInterface
$billLine = new BillLine;
$billLine->setBill($bill);
$billLine->setActive(intval($bill->active) == 1);
$billLine->setMin(floatval($bill->amount_min));
$billLine->setMax(floatval($bill->amount_max));
$billLine->setMin($bill->amount_min);
$billLine->setMax($bill->amount_max);
// 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;
$amount = '0';
foreach ($set as $entry) {
$amount += $entry->amount;
$amount = bcadd($amount, $entry->amount);
}
$billLine->setAmount($amount);
}
@@ -235,165 +409,5 @@ 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::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$set = $repository->getBudgets();
foreach ($set as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
// no repetition(s) for this budget:
if ($repetitions->count() == 0) {
$spent = $repository->spentInPeriodCorrected($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->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, $shared);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($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
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared)
{
$object = new CategoryCollection;
/**
* GET CATEGORIES:
*/
/** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
$set = $repository->getCategories();
foreach ($set as $category) {
$spent = $repository->spentInPeriodCorrected($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.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Expense
*/
public function getExpenseReport($start, $end, $shared)
{
$object = new Expense;
$set = $this->query->expenseInPeriodCorrected($start, $end, $shared);
foreach ($set as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateExpense($entry);
}
return $object;
}
/**
* Get a full report on the users incomes during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Income
*/
public function getIncomeReport($start, $end, $shared)
{
$object = new Income;
$set = $this->query->incomeInPeriodCorrected($start, $end, $shared);
foreach ($set as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateIncome($entry);
}
return $object;
}
/**
* @param Carbon $date
*
* @return array
*/
public function listOfMonths(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$months = [];
while ($start <= $end) {
$year = $start->year;
$months[$year][] = [
'formatted' => $start->formatLocalized('%B %Y'),
'month' => $start->month,
'year' => $year,
];
$start->addMonth();
}
return $months;
}
}

View File

@@ -3,12 +3,14 @@
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account;
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
@@ -20,76 +22,78 @@ interface ReportHelperInterface
/**
* This method generates a full report for the given period on all
* the users asset and cash accounts.
* given accounts
*
* @param Carbon $date
* @param Carbon $end
* @param boolean $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Account
* @return AccountCollection
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared);
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts);
/**
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* Excludes bills which have not had a payment on the mentioned accounts.
*
* @return Account
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return BillCollection
*/
public function getBillReport(Carbon $start, Carbon $end, $shared);
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, $shared);
public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return BudgetCollection
*/
public function getBudgetReport(Carbon $start, Carbon $end, $shared);
public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts);
/**
* @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

@@ -8,10 +8,10 @@ use Crypt;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Steam;
/**
* Class ReportQuery
@@ -20,180 +20,6 @@ use Steam;
*/
class ReportQuery implements ReportQueryInterface
{
/**
* See ReportQueryInterface::incomeInPeriodCorrected
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*
*/
public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
{
$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"');
}
);
}
);
} else {
$query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine.
}
$query->orderBy('transaction_journals.date');
// get everything
$data = $query->get(
['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);
}
}
);
$data = $data->filter(
function (TransactionJournal $journal) {
if ($journal->amount != 0) {
return $journal;
}
} // @codeCoverageIgnore
);
return $data;
}
/**
* Get a users accounts combined with various meta-data related to the start and end date.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false)
{
$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');
}
)
->orderBy('accounts.name', 'ASC')
->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);
}
);
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 "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
*
* @return Collection
*/
public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
{
$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"');
}
);
}
);
} else {
// any deposit is fine.
$query->where('transaction_types.type', 'Deposit');
}
$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']
);
$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;
}
} // @codeCoverageIgnore
);
return $data;
}
/**
* Covers tags
*
@@ -204,42 +30,38 @@ class ReportQuery implements ReportQueryInterface
*
* @return float
*/
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end)
public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end)
{
return floatval(
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;
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([TransactionType::WITHDRAWAL])
->where('transactions.account_id', $account->id)
->before($end)
->after($start)
->where('budget_transaction_journal.budget_id', $budget->id)
->get(['transaction_journals.*'])->sum('amount');
}
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
* @param bool $shared
*
* @return float
* @return string
*/
public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false)
public function spentNoBudget(Account $account, Carbon $start, Carbon $end)
{
return floatval(
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'])
->transactionTypes([TransactionType::WITHDRAWAL])
->where('transactions.account_id', $account->id)
->before($end)
->after($start)
->whereNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*'])->sum('amount')
);
->whereNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*'])->sum('amount');
}
/**
@@ -278,4 +100,135 @@ class ReportQuery implements ReportQueryInterface
return $query;
}
/**
* 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. It will only get the incomes to the specified accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Collection
*/
public function incomeInPeriod(Carbon $start, Carbon $end, Collection $accounts)
{
$query = $this->queryJournalsWithTransactions($start, $end);
$ids = [];
/** @var Account $account */
foreach ($accounts as $account) {
$ids[] = $account->id;
}
// OR is a deposit
// OR any transfer TO the accounts in $accounts, not FROM any of the accounts in $accounts.
$query->where(
function (Builder $query) use ($ids) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', TransactionType::DEPOSIT);
}
);
$query->orWhere(
function (Builder $q) use ($ids) {
$q->where('transaction_types.type', TransactionType::TRANSFER);
$q->whereNotIn('ac_from.id', $ids);
$q->whereIn('ac_to.id', $ids);
}
);
}
);
// only include selected accounts.
$query->whereIn('ac_to.id', $ids);
$query->orderBy('transaction_journals.date');
// get everything
$data = $query->get(
['transaction_journals.*',
'transaction_types.type', 'ac_from.name as name',
't_from.amount as from_amount',
't_to.amount as to_amount',
'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted']
);
$data->each(
function (TransactionJournal $journal) {
if (intval($journal->account_encrypted) == 1) {
$journal->name = Crypt::decrypt($journal->name);
}
}
);
return $data;
}
/**
* See ReportQueryInterface::incomeInPeriod
*
* 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 Collection $accounts
*
* @return Collection
*
*/
public function expenseInPeriod(Carbon $start, Carbon $end, Collection $accounts)
{
$ids = [];
/** @var Account $account */
foreach ($accounts as $account) {
$ids[] = $account->id;
}
$query = $this->queryJournalsWithTransactions($start, $end);
// withdrawals from any account are an expense.
// transfers away, from an account in the list, to an account not in the list, are an expense.
$query->where(
function (Builder $query) use ($ids) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', TransactionType::WITHDRAWAL);
}
);
$query->orWhere(
function (Builder $q) use ($ids) {
$q->where('transaction_types.type', TransactionType::TRANSFER);
$q->whereIn('ac_from.id', $ids);
$q->whereNotIn('ac_to.id', $ids);
}
);
}
);
// expense goes from the selected accounts:
$query->whereIn('ac_from.id', $ids);
$query->orderBy('transaction_journals.date');
$data = $query->get( // get everything
['transaction_journals.*', 'transaction_types.type',
't_from.amount as from_amount',
't_to.amount as to_amount',
'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;
}
}

View File

@@ -16,44 +16,33 @@ interface ReportQueryInterface
{
/**
* See ReportQueryInterface::incomeInPeriodCorrected
* See ReportQueryInterface::incomeInPeriod
*
* 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
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Collection
*
*/
public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false);
/**
* Get a users accounts combined with various meta-data related to the start and end date.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*/
public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false);
public function expenseInPeriod(Carbon $start, Carbon $end, Collection $accounts);
/**
* 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.
* regards to tags. It will only get the incomes to the specified accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return Collection
*/
public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false);
public function incomeInPeriod(Carbon $start, Carbon $end, Collection $accounts);
/**
* Covers tags as well.
@@ -65,17 +54,16 @@ interface ReportQueryInterface
*
* @return float
*/
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end);
public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end);
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
* @param bool $shared
*
* @return float
* @return string
*/
public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false);
public function spentNoBudget(Account $account, Carbon $start, Carbon $end);
}

View File

@@ -3,12 +3,12 @@
use Auth;
use Carbon\Carbon;
use Config;
use FireflyIII\Http\Requests;
use ExpandedForm;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input;
use Redirect;
use Preferences;
use Session;
use Steam;
use URL;
@@ -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,21 +56,24 @@ class AccountController extends Controller
}
/**
* @param Account $account
* @param AccountRepositoryInterface $repository
* @param Account $account
*
* @return \Illuminate\View\View
*/
public function delete(Account $account)
public function delete(AccountRepositoryInterface $repository, Account $account)
{
$typeName = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]);
$typeName = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]);
$accountList = Expandedform::makeSelectList($repository->getAccounts([$account->accountType->type]), true);
unset($accountList[$account->id]);
// put previous url in session
Session::put('accounts.delete.url', URL::previous());
Session::flash('gaEventCategory', 'accounts');
Session::flash('gaEventAction', 'delete-' . $typeName);
return view('accounts.delete', compact('account', 'subTitle'));
return view('accounts.delete', compact('account', 'subTitle', 'accountList'));
}
/**
@@ -79,23 +84,24 @@ class AccountController extends Controller
*/
public function destroy(AccountRepositoryInterface $repository, Account $account)
{
$type = $account->accountType->type;
$typeName = Config::get('firefly.shortNamesByFullName.' . $type);
$name = $account->name;
$moveTo = Auth::user()->accounts()->find(intval(Input::get('move_account_before_delete')));
$repository->destroy($account);
$repository->destroy($account, $moveTo);
Session::flash('success', trans('firefly.' . $typeName . '_deleted', ['name' => $name]));
Preferences::mark();
return Redirect::to(Session::get('accounts.delete.url'));
return redirect(Session::get('accounts.delete.url'));
}
/**
* @param AccountRepositoryInterface $repository
* @param Account $account
*
* @return View
* @return \Illuminate\View\View
*/
public function edit(AccountRepositoryInterface $repository, Account $account)
{
@@ -127,7 +133,7 @@ class AccountController extends Controller
'ccMonthlyPaymentDate' => $account->getMeta('ccMonthlyPaymentDate'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount,
'virtualBalance' => floatval($account->virtual_balance)
'virtualBalance' => round($account->virtual_balance, 2)
];
Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'accounts');
@@ -140,7 +146,7 @@ class AccountController extends Controller
* @param AccountRepositoryInterface $repository
* @param $what
*
* @return View
* @return \Illuminate\View\View
*/
public function index(AccountRepositoryInterface $repository, $what)
{
@@ -153,12 +159,24 @@ class AccountController extends Controller
* HERE WE ARE
*/
$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;
}
$startBalances = Steam::balancesById($ids, $start);
$endBalances = Steam::balancesById($ids, $end);
$activities = Steam::getLastActivities($ids);
$accounts->each(
function (Account $account) use ($start, $repository) {
$account->lastActivityDate = $repository->getLastActivity($account);
$account->startBalance = Steam::balance($account, $start);
$account->endBalance = Steam::balance($account, clone Session::get('end', Carbon::now()->endOfMonth()));
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;
}
);
@@ -169,7 +187,7 @@ class AccountController extends Controller
* @param AccountRepositoryInterface $repository
* @param Account $account
*
* @return View
* @return \Illuminate\View\View
*/
public function show(AccountRepositoryInterface $repository, Account $account)
{
@@ -195,30 +213,32 @@ class AccountController extends Controller
$accountData = [
'name' => $request->input('name'),
'accountType' => $request->input('what'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'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' => floatval($request->input('openingBalance')),
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
];
$account = $repository->store($accountData);
$account = $repository->store($accountData);
Session::flash('success', 'New account "' . $account->name . '" stored!');
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('accounts.create.fromStore', true);
return Redirect::route('accounts.create')->withInput();
return redirect(route('accounts.create', [$request->input('what')]))->withInput();
}
// redirect to previous URL.
return Redirect::to(Session::get('accounts.create.url'));
return redirect(Session::get('accounts.create.url'));
}
/**
@@ -226,7 +246,7 @@ class AccountController extends Controller
* @param AccountRepositoryInterface $repository
* @param Account $account
*
* @return $this|\Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function update(AccountFormRequest $request, AccountRepositoryInterface $repository, Account $account)
{
@@ -235,28 +255,31 @@ class AccountController extends Controller
'name' => $request->input('name'),
'active' => $request->input('active'),
'user' => Auth::user()->id,
'iban' => $request->input('iban'),
'accountRole' => $request->input('accountRole'),
'virtualBalance' => floatval($request->input('virtualBalance')),
'openingBalance' => floatval($request->input('openingBalance')),
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
'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')),
'ccType' => $request->input('ccType'),
'ccMonthlyPaymentDate' => $request->input('ccMonthlyPaymentDate'),
];
$repository->update($account, $accountData);
Session::flash('success', 'Account "' . $account->name . '" updated.');
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('accounts.edit.fromUpdate', true);
return Redirect::route('accounts.edit', $account->id)->withInput(['return_to_edit' => 1]);
return redirect(route('accounts.edit', [$account->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return Redirect::to(Session::get('accounts.edit.url'));
return redirect(Session::get('accounts.edit.url'));
}

View File

@@ -0,0 +1,174 @@
<?php
namespace FireflyIII\Http\Controllers;
use Crypt;
use File;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Requests\AttachmentFormRequest;
use FireflyIII\Models\Attachment;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Input;
use Preferences;
use Response;
use Session;
use URL;
use View;
/**
* Class AttachmentController
*
* @package FireflyIII\Http\Controllers
*/
class AttachmentController extends Controller
{
/**
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
View::share('mainTitleIcon', 'fa-paperclip');
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
*
* @return \Illuminate\View\View
*/
public function delete(Attachment $attachment)
{
$subTitle = trans('firefly.delete_attachment', ['name' => $attachment->filename]);
// put previous url in session
Session::put('attachments.delete.url', URL::previous());
Session::flash('gaEventCategory', 'attachments');
Session::flash('gaEventAction', 'delete-attachment');
return view('attachments.delete', compact('attachment', 'subTitle'));
}
/**
* @param AttachmentRepositoryInterface $repository
* @param Attachment $attachment
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(AttachmentRepositoryInterface $repository, Attachment $attachment)
{
$name = $attachment->filename;
$repository->destroy($attachment);
Session::flash('success', trans('firefly.attachment_deleted', ['name' => $name]));
Preferences::mark();
return redirect(Session::get('attachments.delete.url'));
}
/**
* @param Attachment $attachment
* @param AttachmentHelperInterface $helper
*/
public function download(Attachment $attachment, AttachmentHelperInterface $helper)
{
$file = $helper->getAttachmentLocation($attachment);
if (file_exists($file)) {
$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));
} else {
abort(404);
}
}
/**
* @param Attachment $attachment
*
* @return \Illuminate\Http\Response
*/
public function preview(Attachment $attachment)
{
if ($attachment->mime == 'application/pdf') {
$file = public_path('images/page_white_acrobat.png');
} else {
$file = public_path('images/page_green.png');
}
$response = Response::make(File::get($file));
$response->header('Content-Type', 'image/png');
return $response;
}
/**
* @param AttachmentFormRequest $request
* @param AttachmentRepositoryInterface $repository
* @param Attachment $attachment
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(AttachmentFormRequest $request, AttachmentRepositoryInterface $repository, Attachment $attachment)
{
$attachmentData = [
'title' => $request->input('title'),
'description' => $request->input('description'),
'notes' => $request->input('notes'),
];
$repository->update($attachment, $attachmentData);
Session::flash('success', 'Attachment "' . $attachment->filename . '" updated.');
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('attachments.edit.fromUpdate', true);
return redirect(route('attachments.edit', [$attachment->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return redirect(Session::get('attachments.edit.url'));
}
}

View File

@@ -1,14 +1,20 @@
<?php namespace FireflyIII\Http\Controllers\Auth;
use Auth;
use Config;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\Registrar;
use FireflyIII\Models\Role;
use FireflyIII\User;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Request as Rq;
use Session;
use Twig;
use Validator;
/**
* Class AuthController
@@ -17,35 +23,108 @@ use Twig;
*/
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
/**
* Log the user out of the application.
*
* @return \Illuminate\Http\Response
*/
public function getLogout()
{
Auth::logout();
return redirect('/auth/login');
}
/**
* Show the application registration form.
*
* @return \Illuminate\Http\Response
*/
public function getRegister()
{
$host = Rq::getHttpHost();
return view('auth.register', compact('host'));
}
/**
* Handle a login request to the application.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function postLogin(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.
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
return $this->sendLockoutResponse($request);
}
$credentials = $this->getCredentials($request);
$credentials['blocked'] = 0; // most not be blocked.
if (Auth::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.
/** @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;
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,
]
);
}
use AuthenticatesAndRegistersUsers;
public $redirectTo = '/';
/**
* Create a new authentication controller instance.
*
* @param \Illuminate\Contracts\Auth\Guard $auth
* @param \Illuminate\Contracts\Auth\Registrar $registrar
*
* @codeCoverageIgnore
*
*/
public function __construct(Guard $auth, Registrar $registrar)
public function __construct()
{
$this->auth = $auth;
$this->registrar = $registrar;
parent::__construct();
$this->middleware('guest', ['except' => 'getLogout']);
}
@@ -67,11 +146,11 @@ class AuthController extends Controller
*
* @param Request $request
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function postRegister(Request $request)
{
$validator = $this->registrar->validator($request->all());
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
@@ -81,28 +160,122 @@ class AuthController extends Controller
}
// @codeCoverageIgnoreEnd
$data = $request->all();
$data['password'] = bcrypt($data['password']);
$this->auth->login($this->registrar->create($data));
// is user email domain blocked?
if ($this->isBlockedDomain($data['email'])) {
$validator->getMessageBag()->add('email', trans('validation.invalid_domain'));
$this->throwValidationException(
$request, $validator
);
}
Auth::login($this->create($data));
// get the email address
$email = $this->auth->user()->email;
if (Auth::user() instanceof User) {
$email = Auth::user()->email;
$address = route('index');
$ipAddress = $request->ip();
// send email.
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());
}
// send email.
Mail::send(
'emails.registered', [], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III!');
// set flash message
Session::flash('success', 'You have registered successfully!');
Session::flash('gaEventCategory', 'user');
Session::flash('gaEventAction', 'new-registration');
// first user ever?
if (User::count() == 1) {
$admin = Role::where('name', 'owner')->first();
Auth::user()->attachRole($admin);
}
return redirect($this->redirectPath());
}
);
// @codeCoverageIgnoreStart
abort(500, 'Not a user!');
// set flash message
Session::flash('success', 'You have registered successfully!');
Session::flash('gaEventCategory', 'user');
Session::flash('gaEventAction', 'new-registration');
return redirect($this->redirectPath());
return redirect('/');
// @codeCoverageIgnoreEnd
}
/**
* @return array
*/
protected function getBlockedDomains()
{
$set = Config::get('mail.blocked_domains');
$domains = [];
foreach ($set as $entry) {
$domain = trim($entry);
if (strlen($domain) > 0) {
$domains[] = $domain;
}
}
return $domains;
}
/**
* @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 a validator for an incoming registration request.
*
* @param array $data
*
* @return \Illuminate\Contracts\Validation\Validator
*/
public function validator(array $data)
{
return Validator::make(
$data, [
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]
);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
*
* @return User
*/
public function create(array $data)
{
return User::create(
[
'email' => $data['email'],
'password' => $data['password'],
]
);
}
}

View File

@@ -1,9 +1,11 @@
<?php namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\PasswordBroker;
use FireflyIII\User;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Illuminate\Support\Facades\Password;
/**
* Class PasswordController
@@ -33,19 +35,50 @@ class PasswordController extends Controller
/**
* Create a new password controller instance.
*
* @param \Illuminate\Contracts\Auth\Guard $auth
* @param \Illuminate\Contracts\Auth\PasswordBroker $passwords
*
* @codeCoverageIgnore
*
*/
public function __construct(Guard $auth, PasswordBroker $passwords)
public function __construct()
{
$this->auth = $auth;
$this->passwords = $passwords;
parent::__construct();
$this->middleware('guest');
}
/**
* Send a reset link to the given user.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function postEmail(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 redirect()->back()->with('status', trans($response));
case Password::INVALID_USER:
case 'passwords.blocked':
return redirect()->back()->withErrors(['email' => trans($response)]);
}
abort(404);
return '';
}
}

View File

@@ -1,14 +1,12 @@
<?php namespace FireflyIII\Http\Controllers;
use Config;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BillFormRequest;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input;
use Redirect;
use Preferences;
use Session;
use URL;
use View;
@@ -32,11 +30,13 @@ class BillController extends Controller
}
/**
* @return $this
* @return \Illuminate\View\View
*/
public function create()
{
$periods = Config::get('firefly.periods_to_text');
$periods = Config::get('firefly.periods_to_text');
$subTitle = trans('firefly.create_new_bill');
// put previous url in session if not redirect from store (not "create another").
if (Session::get('bills.create.fromStore') !== true) {
@@ -45,7 +45,6 @@ class BillController extends Controller
Session::forget('bills.create.fromStore');
Session::flash('gaEventCategory', 'bills');
Session::flash('gaEventAction', 'create');
$subTitle = 'Create new bill';
return view('bills.create', compact('periods', 'subTitle'));
}
@@ -53,7 +52,7 @@ class BillController extends Controller
/**
* @param Bill $bill
*
* @return $this
* @return \Illuminate\View\View
*/
public function delete(Bill $bill)
{
@@ -77,20 +76,20 @@ class BillController extends Controller
$repository->destroy($bill);
Session::flash('success', 'The bill was deleted.');
Preferences::mark();
return Redirect::to(Session::get('bills.delete.url'));
return redirect(Session::get('bills.delete.url'));
}
/**
* @param Bill $bill
*
* @return $this
* @return \Illuminate\View\View
*/
public function edit(Bill $bill)
{
$periods = Config::get('firefly.periods_to_text');
$subTitle = 'Edit "' . e($bill->name) . '"';
$subTitle = trans('firefly.edit_bill', ['name' => $bill->name]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('bills.edit.fromUpdate') !== true) {
@@ -132,7 +131,7 @@ class BillController extends Controller
if (intval($bill->active) == 0) {
Session::flash('warning', 'Inactive bills cannot be scanned.');
return Redirect::to(URL::previous());
return redirect(URL::previous());
}
$journals = $repository->getPossiblyRelatedJournals($bill);
@@ -143,15 +142,16 @@ class BillController extends Controller
Session::flash('success', 'Rescanned everything.');
Preferences::mark();
return Redirect::to(URL::previous());
return redirect(URL::previous());
}
/**
* @param BillRepositoryInterface $repository
* @param Bill $bill
*
* @return mixed
* @return \Illuminate\View\View
*/
public function show(BillRepositoryInterface $repository, Bill $bill)
{
@@ -167,23 +167,24 @@ class BillController extends Controller
* @param BillFormRequest $request
* @param BillRepositoryInterface $repository
*
* @return $this|\Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function store(BillFormRequest $request, BillRepositoryInterface $repository)
{
$billData = $request->getBillData();
$bill = $repository->store($billData);
Session::flash('success', 'Bill "' . e($bill->name) . '" stored.');
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('bills.create.fromStore', true);
return Redirect::route('bills.create')->withInput();
return redirect(route('bills.create'))->withInput();
}
// redirect to previous URL.
return Redirect::to(Session::get('bills.create.url'));
return redirect(Session::get('bills.create.url'));
}
@@ -192,7 +193,7 @@ class BillController extends Controller
* @param BillRepositoryInterface $repository
* @param Bill $bill
*
* @return $this|\Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function update(BillFormRequest $request, BillRepositoryInterface $repository, Bill $bill)
{
@@ -200,16 +201,17 @@ class BillController extends Controller
$bill = $repository->update($bill, $billData);
Session::flash('success', 'Bill "' . e($bill->name) . '" updated.');
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('bills.edit.fromUpdate', true);
return Redirect::route('bills.edit', $bill->id)->withInput(['return_to_edit' => 1]);
return redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return Redirect::to(Session::get('bills.edit.url'));
return redirect(Session::get('bills.edit.url'));
}

View File

@@ -3,14 +3,14 @@
use Amount;
use Auth;
use Carbon\Carbon;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Input;
use Navigation;
use Preferences;
use Redirect;
use Response;
use Session;
use URL;
@@ -20,6 +20,7 @@ use View;
* Class BudgetController
*
* @package FireflyIII\Http\Controllers
* @SuppressWarnings(PHPMD.TooManyMethods)
*/
class BudgetController extends Controller
{
@@ -49,13 +50,14 @@ class BudgetController extends Controller
if ($amount == 0) {
$limitRepetition = null;
}
Preferences::mark();
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition ? $limitRepetition->id : 0]);
}
/**
* @return $this
* @return \Illuminate\View\View
*/
public function create()
{
@@ -102,19 +104,20 @@ class BudgetController extends Controller
Session::flash('success', 'The budget "' . e($name) . '" was deleted.');
Preferences::mark();
return Redirect::to(Session::get('budgets.delete.url'));
return redirect(Session::get('budgets.delete.url'));
}
/**
* @param Budget $budget
*
* @return $this
* @return \Illuminate\View\View
*/
public function edit(Budget $budget)
{
$subTitle = 'Edit budget "' . e($budget->name) . '"';
$subTitle = trans('firefly.edit_budget', ['name' => $budget->name]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('budgets.edit.fromUpdate') !== true) {
@@ -129,30 +132,37 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param BudgetRepositoryInterface $repository
*
* @return View
* @param AccountRepositoryInterface $accountRepository
*
* @return \Illuminate\View\View
*/
public function index(BudgetRepositoryInterface $repository)
public function index(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
{
$budgets = $repository->getActiveBudgets();
$inactive = $repository->getInactiveBudgets();
$spent = '0';
$budgeted = '0';
$budgets = $repository->getActiveBudgets();
$inactive = $repository->getInactiveBudgets();
$spent = '0';
$budgeted = '0';
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
$end = Navigation::endOfPeriod($start, $range);
$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:
*/
$repository->cleanupBudgets();
// loop the budgets:
/** @var Budget $budget */
foreach ($budgets as $budget) {
$date = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$budget->spent = $repository->spentInPeriodCorrected($budget, $date, $end);
$budget->currentRep = $repository->getCurrentRepetition($budget, $date);
$budget->spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
$budget->currentRep = $repository->getCurrentRepetition($budget, $start, $end);
if ($budget->currentRep) {
$budgeted = bcadd($budgeted, $budget->currentRep->amount);
}
@@ -160,25 +170,25 @@ class BudgetController extends Controller
}
$dateAsString = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
$budgetIncomeTotal = Preferences::get('budgetIncomeTotal' . $dateAsString, 1000)->data;
$budgetMaximum = Preferences::get('budgetMaximum', 1000)->data;
$defaultCurrency = Amount::getDefaultCurrency();
$budgetMaximum = Preferences::get('budgetMaximum', 1000)->data;
$defaultCurrency = Amount::getDefaultCurrency();
return view(
'budgets.index', compact('budgetMaximum', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
'budgets.index', compact('budgetMaximum', 'period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
);
}
/**
* @param BudgetRepositoryInterface $repository
*
* @return View
* @return \Illuminate\View\View
*/
public function noBudget(BudgetRepositoryInterface $repository)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->startOfMonth());
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
$end = Navigation::endOfPeriod($start, $range);
$list = $repository->getWithoutBudget($start, $end);
$subTitle = trans(
'firefly.without_budget_between',
@@ -193,11 +203,15 @@ class BudgetController extends Controller
*/
public function postUpdateIncome()
{
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
$end = Navigation::endOfPeriod($start, $range);
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
Preferences::set('budgetIncomeTotal' . $date, intval(Input::get('amount')));
Preferences::set($key, intval(Input::get('amount')));
Preferences::mark();
return Redirect::route('budgets.index');
return redirect(route('budgets.index'));
}
/**
@@ -205,7 +219,7 @@ class BudgetController extends Controller
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return View
* @return \Illuminate\View\View
*/
public function show(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition = null)
{
@@ -216,12 +230,15 @@ class BudgetController extends Controller
}
$journals = $repository->getJournals($budget, $repetition);
$limits = !is_null($repetition->id) ? [$repetition->budgetLimit] : $repository->getBudgetLimits($budget);
$subTitle = !is_null($repetition->id)
?
trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)])
:
e($budget->name);
if (is_null($repetition->id)) {
$limits = $repository->getBudgetLimits($budget);
$subTitle = e($budget->name);
} else {
$limits = [$repetition->budgetLimit];
$subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]);
}
$journals->setPath('/budgets/show/' . $budget->id);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
@@ -242,16 +259,17 @@ class BudgetController extends Controller
$budget = $repository->store($budgetData);
Session::flash('success', 'New budget "' . $budget->name . '" stored!');
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
Session::put('budgets.create.fromStore', true);
return Redirect::route('budgets.create')->withInput();
return redirect(route('budgets.create'))->withInput();
}
// redirect to previous URL.
return Redirect::to(Session::get('budgets.create.url'));
return redirect(Session::get('budgets.create.url'));
}
@@ -260,38 +278,42 @@ class BudgetController extends Controller
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
*
* @return $this|\Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function update(BudgetFormRequest $request, BudgetRepositoryInterface $repository, Budget $budget)
{
$budgetData = [
'name' => $request->input('name'),
'active' => intval($request->input('active')) == 1
'active' => intval($request->input('active')) == 1,
];
$repository->update($budget, $budgetData);
Session::flash('success', 'Budget "' . $budget->name . '" updated.');
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
Session::put('budgets.edit.fromUpdate', true);
return Redirect::route('budgets.edit', $budget->id)->withInput(['return_to_edit' => 1]);
return redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return Redirect::to(Session::get('budgets.edit.url'));
return redirect(Session::get('budgets.edit.url'));
}
/**
* @return View
* @return \Illuminate\View\View
*/
public function updateIncome()
{
$date = Session::get('start', Carbon::now()->startOfMonth())->format('FY');
$amount = Preferences::get('budgetIncomeTotal' . $date, 1000);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod(Session::get('start', new Carbon), $range);
$end = Navigation::endOfPeriod($start, $range);
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
$amount = Preferences::get($key, 1000);
return view('budgets.income', compact('amount'));
}

View File

@@ -5,9 +5,12 @@ use Carbon\Carbon;
use FireflyIII\Http\Requests\CategoryFormRequest;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input;
use Redirect;
use Navigation;
use Preferences;
use Session;
use URL;
use View;
@@ -31,7 +34,7 @@ class CategoryController extends Controller
}
/**
* @return $this
* @return \Illuminate\View\View
*/
public function create()
{
@@ -42,7 +45,7 @@ class CategoryController extends Controller
Session::forget('categories.create.fromStore');
Session::flash('gaEventCategory', 'categories');
Session::flash('gaEventAction', 'create');
$subTitle = 'Create a new category';
$subTitle = trans('firefly.create_new_category');
return view('categories.create', compact('subTitle'));
}
@@ -77,18 +80,19 @@ class CategoryController extends Controller
$repository->destroy($category);
Session::flash('success', 'The category "' . e($name) . '" was deleted.');
Preferences::mark();
return Redirect::to(Session::get('categories.delete.url'));
return redirect(Session::get('categories.delete.url'));
}
/**
* @param Category $category
*
* @return $this
* @return \Illuminate\View\View
*/
public function edit(Category $category)
{
$subTitle = 'Edit category "' . e($category->name) . '"';
$subTitle = trans('firefly.edit_category', ['name' => $category->name]);
// put previous url in session if not redirect from store (not "return_to_edit").
if (Session::get('categories.edit.fromUpdate') !== true) {
@@ -105,7 +109,7 @@ class CategoryController extends Controller
/**
* @param CategoryRepositoryInterface $repository
*
* @return $this
* @return \Illuminate\View\View
*/
public function index(CategoryRepositoryInterface $repository)
{
@@ -130,7 +134,10 @@ class CategoryController extends Controller
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->startOfMonth());
$list = $repository->getWithoutCategory($start, $end);
$subTitle = 'Transactions without a category between ' . $start->format('jS F Y') . ' and ' . $end->format('jS F Y');
$subTitle = trans(
'firefly.without_category_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
return view('categories.noCategory', compact('list', 'subTitle'));
}
@@ -139,7 +146,34 @@ class CategoryController extends Controller
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return View
* @param $date
*
* @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)
{
@@ -147,17 +181,54 @@ class CategoryController extends Controller
$page = intval(Input::get('page'));
$set = $repository->getJournals($category, $page);
$count = $repository->countJournals($category);
$subTitle = $category->name;
$journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('categories/show/' . $category->id);
return view('categories.show', compact('category', 'journals', 'hideCategory'));
// list of ranges for list of periods:
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range);
$entries = new Collection;
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category-show');
$cache->addProperty($category->id);
if ($cache->has()) {
$entries = $cache->get();
} else {
while ($end >= $start) {
$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);
$dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range);
$entries->push([$dateStr, $dateName, $spent, $earned]);
$end = Navigation::subtractPeriod($end, $range, 1);
}
$cache->store($entries);
}
return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle'));
}
/**
* @param CategoryFormRequest $request
* @param CategoryRepositoryInterface $repository
*
* @return mixed
* @return \Illuminate\Http\RedirectResponse
*/
public function store(CategoryFormRequest $request, CategoryRepositoryInterface $repository)
{
@@ -168,14 +239,15 @@ class CategoryController extends Controller
$category = $repository->store($categoryData);
Session::flash('success', 'New category "' . $category->name . '" stored!');
Preferences::mark();
if (intval(Input::get('create_another')) === 1) {
Session::put('categories.create.fromStore', true);
return Redirect::route('categories.create')->withInput();
return redirect(route('categories.create'))->withInput();
}
return Redirect::route('categories.index');
return redirect(route('categories.index'));
}
@@ -195,15 +267,16 @@ class CategoryController extends Controller
$repository->update($category, $categoryData);
Session::flash('success', 'Category "' . $category->name . '" updated.');
Preferences::mark();
if (intval(Input::get('return_to_edit')) === 1) {
Session::put('categories.edit.fromUpdate', true);
return Redirect::route('categories.edit', $category->id);
return redirect(route('categories.edit', [$category->id]));
}
// redirect to previous URL.
return Redirect::to(Session::get('categories.edit.url'));
return redirect(Session::get('categories.edit.url'));
}

View File

@@ -6,12 +6,11 @@ use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class AccountController
@@ -20,131 +19,141 @@ use Steam;
*/
class AccountController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Account\AccountChartGenerator */
protected $generator;
/**
* Shows the balances for all the user's accounts.
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Account\AccountChartGenerator');
}
/**
* Shows the balances for a given set of dates and accounts.
*
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function report($report_type, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('all');
$cache->addProperty('accounts');
$cache->addProperty('default');
$cache->addProperty($accounts);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
// make chart:
$data = $this->generator->frontpage($accounts, $start, $end);
$cache->store($data);
return Response::json($data);
}
/**
* Shows the balances for all the user's expense accounts.
*
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function all(GChart $chart, AccountRepositoryInterface $repository, $year, $month, $shared = false)
public function expenseAccounts(AccountRepositoryInterface $repository)
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = clone Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getAccounts(['Expense account', 'Beneficiary account']);
/** @var Collection $accounts */
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
if ($shared === false) {
// remove the shared accounts from the collection:
/** @var Account $account */
foreach ($accounts as $index => $account) {
if ($account->getMeta('accountRole') == 'sharedAsset') {
$accounts->forget($index);
}
}
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expenseAccounts');
$cache->addProperty('accounts');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$data = $this->generator->expenseAccounts($accounts, $start, $end);
$cache->store($data);
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
return Response::json($data);
}
/**
* Shows the balances for all the user's frontpage accounts.
*
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, AccountRepositoryInterface $repository)
public function frontpage(AccountRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = clone Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage);
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty($index);
$index++;
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('frontpage');
$cache->addProperty('accounts');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
$data = $this->generator->frontpage($accounts, $start, $end);
$cache->store($data);
return Response::json($data);
}
/**
* Shows an account's balance for a single month.
*
* @param GChart $chart
* @param Account $account
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function single(GChart $chart, Account $account)
public function single(Account $account)
{
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty(1);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start;
$today = new Carbon;
while ($end >= $current) {
$certain = $current < $today;
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
$current->addDay();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('frontpage');
$cache->addProperty('single');
$cache->addProperty($account->id);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$data = $this->generator->single($account, $start, $end);
$cache->store($data);
$chart->generate();
return Response::json($chart->getData());
return Response::json($data);
}
}

View File

@@ -6,13 +6,10 @@ use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use FireflyIII\Support\CacheProperties;
use Response;
use Session;
use Steam;
/**
* Class BillController
@@ -21,120 +18,82 @@ use Steam;
*/
class BillController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Bill\BillChartGenerator */
protected $generator;
/**
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Bill\BillChartGenerator');
}
/**
* Shows all bills and whether or not they've been paid this month (pie chart).
*
* @param BillRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(BillRepositoryInterface $repository)
{
$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);
}
// build chart:
$data = $this->generator->frontpage($paid, $unpaid);
return Response::json($data);
}
/**
* Shows the overview for a bill. The min/max amount and matched journals.
*
* @param GChart $chart
* @param BillRepositoryInterface $repository
* @param Bill $bill
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function single(GChart $chart, BillRepositoryInterface $repository, Bill $bill)
public function single(BillRepositoryInterface $repository, Bill $bill)
{
$chart->addColumn(trans('firefly.date'), 'date');
$chart->addColumn(trans('firefly.maxAmount'), 'number');
$chart->addColumn(trans('firefly.minAmount'), 'number');
$chart->addColumn(trans('firefly.billEntry'), 'number');
$cache = new CacheProperties;
$cache->addProperty('single');
$cache->addProperty('bill');
$cache->addProperty($bill->id);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
// get first transaction or today for start:
$results = $repository->getJournals($bill);
/** @var TransactionJournal $result */
foreach ($results as $result) {
$chart->addRow(clone $result->date, floatval($bill->amount_max), floatval($bill->amount_min), floatval($result->amount));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows all bills and whether or not theyve been paid this month (pie chart).
*
* @param GChart $chart
*
* @param BillRepositoryInterface $repository
* @param AccountRepositoryInterface $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, BillRepositoryInterface $repository, AccountRepositoryInterface $accounts)
{
$chart->addColumn(trans('firefly.name'), 'string');
$chart->addColumn(trans('firefly.amount'), 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$bills = $repository->getActiveBills();
$paid = new Collection; // journals.
$unpaid = new Collection; // bills
// loop paid and create single entry:
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
if ($journals->count() == 0) {
$unpaid->push([$bill, $range['start']]);
} else {
$paid = $paid->merge($journals);
}
// resort:
$results = $results->sortBy(
function (TransactionJournal $journal) {
return $journal->date->format('U');
}
}
);
$creditCards = $accounts->getCreditCards();
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, $end, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$amount = $balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $amount);
unset($description, $amount);
$unpaid->push([$fakeBill, $date]);
}
if ($balance == 0) {
// find transfer(s) TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$journals = $accounts->getTransfersInRange($creditCard, $start, $end);
$paid = $paid->merge($journals);
}
}
$data = $this->generator->single($bill, $results);
$cache->store($data);
/** @var TransactionJournal $entry */
foreach ($paid as $entry) {
$paidDescriptions[] = $entry->description;
$paidAmount += floatval($entry->amount);
}
// loop unpaid:
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$unpaidDescriptions[] = $description;
$unpaidAmount += $amount;
unset($amount, $description);
}
$chart->addRow(trans('firefly.unpaid') . ': ' . join(', ', $unpaidDescriptions), $unpaidAmount);
$chart->addRow(trans('firefly.paid') . ': ' . join(', ', $paidDescriptions), $paidAmount);
$chart->generate();
return Response::json($chart->getData());
return Response::json($data);
}
}

View File

@@ -6,8 +6,9 @@ use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Navigation;
use Preferences;
@@ -21,175 +22,310 @@ use Session;
*/
class BudgetController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Budget\BudgetChartGenerator */
protected $generator;
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @codeCoverageIgnore
*/
public function budget(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget)
public function __construct()
{
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator');
}
/**
* @param BudgetRepositoryInterface $repository
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Collection $budgets
*
* @return \Illuminate\Http\JsonResponse
*/
public function multiYear(BudgetRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets)
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($report_type);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty($budgets);
$cache->addProperty('multiYearBudget');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
/**
* budget
* year:
* spent: x
* budgeted: x
* year
* spent: x
* budgeted: x
*/
$entries = new Collection;
// go by budget, not by year.
foreach ($budgets as $budget) {
$entry = ['name' => '', 'spent' => [], 'budgeted' => []];
$currentStart = clone $start;
while ($currentStart < $end) {
// fix the date:
$currentEnd = clone $currentStart;
$currentEnd->endOfYear();
// get data:
if (is_null($budget->id)) {
$name = trans('firefly.noBudget');
$sum = $repository->getWithoutBudgetSum($currentStart, $currentEnd);
$budgeted = 0;
} else {
$name = $budget->name;
$sum = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts);
$budgeted = $repository->getBudgetLimitRepetitions($budget, $currentStart, $currentEnd)->sum('amount');
}
// save to array:
$year = $currentStart->year;
$entry['name'] = $name;
$entry['spent'][$year] = ($sum * -1);
$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);
return Response::json($data);
}
/**
* @param BudgetRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
* @param Budget $budget
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budget(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget)
{
// dates and times
$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);
$last = Navigation::endOfX($last, $range, $final);
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($first);
$cache->addProperty($last);
$cache->addProperty('budget');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
while ($first < $last) {
$end = Navigation::addPeriod($first, $range, 0);
$spent = $repository->spentInPeriodCorrected($budget, $first, $end);
$chart->addRow($end, $spent);
$end->subDay();
$chartDate = clone $end;
$chartDate->startOfMonth();
$spent = $repository->balanceInPeriod($budget, $first, $end, $accounts) * -1;
$entries->push([$chartDate, $spent]);
$first = Navigation::addPeriod($first, $range, 0);
}
$chart->generate();
$data = $this->generator->budget($entries);
$cache->store($data);
return Response::json($chart->getData());
return Response::json($data);
}
/**
* Shows the amount left in a specific budget limit.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetLimit(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
public function budgetLimit(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
bcscale(2);
$chart->addColumn(trans('firefly.day'), 'date');
$chart->addColumn(trans('firefly.left'), 'number');
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget');
$cache->addProperty('limit');
$cache->addProperty($budget->id);
$cache->addProperty($repetition->id);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$amount = $repetition->amount;
$entries = new Collection;
$amount = $repetition->amount;
while ($start <= $end) {
/*
* Sum of expenses on this day:
*/
$sum = $repository->expensesOnDayCorrected($budget, $start);
$amount += $sum;
$chart->addRow(clone $start, $amount);
$sum = $repository->expensesOnDay($budget, $start);
$amount = bcadd($amount, $sum);
$entries->push([clone $start, $amount]);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
$data = $this->generator->budgetLimit($entries);
$cache->store($data);
return Response::json($data);
}
/**
* Shows a budget list with spent/left/overspent.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param BudgetRepositoryInterface $repository
*
* @param AccountRepositoryInterface $accountRepository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, BudgetRepositoryInterface $repository)
public function frontpage(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
{
$chart->addColumn(trans('firefly.budget'), 'string');
$chart->addColumn(trans('firefly.left'), 'number');
$chart->addColumn(trans('firefly.spent'), 'number');
$chart->addColumn(trans('firefly.overspent'), 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('budget');
$cache->addProperty('all');
if ($cache->has()) {
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->spentInPeriodCorrected($budget, $start, $end, true);
$allEntries->push([$budget->name, 0, 0, $expenses]);
continue;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, true);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? floatval($repetition->amount) : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$allEntries->push(
[$budget->name . ' (' . $repetition->startdate->formatLocalized($this->monthAndDayFormat) . ')',
$left,
$spent,
$overspent
]
);
// 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;
}
$allEntries->push([$name, $left, $spent, $overspent, $amount, $expenses]);
}
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push([trans('firefly.noBudget'), 0, 0, $noBudgetExpenses]);
foreach ($allEntries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
}
}
$chart->generate();
return Response::json($chart->getData());
$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 GChart $chart
* @param BudgetRepositoryInterface $repository
* @param $year
* @param bool $shared
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function year(GChart $chart, BudgetRepositoryInterface $repository, $year, $shared = false)
public function year(BudgetRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$budgets = $repository->getBudgets();
$allBudgets = $repository->getBudgets();
$budgets = new Collection;
// add columns:
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($report_type);
$cache->addProperty($accounts);
$cache->addProperty('budget');
$cache->addProperty('year');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
// filter empty budgets:
foreach ($allBudgets as $budget) {
$spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
if ($spent != 0) {
$budgets->push($budget);
}
}
$entries = new Collection;
while ($start < $end) {
// month is the current end of the period:
$month = clone $start;
$month->endOfMonth();
// make a row:
$row = [clone $start];
// each budget, fill the row:
foreach ($budgets as $budget) {
$spent = $repository->spentInPeriodCorrected($budget, $start, $month, $shared);
$row[] = $spent;
$spent = $repository->balanceInPeriod($budget, $start, $month, $accounts);
$row[] = $spent * -1;
}
$chart->addRowArray($row);
$start->addMonth();
$entries->push($row);
$start->endOfMonth()->addDay();
}
$chart->generate();
$data = $this->generator->year($budgets, $entries);
$cache->store($data);
return Response::json($chart->getData());
return Response::json($data);
}
}

View File

@@ -7,7 +7,8 @@ use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
@@ -20,42 +21,65 @@ use Session;
*/
class CategoryController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Category\CategoryChartGenerator */
protected $generator;
/**
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Category\CategoryChartGenerator');
}
/**
* Show an overview for a category for all time, per month/week/year.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function all(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
public function all(CategoryRepositoryInterface $repository, Category $category)
{
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
$range = Preferences::get('viewRange', '1M')->data;
// jump to start of week / month / year / etc
$start = Navigation::startOfPeriod($start, $range);
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
$start = $repository->getFirstActivityDate($category);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range);
$end = new Carbon;
$entries = new Collection;
$end = new Carbon;
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriodCorrected($category, $start, $currentEnd);
$chart->addRow(clone $start, $spent);
$start = Navigation::addPeriod($start, $range, 0);
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('all');
$cache->addProperty('categories');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$chart->generate();
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriod($category, $start, $currentEnd);
$earned = $repository->earnedInPeriod($category, $start, $currentEnd);
$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();
return Response::json($chart->getData());
$data = $this->generator->all($entries);
$cache->store($data);
return Response::json($data);
}
@@ -63,116 +87,399 @@ class CategoryController extends Controller
/**
* Show this month's category overview.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, CategoryRepositoryInterface $repository)
public function frontpage(CategoryRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.category'), 'string');
$chart->addColumn(trans('firefly.spent'), 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$set = $repository->getCategoriesAndExpensesCorrected($start, $end);
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('category');
$cache->addProperty('frontpage');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$array = $repository->getCategoriesAndExpenses($start, $end);
// sort by callback:
uasort(
$set,
$array,
function ($left, $right) {
if ($left['sum'] == $right['sum']) {
return 0;
}
return ($left['sum'] < $right['sum']) ? 1 : -1;
return ($left['sum'] < $right['sum']) ? -1 : 1;
}
);
$set = new Collection($array);
$data = $this->generator->frontpage($set);
$cache->store($data);
foreach ($set as $entry) {
$sum = floatval($entry['sum']);
if ($sum != 0) {
$chart->addRow($entry['name'], $sum);
}
}
$chart->generate();
return Response::json($chart->getData());
return Response::json($data);
}
/**
* @param CategoryRepositoryInterface $repository
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Collection $categories
*
* @return \Illuminate\Http\JsonResponse
*/
public function multiYear(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts, Collection $categories)
{
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($report_type);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty($categories);
$cache->addProperty('multiYearCategory');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
/**
* category
* year:
* spent: x
* earned: x
* year
* spent: x
* earned: x
*/
$entries = new Collection;
// go by budget, not by year.
/** @var Category $category */
foreach ($categories as $category) {
$entry = ['name' => '', 'spent' => [], 'earned' => []];
$currentStart = clone $start;
while ($currentStart < $end) {
// fix the date:
$currentEnd = clone $currentStart;
$currentEnd->endOfYear();
// get data:
if (is_null($category->id)) {
$name = trans('firefly.noCategory');
$spent = $repository->spentNoCategoryForAccounts($accounts, $currentStart, $currentEnd);
$earned = $repository->earnedNoCategoryForAccounts($accounts, $currentStart, $currentEnd);
} else {
$name = $category->name;
$spent = $repository->spentInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd);
$earned = $repository->earnedInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd);
}
// save to array:
$year = $currentStart->year;
$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 GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function month(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
public function currentPeriod(CategoryRepositoryInterface $repository, Category $category)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($category->id);
$cache->addProperty('category');
$cache->addProperty('currentPeriod');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
while ($start <= $end) {
$spent = $repository->spentOnDaySumCorrected($category, $start);
$chart->addRow(clone $start, $spent);
$spent = $repository->spentOnDaySum($category, $start);
$earned = $repository->earnedOnDaySum($category, $start);
$date = Navigation::periodShow($start, '1D');
$entries->push([clone $start, $date, $spent, $earned]);
$start->addDay();
}
$chart->generate();
$data = $this->generator->period($entries);
$cache->store($data);
return Response::json($chart->getData());
return Response::json($data);
}
/**
* This chart will only show expenses.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param $year
* @param bool $shared
* @param Category $category
*
* @param $date
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function year(GChart $chart, CategoryRepositoryInterface $repository, $year, $shared = false)
public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, $date)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$categories = $repository->getCategories();
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
// add columns:
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($categories as $category) {
$chart->addColumn($category->name, 'number');
// 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->spentOnDaySum($category, $start);
$earned = $repository->earnedOnDaySum($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);
}
/**
* Returns a chart of what has been earned in this period in each category
* grouped by month.
*
* @param CategoryRepositoryInterface $repository
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function earnedInPeriod(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$original = clone $start;
$cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($report_type);
$cache->addProperty($accounts);
$cache->addProperty('category');
$cache->addProperty('earned-in-period');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$categories = new Collection;
$sets = new Collection;
$entries = new Collection;
// run a very special query each month:
$start = clone $original;
while ($start < $end) {
// month is the current end of the period:
$month = clone $start;
$month->endOfMonth();
// make a row:
$row = [clone $start];
// each budget, fill the row:
foreach ($categories as $category) {
$spent = $repository->spentInPeriodCorrected($category, $start, $month, $shared);
$row[] = $spent;
$currentEnd = clone $start;
$currentStart = clone $start;
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
// get a list of categories, and what the user has earned for that category
// (if the user has earned anything)
$set = $repository->earnedForAccounts($accounts, $currentStart, $currentEnd);
$categories = $categories->merge($set);
// save the set combined with the data that is in it:
// for example:
// december 2015, salary:1000, bonus:200
$sets->push([$currentStart, $set]);
$start->addMonth();
}
// filter categories into a single bunch. Useful later on.
// $categories contains all the categories the user has earned money
// in in this period.
$categories = $categories->unique('id');
$categories = $categories->sortBy(
function (Category $category) {
return $category->name;
}
$chart->addRowArray($row);
);
// start looping the time again, this time processing the
// data for each month.
$start = clone $original;
while ($start < $end) {
$currentEnd = clone $start;
$currentStart = clone $start;
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
// in $sets we have saved all the sets of data for each month
// so now we need to retrieve the corrent one.
// match is on date of course.
$currentSet = $sets->first(
function ($key, $value) use ($currentStart) {
// set for this date.
return ($value[0] == $currentStart);
}
);
// create a row used later on.
$row = [clone $currentStart];
// loop all categories:
/** @var Category $category */
foreach ($categories as $category) {
// if entry is not null, we've earned money in this period for this category.
$entry = $currentSet[1]->first(
function ($key, $value) use ($category) {
return $value->id == $category->id;
}
);
// save amount
if (!is_null($entry)) {
$row[] = $entry->earned;
} else {
$row[] = 0;
}
}
$entries->push($row);
$start->addMonth();
}
$chart->generate();
$data = $this->generator->earnedInPeriod($categories, $entries);
$cache->store($data);
return $data;
return Response::json($chart->getData());
}
/**
* Returns a chart of what has been spent in this period in each category
* grouped by month.
*
* @param CategoryRepositoryInterface $repository
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function spentInPeriod(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$original = clone $start;
$cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($report_type);
$cache->addProperty($accounts);
$cache->addProperty('category');
$cache->addProperty('spent-in-period');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$categories = new Collection;
$sets = new Collection;
$entries = new Collection;
// run a very special query each month:
$start = clone $original;
while ($start < $end) {
$currentEnd = clone $start;
$currentStart = clone $start;
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
$set = $repository->spentForAccounts($accounts, $currentStart, $currentEnd);
$categories = $categories->merge($set);
$sets->push([$currentStart, $set]);
$start->addMonth();
}
$categories = $categories->unique('id');
$categories = $categories->sortBy(
function (Category $category) {
return $category->name;
}
);
$start = clone $original;
while ($start < $end) {
$currentEnd = clone $start;
$currentStart = clone $start;
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
$currentSet = $sets->first(
function ($key, $value) use ($currentStart) {
// set for this date.
return ($value[0] == $currentStart);
}
);
$row = [clone $currentStart];
/** @var Category $category */
foreach ($categories as $category) {
/** @var Category $entry */
$entry = $currentSet[1]->first(
function ($key, $value) use ($category) {
return $value->id == $category->id;
}
);
if (!is_null($entry)) {
$row[] = $entry->spent;
} else {
$row[] = 0;
}
}
$entries->push($row);
$start->addMonth();
}
$data = $this->generator->spentInPeriod($categories, $entries);
$cache->store($data);
return $data;
}
}

View File

@@ -2,11 +2,10 @@
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Response;
@@ -18,32 +17,44 @@ use Response;
*/
class PiggyBankController extends Controller
{
/** @var \FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator */
protected $generator;
/**
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\PiggyBank\PiggyBankChartGenerator');
}
/**
* Shows the piggy bank history.
*
* @param GChart $chart
* @param PiggyBankRepositoryInterface $repository
* @param PiggyBank $piggyBank
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function history(GChart $chart, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
public function history(PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$chart->addColumn(trans('firefly.date'), 'date');
$chart->addColumn(trans('firefly.balance'), 'number');
/** @var Collection $set */
$set = $repository->getEventSummarySet($piggyBank);
$sum = 0;
foreach ($set as $entry) {
$sum += floatval($entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('piggy-history');
$cache->addProperty($piggyBank->id);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$chart->generate();
/** @var Collection $set */
$set = new Collection($repository->getEventSummarySet($piggyBank));
$data = $this->generator->history($set);
$cache->store($data);
return Response::json($chart->getData());
return Response::json($data);
}
}

View File

@@ -6,7 +6,8 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Controllers\Controller;
use Grumpydictator\Gchart\GChart;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Response;
/**
@@ -17,85 +18,160 @@ use Response;
class ReportController extends Controller
{
/** @var \FireflyIII\Generator\Chart\Report\ReportChartGenerator */
protected $generator;
/**
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
// create chart generator:
$this->generator = app('FireflyIII\Generator\Chart\Report\ReportChartGenerator');
}
/**
* Summarizes all income and expenses, per month, for a given year.
*
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function yearInOut(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
public function yearInOut(ReportQueryInterface $query, $report_type, 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->addColumn(trans('firefly.month'), 'date');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$incomeSum = floatval($query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
$expenseSum = floatval($query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
$chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth();
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('yearInOut');
$cache->addProperty($start);
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$chart->generate();
return Response::json($chart->getData());
// per year?
if ($start->diffInMonths($end) > 12) {
$entries = new Collection;
while ($start < $end) {
$startOfYear = clone $start;
$startOfYear->startOfYear();
$endOfYear = clone $startOfYear;
$endOfYear->endOfYear();
// total income and total expenses:
$incomeSum = $query->incomeInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive');
$expenseSum = $query->expenseInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive');
$entries->push([clone $start, $incomeSum, $expenseSum]);
$start->addYear();
}
$data = $this->generator->multiYearInOut($entries);
$cache->store($data);
} else {
// per month:
$entries = new Collection;
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$incomeSum = $query->incomeInPeriod($start, $month, $accounts)->sum('amount_positive');
$expenseSum = $query->expenseInPeriod($start, $month, $accounts)->sum('amount_positive');
$entries->push([clone $start, $incomeSum, $expenseSum]);
$start->addMonth();
}
$data = $this->generator->yearInOut($entries);
$cache->store($data);
}
return Response::json($data);
}
/**
* Summarizes all income and expenses for a given year. Gives a total and an average.
*
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function yearInOutSummarized(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
public function yearInOutSummarized(ReportQueryInterface $query, $report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$income = 0;
$expense = 0;
$count = 0;
$chart->addColumn(trans('firefly.summary'), 'string');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$income += floatval($query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
$expense += floatval($query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
$count++;
$start->addMonth();
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('yearInOutSummarized');
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
// add total + average:
$chart->addRow(trans('firefly.sum'), $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow(trans('firefly.average'), ($income / $count), ($expense / $count));
$income = '0';
$expense = '0';
$count = 0;
$chart->generate();
bcscale(2);
return Response::json($chart->getData());
if ($start->diffInMonths($end) > 12) {
// per year
while ($start < $end) {
$startOfYear = clone $start;
$startOfYear->startOfYear();
$endOfYear = clone $startOfYear;
$endOfYear->endOfYear();
// total income and total expenses:
$currentIncome = $query->incomeInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive');
$currentExpense = $query->expenseInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive');
$income = bcadd($income, $currentIncome);
$expense = bcadd($expense, $currentExpense);
$count++;
$start->addYear();
}
$data = $this->generator->multiYearInOutSummarized($income, $expense, $count);
$cache->store($data);
} else {
// per month!
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$currentIncome = $query->incomeInPeriod($start, $month, $accounts)->sum('amount_positive');
$currentExpense = $query->expenseInPeriod($start, $month, $accounts)->sum('amount_positive');
$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);
}
}

View File

@@ -1,8 +1,7 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Config;
use Illuminate\Foundation\Bus\DispatchesCommands;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Preferences;
@@ -16,7 +15,7 @@ use View;
abstract class Controller extends BaseController
{
use DispatchesCommands, ValidatesRequests;
use DispatchesJobs, ValidatesRequests;
/** @var string */
protected $monthAndDayFormat;
@@ -34,14 +33,15 @@ 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 = trans('config.month');
$this->monthAndDayFormat = trans('config.month_and_day');
View::share('monthFormat', $this->monthFormat);
View::share('monthAndDayFormat', $this->monthAndDayFormat);
View::share('language', $lang);
View::share('localeconv', localeconv());
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace FireflyIII\Http\Controllers;
use FireflyIII\User;
/**
* Class WebhookController
*
* @package FireflyIII\Http\Controllers
*/
class CronController extends Controller
{
/**
* Firefly doesn't have anything that should be in the a cron job, except maybe this one, and it's fairly exceptional.
*
* If you use SendGrid like I do, you can detect bounces and thereby check if users gave an invalid address. If they did,
* it's easy to block them and change their password. Optionally, you could notify yourself about it and send them a message.
*
* But thats something not supported right now.
*/
public function sendgrid()
{
if (strlen(env('SENDGRID_USERNAME')) > 0 && strlen(env('SENDGRID_PASSWORD')) > 0) {
$set = [
'blocks' => 'https://api.sendgrid.com/api/blocks.get.json',
'bounces' => 'https://api.sendgrid.com/api/bounces.get.json',
'invalids' => 'https://api.sendgrid.com/api/invalidemails.get.json',
];
echo '<pre>';
foreach ($set as $name => $URL) {
$parameters = [
'api_user' => env('SENDGRID_USERNAME'),
'api_key' => env('SENDGRID_PASSWORD'),
'date' => 1,
'days' => 7
];
$fullURL = $URL . '?' . http_build_query($parameters);
$data = json_decode(file_get_contents($fullURL));
/*
* Loop the result, if any.
*/
if (is_array($data)) {
echo 'Found ' . count($data) . ' entries in the SendGrid ' . $name . ' list.' . "\n";
foreach ($data as $entry) {
$address = $entry->email;
$user = User::where('email', $address)->where('blocked', 0)->first();
if (!is_null($user)) {
echo 'Found a user: ' . $address . ', who is now blocked.' . "\n";
$user->blocked = 1;
$user->blocked_code = 'bounced';
$user->password = 'bounced';
$user->save();
} else {
echo 'Found no user: ' . $address . ', did nothing.' . "\n";
}
}
}
}
echo 'Done!' . "\n";
} else {
echo 'Please fill in SendGrid details.';
}
}
}

View File

@@ -0,0 +1,412 @@
<?php
namespace FireflyIII\Http\Controllers;
use Config;
use ExpandedForm;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Csv\Data;
use FireflyIII\Helpers\Csv\Importer;
use FireflyIII\Helpers\Csv\WizardInterface;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Http\Request;
use Input;
use Log;
use Preferences;
use Session;
use View;
/**
* Class CsvController
*
* @package FireflyIII\Http\Controllers
*/
class CsvController extends Controller
{
/** @var Data */
protected $data;
/** @var WizardInterface */
protected $wizard;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.csv'));
View::share('mainTitleIcon', 'fa-file-text-o');
if (Config::get('firefly.csv_import_enabled') === false) {
throw new FireflyException('CSV Import is not enabled.');
}
$this->wizard = app('FireflyIII\Helpers\Csv\WizardInterface');
$this->data = app('FireflyIII\Helpers\Csv\Data');
}
/**
* Define column roles and mapping.
*
* STEP THREE
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function columnRoles()
{
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-import-account'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', 'Could not recover upload.');
return redirect(route('csv.index'));
}
$subTitle = trans('firefly.csv_define_column_roles');
$firstRow = $this->data->getReader()->fetchOne();
$count = count($firstRow);
$headers = [];
$example = $this->data->getReader()->fetchOne(1);
$availableRoles = [];
$roles = $this->data->getRoles();
$map = $this->data->getMap();
for ($i = 1; $i <= $count; $i++) {
$headers[] = trans('firefly.csv_column') . ' #' . $i;
}
if ($this->data->hasHeaders()) {
$headers = $firstRow;
}
foreach (Config::get('csv.roles') as $name => $role) {
$availableRoles[$name] = trans('firefly.csv_column_' . $name);//$role['name'];
}
ksort($availableRoles);
return view('csv.column-roles', compact('availableRoles', 'map', 'roles', 'headers', 'example', 'subTitle'));
}
/**
* Optional download of mapping.
*
* STEP FOUR THREE-A
*
* @return \Illuminate\Http\RedirectResponse|string
*/
public function downloadConfig()
{
$fields = ['csv-date-format', 'csv-has-headers'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', 'Could not recover upload.');
return redirect(route('csv.index'));
}
$data = [
'date-format' => Session::get('csv-date-format'),
'has-headers' => Session::get('csv-has-headers')
];
if (Session::has('csv-map')) {
$data['map'] = Session::get('csv-map');
}
if (Session::has('csv-roles')) {
$data['roles'] = Session::get('csv-roles');
}
if (Session::has('csv-mapped')) {
$data['mapped'] = Session::get('csv-mapped');
}
if (Session::has('csv-specifix')) {
$data['specifix'] = Session::get('csv-specifix');
}
$result = json_encode($data, JSON_PRETTY_PRINT);
$name = 'csv-configuration-' . date('Y-m-d') . '.json';
header('Content-disposition: attachment; filename=' . $name);
header('Content-type: application/json');
echo $result;
return '';
}
/**
* @return \Illuminate\View\View
*/
public function downloadConfigPage()
{
$subTitle = trans('firefly.csv_download_config_title');
return view('csv.download-config', compact('subTitle'));
}
/**
* This method shows the initial upload form.
*
* STEP ONE
*
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\View\View
*/
public function index(AccountRepositoryInterface $repository)
{
$subTitle = trans('firefly.csv_import');
Session::forget('csv-date-format');
Session::forget('csv-has-headers');
Session::forget('csv-file');
Session::forget('csv-import-account');
Session::forget('csv-map');
Session::forget('csv-roles');
Session::forget('csv-mapped');
Session::forget('csv-specifix');
// get list of supported specifix
$specifix = [];
foreach (Config::get('csv.specifix') as $entry) {
$specifix[$entry] = trans('firefly.csv_specifix_' . $entry);
}
// get a list of asset accounts:
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Asset account', 'Default account']));
// can actually upload?
$uploadPossible = is_writable(storage_path('upload'));
$path = storage_path('upload');
return view('csv.index', compact('subTitle', 'uploadPossible', 'path', 'specifix', 'accounts'));
}
/**
* Parse the file.
*
* STEP FOUR
*
* @return \Illuminate\Http\RedirectResponse
*/
public function initialParse()
{
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', 'Could not recover upload.');
return redirect(route('csv.index'));
}
// process given roles and mapping:
$roles = $this->wizard->processSelectedRoles(Input::get('role'));
$maps = $this->wizard->processSelectedMapping($roles, Input::get('map'));
Session::put('csv-map', $maps);
Session::put('csv-roles', $roles);
// Go back when no roles defined:
if (count($roles) === 0) {
Session::flash('warning', 'Please select some roles.');
return redirect(route('csv.column-roles'));
}
/*
* Continue with map specification when necessary.
*/
if (count($maps) > 0) {
return redirect(route('csv.map'));
}
/*
* Or simply start processing.
*/
// proceed to download config
return redirect(route('csv.download-config-page'));
}
/**
*
* Map first if necessary,
*
* STEP FIVE.
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
* @throws FireflyException
*/
public function map()
{
// Make sure all fields we need are accounted for.
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', 'Could not recover upload.');
return redirect(route('csv.index'));
}
/*
* The "options" array contains all options the user has
* per column, where the key represents the column.
*
* For each key there is an array which in turn represents
* all the options available: grouped by ID.
*
* options[column index] = [
* field id => field identifier.
* ]
*/
try {
$options = $this->wizard->showOptions($this->data->getMap());
} catch (FireflyException $e) {
return view('error', ['message' => $e->getMessage()]);
}
// After these values are prepped, read the actual CSV file
$reader = $this->data->getReader();
$map = $this->data->getMap();
$hasHeaders = $this->data->hasHeaders();
$values = $this->wizard->getMappableValues($reader, $map, $hasHeaders);
$map = $this->data->getMap();
$mapped = $this->data->getMapped();
$subTitle = trans('firefly.csv_map_values');
return view('csv.map', compact('map', 'options', 'values', 'mapped', 'subTitle'));
}
/**
*
* Finally actually process the CSV file.
*
* STEP SEVEN
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function process()
{
/*
* Make sure all fields we need are accounted for.
*/
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles', 'csv-mapped'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', 'Could not recover upload.');
return redirect(route('csv.index'));
}
Log::debug('Created importer');
$importer = new Importer;
$importer->setData($this->data);
try {
$importer->run();
} catch (FireflyException $e) {
Log::error('Catch error: ' . $e->getMessage());
return view('error', ['message' => $e->getMessage()]);
}
Log::debug('Done importing!');
$rows = $importer->getRows();
$errors = $importer->getErrors();
$imported = $importer->getImported();
$journals = $importer->getJournals();
Preferences::mark();
$subTitle = trans('firefly.csv_process_title');
return view('csv.process', compact('rows', 'errors', 'imported', 'subTitle', 'journals'));
}
/**
* Store the mapping the user has made. This is
*
* STEP SIX
*
* @return \Illuminate\Http\RedirectResponse
*/
public function saveMapping()
{
/*
* Make sure all fields we need are accounted for.
*/
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', 'Could not recover upload.');
return redirect(route('csv.index'));
}
// save mapping to session.
$mapped = [];
if (!is_array(Input::get('mapping'))) {
Session::flash('warning', 'Invalid mapping.');
return redirect(route('csv.map'));
}
foreach (Input::get('mapping') as $index => $data) {
$mapped[$index] = [];
foreach ($data as $value => $mapping) {
if (intval($mapping) !== 0) {
$mapped[$index][$value] = $mapping;
}
}
}
Session::put('csv-mapped', $mapped);
// proceed to process.
return redirect(route('csv.download-config-page'));
}
/**
*
* This method processes the file, puts it away somewhere safe
* and sends you onwards.
*
* STEP TWO
*
* @param Request $request
*
* @return \Illuminate\Http\RedirectResponse
*/
public function upload(Request $request)
{
if (!$request->hasFile('csv')) {
Session::flash('warning', 'No file uploaded.');
return redirect(route('csv.index'));
}
$fullPath = $this->wizard->storeCsvFile($request->file('csv')->getRealPath());
$settings = [];
$settings['date-format'] = Input::get('date_format');
$settings['has-headers'] = intval(Input::get('has_headers')) === 1;
$settings['specifix'] = Input::get('specifix');
$settings['import-account'] = intval(Input::get('csv_import_account'));
$settings['map'] = [];
$settings['mapped'] = [];
$settings['roles'] = [];
if ($request->hasFile('csv_config')) { // Process config file if present.
$data = file_get_contents($request->file('csv_config')->getRealPath());
$json = json_decode($data, true);
if (is_array($json)) {
$settings = array_merge($settings, $json);
}
}
$this->data->setCsvFileLocation($fullPath);
$this->data->setDateFormat($settings['date-format']);
$this->data->setHasHeaders($settings['has-headers']);
$this->data->setMap($settings['map']);
$this->data->setMapped($settings['mapped']);
$this->data->setRoles($settings['roles']);
$this->data->setSpecifix($settings['specifix']);
$this->data->setImportAccount($settings['import-account']);
return redirect(route('csv.column-roles'));
}
}

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