Compare commits

..

210 Commits

Author SHA1 Message Date
James Cole
5a45b25614 Merge branch 'hotfix/chart-fix' 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
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
0fe682bfe6 Merge branch 'release/3.5.3' into develop 2015-11-13 07:01:29 +01:00
170 changed files with 6243 additions and 2518 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

@@ -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
@@ -42,10 +25,10 @@ class ChartJsAccountChartGenerator implements AccountChartGenerator
public function expenseAccounts(Collection $accounts, Carbon $start, Carbon $end)
{
$data = [
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.spent'),
'data' => []]]];
'count' => 1,
'labels' => [], 'datasets' => [[
'label' => trans('firefly.spent'),
'data' => []]]];
bcscale(2);
$start->subDay();
@@ -105,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)',
@@ -129,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, $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;
@@ -151,8 +140,7 @@ 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 = [
'count' => 1,

View File

@@ -71,8 +71,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,

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' => [],
]
],
],
];
@@ -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 period(Collection $entries);
public function multiYear(Collection $entries);
/**
* @param Collection $entries
*
* @return array
*/
public function period(Collection $entries);
/**
* @param Collection $categories
@@ -41,12 +55,4 @@ 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);
}

View File

@@ -2,9 +2,7 @@
namespace FireflyIII\Generator\Chart\Category;
use Config;
use Illuminate\Support\Collection;
use Preferences;
/**
@@ -101,8 +99,7 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = trans('config.month');
$data = [
'count' => 0,
@@ -131,12 +128,11 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
*
* @return array
*/
public function earnedInYear(Collection $categories, Collection $entries)
public function earnedInPeriod(Collection $categories, Collection $entries)
{
// language:
$language = Preferences::get('language', 'en')->data;
$format = Config::get('firefly.month.' . $language);
$format = trans('config.month');
$data = [
'count' => 0,
@@ -158,4 +154,74 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator
return $data;
}
/**
* @param Collection $categories
* @param Collection $entries
*
* @return array
*/
public function spentInPeriod(Collection $categories, Collection $entries)
{
// language:
$format = trans('config.month');
$data = [
'count' => 0,
'labels' => [],
'datasets' => [],
];
foreach ($categories as $category) {
$data['labels'][] = $category->name;
}
foreach ($entries as $entry) {
$date = $entry[0]->formatLocalized($format);
array_shift($entry);
$data['count']++;
$data['datasets'][] = ['label' => $date, 'data' => $entry];
}
return $data;
}
/**
* @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;
}
}

View File

@@ -25,8 +25,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

@@ -22,8 +22,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,
@@ -49,6 +48,39 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
return $data;
}
/**
* 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
@@ -74,9 +106,40 @@ class ChartJsReportChartGenerator implements ReportChartGenerator
],
];
$data['datasets'][0]['data'][] = round($income, 2);
$data['datasets'][1]['data'][] = round( $expense, 2);
$data['datasets'][1]['data'][] = round($expense, 2);
$data['datasets'][0]['data'][] = round(($income / $count), 2);
$data['datasets'][1]['data'][] = round(( $expense / $count), 2);
$data['datasets'][1]['data'][] = round(($expense / $count), 2);
return $data;
}
/**
* @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;
}

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

@@ -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,18 +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_positive, 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_positive);
$existing->amount = bcadd($existing->amount, $amount);
$existing->count++;
$this->expenses->put($accountId, $existing);
}
@@ -55,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);
}
@@ -65,7 +81,7 @@ class Expense
*/
public function getExpenses()
{
$set = $this->expenses->sortByDesc(
$set = $this->expenses->sortBy(
function (stdClass $object) {
return $object->amount;
}

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);
}
@@ -134,6 +300,7 @@ class ReportHelper implements ReportHelperInterface
// get budget amount for current period:
$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,168 +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);
$expenses = $expenses * -1;
$left = $expenses < $repetition->amount ? bcsub($repetition->amount, $expenses) : 0;
$spent = $expenses > $repetition->amount ? 0 : $expenses;
$overspent = $expenses > $repetition->amount ? bcsub($expenses, $repetition->amount) : 0;
$budgetLine->setLeft($left);
$budgetLine->setSpent($spent);
$budgetLine->setOverspent($overspent);
$budgetLine->setBudgeted($repetition->amount);
$object->addBudgeted($repetition->amount);
$object->addSpent($spent);
$object->addLeft($left);
$object->addOverspent($overspent);
$object->addBudgetLine($budgetLine);
}
}
// stuff outside of budgets:
$noBudget = $repository->getWithoutBudgetSum($start, $end);
$budgetLine = new BudgetLine;
$budgetLine->setOverspent($noBudget);
$object->addOverspent($noBudget);
$object->addBudgetLine($budgetLine);
return $object;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @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_positive);
$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_positive);
$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,178 +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"');
$q->where('acm_from.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);
}
}
);
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"');
$q->where('acm_to.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
*
@@ -202,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');
}
/**
@@ -233,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)
@@ -276,4 +100,144 @@ 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);
}
}
);
// $data = $data->filter(
// function (TransactionJournal $journal) {
// if ($journal->amount != 0) {
// return $journal;
// }
//
// return null;
// }
// );
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');
@@ -211,13 +213,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

@@ -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,12 +9,12 @@ use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\Request;
use Illuminate\Mail\Message;
use Log;
use Mail;
use Request as Rq;
use Session;
use Twig;
use Validator;
use Log;
/**
* Class AuthController
@@ -32,9 +33,8 @@ class AuthController extends Controller
public function getLogout()
{
Auth::logout();
Log::debug('Logout and redirect to root.');
return redirect('/login');
return redirect('/auth/login');
}
/**
@@ -88,8 +88,8 @@ 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;
if(strlen($code) == 0) {
$code = $foundUser->blocked_code;
if (strlen($code) == 0) {
$code = 'general_blocked';
}
$message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]);
@@ -160,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!');
@@ -197,6 +211,35 @@ 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;
}
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,38 @@ class PasswordController extends Controller
$this->middleware('guest');
}
/**
* Send a reset link to the given user.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function 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)]);
}
}
}

View File

@@ -6,6 +6,7 @@ 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;
@@ -135,7 +136,7 @@ class BudgetController extends Controller
*
* @return \Illuminate\View\View
*/
public function index(BudgetRepositoryInterface $repository)
public function index(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
{
$budgets = $repository->getActiveBudgets();
$inactive = $repository->getInactiveBudgets();
@@ -147,6 +148,8 @@ class BudgetController extends Controller
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
$budgetIncomeTotal = Preferences::get($key, 1000)->data;
$period = Navigation::periodShow($start, $range);
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
bcscale(2);
/**
* Do some cleanup:
@@ -156,7 +159,7 @@ class BudgetController extends Controller
// loop the budgets:
/** @var Budget $budget */
foreach ($budgets as $budget) {
$budget->spent = $repository->balanceInPeriod($budget, $start, $end);
$budget->spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
$budget->currentRep = $repository->getCurrentRepetition($budget, $start, $end);
if ($budget->currentRep) {
$budgeted = bcadd($budgeted, $budget->currentRep->amount);
@@ -170,7 +173,7 @@ class BudgetController extends Controller
$defaultCurrency = Amount::getDefaultCurrency();
return view(
'budgets.index', compact('budgetMaximum','period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
'budgets.index', compact('budgetMaximum', 'period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
);
}
@@ -279,7 +282,7 @@ class BudgetController extends Controller
{
$budgetData = [
'name' => $request->input('name'),
'active' => intval($request->input('active')) == 1
'active' => intval($request->input('active')) == 1,
];
$repository->update($budget, $budgetData);

View File

@@ -35,45 +35,32 @@ class AccountController extends Controller
/**
* Shows the balances for all the user's accounts.
* Shows the balances for a given set of dates and accounts.
*
* TODO fix parameters.
*
* @param AccountRepositoryInterface $repository
*
* @param $year
* @param $month
* @param bool $shared
* @param $url
*
* @return \Symfony\Component\HttpFoundation\Response
*/
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

@@ -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;
@@ -35,13 +36,86 @@ class BudgetController extends Controller
$this->generator = app('FireflyIII\Generator\Chart\Budget\BudgetChartGenerator');
}
/**
* @param BudgetRepositoryInterface $repository
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
* @param Collection $budgets
*/
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 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 +124,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 +144,7 @@ class BudgetController extends Controller
$end->subDay();
$chartDate = clone $end;
$chartDate->startOfMonth();
$spent = $repository->balanceInPeriod($budget, $first, $end) * -1;
$spent = $repository->balanceInPeriod($budget, $first, $end, $accounts) * -1;
$entries->push([$chartDate, $spent]);
$first = Navigation::addPeriod($first, $range, 0);
}
@@ -113,7 +189,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();
@@ -133,12 +209,13 @@ class BudgetController extends Controller
*
* @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;
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
// chart properties for cache:
$cache = new CacheProperties();
@@ -156,13 +233,13 @@ class BudgetController extends Controller
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->balanceInPeriod($budget, $start, $end, true) * -1;
$expenses = $repository->balanceInPeriod($budget, $start, $end, $accounts) * -1;
$allEntries->push([$budget->name, 0, 0, $expenses, 0, 0]);
continue;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, true) * -1;
$expenses = $repository->balanceInPeriod($budget, $repetition->startdate, $repetition->enddate, $accounts) * -1;
// $left can be less than zero.
// $overspent can be more than zero ( = overspending)
@@ -197,11 +274,8 @@ class BudgetController extends Controller
*
* @return \Symfony\Component\HttpFoundation\Response
*/
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 +283,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 +309,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

@@ -107,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,
@@ -121,6 +121,84 @@ class CategoryController extends Controller
);
$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
*/
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);
@@ -151,8 +229,8 @@ class CategoryController extends Controller
while ($start <= $end) {
$spent = $repository->spentOnDaySumCorrected($category, $start);
$earned = $repository->earnedOnDaySumCorrected($category, $start);
$spent = $repository->spentOnDaySum($category, $start);
$earned = $repository->earnedOnDaySum($category, $start);
$date = Navigation::periodShow($start, '1D');
$entries->push([clone $start, $date, $spent, $earned]);
$start->addDay();
@@ -194,8 +272,8 @@ class CategoryController extends Controller
while ($start <= $end) {
$spent = $repository->spentOnDaySumCorrected($category, $start);
$earned = $repository->earnedOnDaySumCorrected($category, $start);
$spent = $repository->spentOnDaySum($category, $start);
$earned = $repository->earnedOnDaySum($category, $start);
$theDate = Navigation::periodShow($start, '1D');
$entries->push([clone $start, $theDate, $spent, $earned]);
$start->addDay();
@@ -213,31 +291,32 @@ class CategoryController extends Controller
* This chart will only show expenses.
*
* @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 spentInYear(CategoryRepositoryInterface $repository, $year, $shared = false)
public function spentInYear(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:
$cache->addProperty($start);
$cache->addProperty($report_type);
$cache->addProperty($end);
$cache->addProperty($accounts);
$cache->addProperty('category');
$cache->addProperty('spent-in-year');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$shared = $shared == 'shared' ? true : false;
$allCategories = $repository->getCategories();
$entries = new Collection;
$categories = $allCategories->filter(
function (Category $category) use ($repository, $start, $end, $shared) {
$spent = $repository->balanceInPeriod($category, $start, $end, $shared);
function (Category $category) use ($repository, $start, $end, $accounts) {
$spent = $repository->balanceInPeriod($category, $start, $end, $accounts);
if ($spent < 0) {
return $category;
}
@@ -252,7 +331,7 @@ class CategoryController extends Controller
$row = [clone $start]; // make a row:
foreach ($categories as $category) { // each budget, fill the row
$spent = $repository->balanceInPeriod($category, $start, $month, $shared);
$spent = $repository->balanceInPeriod($category, $start, $month, $accounts);
if ($spent < 0) {
$row[] = $spent * -1;
} else {
@@ -269,61 +348,194 @@ class CategoryController extends Controller
}
/**
* This chart will only show income.
* Returns a chart of what has been earned 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 earnedInPeriod(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('earned-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();
// 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;
// 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;
}
}
$allEntries->push($row);
$entries->push($row);
$start->addMonth();
}
$data = $this->generator->earnedInYear($categories, $allEntries);
$data = $this->generator->earnedInPeriod($categories, $entries);
$cache->store($data);
return Response::json($data);
return $data;
}
/**
* Returns a chart of what has been spent in this period in each category
* grouped by month.
*
* @param CategoryRepositoryInterface $repository
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return \Illuminate\Http\JsonResponse
*/
public function spentInPeriod(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$original = clone $start;
$cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($report_type);
$cache->addProperty($accounts);
$cache->addProperty('category');
$cache->addProperty('spent-in-period');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$categories = new Collection;
$sets = new Collection;
$entries = new Collection;
// run a very special query each month:
$start = clone $original;
while ($start < $end) {
$currentEnd = clone $start;
$currentStart = clone $start;
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
$set = $repository->spentForAccounts($accounts, $currentStart, $currentEnd);
$categories = $categories->merge($set);
$sets->push([$currentStart, $set]);
$start->addMonth();
}
$categories = $categories->unique('id');
$categories = $categories->sortBy(
function (Category $category) {
return $category->name;
}
);
$start = clone $original;
while ($start < $end) {
$currentEnd = clone $start;
$currentStart = clone $start;
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
$currentSet = $sets->first(
function ($key, $value) use ($currentStart) {
// set for this date.
return ($value[0] == $currentStart);
}
);
$row = [clone $currentStart];
/** @var Category $category */
foreach ($categories as $category) {
/** @var Category $entry */
$entry = $currentSet[1]->first(
function ($key, $value) use ($category) {
return $value->id == $category->id;
}
);
if (!is_null($entry)) {
$row[] = $entry->spent;
} else {
$row[] = 0;
}
}
$entries->push($row);
$start->addMonth();
}
$data = $this->generator->spentInPeriod($categories, $entries);
$cache->store($data);
return $data;
}
}

View File

@@ -9,7 +9,6 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Response;
use Log;
/**
* Class ReportController
@@ -37,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_positive');
$expenseSum = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
$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);
@@ -81,54 +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:
$currentIncome = $query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
$currentExpense = $query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount_positive');
Log::debug('Date ['.$month->format('M Y').']: income = ['.$income.' + '.$currentIncome.'], out = ['.$expense.' + '.$currentExpense.']');
$income = bcadd($income, $currentIncome);
$expense = bcadd($expense, $currentExpense);
$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

@@ -34,14 +34,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

@@ -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

@@ -120,33 +120,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

@@ -15,7 +15,6 @@ use Illuminate\Support\Collection;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class JsonController
@@ -72,9 +71,8 @@ class JsonController extends Controller
*/
public function boxBillsPaid(BillRepositoryInterface $repository, AccountRepositoryInterface $accountRepository)
{
$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!
@@ -85,21 +83,14 @@ class JsonController extends Controller
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$bills = $repository->getActiveBills(); // these two functions are the same as the chart
// get amount from bills
$amount = $repository->billsPaidInRange($start, $end)->sum('paid');
/** @var Bill $bill */
foreach ($bills as $bill) {
$amount = bcadd($amount, $repository->billPaymentsInRange($bill, $start, $end));
}
unset($bill, $bills);
$amount = $amount * -1; // make the amount positive again.
$creditCards = $accountRepository->getCreditCards(); // Find credit card accounts and possibly unpaid credit card bills.
// add credit card bill.
$creditCards = $accountRepository->getCreditCards($end); // 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) {
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.
$amount = bcadd($amount, $accountRepository->getTransfersInRange($creditCard, $start, $end)->sum('amount'));
@@ -149,14 +140,15 @@ class JsonController extends Controller
}
unset($bill, $bills, $range, $ranges);
$creditCards = $accountRepository->getCreditCards();
$creditCards = $accountRepository->getCreditCards($end);
/** @var Account $creditCard */
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, $end, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($creditCard->balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$fakeAmount = $balance * -1;
$fakeAmount = $creditCard->balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $fakeAmount);
$unpaid->push([$fakeBill, $date]);
}
@@ -178,7 +170,7 @@ class JsonController extends Controller
*
* @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());
@@ -191,8 +183,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);
@@ -205,11 +197,12 @@ class JsonController extends Controller
*
* @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;
@@ -220,8 +213,7 @@ class JsonController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore
}
$amount = $reportQuery->expenseInPeriodCorrected($start, $end, true)->sum('amount');
$amount = $amount * -1;
$amount = $reportQuery->expenseInPeriod($start, $end, $accounts)->sum('to_amount');
$data = ['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
$cache->store($data);
@@ -243,7 +235,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

@@ -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,190 @@ 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;
}
$accountList = join(',', $accountIds);
return view('reports.index', compact('months', 'hasShared'));
return view('reports.index', compact('months', 'accounts', 'start', 'accountList',
'startOfMonth', 'endOfMonth', 'startOfYear', 'endOfYear'
));
}
/**
* @param string $year
* @param string $month
* @param $report_type
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @param bool $shared
*
* @return \Illuminate\View\View
* @return View
*/
public function month($year = '2014', $month = '1', $shared = false)
public function defaultYear($report_type, Carbon $start, Carbon $end, Collection $accounts)
{
$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'));
return view(
'reports.month',
compact(
'start', 'shared',
'subTitle', 'subTitleIcon',
'accounts',
'incomes', 'incomeTopLength',
'expenses', 'expenseTopLength',
'budgets', 'balance',
'categories',
'bills'
)
);
}
/**
* @param $year
*
* @param bool $shared
*
* @return $this
*/
public function year($year, $shared = false)
{
$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'
)
);
}
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 view('error')->with('message', 'No report yet for this time period.');
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);
break;
}
}
}

