Compare commits

...

335 Commits

Author SHA1 Message Date
James Cole
919a35aed3 Merge branch 'release/3.5.6.1' 2015-12-28 16:53:45 +01:00
James Cole
9c929ecd1b New release. 2015-12-28 16:53:30 +01:00
James Cole
f79c9f7cf1 Cleanup. 2015-12-28 16:52:28 +01:00
James Cole
8e75c345d9 Found a bug that requires a new release of FF3 to fix. 2015-12-28 16:52:21 +01:00
James Cole
44886d9aad Merge branch 'release/3.5.6' into develop 2015-12-28 08:07:44 +01:00
James Cole
c2d444347d Merge branch 'release/3.5.6' 2015-12-28 08:07:43 +01:00
James Cole
5cb497596d New release. 2015-12-28 08:07:26 +01:00
James Cole
1857469d2f Removed unused parameters 2015-12-28 08:00:42 +01:00
James Cole
ea71b4843d Formatting fix in chart. 2015-12-28 07:58:40 +01:00
James Cole
97727e2e3d Some code cleanup courtesy of phpstorm. 2015-12-28 07:55:09 +01:00
James Cole
f81e7da8bb Code cleanup. 2015-12-28 07:49:27 +01:00
James Cole
8e827bf83b Removed parameter. 2015-12-28 07:43:57 +01:00
James Cole
9e1fa284ca Update php doc. 2015-12-28 07:43:05 +01:00
James Cole
3bf800be6e null check. 2015-12-28 07:41:44 +01:00
James Cole
635b9f9dba instance check. 2015-12-28 07:39:48 +01:00
James Cole
52a0d7cf7b Clean up code. 2015-12-28 07:38:02 +01:00
James Cole
a34516932b Rename include advised by scrutinizer. 2015-12-28 07:35:09 +01:00
James Cole
929a2a30a2 Fix TODO. 2015-12-28 07:31:48 +01:00
James Cole
ffa88eeb08 Made deleted piggy banks for piggy bank events visible. 2015-12-28 07:27:16 +01:00
James Cole
51b45b4ed4 Code cleanup. 2015-12-28 07:12:47 +01:00
James Cole
f263844793 Fix a bug where you cannot edit transactions. Will warrant a new release of FF. 2015-12-28 07:12:12 +01:00
James Cole
18c46df9aa Fix negative amounts and chart names. 2015-12-27 21:26:44 +01:00
James Cole
15846e157b From 200+ queries back to ~17. 2015-12-27 21:17:04 +01:00
James Cole
bc59f2db0d Optimised queries. 2015-12-27 20:07:49 +01:00
James Cole
cd2be8c1a4 Activate caching. 2015-12-27 19:51:20 +01:00
James Cole
f958115c50 Update composer file. 2015-12-27 17:57:32 +01:00
James Cole
e7d677bfb6 Add rounding, so the number will be a float. 2015-12-27 17:34:31 +01:00
James Cole
3e80ffc52b Huge change to bills and paid/unpaid/cc boxes. 2015-12-27 17:29:41 +01:00
James Cole
d0c7a5c076 Optimised query. 2015-12-27 09:44:12 +01:00
James Cole
f3f4e6b354 Stops date from skipping ahead slowly. 2015-12-27 09:40:28 +01:00
James Cole
5a45b25614 Merge branch 'hotfix/chart-fix' 2015-12-27 09:35:41 +01:00
James Cole
0b5ee1edfc Merge branch 'hotfix/chart-fix' into develop 2015-12-27 09:35:41 +01:00
James Cole
da3dc599f9 Fix chart call. 2015-12-27 09:35:24 +01:00
James Cole
f013b435ab Merge branch 'release/3.5.5' 2015-12-27 08:58:36 +01:00
James Cole
5f6975a113 Merge branch 'release/3.5.5' into develop 2015-12-27 08:58:36 +01:00
James Cole
c5dee29e4b New version. 2015-12-27 08:58:25 +01:00
James Cole
633ee02f13 Remove old Google references 2015-12-27 08:57:51 +01:00
James Cole
6b750c909a Fix forgotten call in bill repository. 2015-12-27 08:39:41 +01:00
James Cole
5f8b6640a9 A lot less queries thanks to efficient query. 2015-12-27 08:39:29 +01:00
James Cole
dd42d8437c Removed code for unused chart. 2015-12-27 08:12:46 +01:00
James Cole
67a178591d Some query optimisations. 2015-12-27 07:59:00 +01:00
James Cole
f5e5659c1f Code cleanup. 2015-12-26 09:40:24 +01:00
James Cole
8b0f0fb615 Optimise queries. 2015-12-26 09:39:35 +01:00
James Cole
209116e766 Query optimisations. 2015-12-26 09:21:45 +01:00
James Cole
79392ab656 Add caching to various queries and lists. 2015-12-26 08:44:34 +01:00
James Cole
3ca1207231 #135 2015-12-26 08:24:41 +01:00
James Cole
cec1b147f2 #135 2015-12-26 08:23:52 +01:00
James Cole
46cfcfa3e7 Update admin template. 2015-12-26 08:22:48 +01:00
James Cole
b833e8dfa2 #135 2015-12-26 08:16:30 +01:00
James Cole
77b843efd8 #135 2015-12-26 08:15:22 +01:00
James Cole
db72ad7c60 Issue #135 2015-12-26 08:12:51 +01:00
James Cole
eadc630fcb #135 2015-12-26 08:12:44 +01:00
James Cole
170c1793cc #135 2015-12-26 08:06:34 +01:00
James Cole
9f7c6c2d0c Extra cache. 2015-12-25 17:11:55 +01:00
James Cole
72d054c55c Add support for virtual balance currency, even though it cannot be stored yet. 2015-12-25 17:10:04 +01:00
James Cole
524edfe7c2 Better formatting (will take currency into account). 2015-12-25 16:40:27 +01:00
James Cole
c25c5623d2 Fixed the currency dropdown when multiple fields present on single page. 2015-12-25 16:38:53 +01:00
James Cole
4f38b77ef6 Better caching. 2015-12-25 09:34:37 +01:00
James Cole
5862803434 This saves some queries. 2015-12-25 09:34:23 +01:00
James Cole
5b3beded39 I can't believe I left this here all this time. 2015-12-25 07:58:19 +01:00
James Cole
c61fb7a598 Marked some unused stuff as deprecated. 2015-12-25 07:52:56 +01:00
James Cole
33d9148029 Make sure charts are cached. 2015-12-25 07:43:34 +01:00
James Cole
63969f5a33 Same routine but for money spent on accounts. 2015-12-25 07:42:00 +01:00
James Cole
edde18aeef Remove old chart. 2015-12-25 07:32:56 +01:00
James Cole
657116d361 Display new chart. 2015-12-25 07:32:03 +01:00
James Cole
e16269daa8 Collect data for new chart. 2015-12-25 07:31:54 +01:00
James Cole
c07591ff5c New method, earnedForAccounts 2015-12-25 07:31:43 +01:00
James Cole
75a478ad54 New chart, earned in period. 2015-12-25 07:31:29 +01:00
James Cole
8dae8b1a7f More code. Forgot to push. 2015-12-24 16:59:38 +01:00
James Cole
15fd8cf486 Completed the renaming of some methods. 2015-12-24 10:27:45 +01:00
James Cole
55333156ac Better cache control for some charts. 2015-12-24 10:14:01 +01:00
James Cole
8cdcba3231 Original fix in place. #133 2015-12-24 09:50:28 +01:00
James Cole
8bab9e84e2 Should not have edited that code. #133 2015-12-24 09:50:16 +01:00
James Cole
2faae83912 Include empty budgets. #133 2015-12-24 09:47:44 +01:00
James Cole
5a61a11a61 Attempt to fix bug #133 2015-12-24 09:45:21 +01:00
James Cole
a6d71988f2 Replaced some language calls. 2015-12-24 08:35:08 +01:00
James Cole
7069e242ae Removed useless entry. 2015-12-24 08:20:59 +01:00
James Cole
56ee830558 Moved locale information from the language to the translation files. 2015-12-24 08:20:47 +01:00
James Cole
6dd12729e6 Small disclaimer in readme. 2015-12-24 08:20:19 +01:00
James Cole
14a48303cb Cleanup. 2015-12-23 11:32:50 +01:00
James Cole
72cf6c9c0f Removed old route. 2015-12-23 11:32:41 +01:00
James Cole
144ee6b8ca Updated read me. 2015-12-23 10:38:42 +01:00
James Cole
8967d86da6 Updated language files. 2015-12-23 09:34:23 +01:00
James Cole
18c6edbb5d Update language files. 2015-12-23 09:09:51 +01:00
James Cole
53de3c4717 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Fix a bug where the frontpage would not honor transaction order.
  Fix a bug where the report page would mess up the session dates.
2015-12-22 20:46:52 +01:00
James Cole
ad577e4e81 Expand language files. 2015-12-22 20:46:16 +01:00
James Cole
44811a3e7c Fix a bug where the frontpage would not honor transaction order. 2015-12-21 11:30:58 +01:00
James Cole
1ab3f05b3a Fix a bug where the report page would mess up the session dates. 2015-12-21 10:25:57 +01:00
James Cole
5e76488ae7 Better localisation in charts. 2015-12-20 08:40:58 +01:00
James Cole
32771fe7e1 Add Português do Brasil 2015-12-20 08:20:50 +01:00
James Cole
9b40cc6881 Fix locale setting for Carbon. 2015-12-20 08:19:26 +01:00
James Cole
2e35260bbb Add some translations. 2015-12-20 07:34:10 +01:00
James Cole
a067704277 Move stuff around. 2015-12-20 07:34:01 +01:00
James Cole
de281818ac Add language 2015-12-19 21:16:09 +01:00
James Cole
c49bfad38d Move languages. 2015-12-19 20:54:59 +01:00
James Cole
c1ba591b26 Rename languages. 2015-12-19 20:54:27 +01:00
James Cole
719af38a61 Cleanup. 2015-12-18 18:42:56 +01:00
James Cole
ac61dfae6b File reformatting. 2015-12-18 16:38:50 +01:00
James Cole
813fb679a7 File reformatting. 2015-12-18 16:37:45 +01:00
James Cole
e7562781f7 File reformatting. 2015-12-18 16:37:27 +01:00
James Cole
56d36b7f53 Remove Google references. 2015-12-18 16:37:02 +01:00
James Cole
53b3f7f821 Merge branch 'release/3.5.4' 2015-12-18 08:13:28 +01:00
James Cole
08a53156bd Merge branch 'release/3.5.4' into develop 2015-12-18 08:13:28 +01:00
James Cole
8985cd6309 New composer for 3.5.4. 2015-12-18 08:13:19 +01:00
James Cole
3833da7410 Add something about security. 2015-12-18 08:11:05 +01:00
James Cole
4210cd10db Cleanup. 2015-12-18 08:10:41 +01:00
James Cole
a7bd1c6892 Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  First attempt cleaning up chart money formatting.
2015-12-18 07:32:03 +01:00
James Cole
52b0111afa Expanded text in register dialog. 2015-12-18 07:31:49 +01:00
James Cole
7921d128e4 Cleanup routine that checks for blocked domains. 2015-12-18 07:31:36 +01:00
James Cole
d7e838701a Blocked domains now in .env file. 2015-12-18 07:31:14 +01:00
James Cole
289bcb22aa First attempt cleaning up chart money formatting. 2015-12-17 15:03:47 +01:00
James Cole
3fe57b7983 Report remembers budgets and categories. 2015-12-16 16:19:15 +01:00
James Cole
32e92c2a16 Committed some dev stuff. 2015-12-16 13:10:49 +01:00
James Cole
1b3d208540 First attempt at functional category chart. 2015-12-16 13:08:26 +01:00
James Cole
6a8bf0aa62 Fixed the "undefined" error. 2015-12-16 12:13:01 +01:00
James Cole
56715556ed Second attempt. 2015-12-16 10:54:56 +01:00
James Cole
838330b909 First attempt at multi-year budget chart. 2015-12-16 10:17:15 +01:00
James Cole
69553b138b First calculations for multi-year budget chart. 2015-12-15 12:52:42 +01:00
James Cole
36d7a02994 Some refactoring. 2015-12-15 12:46:40 +01:00
James Cole
301528e2d2 Quick links. 2015-12-15 12:38:18 +01:00
James Cole
0303b45707 First code for multi year budget chart. 2015-12-15 12:37:55 +01:00
James Cole
ba722e8ed5 Expanded error message. 2015-12-15 08:19:16 +01:00
James Cole
289e5a5442 Add new blocked domain. 2015-12-15 08:19:07 +01:00
James Cole
fdad96e2bc Replaced route. 2015-12-14 21:14:34 +01:00
James Cole
af994e4dae Included first multi-year chart. 2015-12-14 21:12:10 +01:00
James Cole
006d68e279 Expand chart generation. 2015-12-14 21:11:57 +01:00
James Cole
29dc122ad3 New charts (slight variations of previous charts) 2015-12-14 21:11:26 +01:00
James Cole
cf4a8c6204 New translations. 2015-12-14 21:11:12 +01:00
James Cole
3c73fe92bf Lower threshold. 2015-12-14 20:58:23 +01:00
James Cole
6637590797 Empty placeholder for multi-year report. 2015-12-14 20:54:19 +01:00
James Cole
b8bab11acd Fix bread crumbs, clean up routes. 2015-12-14 20:45:12 +01:00
James Cole
a2f600feac Nice clean code. 2015-12-14 20:37:38 +01:00
James Cole
80dd62ef0a Refer to correct translations. 2015-12-14 20:34:08 +01:00
James Cole
827b1c9cd8 Fix some translations. 2015-12-14 20:33:57 +01:00
James Cole
2e4fcf803d Fix JS references. 2015-12-14 20:33:50 +01:00
James Cole
d00d95fc6f Cleanup JS 2015-12-14 20:33:10 +01:00
James Cole
3e3ab9bd25 Move some JS around. 2015-12-14 20:25:48 +01:00
James Cole
6eecc7722d Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop
* 'develop' of https://github.com/JC5/firefly-iii:
  Fix for left unbalanced field in report.
2015-12-14 20:22:47 +01:00
James Cole
ada4aaf69a This will generate buttloads of test data. 2015-12-14 20:21:24 +01:00
James Cole
93244c1f78 Fix for left unbalanced field in report. 2015-12-14 12:35:52 +01:00
James Cole
be056cea6b Update some queries. 2015-12-13 20:41:35 +01:00
James Cole
659ca8be14 Update some queries. 2015-12-13 20:40:41 +01:00
James Cole
ea9af8366d Update some queries. 2015-12-13 20:39:26 +01:00
James Cole
80edd47d36 First attempts at building a multi-year report. 2015-12-13 17:31:25 +01:00
James Cole
d7746b3649 Support multi-year, not implemented yet. 2015-12-13 10:18:25 +01:00
James Cole
c4c4fbc34c Refactor 2015-12-13 10:05:13 +01:00
James Cole
59f57c96e9 Refactor names. 2015-12-13 09:41:22 +01:00
James Cole
a2f852fecf Clean up code. 2015-12-13 09:35:58 +01:00
James Cole
ad114ed329 Remove unused methods. 2015-12-13 09:30:02 +01:00
James Cole
c4c3d0f07f Some refactoring. 2015-12-13 09:01:17 +01:00
James Cole
6cf8102de5 Update version for coming release. 2015-12-12 22:32:19 +01:00
James Cole
e7e4aa2218 Update composer.lock. 2015-12-12 22:32:04 +01:00
James Cole
6d84f4b6c1 Update screenshots. 2015-12-12 22:31:34 +01:00
James Cole
ce3e9ffd11 Better cache control. 2015-12-12 21:20:20 +01:00
James Cole
3ada260e0e This will keep history. 2015-12-12 20:59:29 +01:00
James Cole
8fdd0cb795 Fixed some yearly charts. 2015-12-12 20:56:07 +01:00
James Cole
913e05a2e6 Really, reversed. 2015-12-12 20:20:18 +01:00
James Cole
fa1f703ef6 Some negative sums were failing regarding transfers. 2015-12-12 20:19:40 +01:00
James Cole
4004c53e1b Fix negative amount thing. 2015-12-12 20:16:30 +01:00
James Cole
4838670649 Transfer fix. 2015-12-12 20:13:07 +01:00
James Cole
a985e09282 Fix query. 2015-12-12 20:10:52 +01:00
James Cole
9bd1503cb4 Expand query to catch all expenses. 2015-12-12 20:07:33 +01:00
James Cole
a2ccbf7844 Jump to year report if the period is too long. 2015-12-12 19:04:30 +01:00
James Cole
61bbe8a905 Don't need this file. 2015-12-12 17:51:40 +01:00
James Cole
59bc5d22d1 Clean up some urls 2015-12-12 17:51:07 +01:00
James Cole
1423d5b314 Expand query, let's see what happens. 2015-12-12 12:36:36 +01:00
James Cole
152d0eb1d0 Cleanup and translations. 2015-12-12 12:29:54 +01:00
James Cole
6426d1df06 Fix the report chart. 2015-12-12 10:41:51 +01:00
James Cole
9284eb3fe9 First attempt at account specific bill report. 2015-12-12 10:33:19 +01:00
James Cole
afdae8bc1e Fix a sum for #129 2015-12-12 08:26:12 +01:00
James Cole
2a7085e593 Experimental budget lines. #129 2015-12-12 08:25:08 +01:00
James Cole
2408fb3ed4 Experimental budget lines. #129 2015-12-12 08:24:17 +01:00
James Cole
8316afb176 Experimental budget lines. #129 2015-12-12 08:21:46 +01:00
James Cole
e59fd098a3 Spent amount / withdrawals are negative, #129. 2015-12-12 08:15:14 +01:00
James Cole
e044199693 Spent amount / withdrawals are negative, #129. 2015-12-12 08:14:17 +01:00
James Cole
8f8e29fc22 Fix a sum. 2015-12-12 08:12:27 +01:00
James Cole
8de5384158 Spent amount / withdrawals are negative, #129. 2015-12-12 08:11:30 +01:00
James Cole
216c659335 Spent amount / withdrawals are negative, #129. 2015-12-12 08:07:25 +01:00
James Cole
041ca8a5d3 Amount reversal for #129 2015-12-12 08:05:10 +01:00
James Cole
fe4f1b306d Fix a sum for #129 2015-12-11 18:52:21 +01:00
James Cole
a0972d99fb Made a negative amount a positive as per #129 2015-12-11 18:49:07 +01:00
James Cole
e332bfef7c First attempt at budgets (split by account). 2015-12-11 18:45:39 +01:00
James Cole
cba5e226d8 Fix display of amount. 2015-12-11 18:36:19 +01:00
James Cole
5aff0c4943 Some fixes for #129 2015-12-11 18:35:49 +01:00
James Cole
cb49c00f4d Fix another JS error 2015-12-11 18:33:47 +01:00
James Cole
e26d797d57 Fix JS error. 2015-12-11 18:32:57 +01:00
James Cole
938581527e Fix sorting. 2015-12-11 18:31:15 +01:00
James Cole
c38ae09735 Negative expenses, as per #129 2015-12-11 18:30:28 +01:00
James Cole
28c3cfe084 Fix display of amount. See issue #129 2015-12-11 18:15:37 +01:00
James Cole
4a2823bcba Reverse sort. 2015-12-11 18:05:07 +01:00
James Cole
18eba02026 Expanded report for categories. 2015-12-11 18:03:13 +01:00
James Cole
d4690ce580 Fix date in budget report 2015-12-11 17:54:52 +01:00
James Cole
a785c450b1 First attempt at including a budget report. 2015-12-11 17:53:17 +01:00
James Cole
7480dc4a19 Fix a query. 2015-12-11 16:39:33 +01:00
James Cole
ad01891a67 First attempt at including expense report. 2015-12-11 16:36:40 +01:00
James Cole
67fe35d564 Small query fix. 2015-12-11 11:32:22 +01:00
James Cole
7f19b6957a Expanded new report a bit. Mainly copy/paste work. Will have to see how it pans out. 2015-12-11 09:39:17 +01:00
James Cole
0a54caf202 Tweak more translations. 2015-12-11 08:48:07 +01:00
James Cole
4b4c1c7f8f Moved some translations to see if they will still be picked up by Laravel. 2015-12-11 08:40:45 +01:00
James Cole
d071f3947e Merge pull request #127 from tonicospinelli/demeter-law
applying Demeter law for Transaction Type. Looking good!
2015-12-11 08:36:03 +01:00
Antonio Spinelli
b3d99cd210 apply demeter law for transaction type calls
- adds contants for transaction type names
- demeter law = never speaks with strangers
2015-12-10 16:53:48 -02:00
James Cole
90e696f82c Period is a month 2015-12-07 14:42:28 +01:00
James Cole
958fcd1cfa Report IP address 2015-12-07 14:41:04 +01:00
James Cole
8f57c7dcb3 Some fixes to amounts. 2015-12-06 13:17:00 +01:00
James Cole
77262f52a4 First functional view of default report. 2015-12-06 13:11:43 +01:00
James Cole
16bfbc8a12 Some JS to process the report form beforehand. 2015-12-06 08:42:04 +01:00
James Cole
1fd375b875 Better redirect after logout. 2015-12-05 17:45:33 +01:00
James Cole
46131ad39d Updated view for new reports. 2015-12-04 06:57:08 +01:00
James Cole
0b5c5b2ae9 Some mediocre Javascript for report thing. 2015-12-04 06:56:59 +01:00
James Cole
55be174037 Method to find an account. 2015-12-04 06:56:45 +01:00
James Cole
a17b7025f1 New function to build URL report. 2015-12-04 06:56:35 +01:00
James Cole
170cf7fd77 New routes for new reports. 2015-12-04 06:56:03 +01:00
James Cole
23cdb4d326 Expand month list for new reports. 2015-12-04 06:55:54 +01:00
James Cole
cbbe529572 Show bill if one is connected. 2015-12-04 06:16:19 +01:00
James Cole
0b382426e9 First experimental report generator / choice thing. 2015-12-03 14:52:10 +01:00
James Cole
1cbbf9baa4 Added a missing translation. 2015-12-03 11:46:05 +01:00
James Cole
8d41ff7b79 Prev should be next. Duh. 2015-12-03 11:41:06 +01:00
James Cole
e3b6057bf8 Catch Swift exceptions and do a log only (instead of crashing) because the email message isn't actually critical. 2015-12-03 11:30:43 +01:00
James Cole
66a4042cad Updated composer file. 2015-12-03 11:25:12 +01:00
James Cole
56c08d8302 Can block certain domains from registering, such as ten-minute-mail services. Two example domains provided in configuration. 2015-12-03 11:17:48 +01:00
James Cole
d4e759754d Make password reset impossible for blocked users. 2015-12-02 13:28:11 +01:00
James Cole
a96e171cbf Update composer. 2015-12-02 13:26:58 +01:00
James Cole
bd4a8c8397 Fixed "under" column 2015-12-02 09:03:34 +01:00
James Cole
04f71b3b43 Removed old code. 2015-12-02 09:01:40 +01:00
James Cole
d124de51db Shouldn't be like this? 2015-12-02 09:00:28 +01:00
James Cole
d87d12a0f5 Better query. See if works. 2015-12-02 08:58:40 +01:00
James Cole
f2b08346d0 Log. 2015-12-02 08:50:28 +01:00
James Cole
d3682a6727 Another fix in reports. 2015-12-02 08:46:03 +01:00
James Cole
371bbd9508 Some cosmetic fixes to reports 2015-12-02 08:44:23 +01:00
James Cole
a8a28f442f Also show "zero" amounts. 2015-12-02 08:38:57 +01:00
James Cole
65ddd8a736 One 'equals' sign too many! 2015-12-02 08:37:35 +01:00
James Cole
8bb27de233 More report subtleties. 2015-12-02 08:37:08 +01:00
James Cole
37e2f097ba To make budget report more clear, add spent amount to "spent" column. 2015-12-02 08:35:15 +01:00
James Cole
1966d87ce6 Small formatting fixes in reports. 2015-12-02 08:33:22 +01:00
James Cole
7b8c86e1e3 Only get active piggy banks. 2015-12-02 08:25:38 +01:00
unknown
de634da513 Only get active savings accounts. 2015-12-02 08:22:25 +01:00
James Cole
96836e2d6c Update read me. 2015-11-28 16:04:30 +01:00
James Cole
8a9d576f61 Allow change to default currency. issue #121 2015-11-22 11:30:06 +01:00
James Cole
791d12fbb4 Fix for issue #123 2015-11-22 11:25:15 +01:00
James Cole
d1329be2fa Bill scan routine should not grab transfers and income. 2015-11-20 20:13:10 +01:00
James Cole
3ed6561702 Updated all packages. 2015-11-13 07:16:23 +01:00
James Cole
7a0587f433 Merge branch 'release/3.5.3' 2015-11-13 07:01:29 +01:00
James Cole
0fe682bfe6 Merge branch 'release/3.5.3' into develop 2015-11-13 07:01:29 +01:00
James Cole
0f685e8789 New version. 2015-11-13 07:01:14 +01:00
James Cole
420771c233 New translations. See issue #117 2015-11-13 07:00:30 +01:00
James Cole
5e3e9271ca Added encryption to new password. See issue #118 2015-11-13 06:59:08 +01:00
James Cole
1e603c0833 Merge pull request #113 from RonaldvanMeer/develop
Removed duplicated controlbar item
2015-11-01 08:22:33 +01:00
James Cole
03e1673e92 Fix login message. 2015-11-01 08:06:51 +01:00
James Cole
9f992f003d Fix redirect loop. 2015-11-01 08:03:41 +01:00
James Cole
f50244a41f Merge branch 'release/3.5.2' into develop 2015-10-30 07:23:43 +01:00
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
5fd688b266 Merge branch 'release/3.5.0' into develop 2015-08-13 17:37:55 +02:00
199 changed files with 8943 additions and 5910 deletions