View File

@@ -12,6 +12,7 @@ use FireflyIII\Http\Requests\JournalFormRequest;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Input;
@@ -45,8 +46,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 +97,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
@@ -137,7 +139,7 @@ class TransactionController extends Controller
public function edit(AccountRepositoryInterface $repository, TransactionJournal $journal)
{
// cannot edit opening balance
if ($journal->transactionType->type == 'Opening balance') {
if ($journal->isOpeningBalance()) {
return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!');
}
@@ -145,7 +147,7 @@ class TransactionController extends Controller
$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');
@@ -181,7 +183,7 @@ class TransactionController extends Controller
$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 {
@@ -269,8 +271,8 @@ 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'));
}
@@ -287,7 +289,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;
}
@@ -308,7 +310,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'))));
}
@@ -340,7 +342,7 @@ class TransactionController extends Controller
{
// cannot edit opening balance
if ($journal->transactionType->type == 'Opening balance') {
if ($journal->isOpeningBalance()) {
return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!');
}

View File

@@ -1,13 +1,14 @@
<?php namespace FireflyIII\Http\Middleware;
use App;
use Auth;
use Carbon\Carbon;
use Closure;
use Config;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Preferences;
use Auth;
/**
* Class Authenticate
*
@@ -55,15 +56,19 @@ class Authenticate
if (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

@@ -351,30 +351,13 @@ Breadcrumbs::register(
);
Breadcrumbs::register(
'reports.year', function (Generator $breadcrumbs, Carbon $date, $shared) {
'reports.report', function (Generator $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']));
}
);
@@ -416,7 +399,7 @@ Breadcrumbs::register(
Breadcrumbs::register(
'transactions.show', function (Generator $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]));
}

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;
}
);
// account list! Yay!
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
*/
@@ -284,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']);
@@ -296,18 +399,24 @@ 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']
);
Route::get('/chart/category/earned-in-year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@earnedInYear'])->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/{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']);
@@ -316,10 +425,8 @@ Route::group(
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']);
/**
@@ -385,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,8 @@ 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 \Carbon\Carbon $nextExpectedMatch
* @property \Carbon\Carbon $lastFoundMatch
* @property \Carbon\Carbon $nextExpectedMatch
* @property \Carbon\Carbon $lastFoundMatch
*/
class Bill extends Model
{

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

@@ -68,6 +68,7 @@ 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
*/
@@ -147,10 +148,9 @@ class TransactionJournal extends Model
}
bcscale(2);
$type = $this->transactionType->type;
$transaction = $this->transactions->sortByDesc('amount')->first();
$amount = $transaction->amount;
if ($type == 'Withdrawal') {
if ($this->isWithdrawal()) {
$amount = $amount * -1;
}
$cache->store($amount);
@@ -167,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->amount_positive);
}
return $amount;
}
if ($this->transactionType->type == 'Deposit') {
if ($this->isDeposit()) {
return '0';
}
@@ -190,8 +190,8 @@ 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->amount_positive);
@@ -491,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

@@ -41,7 +41,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 +78,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,23 +99,39 @@ 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()
->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`')
]
);
}
@@ -111,8 +144,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 +166,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 +200,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 +215,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 +271,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 +317,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);
@@ -319,31 +364,23 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function getTransfersInRange(Account $account, Carbon $start, Carbon $end)
{
$set = TransactionJournal::whereIn(
$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('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', 'Transfer');
->where('transaction_types.type', TransactionType::TRANSFER);
}
)->get();
$filtered = $set->filter(
function (TransactionJournal $journal) use ($account) {
if ($journal->destination_account->id == $account->id) {
return $journal;
}
return null;
}
);
return $filtered;
return $set;
}
/**
@@ -372,12 +409,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(['Opening balance'])
->transactionTypes([TransactionType::OPENING_BALANCE])
->orderBy('created_at', 'ASC')
->first(['transaction_journals.*']);
$cache->store($journal);
return $journal;
}
/**
@@ -548,7 +596,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 +690,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.

View File

@@ -5,9 +5,11 @@ 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 Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
use Log;
use Navigation;
@@ -21,6 +23,7 @@ use Steam;
class BillRepository implements BillRepositoryInterface
{
/**
* @deprecated
* Returns the sum of all payments connected to this bill between the dates.
*
* @param Bill $bill
@@ -83,7 +86,7 @@ class BillRepository implements BillRepositoryInterface
public function getActiveBills()
{
/** @var Collection $set */
$set = Auth::user()->bills()->orderBy('name', 'ASC')->where('active', 1)->get()->sortBy('name');
$set = Auth::user()->bills()->where('active', 1)->get()->sortBy('name');
return $set;
}
@@ -108,6 +111,46 @@ class BillRepository implements BillRepositoryInterface
return $set;
}
/**
* @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.
// if 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;
}
/**
* @param Bill $bill
*
@@ -154,7 +197,7 @@ class BillRepository implements BillRepositoryInterface
}
$journals = new Collection;
if (count($ids) > 0) {
$journals = Auth::user()->transactionjournals()->transactionTypes(['Withdrawal'])->whereIn('transaction_journals.id', $ids)->get(
$journals = Auth::user()->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->whereIn('transaction_journals.id', $ids)->get(
['transaction_journals.*']
);
}
@@ -276,12 +319,21 @@ 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_positive, $bill->amount_min, $bill->amount_max);
Log::debug('Journal #' . $journal->id . ' has description "' . $description . '"');
/*
* If both, update!
*/
@@ -446,22 +498,21 @@ class BillRepository implements BillRepositoryInterface
{
$accounts = app('FireflyIII\Repositories\Account\AccountRepositoryInterface');
$creditCards = $accounts->getCreditCards();
$creditCards = $accounts->getCreditCards($end);
$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) {
if ($creditCard->balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$amount = $balance * -1;
$amount = $creditCard->balance * -1;
$fakeBill = $this->createFakeBill($description, $date, $amount);
unset($description, $amount);
$unpaid->push([$fakeBill, $date]);
}
if ($balance == 0) {
if ($creditCard->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);
@@ -474,4 +525,32 @@ class BillRepository implements BillRepositoryInterface
return $set;
}
/**
* This method returns all active bills which have been paid for in the given range,
* with the field "paid" indicating how much the bill was for.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function billsPaidInRange(Carbon $start, Carbon $end)
{
$set = Auth::user()->bills()
->leftJoin('transaction_journals', 'transaction_journals.bill_id', '=', 'bills.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0);
}
)
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('bills.active', 1)
->groupBy('bills.id')->get(
['bills.*', DB::Raw('SUM(`transactions`.`amount`) as `paid`')]
);
return $set;
}
}

View File

@@ -39,6 +39,7 @@ interface BillRepositoryInterface
public function getBillsForChart(Carbon $start, Carbon $end);
/**
* @deprecated
* Returns the sum of all payments connected to this bill between the dates.
*
* @param Bill $bill
@@ -49,6 +50,17 @@ interface BillRepositoryInterface
*/
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end);
/**
* This method returns all active bills which have been paid for in the given range,
* with the field "paid" indicating how much the bill was for.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function billsPaidInRange(Carbon $start, Carbon $end);
/**
* Create a fake bill to help the chart controller.
*
@@ -77,6 +89,15 @@ interface BillRepositoryInterface
*/
public function getBills();
/**
* Gets the bills which have some kind of relevance to the accounts mentioned.
*
* @param Collection $accounts
*
* @return Collection
*/
public function getBillsForAccounts(Collection $accounts);
/**
* @param Bill $bill
*

View File

@@ -7,6 +7,7 @@ use Carbon\Carbon;
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\Query\Builder as QueryBuilder;
@@ -50,10 +51,10 @@ 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 $sum;
}
@@ -139,9 +140,11 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
return $cache->get(); // @codeCoverageIgnore
}
$data = $budget->limitrepetitions()
->where('limit_repetitions.startdate', $start)
->where('limit_repetitions.enddate', $end)
->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.*']);
//Log::debug('Looking for limit reps for budget #' . $budget->id . ' start [' . $start . '] and end [' . $end . '].');
//Log::debug(DB::getQueryLog())
$cache->store($data);
return $data;
@@ -226,6 +229,8 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
}
/**
* @deprecated
*
* @param Budget $budget
*
* @return Carbon
@@ -241,6 +246,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
}
/**
* @deprecated
* @param Budget $budget
* @param Carbon $date
*
@@ -303,25 +309,23 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
)
->after($start)
->before($end)
->transactionTypes(['Withdrawal'])
->transactionTypes([TransactionType::WITHDRAWAL])
->get(['transaction_journals.*'])->sum('amount');
bcscale(2);
return bcmul($noBudgetSet, -1);
return $noBudgetSet;
}
/**
* @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
@@ -96,6 +96,7 @@ interface BudgetRepositoryInterface
public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50);
/**
* @deprecated
* @param Budget $budget
*
* @return Carbon
@@ -103,6 +104,7 @@ interface BudgetRepositoryInterface
public function getLastBudgetLimitDate(Budget $budget);
/**
* @deprecated
* @param Budget $budget
* @param Carbon $date
*
@@ -128,16 +130,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,10 +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;
/**
@@ -47,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(
@@ -55,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');
}
/**
*
@@ -66,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(
@@ -76,7 +153,7 @@ 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);
@@ -177,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);
}
/**
@@ -198,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()->transactionTypes(['Withdrawal'])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
return $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
}
/**
@@ -237,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.
*
@@ -285,9 +362,10 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
return $cache->get(); // @codeCoverageIgnore
}
$sum = $category->transactionjournals()->transactionTypes(['Withdrawal'])->before($end)->after($start)->get(['transaction_journals.*'])->sum(
'amount'
);
$sum = $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->before($end)->after($start)->get(['transaction_journals.*'])
->sum(
'amount'
);
$cache->store($sum);
@@ -315,9 +393,10 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
return $cache->get(); // @codeCoverageIgnore
}
$sum = $category->transactionjournals()->transactionTypes(['Deposit'])->before($end)->after($start)->get(['transaction_journals.*'])->sum(
'amount'
);
$sum = $category->transactionjournals()->transactionTypes([TransactionType::DEPOSIT])->before($end)->after($start)->get(['transaction_journals.*'])
->sum(
'amount'
);
$cache->store($sum);
@@ -365,8 +444,165 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
*
* @return float
*/
public function earnedOnDaySumCorrected(Category $category, Carbon $date)
public function earnedOnDaySum(Category $category, Carbon $date)
{
return $category->transactionjournals()->transactionTypes(['Deposit'])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
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

@@ -34,11 +34,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.
*
@@ -47,7 +120,7 @@ interface CategoryRepositoryInterface
*
* @return array
*/
public function getCategoriesAndExpensesCorrected(Carbon $start, Carbon $end);
public function getCategoriesAndExpenses(Carbon $start, Carbon $end);
/**
* @param Category $category
@@ -73,12 +146,13 @@ interface CategoryRepositoryInterface
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
*/
@@ -100,17 +174,16 @@ 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
@@ -143,7 +216,7 @@ interface CategoryRepositoryInterface
*
* @return float
*/
public function spentOnDaySumCorrected(Category $category, Carbon $date);
public function spentOnDaySum(Category $category, Carbon $date);
/**
*
@@ -154,7 +227,7 @@ interface CategoryRepositoryInterface
*
* @return float
*/
public function earnedOnDaySumCorrected(Category $category, Carbon $date);
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'],
@@ -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 FireflyIII\Models\Account;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\CacheProperties;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
* Class ComponentRepository
@@ -14,46 +17,42 @@ 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');
$ids = [];
/** @var Account $account */
foreach ($accounts as $account) {
$ids[] = $account->id;
}
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
if ($shared === true) { // shared is true: always ignore transfers between accounts!
$sum = $object->transactionjournals()->transactionTypes(['Withdrawal', 'Deposit', 'Opening balance'])->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('amount');
}
$sum = $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)
->get(['transaction_journals.*'])->sum('amount');
$cache->store($sum);

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) {
@@ -100,13 +100,13 @@ class Amount
$symbol = $journal->symbol;
}
if ($journal->transactionType->type == 'Transfer' && $coloured) {
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) {
if ($journal->isTransfer() && !$coloured) {
$txt = $this->formatWithSymbol($symbol, $journal->amount_positive, false);
$cache->store($txt);
@@ -152,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) {
@@ -161,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

@@ -72,6 +72,54 @@ 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
*/
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:
@@ -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

@@ -273,7 +273,7 @@ class FireflyValidator extends Validator
// get entries from table
$set = DB::table($table)->where('user_id', Auth::user()->id)
->where('id', '!=', $exclude)->get([$field]);
->where('id', '!=', $exclude)->get([$field]);
foreach ($set as $entry) {
$fieldValue = $this->tryDecrypt($entry->$field);

737
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.3',
'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.5',
'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',
@@ -98,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'],
@@ -124,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]);
}
}

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

View File

@@ -1,4 +1,4 @@
/* globals $, Chart, currencySymbol */
/* globals $, Chart, currencySymbol,mon_decimal_point ,accounting, mon_thousands_sep, frac_digits */
/*
Make some colours:
@@ -24,6 +24,23 @@ var colourSet = [
];
// Settings object that controls default parameters for library methods:
accounting.settings = {
currency: {
symbol : currencySymbol, // default currency symbol is '$'
format: "%s %v", // controls output: %s = symbol, %v = value/number (can be object: see below)
decimal : mon_decimal_point, // decimal point separator
thousand: mon_thousands_sep, // thousands separator
precision : frac_digits // decimal places
},
number: {
precision : 0, // default precision on numbers is 0
thousand: ",",
decimal : "."
}
};
var fillColors = [];
var strokePointHighColors = [];
@@ -45,9 +62,9 @@ var defaultAreaOptions = {
animation: false,
scaleFontSize: 10,
responsive: false,
scaleLabel: " <%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
scaleLabel: " <%= accounting.formatMoney(value) %>",
tooltipFillColor: "rgba(0,0,0,0.5)",
multiTooltipTemplate: "<%=datasetLabel%>: <%= '" + currencySymbol + " ' + Number(value).toFixed(2).replace('.', ',') %>"
multiTooltipTemplate: "<%=datasetLabel%>: <%= accounting.formatMoney(value) %>"
};
@@ -61,7 +78,7 @@ var defaultPieOptions = {
scaleFontSize: 10,
responsive: false,
tooltipFillColor: "rgba(0,0,0,0.5)",
tooltipTemplate: "<%if (label){%><%=label%>: <%}%>" + currencySymbol + " <%= value %>",
tooltipTemplate: "<%if (label){%><%=label%>: <%}%> <%= accounting.formatMoney(value) %>",
};
@@ -90,10 +107,10 @@ var defaultColumnOptions = {
scaleFontSize: 10,
responsive: false,
animation: false,
scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
scaleLabel: "<%= accounting.formatMoney(value) %>",
tooltipFillColor: "rgba(0,0,0,0.5)",
tooltipTemplate: "<%if (label){%><%=label%>: <%}%>" + currencySymbol + " <%= value %>",
multiTooltipTemplate: "<%=datasetLabel%>: " + currencySymbol + " <%= Number(value).toFixed(2).replace('.', ',') %>"
tooltipTemplate: "<%if (label){%><%=label%>: <%}%> <%= accounting.formatMoney(value) %>",
multiTooltipTemplate: "<%=datasetLabel%>: <%= accounting.formatMoney(value) %>"
};
var defaultStackedColumnOptions = {
@@ -105,9 +122,9 @@ var defaultStackedColumnOptions = {
animation: false,
scaleFontSize: 10,
responsive: false,
scaleLabel: "<%= '" + currencySymbol + " ' + Number(value).toFixed(0).replace('.', ',') %>",
scaleLabel: "<%= accounting.formatMoney(value) %>",
tooltipFillColor: "rgba(0,0,0,0.5)",
multiTooltipTemplate: "<%=datasetLabel%>: " + currencySymbol + " <%= Number(value).toFixed(2).replace('.', ',') %>"
multiTooltipTemplate: "<%=datasetLabel%>: <%= accounting.formatMoney(value) %>"
};

View File

@@ -1,7 +1,9 @@
/* globals token, dateRangeConfig, $, */
$(function () {
"use strict";
$('.currencySelect').click(currencySelect);
// when you click on a currency, this happens:
$('.currency-option').click(currencySelect);
var ranges = {};
// range for the current month:
@@ -62,21 +64,51 @@ $(function () {
function currencySelect(e) {
"use strict";
var target = $(e.target);
// clicked on
var target = $(e.target); // target is the <A> tag.
// name of the field in question:
var name = target.data('name');
// id of menu button (used later on):
var menuID = 'currency_dropdown_' + name;
// the hidden input with the actual value of the selected currency:
var hiddenInputName = 'amount_currency_id_' + target.data('name');
// span with the current selection (next to the caret):
var spanId = 'currency_select_symbol_' + target.data('name');
// the selected currency symbol:
var symbol = target.data('symbol');
var code = target.data('code');
// id of the selected currency.
var id = target.data('id');
var fieldType = target.data('field');
var menu = $('.' + fieldType + 'CurrencyDropdown');
var symbolHolder = $('#' + fieldType + 'CurrentSymbol');
symbolHolder.text(symbol);
$('input[name="' + fieldType + '_currency_id"]').val(id);
// update the hidden input:
$('input[name="' + hiddenInputName + '"]').val(id);
// close dropdown (hack hack)
menu.click();
// update the symbol:
$('#' + spanId).text(symbol);
// close the menu (hack hack)
$('#' + menuID).click();
return false;
//var code = target.data('code');
//var fieldType = target.data('field');
//var menu = $('.' + fieldType + 'CurrencyDropdown');
//
//var symbolHolder = $('#' + fieldType + 'CurrentSymbol');
//symbolHolder.text(symbol);
//$('input[name="' + fieldType + '_currency_id"]').val(id);
//
// close dropdown (hack hack)
//menu.click();
//return false;
}

View File

@@ -1,93 +0,0 @@
/* globals google, expenseRestShow:true, incomeRestShow:true, year, shared, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
$(function () {
"use strict";
drawChart();
});
function drawChart() {
"use strict";
if (typeof columnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
columnChart('chart/report/in-out/' + year + shared, 'income-expenses-chart');
columnChart('chart/report/in-out-sum/' + year + shared, 'income-expenses-sum-chart');
}
if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
stackedColumnChart('chart/budget/year/' + year + shared, 'budgets');
stackedColumnChart('chart/category/spent-in-year/' + year + shared, 'categories-spent-in-year');
stackedColumnChart('chart/category/earned-in-year/' + year + shared, 'categories-earned-in-year');
}
if (typeof lineChart !== 'undefined' && typeof month !== 'undefined') {
lineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart');
}
}
function openModal(e) {
"use strict";
var target = $(e.target).parent();
var URL = target.attr('href');
$.get(URL).success(function (data) {
$('#defaultModal').empty().html(data).modal('show');
}).fail(function () {
alert('Could not load data.');
});
return false;
}
function showIncomes() {
"use strict";
if (incomeRestShow) {
// hide everything, make button say "show"
$('#showIncomes').text(showTheRest);
$('.incomesCollapsed').removeClass('in').addClass('out');
// toggle:
incomeRestShow = false;
} else {
// show everything, make button say "hide".
$('#showIncomes').text(hideTheRest);
$('.incomesCollapsed').removeClass('out').addClass('in');
// toggle:
incomeRestShow = true;
}
return false;
}
function showExpenses() {
"use strict";
if (expenseRestShow) {
// hide everything, make button say "show"
$('#showExpenses').text(showTheRestExpense);
$('.expenseCollapsed').removeClass('in').addClass('out');
// toggle:
expenseRestShow = false;
} else {
// show everything, make button say "hide".
$('#showExpenses').text(hideTheRestExpense);
$('.expenseCollapsed').removeClass('out').addClass('in');
// toggle:
expenseRestShow = true;
}
return false;
}
$(function () {
"use strict";
$('.openModal').on('click', openModal);
// click open the top X income list:
$('#showIncomes').click(showIncomes);
// click open the top X expense list:
$('#showExpenses').click(showExpenses);
});

View File

@@ -0,0 +1,64 @@
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
$(function () {
"use strict";
drawChart();
// click open the top X income list:
$('#showIncomes').click(showIncomes);
// click open the top X expense list:
$('#showExpenses').click(showExpenses);
});
function drawChart() {
"use strict";
// month view:
// draw account chart
lineChart('/chart/account/report/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'account-balances-chart');
}
function showIncomes() {
"use strict";
if (incomeRestShow) {
// hide everything, make button say "show"
$('#showIncomes').text(showTheRest);
$('.incomesCollapsed').removeClass('in').addClass('out');
// toggle:
incomeRestShow = false;
} else {
// show everything, make button say "hide".
$('#showIncomes').text(hideTheRest);
$('.incomesCollapsed').removeClass('out').addClass('in');
// toggle:
incomeRestShow = true;
}
return false;
}
function showExpenses() {
"use strict";
if (expenseRestShow) {
// hide everything, make button say "show"
$('#showExpenses').text(showTheRestExpense);
$('.expenseCollapsed').removeClass('in').addClass('out');
// toggle:
expenseRestShow = false;
} else {
// show everything, make button say "hide".
$('#showExpenses').text(hideTheRestExpense);
$('.expenseCollapsed').removeClass('out').addClass('in');
// toggle:
expenseRestShow = true;
}
return false;
}

View File

@@ -0,0 +1,155 @@
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
$(function () {
"use strict";
drawChart();
});
function drawChart() {
"use strict";
// income and expense over multi year:
columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart');
columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart');
$.each($('.account-chart'), function (i, v) {
var holder = $(v);
console.log('Will draw chart for account #' + holder.data('id'));
});
// draw budget chart based on selected budgets:
$('.budget-checkbox').on('change', updateBudgetChart);
selectBudgetsByCookie();
updateBudgetChart();
// draw category chart based on selected budgets:
$('.category-checkbox').on('change', updateCategoryChart);
selectCategoriesByCookie();
updateCategoryChart();
}
function selectBudgetsByCookie() {
"use strict";
var cookie = readCookie('multi-year-budgets');
if (cookie !== null) {
var cookieArray = cookie.split(',');
for (var x in cookieArray) {
var budgetId = cookieArray[x];
$('.budget-checkbox[value="' + budgetId + '"').prop('checked', true);
}
}
}
function selectCategoriesByCookie() {
"use strict";
var cookie = readCookie('multi-year-categories');
if (cookie !== null) {
var cookieArray = cookie.split(',');
for (var x in cookieArray) {
var categoryId = cookieArray[x];
$('.category-checkbox[value="' + categoryId + '"').prop('checked', true);
}
}
}
function updateBudgetChart() {
"use strict";
console.log('will update budget chart.');
// get all budget ids:
var budgets = [];
$.each($('.budget-checkbox'), function (i, v) {
var current = $(v);
if (current.prop('checked')) {
budgets.push(current.val());
}
});
if (budgets.length > 0) {
var budgetIds = budgets.join(',');
// remove old chart:
$('#budgets-chart').replaceWith('<canvas id="budgets-chart" class="budgets-chart" style="width:100%;height:400px;"></canvas>');
// hide message:
$('#budgets-chart-message').hide();
// draw chart. Redraw when exists? Not sure if we support that.
columnChart('chart/budget/multi-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds + '/' + budgetIds, 'budgets-chart');
createCookie('multi-year-budgets', budgets, 365);
} else {
// hide canvas, show message:
$('#budgets-chart-message').show();
$('#budgets-chart').hide();
}
}
function updateCategoryChart() {
"use strict";
console.log('will update category chart.');
// get all category ids:
var categories = [];
$.each($('.category-checkbox'), function (i, v) {
var current = $(v);
if (current.prop('checked')) {
categories.push(current.val());
}
});
if (categories.length > 0) {
var categoryIds = categories.join(',');
// remove old chart:
$('#categories-chart').replaceWith('<canvas id="categories-chart" class="budgets-chart" style="width:100%;height:400px;"></canvas>');
// hide message:
$('#categories-chart-message').hide();
// draw chart. Redraw when exists? Not sure if we support that.
columnChart('chart/category/multi-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds + '/' + categoryIds, 'categories-chart');
createCookie('multi-year-categories', categories, 365);
} else {
// hide canvas, show message:
$('#categories-chart-message').show();
$('#categories-chart').hide();
}
}
function createCookie(name, value, days) {
"use strict";
var expires;
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toGMTString();
} else {
expires = "";
}
document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
}
function readCookie(name) {
"use strict";
var nameEQ = encodeURIComponent(name) + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
}
return null;
}
function eraseCookie(name) {
createCookie(name, "", -1);
}

View File

@@ -0,0 +1,208 @@
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
$(function () {
"use strict";
drawChart();
if ($('#inputDateRange').length > 0) {
picker = $('#inputDateRange').daterangepicker(
{
locale: {
format: 'YYYY-MM-DD',
firstDay: 1,
},
minDate: minDate,
drops: 'up',
}
);
// set values from cookies, if any:
if (readCookie('report-type') !== null) {
$('select[name="report_type"]').val(readCookie('report-type'));
}
if ((readCookie('report-accounts') !== null)) {
var arr = readCookie('report-accounts').split(',');
arr.forEach(function (val) {
$('input[type="checkbox"][value="' + val + '"]').prop('checked', true);
});
}
// set date:
var startStr = readCookie('report-start');
var endStr = readCookie('report-end');
if (startStr !== null && endStr !== null && startStr.length == 8 && endStr.length == 8) {
var startDate = moment(startStr, "YYYYMMDD");
var endDate = moment(endStr, "YYYYMMDD");
var datePicker = $('#inputDateRange').data('daterangepicker');
datePicker.setStartDate(startDate);
datePicker.setEndDate(endDate);
}
}
$('.openModal').on('click', openModal);
$('.date-select').on('click', preSelectDate);
$('#report-form').on('submit', catchSubmit);
// click open the top X income list:
$('#showIncomes').click(showIncomes);
// click open the top X expense list:
$('#showExpenses').click(showExpenses);
});
function catchSubmit() {
"use strict";
// default;20141201;20141231;4;5
// report name:
var url = '' + $('select[name="report_type"]').val() + '/';
// date, processed:
var picker = $('#inputDateRange').data('daterangepicker');
url += moment(picker.startDate).format("YYYYMMDD") + '/';
url += moment(picker.endDate).format("YYYYMMDD") + '/';
// all account ids:
var count = 0;
var accounts = [];
$.each($('.account-checkbox'), function (i, v) {
var c = $(v);
if (c.prop('checked')) {
url += c.val() + ';';
accounts.push(c.val());
count++;
}
});
if (count > 0) {
// set cookie to remember choices.
createCookie('report-type', $('select[name="report_type"]').val(), 365);
createCookie('report-accounts', accounts, 365);
createCookie('report-start', moment(picker.startDate).format("YYYYMMDD"), 365);
createCookie('report-end', moment(picker.endDate).format("YYYYMMDD"), 365);
window.location.href = reportURL + "/" + url;
}
//console.log(url);
return false;
}
function preSelectDate(e) {
"use strict";
var link = $(e.target);
var picker = $('#inputDateRange').data('daterangepicker');
picker.setStartDate(link.data('start'));
picker.setEndDate(link.data('end'));
return false;
}
function drawChart() {
"use strict";
if (typeof columnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart');
columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart');
}
if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
stackedColumnChart('chart/budget/year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budgets');
stackedColumnChart('chart/category/spent-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-spent-in-year');
stackedColumnChart('chart/category/earned-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-earned-in-year');
}
if (typeof lineChart !== 'undefined' && typeof accountIds !== 'undefined') {
lineChart('/chart/account/report/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'account-balances-chart');
}
}
function openModal(e) {
"use strict";
var target = $(e.target).parent();
var URL = target.attr('href');
$.get(URL).success(function (data) {
$('#defaultModal').empty().html(data).modal('show');
}).fail(function () {
alert('Could not load data.');
});
return false;
}
function showIncomes() {
"use strict";
if (incomeRestShow) {
// hide everything, make button say "show"
$('#showIncomes').text(showTheRest);
$('.incomesCollapsed').removeClass('in').addClass('out');
// toggle:
incomeRestShow = false;
} else {
// show everything, make button say "hide".
$('#showIncomes').text(hideTheRest);
$('.incomesCollapsed').removeClass('out').addClass('in');
// toggle:
incomeRestShow = true;
}
return false;
}
function showExpenses() {
"use strict";
if (expenseRestShow) {
// hide everything, make button say "show"
$('#showExpenses').text(showTheRestExpense);
$('.expenseCollapsed').removeClass('in').addClass('out');
// toggle:
expenseRestShow = false;
} else {
// show everything, make button say "hide".
$('#showExpenses').text(hideTheRestExpense);
$('.expenseCollapsed').removeClass('out').addClass('in');
// toggle:
expenseRestShow = true;
}
return false;
}
function createCookie(name, value, days) {
var expires;
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toGMTString();
} else {
expires = "";
}
document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
}
function readCookie(name) {
var nameEQ = encodeURIComponent(name) + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
}
return null;
}
function eraseCookie(name) {
createCookie(name, "", -1);
}

View File

@@ -0,0 +1,67 @@
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
$(function () {
"use strict";
drawChart();
// click open the top X income list:
$('#showIncomes').click(showIncomes);
// click open the top X expense list:
$('#showExpenses').click(showExpenses);
});
function drawChart() {
"use strict";
columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart');
columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart');
stackedColumnChart('chart/budget/year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budgets');
stackedColumnChart('chart/category/spent-in-period/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-spent-in-period');
stackedColumnChart('chart/category/earned-in-period/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-earned-in-period');
}
function showIncomes() {
"use strict";
if (incomeRestShow) {
// hide everything, make button say "show"
$('#showIncomes').text(showTheRest);
$('.incomesCollapsed').removeClass('in').addClass('out');
// toggle:
incomeRestShow = false;
} else {
// show everything, make button say "hide".
$('#showIncomes').text(hideTheRest);
$('.incomesCollapsed').removeClass('out').addClass('in');
// toggle:
incomeRestShow = true;
}
return false;
}
function showExpenses() {
"use strict";
if (expenseRestShow) {
// hide everything, make button say "show"
$('#showExpenses').text(showTheRestExpense);
$('.expenseCollapsed').removeClass('in').addClass('out');
// toggle:
expenseRestShow = false;
} else {
// show everything, make button say "hide".
$('#showExpenses').text(hideTheRestExpense);
$('.expenseCollapsed').removeClass('out').addClass('in');
// toggle:
expenseRestShow = true;
}
return false;
}

122
public/js/reports/index.js Normal file
View File

@@ -0,0 +1,122 @@
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
$(function () {
"use strict";
if ($('#inputDateRange').length > 0) {
picker = $('#inputDateRange').daterangepicker(
{
locale: {
format: 'YYYY-MM-DD',
firstDay: 1,
},
minDate: minDate,
drops: 'up',
}
);
// set values from cookies, if any:
if (readCookie('report-type') !== null) {
$('select[name="report_type"]').val(readCookie('report-type'));
}
if ((readCookie('report-accounts') !== null)) {
var arr = readCookie('report-accounts').split(',');
arr.forEach(function (val) {
$('input[type="checkbox"][value="' + val + '"]').prop('checked', true);
});
}
// set date:
var startStr = readCookie('report-start');
var endStr = readCookie('report-end');
if (startStr !== null && endStr !== null && startStr.length == 8 && endStr.length == 8) {
var startDate = moment(startStr, "YYYYMMDD");
var endDate = moment(endStr, "YYYYMMDD");
var datePicker = $('#inputDateRange').data('daterangepicker');
datePicker.setStartDate(startDate);
datePicker.setEndDate(endDate);
}
}
$('.date-select').on('click', preSelectDate);
$('#report-form').on('submit', catchSubmit);
});
function catchSubmit() {
"use strict";
// default;20141201;20141231;4;5
// report name:
var url = '' + $('select[name="report_type"]').val() + '/';
// date, processed:
var picker = $('#inputDateRange').data('daterangepicker');
url += moment(picker.startDate).format("YYYYMMDD") + '/';
url += moment(picker.endDate).format("YYYYMMDD") + '/';
// all account ids:
var count = 0;
var accounts = [];
$.each($('.account-checkbox'), function (i, v) {
var c = $(v);
if (c.prop('checked')) {
url += c.val() + ',';
accounts.push(c.val());
count++;
}
});
if (count > 0) {
// set cookie to remember choices.
createCookie('report-type', $('select[name="report_type"]').val(), 365);
createCookie('report-accounts', accounts, 365);
createCookie('report-start', moment(picker.startDate).format("YYYYMMDD"), 365);
createCookie('report-end', moment(picker.endDate).format("YYYYMMDD"), 365);
window.location.href = reportURL + "/" + url;
}
//console.log(url);
return false;
}
function preSelectDate(e) {
"use strict";
var link = $(e.target);
var picker = $('#inputDateRange').data('daterangepicker');
picker.setStartDate(link.data('start'));
picker.setEndDate(link.data('end'));
return false;
}
function createCookie(name, value, days) {
"use strict";
var expires;
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toGMTString();
} else {
expires = "";
}
document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
}
function readCookie(name) {
"use strict";
var nameEQ = encodeURIComponent(name) + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
}
return null;
}

View File

@@ -27,8 +27,8 @@ return [
// reports
'reports' => 'Reports',
'monthly_report' => 'Montly report for :date',
'monthly_report_shared' => 'Montly report for :date (including shared accounts)',
'monthly_report' => 'Monthly report for :date',
'monthly_report_shared' => 'Monthly report for :date (including shared accounts)',
'yearly_report' => 'Yearly report for :date',
'yearly_report_shared' => 'Yearly report for :date (including shared accounts)',
'budget_report' => 'Budget report for :date',

View File

@@ -0,0 +1,8 @@
<?php
return [
'locale' => 'en, English, en_US, en_US.utf8',
'month' => '%B %Y',
'month_and_day' => '%B %e, %Y',
];

View File

@@ -2,6 +2,7 @@
return [
// general stuff:
'language_incomplete' => 'This language is not yet fully translated',
'test' => 'You have selected English.',
'close' => 'Close',
'pleaseHold' => 'Please hold...',
@@ -67,8 +68,7 @@ return [
'new_password' => 'New password',
'new_password_again' => 'New password (again)',
'delete_your_account' => 'Delete your account',
'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, <em>anything</em> you might have saved' .
' into Firefly III. It\'ll be GONE.',
'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, <em>anything</em> you might have saved into Firefly III. It\'ll be GONE.',
'delete_your_account_password' => 'Enter your password to continue.',
'password' => 'Password',
'are_you_sure' => 'Are you sure? You cannot undo this.',
@@ -109,23 +109,15 @@ return [
'csv_define_column_roles' => 'Define column roles',
'csv_map_values' => 'Map found values to existing values',
'csv_download_config' => 'Download CSV configuration file.',
'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV' .
' importer made by the folks at <a href="https://www.atlassian.com/">Atlassian</a>. Simply upload your CSV' .
' file and follow the instructions. If you would like to learn more, please click on the <i ' .
'class="fa fa-question-circle"></i> button at the top of this page.',
'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at <a href="https://www.atlassian.com/">Atlassian</a>. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the <i class="fa fa-question-circle"></i> button at the top of this page.',
'csv_index_beta_warning' => 'This tool is very much in beta. Please proceed with caution',
'csv_header_help' => 'Check this box when your CSV file\'s first row consists of column names, not actual data',
'csv_date_help' => 'Date time format in your CSV. Follow the format like <a href="https://secure.php.net/manual/en/' .
'datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this page</a> ' .
'indicates. The default value will parse dates that look like this: ' . date('Ymd'),
'csv_date_help' => 'Date time format in your CSV. Follow the format like <a href="https://secure.php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this page</a> indicates. The default value will parse dates that look like this: :dateExample.',
'csv_csv_file_help' => 'Select the CSV file here. You can only upload one file at a time',
'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.',
'csv_upload_button' => 'Start importing CSV',
'csv_column_roles_title' => 'Define column roles',
'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. ' .
'Please check out the example data if you\'re not sure yourself. Click on the question mark ' .
'(top right of the page) to learn what each column means. If you want to map imported data ' .
'onto existing data in Firefly, use the checkbox. The next step will show you what this button does.',
'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.',
'csv_column_roles_table' => 'Column roles',
'csv_column' => 'CSV column',
'csv_column_name' => 'CSV column name',
@@ -135,15 +127,13 @@ return [
'csv_continue' => 'Continue to the next step',
'csv_go_back' => 'Go back to the previous step',
'csv_map_title' => 'Map found values to existing values',
'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your ' .
'database. This ensures that accounts and other things won\'t be created twice.',
'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.',
'csv_field_value' => 'Field value from CSV',
'csv_field_mapped_to' => 'Must be mapped to...',
'csv_do_not_map' => 'Do not map this value',
'csv_download_config_title' => 'Download CSV configuration',
'csv_download_config_text' => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.',
'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all ' .
'over again. But, if the import succeeds, it will be easier to upload similar CSV files.',
'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.',
'csv_do_download_config' => 'Download configuration file.',
'csv_empty_description' => '(empty description)',
'csv_upload_form' => 'CSV upload form',
@@ -189,11 +179,9 @@ return [
'csv_column_tags-space' => 'Tags (space separated)',
'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.',
'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.',
'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which'
. ' account the transactions in the CSV belong to.',
'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.',
'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?',
// create new stuff:
'create_new_withdrawal' => 'Create new withdrawal',
'create_new_deposit' => 'Create new deposit',
@@ -204,7 +192,6 @@ return [
'create_new_piggy_bank' => 'Create new piggy bank',
'create_new_bill' => 'Create new bill',
// currencies:
'create_currency' => 'Create a new currency',
'edit_currency' => 'Edit currency ":name"',
@@ -250,35 +237,27 @@ return [
'details_for_expense' => 'Details for expense account ":name"',
'details_for_revenue' => 'Details for revenue account ":name"',
'details_for_cash' => 'Details for cash account ":name"',
'store_new_asset_account' => 'Store new asset account',
'store_new_expense_account' => 'Store new expense account',
'store_new_revenue_account' => 'Store new revenue account',
'edit_asset_account' => 'Edit asset account ":name"',
'edit_expense_account' => 'Edit expense account ":name"',
'edit_revenue_account' => 'Edit revenue account ":name"',
'delete_asset_account' => 'Delete asset account ":name"',
'delete_expense_account' => 'Delete expense account ":name"',
'delete_revenue_account' => 'Delete revenue account ":name"',
'asset_deleted' => 'Successfully deleted asset account ":name"',
'expense_deleted' => 'Successfully deleted expense account ":name"',
'revenue_deleted' => 'Successfully deleted revenue account ":name"',
'update_asset_account' => 'Update asset account',
'update_expense_account' => 'Update expense account',
'update_revenue_account' => 'Update revenue account',
'make_new_asset_account' => 'Create a new asset account',
'make_new_expense_account' => 'Create a new expense account',
'make_new_revenue_account' => 'Create a new revenue account',
'asset_accounts' => 'Asset accounts',
'expense_accounts' => 'Expense accounts',
'revenue_accounts' => 'Revenue accounts',
'accountExtraHelp_asset' => '',
'accountExtraHelp_expense' => '',
'accountExtraHelp_revenue' => '',
@@ -341,6 +320,7 @@ return [
'Default account' => 'Asset account',
'Expense account' => 'Expense account',
'Revenue account' => 'Revenue account',
'Initial balance account' => 'Initial balance account',
'budgets' => 'Budgets',
'tags' => 'Tags',
'reports' => 'Reports',
@@ -376,10 +356,13 @@ return [
'profile' => 'Profile',
// reports:
'reportForYear' => 'Yearly report for :year',
'reportForYearShared' => 'Yearly report for :year (including shared accounts)',
'reportForMonth' => 'Montly report for :month',
'reportForMonthShared' => 'Montly report for :month (including shared accounts)',
'report_default' => 'Default financial report for :start until :end',
'quick_link_reports' => 'Quick links',
'quick_link_default_report' => 'Default financial report',
'report_this_month_quick' => 'Current month, all accounts',
'report_this_year_quick' => 'Current year, all accounts',
'report_all_time_quick' => 'All-time, all accounts',
'reports_can_bookmark' => 'Remember that reports can be bookmarked.',
'incomeVsExpenses' => 'Income vs. expenses',
'accountBalances' => 'Account balances',
'balanceStartOfYear' => 'Balance at start of year',
@@ -398,6 +381,7 @@ return [
'outsideOfBudgets' => 'Outside of budgets',
'leftInBudget' => 'Left in budget',
'sumOfSums' => 'Sum of sums',
'noCategory' => '(no category)',
'notCharged' => 'Not charged (yet)',
'inactive' => 'Inactive',
'difference' => 'Difference',
@@ -407,9 +391,18 @@ return [
'showTheRest' => 'Show everything',
'hideTheRest' => 'Show only the top :number',
'sum_of_year' => 'Sum of year',
'sum_of_years' => 'Sum of years',
'average_of_year' => 'Average of year',
'average_of_years' => 'Average of years',
'categories_earned_in_year' => 'Categories (by earnings)',
'categories_spent_in_year' => 'Categories (by spendings)',
'report_type' => 'Report type',
'report_type_default' => 'Default financial report',
'report_included_accounts' => 'Included accounts',
'report_date_range' => 'Date range',
'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.',
'report_preset_ranges' => 'Pre-set ranges',
'shared' => 'Shared',
// charts:
'dayOfMonth' => 'Day of the month',
@@ -470,7 +463,6 @@ return [
'regular_tag' => 'Just a regular tag.',
'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.',
'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.',
'delete_tag' => 'Delete tag ":tag"',
'new_tag' => 'Make new tag',
'edit_tag' => 'Edit tag ":tag"',
@@ -479,17 +471,8 @@ return [
'tag_title_nothing' => 'Default tags',
'tag_title_balancingAct' => 'Balancing act tags',
'tag_title_advancePayment' => 'Advance payment tags',
'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like' .
' <span class="label label-info">expensive</span>, <span class="label label-info">bill</span>' .
' or <span class="label label-info">for-party</span>. In Firefly III, tags can have more properties' .
' such as a date, description and location. This allows you to join transactions together in a more' .
' meaningful way. For example, you could make a tag called <span class="label label-success">' .
'Christmas dinner with friends</span> and add information about the restaurant. Such tags are "singular",' .
' you would only use them for a single occasion, perhaps with multiple transactions.',
'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money' .
' for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where ' .
'expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you.' .
' Using tags the old-fashioned way is of course always possible. ',
'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like <span class="label label-info">expensive</span>, <span class="label label-info">bill</span> or <span class="label label-info">for-party</span>. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called <span class="label label-success"> Christmas dinner with friends</span> and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.',
'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.',
'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.',
];

View File

@@ -88,16 +88,10 @@ return [
'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?',
'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.',
'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.' .
'|All :count transactions connected to this account will be deleted as well.',
'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.' .
'|All :count piggy bank connected to this account will be deleted as well.',
'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.' .
'|All :count transactions connected to this bill will spared deletion.',
'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.' .
'|All :count transactions connected to this budget will spared deletion.',
'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.' .
'|All :count transactions connected to this category will spared deletion.',
'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.' .
'|All :count transactions connected to this tag will spared deletion.',
'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.',
'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.',
'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.',
'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.',
'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.',
'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.',
];

View File

@@ -0,0 +1,84 @@
<?php
return [
// tour!
'main-content-title' => 'Welcome to Firefly III',
'main-content-text' => 'Do yourself a favor and follow this short guide to make sure you know your way around.',
'sidebar-toggle-title' => 'Sidebar to create stuff',
'sidebar-toggle-text' => 'Hidden under the plus icon are all the buttons to create new stuff. Accounts, transactions, everything!',
'account-menu-title' => 'All your accounts',
'account-menu-text' => 'Here you can find all the accounts you\'ve made.',
'budget-menu-title' => 'Budgets',
'budget-menu-text' => 'Use this page to organise your finances and limit spending.',
'report-menu-title' => 'Reports',
'report-menu-text' => 'Check this out when you want a solid overview of your fiances.',
'transaction-menu-title' => 'Transactions',
'transaction-menu-text' => 'All transactions you\'ve created can be found here.',
'option-menu-title' => 'Options',
'option-menu-text' => 'This is pretty self-explanatory.',
'main-content-end-title' => 'The end!',
'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.',
'index' => 'index',
'home' => 'home',
'accounts-index' => 'accounts.index',
'accounts-create' => 'accounts.create',
'accounts-edit' => 'accounts.edit',
'accounts-delete' => 'accounts.delete',
'accounts-show' => 'accounts.show',
'attachments-edit' => 'attachments.edit',
'attachments-delete' => 'attachments.delete',
'attachments-show' => 'attachments.show',
'attachments-preview' => 'attachments.preview',
'bills-index' => 'bills.index',
'bills-create' => 'bills.create',
'bills-edit' => 'bills.edit',
'bills-delete' => 'bills.delete',
'bills-show' => 'bills.show',
'budgets-index' => 'budgets.index',
'budgets-create' => 'budgets.create',
'budgets-edit' => 'budgets.edit',
'budgets-delete' => 'budgets.delete',
'budgets-show' => 'budgets.show',
'budgets-noBudget' => 'budgets.noBudget',
'categories-index' => 'categories.index',
'categories-create' => 'categories.create',
'categories-edit' => 'categories.edit',
'categories-delete' => 'categories.delete',
'categories-show' => 'categories.show',
'categories-show-date' => 'categories.show.date',
'categories-noCategory' => 'categories.noCategory',
'csv-index' => 'csv.index',
'csv-column-roles' => 'csv.column-roles',
'csv-map' => 'csv.map',
'csv-download-config-page' => 'csv.download-config-page',
'csv-process' => 'csv.process',
'currency-index' => 'currency.index',
'currency-create' => 'currency.create',
'currency-edit' => 'currency.edit',
'currency-delete' => 'currency.delete',
'new-user-index' => 'new-user.index',
'piggy-banks-index' => 'piggy-banks.index',
'piggy-banks-create' => 'piggy-banks.create',
'piggy-banks-edit' => 'piggy-banks.edit',
'piggy-banks-delete' => 'piggy-banks.delete',
'piggy-banks-show' => 'piggy-banks.show',
'preferences' => 'preferences',
'profile' => 'profile',
'profile-change-password' => 'profile.change-password',
'profile-delete-account' => 'profile.delete-account',
'reports-index' => 'reports.index',
'reports-report' => 'reports.report',
'search' => 'search',
'tags-index' => 'tags.index',
'tags-create' => 'tags.create',
'tags-show' => 'tags.show',
'tags-edit' => 'tags.edit',
'tags-delete' => 'tags.delete',
'transactions-index' => 'transactions.index',
'transactions-create' => 'transactions.create',
'transactions-edit' => 'transactions.edit',
'transactions-delete' => 'transactions.delete',
'transactions-show' => 'transactions.show',
];

View File

@@ -14,7 +14,7 @@ return [
'matchingAmount' => 'Amount',
'lastMatch' => 'Last match',
'expectedMatch' => 'Expected match',
'automatch' => 'Automatch?',
'automatch' => 'Auto match?',
'repeat_freq' => 'Repeats',
'description' => 'Description',
'amount' => 'Amount',

View File

@@ -17,5 +17,6 @@ return [
"token" => "This password reset token is invalid.",
"sent" => "We have e-mailed your password reset link!",
"reset" => "Your password has been reset!",
'blocked' => 'Nice try though.'
];

View File

@@ -1,18 +1,7 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'invalid_domain' => 'Due to security constraints, you cannot register from this domain.',
'file_already_attached' => 'Uploaded file ":name" is already attached to this object.',
'file_attached' => 'Succesfully uploaded file ":name".',
'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.',
@@ -28,12 +17,10 @@ return [
"before" => "The :attribute must be a date before :date.",
'unique_object_for_user' => 'This name is already in use',
'unique_account_for_user' => 'This account name is already in use',
"between" => [
"numeric" => "The :attribute must be between :min and :max.",
"file" => "The :attribute must be between :min and :max kilobytes.",
"string" => "The :attribute must be between :min and :max characters.",
"array" => "The :attribute must have between :min and :max items.",
],
"between.numeric" => "The :attribute must be between :min and :max.",
"between.file" => "The :attribute must be between :min and :max kilobytes.",
"between.string" => "The :attribute must be between :min and :max characters.",
"between.array" => "The :attribute must have between :min and :max items.",
"boolean" => "The :attribute field must be true or false.",
"confirmed" => "The :attribute confirmation does not match.",
"date" => "The :attribute is not a valid date.",
@@ -48,67 +35,33 @@ return [
"in" => "The selected :attribute is invalid.",
"integer" => "The :attribute must be an integer.",
"ip" => "The :attribute must be a valid IP address.",
"max" => [
"numeric" => "The :attribute may not be greater than :max.",
"file" => "The :attribute may not be greater than :max kilobytes.",
"string" => "The :attribute may not be greater than :max characters.",
"array" => "The :attribute may not have more than :max items.",
],
'json' => 'The :attribute must be a valid JSON string.',
"max.numeric" => "The :attribute may not be greater than :max.",
"max.file" => "The :attribute may not be greater than :max kilobytes.",
"max.string" => "The :attribute may not be greater than :max characters.",
"max.array" => "The :attribute may not have more than :max items.",
"mimes" => "The :attribute must be a file of type: :values.",
"min" => [
"numeric" => "The :attribute must be at least :min.",
"file" => "The :attribute must be at least :min kilobytes.",
"string" => "The :attribute must be at least :min characters.",
"array" => "The :attribute must have at least :min items.",
],
"min.numeric" => "The :attribute must be at least :min.",
"min.file" => "The :attribute must be at least :min kilobytes.",
"min.string" => "The :attribute must be at least :min characters.",
"min.array" => "The :attribute must have at least :min items.",
"not_in" => "The selected :attribute is invalid.",
"numeric" => "The :attribute must be a number.",
"regex" => "The :attribute format is invalid.",
"required" => "The :attribute field is required.",
"required_if" => "The :attribute field is required when :other is :value.",
'required_unless' => 'The :attribute field is required unless :other is in :values.',
"required_with" => "The :attribute field is required when :values is present.",
"required_with_all" => "The :attribute field is required when :values is present.",
"required_without" => "The :attribute field is required when :values is not present.",
"required_without_all" => "The :attribute field is required when none of :values are present.",
"same" => "The :attribute and :other must match.",
"size" => [
"numeric" => "The :attribute must be :size.",
"file" => "The :attribute must be :size kilobytes.",
"string" => "The :attribute must be :size characters.",
"array" => "The :attribute must contain :size items.",
],
"size.numeric" => "The :attribute must be :size.",
"size.file" => "The :attribute must be :size kilobytes.",
"size.string" => "The :attribute must be :size characters.",
"size.array" => "The :attribute must contain :size items.",
"unique" => "The :attribute has already been taken.",
'string' => 'The :attribute must be a string.',
"url" => "The :attribute format is invalid.",
"timezone" => "The :attribute must be a valid zone.",
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [],
];

View File

@@ -0,0 +1,60 @@
<?php
return [
'home' => 'Accueil',
// accounts
'cash_accounts' => 'Cash accounts',
'edit_account' => 'Editer le compte : ":name"',
// currencies
'edit_currency' => 'Editer la devise : ";name"',
'delete_currency' => 'Supprimer la devise ":name"',
// piggy banks
'newPiggyBank' => 'Create a new piggy bank',
'edit_piggyBank' => 'Edit piggy bank ":name"',
// top menu
'preferences' => 'Preferences',
'profile' => 'Profil',
'changePassword' => 'Modifier le mot de passe',
// bills
'bills' => 'Factures',
'newBill' => 'Nouvelle facture',
'edit_bill' => 'Editer la facture : ":name"',
'delete_bill' => 'Supprimer la facture ":name"',
// reports
'reports' => 'Rapport',
'monthly_report' => 'Rapport mensuel pour :date',
'monthly_report_shared' => 'Rapport mensuel pour :date (avec les comptes joints)',
'yearly_report' => 'Rapport annuel pour :date',
'yearly_report_shared' => 'Rapport annuel pour :date (avec les comptes joints)',
'budget_report' => 'Rapport budgetaire pour :date',
// search
'searchResult' => 'Resultat de recherche pour ":query"',
// transaction lists.
'withdrawal_list' => 'Dépenses',
'deposit_list' => 'Revenue, Salaire et depots ',
'transfer_list' => 'Transferts',
'transfers_list' => 'Transferts',
// create transactions
'create_withdrawal' => 'Creer un nouveau retrait',
'create_deposit' => 'Create new deposit',
'create_transfer' => 'Creer un nouveau transfert',
// edit transactions
'edit_journal' => 'Editer la transaction ":description"',
'delete_journal' => 'Supprimer la transaction ":description"',
// tags
'tags' => 'Tags',
'createTag' => 'Créer un nouveau tag',
'edit_tag' => 'Editer le tag ":tag"',
'delete_tag' => 'Supprimer le tag ":tag"',
];

View File

@@ -0,0 +1,8 @@
<?php
return [
'locale' => 'fr, French, fr_FR, fr_FR.utf8',
'month' => '%B %Y',
'month_and_day' => '%e %B %Y',
];

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