View File

@@ -2,6 +2,7 @@ APP_ENV=production
APP_DEBUG=false
APP_KEY=SomeRandomStringOf32CharsExactly
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=homestead
@@ -11,14 +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
SHOW_INCOMPLETE_TRANSLATIONS=false
ANALYTICS_ID=
RUNCLEANUP=true
SITE_OWNER=mail@example.com
SENDGRID_USERNAME=
SENDGRID_PASSWORD=
SENDGRID_PASSWORD=
BLOCKED_DOMAINS=

2
.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

View File

@@ -10,6 +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

View File

@@ -19,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.
@@ -61,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/c6hz06d3)
![Index](https://i.nder.be/hmp5mhw5)
![Accounts](https://i.nder.be/gzxxyz6n)
![Accounts](https://i.nder.be/hf5k02g9)
![Budgets](https://i.nder.be/hhu3krqk)
![Budgets](https://i.nder.be/gzv635mz)
![Reports 1](https://i.nder.be/cc3yspf6)
![Reports 1](https://i.nder.be/g0w698s3)
![Reports 2](https://i.nder.be/h6fp7xkb)
![Reports 2](https://i.nder.be/cr77nyxq)
![Bills](https://i.nder.be/c30zkcpv)
![Bills](https://i.nder.be/c7sugsz5)
![Piggy banks](https://i.nder.be/g20k0mdq)
![Piggy banks](https://i.nder.be/gy2nk0y4)
## Running and installing
@@ -82,7 +81,19 @@ 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. Accounts on the demo sites will stop working after one week.
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

View File

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

View File

@@ -14,15 +14,6 @@ use Illuminate\Support\Collection;
interface AccountChartGenerator
{
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function all(Collection $accounts, Carbon $start, Carbon $end);
/**
* @param Collection $accounts
* @param Carbon $start

View File

@@ -3,10 +3,8 @@
namespace FireflyIII\Generator\Chart\Account;
use Carbon\Carbon;
use Config;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
use Preferences;
use Steam;
/**
@@ -17,21 +15,6 @@ use Steam;
class ChartJsAccountChartGenerator implements AccountChartGenerator
{
/**
* @codeCoverageIgnore
*
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function all(Collection $accounts, Carbon $start, Carbon $end)
{
return $this->frontpage($accounts, $start, $end);
}
/**
* @param Collection $accounts
* @param Carbon $start
@@ -41,34 +24,24 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
*/
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end)
{
// language:
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
[
'label' => trans('firefly.spent'),
'data' => []
]
],
];
$ids = [];
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.spent'),
'data' => []]]];
foreach ($accounts as $account) {
$ids[] = $account->id;
}
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 = isset($startBalances[$id]) ? $startBalances[$id] : 0;
$endBalance = isset($endBalances[$id]) ? $endBalances[$id] : 0;
$diff = $endBalance - $startBalance;
$startBalance = $this->isInArray($startBalances, $id);
$endBalance = $this->isInArray($endBalances, $id);
$diff = bcsub($endBalance, $startBalance);
$account->difference = round($diff, 2);
}
);
@@ -86,10 +59,24 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
}
}
return $data;
}
/**
* @param $array
* @param $entryId
*
* @return string
*/
protected function isInArray($array, $entryId)
{
if (isset($array[$entryId])) {
return $array[$entryId];
}
return '0';
}
/**
* @param Collection $accounts
@@ -101,21 +88,21 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
public function frontpage(Collection $accounts, Carbon $start, Carbon $end)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.monthAndDay.' . $language);
$data = [
$format = trans('config.month_and_day');
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
$current = clone $start;
$current = clone $start;
while ($current <= $end) {
$data['labels'][] = $current->formatLocalized($format);
$current->addDay();
}
foreach ($accounts as $account) {
$set = [
$set = [
'label' => $account->name,
'fillColor' => 'rgba(220,220,220,0.2)',
'strokeColor' => 'rgba(220,220,220,1)',
@@ -125,9 +112,15 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
'pointHighlightStroke' => 'rgba(220,220,220,1)',
'data' => [],
];
$current = clone $start;
$current = clone $start;
$range = Steam::balanceInRange($account, $start, clone $end);
$previous = array_values($range)[0];
while ($current <= $end) {
$set['data'][] = Steam::balance($account, $current);
$format = $current->format('Y-m-d');
$balance = isset($range[$format]) ? $range[$format] : $previous;
$set['data'][] = $balance;
$previous = $balance;
$current->addDay();
}
$data['datasets'][] = $set;
@@ -147,10 +140,9 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
public function single(Account $account, Carbon $start, Carbon $end)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.monthAndDay.' . $language);
$format = trans('config.month_and_day');
$data = [
$data = [
'count' => 1,
'labels' => [],
'datasets' => [
@@ -160,15 +152,36 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
]
],
];
$current = clone $start;
$range = Steam::balanceInRange($account, $start, $end);
$current = clone $start;
$previous = array_values($range)[0];
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
$balance = isset($range[$theDate]) ? $range[$theDate] : $previous;
$data['labels'][] = $current->formatLocalized($format);
$data['datasets'][0]['data'][] = Steam::balance($account, $current);
$data['datasets'][0]['data'][] = $balance;
$previous = $balance;
$current->addDay();
}
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

@@ -14,12 +14,12 @@ interface BillChartGenerator
{
/**
* @param Collection $paid
* @param Collection $unpaid
* @param string $paid
* @param string $unpaid
*
* @return array
*/
public function frontpage(Collection $paid, Collection $unpaid);
public function frontpage($paid, $unpaid);
/**
* @param Bill $bill

View File

@@ -2,11 +2,9 @@
namespace FireflyIII\Generator\Chart\Bill;
use Config;
use FireflyIII\Models\Bill;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use Preferences;
/**
* Class ChartJsBillChartGenerator
@@ -17,42 +15,23 @@ class ChartJsBillChartGenerator implements BillChartGenerator
{
/**
* @param Collection $paid
* @param Collection $unpaid
* @param string $paid
* @param string $unpaid
*
* @return array
*/
public function frontpage(Collection $paid, Collection $unpaid)
public function frontpage($paid, $unpaid)
{
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
bcscale(2);
/** @var TransactionJournal $entry */
foreach ($paid as $entry) { // loop paid and create single entry:
$paidDescriptions[] = $entry->description;
$paidAmount = bcadd($paidAmount, $entry->amount);
}
/** @var Bill $entry */
foreach ($unpaid as $entry) { // loop unpaid:
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = bcdiv(bcadd($entry[0]->amount_max, $entry[0]->amount_min), 2);
$unpaidDescriptions[] = $description;
$unpaidAmount = bcadd($unpaidAmount, $amount);
unset($amount, $description);
}
$data = [
[
'value' => $unpaidAmount,
'value' => round($unpaid, 2),
'color' => 'rgba(53, 124, 165,0.7)',
'highlight' => 'rgba(53, 124, 165,0.9)',
'label' => trans('firefly.unpaid'),
],
[
'value' => $paidAmount,
'value' => round($paid * -1, 2), // paid is negative, must be positive.
'color' => 'rgba(0, 141, 76, 0.7)',
'highlight' => 'rgba(0, 141, 76, 0.9)',
'label' => trans('firefly.paid'),
@@ -71,8 +50,7 @@ class ChartJsBillChartGenerator implements BillChartGenerator
public function single(Bill $bill, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = trans('config.month');
$data = [
'count' => 3,
@@ -87,11 +65,15 @@ class ChartJsBillChartGenerator implements BillChartGenerator
$minAmount = [];
$maxAmount = [];
$actualAmount = [];
/** @var TransactionJournal $entry */
foreach ($entries as $entry) {
$data['labels'][] = $entry->date->formatLocalized($format);
$minAmount[] = round($bill->amount_min, 2);
$maxAmount[] = round($bill->amount_max, 2);
$actualAmount[] = round($entry->amount, 2);
/*
* journalAmount has been collected in BillRepository::getJournals
*/
$actualAmount[] = round(($entry->journalAmount * -1), 2);
}
$data['datasets'][] = [

View File

@@ -32,6 +32,13 @@ interface BudgetChartGenerator
*/
public function frontpage(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries);
/**
* @param Collection $budgets
* @param Collection $entries

View File

@@ -24,7 +24,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
public function budget(Collection $entries, $dateFormat = 'month')
{
// language:
$language = Preferences::get('language', 'en')->data;
$language = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'))->data;
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
$data = [
@@ -33,7 +33,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
[
'label' => 'Amount',
'data' => [],
]
],
],
];
@@ -83,8 +83,8 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$data['labels'][] = $entry[0];
$left[] = round($entry[1], 2);
$spent[] = round($entry[2], 2);
$overspent[] = round($entry[3], 2);
$spent[] = round($entry[2] * -1, 2); // spent is coming in negative, must be positive
$overspent[] = round($entry[3] * -1, 2); // same
}
}
@@ -115,8 +115,7 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
public function year(Collection $budgets, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = trans('config.month');
$data = [
'labels' => [],
@@ -141,4 +140,37 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator
return $data;
}
/**
* @param Collection $entries
*
* @return array
*/
public function multiYear(Collection $entries)
{
// dataset:
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
// get labels from one of the budgets (assuming there's at least one):
$first = $entries->first();
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

@@ -19,6 +19,14 @@ interface CategoryChartGenerator
*/
public function all(Collection $entries);
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInPeriod(Collection $categories, Collection $entries);
/**
* @param Collection $entries
*
@@ -31,8 +39,14 @@ interface CategoryChartGenerator
*
* @return array
*/
public function month(Collection $entries);
public function multiYear(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function period(Collection $entries);
/**
* @param Collection $categories
@@ -40,13 +54,5 @@ interface CategoryChartGenerator
*
* @return array
*/
public function spentInYear(Collection $categories, Collection $entries);
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInYear(Collection $categories, Collection $entries);
public function spentInPeriod(Collection $categories, Collection $entries);
}

View File

@@ -2,9 +2,7 @@
namespace FireflyIII\Generator\Chart\Category;
use Config;
use Illuminate\Support\Collection;
use Preferences;
/**
@@ -17,16 +15,12 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
/**
* @param Collection $entries
* @param string $dateFormat
*
* @return array
*/
public function all(Collection $entries, $dateFormat = 'month')
public function all(Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.' . $dateFormat . '.' . $language);
$data = [
'count' => 2,
@@ -44,20 +38,50 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
];
foreach ($entries as $entry) {
$data['labels'][] = $entry[0]->formatLocalized($format);
$amount = round($entry[1], 2);
if ($amount > 0) {
$data['datasets'][0]['data'][] = null;
$data['datasets'][1]['data'][] = $amount;
} else {
$data['datasets'][0]['data'][] = $amount * -1;
$data['datasets'][1]['data'][] = null;
}
$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
*
@@ -78,13 +102,50 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
foreach ($entries as $entry) {
if ($entry['sum'] != 0) {
$data['labels'][] = $entry['name'];
$data['datasets'][0]['data'][] = round($entry['sum'], 2);
$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
*
@@ -92,9 +153,9 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
*
* @return array
*/
public function month(Collection $entries)
public function period(Collection $entries)
{
return $this->all($entries, 'monthAndDay');
return $this->all($entries);
}
@@ -104,46 +165,11 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
*
* @return array
*/
public function spentInYear(Collection $categories, Collection $entries)
public function spentInPeriod(Collection $categories, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$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 $categories
* @param Collection $entries
*
* @return array
*/
public function earnedInYear(Collection $categories, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = trans('config.month');
$data = [
'count' => 0,

View File

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

View File

@@ -2,9 +2,7 @@
namespace FireflyIII\Generator\Chart\Report;
use Config;
use Illuminate\Support\Collection;
use Preferences;
/**
* Class ChartJsReportChartGenerator
@@ -14,6 +12,70 @@ use Preferences;
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
*
@@ -22,8 +84,7 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
public function yearInOut(Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = trans('config.month');
$data = [
'count' => 2,

View File

@@ -12,6 +12,22 @@ use Illuminate\Support\Collection;
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
*

View File

@@ -53,7 +53,7 @@ class ConnectJournalToPiggyBank
}
bcscale(2);
$amount = $journal->actual_amount;
$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;

View File

@@ -67,7 +67,9 @@ class AttachmentHelper implements AttachmentHelperInterface
}
}
} else {
$this->processFile($files, $model);
if (!is_null($files)) {
$this->processFile($files, $model);
}
}
return true;

View File

@@ -150,24 +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';
bcscale(2);
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$sum = bcadd($sum, $balanceEntry->getLeft());
}
return $sum;
}
}

View File

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

View File

@@ -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,7 +81,7 @@ class Expense
*/
public function getExpenses()
{
$set = $this->expenses->sortByDesc(
$set = $this->expenses->sortBy(
function (stdClass $object) {
return $object->amount;
}

View File

@@ -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);
}

View File

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

View File

@@ -296,7 +296,7 @@ class Importer
// some debug info:
$journalId = $journal->id;
$type = $journal->transactionType->type;
$type = $journal->getTransactionType();
/** @var Account $asset */
$asset = $this->importData['asset-account-object'];
/** @var Account $opposing */
@@ -314,13 +314,13 @@ class Importer
*/
protected function getTransactionType()
{
$transactionType = TransactionType::where('type', 'Deposit')->first();
$transactionType = TransactionType::where('type', TransactionType::DEPOSIT)->first();
if ($this->importData['amount'] < 0) {
$transactionType = TransactionType::where('type', 'Withdrawal')->first();
$transactionType = TransactionType::where('type', TransactionType::WITHDRAWAL)->first();
}
if (in_array($this->importData['opposing-account-object']->accountType->type, ['Asset account', 'Default account'])) {
$transactionType = TransactionType::where('type', 'Transfer')->first();
$transactionType = TransactionType::where('type', TransactionType::TRANSFER)->first();
}
return $transactionType;

View File

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

View File

@@ -19,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
@@ -43,48 +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 AccountCollection
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared)
public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts)
{
$accounts = $this->query->getAllAccounts($date, $end, $shared);
$start = '0';
$end = '0';
$diff = '0';
$startAmount = '0';
$endAmount = '0';
$diff = '0';
bcscale(2);
// remove cash account, if any:
$accounts = $accounts->filter(
function (Account $account) {
if ($account->accountType->type != 'Cash account') {
return $account;
}
$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();
return null;
/** @noinspection PhpParamsInspection */
$account->startBalance = Steam::balance($account, $yesterday);
$account->endBalance = Steam::balance($account, $end);
}
);
// summarize:
foreach ($accounts as $account) {
$start = bcadd($start, $account->startBalance);
$end = bcadd($end, $account->endBalance);
$diff = bcadd($diff, bcsub($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);
@@ -92,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('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);
}
@@ -133,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:
@@ -142,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);
@@ -153,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;
@@ -164,7 +332,7 @@ class ReportHelper implements ReportHelperInterface
$spent = $this->query->spentNoBudget($account, $start, $end);
$left = $tagRepository->coveredByBalancingActs($account, $start, $end);
bcscale(2);
$diff = bcsub($spent, $left);
$diff = bcadd($spent, $left);
// budget
$budgetEntry = new BalanceEntry;
@@ -199,16 +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
* Excludes bills which have not had a payment on the mentioned accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return BillCollection
*/
public function getBillReport(Carbon $start, Carbon $end)
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts)
{
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
$repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$bills = $repository->getBills();
$bills = $repository->getBillsForAccounts($accounts);
$collection = new BillCollection;
/** @var Bill $bill */
@@ -238,167 +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('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$set = $repository->getBudgets();
bcscale(2);
foreach ($set as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
// no repetition(s) for this budget:
if ($repetitions->count() == 0) {
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setOverspent($spent);
$object->addOverspent($spent);
$object->addBudgetLine($budgetLine);
continue;
}
// one or more repetitions for budget:
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setRepetition($repetition);
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, $shared);
$left = $expenses < $repetition->amount ? bcsub($repetition->amount, $expenses) : 0;
$spent = $expenses > $repetition->amount ? 0 : $expenses;
$overspent = $expenses > $repetition->amount ? bcsub($expenses, $repetition->amount) : 0;
$budgetLine->setLeft($left);
$budgetLine->setSpent($spent);
$budgetLine->setOverspent($overspent);
$budgetLine->setBudgeted($repetition->amount);
$object->addBudgeted($repetition->amount);
$object->addSpent($spent);
$object->addLeft($left);
$object->addOverspent($overspent);
$object->addBudgetLine($budgetLine);
}
}
// stuff outside of budgets:
$noBudget = $repository->getWithoutBudgetSum($start, $end);
$budgetLine = new BudgetLine;
$budgetLine->setOverspent($noBudget);
$object->addOverspent($noBudget);
$object->addBudgetLine($budgetLine);
return $object;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared)
{
$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, $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

@@ -10,6 +10,7 @@ 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
@@ -21,75 +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 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
* Excludes bills which have not had a payment on the mentioned accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return BillCollection
*/
public function getBillReport(Carbon $start, Carbon $end);
public function getBillReport(Carbon $start, Carbon $end, Collection $accounts);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @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,185 +20,6 @@ use Steam;
*/
class ReportQuery implements ReportQueryInterface
{
/**
* See ReportQueryInterface::incomeInPeriodCorrected.
*
* This method's length is caused mainly by the query build stuff. Therefor:
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*
*/
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');
$data = $query->get( // get everything
['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted']
);
$data->each(
function (TransactionJournal $journal) {
if (intval($journal->account_encrypted) == 1) {
$journal->name = Crypt::decrypt($journal->name);
}
}
);
$data = $data->filter(
function (TransactionJournal $journal) {
if ($journal->amount != 0) {
return $journal;
}
return null;
}
);
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');
}
)
->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;
}
return null;
}
);
return $data;
}
/**
* Covers tags
*
@@ -209,22 +30,18 @@ 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)
{
bcscale(2);
return bcmul(
Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->transactionTypes(['Withdrawal'])
->where('transactions.account_id', $account->id)
->before($end)
->after($start)
->where('budget_transaction_journal.budget_id', $budget->id)
->get(['transaction_journals.*'])->sum('amount'), -1
);
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');
}
/**
@@ -240,7 +57,7 @@ class ReportQuery implements ReportQueryInterface
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)
@@ -283,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,7 +54,7 @@ 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

View File

@@ -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,7 +56,8 @@ class AccountController extends Controller
}
/**
* @param Account $account
* @param AccountRepositoryInterface $repository
* @param Account $account
*
* @return \Illuminate\View\View
*/
@@ -211,13 +214,14 @@ class AccountController extends Controller
'name' => $request->input('name'),
'accountType' => $request->input('what'),
'virtualBalance' => round($request->input('virtualBalance'), 2),
'virtualBalanceCurrency' => intval($request->input('amount_currency_id_virtualBalance')),
'active' => true,
'user' => Auth::user()->id,
'iban' => $request->input('iban'),
'accountRole' => $request->input('accountRole'),
'openingBalance' => round($request->input('openingBalance'), 2),
'openingBalanceDate' => new Carbon((string)$request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_openingBalance')),
];

View File

@@ -90,7 +90,8 @@ class AttachmentController extends Controller
}
/**
* @param Attachment $attachment
* @param Attachment $attachment
* @param AttachmentHelperInterface $helper
*/
public function download(Attachment $attachment, AttachmentHelperInterface $helper)
{

View File

@@ -1,6 +1,7 @@
<?php namespace FireflyIII\Http\Controllers\Auth;
use Auth;
use Config;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Role;
use FireflyIII\User;
@@ -8,6 +9,7 @@ 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;
@@ -23,6 +25,18 @@ class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
/**
* 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.
*
@@ -74,7 +88,10 @@ class AuthController extends Controller
$foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first();
if (!is_null($foundUser)) {
// if it exists, show message:
$code = $foundUser->blocked_code;
$code = $foundUser->blocked_code;
if (strlen($code) == 0) {
$code = 'general_blocked';
}
$message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]);
}
@@ -143,21 +160,35 @@ class AuthController extends Controller
}
// @codeCoverageIgnoreEnd
$data = $request->all();
$data['password'] = bcrypt($data['password']);
// 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
if (Auth::user() instanceof User) {
$email = Auth::user()->email;
$address = route('index');
$email = Auth::user()->email;
$address = route('index');
$ipAddress = $request->ip();
// send email.
Mail::send(
['emails.registered-html', 'emails.registered'], ['address' => $address], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
try {
Mail::send(
['emails.registered-html', 'emails.registered'], ['address' => $address, 'ip' => $ipAddress], function (Message $message) use ($email) {
$message->to($email, $email)->subject('Welcome to Firefly III! ');
}
);
} catch (\Swift_TransportException $e) {
Log::error($e->getMessage());
}
);
// set flash message
Session::flash('success', 'You have registered successfully!');
@@ -180,6 +211,40 @@ class AuthController extends Controller
// @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.
*

View File

@@ -1,7 +1,11 @@
<?php namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\User;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Illuminate\Support\Facades\Password;
/**
* Class PasswordController
@@ -41,4 +45,40 @@ class PasswordController extends Controller
$this->middleware('guest');
}
/**
* Send a reset link to the given user.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function 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

@@ -6,8 +6,10 @@ use Carbon\Carbon;
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 Response;
use Session;
@@ -130,30 +132,37 @@ class BudgetController extends Controller
}
/**
* @param BudgetRepositoryInterface $repository
* @param BudgetRepositoryInterface $repository
*
* @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->balanceInPeriod($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);
}
@@ -161,13 +170,12 @@ 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')
);
}
@@ -178,8 +186,9 @@ class BudgetController extends Controller
*/
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',
@@ -194,9 +203,12 @@ 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'));
@@ -272,7 +284,7 @@ class BudgetController extends Controller
{
$budgetData = [
'name' => $request->input('name'),
'active' => intval($request->input('active')) == 1
'active' => intval($request->input('active')) == 1,
];
$repository->update($budget, $budgetData);
@@ -297,8 +309,11 @@ class BudgetController extends Controller
*/
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,8 +5,11 @@ 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 Navigation;
use Preferences;
use Session;
use URL;
@@ -139,6 +142,33 @@ class CategoryController extends Controller
return view('categories.noCategory', compact('list', 'subTitle'));
}
/**
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @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
@@ -151,12 +181,47 @@ class CategoryController extends Controller
$page = intval(Input::get('page'));
$set = $repository->getJournals($category, $page);
$count = $repository->countJournals($category);
$totalSum = $repository->journalsSum($category);
$periodSum = $repository->journalsSum($category, Session::get('start'), Session::get('end'));
$subTitle = $category->name;
$journals = new LengthAwarePaginator($set, $count, 50, $page);
$journals->setPath('categories/show/' . $category->id);
return view('categories.show', compact('category', 'journals', 'hideCategory', 'totalSum', 'periodSum'));
// 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'));
}
/**

View File

@@ -35,45 +35,31 @@ class AccountController extends Controller
/**
* Shows the balances for all the user's accounts.
* Shows the balances for a given set of dates and accounts.
*
* @param AccountRepositoryInterface $repository
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @param $year
* @param $month
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function all(AccountRepositoryInterface $repository, $year, $month, $shared = false)
public function report($report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
// chart properties for cache:
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('all');
$cache->addProperty('accounts');
$cache->addProperty('default');
$cache->addProperty($accounts);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
/** @var Collection $accounts */
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
if ($shared === false) {
/** @var Account $account */
foreach ($accounts as $index => $account) {
if ($account->getMeta('accountRole') == 'sharedAsset') {
$accounts->forget($index);
}
}
}
// make chart:
$data = $this->generator->all($accounts, $start, $end);
$data = $this->generator->frontpage($accounts, $start, $end);
$cache->store($data);
return Response::json($data);

View File

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

View File

@@ -6,6 +6,7 @@ use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
@@ -37,11 +38,87 @@ class BudgetController extends Controller
/**
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @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, Budget $budget)
public function budget(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget)
{
// dates and times
@@ -50,7 +127,9 @@ class BudgetController extends Controller
$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();
@@ -68,7 +147,7 @@ class BudgetController extends Controller
$end->subDay();
$chartDate = clone $end;
$chartDate->startOfMonth();
$spent = $repository->balanceInPeriod($budget, $first, $end);
$spent = $repository->balanceInPeriod($budget, $first, $end, $accounts) * -1;
$entries->push([$chartDate, $spent]);
$first = Navigation::addPeriod($first, $range, 0);
}
@@ -113,7 +192,7 @@ class BudgetController extends Controller
/*
* Sum of expenses on this day:
*/
$sum = $repository->expensesOnDayCorrected($budget, $start);
$sum = $repository->expensesOnDay($budget, $start);
$amount = bcadd($amount, $sum);
$entries->push([clone $start, $amount]);
$start->addDay();
@@ -129,16 +208,16 @@ class BudgetController extends Controller
/**
* Shows a budget list with spent/left/overspent.
*
* @param BudgetRepositoryInterface $repository
* @param BudgetRepositoryInterface $repository
*
* @param AccountRepositoryInterface $accountRepository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(BudgetRepositoryInterface $repository)
public function frontpage(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
{
$budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$allEntries = new Collection;
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
// chart properties for cache:
$cache = new CacheProperties();
@@ -150,58 +229,60 @@ class BudgetController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore
}
$budgets = $repository->getBudgetsAndLimitsInRange($start, $end);
$allEntries = new Collection;
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
bcscale(2);
/** @var Budget $budget */
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->balanceInPeriod($budget, $start, $end, true);
$allEntries->push([$budget->name, 0, 0, $expenses, 0, 0]);
continue;
// we already have amount, startdate and enddate.
// if this "is" a limit repetition (as opposed to a budget without one entirely)
// depends on whether startdate and enddate are null.
$name = $budget->name;
if (is_null($budget->startdate) && is_null($budget->enddate)) {
$currentStart = clone $start;
$currentEnd = clone $end;
$expenses = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts);
$amount = 0;
$left = 0;
$spent = $expenses;
$overspent = 0;
} else {
$currentStart = clone $budget->startdate;
$currentEnd = clone $budget->enddate;
$expenses = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts);
$amount = $budget->amount;
// smaller than 1 means spent MORE than budget allows.
$left = bccomp(bcadd($budget->amount, $expenses), '0') < 1 ? 0 : bcadd($budget->amount, $expenses);
$spent = bccomp(bcadd($budget->amount, $expenses), '0') < 1 ? ($amount * -1) : $expenses;
$overspent = bccomp(bcadd($budget->amount, $expenses), '0') < 1 ? bcadd($budget->amount, $expenses) : 0;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, true);
// $left can be less than zero.
// $overspent can be more than zero ( = overspending)
$left = max(bcsub($repetition->amount, $expenses), 0); // limited at zero.
$overspent = max(bcsub($expenses, $repetition->amount), 0); // limited at zero.
$name = $budget->name;
// $spent is maxed to the repetition amount:
$spent = $expenses > $repetition->amount ? $repetition->amount : $expenses;
$allEntries->push([$name, $left, $spent, $overspent, $repetition->amount, $expenses]);
}
$allEntries->push([$name, $left, $spent, $overspent, $amount, $expenses]);
}
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end) * -1;
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push([trans('firefly.noBudget'), 0, 0, $noBudgetExpenses, 0, 0]);
$data = $this->generator->frontpage($allEntries);
$cache->store($data);
return Response::json($data);
}
/**
* Show a yearly overview for a budget.
*
* @param BudgetRepositoryInterface $repository
* @param $year
* @param bool $shared
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function year(BudgetRepositoryInterface $repository, $year, $shared = false)
public function year(BudgetRepositoryInterface $repository, $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;
$allBudgets = $repository->getBudgets();
$budgets = new Collection;
@@ -209,16 +290,17 @@ class BudgetController extends Controller
$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:
// filter empty budgets:
foreach ($allBudgets as $budget) {
$spent = $repository->balanceInPeriod($budget, $start, $end, $shared);
$spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
if ($spent != 0) {
$budgets->push($budget);
}
@@ -234,14 +316,14 @@ class BudgetController extends Controller
// each budget, fill the row:
foreach ($budgets as $budget) {
$spent = $repository->balanceInPeriod($budget, $start, $month, $shared);
$spent = $repository->balanceInPeriod($budget, $start, $month, $accounts);
$row[] = $spent * -1;
}
$entries->push($row);
$start->endOfMonth()->addDay();
}
$data = $this->generator->year($allBudgets, $entries);
$data = $this->generator->year($budgets, $entries);
$cache->store($data);
return Response::json($data);

View File

@@ -65,11 +65,16 @@ class CategoryController extends Controller
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->balanceInPeriod($category, $start, $currentEnd);
$entries->push([clone $start, $spent]);
$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();
$data = $this->generator->all($entries);
$cache->store($data);
@@ -102,7 +107,7 @@ class CategoryController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore
}
$array = $repository->getCategoriesAndExpensesCorrected($start, $end);
$array = $repository->getCategoriesAndExpenses($start, $end);
// sort by callback:
uasort(
$array,
@@ -111,11 +116,91 @@ class CategoryController extends Controller
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);
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);
@@ -127,7 +212,7 @@ class CategoryController extends Controller
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function month(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());
@@ -138,7 +223,7 @@ class CategoryController extends Controller
$cache->addProperty($end);
$cache->addProperty($category->id);
$cache->addProperty('category');
$cache->addProperty('month');
$cache->addProperty('currentPeriod');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
@@ -146,13 +231,14 @@ class CategoryController extends Controller
while ($start <= $end) {
$spent = $repository->spentOnDaySumCorrected($category, $start);
$entries->push([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();
}
$data = $this->generator->month($entries);
$data = $this->generator->period($entries);
$cache->store($data);
return Response::json($data);
@@ -161,51 +247,139 @@ class CategoryController extends Controller
}
/**
* This chart will only show expenses.
*
* @param CategoryRepositoryInterface $repository
* @param $year
* @param bool $shared
* @param Category $category
*
* @param $date
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function spentInYear(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');
$carbon = new Carbon($date);
$range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($carbon, $range);
$end = Navigation::endOfPeriod($carbon, $range);
$cache = new CacheProperties; // chart properties for cache:
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($category->id);
$cache->addProperty('category');
$cache->addProperty('spent-in-year');
$cache->addProperty('specificPeriod');
$cache->addProperty($date);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
$shared = $shared == 'shared' ? true : false;
$allCategories = $repository->getCategories();
$entries = new Collection;
$categories = $allCategories->filter(
function (Category $category) use ($repository, $start, $end, $shared) {
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
if ($spent < 0) {
return $category;
}
return null;
while ($start <= $end) {
$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) {
$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;
}
);
// start looping the time again, this time processing the
// data for each month.
$start = clone $original;
while ($start < $end) {
$month = clone $start; // month is the current end of the period
$month->endOfMonth();
$row = [clone $start]; // make a row:
$currentEnd = clone $start;
$currentStart = clone $start;
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
foreach ($categories as $category) { // each budget, fill the row
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
if ($spent < 0) {
$row[] = $spent * -1;
// 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;
}
@@ -213,68 +387,99 @@ class CategoryController extends Controller
$entries->push($row);
$start->addMonth();
}
$data = $this->generator->spentInYear($categories, $entries);
$data = $this->generator->earnedInPeriod($categories, $entries);
$cache->store($data);
return Response::json($data);
return $data;
}
/**
* This chart will only show income.
* Returns a chart of what has been spent in this period in each category
* grouped by month.
*
* @param CategoryRepositoryInterface $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 earnedInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
public function spentInPeriod(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$cache = new CacheProperties; // chart properties for cache:
$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-year');
$cache->addProperty('spent-in-period');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$categories = new Collection;
$sets = new Collection;
$entries = new Collection;
$shared = $shared == 'shared' ? true : false;
$allCategories = $repository->getCategories();
$allEntries = new Collection;
$categories = $allCategories->filter(
function (Category $category) use ($repository, $start, $end, $shared) {
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
if ($spent > 0) {
return $category;
}
return null;
// 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) {
$month = clone $start; // month is the current end of the period
$month->endOfMonth();
$row = [clone $start]; // make a row:
$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];
foreach ($categories as $category) { // each budget, fill the row
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
if ($spent > 0) {
$row[] = $spent;
/** @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;
}
}
$allEntries->push($row);
$entries->push($row);
$start->addMonth();
}
$data = $this->generator->earnedInYear($categories, $allEntries);
$data = $this->generator->spentInPeriod($categories, $entries);
$cache->store($data);
return Response::json($data);
return $data;
}
}

View File

@@ -36,41 +36,66 @@ class ReportController extends Controller
* Summarizes all income and expenses, per month, for a given year.
*
* @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(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 properties for cache:
$cache = new CacheProperties;
$cache->addProperty('yearInOut');
$cache->addProperty($year);
$cache->addProperty($shared);
$cache->addProperty($start);
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$entries = new Collection;
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$incomeSum = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount');
$expenseSum = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount');
$entries->push([clone $start, $incomeSum, $expenseSum]);
$start->addMonth();
// 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);
}
$data = $this->generator->yearInOut($entries);
$cache->store($data);
return Response::json($data);
@@ -80,44 +105,71 @@ class ReportController extends Controller
* Summarizes all income and expenses for a given year. Gives a total and an average.
*
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function yearInOutSummarized(ReportQueryInterface $query, $year, $shared = false)
public function yearInOutSummarized(ReportQueryInterface $query, $report_type, Carbon $start, Carbon $end, Collection $accounts)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty('yearInOutSummarized');
$cache->addProperty($year);
$cache->addProperty($shared);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($accounts);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$income = '0';
$expense = '0';
$count = 0;
bcscale(2);
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$income = bcadd($income, $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
$expense = bcadd($expense, $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
$count++;
$start->addMonth();
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);
}
$data = $this->generator->yearInOutSummarized($income, $expense, $count);
$cache->store($data);
return Response::json($data);

View File

@@ -1,7 +1,6 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Config;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
@@ -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

@@ -25,32 +25,42 @@ class CronController extends Controller
if (strlen(env('SENDGRID_USERNAME')) > 0 && strlen(env('SENDGRID_PASSWORD')) > 0) {
$URL = 'https://api.sendgrid.com/api/bounces.get.json';
$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));
$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',
/*
* Loop the result, if any.
*/
if (is_array($data)) {
echo 'Found ' . count($data) . ' entries in the SendGrid bounce 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 '<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";
}
}
}
}

View File

@@ -146,6 +146,8 @@ class CsvController extends Controller
*
* STEP ONE
*
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\View\View
*/
public function index(AccountRepositoryInterface $repository)

View File

@@ -147,7 +147,7 @@ class CurrencyController extends Controller
public function index(CurrencyRepositoryInterface $repository)
{
$currencies = $repository->get();
$defaultCurrency = $repository->getCurrencyByPreference(Preferences::get('currencyPreference', 'EUR'));
$defaultCurrency = $repository->getCurrencyByPreference(Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR')));
if (!Auth::user()->hasRole('owner')) {

View File

@@ -1,15 +1,12 @@
<?php namespace FireflyIII\Http\Controllers;
use Artisan;
use Auth;
use Carbon\Carbon;
use Config;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input;
use Log;
use Preferences;
use Route;
use Session;
use Steam;
@@ -120,33 +117,4 @@ class HomeController extends Controller
);
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Http\RedirectResponse|string
*/
public function routes()
{
if (!Auth::user()->hasRole('owner')) {
Session::flash('warning', 'This page is broken.');
return redirect(route('index'));
}
Log::debug('Make log.');
// get all routes:
$routeCollection = Route::getRoutes();
/** @var \Illuminate\Routing\Route $value */
foreach ($routeCollection as $value) {
$name = $value->getName();
$methods = $value->getMethods();
$isPost = in_array('POST', $methods);
$index = str_replace('.', '-', $name);
if (strlen($name) > 0 && !$isPost) {
echo "'" . $index . "' => '" . $name . "',<br />";
}
}
return '&nbsp;';
}
}

View File

@@ -3,19 +3,15 @@
use Amount;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class JsonController
@@ -66,117 +62,62 @@ class JsonController extends Controller
/**
* @param BillRepositoryInterface $repository
*
* @param AccountRepositoryInterface $accountRepository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function boxBillsPaid(BillRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
public function boxBillsPaid(BillRepositoryInterface $repository)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$amount = 0;
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
bcscale(2);
// works for json too!
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('box-bills-paid');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
/*
* Since both this method and the chart use the exact same data, we can suffice
* with calling the one method in the bill repository that will get this amount.
*/
$amount = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
$creditCardDue = $repository->getCreditCardBill($start, $end);
if ($creditCardDue >= 0) {
$amount = bcadd($amount, $creditCardDue);
}
$bills = $repository->getActiveBills(); // these two functions are the same as the chart
$amount = $amount * -1;
/** @var Bill $bill */
foreach ($bills as $bill) {
$amount = bcadd($amount, $repository->billPaymentsInRange($bill, $start, $end));
}
unset($bill, $bills);
$creditCards = $accountRepository->getCreditCards(); // Find credit card accounts and possibly unpaid credit card bills.
/** @var Account $creditCard */
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, $end, true); // if the balance is not zero, the monthly payment is still underway.
if ($balance == 0) {
// find a transfer TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$amount = bcadd($amount, $accountRepository->getTransfersInRange($creditCard, $start, $end)->sum('amount'));
}
}
$data = ['box' => 'bills-paid', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
$cache->store($data);
return Response::json($data);
}
/**
* @param BillRepositoryInterface $repository
* @param AccountRepositoryInterface $accountRepository
* @param BillRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
* @return \Illuminate\Http\JsonResponse
*/
public function boxBillsUnpaid(BillRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
public function boxBillsUnpaid(BillRepositoryInterface $repository)
{
$amount = 0;
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
bcscale(2);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$amount = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
$creditCardDue = $repository->getCreditCardBill($start, $end);
// works for json too!
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('box-bills-unpaid');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$bills = $repository->getActiveBills();
$unpaid = new Collection; // bills
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
$journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
if ($journals->count() == 0) {
$unpaid->push([$bill, $range['start']]);
}
}
}
unset($bill, $bills, $range, $ranges);
$creditCards = $accountRepository->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;
$fakeAmount = $balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $fakeAmount);
$unpaid->push([$fakeBill, $date]);
}
}
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$current = bcdiv(bcadd($entry[0]->amount_max, $entry[0]->amount_min), 2);
$amount = bcadd($amount, $current);
if ($creditCardDue < 0) {
// expenses are negative (bill not yet paid),
$creditCardDue = bcmul($creditCardDue, '-1');
$amount = bcadd($amount, $creditCardDue);
}
$data = ['box' => 'bills-unpaid', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
$cache->store($data);
return Response::json($data);
}
/**
* @param ReportQueryInterface $reportQuery
* @param ReportQueryInterface $reportQuery
*
* @param AccountRepositoryInterface $accountRepository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function boxIn(ReportQueryInterface $reportQuery)
public function boxIn(ReportQueryInterface $reportQuery, AccountRepositoryInterface $accountRepository)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
@@ -189,8 +130,8 @@ class JsonController extends Controller
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$amount = $reportQuery->incomeInPeriodCorrected($start, $end, true)->sum('amount');
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
$amount = $reportQuery->incomeInPeriod($start, $end, $accounts)->sum('to_amount');
$data = ['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
$cache->store($data);
@@ -199,15 +140,18 @@ class JsonController extends Controller
}
/**
* @param ReportQueryInterface $reportQuery
* @param ReportQueryInterface $reportQuery
*
* @param AccountRepositoryInterface $accountRepository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function boxOut(ReportQueryInterface $reportQuery)
public function boxOut(ReportQueryInterface $reportQuery, AccountRepositoryInterface $accountRepository)
{
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
// works for json too!
$cache = new CacheProperties;
@@ -218,7 +162,7 @@ class JsonController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore
}
$amount = $reportQuery->expenseInPeriodCorrected($start, $end, true)->sum('amount');
$amount = $reportQuery->expenseInPeriod($start, $end, $accounts)->sum('to_amount');
$data = ['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
$cache->store($data);
@@ -240,7 +184,6 @@ class JsonController extends Controller
foreach ($list as $entry) {
$return[] = $entry->name;
}
sort($return);
return Response::json($return);
}

View File

@@ -49,7 +49,6 @@ class NewUserController extends Controller
*/
public function submit(NewUserFormRequest $request, AccountRepositoryInterface $repository)
{
// create normal asset account:
$assetAccount = [
'name' => $request->get('bank_name'),
@@ -61,7 +60,7 @@ class NewUserController extends Controller
'accountRole' => 'defaultAsset',
'openingBalance' => round($request->input('bank_balance'), 2),
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_bank_balance')),
];
$repository->store($assetAccount);
@@ -78,7 +77,7 @@ class NewUserController extends Controller
'accountRole' => 'savingAsset',
'openingBalance' => round($request->input('savings_balance'), 2),
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_savings_balance')),
];
$repository->store($savingsAccount);
}
@@ -96,7 +95,7 @@ class NewUserController extends Controller
'accountRole' => 'ccAsset',
'openingBalance' => null,
'openingBalanceDate' => null,
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
'openingBalanceCurrency' => intval($request->input('amount_currency_id_credit_card_limit')),
];
$creditCard = $repository->store($creditAccount);

View File

@@ -37,10 +37,12 @@ class PreferencesController extends Controller
$viewRange = $viewRangePref->data;
$frontPageAccounts = Preferences::get('frontPageAccounts', []);
$budgetMax = Preferences::get('budgetMaximum', 1000);
$language = Preferences::get('language', 'en')->data;
$language = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'))->data;
$budgetMaximum = $budgetMax->data;
return view('preferences.index', compact('budgetMaximum', 'language', 'accounts', 'frontPageAccounts', 'viewRange'));
$showIncomplete = env('SHOW_INCOMPLETE_TRANSLATIONS', 'false') == 'true';
return view('preferences.index', compact('budgetMaximum', 'language', 'accounts', 'frontPageAccounts', 'viewRange', 'showIncomplete'));
}
/**
@@ -70,7 +72,7 @@ class PreferencesController extends Controller
// language:
$lang = Input::get('language');
if (in_array($lang, array_keys(Config::get('firefly.lang')))) {
if (in_array($lang, array_keys(Config::get('firefly.languages')))) {
Preferences::set('language', $lang);
}

View File

@@ -66,7 +66,7 @@ class ProfileController extends Controller
}
// update the user with the new password.
Auth::user()->password = $request->get('new_password');
Auth::user()->password = bcrypt($request->get('new_password'));
Auth::user()->save();
Session::flash('success', trans('firefly.password_changed'));

View File

@@ -4,6 +4,7 @@ use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Support\Collection;
use Session;
use View;
@@ -41,111 +42,199 @@ class ReportController extends Controller
*/
public function index(AccountRepositoryInterface $repository)
{
$start = Session::get('first');
$months = $this->helper->listOfMonths($start);
$start = Session::get('first');
$months = $this->helper->listOfMonths($start);
$startOfMonth = clone Session::get('start');
$endOfMonth = clone Session::get('start');
$startOfYear = clone Session::get('start');
$endOfYear = clone Session::get('start');
$startOfMonth->startOfMonth();
$endOfMonth->endOfMonth();
$startOfYear->startOfYear();
$endOfYear->endOfYear();
// does the user have shared accounts?
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
$hasShared = false;
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
// get id's for quick links:
$accountIds = [];
/** @var Account $account */
foreach ($accounts as $account) {
if ($account->getMeta('accountRole') == 'sharedAsset') {
$hasShared = true;
}
$accountIds [] = $account->id;
}
return view('reports.index', compact('months', 'hasShared'));
}
/**
* @param string $year
* @param string $month
*
* @param bool $shared
*
* @return \Illuminate\View\View
*/
public function month($year = '2014', $month = '1', $shared = false)
{
$start = new Carbon($year . '-' . $month . '-01');
$subTitle = trans('firefly.reportForMonth', ['month' => $start->formatLocalized($this->monthFormat)]);
$subTitleIcon = 'fa-calendar';
$end = clone $start;
$incomeTopLength = 8;
$expenseTopLength = 8;
if ($shared == 'shared') {
$shared = true;
$subTitle = trans('firefly.reportForMonthShared', ['month' => $start->formatLocalized($this->monthFormat)]);
}
$end->endOfMonth();
$accounts = $this->helper->getAccountReport($start, $end, $shared);
$incomes = $this->helper->getIncomeReport($start, $end, $shared);
$expenses = $this->helper->getExpenseReport($start, $end, $shared);
$budgets = $this->helper->getBudgetReport($start, $end, $shared);
$categories = $this->helper->getCategoryReport($start, $end, $shared);
$balance = $this->helper->getBalanceReport($start, $end, $shared);
$bills = $this->helper->getBillReport($start, $end);
Session::flash('gaEventCategory', 'report');
Session::flash('gaEventAction', 'month');
Session::flash('gaEventLabel', $start->format('F Y'));
$accountList = join(',', $accountIds);
return view(
'reports.month',
compact(
'start', 'shared',
'subTitle', 'subTitleIcon',
'accounts',
'incomes', 'incomeTopLength',
'expenses', 'expenseTopLength',
'budgets', 'balance',
'categories',
'bills'
)
'reports.index', compact(
'months', 'accounts', 'start', 'accountList',
'startOfMonth', 'endOfMonth', 'startOfYear', 'endOfYear'
)
);
}
/**
* @param $year
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @param bool $shared
*
* @return $this
* @return View
*/
public function year($year, $shared = false)
public function defaultYear($report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$start = new Carbon('01-01-' . $year);
$end = clone $start;
$subTitle = trans('firefly.reportForYear', ['year' => $year]);
$subTitleIcon = 'fa-bar-chart';
$incomeTopLength = 8;
$expenseTopLength = 8;
if ($shared == 'shared') {
$shared = true;
$subTitle = trans('firefly.reportForYearShared', ['year' => $year]);
}
$end->endOfYear();
$accounts = $this->helper->getAccountReport($start, $end, $shared);
$incomes = $this->helper->getIncomeReport($start, $end, $shared);
$expenses = $this->helper->getExpenseReport($start, $end, $shared);
$accountReport = $this->helper->getAccountReport($start, $end, $accounts);
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
Session::flash('gaEventCategory', 'report');
Session::flash('gaEventAction', 'year');
Session::flash('gaEventLabel', $start->format('Y'));
// and some id's, joined:
$accountIds = [];
/** @var Account $account */
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$accountIds = join(',', $accountIds);
return view(
'reports.year',
compact('start', 'shared', 'accounts', 'incomes', 'expenses', 'subTitle', 'subTitleIcon', 'incomeTopLength', 'expenseTopLength')
'reports.default.year',
compact(
'start', 'accountReport', 'incomes', 'report_type', 'accountIds', 'end',
'expenses', 'incomeTopLength', 'expenseTopLength'
)
);
}
/**
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return View
*/
public function defaultMonth($report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$incomeTopLength = 8;
$expenseTopLength = 8;
// get report stuff!
$accountReport = $this->helper->getAccountReport($start, $end, $accounts);
$incomes = $this->helper->getIncomeReport($start, $end, $accounts);
$expenses = $this->helper->getExpenseReport($start, $end, $accounts);
$budgets = $this->helper->getBudgetReport($start, $end, $accounts);
$categories = $this->helper->getCategoryReport($start, $end, $accounts);
$balance = $this->helper->getBalanceReport($start, $end, $accounts);
$bills = $this->helper->getBillReport($start, $end, $accounts);
// and some id's, joined:
$accountIds = [];
/** @var Account $account */
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$accountIds = join(',', $accountIds);
// continue!
return view(
'reports.default.month',
compact(
'start', 'end', 'report_type',
'accountReport',
'incomes', 'incomeTopLength',
'expenses', 'expenseTopLength',
'budgets', 'balance',
'categories',
'bills',
'accountIds', 'report_type'
)
);
}
/**
* @param $report_type
* @param $start
* @param $end
* @param $accounts
*
* @return View
*/
public function defaultMultiYear($report_type, $start, $end, $accounts)
{
// list of users stuff:
$budgets = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface')->getActiveBudgets();
$categories = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface')->getCategories();
// and some id's, joined:
$accountIds = [];
/** @var Account $account */
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$accountIds = join(',', $accountIds);
return view(
'reports.default.multi-year', compact('budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'report_type')
);
}
/**
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return View
*/
public function report($report_type, Carbon $start, Carbon $end, Collection $accounts)
{
// throw an error if necessary.
if ($end < $start) {
return view('error')->with('message', 'End date cannot be before start date, silly!');
}
// lower threshold
if ($start < Session::get('first')) {
$start = Session::get('first');
}
switch ($report_type) {
default:
case 'default':
View::share(
'subTitle', trans(
'firefly.report_default',
[
'start' => $start->formatLocalized($this->monthFormat),
'end' => $end->formatLocalized($this->monthFormat),
]
)
);
View::share('subTitleIcon', 'fa-calendar');
// more than one year date difference means year report.
if ($start->diffInMonths($end) > 12) {
return $this->defaultMultiYear($report_type, $start, $end, $accounts);
}
// more than two months date difference means year report.
if ($start->diffInMonths($end) > 1) {
return $this->defaultYear($report_type, $start, $end, $accounts);
}
return $this->defaultMonth($report_type, $start, $end, $accounts);
}
}
}

View File

@@ -44,9 +44,9 @@ class TagController extends Controller
View::share('mainTitleIcon', 'fa-tags');
View::share('hideTags', true);
$this->tagOptions = [
'nothing' => 'Just a regular tag.',
'balancingAct' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.',
'advancePayment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.',
'nothing' => trans('firefly.regular_tag'),
'balancingAct' => trans('firefly.balancing_act'),
'advancePayment' => trans('firefly.advance_payment'),
];
View::share('tagOptions', $this->tagOptions);
}

View File

@@ -10,10 +10,13 @@ use FireflyIII\Events\JournalSaved;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Requests\JournalFormRequest;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Support\Collection;
use Input;
use Preferences;
use Response;
@@ -45,8 +48,9 @@ class TransactionController extends Controller
*
* @return \Illuminate\View\View
*/
public function create(AccountRepositoryInterface $repository, $what = 'deposit')
public function create(AccountRepositoryInterface $repository, $what = TransactionType::DEPOSIT)
{
$what = strtolower($what);
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
$uploadSize = min($maxFileSize, $maxPostSize);
@@ -95,7 +99,7 @@ class TransactionController extends Controller
*/
public function delete(TransactionJournal $journal)
{
$what = strtolower($journal->transactionType->type);
$what = strtolower($journal->getTransactionType());
$subTitle = trans('firefly.delete_' . $what, ['description' => $journal->description]);
// put previous url in session
@@ -136,10 +140,16 @@ class TransactionController extends Controller
*/
public function edit(AccountRepositoryInterface $repository, TransactionJournal $journal)
{
// cannot edit opening balance
if ($journal->isOpeningBalance()) {
return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!');
}
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
$uploadSize = min($maxFileSize, $maxPostSize);
$what = strtolower($journal->transactionType->type);
$what = strtolower($journal->getTransactionType());
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = trans('form.noBudget');
@@ -173,9 +183,9 @@ class TransactionController extends Controller
$preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->orderBy('date', 'DESC')->first()->piggy_bank_id;
}
$preFilled['amount'] = $journal->actual_amount;
$preFilled['amount'] = $journal->amount_positive;
if ($journal->transactionType->type == 'Withdrawal') {
if ($journal->isWithdrawal()) {
$preFilled['account_id'] = $journal->source_account->id;
$preFilled['expense_account'] = $journal->destination_account->name_for_editform;
} else {
@@ -256,6 +266,15 @@ class TransactionController extends Controller
*/
public function show(JournalRepositoryInterface $repository, TransactionJournal $journal)
{
/** @var Collection $set */
$events = $journal->piggyBankEvents()->get();
$events->each(
function (PiggyBankEvent $event) {
$event->piggyBank = $event->piggyBank()->withTrashed()->first();
}
);
bcscale(2);
$journal->transactions->each(
function (Transaction $t) use ($journal, $repository) {
@@ -263,16 +282,18 @@ class TransactionController extends Controller
$t->after = bcadd($t->before, $t->amount);
}
);
$what = strtolower($journal->transactionType->type);
$subTitle = trans('firefly.' . $journal->transactionType->type) . ' "' . e($journal->description) . '"';
$what = strtolower($journal->getTransactionType());
$subTitle = trans('firefly.' . $journal->getTransactionType()) . ' "' . e($journal->description) . '"';
return view('transactions.show', compact('journal', 'subTitle', 'what'));
return view('transactions.show', compact('journal','events', 'subTitle', 'what'));
}
/**
* @param JournalFormRequest $request
* @param JournalRepositoryInterface $repository
*
* @param AttachmentHelperInterface $att
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository, AttachmentHelperInterface $att)
@@ -281,7 +302,7 @@ class TransactionController extends Controller
$journalData = $request->getJournalData();
// if not withdrawal, unset budgetid.
if ($journalData['what'] != 'withdrawal') {
if ($journalData['what'] != strtolower(TransactionType::WITHDRAWAL)) {
$journalData['budget_id'] = 0;
}
@@ -302,7 +323,7 @@ class TransactionController extends Controller
// rescan journal, UpdateJournalConnection
event(new JournalSaved($journal));
if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) {
if ($journal->isTransfer() && intval($request->get('piggy_bank_id')) > 0) {
event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
}
@@ -333,6 +354,11 @@ class TransactionController extends Controller
public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, AttachmentHelperInterface $att, TransactionJournal $journal)
{
// cannot edit opening balance
if ($journal->isOpeningBalance()) {
return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!');
}
$journalData = $request->getJournalData();
$repository->update($journal, $journalData);

View File

@@ -1,9 +1,10 @@
<?php namespace FireflyIII\Http\Middleware;
use App;
use Auth;
use Carbon\Carbon;
use Closure;
use Config;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Preferences;
@@ -45,6 +46,7 @@ class Authenticate
*/
public function handle(Request $request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
@@ -53,16 +55,21 @@ class Authenticate
}
}
if (intval($this->auth->user()->blocked) == 1) {
return redirect()->route('logout');
if ($this->auth->user() instanceof User && intval($this->auth->user()->blocked) == 1) {
Auth::logout();
return redirect()->route('index');
}
// if logged in, set user language:
$pref = Preferences::get('language', 'en');
$pref = Preferences::get('language', env('DEFAULT_LANGUAGE', 'en_US'));
App::setLocale($pref->data);
Carbon::setLocale($pref->data);
Carbon::setLocale(substr($pref->data, 0, 2));
$locale = explode(',', trans('config.locale'));
$locale = array_map('trim', $locale);
setlocale(LC_TIME, Config::get('firefly.locales.' . $pref->data));
setlocale(LC_TIME, $locale);
setlocale(LC_MONETARY, $locale);
return $next($request);
}

View File

@@ -41,18 +41,19 @@ class AccountFormRequest extends Request
}
return [
'id' => $idRule,
'name' => $nameRule,
'openingBalance' => 'numeric',
'iban' => 'iban',
'virtualBalance' => 'numeric',
'openingBalanceDate' => 'date',
'accountRole' => 'in:' . $accountRoles,
'active' => 'boolean',
'ccType' => 'in:' . $ccPaymentTypes,
'ccMonthlyPaymentDate' => 'date',
'balance_currency_id' => 'exists:transaction_currencies,id',
'what' => 'in:' . $types
'id' => $idRule,
'name' => $nameRule,
'openingBalance' => 'numeric',
'iban' => 'iban',
'virtualBalance' => 'numeric',
'openingBalanceDate' => 'date',
'accountRole' => 'in:' . $accountRoles,
'active' => 'boolean',
'ccType' => 'in:' . $ccPaymentTypes,
'ccMonthlyPaymentDate' => 'date',
'amount_currency_id_openingBalance' => 'exists:transaction_currencies,id',
'amount_currency_id_virtualBalance' => 'exists:transaction_currencies,id',
'what' => 'in:' . $types
];
}
}

View File

@@ -29,17 +29,18 @@ class BillFormRequest extends Request
public function getBillData()
{
return [
'name' => $this->get('name'),
'match' => $this->get('match'),
'amount_min' => round($this->get('amount_min'), 2),
'amount_currency_id' => round($this->get('amount_currency_id'), 2),
'amount_max' => round($this->get('amount_max'), 2),
'date' => new Carbon($this->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $this->get('repeat_freq'),
'skip' => intval($this->get('skip')),
'automatch' => intval($this->get('automatch')) === 1,
'active' => intval($this->get('active')) === 1,
'name' => $this->get('name'),
'match' => $this->get('match'),
'amount_min' => round($this->get('amount_min'), 2),
'amount_currency_id_amount_min' => intval($this->get('amount_currency_id_amount_min')),
'amount_currency_id_amount_max' => intval($this->get('amount_currency_id_amount_max')),
'amount_max' => round($this->get('amount_max'), 2),
'date' => new Carbon($this->get('date')),
'user' => Auth::user()->id,
'repeat_freq' => $this->get('repeat_freq'),
'skip' => intval($this->get('skip')),
'automatch' => intval($this->get('automatch')) === 1,
'active' => intval($this->get('active')) === 1,
];
}
@@ -56,16 +57,17 @@ class BillFormRequest extends Request
}
$rules = [
'name' => $nameRule,
'match' => $matchRule,
'amount_min' => 'required|numeric|min:0.01',
'amount_max' => 'required|numeric|min:0.01',
'amount_currency_id' => 'required|exists:transaction_currencies,id',
'date' => 'required|date',
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
'skip' => 'required|between:0,31',
'automatch' => 'in:1',
'active' => 'in:1',
'name' => $nameRule,
'match' => $matchRule,
'amount_min' => 'required|numeric|min:0.01',
'amount_max' => 'required|numeric|min:0.01',
'amount_currency_id_amount_min' => 'required|exists:transaction_currencies,id',
'amount_currency_id_amount_max' => 'required|exists:transaction_currencies,id',
'date' => 'required|date',
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
'skip' => 'required|between:0,31',
'automatch' => 'in:1',
'active' => 'in:1',
];
return $rules;

View File

@@ -5,6 +5,7 @@ namespace FireflyIII\Http\Requests;
use Auth;
use Carbon\Carbon;
use Exception;
use FireflyIII\Models\TransactionType;
use Input;
/**
@@ -30,20 +31,20 @@ class JournalFormRequest extends Request
public function getJournalData()
{
return [
'what' => $this->get('what'),
'description' => $this->get('description'),
'account_id' => intval($this->get('account_id')),
'account_from_id' => intval($this->get('account_from_id')),
'account_to_id' => intval($this->get('account_to_id')),
'expense_account' => $this->get('expense_account'),
'revenue_account' => $this->get('revenue_account'),
'amount' => round($this->get('amount'), 2),
'user' => Auth::user()->id,
'amount_currency_id' => intval($this->get('amount_currency_id')),
'date' => new Carbon($this->get('date')),
'budget_id' => intval($this->get('budget_id')),
'category' => $this->get('category'),
'tags' => explode(',', $this->get('tags')),
'what' => $this->get('what'),
'description' => $this->get('description'),
'account_id' => intval($this->get('account_id')),
'account_from_id' => intval($this->get('account_from_id')),
'account_to_id' => intval($this->get('account_to_id')),
'expense_account' => $this->get('expense_account'),
'revenue_account' => $this->get('revenue_account'),
'amount' => round($this->get('amount'), 2),
'user' => Auth::user()->id,
'amount_currency_id_amount' => intval($this->get('amount_currency_id_amount')),
'date' => new Carbon($this->get('date')),
'budget_id' => intval($this->get('budget_id')),
'category' => $this->get('category'),
'tags' => explode(',', $this->get('tags')),
];
}
@@ -56,16 +57,16 @@ class JournalFormRequest extends Request
{
$what = Input::get('what');
$rules = [
'description' => 'required|min:1,max:255',
'what' => 'required|in:withdrawal,deposit,transfer',
'amount' => 'numeric|required|min:0.01',
'date' => 'required|date',
'amount_currency_id' => 'required|exists:transaction_currencies,id',
'description' => 'required|min:1,max:255',
'what' => 'required|in:withdrawal,deposit,transfer',
'amount' => 'numeric|required|min:0.01',
'date' => 'required|date',
'amount_currency_id_amount' => 'required|exists:transaction_currencies,id',
];
switch ($what) {
case 'withdrawal':
case strtolower(TransactionType::WITHDRAWAL):
$rules['account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
$rules['expense_account'] = 'between:1,255';
$rules['category'] = 'between:1,255';
@@ -73,12 +74,12 @@ class JournalFormRequest extends Request
$rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets';
}
break;
case 'deposit':
case strtolower(TransactionType::DEPOSIT):
$rules['category'] = 'between:1,255';
$rules['account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
$rules['revenue_account'] = 'between:1,255';
break;
case 'transfer':
case strtolower(TransactionType::TRANSFER):
$rules['account_from_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:account_to_id';
$rules['account_to_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:account_from_id';
$rules['category'] = 'between:1,255';

View File

@@ -27,11 +27,13 @@ class NewUserFormRequest extends Request
public function rules()
{
return [
'bank_name' => 'required|between:1,200',
'bank_balance' => 'required|numeric',
'savings_balance' => 'numeric',
'credit_card_limit' => 'numeric',
'balance_currency_id' => 'exists:transaction_currencies,id',
'bank_name' => 'required|between:1,200',
'bank_balance' => 'required|numeric',
'savings_balance' => 'numeric',
'credit_card_limit' => 'numeric',
'amount_currency_id_bank_balance' => 'exists:transaction_currencies,id',
'amount_currency_id_savings_balance' => 'exists:transaction_currencies,id',
'amount_currency_id_credit_card_limit' => 'exists:transaction_currencies,id',
];
}
}

View File

@@ -36,13 +36,13 @@ class PiggyBankFormRequest extends Request
$rules = [
'name' => $nameRule,
'account_id' => 'required|belongsToUser:accounts',
'targetamount' => 'required|min:0.01',
'amount_currency_id' => 'exists:transaction_currencies,id',
'startdate' => 'date',
'targetdate' => $targetDateRule,
'order' => 'integer|min:1',
'name' => $nameRule,
'account_id' => 'required|belongsToUser:accounts',
'targetamount' => 'required|min:0.01',
'amount_currency_id_targetamount' => 'exists:transaction_currencies,id',
'startdate' => 'date',
'targetdate' => $targetDateRule,
'order' => 'integer|min:1',
];

View File

@@ -1,6 +1,6 @@
<?php
use Carbon\Carbon;
use DaveJamesMiller\Breadcrumbs\Generator;
use DaveJamesMiller\Breadcrumbs\Generator as BreadCrumbGenerator;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
@@ -16,7 +16,7 @@ use FireflyIII\Models\TransactionJournal;
*/
Breadcrumbs::register(
'home',
function (Generator $breadcrumbs) {
function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->push(trans('breadcrumbs.home'), route('index'));
}
@@ -24,7 +24,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'index',
function (Generator $breadcrumbs) {
function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->push(trans('breadcrumbs.home'), route('index'));
}
@@ -33,21 +33,21 @@ Breadcrumbs::register(
// accounts
Breadcrumbs::register(
'accounts.index', function (Generator $breadcrumbs, $what) {
'accounts.index', function (BreadCrumbGenerator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('firefly.' . strtolower(e($what)) . '_accounts'), route('accounts.index', [$what]));
}
);
Breadcrumbs::register(
'accounts.create', function (Generator $breadcrumbs, $what) {
'accounts.create', function (BreadCrumbGenerator $breadcrumbs, $what) {
$breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push(trans('breadcrumbs.new_' . strtolower(e($what)) . '_account'), route('accounts.create', [$what]));
$breadcrumbs->push(trans('firefly.new_' . strtolower(e($what)) . '_account'), route('accounts.create', [$what]));
}
);
Breadcrumbs::register(
'accounts.show', function (Generator $breadcrumbs, Account $account) {
'accounts.show', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
@@ -57,7 +57,7 @@ Breadcrumbs::register(
}
);
Breadcrumbs::register(
'accounts.delete', function (Generator $breadcrumbs, Account $account) {
'accounts.delete', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push(trans('firefly.delete_account', ['name' => e($account->name)]), route('accounts.delete', [$account->id]));
}
@@ -65,7 +65,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'accounts.edit', function (Generator $breadcrumbs, Account $account) {
'accounts.edit', function (BreadCrumbGenerator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account);
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
@@ -75,40 +75,40 @@ Breadcrumbs::register(
// budgets.
Breadcrumbs::register(
'budgets.index', function (Generator $breadcrumbs) {
'budgets.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('firefly.budgets'), route('budgets.index'));
}
);
Breadcrumbs::register(
'budgets.create', function (Generator $breadcrumbs) {
'budgets.create', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push(trans('firefly.create_new_budget'), route('budgets.create'));
}
);
Breadcrumbs::register(
'budgets.edit', function (Generator $breadcrumbs, Budget $budget) {
'budgets.edit', function (BreadCrumbGenerator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push(trans('firefly.edit_budget', ['name' => e($budget->name)]), route('budgets.edit', [$budget->id]));
}
);
Breadcrumbs::register(
'budgets.delete', function (Generator $breadcrumbs, Budget $budget) {
'budgets.delete', function (BreadCrumbGenerator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push(trans('firefly.delete_budget', ['name' => e($budget->name)]), route('budgets.delete', [$budget->id]));
}
);
Breadcrumbs::register(
'budgets.noBudget', function (Generator $breadcrumbs, $subTitle) {
'budgets.noBudget', function (BreadCrumbGenerator $breadcrumbs, $subTitle) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push($subTitle, route('budgets.noBudget'));
}
);
Breadcrumbs::register(
'budgets.show', function (Generator $breadcrumbs, Budget $budget, LimitRepetition $repetition = null) {
'budgets.show', function (BreadCrumbGenerator $breadcrumbs, Budget $budget, LimitRepetition $repetition = null) {
$breadcrumbs->parent('budgets.index');
$breadcrumbs->push(e($budget->name), route('budgets.show', [$budget->id]));
if (!is_null($repetition) && !is_null($repetition->id)) {
@@ -121,33 +121,33 @@ Breadcrumbs::register(
// categories
Breadcrumbs::register(
'categories.index', function (Generator $breadcrumbs) {
'categories.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('firefly.categories'), route('categories.index'));
}
);
Breadcrumbs::register(
'categories.create', function (Generator $breadcrumbs) {
'categories.create', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push(trans('breadcrumbs.newCategory'), route('categories.create'));
$breadcrumbs->push(trans('firefly.new_category'), route('categories.create'));
}
);
Breadcrumbs::register(
'categories.edit', function (Generator $breadcrumbs, Category $category) {
'categories.edit', function (BreadCrumbGenerator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push(trans('firefly.edit_category', ['name' => e($category->name)]), route('categories.edit', [$category->id]));
}
);
Breadcrumbs::register(
'categories.delete', function (Generator $breadcrumbs, Category $category) {
'categories.delete', function (BreadCrumbGenerator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push(trans('firefly.delete_category', ['name' => e($category->name)]), route('categories.delete', [$category->id]));
}
);
Breadcrumbs::register(
'categories.show', function (Generator $breadcrumbs, Category $category) {
'categories.show', function (BreadCrumbGenerator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push(e($category->name), route('categories.show', [$category->id]));
@@ -155,7 +155,20 @@ Breadcrumbs::register(
);
Breadcrumbs::register(
'categories.noCategory', function (Generator $breadcrumbs, $subTitle) {
'categories.show.date', function (BreadCrumbGenerator $breadcrumbs, Category $category, Carbon $date) {
// get current period preference.
$range = Preferences::get('viewRange', '1M')->data;
$breadcrumbs->parent('categories.index');
$breadcrumbs->push(e($category->name), route('categories.show', [$category->id]));
$breadcrumbs->push(Navigation::periodShow($date, $range), route('categories.show.date', [$category->id, $date->format('Y-m-d')]));
}
);
Breadcrumbs::register(
'categories.noCategory', function (BreadCrumbGenerator $breadcrumbs, $subTitle) {
$breadcrumbs->parent('categories.index');
$breadcrumbs->push($subTitle, route('categories.noCategory'));
}
@@ -163,35 +176,35 @@ Breadcrumbs::register(
// CSV:
Breadcrumbs::register(
'csv.index', function (Generator $breadcrumbs) {
'csv.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('firefly.csv_index_title'), route('csv.index'));
}
);
Breadcrumbs::register(
'csv.column-roles', function (Generator $breadcrumbs) {
'csv.column-roles', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('csv.index');
$breadcrumbs->push(trans('firefly.csv_define_column_roles'), route('csv.column-roles'));
}
);
Breadcrumbs::register(
'csv.map', function (Generator $breadcrumbs) {
'csv.map', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('csv.index');
$breadcrumbs->push(trans('firefly.csv_map_values'), route('csv.map'));
}
);
Breadcrumbs::register(
'csv.download-config-page', function (Generator $breadcrumbs) {
'csv.download-config-page', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('csv.index');
$breadcrumbs->push(trans('firefly.csv_download_config'), route('csv.download-config-page'));
}
);
Breadcrumbs::register(
'csv.process', function (Generator $breadcrumbs) {
'csv.process', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('csv.index');
$breadcrumbs->push(trans('firefly.csv_process_title'), route('csv.process'));
}
@@ -200,27 +213,27 @@ Breadcrumbs::register(
// currencies.
Breadcrumbs::register(
'currency.index', function (Generator $breadcrumbs) {
'currency.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('firefly.currencies'), route('currency.index'));
}
);
Breadcrumbs::register(
'currency.create', function (Generator $breadcrumbs) {
'currency.create', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('currency.index');
$breadcrumbs->push(trans('firefly.create_currency'), route('currency.create'));
}
);
Breadcrumbs::register(
'currency.edit', function (Generator $breadcrumbs, TransactionCurrency $currency) {
'currency.edit', function (BreadCrumbGenerator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index');
$breadcrumbs->push(trans('breadcrumbs.edit_currency', ['name' => e($currency->name)]), route('currency.edit', [$currency->id]));
}
);
Breadcrumbs::register(
'currency.delete', function (Generator $breadcrumbs, TransactionCurrency $currency) {
'currency.delete', function (BreadCrumbGenerator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index');
$breadcrumbs->push(trans('breadcrumbs.delete_currency', ['name' => e($currency->name)]), route('currency.delete', [$currency->id]));
}
@@ -229,33 +242,33 @@ Breadcrumbs::register(
// piggy banks
Breadcrumbs::register(
'piggy-banks.index', function (Generator $breadcrumbs) {
'piggy-banks.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.piggyBanks'), route('piggy-banks.index'));
$breadcrumbs->push(trans('firefly.piggyBanks'), route('piggy-banks.index'));
}
);
Breadcrumbs::register(
'piggy-banks.create', function (Generator $breadcrumbs) {
'piggy-banks.create', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('piggy-banks.index');
$breadcrumbs->push(trans('breadcrumbs.newPiggyBank'), route('piggy-banks.create'));
}
);
Breadcrumbs::register(
'piggy-banks.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
'piggy-banks.edit', function (BreadCrumbGenerator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push(trans('breadcrumbs.edit_piggyBank', ['name' => e($piggyBank->name)]), route('piggy-banks.edit', [$piggyBank->id]));
}
);
Breadcrumbs::register(
'piggy-banks.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
'piggy-banks.delete', function (BreadCrumbGenerator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push(trans('breadcrumbs.delete_piggyBank', ['name' => e($piggyBank->name)]), route('piggy-banks.delete', [$piggyBank->id]));
$breadcrumbs->push(trans('firefly.delete_piggy_bank', ['name' => e($piggyBank->name)]), route('piggy-banks.delete', [$piggyBank->id]));
}
);
Breadcrumbs::register(
'piggy-banks.show', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
'piggy-banks.show', function (BreadCrumbGenerator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.index');
$breadcrumbs->push(e($piggyBank->name), route('piggy-banks.show', [$piggyBank->id]));
@@ -264,7 +277,7 @@ Breadcrumbs::register(
// preferences
Breadcrumbs::register(
'preferences', function (Generator $breadcrumbs) {
'preferences', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences'));
@@ -273,21 +286,21 @@ Breadcrumbs::register(
// profile
Breadcrumbs::register(
'profile', function (Generator $breadcrumbs) {
'profile', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.profile'), route('profile'));
}
);
Breadcrumbs::register(
'profile.change-password', function (Generator $breadcrumbs) {
'profile.change-password', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('profile');
$breadcrumbs->push(trans('breadcrumbs.changePassword'), route('profile.change-password'));
}
);
Breadcrumbs::register(
'profile.delete-account', function (Generator $breadcrumbs) {
'profile.delete-account', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('profile');
$breadcrumbs->push(trans('firefly.delete_account'), route('profile.delete-account'));
@@ -296,33 +309,33 @@ Breadcrumbs::register(
// bills
Breadcrumbs::register(
'bills.index', function (Generator $breadcrumbs) {
'bills.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.bills'), route('bills.index'));
}
);
Breadcrumbs::register(
'bills.create', function (Generator $breadcrumbs) {
'bills.create', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('bills.index');
$breadcrumbs->push(trans('breadcrumbs.newBill'), route('bills.create'));
}
);
Breadcrumbs::register(
'bills.edit', function (Generator $breadcrumbs, Bill $bill) {
'bills.edit', function (BreadCrumbGenerator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push(trans('breadcrumbs.edit_bill', ['name' => e($bill->name)]), route('bills.edit', [$bill->id]));
}
);
Breadcrumbs::register(
'bills.delete', function (Generator $breadcrumbs, Bill $bill) {
'bills.delete', function (BreadCrumbGenerator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push(trans('breadcrumbs.delete_bill', ['name' => e($bill->name)]), route('bills.delete', [$bill->id]));
}
);
Breadcrumbs::register(
'bills.show', function (Generator $breadcrumbs, Bill $bill) {
'bills.show', function (BreadCrumbGenerator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.index');
$breadcrumbs->push(e($bill->name), route('bills.show', [$bill->id]));
@@ -331,43 +344,26 @@ Breadcrumbs::register(
// reports
Breadcrumbs::register(
'reports.index', function (Generator $breadcrumbs) {
'reports.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.reports'), route('reports.index'));
}
);
Breadcrumbs::register(
'reports.year', function (Generator $breadcrumbs, Carbon $date, $shared) {
'reports.report', function (BreadCrumbGenerator $breadcrumbs, Carbon $start, Carbon $end, $reportType, $accountIds) {
$breadcrumbs->parent('reports.index');
if ($shared) {
$title = trans('breadcrumbs.yearly_report_shared', ['date' => $date->year]);
} else {
$title = trans('breadcrumbs.yearly_report', ['date' => $date->year]);
}
$breadcrumbs->push($title, route('reports.year', [$date->year]));
}
);
Breadcrumbs::register(
'reports.month', function (Generator $breadcrumbs, Carbon $date, $shared) {
$breadcrumbs->parent('reports.year', $date, $shared);
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$monthFormat = trans('config.month_and_day');
$title = trans('firefly.report_default', ['start' => $start->formatLocalized($monthFormat), 'end' => $end->formatLocalized($monthFormat)]);
if ($shared) {
$title = trans('breadcrumbs.monthly_report_shared', ['date' => $date->formatLocalized($format)]);
} else {
$title = trans('breadcrumbs.monthly_report', ['date' => $date->formatLocalized($format)]);
}
$breadcrumbs->push($title, route('reports.month', [$date->year, $date->month]));
$breadcrumbs->push($title, route('reports.report', ['url' => 'abcde']));
}
);
// search
Breadcrumbs::register(
'search', function (Generator $breadcrumbs, $query) {
'search', function (BreadCrumbGenerator $breadcrumbs, $query) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search'));
}
@@ -375,35 +371,35 @@ Breadcrumbs::register(
// transactions
Breadcrumbs::register(
'transactions.index', function (Generator $breadcrumbs, $what) {
'transactions.index', function (BreadCrumbGenerator $breadcrumbs, $what) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', [$what]));
}
);
Breadcrumbs::register(
'transactions.create', function (Generator $breadcrumbs, $what) {
'transactions.create', function (BreadCrumbGenerator $breadcrumbs, $what) {
$breadcrumbs->parent('transactions.index', $what);
$breadcrumbs->push(trans('breadcrumbs.create_' . e($what)), route('transactions.create', [$what]));
}
);
Breadcrumbs::register(
'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $journal) {
'transactions.edit', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push(trans('breadcrumbs.edit_journal', ['description' => $journal->description]), route('transactions.edit', [$journal->id]));
}
);
Breadcrumbs::register(
'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $journal) {
'transactions.delete', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push(trans('breadcrumbs.delete_journal', ['description' => e($journal->description)]), route('transactions.delete', [$journal->id]));
}
);
Breadcrumbs::register(
'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) {
'transactions.show', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type));
$breadcrumbs->parent('transactions.index', strtolower($journal->getTransactionType()));
$breadcrumbs->push($journal->description, route('transactions.show', [$journal->id]));
}
@@ -411,28 +407,28 @@ Breadcrumbs::register(
// tags
Breadcrumbs::register(
'tags.index', function (Generator $breadcrumbs) {
'tags.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.tags'), route('tags.index'));
}
);
Breadcrumbs::register(
'tags.create', function (Generator $breadcrumbs) {
'tags.create', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('tags.index');
$breadcrumbs->push(trans('breadcrumbs.createTag'), route('tags.create'));
}
);
Breadcrumbs::register(
'tags.edit', function (Generator $breadcrumbs, Tag $tag) {
'tags.edit', function (BreadCrumbGenerator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag);
$breadcrumbs->push(trans('breadcrumbs.edit_tag', ['tag' => e($tag->tag)]), route('tags.edit', [$tag->id]));
}
);
Breadcrumbs::register(
'tags.delete', function (Generator $breadcrumbs, Tag $tag) {
'tags.delete', function (BreadCrumbGenerator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag);
$breadcrumbs->push(trans('breadcrumbs.delete_tag', ['tag' => e($tag->tag)]), route('tags.delete', [$tag->id]));
}
@@ -440,7 +436,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'tags.show', function (Generator $breadcrumbs, Tag $tag) {
'tags.show', function (BreadCrumbGenerator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.index');
$breadcrumbs->push(e($tag->tag), route('tags.show', [$tag->id]));
}

View File

@@ -1,4 +1,5 @@
<?php
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Bill;
@@ -29,6 +30,111 @@ Route::bind(
throw new NotFoundHttpException;
}
);
// accounts
Route::bind(
'accountList',
function ($value) {
if (Auth::check()) {
$ids = explode(',', $value);
/** @var \Illuminate\Support\Collection $object */
$object = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.editable', 1)
->whereIn('accounts.id', $ids)
->where('user_id', Auth::user()->id)
->get(['accounts.*']);
if ($object->count() > 0) {
return $object;
}
}
throw new NotFoundHttpException;
}
);
// budget list
Route::bind(
'budgetList',
function ($value) {
if (Auth::check()) {
$ids = explode(',', $value);
/** @var \Illuminate\Support\Collection $object */
$object = Budget::where('active', 1)
->whereIn('id', $ids)
->where('user_id', Auth::user()->id)
->get();
// add empty budget if applicable.
if (in_array('0', $ids)) {
$object->push(new Budget);
}
if ($object->count() > 0) {
return $object;
}
}
throw new NotFoundHttpException;
}
);
// category list
Route::bind(
'categoryList',
function ($value) {
if (Auth::check()) {
$ids = explode(',', $value);
/** @var \Illuminate\Support\Collection $object */
$object = Category::whereIn('id', $ids)
->where('user_id', Auth::user()->id)
->get();
// add empty budget if applicable.
if (in_array('0', $ids)) {
$object->push(new Category);
}
if ($object->count() > 0) {
return $object;
}
}
throw new NotFoundHttpException;
}
);
// Date
Route::bind(
'start_date',
function ($value) {
if (Auth::check()) {
try {
$date = new Carbon($value);
} catch (Exception $e) {
Log::error('Could not parse date "' . $value . '" for user #' . Auth::user()->id);
throw new NotFoundHttpException;
}
return $date;
}
throw new NotFoundHttpException;
}
);
// Date
Route::bind(
'end_date',
function ($value) {
if (Auth::check()) {
try {
$date = new Carbon($value);
} catch (Exception $e) {
Log::error('Could not parse date "' . $value . '" for user #' . Auth::user()->id);
throw new NotFoundHttpException;
}
return $date;
}
throw new NotFoundHttpException;
}
);
Route::bind(
'tj', function ($value) {
@@ -162,7 +268,7 @@ Route::get('/cron/sendgrid', ['uses' => 'CronController@sendgrid']);
Route::controllers(
[
'auth' => 'Auth\AuthController',
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]
);
@@ -178,7 +284,6 @@ Route::group(
Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']);
Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']);
Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']);
Route::get('/routes', ['uses' => 'HomeController@routes']);
/**
* Account Controller
*/
@@ -244,6 +349,7 @@ Route::group(
Route::get('/categories/edit/{category}', ['uses' => 'CategoryController@edit', 'as' => 'categories.edit']);
Route::get('/categories/delete/{category}', ['uses' => 'CategoryController@delete', 'as' => 'categories.delete']);
Route::get('/categories/show/{category}', ['uses' => 'CategoryController@show', 'as' => 'categories.show']);
Route::get('/categories/show/{category}/{date}', ['uses' => 'CategoryController@showWithDate', 'as' => 'categories.show.date']);
Route::get('/categories/list/noCategory', ['uses' => 'CategoryController@noCategory', 'as' => 'categories.noCategory']);
Route::post('/categories/store', ['uses' => 'CategoryController@store', 'as' => 'categories.store']);
Route::post('/categories/update/{category}', ['uses' => 'CategoryController@update', 'as' => 'categories.update']);
@@ -283,9 +389,7 @@ Route::group(
// accounts:
Route::get('/chart/account/frontpage', ['uses' => 'Chart\AccountController@frontpage']);
Route::get('/chart/account/expense', ['uses' => 'Chart\AccountController@expenseAccounts']);
Route::get('/chart/account/month/{year}/{month}/{shared?}', ['uses' => 'Chart\AccountController@all'])->where(
['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
);
Route::get('/chart/account/report/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\AccountController@report']);
Route::get('/chart/account/{account}', ['uses' => 'Chart\AccountController@single']);
@@ -295,29 +399,34 @@ Route::group(
// budgets:
Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']);
Route::get('/chart/budget/year/{year}/{shared?}', ['uses' => 'Chart\BudgetController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
// this chart is used in reports:
Route::get('/chart/budget/year/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@year']);
Route::get('/chart/budget/multi-year/{report_type}/{start_date}/{end_date}/{accountList}/{budgetList}', ['uses' => 'Chart\BudgetController@multiYear']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']);
Route::get('/chart/budget/{budget}', ['uses' => 'Chart\BudgetController@budget']);
// categories:
Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']);
Route::get('/chart/category/spent-in-year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@spentInYear'])->where(
['year' => '[0-9]{4}', 'shared' => 'shared']
// these three charts are for reports:
Route::get('/chart/category/earned-in-period/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@earnedInPeriod']);
Route::get('/chart/category/spent-in-period/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@spentInPeriod']);
Route::get(
'/chart/category/multi-year/{report_type}/{start_date}/{end_date}/{accountList}/{categoryList}', ['uses' => 'Chart\CategoryController@multiYear']
);
Route::get('/chart/category/earned-in-year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@earnedInYear'])->where(
['year' => '[0-9]{4}', 'shared' => 'shared']
);
Route::get('/chart/category/{category}/month', ['uses' => 'Chart\CategoryController@month']); // should be period.
Route::get('/chart/category/{category}/period', ['uses' => 'Chart\CategoryController@currentPeriod']);
Route::get('/chart/category/{category}/period/{date}', ['uses' => 'Chart\CategoryController@specificPeriod']);
Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']);
// piggy banks:
Route::get('/chart/piggyBank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']);
// reports:
Route::get('/chart/report/in-out/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOut'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/report/in-out-sum/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOutSummarized'])->where(
['year' => '[0-9]{4}', 'shared' => 'shared']
);
Route::get('/chart/report/in-out/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOut']);
Route::get('/chart/report/in-out-sum/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized']);
/**
@@ -383,12 +492,7 @@ Route::group(
* Report Controller
*/
Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
Route::get('/reports/{year}/{shared?}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/reports/{year}/{month}/{shared?}', ['uses' => 'ReportController@month', 'as' => 'reports.month'])->where(
['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
);
// pop ups for budget report:
Route::get('/reports/report/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@report', 'as' => 'reports.report']);
/**
* Search Controller

View File

@@ -39,8 +39,9 @@ use Illuminate\Database\Eloquent\Model;
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereSkip($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereNameEncrypted($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereMatchEncrypted($value)
* @property-read \Carbon\Carbon $nextExpectedMatch
* @property-read \Carbon\Carbon $lastFoundMatch
* @property \Carbon\Carbon $nextExpectedMatch
* @property \Carbon\Carbon $lastFoundMatch
* @property-read string $expectedAmount
*/
class Bill extends Model
{

View File

@@ -78,7 +78,7 @@ class Budget extends Model
*/
public function getDates()
{
return ['created_at', 'updated_at', 'deleted_at'];
return ['created_at', 'updated_at', 'deleted_at', 'startdate', 'enddate'];
}
/**

View File

@@ -25,7 +25,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereEncrypted($value)
* @property-read float $spent
* @property \Carbon\Carbon $lastActivity
* @property \Carbon\Carbon $lastActivity
*/
class Category extends Model
{

View File

@@ -13,7 +13,7 @@ use Illuminate\Database\Eloquent\Model;
* @property integer $transaction_journal_id
* @property \Carbon\Carbon $date
* @property float $amount
* @property-read \FireflyIII\Models\PiggyBank $piggyBank
* @property \FireflyIII\Models\PiggyBank $piggyBank
* @property-read \FireflyIII\Models\TransactionJournal $transactionJournal
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereCreatedAt($value)

View File

@@ -68,7 +68,9 @@ use Watson\Validating\ValidatingTrait;
* @property-read int $account_id
* @property string $name
* @property-read string $symbol
* @property-read string $type
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Attachment[] $attachments
* @property-read mixed $amount_positive
*/
class TransactionJournal extends Model
{
@@ -120,7 +122,7 @@ class TransactionJournal extends Model
/**
* @return string
*/
public function getActualAmountAttribute()
public function getAmountPositiveAttribute()
{
$amount = '0';
/** @var Transaction $t */
@@ -146,18 +148,10 @@ class TransactionJournal extends Model
}
bcscale(2);
$set = $this->transactions->sortByDesc('amount');
$amount = $set->first()->amount;
if (intval($this->tag_count) === 1) {
// get amount for single tag:
$amount = $this->amountByTag($this->tags()->first(), $amount);
}
if (intval($this->tag_count) > 1) {
// get amount for either tag.
$amount = $this->amountByTags($amount);
$transaction = $this->transactions->sortByDesc('amount')->first();
$amount = $transaction->amount;
if ($this->isWithdrawal()) {
$amount = $amount * -1;
}
$cache->store($amount);
@@ -173,15 +167,15 @@ class TransactionJournal extends Model
*/
protected function amountByTagAdvancePayment(Tag $tag, $amount)
{
if ($this->transactionType->type == 'Withdrawal') {
$others = $tag->transactionJournals()->transactionTypes(['Deposit'])->get();
if ($this->isWithdrawal()) {
$others = $tag->transactionJournals()->transactionTypes([TransactionType::DEPOSIT])->get();
foreach ($others as $other) {
$amount = bcsub($amount, $other->actual_amount);
$amount = bcsub($amount, $other->amount_positive);
}
return $amount;
}
if ($this->transactionType->type == 'Deposit') {
if ($this->isDeposit()) {
return '0';
}
@@ -196,10 +190,10 @@ class TransactionJournal extends Model
*/
protected function amountByTagBalancingAct($tag, $amount)
{
if ($this->transactionType->type == 'Withdrawal') {
$transfer = $tag->transactionJournals()->transactionTypes(['Transfer'])->first();
if ($this->isWithdrawal()) {
$transfer = $tag->transactionJournals()->transactionTypes([TransactionType::TRANSFER])->first();
if ($transfer) {
$amount = bcsub($amount, $transfer->actual_amount);
$amount = bcsub($amount, $transfer->amount_positive);
return $amount;
}
@@ -260,21 +254,6 @@ class TransactionJournal extends Model
return $amount;
}
/**
* @return string
*/
public function getCorrectAmountAttribute()
{
switch ($this->transactionType->type) {
case 'Deposit':
return $this->transactions()->where('amount', '>', 0)->first()->amount;
case 'Withdrawal':
return $this->transactions()->where('amount', '<', 0)->first()->amount;
}
return $this->transactions()->where('amount', '>', 0)->first()->amount;
}
/**
* @codeCoverageIgnore
@@ -512,4 +491,59 @@ class TransactionJournal extends Model
return $this->belongsTo('FireflyIII\User');
}
/**
* @return string
*/
public function getTransactionType()
{
return $this->transactionType->type;
}
/**
* @return bool
*/
public function isWithdrawal()
{
if (!is_null($this->type)) {
return $this->type == TransactionType::WITHDRAWAL;
}
return $this->transactionType->isWithdrawal();
}
/**
* @return bool
*/
public function isDeposit()
{
if (!is_null($this->type)) {
return $this->type == TransactionType::DEPOSIT;
}
return $this->transactionType->isDeposit();
}
/**
* @return bool
*/
public function isTransfer()
{
if (!is_null($this->type)) {
return $this->type == TransactionType::TRANSFER;
}
return $this->transactionType->isTransfer();
}
/**
* @return bool
*/
public function isOpeningBalance()
{
if (!is_null($this->type)) {
return $this->type == TransactionType::OPENING_BALANCE;
}
return $this->transactionType->isOpeningBalance();
}
}

View File

@@ -23,6 +23,11 @@ class TransactionType extends Model
{
use SoftDeletes;
const WITHDRAWAL = 'Withdrawal';
const DEPOSIT = 'Deposit';
const TRANSFER = 'Transfer';
const OPENING_BALANCE = 'Opening balance';
/**
* @return array
*/
@@ -31,6 +36,38 @@ class TransactionType extends Model
return ['created_at', 'updated_at', 'deleted_at'];
}
/**
* @return bool
*/
public function isDeposit()
{
return $this->type === TransactionType::DEPOSIT;
}
/**
* @return bool
*/
public function isOpeningBalance()
{
return $this->type === TransactionType::OPENING_BALANCE;
}
/**
* @return bool
*/
public function isTransfer()
{
return $this->type === TransactionType::TRANSFER;
}
/**
* @return bool
*/
public function isWithdrawal()
{
return $this->type === TransactionType::WITHDRAWAL;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/

View File

@@ -16,7 +16,6 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Query\Builder;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
@@ -41,7 +40,16 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function countAccounts(array $types)
{
return Auth::user()->accounts()->accountTypeIn($types)->count();
$cache = new CacheProperties;
$cache->addProperty('user-count-accounts');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$count = Auth::user()->accounts()->accountTypeIn($types)->count();
$cache->store($count);
return $count;
}
/**
@@ -69,6 +77,14 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function getAccounts(array $types)
{
$cache = new CacheProperties();
$cache->addProperty('get-accounts');
$cache->addProperty($types);
if ($cache->has()) {
return $cache->get();
}
/** @var Collection $result */
$result = Auth::user()->accounts()->with(
['accountmeta' => function (HasMany $query) {
@@ -82,25 +98,50 @@ class AccountRepository implements AccountRepositoryInterface
}
);
$cache->store($result);
return $result;
}
/**
* This method returns the users credit cards, along with some basic information about the
* balance they have on their CC. To be used in the JSON boxes on the front page that say
* how many bills there are still left to pay. The balance will be saved in field "balance".
*
* To get the balance, the field "date" is necessary.
*
* @param Carbon $date
*
* @return Collection
*/
public function getCreditCards()
public function getCreditCards(Carbon $date)
{
return Auth::user()->accounts()
$cache = new CacheProperties();
$cache->addProperty('user-credit-cards');
if ($cache->has()) {
return $cache->get();
}
$set = Auth::user()->accounts()
->hasMetaValue('accountRole', 'ccAsset')
->hasMetaValue('ccType', 'monthlyFull')
->leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereNull('transactions.deleted_at')
->where('transaction_journals.date', '<=', $date->format('Y-m-d'))
->groupBy('accounts.id')
->get(
[
'accounts.*',
'ccType.data as ccType',
'accountRole.data as accountRole'
'accountRole.data as accountRole',
DB::Raw('SUM(`transactions`.`amount`) AS `balance`')
]
);
$cache->store($set);
return $set;
}
/**
@@ -111,8 +152,18 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function getFirstTransaction(TransactionJournal $journal, Account $account)
{
$cache = new CacheProperties();
$cache->addProperty('first-transaction');
$cache->addProperty($journal->id);
$cache->addProperty($account->id);
return $journal->transactions()->where('account_id', $account->id)->first();
if ($cache->has()) {
return $cache->get();
}
$transaction = $journal->transactions()->where('account_id', $account->id)->first();
$cache->store($transaction);
return $transaction;
}
/**
@@ -123,8 +174,7 @@ class AccountRepository implements AccountRepositoryInterface
public function getFrontpageAccounts(Preference $preference)
{
$cache = new CacheProperties();
$cache->addProperty($preference->data);
$cache->addProperty('frontPageaccounts');
$cache->addProperty('user-frontpage-accounts');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
@@ -158,6 +208,7 @@ class AccountRepository implements AccountRepositoryInterface
$cache->addProperty($account->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('frontpage-transactions');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
@@ -172,6 +223,7 @@ class AccountRepository implements AccountRepositoryInterface
->before($end)
->after($start)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->take(10)
->get(['transaction_journals.*', 'transaction_currencies.symbol', 'transaction_types.type']);
@@ -227,14 +279,14 @@ class AccountRepository implements AccountRepositoryInterface
$cache = new CacheProperties;
$cache->addProperty($ids);
$cache->addProperty('piggyAccounts');
$cache->addProperty('user-piggy-bank-accounts');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$ids = array_unique($ids);
if (count($ids) > 0) {
$accounts = Auth::user()->accounts()->whereIn('id', $ids)->get();
$accounts = Auth::user()->accounts()->whereIn('id', $ids)->where('accounts.active', 1)->get();
}
bcscale(2);
@@ -273,6 +325,7 @@ class AccountRepository implements AccountRepositoryInterface
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', 'accountRole')
->where('accounts.active', 1)
->where('account_meta.data', '"savingAsset"')
->get(['accounts.*']);
$start = clone Session::get('start', new Carbon);
@@ -308,44 +361,6 @@ class AccountRepository implements AccountRepositoryInterface
return $accounts;
}
/**
* Get all transfers TO this account in this range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransfersInRange(Account $account, Carbon $start, Carbon $end)
{
$set = TransactionJournal::whereIn(
'id', function (Builder $q) use ($account, $start, $end) {
$q->select('transaction_journals.id')
->from('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transactions.account_id', $account->id)
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Transfer');
}
)->get();
$filtered = $set->filter(
function (TransactionJournal $journal) use ($account) {
if ($journal->destination_account->id == $account->id) {
return $journal;
}
return null;
}
);
return $filtered;
}
/**
* @param Account $account
* @param Carbon $date
@@ -372,11 +387,23 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function openingBalanceTransaction(Account $account)
{
return TransactionJournal
$cache = new CacheProperties;
$cache->addProperty($account->id);
$cache->addProperty('opening-balance-journal');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$journal = TransactionJournal
::orderBy('transaction_journals.date', 'ASC')
->accountIs($account)
->transactionTypes([TransactionType::OPENING_BALANCE])
->orderBy('created_at', 'ASC')
->first(['transaction_journals.*']);
$cache->store($journal);
return $journal;
}
/**
@@ -394,10 +421,9 @@ class AccountRepository implements AccountRepositoryInterface
// continue with the opposing account:
if ($data['openingBalance'] != 0) {
$type = $data['openingBalance'] < 0 ? 'expense' : 'revenue';
$opposingData = [
'user' => $data['user'],
'accountType' => $type,
'accountType' => 'initial',
'virtualBalance' => 0,
'name' => $data['name'] . ' initial balance',
'active' => false,
@@ -548,7 +574,7 @@ class AccountRepository implements AccountRepositoryInterface
*/
protected function storeInitialBalance(Account $account, Account $opposing, array $data)
{
$transactionType = TransactionType::whereType('Opening balance')->first();
$transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first();
$journal = TransactionJournal::create(
[
'user_id' => $data['user'],
@@ -642,4 +668,16 @@ class AccountRepository implements AccountRepositoryInterface
return $journal;
}
/**
* @deprecated
*
* @param $accountId
*
* @return Account
*/
public function find($accountId)
{
return Auth::user()->accounts()->findOrNew($accountId);
}
}

View File

@@ -24,6 +24,15 @@ interface AccountRepositoryInterface
*/
public function countAccounts(array $types);
/**
* @param $accountId
*
* @deprecated
*
* @return Account
*/
public function find($accountId);
/**
* @param Account $account
* @param Account $moveTo
@@ -48,9 +57,17 @@ interface AccountRepositoryInterface
public function getFirstTransaction(TransactionJournal $journal, Account $account);
/**
* This method returns the users credit cards, along with some basic information about the
* balance they have on their CC. To be used in the JSON boxes on the front page that say
* how many bills there are still left to pay. The balance will be saved in field "balance".
*
* To get the balance, the field "date" is necessary.
*
* @param Carbon $date
*
* @return Collection
*/
public function getCreditCards();
public function getCreditCards(Carbon $date);
/**
* Get the accounts of a user that have piggy banks connected to them.
@@ -59,18 +76,6 @@ interface AccountRepositoryInterface
*/
public function getPiggyBankAccounts();
/**
* Get all transfers TO this account in this range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransfersInRange(Account $account, Carbon $start, Carbon $end);
/**
* @param Preference $preference
*

View File

@@ -5,12 +5,18 @@ namespace FireflyIII\Repositories\Bill;
use Auth;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Log;
use Navigation;
use Steam;
/**
* Class BillRepository
@@ -19,52 +25,6 @@ use Steam;
*/
class BillRepository implements BillRepositoryInterface
{
/**
* Returns the sum of all payments connected to this bill between the dates.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return integer
*/
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end)
{
$amount = 0;
$journals = $bill->transactionjournals()->before($end)->after($start)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$amount += $journal->amount;
}
return $amount;
}
/**
* Create a fake bill to help the chart controller.
*
* @param string $description
* @param Carbon $date
* @param float $amount
*
* @return Bill
*/
public function createFakeBill($description, Carbon $date, $amount)
{
$bill = new Bill;
$bill->name = $description;
$bill->match = $description;
$bill->amount_min = $amount;
$bill->amount_max = $amount;
$bill->date = $date;
$bill->repeat_freq = 'monthly';
$bill->skip = 0;
$bill->automatch = false;
$bill->active = false;
return $bill;
}
/**
* @param Bill $bill
@@ -76,16 +36,6 @@ class BillRepository implements BillRepositoryInterface
return $bill->delete();
}
/**
* @return Collection
*/
public function getActiveBills()
{
/** @var Collection $set */
$set = Auth::user()->bills()->orderBy('name', 'ASC')->where('active', 1)->get()->sortBy('name');
return $set;
}
/**
* @return Collection
@@ -97,7 +47,10 @@ class BillRepository implements BillRepositoryInterface
$set = $set->sortBy(
function (Bill $bill) {
return strtolower($bill->name);
$int = $bill->active == 1 ? 0 : 1;
return $int . strtolower($bill->name);
}
);
@@ -105,22 +58,83 @@ class BillRepository implements BillRepositoryInterface
}
/**
* @param Collection $accounts
*
* @return Collection
*/
public function getBillsForAccounts(Collection $accounts)
{
/** @var Collection $set */
$set = Auth::user()->bills()->orderBy('name', 'ASC')->get();
$ids = [];
/** @var Account $account */
foreach ($accounts as $account) {
$ids[] = $account->id;
}
$set = $set->filter(
function (Bill $bill) use ($ids) {
// get transaction journals from or to any of the mentioned accounts.
// when zero, return null.
$journals = $bill->transactionjournals()->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereIn('transactions.account_id', $ids)->count();
return ($journals > 0);
}
);
$set = $set->sortBy(
function (Bill $bill) {
$int = $bill->active == 1 ? 0 : 1;
return $int . strtolower($bill->name);
}
);
return $set;
}
/**
* This method also returns the amount of the journal in "journalAmount"
* for easy access.
*
* @param Bill $bill
*
* @return Collection
*/
public function getJournals(Bill $bill)
{
return $bill->transactionjournals()->withRelevantData()
$cache = new CacheProperties;
$cache->addProperty($bill->id);
$cache->addProperty('journals-for-bill');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$set = $bill->transactionjournals()
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('amount', '<', 0);
}
)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*']);
->get(['transaction_journals.*', 'transactions.amount as journalAmount']);
$cache->store($set);
return $set;
}
/**
* Get all journals that were recorded on this bill between these dates.
*
* @deprecated
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
@@ -150,7 +164,9 @@ class BillRepository implements BillRepositoryInterface
}
$journals = new Collection;
if (count($ids) > 0) {
$journals = Auth::user()->transactionjournals()->whereIn('id', $ids)->get();
$journals = Auth::user()->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->whereIn('transaction_journals.id', $ids)->get(
['transaction_journals.*']
);
}
return $journals;
@@ -270,10 +286,20 @@ class BillRepository implements BillRepositoryInterface
*/
public function scan(Bill $bill, TransactionJournal $journal)
{
/*
* Can only support withdrawals.
*/
if (false === $journal->isWithdrawal()) {
return false;
}
$matches = explode(',', $bill->match);
$description = strtolower($journal->description) . ' ' . strtolower($journal->destination_account->name);
$wordMatch = $this->doWordMatch($matches, $description);
$amountMatch = $this->doAmountMatch($journal->amount, $bill->amount_min, $bill->amount_max);
$amountMatch = $this->doAmountMatch($journal->amount_positive, $bill->amount_min, $bill->amount_max);
Log::debug('Journal #' . $journal->id . ' has description "' . $description . '"');
/*
* If both, update!
@@ -283,6 +309,8 @@ class BillRepository implements BillRepositoryInterface
$journal->save();
return true;
} else {
Log::debug('Wordmatch: ' . (($wordMatch) ? 'true' : 'false') . ' AmountMatch: ' . (($amountMatch) ? 'true' : 'false'));
}
if ($bill->id == $journal->bill_id) {
// if no match, but bill used to match, remove it:
@@ -387,82 +415,166 @@ class BillRepository implements BillRepositoryInterface
}
/**
* Gets a collection of paid bills and a collection of unpaid bills to be used
* in the pie chart on the front page.
* Get the total amount of money paid for the users active bills in the date range given.
* This amount will be negative (they're expenses).
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
* @return string
*/
public function getBillsForChart(Carbon $start, Carbon $end)
public function getBillsPaidInRange(Carbon $start, Carbon $end)
{
$paid = new Collection;
$unpaid = new Collection;
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('bills-paid-in-range');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$amount = '0';
$bills = $this->getActiveBills();
$bills = $this->getActiveBills();
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $this->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$journals = $this->getJournalsInRange($bill, $range['start'], $range['end']);
if ($journals->count() == 0) {
$unpaid->push([$bill, $range['start']]);
} else {
$paid = $paid->merge($journals);
}
$paid = $bill->transactionjournals()
->before($range['end'])
->after($range['start'])
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->first([DB::Raw('SUM(`transactions`.`amount`) as `sum_amount`')]);
$amount = bcadd($amount, $paid->sum_amount);
}
}
$set = new Collection;
$set->put('paid', $paid);
$set->put('unpaid', $unpaid);
$cache->store($amount);
return $set;
return $amount;
}
/**
* Takes the paid/unpaid bills collection set up before and expands it using
* credit cards the user might have.
*
* @param Collection $set
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getCreditCardInfoForChart(Collection $set, Carbon $start, Carbon $end)
public function getActiveBills()
{
$accounts = app('FireflyIII\Repositories\Account\AccountRepositoryInterface');
$creditCards = $accounts->getCreditCards();
$paid = $set->get('paid');
$unpaid = $set->get('unpaid');
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 = $this->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);
}
}
$set = new Collection;
$set->put('paid', $paid);
$set->put('unpaid', $unpaid);
/** @var Collection $set */
$set = Auth::user()->bills()
->where('active', 1)
->get(
[
'bills.*',
DB::Raw('(`bills`.`amount_min` + `bills`.`amount_max` / 2) as `expectedAmount`')
]
)->sortBy('name');
return $set;
}
/**
* Get the total amount of money due for the users active bills in the date range given. This amount will be positive.
*
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function getBillsUnpaidInRange(Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('bills-unpaid-in-range');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$amount = '0';
$bills = $this->getActiveBills();
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $this->getRanges($bill, $start, $end);
$paidBill = '0';
foreach ($ranges as $range) {
$paid = $bill->transactionjournals()
->before($range['end'])
->after($range['start'])
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0);
}
)
->first([DB::Raw('SUM(`transactions`.`amount`) as `sum_amount`')]);
$paidBill = bcadd($paid->sum_amount, $paidBill);
}
if ($paidBill == 0) {
$amount = bcadd($amount, $bill->expectedAmount);
}
}
$cache->store($amount);
return $amount;
}
/**
* This method will tell you if you still have a CC bill to pay. Amount will be positive if the amount
* has been paid, otherwise it will be negative.
*
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function getCreditCardBill(Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('credit-card-bill');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app('FireflyIII\Repositories\Account\AccountRepositoryInterface');
$amount = '0';
$creditCards = $accountRepository->getCreditCards($end); // Find credit card accounts and possibly unpaid credit card bills.
/** @var Account $creditCard */
foreach ($creditCards as $creditCard) {
if ($creditCard->balance == 0) {
// find a transfer TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$set = TransactionJournal::whereIn(
'transaction_journals.id', function (Builder $q) use ($creditCard, $start, $end) {
$q->select('transaction_journals.id')
->from('transactions')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transactions.account_id', $creditCard->id)
->where('transactions.amount', '>', 0)// this makes the filter unnecessary.
->where('transaction_journals.user_id', Auth::user()->id)
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', TransactionType::TRANSFER);
}
)->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0);
}
)->first([DB::Raw('SUM(`transactions`.`amount`) as `sum_amount`')]);
$amount = bcadd($amount, $set->sum_amount);
} else {
$amount = bcadd($amount, $creditCard->balance);
}
}
return $amount;
}
}

View File

@@ -16,49 +16,41 @@ interface BillRepositoryInterface
{
/**
* Takes the paid/unpaid bills collection set up before and expands it using
* credit cards the user might have.
*
* @param Collection $set
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getCreditCardInfoForChart(Collection $set, Carbon $start, Carbon $end);
/**
* Gets a collection of paid bills and a collection of unpaid bills to be used
* in the pie chart on the front page.
* This method will tell you if you still have a CC bill to pay. Amount will be negative if the amount
* has been paid
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
* @return string
*/
public function getBillsForChart(Carbon $start, Carbon $end);
public function getCreditCardBill(Carbon $start, Carbon $end);
/**
* Returns the sum of all payments connected to this bill between the dates.
* Get the total amount of money paid for the users active bills in the date range given.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return float
* @return string
*/
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end);
public function getBillsPaidInRange(Carbon $start, Carbon $end);
/**
* Create a fake bill to help the chart controller.
* Get the total amount of money due for the users active bills in the date range given.
*
* @param string $description
* @param Carbon $date
* @param float $amount
* @param Carbon $start
* @param Carbon $end
*
* @return Bill
* @return string
*/
public function createFakeBill($description, Carbon $date, $amount);
public function getBillsUnpaidInRange(Carbon $start, Carbon $end);
/**
* @return Collection
*/
public function getActiveBills();
/**
* @param Bill $bill
@@ -70,12 +62,16 @@ interface BillRepositoryInterface
/**
* @return Collection
*/
public function getActiveBills();
public function getBills();
/**
* Gets the bills which have some kind of relevance to the accounts mentioned.
*
* @param Collection $accounts
*
* @return Collection
*/
public function getBills();
public function getBillsForAccounts(Collection $accounts);
/**
* @param Bill $bill
@@ -105,7 +101,7 @@ interface BillRepositoryInterface
/**
* Every bill repeats itself weekly, monthly or yearly (or whatever). This method takes a date-range (usually the view-range of Firefly itself)
* and returns date ranges that fall within the given range; those ranges are the bills expected. When a bill is due on the 14th of the month and
* you give 1st and the 31st of that month as argument, you'll get one response, matching the range of your bill.
* you give 1st and the 31st of that month as argument, you'll get one response, matching the range of your bill (from the 14th to the 31th).
*
* @param Bill $bill
* @param Carbon $start

View File

@@ -4,12 +4,16 @@ namespace FireflyIII\Repositories\Budget;
use Auth;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Shared\ComponentRepository;
use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Input;
@@ -50,12 +54,12 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
*
* @return float
*/
public function expensesOnDayCorrected(Budget $budget, Carbon $date)
public function expensesOnDay(Budget $budget, Carbon $date)
{
bcscale(2);
$sum = $budget->transactionjournals()->transactionTypes(['Withdrawal'])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
$sum = $budget->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
return bcmul($sum, -1);
return $sum;
}
/**
@@ -75,6 +79,50 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
return $set;
}
/**
* Returns a list of budgets, budget limits and limit repetitions
* (doubling any of them in a left join)
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getBudgetsAndLimitsInRange(Carbon $start, Carbon $end)
{
/** @var Collection $set */
$set = Auth::user()
->budgets()
->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id')
->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where(
function (Builder $query) use ($start, $end) {
$query->where(
function (Builder $query) use ($start, $end) {
$query->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d'));
$query->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d'));
}
);
$query->orWhere(
function (Builder $query) {
$query->whereNull('limit_repetitions.startdate');
$query->whereNull('limit_repetitions.enddate');
}
);
}
)
->get(['budgets.*', 'limit_repetitions.startdate', 'limit_repetitions.enddate', 'limit_repetitions.amount']);
$set = $set->sortBy(
function (Budget $budget) {
return strtolower($budget->name);
}
);
return $set;
}
/**
* @param Budget $budget
* @param Carbon $start
@@ -122,20 +170,26 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
/**
* @param Budget $budget
* @param Carbon $date
* @param Carbon $start
* @param Carbon $end
*
* @return LimitRepetition|null
*/
public function getCurrentRepetition(Budget $budget, Carbon $date)
public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty($budget->id);
$cache->addProperty($date);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('getCurrentRepetition');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$data = $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']);
$data = $budget->limitrepetitions()
->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
->where('limit_repetitions.enddate', $end->format('Y-m-d 00:00:00'))
->first(['limit_repetitions.*']);
$cache->store($data);
return $data;
@@ -220,6 +274,8 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
}
/**
* @deprecated
*
* @param Budget $budget
*
* @return Carbon
@@ -235,6 +291,8 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
}
/**
* @deprecated
*
* @param Budget $budget
* @param Carbon $date
*
@@ -282,40 +340,43 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
*/
public function getWithoutBudgetSum(Carbon $start, Carbon $end)
{
$noBudgetSet = Auth::user()
->transactionjournals()
->whereNotIn(
'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) {
$query
->select('transaction_journals.id')
->from('transaction_journals')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'))
->whereNotNull('budget_transaction_journal.budget_id');
}
)
->after($start)
->before($end)
->transactionTypes(['Withdrawal'])
->get(['transaction_journals.*'])->sum('amount');
$entry = Auth::user()
->transactionjournals()
->whereNotIn(
'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) {
$query
->select('transaction_journals.id')
->from('transaction_journals')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'))
->whereNotNull('budget_transaction_journal.budget_id');
}
)
->after($start)
->before($end)
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->transactionTypes([TransactionType::WITHDRAWAL])
->first([DB::Raw('SUM(`transactions`.`amount`) as `journalAmount`')]);
bcscale(2);
return bcmul($noBudgetSet, -1);
return $entry->journalAmount;
}
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param bool $shared
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true)
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, Collection $accounts)
{
return $this->commonBalanceInPeriod($budget, $start, $end, $shared);
return $this->commonBalanceInPeriod($budget, $start, $end, $accounts);
}
/**

View File

@@ -35,7 +35,7 @@ interface BudgetRepositoryInterface
*
* @return float
*/
public function expensesOnDayCorrected(Budget $budget, Carbon $date);
public function expensesOnDay(Budget $budget, Carbon $date);
/**
* @return Collection
@@ -63,13 +63,25 @@ interface BudgetRepositoryInterface
*/
public function getBudgets();
/**
* Returns a list of budgets, budget limits and limit repetitions
* (doubling any of them in a left join)
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getBudgetsAndLimitsInRange(Carbon $start, Carbon $end);
/**
* @param Budget $budget
* @param Carbon $date
* @param Carbon $start
* @param Carbon $end
*
* @return LimitRepetition|null
*/
public function getCurrentRepetition(Budget $budget, Carbon $date);
public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end);
/**
* @param Budget $budget
@@ -95,6 +107,8 @@ interface BudgetRepositoryInterface
public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50);
/**
* @deprecated
*
* @param Budget $budget
*
* @return Carbon
@@ -102,6 +116,8 @@ interface BudgetRepositoryInterface
public function getLastBudgetLimitDate(Budget $budget);
/**
* @deprecated
*
* @param Budget $budget
* @param Carbon $date
*
@@ -127,16 +143,16 @@ interface BudgetRepositoryInterface
/**
*
* Same as ::spentInPeriod but corrects journals for their amount (tags).
* Same as ::spentInPeriod but corrects journals for a set of accounts
*
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true);
public function balanceInPeriod(Budget $budget, Carbon $start, Carbon $end, Collection $accounts);
/**
* @param array $data

View File

@@ -5,9 +5,13 @@ namespace FireflyIII\Repositories\Category;
use Auth;
use Carbon\Carbon;
use Crypt;
use DB;
use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Shared\ComponentRepository;
use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
@@ -46,6 +50,13 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
*/
public function getCategories()
{
$cache = new CacheProperties;
$cache->addProperty('category-list');
if ($cache->has()) {
return $cache->get();
}
/** @var Collection $set */
$set = Auth::user()->categories()->orderBy('name', 'ASC')->get();
$set = $set->sortBy(
@@ -54,9 +65,76 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
}
);
$cache->store($set);
return $set;
}
/**
* Returns the amount earned without category by accounts in period.
*
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function earnedNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end)
{
$accountIds = [];
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
// is deposit AND account_from is in the list of $accounts
// not from any of the accounts in the list?
return Auth::user()
->transactionjournals()
->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('category_transaction_journal.id')
->before($end)
->after($start)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereIn('transactions.account_id', $accountIds)
->transactionTypes([TransactionType::DEPOSIT])
->get(['transaction_journals.*'])->sum('amount');
}
/**
* Returns the amount spent without category by accounts in period.
*
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function spentNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end)
{
$accountIds = [];
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
// is withdrawal or transfer AND account_from is in the list of $accounts
return Auth::user()
->transactionjournals()
->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('category_transaction_journal.id')
->before($end)
->after($start)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereIn('transactions.account_id', $accountIds)
->transactionTypes([TransactionType::WITHDRAWAL])
->get(['transaction_journals.*'])->sum('amount');
}
/**
*
@@ -65,7 +143,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
*
* @return array
*/
public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end)
public function getCategoriesAndExpenses(Carbon $start, Carbon $end)
{
$set = Auth::user()->transactionjournals()
->leftJoin(
@@ -75,18 +153,18 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
->before($end)
->where('categories.user_id', Auth::user()->id)
->after($start)
->transactionTypes(['Withdrawal'])
->transactionTypes([TransactionType::WITHDRAWAL])
->get(['categories.id as category_id', 'categories.encrypted as category_encrypted', 'categories.name', 'transaction_journals.*']);
bcscale(2);
$result = [];
foreach ($set as $entry) {
$categoryId = intval($entry->category_id);
if (isset($result[$categoryId])) {
bcscale(2);
$result[$categoryId]['sum'] = bcadd($result[$categoryId]['sum'], $entry->amount);
} else {
$isEncrypted = intval($entry->category_encrypted) == 1 ? true : false;
$name = strlen($entry->name) == 0 ? trans('firefly.no_category') : $entry->name;
$isEncrypted = intval($entry->category_encrypted) === 1 ? true : false;
$name = strlen($entry->name) === 0 ? trans('firefly.no_category') : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$result[$categoryId] = [
'name' => $name,
@@ -176,17 +254,16 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
}
/**
* @param Category $category
* @param Carbon $start
* @param Carbon $end
*
* @param bool $shared
* @param Category $category
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false)
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, Collection $accounts)
{
return $this->commonBalanceInPeriod($category, $start, $end, $shared);
return $this->commonBalanceInPeriod($category, $start, $end, $accounts);
}
/**
@@ -197,9 +274,9 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
*
* @return string
*/
public function spentOnDaySumCorrected(Category $category, Carbon $date)
public function spentOnDaySum(Category $category, Carbon $date)
{
return $category->transactionjournals()->onDate($date)->get(['transaction_journals.*'])->sum('correct_amount');
return $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
}
/**
@@ -236,6 +313,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
}
/**
* @deprecated
* This method returns the sum of the journals in the category, optionally
* limited by a start or end date.
*
@@ -259,7 +337,274 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
$query->before($end);
}
return $query->get(['transaction_journals.*'])->sum('correct_amount');
return $query->get(['transaction_journals.*'])->sum('amount');
}
/**
* @param Category $category
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
*
* @return string
*/
public function spentInPeriod(Category $category, Carbon $start, Carbon $end)
{
$cache = new CacheProperties; // we must cache this.
$cache->addProperty($category->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('spentInPeriod');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$sum = $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->before($end)->after($start)->get(['transaction_journals.*'])
->sum(
'amount'
);
$cache->store($sum);
return $sum;
}
/**
* @param Category $category
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
*
* @return string
*/
public function earnedInPeriod(Category $category, Carbon $start, Carbon $end)
{
$cache = new CacheProperties; // we must cache this.
$cache->addProperty($category->id);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('earnedInPeriod');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$sum = $category->transactionjournals()->transactionTypes([TransactionType::DEPOSIT])->before($end)->after($start)->get(['transaction_journals.*'])
->sum(
'amount'
);
$cache->store($sum);
return $sum;
}
/**
* @param Category $category
* @param int $page
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end)
{
$offset = $page > 0 ? $page * 50 : 0;
return $category->transactionJournals()
->after($start)
->before($end)
->withRelevantData()->take(50)->offset($offset)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->get(
['transaction_journals.*']
);
}
/**
* @param Category $category
*
* @param Carbon $start
* @param Carbon $end
*
* @return int
*/
public function countJournalsInRange(Category $category, Carbon $start, Carbon $end)
{
return $category->transactionJournals()->before($end)->after($start)->count();
}
/**
*
* Corrected for tags.
*
* @param Category $category
* @param Carbon $date
*
* @return float
*/
public function earnedOnDaySum(Category $category, Carbon $date)
{
return $category->transactionjournals()->transactionTypes([TransactionType::DEPOSIT])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
}
/**
* Calculates how much is spent in this period.
*
* @param Category $category
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function spentInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end)
{
$accountIds = [];
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$sum
= $category
->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->after($start)
->before($end)
->whereIn('transactions.account_id', $accountIds)
->transactionTypes([TransactionType::WITHDRAWAL])
->get(['transaction_journals.*'])
->sum('amount');
return $sum;
}
/**
* Calculate how much is earned in this period.
*
* @param Category $category
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function earnedInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end)
{
$accountIds = [];
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$sum
= $category
->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->before($end)
->whereIn('transactions.account_id', $accountIds)
->transactionTypes([TransactionType::DEPOSIT])
->after($start)
->get(['transaction_journals.*'])
->sum('amount');
return $sum;
}
/**
* Returns a collection of Categories appended with the amount of money that has been earned
* in these categories, based on the $accounts involved, in period X.
* The amount earned in category X in period X is saved in field "earned".
*
* @param $accounts
* @param $start
* @param $end
*
* @return Collection
*/
public function earnedForAccounts(Collection $accounts, Carbon $start, Carbon $end)
{
$accountIds = [];
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$collection = Auth::user()->categories()
->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin('transaction_journals', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions AS t_src', function (JoinClause $join) {
$join->on('t_src.transaction_journal_id', '=', 'transaction_journals.id')->where('t_src.amount', '<', 0);
}
)
->leftJoin(
'transactions AS t_dest', function (JoinClause $join) {
$join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0);
}
)
->whereIn('t_dest.account_id', $accountIds)// to these accounts (earned)
->whereNotIn('t_src.account_id', $accountIds)//-- but not from these accounts
->whereIn(
'transaction_types.type', [TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
)// earned from these things.
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->groupBy('categories.id')
->get(['categories.*', DB::Raw('SUM(`t_dest`.`amount`) AS `earned`')]);
return $collection;
}
/**
* Returns a collection of Categories appended with the amount of money that has been spent
* in these categories, based on the $accounts involved, in period X.
* The amount earned in category X in period X is saved in field "spent".
*
* @param $accounts
* @param $start
* @param $end
*
* @return Collection
*/
public function spentForAccounts(Collection $accounts, Carbon $start, Carbon $end)
{
$accountIds = [];
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$collection = Auth::user()->categories()
->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin('transaction_journals', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions AS t_src', function (JoinClause $join) {
$join->on('t_src.transaction_journal_id', '=', 'transaction_journals.id')->where('t_src.amount', '<', 0);
}
)
->leftJoin(
'transactions AS t_dest', function (JoinClause $join) {
$join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0);
}
)
->whereIn('t_src.account_id', $accountIds)// from these accounts (spent)
->whereNotIn('t_dest.account_id', $accountIds)//-- but not from these accounts (spent internally)
->whereIn(
'transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
)// spent on these things.
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->groupBy('categories.id')
->get(['categories.*', DB::Raw('SUM(`t_dest`.`amount`) AS `spent`')]);
return $collection;
}
}

View File

@@ -20,6 +20,16 @@ interface CategoryRepositoryInterface
*/
public function countJournals(Category $category);
/**
* @param Category $category
*
* @param Carbon $start
* @param Carbon $end
*
* @return int
*/
public function countJournalsInRange(Category $category, Carbon $start, Carbon $end);
/**
* @param Category $category
*
@@ -27,11 +37,84 @@ interface CategoryRepositoryInterface
*/
public function destroy(Category $category);
/**
* Returns a collection of Categories appended with the amount of money that has been earned
* in these categories, based on the $accounts involved, in period X.
* The amount earned in category X in period X is saved in field "earned".
*
* @param $accounts
* @param $start
* @param $end
*
* @return Collection
*/
public function earnedForAccounts(Collection $accounts, Carbon $start, Carbon $end);
/**
* Returns a collection of Categories appended with the amount of money that has been spent
* in these categories, based on the $accounts involved, in period X.
* The amount earned in category X in period X is saved in field "spent".
*
* @param $accounts
* @param $start
* @param $end
*
* @return Collection
*/
public function spentForAccounts(Collection $accounts, Carbon $start, Carbon $end);
/**
* @return Collection
*/
public function getCategories();
/**
* Calculates how much is spent in this period.
*
* @param Category $category
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function spentInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end);
/**
* Calculate how much is earned in this period.
*
* @param Category $category
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function earnedInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end);
/**
* Returns the amount spent without category by accounts in period.
*
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function spentNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end);
/**
* Returns the amount earned without category by accounts in period.
*
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
public function earnedNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end);
/**
* Corrected for tags.
*
@@ -40,7 +123,7 @@ interface CategoryRepositoryInterface
*
* @return array
*/
public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end);
public function getCategoriesAndExpenses(Carbon $start, Carbon $end);
/**
* @param Category $category
@@ -58,12 +141,24 @@ interface CategoryRepositoryInterface
public function getJournals(Category $category, $page);
/**
* @param Category $category
* @param int $page
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end);
/**
* @deprecated
* This method returns the sum of the journals in the category, optionally
* limited by a start or end date.
*
* @param Category $category
* @param Carbon $start
* @param Carbon $end
* @param Carbon $start
* @param Carbon $end
*
* @return string
*/
@@ -85,17 +180,34 @@ interface CategoryRepositoryInterface
public function getWithoutCategory(Carbon $start, Carbon $end);
/**
* Corrected for tags.
* Corrected for tags and list of accounts.
*
* @param Category $category
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
*
* @param bool $shared
* @param Collection $accounts
*
* @return string
*/
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false);
public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, Collection $accounts);
/**
* @param Category $category
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
*
* @return string
*/
public function spentInPeriod(Category $category, Carbon $start, Carbon $end);
/**
* @param Category $category
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
*
* @return string
*/
public function earnedInPeriod(Category $category, Carbon $start, Carbon $end);
/**
*
@@ -106,7 +218,18 @@ interface CategoryRepositoryInterface
*
* @return float
*/
public function spentOnDaySumCorrected(Category $category, Carbon $date);
public function spentOnDaySum(Category $category, Carbon $date);
/**
*
* Corrected for tags.
*
* @param Category $category
* @param Carbon $date
*
* @return float
*/
public function earnedOnDaySum(Category $category, Carbon $date);
/**
* @param array $data

View File

@@ -13,6 +13,7 @@ use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\CacheProperties;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
@@ -44,7 +45,16 @@ class JournalRepository implements JournalRepositoryInterface
*/
public function first()
{
return Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
$cache = new CacheProperties;
$cache->addProperty('user-first-journal');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$entry = Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
$cache->store($entry);
return $entry;
}
/**
@@ -164,7 +174,7 @@ class JournalRepository implements JournalRepositoryInterface
[
'user_id' => $data['user'],
'transaction_type_id' => $transactionType->id,
'transaction_currency_id' => $data['amount_currency_id'],
'transaction_currency_id' => $data['amount_currency_id_amount'],
'description' => $data['description'],
'completed' => 0,
'date' => $data['date'],
@@ -226,7 +236,7 @@ class JournalRepository implements JournalRepositoryInterface
public function update(TransactionJournal $journal, array $data)
{
// update actual journal.
$journal->transaction_currency_id = $data['amount_currency_id'];
$journal->transaction_currency_id = $data['amount_currency_id_amount'];
$journal->description = $data['description'];
$journal->date = $data['date'];
@@ -327,15 +337,15 @@ class JournalRepository implements JournalRepositoryInterface
$fromAccount = null;
$toAccount = null;
switch ($type->type) {
case 'Withdrawal':
case TransactionType::WITHDRAWAL:
list($fromAccount, $toAccount) = $this->storeWithdrawalAccounts($data);
break;
case 'Deposit':
case TransactionType::DEPOSIT:
list($fromAccount, $toAccount) = $this->storeDepositAccounts($data);
break;
case 'Transfer':
case TransactionType::TRANSFER:
$fromAccount = Account::find($data['account_from_id']);
$toAccount = Account::find($data['account_to_id']);
break;

View File

@@ -1,149 +0,0 @@
<?php
namespace FireflyIII\Repositories\PiggyBank;
use Carbon\Carbon;
use FireflyIII\Models\PiggyBankRepetition;
/**
* Class PiggyBankPart
*
* @codeCoverageIgnore
* @package FireflyIII\Collection
*/
class PiggyBankPart
{
/** @var float */
public $amountPerBar;
/** @var float */
public $cumulativeAmount;
/** @var float */
public $currentamount;
/** @var PiggyBankRepetition */
public $repetition;
/** @var Carbon */
public $startdate;
/** @var Carbon */
public $targetdate;
/**
* @return PiggyBankRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param PiggyBankRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
/**
* @return Carbon
*/
public function getStartdate()
{
return $this->startdate;
}
/**
* @param Carbon $startdate
*/
public function setStartdate($startdate)
{
$this->startdate = $startdate;
}
/**
* @return Carbon
*/
public function getTargetdate()
{
return $this->targetdate;
}
/**
* @param Carbon $targetdate
*/
public function setTargetdate($targetdate)
{
$this->targetdate = $targetdate;
}
/**
* @return float|int
*/
public function percentage()
{
bcscale(2);
if ($this->getCurrentamount() < $this->getCumulativeAmount()) {
$pct = 0;
// calculate halfway point?
if (bcsub($this->getCumulativeAmount(), $this->getCurrentamount()) < $this->getAmountPerBar()) {
$left = $this->getCurrentamount() % $this->getAmountPerBar();
$pct = round($left / $this->getAmountPerBar() * 100);
}
return $pct;
} else {
return 100;
}
}
/**
* @return float
*/
public function getCurrentamount()
{
return $this->currentamount;
}
/**
* @param float $currentamount
*/
public function setCurrentamount($currentamount)
{
$this->currentamount = $currentamount;
}
/**
* @return float
*/
public function getCumulativeAmount()
{
return $this->cumulativeAmount;
}
/**
* @param float $cumulativeAmount
*/
public function setCumulativeAmount($cumulativeAmount)
{
$this->cumulativeAmount = $cumulativeAmount;
}
/**
* @return float
*/
public function getAmountPerBar()
{
return $this->amountPerBar;
}
/**
* @param float $amountPerBar
*/
public function setAmountPerBar($amountPerBar)
{
$this->amountPerBar = $amountPerBar;
}
}

View File

@@ -3,8 +3,11 @@
namespace FireflyIII\Repositories\Shared;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
* Class ComponentRepository
@@ -14,49 +17,47 @@ use Illuminate\Database\Query\JoinClause;
class ComponentRepository
{
/**
* @param $object
* @param Carbon $start
* @param Carbon $end
*
* @param bool $shared
* @param $object
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
*/
protected function commonBalanceInPeriod($object, Carbon $start, Carbon $end, $shared = false)
protected function commonBalanceInPeriod($object, Carbon $start, Carbon $end, Collection $accounts)
{
$cache = new CacheProperties; // we must cache this.
$cache->addProperty($object->id);
$cache->addProperty(get_class($object));
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($shared);
$cache->addProperty('balanceInPeriod');
$cache->addProperty($accounts);
$cache->addProperty('balanceInPeriodList');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
if ($shared === true) { // shared is true: always ignore transfers between accounts!
$sum = $object->transactionjournals()->transactionTypes(['Withdrawal'])->before($end)->after($start)
->get(['transaction_journals.*'])->sum('amount');
} else {
// do something else, SEE budgets.
// get all journals in this month where the asset account is NOT shared.
$sum = $object->transactionjournals()->before($end)->after($start)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->transactionTypes(['Withdrawal', 'Deposit', 'Opening balance'])
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"')->get(['transaction_journals.*'])->sum('correct_amount');
$ids = [];
/** @var Account $account */
foreach ($accounts as $account) {
$ids[] = $account->id;
}
$cache->store($sum);
return $sum;
$entry = $object->transactionjournals()
->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE])
->before($end)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->whereIn('accounts.id', $ids)
->after($start)
->first([DB::Raw('SUM(`transactions`.`amount`) as `journalAmount`')]);
$amount = $entry->journalAmount;
$cache->store($amount);
return $amount;
}
}

View File

@@ -9,6 +9,7 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
@@ -54,6 +55,7 @@ class TagRepository implements TagRepositoryInterface
}
/**
* @deprecated
* This method scans the transaction journals from or to the given asset account
* and checks if these are part of a balancing act. If so, it will sum up the amounts
* transferred into the balancing act (if any) and return this amount.
@@ -77,7 +79,9 @@ class TagRepository implements TagRepositoryInterface
/** @var Tag $tag */
foreach ($tags as $tag) {
$journals = $tag->transactionjournals()->after($start)->before($end)->transactionTypes(['Transfer'])->get(['transaction_journals.*']);
$journals = $tag->transactionjournals()->after($start)->before($end)->transactionTypes([TransactionType::TRANSFER])->get(
['transaction_journals.*']
);
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
@@ -108,6 +112,13 @@ class TagRepository implements TagRepositoryInterface
*/
public function get()
{
$cache = new CacheProperties;
$cache->addProperty('tags-list');
if ($cache->has()) {
return $cache->get();
}
/** @var Collection $tags */
$tags = Auth::user()->tags()->get();
$tags = $tags->sortBy(
@@ -116,6 +127,8 @@ class TagRepository implements TagRepositoryInterface
}
);
$cache->store($tags);
return $tags;
}
@@ -158,7 +171,7 @@ class TagRepository implements TagRepositoryInterface
if ($tag->tagMode == 'balancingAct' || $tag->tagMode == 'nothing') {
foreach ($tag->transactionjournals as $journal) {
if ($journal->transactionType->type == 'Transfer') {
if ($journal->isTransfer()) {
return false;
}
}
@@ -169,7 +182,7 @@ class TagRepository implements TagRepositoryInterface
*/
$count = 0;
foreach ($tag->transactionjournals as $journal) {
if ($journal->transactionType->type == 'Withdrawal') {
if ($journal->isWithdrawal()) {
$count++;
}
}
@@ -201,7 +214,7 @@ class TagRepository implements TagRepositoryInterface
* If any transaction is a deposit, cannot become a balancing act.
*/
foreach ($tag->transactionjournals as $journal) {
if ($journal->transactionType->type == 'Deposit') {
if ($journal->isDeposit()) {
return false;
}
}
@@ -239,10 +252,10 @@ class TagRepository implements TagRepositoryInterface
protected function connectBalancingAct(TransactionJournal $journal, Tag $tag)
{
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
$transfer = TransactionType::whereType(TransactionType::TRANSFER)->first();
$transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count();
@@ -275,11 +288,11 @@ class TagRepository implements TagRepositoryInterface
protected function connectAdvancePayment(TransactionJournal $journal, Tag $tag)
{
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
$transfer = TransactionType::whereType(TransactionType::TRANSFER)->first();
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first();
/** @var TransactionType $deposit */
$deposit = TransactionType::whereType('Deposit')->first();
$deposit = TransactionType::whereType(TransactionType::DEPOSIT)->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
$deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count();
@@ -326,10 +339,10 @@ class TagRepository implements TagRepositoryInterface
foreach ($tag->transactionjournals as $check) {
// $checkAccount is the source_account for a withdrawal
// $checkAccount is the destination_account for a deposit
if ($check->transactionType->type == 'Withdrawal' && $check->source_account->id != $journal->destination_account->id) {
if ($check->isWithdrawal() && $check->source_account->id != $journal->destination_account->id) {
$match = false;
}
if ($check->transactionType->type == 'Deposit' && $check->destination_account->id != $journal->destination_account->id) {
if ($check->isDeposit() && $check->destination_account->id != $journal->destination_account->id) {
$match = false;
}

View File

@@ -27,6 +27,7 @@ interface TagRepositoryInterface
public function connect(TransactionJournal $journal, Tag $tag);
/**
* @deprecated
* This method scans the transaction journals from or to the given asset account
* and checks if these are part of a balancing act. If so, it will sum up the amounts
* transferred into the balancing act (if any) and return this amount.

View File

@@ -40,7 +40,7 @@ class Amount
if ($cache->has()) {
return $cache->get();
} else {
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
$currencyPreference = Prefs::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR'));
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
$cache->store($currency->symbol);
@@ -60,7 +60,7 @@ class Amount
{
$amount = floatval($amount);
$amount = round($amount, 2);
$string = number_format($amount, 2, ',', '.');
$string = money_format('%!.2n', $amount);
if ($coloured === true) {
if ($amount === 0.0) {
@@ -99,24 +99,21 @@ class Amount
} else {
$symbol = $journal->symbol;
}
$amount = $journal->amount;
if ($journal->transactionType->type == 'Withdrawal') {
$amount = $amount * -1;
}
if ($journal->transactionType->type == 'Transfer' && $coloured) {
$txt = '<span class="text-info">' . $this->formatWithSymbol($symbol, $amount, false) . '</span>';
if ($journal->isTransfer() && $coloured) {
$txt = '<span class="text-info">' . $this->formatWithSymbol($symbol, $journal->amount_positive, false) . '</span>';
$cache->store($txt);
return $txt;
}
if ($journal->transactionType->type == 'Transfer' && !$coloured) {
$txt = $this->formatWithSymbol($symbol, $amount, false);
if ($journal->isTransfer() && !$coloured) {
$txt = $this->formatWithSymbol($symbol, $journal->amount_positive, false);
$cache->store($txt);
return $txt;
}
$txt = $this->formatWithSymbol($symbol, $amount, $coloured);
$txt = $this->formatWithSymbol($symbol, $journal->amount, $coloured);
$cache->store($txt);
return $txt;
@@ -155,7 +152,7 @@ class Amount
if ($cache->has()) {
return $cache->get();
} else {
$currencyPreference = Prefs::get('currencyPreference', 'EUR');
$currencyPreference = Prefs::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR'));
$currency = TransactionCurrency::whereCode($currencyPreference->data)->first();
if ($currency) {
@@ -164,9 +161,9 @@ class Amount
return $currency->code;
}
$cache->store('EUR');
$cache->store(env('DEFAULT_CURRENCY', 'EUR'));
return 'EUR'; // @codeCoverageIgnore
return env('DEFAULT_CURRENCY', 'EUR'); // @codeCoverageIgnore
}
}

View File

@@ -106,16 +106,21 @@ class Navigation
*
* @return Carbon
*/
public function endOfX(Carbon $theCurrentEnd, $repeatFreq, Carbon $maxDate)
public function endOfX(Carbon $theCurrentEnd, $repeatFreq, Carbon $maxDate = null)
{
$functionMap = [
'1D' => 'endOfDay',
'daily' => 'endOfDay',
'1W' => 'endOfWeek',
'week' => 'endOfWeek',
'weekly' => 'endOfWeek',
'month' => 'endOfMonth',
'1M' => 'endOfMonth',
'monthly' => 'endOfMonth',
'3M' => 'lastOfQuarter',
'quarter' => 'lastOfQuarter',
'quarterly' => 'lastOfQuarter',
'1Y' => 'endOfYear',
'year' => 'endOfYear',
'yearly' => 'endOfYear',
];
@@ -128,7 +133,7 @@ class Navigation
}
if ($currentEnd > $maxDate) {
if (!is_null($maxDate) && $currentEnd > $maxDate) {
return clone $maxDate;
}
@@ -145,12 +150,17 @@ class Navigation
public function periodShow(Carbon $date, $repeatFrequency)
{
$formatMap = [
'1D' => '%e %B %Y',
'daily' => '%e %B %Y',
'1W' => 'Week %W, %Y',
'week' => 'Week %W, %Y',
'weekly' => 'Week %W, %Y',
'3M' => '%B %Y',
'quarter' => '%B %Y',
'1M' => '%B %Y',
'month' => '%B %Y',
'monthly' => '%B %Y',
'1Y' => '%Y',
'year' => '%Y',
'yearly' => '%Y',
@@ -218,12 +228,15 @@ class Navigation
public function subtractPeriod(Carbon $theDate, $repeatFreq, $subtract = 1)
{
$date = clone $theDate;
// 1D 1W 1M 3M 6M 1Y
$functionMap = [
'1D' => 'subDays',
'daily' => 'subDays',
'week' => 'subWeeks',
'1W' => 'subWeeks',
'weekly' => 'subWeeks',
'month' => 'subMonths',
'1M' => 'subMonths',
'monthly' => 'subMonths',
'year' => 'subYears',
'yearly' => 'subYears',

View File

@@ -72,6 +72,55 @@ class Steam
return round($balance, 2);
}
/**
* Gets the balance for the given account during the whole range, using this format:
*
* [yyyy-mm-dd] => 123,2
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function balanceInRange(Account $account, Carbon $start, Carbon $end)
{
// abuse chart properties:
$cache = new CacheProperties;
$cache->addProperty($account->id);
$cache->addProperty('balance-in-range');
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$balances = [];
$start->subDay();
$end->addDay();
$startBalance = $this->balance($account, $start);
$balances[$start->format('Y-m-d')] = $startBalance;
$start->addDay();
// query!
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->groupBy('transaction_journals.date')
->get(['transaction_journals.date', DB::Raw('SUM(`transactions`.`amount`) as `modified`')]);
$currentBalance = $startBalance;
foreach ($set as $entry) {
$currentBalance = bcadd($currentBalance, $entry->modified);
$balances[$entry->date] = $currentBalance;
}
$cache->store($balances);
return $balances;
}
/**
*
* @param array $ids

View File

@@ -23,10 +23,10 @@ class Budget extends Twig_Extension
{
$functions = [];
$functions[] = new Twig_SimpleFunction(
'spentInRepetitionCorrected', function (LimitRepetition $repetition) {
'spentInRepetition', function (LimitRepetition $repetition) {
$cache = new CacheProperties;
$cache->addProperty($repetition->id);
$cache->addProperty('spentInRepetitionCorrected');
$cache->addProperty('spentInRepetition');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}

View File

@@ -68,19 +68,17 @@ class Journal extends Twig_Extension
return $cache->get(); // @codeCoverageIgnore
}
$type = $journal->transactionType->type;
switch ($type) {
case 'Withdrawal':
switch (true) {
case $journal->isWithdrawal():
$txt = '<i class="fa fa-long-arrow-left fa-fw" title="' . trans('firefly.withdrawal') . '"></i>';
break;
case 'Deposit':
case $journal->isDeposit():
$txt = '<i class="fa fa-long-arrow-right fa-fw" title="' . trans('firefly.deposit') . '"></i>';
break;
case 'Transfer':
case $journal->isTransfer():
$txt = '<i class="fa fa-fw fa-exchange" title="' . trans('firefly.transfer') . '"></i>';
break;
case 'Opening balance':
case $journal->isOpeningBalance():
$txt = '<i class="fa-fw fa fa-ban" title="' . trans('firefly.openingBalance') . '"></i>';
break;
default:
@@ -181,7 +179,7 @@ class Journal extends Twig_Extension
if ($tag->tagMode == 'balancingAct') {
// return tag formatted for a "balancing act", even if other
// tags are present.
$amount = app('amount')->format($journal->actual_amount, false);
$amount = app('amount')->format($journal->amount_positive, false);
$string = '<a href="' . route('tags.show', [$tag->id]) . '" class="label label-success" title="' . $amount
. '"><i class="fa fa-fw fa-refresh"></i> ' . $tag->tag . '</a>';
@@ -189,7 +187,7 @@ class Journal extends Twig_Extension
}
if ($tag->tagMode == 'advancePayment') {
if ($journal->transactionType->type == 'Deposit') {
if ($journal->isDeposit()) {
$amount = app('amount')->formatJournal($journal, false);
$string = '<a href="' . route('tags.show', [$tag->id]) . '" class="label label-success" title="' . $amount
. '"><i class="fa fa-fw fa-sort-numeric-desc"></i> ' . $tag->tag . '</a>';
@@ -201,7 +199,7 @@ class Journal extends Twig_Extension
* AdvancePayment with a withdrawal will show the amount with a link to
* the tag. The TransactionJournal should properly calculate the amount.
*/
if ($journal->transactionType->type == 'Withdrawal') {
if ($journal->isWithdrawal()) {
$amount = app('amount')->formatJournal($journal);
$string = '<a href="' . route('tags.show', [$tag->id]) . '">' . $amount . '</a>';

View File

@@ -62,21 +62,11 @@ class FireflyValidator extends Validator
*/
public function validateIban($attribute, $value)
{
if (!is_string($value)) {
return false;
}
if (is_null($value)) {
return false;
}
if (strlen($value) === 0) {
if (!is_string($value) || is_null($value) || strlen($value) < 6) {
return false;
}
$value = strtoupper($value);
if (strlen($value) < 6) {
return false;
}
$search = [' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
$replace = ['', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31',
@@ -282,7 +272,8 @@ class FireflyValidator extends Validator
$exclude = isset($parameters[2]) ? intval($parameters[2]) : 0;
// get entries from table
$set = DB::table($table)->where('user_id', Auth::user()->id)->where('id', '!=', $exclude)->get([$field]);
$set = DB::table($table)->where('user_id', Auth::user()->id)
->where('id', '!=', $exclude)->get([$field]);
foreach ($set as $entry) {
$fieldValue = $this->tryDecrypt($entry->$field);
@@ -306,6 +297,7 @@ class FireflyValidator extends Validator
{
$exclude = isset($parameters[0]) ? $parameters[0] : null;
$query = DB::table('piggy_banks');
$query->whereNull('piggy_banks.deleted_at');
$query->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id');
$query->where('accounts.user_id', Auth::user()->id);
if (!is_null($exclude)) {

View File

@@ -22,16 +22,13 @@
"require": {
"laravel/framework": "5.1.*",
"davejamesmiller/laravel-breadcrumbs": "~3.0",
"grumpydictator/gchart": "~1",
"watson/validating": "~1.0",
"doctrine/dbal": "~2.5",
"illuminate/html": "~5.0",
"league/commonmark": "0.7.*",
"rcrowe/twigbridge": "0.7.x@dev",
"league/commonmark": "~0.7",
"rcrowe/twigbridge": "~0.9",
"zizaco/entrust": "dev-laravel-5",
"codeception/codeception": "*",
"league/csv": "^7.1",
"nesbot/carbon": "^1.20"
"league/csv": "^7.1"
},
"require-dev": {
"barryvdh/laravel-debugbar": "@stable",

2151
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,7 @@ return [
|
*/
'locale' => 'en',
'locale' => 'en_US',
/*
|--------------------------------------------------------------------------
@@ -65,7 +65,7 @@ return [
|
*/
'fallback_locale' => 'en',
'fallback_locale' => 'en_US',
/*
|--------------------------------------------------------------------------
@@ -139,8 +139,8 @@ return [
'TwigBridge\ServiceProvider',
'DaveJamesMiller\Breadcrumbs\ServiceProvider',
//'Barryvdh\Debugbar\ServiceProvider',
//'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
// 'Barryvdh\Debugbar\ServiceProvider',
// 'Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider',
'Zizaco\Entrust\EntrustServiceProvider',
/*

View File

@@ -49,7 +49,7 @@ return [
'sqlite' => [
'driver' => 'sqlite',
'database' => __DIR__ . '/../storage/database/testing.db',
'database' => storage_path('database/testing.db'),
'prefix' => '',
],

View File

@@ -1,20 +1,20 @@
<?php
return [
'chart' => 'chartjs',
'version' => '3.5.0',
'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'],
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
'csv_import_enabled' => true,
'maxUploadSize' => 5242880,
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],
'piggy_bank_periods' => [
'chart' => 'chartjs',
'version' => '3.5.6.1',
'index_periods' => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'],
'budget_periods' => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
'csv_import_enabled' => true,
'maxUploadSize' => 5242880,
'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'],
'piggy_bank_periods' => [
'week' => 'Week',
'month' => 'Month',
'quarter' => 'Quarter',
'year' => 'Year'
],
'periods_to_text' => [
'periods_to_text' => [
'weekly' => 'A week',
'monthly' => 'A month',
'quarterly' => 'A quarter',
@@ -22,7 +22,7 @@ return [
'yearly' => 'A year',
],
'accountRoles' => [
'accountRoles' => [
'defaultAsset' => 'Default asset account',
'sharedAsset' => 'Shared asset account',
'savingAsset' => 'Savings account',
@@ -85,7 +85,9 @@ return [
[
'asset' => 'Asset account',
'expense' => 'Expense account',
'revenue' => 'Revenue account'
'revenue' => 'Revenue account',
'opening' => 'Initial balance account',
'initial' => 'Initial balance account',
],
'shortNamesByFullName' =>
[
@@ -96,13 +98,23 @@ return [
'Revenue account' => 'revenue',
'Cash account' => 'cash',
],
'languages' => [
'en_US' => ['name_locale' => 'English', 'name_english' => 'English', 'complete' => true],
'nl_NL' => ['name_locale' => 'Nederlands', 'name_english' => 'Dutch', 'complete' => true],
'pt_BR' => ['name_locale' => 'Português do Brasil', 'name_english' => 'Portugese (Brazil)', 'complete' => false],
'fr_FR' => ['name_locale' => 'Français', 'name_english' => 'French', 'complete' => false],
],
'lang' => [
'en' => 'English',
'nl' => 'Nederlands'
'en_US' => 'English',
'nl_NL' => 'Nederlands',
'fr_FR' => 'Français',
'pt_BR' => 'Português do Brasil',
],
'locales' => [
'en' => ['en', 'English', 'en_US', 'en_US.utf8'],
'nl' => ['nl', 'Dutch', 'nl_NL', 'nl_NL.utf8'],
'en_US' => ['en', 'English', 'en_US', 'en_US.utf8'],
'nl_NL' => ['nl', 'Dutch', 'nl_NL', 'nl_NL.utf8'],
'pt_BR' => ['pt_BR', 'pt_BR.utf8'],
'fr_FR' => ['fr_FR', 'fr_FR.utf8'],
],
'transactionTypesByWhat' => [
'expenses' => ['Withdrawal'],
@@ -122,13 +134,17 @@ return [
],
'month' => [
'en' => '%B %Y',
'nl' => '%B %Y',
'month' => [
'en_US' => '%B %Y',
'nl_NL' => '%B %Y',
'fr_FR' => '%B %Y',
'pt_BR' => '%B %Y',
],
'monthAndDay' => [
'en' => '%B %e, %Y',
'nl' => '%e %B %Y',
'monthAndDay' => [
'en_US' => '%B %e, %Y',
'nl_NL' => '%e %B %Y',
'fr_FR' => '%B %e, %Y',
'pt_BR' => '%B %e, %Y',
],
];

View File

@@ -15,7 +15,8 @@ return [
|
*/
'driver' => env('EMAIL_DRIVER', 'smtp'),
'blocked_domains' => explode(',', env('BLOCKED_DOMAINS')),
'driver' => env('EMAIL_DRIVER', 'smtp'),
/*
|--------------------------------------------------------------------------
@@ -28,7 +29,7 @@ return [
|
*/
'host' => env('EMAIL_SMTP', 'smtp.mailgun.org'),
'host' => env('EMAIL_SMTP', 'smtp.mailgun.org'),
/*
|--------------------------------------------------------------------------
@@ -41,7 +42,7 @@ return [
|
*/
'port' => 587,
'port' => 587,
/*
|--------------------------------------------------------------------------
@@ -54,7 +55,7 @@ return [
|
*/
'from' => ['address' => env('EMAIL_USERNAME', null), 'name' => 'Firefly III Mailer'],
'from' => ['address' => env('EMAIL_USERNAME', null), 'name' => 'Firefly III Mailer'],
/*
|--------------------------------------------------------------------------
@@ -80,7 +81,7 @@ return [
|
*/
'username' => env('EMAIL_USERNAME', null),
'username' => env('EMAIL_USERNAME', null),
/*
|--------------------------------------------------------------------------
@@ -93,7 +94,7 @@ return [
|
*/
'password' => env('EMAIL_PASSWORD', null),
'password' => env('EMAIL_PASSWORD', null),
/*
|--------------------------------------------------------------------------
@@ -106,7 +107,7 @@ return [
|
*/
'sendmail' => '/usr/sbin/sendmail -bs',
'sendmail' => '/usr/sbin/sendmail -bs',
/*
|--------------------------------------------------------------------------
@@ -119,6 +120,6 @@ return [
|
*/
'pretend' => env('EMAIL_PRETEND', false),
'pretend' => env('EMAIL_PRETEND', false),
];

View File

@@ -49,7 +49,7 @@ class TestDataSeeder extends Seeder
$this->createPiggybanks();
// dates:
$start = Carbon::now()->subyear()->startOfMonth();
$start = Carbon::now()->subYears(5)->startOfMonth();
$end = Carbon::now()->endOfDay();

View File

@@ -12,10 +12,10 @@ class TransactionTypeSeeder extends Seeder
DB::table('transaction_types')->delete();
TransactionType::create(['type' => 'Withdrawal']);
TransactionType::create(['type' => 'Deposit']);
TransactionType::create(['type' => 'Transfer']);
TransactionType::create(['type' => 'Opening balance']);
TransactionType::create(['type' => TransactionType::WITHDRAWAL]);
TransactionType::create(['type' => TransactionType::DEPOSIT]);
TransactionType::create(['type' => TransactionType::TRANSFER]);
TransactionType::create(['type' => TransactionType::OPENING_BALANCE]);
}
}

View File

@@ -1,335 +0,0 @@
/*!
* Stylesheet for the Date Range Picker, for use with Bootstrap 3.x
*
* Copyright 2013-2015 Dan Grossman ( http://www.dangrossman.info )
* Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
*
* Built for http://www.improvely.com
*/
.daterangepicker.dropdown-menu {
max-width: none;
z-index: 3000;
}
.daterangepicker.opensleft .ranges, .daterangepicker.opensleft .calendar {
float: left;
margin: 4px;
}
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar,
.daterangepicker.openscenter .ranges, .daterangepicker.openscenter .calendar {
float: right;
margin: 4px;
}
.daterangepicker.single .ranges, .daterangepicker.single .calendar {
float: none;
}
.daterangepicker .ranges {
width: 160px;
text-align: left;
}
.daterangepicker .ranges .range_inputs>div {
float: left;
}
.daterangepicker .ranges .range_inputs>div:nth-child(2) {
padding-left: 11px;
}
.daterangepicker .calendar {
display: none;
max-width: 270px;
}
.daterangepicker.show-calendar .calendar {
display: block;
}
.daterangepicker .calendar.single .calendar-date {
border: none;
}
.daterangepicker .calendar th, .daterangepicker .calendar td {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
white-space: nowrap;
text-align: center;
min-width: 32px;
}
.daterangepicker .daterangepicker_start_input label,
.daterangepicker .daterangepicker_end_input label {
color: #333;
display: block;
font-size: 11px;
font-weight: normal;
height: 20px;
line-height: 20px;
margin-bottom: 2px;
text-shadow: #fff 1px 1px 0px;
text-transform: uppercase;
width: 74px;
}
.daterangepicker .ranges input {
font-size: 11px;
}
.daterangepicker .ranges .input-mini {
border: 1px solid #ccc;
border-radius: 4px;
color: #555;
display: block;
font-size: 11px;
height: 30px;
line-height: 30px;
vertical-align: middle;
margin: 0 0 10px 0;
padding: 0 6px;
width: 74px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0;
padding: 0;
}
.daterangepicker .ranges li {
font-size: 13px;
background: #f5f5f5;
border: 1px solid #f5f5f5;
color: #08c;
padding: 3px 12px;
margin-bottom: 8px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
cursor: pointer;
}
.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover {
background: #08c;
border: 1px solid #08c;
color: #fff;
}
.daterangepicker .calendar-date {
border: 1px solid #ddd;
padding: 4px;
border-radius: 4px;
background: #fff;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 8px auto 0 auto;
line-height: 30px;
}
.daterangepicker {
position: absolute;
background: #fff;
top: 100px;
left: 20px;
padding: 4px;
margin-top: 1px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.daterangepicker.opensleft:before {
position: absolute;
top: -7px;
right: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensleft:after {
position: absolute;
top: -6px;
right: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.openscenter:before {
position: absolute;
top: -7px;
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.openscenter:after {
position: absolute;
top: -6px;
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.opensright:before {
position: absolute;
top: -7px;
left: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensright:after {
position: absolute;
top: -6px;
left: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.dropup{
margin-top: -5px;
}
.daterangepicker.dropup:before{
top: initial;
bottom:-7px;
border-bottom: initial;
border-top: 7px solid #ccc;
}
.daterangepicker.dropup:after{
top: initial;
bottom:-6px;
border-bottom: initial;
border-top: 6px solid #fff;
}
.daterangepicker table {
width: 100%;
margin: 0;
}
.daterangepicker td, .daterangepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
cursor: pointer;
white-space: nowrap;
}
.daterangepicker td.off {
color: #999;
}
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background: #eee;
}
.daterangepicker td.in-range {
background: #ebf4f8;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.daterangepicker td.start-date {
-webkit-border-radius: 4px 0 0 4px;
-moz-border-radius: 4px 0 0 4px;
border-radius: 4px 0 0 4px;
}
.daterangepicker td.end-date {
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: #3071a9;
color: #fff;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
width: 50px;
margin-bottom: 0;
}
.daterangepicker_start_input {
float: left;
}
.daterangepicker_end_input {
float: left;
padding-left: 11px
}
.daterangepicker th.month {
width: auto;
}

341
public/css/daterangepicker.css Executable file
View File

@@ -0,0 +1,341 @@
/**
* A stylesheet for use with Bootstrap 3.x
* @author: Dan Grossman http://www.dangrossman.info/
* @copyright: Copyright (c) 2012-2015 Dan Grossman. All rights reserved.
* @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
* @website: https://www.improvely.com/
*/
/* Container Appearance */
.daterangepicker {
position: absolute;
background: #fff;
top: 100px;
left: 20px;
padding: 4px;
margin-top: 1px;
border-radius: 4px;
}
.daterangepicker.opensleft:before {
position: absolute;
top: -7px;
right: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensleft:after {
position: absolute;
top: -6px;
right: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.openscenter:before {
position: absolute;
top: -7px;
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.openscenter:after {
position: absolute;
top: -6px;
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.opensright:before {
position: absolute;
top: -7px;
left: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensright:after {
position: absolute;
top: -6px;
left: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.dropup{
margin-top: -5px;
}
.daterangepicker.dropup:before{
top: initial;
bottom:-7px;
border-bottom: initial;
border-top: 7px solid #ccc;
}
.daterangepicker.dropup:after{
top: initial;
bottom:-6px;
border-bottom: initial;
border-top: 6px solid #fff;
}
.daterangepicker.dropdown-menu {
max-width: none;
z-index: 3000;
}
.daterangepicker .ranges, .daterangepicker .calendar {
float: left;
}
.daterangepicker.single .ranges, .daterangepicker.single .calendar {
float: none;
}
.daterangepicker .ranges {
margin: 4px;
text-align: left;
}
.daterangepicker .calendar {
display: none;
max-width: 270px;
}
.daterangepicker.show-calendar .calendar {
display: block;
}
.daterangepicker .calendar.single .calendar-table {
border: none;
}
/* Calendars */
.daterangepicker .calendar th, .daterangepicker .calendar td {
white-space: nowrap;
text-align: center;
min-width: 32px;
}
.daterangepicker .calendar-table {
border: 1px solid #ddd;
padding: 4px;
border-radius: 4px;
background: #fff;
}
.daterangepicker .calendar.left .calendar-table {
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.daterangepicker .calendar.right .calendar-table {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.daterangepicker .calendar.left {
margin: 4px 0 4px 4px;
}
.daterangepicker .calendar.right {
margin: 4px 4px 4px 0;
}
.daterangepicker .calendar.left .calendar-table {
padding-right: 12px;
}
.daterangepicker table {
width: 100%;
margin: 0;
}
.daterangepicker td, .daterangepicker th {
text-align: center;
width: 20px;
height: 20px;
border-radius: 4px;
white-space: nowrap;
cursor: pointer;
}
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
color: #999;
background: #fff;
}
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background: #eee;
}
.daterangepicker td.in-range {
background: #ebf4f8;
border-radius: 0;
}
.daterangepicker td.start-date {
border-radius: 4px 0 0 4px;
}
.daterangepicker td.end-date {
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date {
border-radius: 4px;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: #3071a9;
color: #fff;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
width: 50px;
margin-bottom: 0;
}
.daterangepicker th.month {
width: auto;
}
/* Text Input Above Each Calendar */
.daterangepicker .input-mini {
border: 1px solid #ccc;
border-radius: 4px;
color: #555;
display: block;
height: 30px;
line-height: 30px;
vertical-align: middle;
margin: 0 0 5px 0;
padding: 0 6px 0 28px;
width: 100%;
}
.daterangepicker .input-mini.active {
border: 1px solid #357ebd;
}
.daterangepicker .daterangepicker_input i {
position: absolute;
left: 8px;
top: 8px;
}
.daterangepicker .left .daterangepicker_input {
padding-right: 12px;
}
.daterangepicker .daterangepicker_input {
position: relative;
}
/* Time Picker */
.daterangepicker .calendar-time {
text-align: center;
margin: 5px auto;
line-height: 30px;
position: relative;
padding-left: 28px;
}
.daterangepicker .calendar-time select.disabled {
color: #ccc;
cursor: not-allowed;
}
/* Predefined Ranges */
.daterangepicker .ranges {
font-size: 11px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0;
padding: 0;
width: 160px;
}
.daterangepicker .ranges li {
font-size: 13px;
background: #f5f5f5;
border: 1px solid #f5f5f5;
color: #08c;
padding: 3px 12px;
margin-bottom: 8px;
border-radius: 5px;
cursor: pointer;
}
.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover {
background: #08c;
border: 1px solid #08c;
color: #fff;
}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
.skin-blue-light .main-header .navbar{background-color:#3c8dbc}.skin-blue-light .main-header .navbar .nav>li>a{color:#fff}.skin-blue-light .main-header .navbar .nav>li>a:hover,.skin-blue-light .main-header .navbar .nav>li>a:active,.skin-blue-light .main-header .navbar .nav>li>a:focus,.skin-blue-light .main-header .navbar .nav .open>a,.skin-blue-light .main-header .navbar .nav .open>a:hover,.skin-blue-light .main-header .navbar .nav .open>a:focus{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue-light .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue-light .main-header .logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue-light .main-header .logo:hover{background-color:#3b8ab8}.skin-blue-light .main-header li.user-header{background-color:#3c8dbc}.skin-blue-light .content-header{background:transparent}.skin-blue-light .wrapper,.skin-blue-light .main-sidebar,.skin-blue-light .left-side{background-color:#f9fafc}.skin-blue-light .content-wrapper,.skin-blue-light .main-footer{border-left:1px solid #d2d6de}.skin-blue-light .user-panel>.info,.skin-blue-light .user-panel>.info>a{color:#444}.skin-blue-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-blue-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-blue-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-blue-light .sidebar-menu>li:hover>a,.skin-blue-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-blue-light .sidebar-menu>li.active{border-left-color:#3c8dbc}.skin-blue-light .sidebar-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-blue-light .sidebar a{color:#444}.skin-blue-light .sidebar a:hover{text-decoration:none}.skin-blue-light .treeview-menu>li>a{color:#777}.skin-blue-light .treeview-menu>li.active>a,.skin-blue-light .treeview-menu>li>a:hover{color:#000}.skin-blue-light .treeview-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-blue-light .sidebar-form input[type="text"],.skin-blue-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px !important;border-top-right-radius:0 !important;border-bottom-right-radius:0 !important;border-bottom-left-radius:2px !important}.skin-blue-light .sidebar-form input[type="text"]:focus,.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue-light .sidebar-form .btn{color:#999;border-top-left-radius:0 !important;border-top-right-radius:2px !important;border-bottom-right-radius:2px !important;border-bottom-left-radius:0 !important}@media (min-width:768px){.skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}.skin-blue-light .main-footer{border-top-color:#d2d6de}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}
.skin-blue-light .main-header .navbar{background-color:#3c8dbc}.skin-blue-light .main-header .navbar .nav>li>a{color:#fff}.skin-blue-light .main-header .navbar .nav>li>a:hover,.skin-blue-light .main-header .navbar .nav>li>a:active,.skin-blue-light .main-header .navbar .nav>li>a:focus,.skin-blue-light .main-header .navbar .nav .open>a,.skin-blue-light .main-header .navbar .nav .open>a:hover,.skin-blue-light .main-header .navbar .nav .open>a:focus,.skin-blue-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue-light .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue-light .main-header .logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue-light .main-header .logo:hover{background-color:#3b8ab8}.skin-blue-light .main-header li.user-header{background-color:#3c8dbc}.skin-blue-light .content-header{background:transparent}.skin-blue-light .wrapper,.skin-blue-light .main-sidebar,.skin-blue-light .left-side{background-color:#f9fafc}.skin-blue-light .content-wrapper,.skin-blue-light .main-footer{border-left:1px solid #d2d6de}.skin-blue-light .user-panel>.info,.skin-blue-light .user-panel>.info>a{color:#444}.skin-blue-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-blue-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-blue-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-blue-light .sidebar-menu>li:hover>a,.skin-blue-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-blue-light .sidebar-menu>li.active{border-left-color:#3c8dbc}.skin-blue-light .sidebar-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-blue-light .sidebar a{color:#444}.skin-blue-light .sidebar a:hover{text-decoration:none}.skin-blue-light .treeview-menu>li>a{color:#777}.skin-blue-light .treeview-menu>li.active>a,.skin-blue-light .treeview-menu>li>a:hover{color:#000}.skin-blue-light .treeview-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-blue-light .sidebar-form input[type="text"],.skin-blue-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px;-webkit-transition:all .3s ease-in-out;-o-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.skin-blue-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue-light .sidebar-form input[type="text"]:focus,.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}.skin-blue-light .main-footer{border-top-color:#d2d6de}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

4
public/js/accounting.min.js vendored Normal file
View File

@@ -0,0 +1,4 @@
/*!
* accounting.js v0.4.2, copyright 2014 Open Exchange Rates, MIT license, http://openexchangerates.github.io/accounting.js
*/
(function(p,z){function q(a){return!!(""===a||a&&a.charCodeAt&&a.substr)}function m(a){return u?u(a):"[object Array]"===v.call(a)}function r(a){return"[object Object]"===v.call(a)}function s(a,b){var d,a=a||{},b=b||{};for(d in b)b.hasOwnProperty(d)&&null==a[d]&&(a[d]=b[d]);return a}function j(a,b,d){var c=[],e,h;if(!a)return c;if(w&&a.map===w)return a.map(b,d);for(e=0,h=a.length;e<h;e++)c[e]=b.call(d,a[e],e,a);return c}function n(a,b){a=Math.round(Math.abs(a));return isNaN(a)?b:a}function x(a){var b=c.settings.currency.format;"function"===typeof a&&(a=a());return q(a)&&a.match("%v")?{pos:a,neg:a.replace("-","").replace("%v","-%v"),zero:a}:!a||!a.pos||!a.pos.match("%v")?!q(b)?b:c.settings.currency.format={pos:b,neg:b.replace("%v","-%v"),zero:b}:a}var c={version:"0.4.1",settings:{currency:{symbol:"$",format:"%s%v",decimal:".",thousand:",",precision:2,grouping:3},number:{precision:0,grouping:3,thousand:",",decimal:"."}}},w=Array.prototype.map,u=Array.isArray,v=Object.prototype.toString,o=c.unformat=c.parse=function(a,b){if(m(a))return j(a,function(a){return o(a,b)});a=a||0;if("number"===typeof a)return a;var b=b||".",c=RegExp("[^0-9-"+b+"]",["g"]),c=parseFloat((""+a).replace(/\((.*)\)/,"-$1").replace(c,"").replace(b,"."));return!isNaN(c)?c:0},y=c.toFixed=function(a,b){var b=n(b,c.settings.number.precision),d=Math.pow(10,b);return(Math.round(c.unformat(a)*d)/d).toFixed(b)},t=c.formatNumber=c.format=function(a,b,d,i){if(m(a))return j(a,function(a){return t(a,b,d,i)});var a=o(a),e=s(r(b)?b:{precision:b,thousand:d,decimal:i},c.settings.number),h=n(e.precision),f=0>a?"-":"",g=parseInt(y(Math.abs(a||0),h),10)+"",l=3<g.length?g.length%3:0;return f+(l?g.substr(0,l)+e.thousand:"")+g.substr(l).replace(/(\d{3})(?=\d)/g,"$1"+e.thousand)+(h?e.decimal+y(Math.abs(a),h).split(".")[1]:"")},A=c.formatMoney=function(a,b,d,i,e,h){if(m(a))return j(a,function(a){return A(a,b,d,i,e,h)});var a=o(a),f=s(r(b)?b:{symbol:b,precision:d,thousand:i,decimal:e,format:h},c.settings.currency),g=x(f.format);return(0<a?g.pos:0>a?g.neg:g.zero).replace("%s",f.symbol).replace("%v",t(Math.abs(a),n(f.precision),f.thousand,f.decimal))};c.formatColumn=function(a,b,d,i,e,h){if(!a)return[];var f=s(r(b)?b:{symbol:b,precision:d,thousand:i,decimal:e,format:h},c.settings.currency),g=x(f.format),l=g.pos.indexOf("%s")<g.pos.indexOf("%v")?!0:!1,k=0,a=j(a,function(a){if(m(a))return c.formatColumn(a,f);a=o(a);a=(0<a?g.pos:0>a?g.neg:g.zero).replace("%s",f.symbol).replace("%v",t(Math.abs(a),n(f.precision),f.thousand,f.decimal));if(a.length>k)k=a.length;return a});return j(a,function(a){return q(a)&&a.length<k?l?a.replace(f.symbol,f.symbol+Array(k-a.length+1).join(" ")):Array(k-a.length+1).join(" ")+a:a})};if("undefined"!==typeof exports){if("undefined"!==typeof module&&module.exports)exports=module.exports=c;exports.accounting=c}else"function"===typeof define&&define.amd?define([],function(){return c}):(c.noConflict=function(a){return function(){p.accounting=a;c.noConflict=z;return c}}(p.accounting),p.accounting=c)})(this);

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