Compare commits

...

150 Commits

Author SHA1 Message Date
James Cole
50cf7f6a3b Merge branch 'release/3.4.0.8' 2015-05-22 18:55:13 +02:00
James Cole
eecb4db34c Built chart tests. 2015-05-22 18:31:57 +02:00
James Cole
1f865d3ea4 Improved coverage. 2015-05-22 15:05:32 +02:00
James Cole
623bb4b350 New tests. 2015-05-22 10:17:20 +02:00
James Cole
dc8ad673a6 New test to cover some reminder stuff. 2015-05-22 08:52:30 +02:00
James Cole
4914ad821e Fixed coverage for transaction controller. 2015-05-22 08:03:34 +02:00
James Cole
f099cbadc3 Version increment. 2015-05-22 07:16:36 +02:00
James Cole
42cda384c8 Fix help routes 2015-05-22 07:15:40 +02:00
James Cole
23c91b9990 Some translations, playing around with popups. [skip ci] 2015-05-21 23:05:31 +02:00
James Cole
ff0379182e Sort chart [skip ci] 2015-05-21 18:57:14 +02:00
James Cole
e08a23948f Fixed chart. [skip ci] 2015-05-21 18:50:23 +02:00
James Cole
bd56de6d36 Fix other translations [skip ci] 2015-05-21 17:47:10 +02:00
James Cole
42970aea80 Fix some translations. 2015-05-21 17:45:01 +02:00
James Cole
003a05ee8d Fixed the category chart. 2015-05-21 17:27:48 +02:00
James Cole
ffb11b01a6 Some new translations. 2015-05-21 07:44:44 +02:00
James Cole
e426f5d5da Some new translations. 2015-05-21 07:30:38 +02:00
James Cole
6989f61e1b Extra translations [skip ci] 2015-05-20 20:10:01 +02:00
James Cole
0e6677ccb3 Remove cash accounts from list [skip ci] 2015-05-20 20:04:27 +02:00
James Cole
8f104d555a Added newlines to files. 2015-05-20 19:56:14 +02:00
James Cole
b1d3158db1 Code cleanup. 2015-05-20 19:55:53 +02:00
James Cole
7645005d5a Return of account id. 2015-05-20 18:18:05 +02:00
James Cole
411f77fd29 Fixed some other displays of money 2015-05-20 18:09:44 +02:00
James Cole
568ab26db1 Spell check [skip ci] 2015-05-20 17:06:47 +02:00
James Cole
29652108f0 Fixed tests. 2015-05-20 17:04:53 +02:00
James Cole
f07e4dc711 Fix sort. [skip ci] 2015-05-20 07:21:40 +02:00
James Cole
8a2ac457c2 Name fix [skip ci] 2015-05-20 07:21:01 +02:00
James Cole
9e54eecfaa Fixed some views. 2015-05-20 07:20:02 +02:00
James Cole
95ef691077 Fixed some more reports and charts. [skip ci] 2015-05-20 07:07:46 +02:00
James Cole
7a0ad5a587 Fixed a chart [skip ci] 2015-05-20 06:50:24 +02:00
James Cole
42b49d0e4b Added some correcting methods. [skip ci] 2015-05-20 06:50:15 +02:00
James Cole
9217c2f003 Corrected some charts [skip ci] 2015-05-20 06:49:51 +02:00
James Cole
fbdf66998d Added a forgotten chart, corrected some others. [skip ci] 2015-05-20 06:49:22 +02:00
James Cole
deda9d3c54 Add corrected expense routes [skip ci] 2015-05-20 06:49:03 +02:00
James Cole
a5d78f20ae Add corrected income and expense methods [skip ci] 2015-05-20 06:48:52 +02:00
James Cole
5ed09e3f38 Call the corrected spent function [skip ci] 2015-05-20 06:47:53 +02:00
James Cole
3e9774cd66 Merge branch 'release/3.4.0.7' 2015-05-18 18:23:07 +02:00
James Cole
54387c8fdf Merge branch 'release/3.4.0.7' into develop 2015-05-18 18:23:07 +02:00
James Cole
7eec949a13 Push new release. 2015-05-18 18:23:01 +02:00
James Cole
4113c4ff40 Fixed the tests. 2015-05-18 17:57:44 +02:00
James Cole
1bf0968bfe Show correct icon. [skip ci] 2015-05-18 17:05:22 +02:00
James Cole
374b90fb00 Show correct amount [skip ci] 2015-05-18 17:04:10 +02:00
James Cole
064e60e9d5 Show tag amount better [skip ci] 2015-05-18 17:02:18 +02:00
James Cole
b637455970 New translations [skip ci] 2015-05-18 16:59:01 +02:00
James Cole
68158937d1 Fixed translation [skip ci] 2015-05-18 06:56:59 +02:00
James Cole
adb1356b7a Small bug fix in display of income vs. expenses [skip ci] 2015-05-18 06:55:36 +02:00
James Cole
d880ccb8e0 Small bug fix in display of income vs. expenses [skip ci] 2015-05-18 06:54:46 +02:00
James Cole
050fb1d1ef Some translations [skip ci] 2015-05-17 18:06:00 +02:00
James Cole
6580752bde Now includes chart! [skip ci] 2015-05-17 18:03:16 +02:00
James Cole
c9df265c9b Included bills in month report. [skip ci] 2015-05-17 17:54:13 +02:00
James Cole
098e5bc162 Small translation fix [skip ci] 2015-05-17 17:27:56 +02:00
James Cole
4b2dcc74d4 Expanded the amount thing. 2015-05-17 16:12:00 +02:00
James Cole
a9254c5c9a Some new translations, better tag view [skip ci] 2015-05-17 15:55:24 +02:00
James Cole
7ce57e6ccb Fix a view, fix tag format. 2015-05-17 15:24:30 +02:00
James Cole
0fcb32a66f Fix update error. 2015-05-17 15:17:06 +02:00
James Cole
9946535f01 Some new bread crumbs [skip ci] 2015-05-17 15:12:45 +02:00
James Cole
2b17396d6b Fix some translations. 2015-05-17 13:44:58 +02:00
James Cole
b01d5bc237 Translation fixes [skip ci] 2015-05-17 12:53:32 +02:00
James Cole
b123860304 More stuff. 2015-05-17 12:49:09 +02:00
James Cole
033f5b67db Code cleanup 2015-05-17 11:28:58 +02:00
James Cole
6280448dfb Fixed more tests. 2015-05-17 10:47:12 +02:00
James Cole
01cd3333e4 Fixing tests. 2015-05-17 10:36:11 +02:00
James Cole
63050907b9 Code cleanup. 2015-05-17 10:30:18 +02:00
James Cole
beedf7d780 Cleaning up 2015-05-17 10:10:58 +02:00
James Cole
6b8194261f Clean up and simplify code. 2015-05-17 10:01:47 +02:00
James Cole
dbb1c4d534 Fixed some reports. 2015-05-17 09:35:49 +02:00
James Cole
e6263f9ff5 Some cleaning up 2015-05-17 09:18:44 +02:00
James Cole
6ca119c4db Minor changes [skip ci] 2015-05-17 09:04:55 +02:00
James Cole
c483a1ab3a Ignore simple classes. 2015-05-16 19:08:54 +02:00
James Cole
2e7edd033c Some expanded things. 2015-05-16 16:47:52 +02:00
James Cole
c576902501 Fixed tests. 2015-05-16 16:04:51 +02:00
James Cole
66c2951594 Cleanup 2015-05-16 15:52:09 +02:00
James Cole
b812881cdb More hip report stuff. 2015-05-16 15:43:58 +02:00
James Cole
cdeac2c6db Should simplify reports. 2015-05-16 14:51:23 +02:00
James Cole
bca2ddd529 Budget report thing. 2015-05-16 14:14:22 +02:00
James Cole
e7285c6499 Some overhauls. 2015-05-16 13:53:08 +02:00
James Cole
bdff275672 Simplified report code. 2015-05-16 13:06:38 +02:00
James Cole
bec58a1ee6 Experimental test coverage. 2015-05-16 10:34:38 +02:00
James Cole
f64616748c Expenses only [skip ci] 2015-05-16 10:18:32 +02:00
James Cole
512ce15973 Small tweaks in charts. 2015-05-16 10:13:41 +02:00
James Cole
ed8b301574 Routes and JS cleanup. 2015-05-16 10:05:22 +02:00
James Cole
d22a6c019c Fixed lots of chart references. 2015-05-16 09:57:31 +02:00
James Cole
a0cb1b9d9e Clean up and split chart controllers 2015-05-16 09:41:14 +02:00
James Cole
a5294c62ea Renamed a method. 2015-05-16 09:26:54 +02:00
James Cole
e155d3311c Fixed tests 2015-05-16 09:25:14 +02:00
James Cole
0a372b0daf Cleanup tests 2015-05-16 09:16:32 +02:00
James Cole
69143399d1 Moved some stuff around. 2015-05-16 09:09:52 +02:00
James Cole
3270d3bf96 Cleanup. 2015-05-16 08:14:26 +02:00
James Cole
3896a66122 Fixed tests. 2015-05-16 08:09:15 +02:00
James Cole
b94781aef1 Some optimisations. 2015-05-16 08:05:04 +02:00
James Cole
bed1adc367 Fixed budget. 2015-05-16 07:56:15 +02:00
James Cole
ae54497efa Differ between shared and normal transactions (not build yet). 2015-05-16 07:49:02 +02:00
James Cole
06b747c221 Some cleanup and translations. 2015-05-16 07:28:58 +02:00
James Cole
f159beee0d Fixed preferences tests. 2015-05-15 23:24:55 +02:00
James Cole
49d7dea086 Fixed some coverage. 2015-05-15 23:02:42 +02:00
James Cole
3e65733dc5 Small fix in report view [skip ci] 2015-05-15 22:12:31 +02:00
James Cole
cc375d58bb Fixed tests. 2015-05-15 22:06:52 +02:00
James Cole
911c7c662a Expanded reports. 2015-05-15 22:00:00 +02:00
James Cole
aae003be33 Some more cleaning up and fixing 2015-05-15 21:01:24 +02:00
James Cole
aede03d8b2 Some more layout changes. 2015-05-15 20:43:50 +02:00
James Cole
f0f5ada7de More changes to reports. 2015-05-15 20:38:39 +02:00
James Cole
58365121a3 Fixed tests. 2015-05-15 20:15:58 +02:00
James Cole
d5a154d2e6 Cleaned up reports and associated views. 2015-05-15 20:07:51 +02:00
James Cole
b20f369aef Some new tests. 2015-05-15 18:39:59 +02:00
James Cole
abb8aa0b29 Merge branch 'feature/translations' into develop 2015-05-15 18:38:26 +02:00
James Cole
5368a0f1d7 Some new translations. 2015-05-14 19:44:20 +02:00
James Cole
d3897eece7 This should fix the tests on the translations branch. 2015-05-14 18:15:31 +02:00
James Cole
a82b829da9 More translations. 2015-05-14 18:00:56 +02:00
James Cole
9f5058e81a Some new translations. 2015-05-14 17:52:50 +02:00
James Cole
5b19263720 Some more [skip ci] 2015-05-14 16:43:12 +02:00
James Cole
9d5a0db0d9 [skip ci] 2015-05-14 16:36:08 +02:00
James Cole
4bd8a7014f Count things [skip ci] 2015-05-14 16:35:41 +02:00
James Cole
353e96d951 Better (and translated) delete forms. [skip ci] 2015-05-14 16:32:35 +02:00
James Cole
149a6f92b0 Translations [skip ci] 2015-05-14 15:56:23 +02:00
James Cole
d66426c137 Nieuw vertalingen [skip ci] 2015-05-14 15:53:56 +02:00
James Cole
4fc9966392 Chart translations [skip ci] 2015-05-14 13:43:58 +02:00
James Cole
417766f0db Various date localisation. [skip ci] 2015-05-14 13:41:21 +02:00
James Cole
de9ac97887 Also english. [skip ci] 2015-05-14 13:19:26 +02:00
James Cole
6be42f112a Charts and titles in Dutch. [skip ci] 2015-05-14 13:17:53 +02:00
James Cole
3895ae63c7 Some translations for lists [skip ci] 2015-05-14 13:00:43 +02:00
James Cole
607d416d54 Better translation for 'balance' [skip ci] 2015-05-14 12:11:56 +02:00
James Cole
038693dc86 More index translations [skip ci] 2015-05-14 12:10:42 +02:00
James Cole
cc9be13544 Added a bread crumb [skip ci] 2015-05-14 11:50:48 +02:00
James Cole
9c1474087f Forgot one [skip ci] 2015-05-14 11:50:04 +02:00
James Cole
831de2bcf4 New menu translations [skip ci] 2015-05-14 11:48:59 +02:00
James Cole
eb687333bb Small edits to buttons and others. [skip ci] 2015-05-14 11:31:54 +02:00
James Cole
5cc7966d54 Some new form things. [skip ci] 2015-05-14 11:25:43 +02:00
James Cole
d1a34e7a6f Breadcrumbs translated to English. [skip ci] 2015-05-14 11:22:20 +02:00
James Cole
d63d791717 Translated all bread crumbs to Dutch. [skip ci] 2015-05-14 11:17:56 +02:00
James Cole
015570c741 Small translations as a basic start [skip ci] 2015-05-14 10:26:15 +02:00
James Cole
8400ebc9c6 Added a language test, added Dutch basic file. [skip ci] 2015-05-14 10:03:57 +02:00
James Cole
9f99e7c0af Edit and set language [skip ci] 2015-05-14 09:59:30 +02:00
James Cole
392c1fc399 Add language selector to preferences [skip ci] 2015-05-14 09:56:41 +02:00
James Cole
d3cea7a89c Add new supported languages [skip ci] 2015-05-14 09:52:58 +02:00
James Cole
1dcf7407e6 Cleanup [skip ci] 2015-05-14 09:51:54 +02:00
James Cole
d543c033a3 Small fixes to translations. 2015-05-14 09:50:07 +02:00
James Cole
aaa186be5e Merge branch 'release/3.4.0.6' 2015-05-14 09:21:23 +02:00
James Cole
2054b5b3dd Merge branch 'release/3.4.0.6' into develop 2015-05-14 09:21:23 +02:00
James Cole
98ae5b0ca0 New read me. 2015-05-14 09:21:12 +02:00
James Cole
36c8171d0f Clean up and tests. 2015-05-14 09:01:10 +02:00
James Cole
3603eb94cc Different budget query [skip ci] 2015-05-14 08:08:25 +02:00
James Cole
0e068d4ccf Fixed some tests. 2015-05-13 21:37:11 +02:00
James Cole
199f348ff4 Smaller mobile pages. 2015-05-13 21:32:33 +02:00
James Cole
b22655fb7c Fix helper 2015-05-13 21:32:27 +02:00
James Cole
06cc9618ba Some new tests 2015-05-13 21:32:21 +02:00
James Cole
b9019c8c7f Smaller screens update [skip ci] 2015-05-13 06:34:17 +02:00
James Cole
77e133e67c Some cleaning up for mobile screens [skip ci] 2015-05-13 06:31:32 +02:00
James Cole
571e7df807 Some tiny optimisations. 2015-05-12 20:51:03 +02:00
James Cole
22f4d2979a Added some more ignores, edited some middleware. 2015-05-11 22:12:53 +02:00
James Cole
e46e366694 Added some tests to cover an event. 2015-05-11 21:45:31 +02:00
James Cole
9ca79f767c Merge branch 'release/3.4.0.5' into develop 2015-05-10 19:18:57 +02:00
203 changed files with 7471 additions and 4204 deletions

View File

@@ -1,5 +1,5 @@
# Firefly III # Firefly III
#### v3.4.0.5 #### v3.4.0.8
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii) [![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii) [![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
@@ -13,11 +13,13 @@
"Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared "Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared
household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money. household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money.
Firefly is a system you'll have install yourself on webhosting of your choosing. _Firefly is a system you'll have install yourself on webhosting of your choosing._
Personal financial management is pretty difficult, and everybody has their own approach to it. Some people Personal financial management is pretty difficult, and everybody has their own approach to it. Some people
make budgets, other people limit their cashflow by throwing away their credit cards, others get a better job. make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase
There are tons of ways to save and earn money. 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: To get to know Firefly, and to see if it fits you, check out these resources:
@@ -28,24 +30,15 @@ To get to know Firefly, and to see if it fits you, check out these resources:
and the philosophy behind it. and the philosophy behind it.
### About the name (if you care) #### About the name (if you care)
It's III, or 3, because [version 2](https://github.com/JC5/Firefly) and version 1 (not online) preceded it. It has been growing steadily ever since. It's III, or 3, because [version 2](https://github.com/JC5/Firefly) and version 1 (not online) preceded it. It has been growing steadily ever since.
## Running and installing
If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable)
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site.
## Current features ## Current features
- [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system); - [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system);
- You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management; - You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management;
- You can manage different types of accounts - You can manage different types of accounts;
- [Asset](https://en.wikipedia.org/wiki/Asset) accounts - [Asset](https://en.wikipedia.org/wiki/Asset) accounts
- Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household)) - Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household))
- Saving accounts - Saving accounts
@@ -63,8 +56,8 @@ Everything is organised:
- Clear views that should show you how you're doing; - Clear views that should show you how you're doing;
- Easy navigation through your records; - Easy navigation through your records;
- Browse back and forth to see previous months or even years; - Browse back and forth to see previous months or even years;
- Lots of charts because we all love them. - Lots of charts because we all love them;
- Financial reporting showing you how well you are doing; - Financial reporting showing you how well you are doing.
## Screenshots ## Screenshots
@@ -84,6 +77,16 @@ _Please note that everything in these screenshots is fictional and may not be re
![Piggy banks](https://i.nder.be/hkud0h53) ![Piggy banks](https://i.nder.be/hkud0h53)
## Running and installing
If you're still interested please read [the installation guide](https://github.com/JC5/firefly-iii/wiki/Installation),
[the upgrade guide](https://github.com/JC5/firefly-iii/wiki/Upgrade-instructions) (if applicable)
and the **[first use guide](https://github.com/JC5/firefly-iii/wiki/First-use)**.
If you want to try out Firefly III, you can do so on [this dedicated website](https://geld.nder.be/).
This site always runs the latest version of Firefly III. If you want to use it, please read the [privacy considerations](https://github.com/JC5/firefly-iii/wiki/Privacy-on-demo-site) for this demo-site.
## Current state ## Current state
Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. One of the things on the todo-list Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. One of the things on the todo-list

View File

@@ -26,6 +26,8 @@ class Kernel extends ConsoleKernel
* *
* @param \Illuminate\Console\Scheduling\Schedule $schedule * @param \Illuminate\Console\Scheduling\Schedule $schedule
* *
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return void * @return void
*/ */
protected function schedule(Schedule $schedule) protected function schedule(Schedule $schedule)

View File

@@ -27,6 +27,7 @@ class Handler extends ExceptionHandler
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param \Exception $e * @param \Exception $e
* @SuppressWarnings(PHPMD.ShortVariable)
* *
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
@@ -43,6 +44,7 @@ class Handler extends ExceptionHandler
* Report or log an exception. * Report or log an exception.
* *
* This is a great spot to send exceptions to Sentry, Bugsnag, etc. * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
* @SuppressWarnings(PHPMD.ShortVariable)
* *
* @param \Exception $e * @param \Exception $e
* *

View File

@@ -6,7 +6,6 @@ use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Log;
/** /**
* Class ConnectJournalToPiggyBank * Class ConnectJournalToPiggyBank
@@ -28,54 +27,36 @@ class ConnectJournalToPiggyBank
/** /**
* Handle the event when journal is saved. * Handle the event when journal is saved.
* *
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param JournalCreated $event * @param JournalCreated $event
* *
* @return void * @return boolean
*/ */
public function handle(JournalCreated $event) public function handle(JournalCreated $event)
{ {
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
$journal = $event->journal; $journal = $event->journal;
$piggyBankId = $event->piggyBankId; $piggyBankId = $event->piggyBankId;
if (intval($piggyBankId) < 1) {
return;
}
Log::debug('JournalCreated event: ' . $journal->id . ', ' . $piggyBankId);
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
$piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); $piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']);
if (is_null($piggyBank) || $journal->transactionType->type != 'Transfer') { if (is_null($piggyBank)) {
return; return false;
}
Log::debug('Found a piggy bank');
$amount = $journal->amount;
Log::debug('Amount: ' . $amount);
if ($amount == 0) {
return;
} }
// update piggy bank rep for date of transaction journal. // update piggy bank rep for date of transaction journal.
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
if (is_null($repetition)) { if (is_null($repetition)) {
Log::debug('Found no repetition for piggy bank for date ' . $journal->date->format('Y M d')); return false;
return;
} }
Log::debug('Found rep! ' . $repetition->id); $amount = $journal->amount;
/*
* Add amount when
*/
/** @var Transaction $transaction */ /** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) { foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id == $piggyBank->account_id) { if ($transaction->account_id == $piggyBank->account_id) {
if ($transaction->amount < 0) { if ($transaction->amount < 0) {
$amount = $amount * -1; $amount = $transaction->amount * -1;
Log::debug('Transaction is away from piggy, so amount becomes ' . $amount);
} else {
Log::debug('Transaction is to from piggy, so amount stays ' . $amount);
} }
} }
} }
@@ -83,14 +64,9 @@ class ConnectJournalToPiggyBank
$repetition->currentamount += $amount; $repetition->currentamount += $amount;
$repetition->save(); $repetition->save();
PiggyBankEvent::create( PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]);
[
'piggy_bank_id' => $piggyBank->id, return true;
'transaction_journal_id' => $journal->id,
'date' => $journal->date,
'amount' => $amount
]
);
} }

View File

@@ -1,36 +0,0 @@
<?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalDeleted;
/**
* Class JournalDeletedHandler
*
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events
*/
class JournalDeletedHandler
{
/**
* Create the event handler.
*
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param JournalDeleted $event
*
* @return void
*/
public function handle(JournalDeleted $event)
{
//
}
}

View File

@@ -7,6 +7,7 @@ use Log;
/** /**
* Class RescanJournal * Class RescanJournal
* *
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events * @package FireflyIII\Handlers\Events
*/ */
class RescanJournal class RescanJournal

View File

@@ -2,10 +2,12 @@
use FireflyIII\Events\JournalSaved; use FireflyIII\Events\JournalSaved;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
/** /**
* Class UpdateJournalConnection * Class UpdateJournalConnection
* *
* @codeCoverageIgnore
* @package FireflyIII\Handlers\Events * @package FireflyIII\Handlers\Events
*/ */
class UpdateJournalConnection class UpdateJournalConnection
@@ -38,7 +40,11 @@ class UpdateJournalConnection
return; return;
} }
$piggyBank = $event->piggyBank()->first(); $piggyBank = $event->piggyBank()->first();
$repetition = null;
if ($piggyBank) {
/** @var PiggyBankRepetition $repetition */
$repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first();
}
if (is_null($repetition)) { if (is_null($repetition)) {
return; return;

View File

@@ -0,0 +1,90 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
* Class Account
*
* @package FireflyIII\Helpers\Collection
*/
class Account
{
/** @var Collection */
protected $accounts;
/** @var float */
protected $difference;
/** @var float */
protected $end;
/** @var float */
protected $start;
/**
* @return \Illuminate\Support\Collection
*/
public function getAccounts()
{
return $this->accounts;
}
/**
* @param \Illuminate\Support\Collection $accounts
*/
public function setAccounts($accounts)
{
$this->accounts = $accounts;
}
/**
* @return float
*/
public function getDifference()
{
return $this->difference;
}
/**
* @param float $difference
*/
public function setDifference($difference)
{
$this->difference = $difference;
}
/**
* @return float
*/
public function getEnd()
{
return $this->end;
}
/**
* @param float $end
*/
public function setEnd($end)
{
$this->end = $end;
}
/**
* @return float
*/
public function getStart()
{
return $this->start;
}
/**
* @param float $start
*/
public function setStart($start)
{
$this->start = $start;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class Balance
*
* @package FireflyIII\Helpers\Collection
*/
class Balance
{
/** @var BalanceHeader */
protected $balanceHeader;
/** @var Collection */
protected $balanceLines;
/**
*
*/
public function __construct()
{
$this->balanceLines = new Collection;
}
/**
* @param BalanceLine $line
*/
public function addBalanceLine(BalanceLine $line)
{
$this->balanceLines->push($line);
}
/**
* @return BalanceHeader
*/
public function getBalanceHeader()
{
return $this->balanceHeader;
}
/**
* @param BalanceHeader $balanceHeader
*/
public function setBalanceHeader($balanceHeader)
{
$this->balanceHeader = $balanceHeader;
}
/**
* @return \Illuminate\Support\Collection
*/
public function getBalanceLines()
{
return $this->balanceLines;
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Account as AccountModel;
/**
* @codeCoverageIgnore
*
* Class BalanceEntry
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceEntry
{
/** @var AccountModel */
protected $account;
/** @var float */
protected $left = 0.0;
/** @var float */
protected $spent = 0.0;
/**
* @return AccountModel
*/
public function getAccount()
{
return $this->account;
}
/**
* @param AccountModel $account
*/
public function setAccount($account)
{
$this->account = $account;
}
/**
* @return float
*/
public function getLeft()
{
return $this->left;
}
/**
* @param float $left
*/
public function setLeft($left)
{
$this->left = $left;
}
/**
* @return float
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param float $spent
*/
public function setSpent($spent)
{
$this->spent = $spent;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Account as AccountModel;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class BalanceHeader
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceHeader
{
/** @var Collection */
protected $accounts;
/**
*
*/
public function __construct()
{
$this->accounts = new Collection;
}
/**
* @param AccountModel $account
*/
public function addAccount(AccountModel $account)
{
$this->accounts->push($account);
}
/**
* @return Collection
*/
public function getAccounts()
{
return $this->accounts;
}
}

View File

@@ -0,0 +1,170 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class BalanceLine
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceLine
{
const ROLE_DEFAULTROLE = 1;
const ROLE_TAGROLE = 2;
const ROLE_DIFFROLE = 3;
/** @var Collection */
protected $balanceEntries;
/** @var BudgetModel */
protected $budget;
/** @var LimitRepetition */
protected $repetition;
protected $role = self::ROLE_DEFAULTROLE;
/**
*
*/
public function __construct()
{
$this->balanceEntries = new Collection;
}
/**
* @param BalanceEntry $balanceEntry
*/
public function addBalanceEntry(BalanceEntry $balanceEntry)
{
$this->balanceEntries->push($balanceEntry);
}
/**
* @return string
*/
public function getTitle()
{
if ($this->getBudget() instanceof BudgetModel) {
return $this->getBudget()->name;
}
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
return trans('firefly.noBudget');
}
if ($this->getRole() == self::ROLE_TAGROLE) {
return trans('firefly.coveredWithTags');
}
if ($this->getRole() == self::ROLE_DIFFROLE) {
return trans('firefly.leftUnbalanced');
}
}
/**
* @return BudgetModel
*/
public function getBudget()
{
return $this->budget;
}
/**
* @param BudgetModel $budget
*/
public function setBudget($budget)
{
$this->budget = $budget;
}
/**
* @return int
*/
public function getRole()
{
return $this->role;
}
/**
* @param int $role
*/
public function setRole($role)
{
$this->role = $role;
}
/**
* If a BalanceLine has a budget/repetition, each BalanceEntry in this BalanceLine
* should have a "spent" value, which is the amount of money that has been spent
* on the given budget/repetition. If you subtract all those amounts from the budget/repetition's
* total amount, this is returned:
*
* @return float
*/
public function leftOfRepetition()
{
$start = $this->getRepetition() ? $this->getRepetition()->amount : 0;
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$start += $balanceEntry->getSpent();
}
return $start;
}
/**
* @return LimitRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param LimitRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
/**
* @return Collection
*/
public function getBalanceEntries()
{
return $this->balanceEntries;
}
/**
* @param Collection $balanceEntries
*/
public function setBalanceEntries($balanceEntries)
{
$this->balanceEntries = $balanceEntries;
}
/**
* If the BalanceEntries for a BalanceLine have a "left" value, the amount
* of money left in the entire BalanceLine is returned here:
*
* @return float
*/
public function sumOfLeft()
{
$sum = 0.0;
/** @var BalanceEntry $balanceEntry */
foreach ($this->getBalanceEntries() as $balanceEntry) {
$sum += $balanceEntry->getLeft();
}
return $sum;
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Bill as BillModel;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
* Class Bill
*
* @package FireflyIII\Helpers\Collection
*/
class Bill
{
/**
* @var Collection
*/
protected $bills;
/**
*
*/
public function __construct()
{
$this->bills = new Collection;
}
/**
* @param BillLine $bill
*/
public function addBill(BillLine $bill)
{
$this->bills->push($bill);
}
/**
* @return Collection
*/
public function getBills()
{
$this->bills->sortBy(
function (BillLine $bill) {
$active = intval($bill->getBill()->active) == 0 ? 1 : 0;
$name = $bill->getBill()->name;
return $active . $name;
}
);
return $this->bills;
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Bill as BillModel;
/**
* @codeCoverageIgnore
*
* Class BillLine
*
* @package FireflyIII\Helpers\Collection
*/
class BillLine
{
/** @var bool */
protected $active;
/** @var float */
protected $amount;
/** @var BillModel */
protected $bill;
/** @var bool */
protected $hit;
/** @var float */
protected $max;
/** @var float */
protected $min;
/**
* @return float
*/
public function getAmount()
{
return $this->amount;
}
/**
* @param float $amount
*/
public function setAmount($amount)
{
$this->amount = $amount;
}
/**
* @return BillModel
*/
public function getBill()
{
return $this->bill;
}
/**
* @param BillModel $bill
*/
public function setBill($bill)
{
$this->bill = $bill;
}
/**
* @return float
*/
public function getMax()
{
return $this->max;
}
/**
* @param float $max
*/
public function setMax($max)
{
$this->max = $max;
}
/**
* @return float
*/
public function getMin()
{
return $this->min;
}
/**
* @param float $min
*/
public function setMin($min)
{
$this->min = $min;
}
/**
* @return boolean
*/
public function isActive()
{
return $this->active;
}
/**
* @param boolean $active
*/
public function setActive($active)
{
$this->active = $active;
}
/**
* @return boolean
*/
public function isHit()
{
return $this->hit;
}
/**
* @param boolean $hit
*/
public function setHit($hit)
{
$this->hit = $hit;
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class Budget
*
* @package FireflyIII\Helpers\Collection
*/
class Budget
{
/** @var Collection */
protected $budgetLines;
/** @var float */
protected $budgeted = 0;
/** @var float */
protected $left = 0;
/** @var float */
protected $overspent = 0;
/** @var float */
protected $spent = 0;
/**
*
*/
public function __construct()
{
$this->budgetLines = new Collection;
}
/**
* @param BudgetLine $budgetLine
*/
public function addBudgetLine(BudgetLine $budgetLine)
{
$this->budgetLines->push($budgetLine);
}
/**
* @param float $add
*/
public function addBudgeted($add)
{
$this->budgeted += floatval($add);
}
/**
* @param float $add
*/
public function addLeft($add)
{
$this->left += floatval($add);
}
/**
* @param float $add
*/
public function addOverspent($add)
{
$this->overspent += floatval($add);
}
/**
* @param float $add
*/
public function addSpent($add)
{
$this->spent += floatval($add);
}
/**
* @return \Illuminate\Support\Collection
*/
public function getBudgetLines()
{
return $this->budgetLines;
}
/**
* @return float
*/
public function getBudgeted()
{
return $this->budgeted;
}
/**
* @param float $budgeted
*/
public function setBudgeted($budgeted)
{
$this->budgeted = $budgeted;
}
/**
* @return float
*/
public function getLeft()
{
return $this->left;
}
/**
* @param float $left
*/
public function setLeft($left)
{
$this->left = $left;
}
/**
* @return float
*/
public function getOverspent()
{
return $this->overspent;
}
/**
* @param float $overspent
*/
public function setOverspent($overspent)
{
$this->overspent = $overspent;
}
/**
* @return float
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param float $spent
*/
public function setSpent($spent)
{
$this->spent = $spent;
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
/**
* @codeCoverageIgnore
*
* Class BudgetLine
*
* @package FireflyIII\Helpers\Collection
*/
class BudgetLine
{
/** @var BudgetModel */
protected $budget;
/** @var LimitRepetition */
protected $repetition;
/** @var float */
protected $budgeted = 0;
/** @var float */
protected $left = 0;
/** @var float */
protected $overspent = 0;
/** @var float */
protected $spent = 0;
/**
* @return BudgetModel
*/
public function getBudget()
{
return $this->budget;
}
/**
* @param BudgetModel $budget
*/
public function setBudget($budget)
{
$this->budget = $budget;
}
/**
* @return float
*/
public function getBudgeted()
{
return $this->budgeted;
}
/**
* @param float $budgeted
*/
public function setBudgeted($budgeted)
{
$this->budgeted = $budgeted;
}
/**
* @return float
*/
public function getLeft()
{
return $this->left;
}
/**
* @param float $left
*/
public function setLeft($left)
{
$this->left = $left;
}
/**
* @return float
*/
public function getOverspent()
{
return $this->overspent;
}
/**
* @param float $overspent
*/
public function setOverspent($overspent)
{
$this->overspent = $overspent;
}
/**
* @return float
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param float $spent
*/
public function setSpent($spent)
{
$this->spent = $spent;
}
/**
* @return LimitRepetition
*/
public function getRepetition()
{
return $this->repetition;
}
/**
* @param LimitRepetition $repetition
*/
public function setRepetition($repetition)
{
$this->repetition = $repetition;
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 16/05/15
* Time: 13:09
*/
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Category as CategoryModel;
use Illuminate\Support\Collection;
/**
* @codeCoverageIgnore
*
* Class Category
*
* @package FireflyIII\Helpers\Collection
*/
class Category
{
/** @var Collection */
protected $categories;
/** @var float */
protected $total = 0;
/**
*
*/
public function __construct()
{
$this->categories = new Collection;
}
/**
* @param CategoryModel $category
*/
public function addCategory(CategoryModel $category)
{
if ($category->spent > 0) {
$this->categories->push($category);
}
}
/**
* @param float $add
*/
public function addTotal($add)
{
$this->total += floatval($add);
}
/**
* @return Collection
*/
public function getCategories()
{
$this->categories->sortByDesc(
function (CategoryModel $category) {
return $category->spent;
}
);
return $this->categories;
}
/**
* @return float
*/
public function getTotal()
{
return $this->total;
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use stdClass;
/**
* @codeCoverageIgnore
*
* Class Expense
*
* @package FireflyIII\Helpers\Collection
*/
class Expense
{
/** @var Collection */
protected $expenses;
/** @var float */
protected $total;
/**
*
*/
public function __construct()
{
$this->expenses = new Collection;
}
/**
* @param TransactionJournal $entry
*/
public function addOrCreateExpense(TransactionJournal $entry)
{
$accountId = $entry->account_id;
if (!$this->expenses->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = floatval($entry->amount);
$newObject->name = $entry->name;
$newObject->count = 1;
$newObject->id = $accountId;
$this->expenses->put($accountId, $newObject);
} else {
$existing = $this->expenses->get($accountId);
$existing->amount += floatval($entry->amount);
$existing->count++;
$this->expenses->put($accountId, $existing);
}
}
/**
* @param $add
*/
public function addToTotal($add)
{
$this->total += floatval($add);
}
/**
* @return Collection
*/
public function getExpenses()
{
$this->expenses->sortByDesc(
function (stdClass $object) {
return $object->amount;
}
);
return $this->expenses;
}
/**
* @return float
*/
public function getTotal()
{
return $this->total;
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use stdClass;
/**
* @codeCoverageIgnore
*
* Class Income
*
* @package FireflyIII\Helpers\Collection
*/
class Income
{
/** @var Collection */
protected $incomes;
/** @var float */
protected $total;
/**
*
*/
public function __construct()
{
$this->incomes = new Collection;
}
/**
* @param TransactionJournal $entry
*/
public function addOrCreateIncome(TransactionJournal $entry)
{
$accountId = $entry->account_id;
if (!$this->incomes->has($accountId)) {
$newObject = new stdClass;
$newObject->amount = floatval($entry->amount);
$newObject->name = $entry->name;
$newObject->count = 1;
$newObject->id = $accountId;
$this->incomes->put($accountId, $newObject);
} else {
$existing = $this->incomes->get($accountId);
$existing->amount += floatval($entry->amount);
$existing->count++;
$this->incomes->put($accountId, $existing);
}
}
/**
* @param $add
*/
public function addToTotal($add)
{
$this->total += floatval($add);
}
/**
* @return Collection
*/
public function getIncomes()
{
$this->incomes->sortByDesc(
function (stdClass $object) {
return $object->amount;
}
);
return $this->incomes;
}
/**
* @return float
*/
public function getTotal()
{
return $this->total;
}
}

View File

@@ -17,6 +17,8 @@ class Help implements HelpInterface
{ {
/** /**
* @codeCoverageIgnore
*
* @param $key * @param $key
* *
* @return string * @return string
@@ -27,13 +29,15 @@ class Help implements HelpInterface
} }
/** /**
* @codeCoverageIgnore
*
* @param $route * @param $route
* *
* @return array * @return array
*/ */
public function getFromGithub($route) public function getFromGithub($route)
{ {
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md'; $uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/en/' . e($route) . '.md';
$content = [ $content = [
'text' => '<p>There is no help for this route!</p>', 'text' => '<p>There is no help for this route!</p>',
'title' => $route, 'title' => $route,
@@ -54,6 +58,8 @@ class Help implements HelpInterface
} }
/** /**
* @codeCoverageIgnore
*
* @param $route * @param $route
* *
* @return bool * @return bool
@@ -64,6 +70,8 @@ class Help implements HelpInterface
} }
/** /**
* @codeCoverageIgnore
*
* @param $route * @param $route
* @param array $content * @param array $content
* *
@@ -76,6 +84,8 @@ class Help implements HelpInterface
} }
/** /**
* @codeCoverageIgnore
*
* @param $route * @param $route
* *
* @return bool * @return bool

View File

@@ -33,7 +33,7 @@ class ReminderHelper implements ReminderHelperInterface
$ranges = $this->getReminderRanges($piggyBank, $start); $ranges = $this->getReminderRanges($piggyBank, $start);
$currentRep = $piggyBank->currentRelevantRep(); $currentRep = $piggyBank->currentRelevantRep();
$left = $piggyBank->targetamount - $currentRep->currentamount; $left = $piggyBank->targetamount - $currentRep->currentamount;
$perReminder = $left / count($ranges); $perReminder = count($ranges) == 0 ? $left : $left / count($ranges);
} else { } else {
$perReminder = null; $perReminder = null;
$ranges = []; $ranges = [];
@@ -46,8 +46,6 @@ class ReminderHelper implements ReminderHelperInterface
'leftToSave' => $left, 'leftToSave' => $left,
]; ];
// create one:
$reminder = new Reminder; $reminder = new Reminder;
$reminder->user()->associate(Auth::user()); $reminder->user()->associate(Auth::user());
$reminder->startdate = $start; $reminder->startdate = $start;
@@ -65,6 +63,30 @@ class ReminderHelper implements ReminderHelperInterface
} }
} }
/**
* Create all reminders for a piggy bank for a given date.
*
* @param PiggyBank $piggyBank
*
* @param Carbon $date
*
* @return mixed
*/
public function createReminders(PiggyBank $piggyBank, Carbon $date)
{
$ranges = $this->getReminderRanges($piggyBank);
foreach ($ranges as $range) {
if ($date < $range['end'] && $date > $range['start']) {
// create a reminder here!
$this->createReminder($piggyBank, $range['start'], $range['end']);
// stop looping, we're done.
break;
}
}
}
/** /**
* This routine will return an array consisting of two dates which indicate the start * This routine will return an array consisting of two dates which indicate the start
* and end date for each reminder that this piggy bank will have, if the piggy bank has * and end date for each reminder that this piggy bank will have, if the piggy bank has

View File

@@ -4,6 +4,7 @@ namespace FireflyIII\Helpers\Reminders;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\Reminder; use FireflyIII\Models\Reminder;
/** /**
@@ -49,4 +50,15 @@ interface ReminderHelperInterface
* @return Reminder * @return Reminder
*/ */
public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end); public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end);
/**
* Create all reminders for a piggy bank for a given date.
*
* @param PiggyBank $piggyBank
*
* @param Carbon $date
*
* @return mixed
*/
public function createReminders(PiggyBank $piggyBank, Carbon $date);
} }

View File

@@ -3,12 +3,23 @@
namespace FireflyIII\Helpers\Report; namespace FireflyIII\Helpers\Report;
use App; use App;
use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\BalanceEntry;
use FireflyIII\Helpers\Collection\BalanceHeader;
use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Helpers\Collection\Bill as BillCollection;
use FireflyIII\Helpers\Collection\BillLine;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\BudgetLine;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use Illuminate\Database\Query\JoinClause; use FireflyIII\Models\Bill;
use Illuminate\Support\Collection; use FireflyIII\Models\Budget as BudgetModel;
use Steam; use FireflyIII\Models\LimitRepetition;
/** /**
* Class ReportHelper * Class ReportHelper
@@ -18,66 +29,345 @@ use Steam;
class ReportHelper implements ReportHelperInterface class ReportHelper implements ReportHelperInterface
{ {
/** @var ReportQueryInterface */
protected $query;
/** /**
* This methods fails to take in account transfers FROM shared accounts. * @codeCoverageIgnore
* @param ReportQueryInterface $query
*
*/
public function __construct(ReportQueryInterface $query)
{
$this->query = $query;
}
/**
* This method generates a full report for the given period on all
* the users asset and cash accounts.
*
* @param Carbon $date
* @param Carbon $end
* @param $shared
*
* @return Account
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared)
{
$accounts = $this->query->getAllAccounts($date, $end, $shared);
$start = 0;
$end = 0;
$diff = 0;
// remove cash account, if any:
$accounts =$accounts->filter(function(Account $account) {
if($account->accountType->type != 'Cash account') {
return $account;
}
});
// summarize:
foreach ($accounts as $account) {
$start += $account->startBalance;
$end += $account->endBalance;
$diff += ($account->endBalance - $account->startBalance);
}
$object = new AccountCollection;
$object->setStart($start);
$object->setEnd($end);
$object->setDifference($diff);
$object->setAccounts($accounts);
return $object;
}
/**
*
* The balance report contains a Balance object which in turn contains:
*
* A BalanceHeader object which contains all relevant user asset accounts for the report.
*
* A number of BalanceLine objects, which hold:
* - A budget
* - A number of BalanceEntry objects.
*
* The BalanceEntry object holds:
* - The same budget (again)
* - A user asset account as mentioned in the BalanceHeader
* - The amount of money spent on the budget by the user asset account
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param int $limit * @param boolean $shared
* *
* @return Collection * @return Balance
*/ */
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15) public function getBalanceReport(Carbon $start, Carbon $end, $shared)
{ {
$result = $this->_queries->journalsByExpenseAccount($start, $end); $repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$array = $this->_helper->makeArray($result); $tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface');
$limited = $this->_helper->limitArray($array, $limit); $balance = new Balance;
return $limited; // build a balance header:
$header = new BalanceHeader;
$accounts = $this->query->getAllAccounts($start, $end, $shared);
$budgets = $repository->getBudgets();
foreach ($accounts as $account) {
$header->addAccount($account);
}
/** @var BudgetModel $budget */
foreach ($budgets as $budget) {
$line = new BalanceLine;
$line->setBudget($budget);
// get budget amount for current period:
$rep = $repository->getCurrentRepetition($budget, $start);
$line->setRepetition($rep);
// loop accounts:
foreach ($accounts as $account) {
$balanceEntry = new BalanceEntry;
$balanceEntry->setAccount($account);
// get spent:
$spent = $this->query->spentInBudgetCorrected($account, $budget, $start, $end); // I think shared is irrelevant.
$balanceEntry->setSpent($spent);
$line->addBalanceEntry($balanceEntry);
}
// add line to balance:
$balance->addBalanceLine($line);
}
// then a new line for without budget.
// and one for the tags:
$empty = new BalanceLine;
$tags = new BalanceLine;
$diffLine = new BalanceLine;
$tags->setRole(BalanceLine::ROLE_TAGROLE);
$diffLine->setRole(BalanceLine::ROLE_DIFFROLE);
foreach ($accounts as $account) {
$spent = $this->query->spentNoBudget($account, $start, $end);
$left = $tagRepository->coveredByBalancingActs($account, $start, $end);
$diff = $spent + $left;
// budget
$budgetEntry = new BalanceEntry;
$budgetEntry->setAccount($account);
$budgetEntry->setSpent($spent);
$empty->addBalanceEntry($budgetEntry);
// balanced by tags
$tagEntry = new BalanceEntry;
$tagEntry->setAccount($account);
$tagEntry->setLeft($left);
$tags->addBalanceEntry($tagEntry);
// difference:
$diffEntry = new BalanceEntry;
$diffEntry->setAccount($account);
$diffEntry->setSpent($diff);
$diffLine->addBalanceEntry($diffEntry);
}
$balance->addBalanceLine($empty);
$balance->addBalanceLine($tags);
$balance->addBalanceLine($diffLine);
$balance->setBalanceHeader($header);
return $balance;
}
/**
* This method generates a full report for the given period on all
* the users bills and their payments.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return BillCollection
*/
public function getBillReport(Carbon $start, Carbon $end, $shared)
{
/** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Bill\BillRepositoryInterface');
$bills = $repository->getBills();
$collection = new BillCollection;
/** @var Bill $bill */
foreach ($bills as $bill) {
$billLine = new BillLine;
$billLine->setBill($bill);
$billLine->setActive(intval($bill->active) == 1);
$billLine->setMin(floatval($bill->amount_min));
$billLine->setMax(floatval($bill->amount_max));
// is hit in period?
$set = $repository->getJournalsInRange($bill, $start, $end);
if ($set->count() == 0) {
$billLine->setHit(false);
} else {
$billLine->setHit(true);
$amount = 0;
foreach ($set as $entry) {
$amount += $entry->amount;
}
$billLine->setAmount($amount);
}
$collection->addBill($billLine);
}
return $collection;
} }
/** /**
* This method gets some kind of list for a monthly overview. * @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* *
* @param Carbon $date * @return BudgetCollection
* @param bool $showSharedReports
*
* @return Collection
*/ */
public function getBudgetsForMonth(Carbon $date, $showSharedReports = false) public function getBudgetReport(Carbon $start, Carbon $end, $shared)
{ {
/** @var \FireflyIII\Helpers\Report\ReportQueryInterface $query */ $object = new BudgetCollection;
$query = App::make('FireflyIII\Helpers\Report\ReportQueryInterface'); /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$set = $repository->getBudgets();
$start = clone $date; foreach ($set as $budget) {
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
$set = Auth::user()->budgets()->orderBy('budgets.name', 'ASC')
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as queryAmount']);
$budgets = Steam::makeArray($set); $repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
$amountSet = $query->journalsByBudget($start, $end, $showSharedReports);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['queryAmount'] = isset($budgets[0]['queryAmount']) ? $budgets[0]['queryAmount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared asset accounts, which are without a budget by default: // no repetition(s) for this budget:
// which is only relevant when shared asset accounts are hidden. if ($repetitions->count() == 0) {
if ($showSharedReports === false) { $spent = $repository->spentInPeriodCorrected($budget, $start, $end, $shared);
$transfers = $query->sharedExpenses($start, $end)->sum('queryAmount'); $budgetLine = new BudgetLine;
$budgets[0]['spent'] += floatval($transfers) * -1; $budgetLine->setBudget($budget);
$budgetLine->setOverspent($spent);
$object->addOverspent($spent);
$object->addBudgetLine($budgetLine);
continue;
} }
return $budgets; // one or more repetitions for budget:
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$budgetLine = new BudgetLine;
$budgetLine->setBudget($budget);
$budgetLine->setRepetition($repetition);
$expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, $shared);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$budgetLine->setLeft($left);
$budgetLine->setSpent($spent);
$budgetLine->setOverspent($overspent);
$budgetLine->setBudgeted($repetition->amount);
$object->addBudgeted($repetition->amount);
$object->addSpent($spent);
$object->addLeft($left);
$object->addOverspent($overspent);
$object->addBudgetLine($budgetLine);
}
}
// stuff outside of budgets:
$noBudget = $repository->getWithoutBudgetSum($start, $end);
$budgetLine = new BudgetLine;
$budgetLine->setOverspent($noBudget);
$object->addOverspent($noBudget);
$object->addBudgetLine($budgetLine);
return $object;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared)
{
$object = new CategoryCollection;
/**
* GET CATEGORIES:
*/
/** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
$set = $repository->getCategories();
foreach ($set as $category) {
$spent = $repository->spentInPeriodCorrected($category, $start, $end, $shared);
$category->spent = $spent;
$object->addCategory($category);
$object->addTotal($spent);
}
return $object;
}
/**
* Get a full report on the users expenses during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Expense
*/
public function getExpenseReport($start, $end, $shared)
{
$object = new Expense;
$set = $this->query->expenseInPeriodCorrected($start, $end, $shared);
foreach ($set as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateExpense($entry);
}
return $object;
}
/**
* Get a full report on the users incomes during the period.
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Income
*/
public function getIncomeReport($start, $end, $shared)
{
$object = new Income;
$set = $this->query->incomeInPeriodCorrected($start, $end, $shared);
foreach ($set as $entry) {
$object->addToTotal($entry->amount);
$object->addOrCreateIncome($entry);
}
return $object;
} }
/** /**
@@ -87,13 +377,14 @@ class ReportHelper implements ReportHelperInterface
*/ */
public function listOfMonths(Carbon $date) public function listOfMonths(Carbon $date)
{ {
$start = clone $date; $start = clone $date;
$end = Carbon::now(); $end = Carbon::now();
$months = []; $months = [];
while ($start <= $end) { while ($start <= $end) {
$year = $start->year; $year = $start->year;
$months[$year][] = [ $months[$year][] = [
'formatted' => $start->format('F Y'), 'formatted' => $start->formatLocalized('%B %Y'),
'month' => $start->month, 'month' => $start->month,
'year' => $year, 'year' => $year,
]; ];
@@ -102,77 +393,4 @@ class ReportHelper implements ReportHelperInterface
return $months; return $months;
} }
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$years = [];
while ($start <= $end) {
$years[] = $start->year;
$start->addYear();
}
$years[] = Carbon::now()->year;
// force the current year.
$years = array_unique($years);
return $years;
}
/**
* @param Carbon $date
* @param bool $showSharedReports
*
* @return array
*/
public function yearBalanceReport(Carbon $date, $showSharedReports = false)
{
$start = clone $date;
$end = clone $date;
$sharedAccounts = [];
if ($showSharedReports === false) {
$sharedCollection = Auth::user()->accounts()
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', '=', 'accountRole')
->where('account_meta.data', '=', json_encode('sharedAsset'))
->get(['accounts.id']);
foreach ($sharedCollection as $account) {
$sharedAccounts[] = $account->id;
}
}
$accounts = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*'])
->filter(
function (Account $account) use ($sharedAccounts) {
if (!in_array($account->id, $sharedAccounts)) {
return $account;
}
return null;
}
);
$report = [];
$start->startOfYear()->subDay();
$end->endOfYear();
foreach ($accounts as $account) {
$startBalance = Steam::balance($account, $start);
$endBalance = Steam::balance($account, $end);
$report[] = [
'start' => $startBalance,
'end' => $endBalance,
'hide' => ($startBalance == 0 && $endBalance == 0),
'account' => $account,
'shared' => $account->accountRole == 'sharedAsset'
];
}
return $report;
}
} }

View File

@@ -3,7 +3,12 @@
namespace FireflyIII\Helpers\Report; namespace FireflyIII\Helpers\Report;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Collection; use FireflyIII\Helpers\Collection\Account;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
/** /**
* Interface ReportHelperInterface * Interface ReportHelperInterface
@@ -13,26 +18,78 @@ use Illuminate\Support\Collection;
interface ReportHelperInterface interface ReportHelperInterface
{ {
/**
* This method generates a full report for the given period on all
* the users asset and cash accounts.
*
* @param Carbon $date
* @param Carbon $end
* @param boolean $shared
*
* @return Account
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared);
/** /**
* This methods fails to take in account transfers FROM shared accounts. * This method generates a full report for the given period on all
* the users bills and their payments.
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param int $limit * @param boolean $shared
* *
* @return Collection * @return Account
*/ */
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15); public function getBillReport(Carbon $start, Carbon $end, $shared);
/** /**
* This method gets some kind of list for a monthly overview. * @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* *
* @param Carbon $date * @return Balance
*
* @return Collection
*/ */
public function getBudgetsForMonth(Carbon $date); public function getBalanceReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return BudgetCollection
*/
public function getBudgetReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return CategoryCollection
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared);
/**
* 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);
/**
* 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);
/** /**
* @param Carbon $date * @param Carbon $date
@@ -41,18 +98,4 @@ interface ReportHelperInterface
*/ */
public function listOfMonths(Carbon $date); public function listOfMonths(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date);
/**
* @param Carbon $date
* @param bool $showSharedReports
*
* @return array
*/
public function yearBalanceReport(Carbon $date, $showSharedReports = false);
} }

View File

@@ -7,6 +7,7 @@ use Carbon\Carbon;
use Crypt; use Crypt;
use DB; use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@@ -21,101 +22,62 @@ use Steam;
*/ */
class ReportQuery implements ReportQueryInterface class ReportQuery implements ReportQueryInterface
{ {
/** /**
* This query retrieves a list of accounts that are active and not shared. * See ReportQueryInterface::incomeInPeriodCorrected
* *
* @param bool $showSharedReports * @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
* *
* @return Collection * @return Collection
*
*/ */
public function accountList($showSharedReports = false) public function expenseInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
{ {
$query = Auth::user()->accounts(); $query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) { if ($includeShared === false) {
$query->where(
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', "accountRole");
}
)->where(
function (Builder $query) { function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedAsset"'); $query->where(
$query->orWhereNull('account_meta.data'); function (Builder $q) { // only get withdrawals not from a shared account
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) { // and transfers from a shared account.
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
}
);
}
);
} else {
$query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine.
}
$query->orderBy('transaction_journals.date');
// get everything
$data = $query->get(
['transaction_journals.*', 'transaction_types.type', 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted']
);
$data->each(
function (TransactionJournal $journal) {
if (intval($journal->account_encrypted) == 1) {
$journal->name = Crypt::decrypt($journal->name);
}
}
);
$data = $data->filter(
function (TransactionJournal $journal) {
if ($journal->amount != 0) {
return $journal;
}
} }
); );
} return $data;
$query->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('account_types.type', ['Default account', 'Cash account', 'Asset account'])
->where('active', 1)
->orderBy('accounts.name', 'ASC');
return $query->get(['accounts.*']);
}
/**
* This method will get a list of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end)
{
$set = TransactionJournal::
leftJoin('transaction_group_transaction_journal', 'transaction_group_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin(
'transaction_group_transaction_journal as otherFromGroup', function (JoinClause $join) {
$join->on('otherFromGroup.transaction_group_id', '=', 'transaction_group_transaction_journal.transaction_group_id')
->on('otherFromGroup.transaction_journal_id', '!=', 'transaction_journals.id');
}
)
->leftJoin('transaction_journals as otherJournals', 'otherJournals.id', '=', 'otherFromGroup.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'otherJournals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'otherJournals.id')
->before($end)->after($start)
->where('transaction_types.type', 'Withdrawal')
->where('transaction_journals.user_id', Auth::user()->id)
->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at')
->whereNull('otherJournals.deleted_at')
->where('transactions.account_id', $account->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
->get(
[
'transaction_journals.*',
'transactions.amount as queryAmount'
]
);
return $set;
}
/**
* This method will get the sum of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end)
{
return floatval($this->balancedTransactionsList($account, $start, $end)->sum('queryAmount'));
} }
/** /**
@@ -123,15 +85,15 @@ class ReportQuery implements ReportQueryInterface
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $showSharedReports * @param bool $includeShared
* *
* @return Collection * @return Collection
*/ */
public function getAllAccounts(Carbon $start, Carbon $end, $showSharedReports = false) public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false)
{ {
$query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC') $query = Auth::user()->accounts()->orderBy('accounts.name', 'ASC')
->accountTypeIn(['Default account', 'Asset account', 'Cash account']); ->accountTypeIn(['Default account', 'Asset account', 'Cash account']);
if ($showSharedReports === false) { if ($includeShared === false) {
$query->leftJoin( $query->leftJoin(
'account_meta', function (JoinClause $join) { 'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole'); $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
@@ -167,55 +129,27 @@ class ReportQuery implements ReportQueryInterface
return $set; return $set;
} }
/**
* Grabs a summary of all expenses grouped by budget, related to the account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end)
{
$query = $this->queryJournalsNoBudget($account, $start, $end);
return $query->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) as `queryAmount`')]);
}
/** /**
* Get a list of transaction journals that have no budget, filtered for the specified account * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results
* and the specified date range. * will simply list the transaction journals only. This should allow any follow up counting to be accurate with
* regards to tags.
* *
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end)
{
$query = $this->queryJournalsNoBudget($account, $start, $end);
return $query->get(['budgets.name', 'transactions.amount as queryAmount', 'transaction_journals.*']);
}
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account * 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 * and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields. * not group and returns different fields.
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $showSharedReports * @param bool $includeShared
* *
* @return Collection * @return Collection
*/ */
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false) public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false)
{ {
$query = $this->queryJournalsWithTransactions($start, $end); $query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) { if ($includeShared === false) {
// only get deposits not to a shared account // only get deposits not to a shared account
// and transfers to a shared account. // and transfers to a shared account.
$query->where( $query->where(
@@ -238,25 +172,25 @@ class ReportQuery implements ReportQueryInterface
// any deposit is fine. // any deposit is fine.
$query->where('transaction_types.type', 'Deposit'); $query->where('transaction_types.type', 'Deposit');
} }
$query->groupBy('transaction_journals.id')->orderBy('transaction_journals.date'); $query->orderBy('transaction_journals.date');
// get everything, decrypt and return // get everything
$data = $query->get( $data = $query->get(
['transaction_journals.id', ['transaction_journals.*', 'transaction_types.type', 'ac_from.name as name', 'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted']
'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_types.type',
DB::Raw('SUM(`t_to`.`amount`) as `queryAmount`'),
'transaction_journals.date',
't_from.account_id as account_id',
'ac_from.name as name',
'ac_from.encrypted as account_encrypted'
]
); );
$data->each( $data->each(
function (Model $object) { function (TransactionJournal $journal) {
$object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name; if (intval($journal->account_encrypted) == 1) {
$journal->name = Crypt::decrypt($journal->name);
}
}
);
$data = $data->filter(
function (TransactionJournal $journal) {
if ($journal->amount != 0) {
return $journal;
}
} }
); );
@@ -264,313 +198,53 @@ class ReportQuery implements ReportQueryInterface
} }
/** /**
* Gets a list of expenses grouped by the budget they were filed under. * Covers tags
* *
* @param Account $account
* @param Budget $budget
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $showSharedReports
* *
* @return Collection * @return float
*/ */
public function journalsByBudget(Carbon $start, Carbon $end, $showSharedReports = false) public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end)
{ {
$query = Auth::user()->transactionjournals()
return floatval(
Auth::user()->transactionjournals()
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id') ->transactionTypes(['Withdrawal'])
->leftJoin( ->where('transactions.account_id', $account->id)
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id');
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"');
}
$query->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC');
return $query->get(['budgets.id', 'budgets.name', DB::Raw('SUM(`transactions`.`amount`) AS `spent`')]);
}
/**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = Auth::user()->transactionjournals()
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id');
if ($showSharedReports === false) {
$query->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)->where('account_meta.data', '!=', '"sharedAsset"');
}
$query->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('queryAmount');
$data = $query->get(['categories.id', 'categories.encrypted', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `queryAmount`')]);
// decrypt data:
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
// get all withdrawals not from a shared accounts
// and all transfers to a shared account
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
}
);
}
);
} else {
// any withdrawal goes:
$query->where('transaction_types.type', 'Withdrawal');
}
$query->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id')
->orderBy('queryAmount', 'DESC');
$data = $query->get(['t_to.account_id as id', 'ac_to.name as name', 'ac_to.encrypted', DB::Raw('SUM(t_to.amount) as `queryAmount`')]);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($showSharedReports === false) {
// show queries where transfer type is deposit, and its not to a shared account
// or where its a transfer and its from a shared account (both count as incomes)
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedAsset"');
}
);
}
);
} else {
// any deposit goes:
$query->where('transaction_types.type', 'Deposit');
}
$query->groupBy('t_from.account_id')->orderBy('queryAmount');
$data = $query->get(
['t_from.account_id as account_id', 'ac_from.name as name', 'ac_from.encrypted as encrypted', DB::Raw('SUM(t_from.amount) as `queryAmount`')]
);
// decrypt
$data->each(
function (Model $object) {
$object->name = intval($object->encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
return $data;
}
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '"sharedAsset"')
->after($start)
->before($end) ->before($end)
->where('transaction_types.type', 'Transfer')
->where('transaction_journals.user_id', Auth::user()->id)
->get(
['transaction_journals.id', 'transaction_journals.description', 'transactions.account_id', 'accounts.name',
'transactions.amount as queryAmount']
);
}
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->where('account_meta.data', '"sharedAsset"')
->after($start) ->after($start)
->before($end) ->where('budget_transaction_journal.budget_id', $budget->id)
->where('transaction_types.type', 'Transfer') ->get(['transaction_journals.*'])->sum('amount')
->where('transaction_journals.user_id', Auth::user()->id) ) * -1;
->groupBy('categories.name')
->get(
[
'categories.id',
'categories.name as name',
DB::Raw('SUM(`transactions`.`amount`) * -1 AS `queryAmount`')
]
);
} }
/** /**
*
* This query will get all transaction journals and budget information for a specified account
* in a certain date range, where the transaction journal does not have a budget.
* There is no get() specified, this is up to the method itself.
*
* @param Account $account * @param Account $account
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $shared
* *
* @return Builder * @return float
*/ */
protected function queryJournalsNoBudget(Account $account, Carbon $start, Carbon $end) public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false)
{ {
return TransactionJournal:: return floatval(
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') Auth::user()->transactionjournals()
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin( ->where('transactions.amount', '<', 0)
'transactions', function (JoinClause $join) { ->transactionTypes(['Withdrawal'])
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); ->where('transactions.account_id', $account->id)
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->before($end) ->before($end)
->after($start) ->after($start)
->where('accounts.id', $account->id) ->whereNull('budget_transaction_journal.budget_id')
->where('transaction_journals.user_id', Auth::user()->id) ->sum('transactions.amount')
->where('transaction_types.type', 'Withdrawal') );
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC');
} }
/** /**
@@ -609,5 +283,4 @@ class ReportQuery implements ReportQueryInterface
return $query; return $query;
} }
} }

View File

@@ -4,6 +4,7 @@ namespace FireflyIII\Helpers\Report;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@@ -15,152 +16,66 @@ interface ReportQueryInterface
{ {
/** /**
* This query retrieves a list of accounts that are active and not shared. * See ReportQueryInterface::incomeInPeriodCorrected
* *
* @param bool $showSharedReports * 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.
* *
* @return Collection
*/
public function accountList($showSharedReports = false);
/**
* This method will get a list of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
*
* @param Account $account
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $includeShared
* *
* @return Collection * @return Collection
*/
public function balancedTransactionsList(Account $account, Carbon $start, Carbon $end);
/**
* This method will get the sum of all expenses in a certain time period that have no budget
* and are balanced by a transfer to make up for it.
* *
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/ */
public function balancedTransactionsSum(Account $account, Carbon $start, Carbon $end); 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. * Get a users accounts combined with various meta-data related to the start and end date.
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $showSharedReports * @param bool $includeShared
* *
* @return Collection * @return Collection
*/ */
public function getAllAccounts(Carbon $start, Carbon $end, $showSharedReports = false); public function getAllAccounts(Carbon $start, Carbon $end, $includeShared = false);
/** /**
* Grabs a summary of all expenses grouped by budget, related to the account. * 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.
* *
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*/
public function incomeInPeriodCorrected(Carbon $start, Carbon $end, $includeShared = false);
/**
* Covers tags as well.
*
* @param Account $account
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function spentInBudgetCorrected(Account $account, Budget $budget, Carbon $start, Carbon $end);
/**
* @param Account $account * @param Account $account
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* @param bool $shared
* *
* @return mixed * @return float
*/ */
public function getBudgetSummary(Account $account, Carbon $start, Carbon $end); public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false);
/**
* Get a list of transaction journals that have no budget, filtered for the specified account
* and the specified date range.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getTransactionsWithoutBudget(Account $account, Carbon $start, Carbon $end);
/**
* 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 $showSharedReports
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
* @param bool $showSharedReports
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $showSharedReports = false);
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end);
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end);
} }

View File

@@ -28,7 +28,7 @@ class AccountController extends Controller
{ {
parent::__construct(); parent::__construct();
View::share('mainTitleIcon', 'fa-credit-card'); View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', 'Accounts'); View::share('title', trans('firefly.accounts'));
} }
/** /**
@@ -96,7 +96,7 @@ class AccountController extends Controller
{ {
$what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type]; $what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = 'Edit ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"'; $subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$openingBalance = $repository->openingBalanceTransaction($account); $openingBalance = $repository->openingBalanceTransaction($account);
@@ -137,7 +137,7 @@ class AccountController extends Controller
*/ */
public function index(AccountRepositoryInterface $repository, $what) public function index(AccountRepositoryInterface $repository, $what)
{ {
$subTitle = Config::get('firefly.subTitlesByIdentifier.' . $what); $subTitle = trans('firefly.' . $what . '_accounts');
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$types = Config::get('firefly.accountTypesByIdentifier.' . $what); $types = Config::get('firefly.accountTypesByIdentifier.' . $what);
$accounts = $repository->getAccounts($types); $accounts = $repository->getAccounts($types);
@@ -170,7 +170,7 @@ class AccountController extends Controller
$subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $account->accountType->type); $subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $account->accountType->type);
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type); $what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$journals = $repository->getJournals($account, $page); $journals = $repository->getJournals($account, $page);
$subTitle = 'Details for ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"'; $subTitle = trans('firefly.details_for_' . $what, ['name' => $account->name]);
$journals->setPath('accounts/show/' . $account->id); $journals->setPath('accounts/show/' . $account->id);

View File

@@ -38,6 +38,7 @@ class AuthController extends Controller
* *
* @param \Illuminate\Contracts\Auth\Guard $auth * @param \Illuminate\Contracts\Auth\Guard $auth
* @param \Illuminate\Contracts\Auth\Registrar $registrar * @param \Illuminate\Contracts\Auth\Registrar $registrar
*
* @codeCoverageIgnore * @codeCoverageIgnore
* *
*/ */

View File

@@ -7,6 +7,7 @@ use Illuminate\Foundation\Auth\ResetsPasswords;
/** /**
* Class PasswordController * Class PasswordController
*
* @codeCoverageIgnore * @codeCoverageIgnore
* @package FireflyIII\Http\Controllers\Auth * @package FireflyIII\Http\Controllers\Auth
*/ */

View File

@@ -7,7 +7,6 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Input; use Input;
use Redirect; use Redirect;
@@ -29,54 +28,10 @@ class BillController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Bills'); View::share('title', trans('firefly.bills'));
View::share('mainTitleIcon', 'fa-calendar-o'); View::share('mainTitleIcon', 'fa-calendar-o');
} }
/**
* @param AccountRepositoryInterface $repository
* @param Bill $bill
*
* @return \Illuminate\Http\RedirectResponse
*/
public function add(AccountRepositoryInterface $repository, Bill $bill)
{
$matches = explode(',', $bill->match);
$description = [];
$expense = null;
// get users expense accounts:
$accounts = $repository->getAccounts(Config::get('firefly.accountTypesByIdentifier.expense'));
foreach ($matches as $match) {
$match = strtolower($match);
// find expense account for each word if not found already:
if (is_null($expense)) {
/** @var Account $account */
foreach ($accounts as $account) {
$name = strtolower($account->name);
if (!(strpos($name, $match) === false)) {
$expense = $account;
break;
}
}
}
if (is_null($expense)) {
$description[] = $match;
}
}
$parameters = [
'description' => ucfirst(join(' ', $description)),
'expense_account' => is_null($expense) ? '' : $expense->name,
'amount' => round(($bill->amount_min + $bill->amount_max), 2),
];
Session::put('preFilled', $parameters);
return Redirect::to(route('transactions.create', 'withdrawal'));
}
/** /**
* @return $this * @return $this
*/ */

View File

@@ -29,7 +29,7 @@ class BudgetController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Budgets'); View::share('title', trans('firefly.budgets'));
View::share('mainTitleIcon', 'fa-tasks'); View::share('mainTitleIcon', 'fa-tasks');
View::share('hideBudgets', true); View::share('hideBudgets', true);
} }
@@ -60,7 +60,7 @@ class BudgetController extends Controller
Session::put('budgets.create.url', URL::previous()); Session::put('budgets.create.url', URL::previous());
} }
Session::forget('budgets.create.fromStore'); Session::forget('budgets.create.fromStore');
$subTitle = 'Create a new budget'; $subTitle = trans('firefly.create_new_budget');
return view('budgets.create', compact('subTitle')); return view('budgets.create', compact('subTitle'));
} }
@@ -137,7 +137,8 @@ class BudgetController extends Controller
$budgets->each( $budgets->each(
function (Budget $budget) use ($repository) { function (Budget $budget) use ($repository) {
$date = Session::get('start', Carbon::now()->startOfMonth()); $date = Session::get('start', Carbon::now()->startOfMonth());
$budget->spent = $repository->spentInMonth($budget, $date); $end = Session::get('end', Carbon::now()->endOfMonth());
$budget->spent = $repository->spentInPeriodCorrected($budget, $date, $end);
$budget->currentRep = $repository->getCurrentRepetition($budget, $date); $budget->currentRep = $repository->getCurrentRepetition($budget, $date);
} }
); );

View File

@@ -26,7 +26,7 @@ class CategoryController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Categories'); View::share('title', trans('firefly.categories'));
View::share('mainTitleIcon', 'fa-bar-chart'); View::share('mainTitleIcon', 'fa-bar-chart');
} }

View File

@@ -0,0 +1,150 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class AccountController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class AccountController extends Controller
{
/**
* Shows the balances for all the user's accounts.
*
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function all(GChart $chart, AccountRepositoryInterface $repository, $year, $month, $shared = false)
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
/** @var Collection $accounts */
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
if ($shared === false) {
// remove the shared accounts from the collection:
/** @var Account $account */
foreach ($accounts as $index => $account) {
if ($account->getMeta('accountRole') == 'sharedAsset') {
$accounts->forget($index);
}
}
}
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows the balances for all the user's frontpage accounts.
*
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, AccountRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage);
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows an account's balance for a single month.
*
* @param GChart $chart
* @param Account $account
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function single(GChart $chart, Account $account)
{
$chart->addColumn(trans('firefly.dayOfMonth'), 'date');
$chart->addColumn(trans('firefly.balanceFor', ['name' => $account->name]), 'number');
$chart->addCertainty(1);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start;
$today = new Carbon;
while ($end >= $current) {
$certain = $current < $today;
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

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

View File

@@ -0,0 +1,195 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
use Session;
/**
* Class BudgetController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class BudgetController extends Controller
{
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
*/
public function budget(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget)
{
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
$first = $repository->getFirstBudgetLimitDate($budget);
$range = $viewRange = Preferences::get('viewRange', '1M')->data;
$last = Session::get('end', new Carbon);
$final = clone $last;
$final->addYears(2);
$last = Navigation::endOfX($last, $range, $final);
while ($first < $last) {
$end = Navigation::addPeriod($first, $range, 0);
$spent = $repository->spentInPeriodCorrected($budget, $first, $end);
$chart->addRow($end, $spent);
$first = Navigation::addPeriod($first, $range, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows the amount left in a specific budget limit.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetLimit(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
$chart->addColumn(trans('firefly.day'), 'date');
$chart->addColumn(trans('firefly.left'), 'number');
$amount = $repetition->amount;
while ($start <= $end) {
/*
* Sum of expenses on this day:
*/
$sum = $repository->expensesOnDayCorrected($budget, $start);
$amount += $sum;
$chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Shows a budget list with spent/left/overspent.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, BudgetRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.budget'), 'string');
$chart->addColumn(trans('firefly.left'), 'number');
$chart->addColumn(trans('firefly.spent'), 'number');
$chart->addColumn(trans('firefly.overspent'), 'number');
$budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$allEntries = new Collection;
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->spentInPeriodCorrected($budget, $start, $end, true);
$allEntries->push([$budget->name, 0, 0, $expenses]);
continue;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->spentInPeriodCorrected($budget, $repetition->startdate, $repetition->enddate, true);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? floatval($repetition->amount) : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$allEntries->push(
[$budget->name . ' (' . $repetition->startdate->formatLocalized($this->monthAndDayFormat) . ')',
$left,
$spent,
$overspent
]
);
}
}
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push([trans('firefly.noBudget'), 0, 0, $noBudgetExpenses]);
foreach ($allEntries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
}
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Show a yearly overview for a budget.
*
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function year(GChart $chart, BudgetRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$budgets = $repository->getBudgets();
// add columns:
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
while ($start < $end) {
// month is the current end of the period:
$month = clone $start;
$month->endOfMonth();
// make a row:
$row = [clone $start];
// each budget, fill the row:
foreach ($budgets as $budget) {
$spent = $repository->spentInPeriodCorrected($budget, $start, $month, $shared);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Navigation;
use Preferences;
use Response;
use Session;
/**
* Class CategoryController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class CategoryController extends Controller
{
/**
* Show an overview for a category for all time, per month/week/year.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function all(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
$range = Preferences::get('viewRange', '1M')->data;
// jump to start of week / month / year / etc
$start = Navigation::startOfPeriod($start, $range);
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
$end = new Carbon;
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range);
$spent = $repository->spentInPeriodCorrected($category, $start, $currentEnd);
$chart->addRow(clone $start, $spent);
$start = Navigation::addPeriod($start, $range, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Show this month's category overview.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function frontpage(GChart $chart, CategoryRepositoryInterface $repository)
{
$chart->addColumn(trans('firefly.category'), 'string');
$chart->addColumn(trans('firefly.spent'), 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$set = $repository->getCategoriesAndExpensesCorrected($start, $end);
// sort by callback:
uasort(
$set,
function ($left, $right) {
if ($left['sum'] == $right['sum']) {
return 0;
}
return ($left['sum'] < $right['sum']) ? 1 : -1;
}
);
foreach ($set as $entry) {
$sum = floatval($entry['sum']);
if ($sum != 0) {
$chart->addRow($entry['name'], $sum);
}
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function month(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$chart->addColumn(trans('firefly.period'), 'date');
$chart->addColumn(trans('firefly.spent'), 'number');
while ($start <= $end) {
$spent = $repository->spentOnDaySumCorrected($category, $start);
$chart->addRow(clone $start, $spent);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* This chart will only show expenses.
*
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function year(GChart $chart, CategoryRepositoryInterface $repository, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$categories = $repository->getCategories();
// add columns:
$chart->addColumn(trans('firefly.month'), 'date');
foreach ($categories as $category) {
$chart->addColumn($category->name, 'number');
}
while ($start < $end) {
// month is the current end of the period:
$month = clone $start;
$month->endOfMonth();
// make a row:
$row = [clone $start];
// each budget, fill the row:
foreach ($categories as $category) {
$spent = $repository->spentInPeriodCorrected($category, $start, $month, $shared);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 16/05/15
* Time: 09:36
*/
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Response;
/**
* Class PiggyBankController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class PiggyBankController extends Controller
{
/**
* Shows the piggy bank history.
*
* @param GChart $chart
* @param PiggyBankRepositoryInterface $repository
* @param PiggyBank $piggyBank
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function history(GChart $chart, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$chart->addColumn(trans('firefly.date'), 'date');
$chart->addColumn(trans('firefly.balance'), 'number');
/** @var Collection $set */
$set = $repository->getEventSummarySet($piggyBank);
$sum = 0;
foreach ($set as $entry) {
$sum += floatval($entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Controllers\Controller;
use Grumpydictator\Gchart\GChart;
use Response;
/**
* Class ReportController
*
* @package FireflyIII\Http\Controllers\Chart
*/
class ReportController extends Controller
{
/**
* Summarizes all income and expenses, per month, for a given year.
*
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInOut(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
{
// get start and end of year
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$chart->addColumn(trans('firefly.month'), 'date');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$incomeSum = floatval($query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
$expenseSum = floatval($query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
$chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* Summarizes all income and expenses for a given year. Gives a total and an average.
*
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
* @param bool $shared
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInOutSummarized(GChart $chart, ReportQueryInterface $query, $year, $shared = false)
{
$start = new Carbon($year . '-01-01');
$end = new Carbon($year . '-12-31');
$shared = $shared == 'shared' ? true : false;
$income = 0;
$expense = 0;
$count = 0;
$chart->addColumn(trans('firefly.summary'), 'string');
$chart->addColumn(trans('firefly.income'), 'number');
$chart->addColumn(trans('firefly.expenses'), 'number');
while ($start < $end) {
$month = clone $start;
$month->endOfMonth();
// total income and total expenses:
$income += floatval($query->incomeInPeriodCorrected($start, $month, $shared)->sum('amount'));
$expense += floatval($query->expenseInPeriodCorrected($start, $month, $shared)->sum('amount'));
$count++;
$start->addMonth();
}
// add total + average:
$chart->addRow(trans('firefly.sum'), $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow(trans('firefly.average'), ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -1,8 +1,11 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Auth;
use Config;
use Illuminate\Foundation\Bus\DispatchesCommands; use Illuminate\Foundation\Bus\DispatchesCommands;
use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController; use Illuminate\Routing\Controller as BaseController;
use Preferences;
use View; use View;
/** /**
@@ -15,6 +18,11 @@ abstract class Controller extends BaseController
use DispatchesCommands, ValidatesRequests; use DispatchesCommands, ValidatesRequests;
/** @var string */
protected $monthAndDayFormat;
/** @var string */
protected $monthFormat;
/** /**
* *
*/ */
@@ -24,5 +32,16 @@ abstract class Controller extends BaseController
View::share('hideCategories', false); View::share('hideCategories', false);
View::share('hideBills', false); View::share('hideBills', false);
View::share('hideTags', false); View::share('hideTags', false);
if (Auth::check()) {
$pref = Preferences::get('language', 'en');
$lang = $pref->data;
$this->monthFormat = Config::get('firefly.month.' . $lang);
$this->monthAndDayFormat = Config::get('firefly.monthAndDay.' . $lang);
View::share('monthFormat', $this->monthFormat);
View::share('monthAndDayFormat', $this->monthAndDayFormat);
View::share('language', $lang);
}
} }
} }

View File

@@ -28,7 +28,7 @@ class CurrencyController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Currencies'); View::share('title', trans('firefly.currencies'));
View::share('mainTitleIcon', 'fa-usd'); View::share('mainTitleIcon', 'fa-usd');
} }

View File

@@ -1,590 +0,0 @@
<?php namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use Crypt;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Support\Collection;
use Navigation;
use Preferences;
use Response;
use Session;
use Steam;
/**
* Class GoogleChartController
*
* @package FireflyIII\Http\Controllers
*/
class GoogleChartController extends Controller
{
/**
* @param GChart $chart
* @param Account $account
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function accountBalanceChart(GChart $chart, Account $account)
{
$chart->addColumn('Day of month', 'date');
$chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addCertainty(1);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$current = clone $start;
$today = new Carbon;
while ($end >= $current) {
$certain = $current < $today;
$chart->addRow(clone $current, Steam::balance($account, $current), $certain);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param AccountRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allAccountsBalanceChart(GChart $chart, AccountRepositoryInterface $repository)
{
$chart->addColumn('Day of the month', 'date');
$frontPage = Preferences::get('frontPageAccounts', []);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = $repository->getFrontpageAccounts($frontPage);
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$chart->addColumn('Balance for ' . $account->name, 'number');
$chart->addCertainty($index);
$index++;
}
$current = clone $start;
$current->subDay();
$today = Carbon::now();
while ($end >= $current) {
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$chart->addRowArray($row);
$current->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allBudgetsAndSpending(GChart $chart, BudgetRepositoryInterface $repository, $year)
{
$budgets = $repository->getBudgets();
$chart->addColumn('Month', 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$row = [clone $start];
foreach ($budgets as $budget) {
$spent = $repository->spentInMonth($budget, $start);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allBudgetsHomeChart(GChart $chart, BudgetRepositoryInterface $repository)
{
$chart->addColumn('Budget', 'string');
$chart->addColumn('Left', 'number');
$chart->addColumn('Spent', 'number');
$chart->addColumn('Overspent', 'number');
$budgets = $repository->getBudgets();
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$allEntries = new Collection;
foreach ($budgets as $budget) {
$repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end);
if ($repetitions->count() == 0) {
$expenses = $repository->sumBudgetExpensesInPeriod($budget, $start, $end);
$allEntries->push([$budget->name, 0, 0, $expenses]);
continue;
}
/** @var LimitRepetition $repetition */
foreach ($repetitions as $repetition) {
$expenses = $repository->sumBudgetExpensesInPeriod($budget, $repetition->startdate, $repetition->enddate);
$left = $expenses < floatval($repetition->amount) ? floatval($repetition->amount) - $expenses : 0;
$spent = $expenses > floatval($repetition->amount) ? 0 : $expenses;
$overspent = $expenses > floatval($repetition->amount) ? $expenses - floatval($repetition->amount) : 0;
$allEntries->push(
[$budget->name . ' (' . $repetition->startdate->format('j M Y') . ')',
$left,
$spent,
$overspent
]
);
}
}
$noBudgetExpenses = $repository->getWithoutBudgetSum($start, $end);
$allEntries->push(['(no budget)', 0, 0, $noBudgetExpenses]);
foreach ($allEntries as $entry) {
if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) {
$chart->addRow($entry[0], $entry[1], $entry[2], $entry[3]);
}
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function allCategoriesHomeChart(GChart $chart, CategoryRepositoryInterface $repository)
{
$chart->addColumn('Category', 'string');
$chart->addColumn('Spent', 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$set = $repository->getCategoriesAndExpenses($start, $end);
foreach ($set as $entry) {
$isEncrypted = intval($entry->encrypted) == 1 ? true : false;
$name = strlen($entry->name) == 0 ? '(no category)' : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$chart->addRow($name, floatval($entry->sum));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BillRepositoryInterface $repository
* @param Bill $bill
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function billOverview(GChart $chart, BillRepositoryInterface $repository, Bill $bill)
{
$chart->addColumn('Date', 'date');
$chart->addColumn('Max amount', 'number');
$chart->addColumn('Min amount', 'number');
$chart->addColumn('Recorded bill entry', 'number');
// get first transaction or today for start:
$results = $repository->getJournals($bill);
/** @var TransactionJournal $result */
foreach ($results as $result) {
$chart->addRow(clone $result->date, floatval($bill->amount_max), floatval($bill->amount_min), floatval($result->amount));
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
*
* @param BillRepositoryInterface $repository
* @param AccountRepositoryInterface $accounts
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function billsOverview(GChart $chart, BillRepositoryInterface $repository, AccountRepositoryInterface $accounts)
{
$chart->addColumn('Name', 'string');
$chart->addColumn('Amount', 'number');
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
$bills = $repository->getActiveBills();
$paid = new Collection; // journals.
$unpaid = new Collection; // bills
// loop paid and create single entry:
$paidDescriptions = [];
$paidAmount = 0;
$unpaidDescriptions = [];
$unpaidAmount = 0;
/** @var Bill $bill */
foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$journals = $repository->getJournalsInRange($bill, $range['start'], $range['end']);
if ($journals->count() == 0) {
$unpaid->push([$bill, $range['start']]);
} else {
$paid = $paid->merge($journals);
}
}
}
$creditCards = $accounts->getCreditCards();
foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) {
// unpaid! create a fake bill that matches the amount.
$description = $creditCard->name;
$amount = $balance * -1;
$fakeBill = $repository->createFakeBill($description, $date, $amount);
unset($description, $amount);
$unpaid->push([$fakeBill, $date]);
}
if ($balance == 0) {
// find transfer(s) TO the credit card which should account for
// anything paid. If not, the CC is not yet used.
$journals = $accounts->getTransfersInRange($creditCard, $start, $end);
$paid = $paid->merge($journals);
}
}
/** @var TransactionJournal $entry */
foreach ($paid as $entry) {
$paidDescriptions[] = $entry->description;
$paidAmount += floatval($entry->amount);
}
// loop unpaid:
/** @var Bill $entry */
foreach ($unpaid as $entry) {
$description = $entry[0]->name . ' (' . $entry[1]->format('jS M Y') . ')';
$amount = ($entry[0]->amount_max + $entry[0]->amount_min) / 2;
$unpaidDescriptions[] = $description;
$unpaidAmount += $amount;
unset($amount, $description);
}
$chart->addRow('Unpaid: ' . join(', ', $unpaidDescriptions), $unpaidAmount);
$chart->addRow('Paid: ' . join(', ', $paidDescriptions), $paidAmount);
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param LimitRepetition $repetition
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetLimitSpending(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition)
{
$start = clone $repetition->startdate;
$end = $repetition->enddate;
$chart->addColumn('Day', 'date');
$chart->addColumn('Left', 'number');
$amount = $repetition->amount;
while ($start <= $end) {
/*
* Sum of expenses on this day:
*/
$sum = $repository->expensesOnDay($budget, $start);
$amount += $sum;
$chart->addRow(clone $start, $amount);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
* @param int $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function budgetsAndSpending(GChart $chart, BudgetRepositoryInterface $repository, Budget $budget, $year = 0)
{
$chart->addColumn('Month', 'date');
$chart->addColumn('Budgeted', 'number');
$chart->addColumn('Spent', 'number');
if ($year == 0) {
$start = $repository->getFirstBudgetLimitDate($budget);
$end = $repository->getLastBudgetLimitDate($budget);
} else {
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
}
while ($start <= $end) {
$spent = $repository->spentInMonth($budget, $start);
$budgeted = $repository->getLimitAmountOnDate($budget, $start);
$chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function categoryOverviewChart(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
// oldest transaction in category:
$start = $repository->getFirstActivityDate($category);
/** @var Preference $range */
$range = Preferences::get('viewRange', '1M');
// jump to start of week / month / year / etc (TODO).
$start = Navigation::startOfPeriod($start, $range->data);
$chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number');
$end = new Carbon;
while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range->data);
$spent = $repository->spentInPeriodSum($category, $start, $currentEnd);
$chart->addRow(clone $start, $spent);
$start = Navigation::addPeriod($start, $range->data, 0);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param CategoryRepositoryInterface $repository
* @param Category $category
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function categoryPeriodChart(GChart $chart, CategoryRepositoryInterface $repository, Category $category)
{
$start = clone Session::get('start', Carbon::now()->startOfMonth());
$chart->addColumn('Period', 'date');
$chart->addColumn('Spent', 'number');
$end = Session::get('end', Carbon::now()->endOfMonth());
while ($start <= $end) {
$spent = $repository->spentOnDaySum($category, $start);
$chart->addRow(clone $start, $spent);
$start->addDay();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param PiggyBankRepositoryInterface $repository
* @param PiggyBank $piggyBank
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function piggyBankHistory(GChart $chart, PiggyBankRepositoryInterface $repository, PiggyBank $piggyBank)
{
$chart->addColumn('Date', 'date');
$chart->addColumn('Balance', 'number');
/** @var Collection $set */
$set = $repository->getEventSummarySet($piggyBank);
$sum = 0;
foreach ($set as $entry) {
$sum += floatval($entry->sum);
$chart->addRow(new Carbon($entry->date), $sum);
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInExp(GChart $chart, ReportQueryInterface $query, $year)
{
$start = new Carbon('01-01-' . $year);
$chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
// get report query interface.
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income && total expenses:
$incomeSum = floatval($query->incomeByPeriod($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$expenseSum = floatval($query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param GChart $chart
* @param ReportQueryInterface $query
* @param $year
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function yearInExpSum(GChart $chart, ReportQueryInterface $query, $year)
{
$start = new Carbon('01-01-' . $year);
$chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$income = 0;
$expense = 0;
$count = 0;
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income:
$incomeSum = floatval($query->incomeByPeriod($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
// total expenses:
$expenseSum = floatval($query->journalsByExpenseAccount($start, $currentEnd, $showSharedReports)->sum('queryAmount'));
$income += $incomeSum;
$expense += $expenseSum;
$count++;
$start->addMonth();
}
$chart->addRow('Sum', $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow('Average', ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -53,7 +53,7 @@ class HomeController extends Controller
$types = Config::get('firefly.accountTypesByIdentifier.asset'); $types = Config::get('firefly.accountTypesByIdentifier.asset');
$count = $repository->countAccounts($types); $count = $repository->countAccounts($types);
$title = 'Firefly'; $title = 'Firefly';
$subTitle = 'What\'s playing?'; $subTitle = trans('firefly.welcomeBack');
$mainTitleIcon = 'fa-fire'; $mainTitleIcon = 'fa-fire';
$transactions = []; $transactions = [];
$frontPage = Preferences::get('frontPageAccounts', []); $frontPage = Preferences::get('frontPageAccounts', []);
@@ -63,13 +63,11 @@ class HomeController extends Controller
$savings = $repository->getSavingsAccounts(); $savings = $repository->getSavingsAccounts();
$piggyBankAccounts = $repository->getPiggyBankAccounts(); $piggyBankAccounts = $repository->getPiggyBankAccounts();
$savingsTotal = 0; $savingsTotal = 0;
foreach ($savings as $savingAccount) { foreach ($savings as $savingAccount) {
$savingsTotal += Steam::balance($savingAccount, $end); $savingsTotal += Steam::balance($savingAccount, $end);
} }
// check if all books are correct.
$sum = $repository->sumOfEverything(); $sum = $repository->sumOfEverything();
if ($sum != 0) { if ($sum != 0) {
Session::flash( Session::flash(

View File

@@ -12,7 +12,6 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Preferences;
use Response; use Response;
use Session; use Session;
use Steam; use Steam;
@@ -39,19 +38,14 @@ class JsonController extends Controller
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$amount = 0; $amount = 0;
// these two functions are the same as the chart TODO // these two functions are the same as the chart
$bills = $repository->getActiveBills(); $bills = $repository->getActiveBills();
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($bills as $bill) { foreach ($bills as $bill) {
$ranges = $repository->getRanges($bill, $start, $end); $amount += $repository->billPaymentsInRange($bill, $start, $end);
foreach ($ranges as $range) {
// paid a bill in this range?
$amount += $repository->getJournalsInRange($bill, $range['start'], $range['end'])->sum('amount');
} }
} unset($bill, $bills);
unset($ranges, $bill, $range, $bills);
/** /**
* Find credit card accounts and possibly unpaid credit card bills. * Find credit card accounts and possibly unpaid credit card bills.
@@ -60,7 +54,7 @@ class JsonController extends Controller
// if the balance is not zero, the monthly payment is still underway. // if the balance is not zero, the monthly payment is still underway.
/** @var Account $creditCard */ /** @var Account $creditCard */
foreach ($creditCards as $creditCard) { foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true); $balance = Steam::balance($creditCard, $end, true);
if ($balance == 0) { if ($balance == 0) {
// find a transfer TO the credit card which should account for // find a transfer TO the credit card which should account for
// anything paid. If not, the CC is not yet used. // anything paid. If not, the CC is not yet used.
@@ -100,7 +94,7 @@ class JsonController extends Controller
$creditCards = $accountRepository->getCreditCards(); $creditCards = $accountRepository->getCreditCards();
foreach ($creditCards as $creditCard) { foreach ($creditCards as $creditCard) {
$balance = Steam::balance($creditCard, null, true); $balance = Steam::balance($creditCard, $end, true);
$date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate')); $date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate'));
if ($balance < 0) { if ($balance < 0) {
// unpaid! create a fake bill that matches the amount. // unpaid! create a fake bill that matches the amount.
@@ -128,7 +122,12 @@ class JsonController extends Controller
{ {
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$amount = $reportQuery->incomeByPeriod($start, $end, true)->sum('queryAmount'); $amount = $reportQuery->incomeInPeriodCorrected($start, $end, true)->sum('amount');
// $amount = 0;
// foreach($set as $entry) {
// //echo $entry->description.' ('.$entry->tags->count().'): ' . $entry->amount."\n";
// $amount += $entry->amount;
// }
return Response::json(['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]); return Response::json(['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
} }
@@ -142,7 +141,7 @@ class JsonController extends Controller
{ {
$start = Session::get('start', Carbon::now()->startOfMonth()); $start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth());
$amount = $reportQuery->journalsByExpenseAccount($start, $end, true)->sum('queryAmount'); $amount = $reportQuery->expenseInPeriodCorrected($start, $end, true)->sum('amount');
return Response::json(['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]); return Response::json(['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]);
} }
@@ -202,30 +201,6 @@ class JsonController extends Controller
} }
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function setSharedReports()
{
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$new = !$pref->data;
Preferences::set('showSharedReports', $new);
return Response::json(['value' => $new]);
}
/**
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showSharedReports()
{
$pref = Preferences::get('showSharedReports', false);
return Response::json(['value' => $pref->data]);
}
/** /**
* Returns a JSON list of all beneficiaries. * Returns a JSON list of all beneficiaries.
* *

View File

@@ -31,7 +31,7 @@ class PiggyBankController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Piggy banks'); View::share('title', trans('firefly.piggyBanks'));
View::share('mainTitleIcon', 'fa-sort-amount-asc'); View::share('mainTitleIcon', 'fa-sort-amount-asc');
} }
@@ -45,7 +45,8 @@ class PiggyBankController extends Controller
*/ */
public function add(AccountRepositoryInterface $repository, PiggyBank $piggyBank) public function add(AccountRepositoryInterface $repository, PiggyBank $piggyBank)
{ {
$leftOnAccount = $repository->leftOnAccount($piggyBank->account); $date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $repository->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount; $savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar; $leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = min($leftOnAccount, $leftToSave); $maxAmount = min($leftOnAccount, $leftToSave);
@@ -157,6 +158,7 @@ class PiggyBankController extends Controller
{ {
/** @var Collection $piggyBanks */ /** @var Collection $piggyBanks */
$piggyBanks = $piggyRepository->getPiggyBanks(); $piggyBanks = $piggyRepository->getPiggyBanks();
$end = Session::get('end', Carbon::now()->endOfMonth());
$accounts = []; $accounts = [];
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
@@ -172,8 +174,8 @@ class PiggyBankController extends Controller
if (!isset($accounts[$account->id])) { if (!isset($accounts[$account->id])) {
$accounts[$account->id] = [ $accounts[$account->id] = [
'name' => $account->name, 'name' => $account->name,
'balance' => Steam::balance($account, null, true), 'balance' => Steam::balance($account, $end, true),
'leftForPiggyBanks' => $repository->leftOnAccount($account), 'leftForPiggyBanks' => $repository->leftOnAccount($account, $end),
'sumOfSaved' => $piggyBank->savedSoFar, 'sumOfSaved' => $piggyBank->savedSoFar,
'sumOfTargets' => floatval($piggyBank->targetamount), 'sumOfTargets' => floatval($piggyBank->targetamount),
'leftToSave' => $piggyBank->leftToSave 'leftToSave' => $piggyBank->leftToSave
@@ -215,7 +217,8 @@ class PiggyBankController extends Controller
public function postAdd(PiggyBankRepositoryInterface $repository, AccountRepositoryInterface $accounts, PiggyBank $piggyBank) public function postAdd(PiggyBankRepositoryInterface $repository, AccountRepositoryInterface $accounts, PiggyBank $piggyBank)
{ {
$amount = round(floatval(Input::get('amount')), 2); $amount = round(floatval(Input::get('amount')), 2);
$leftOnAccount = $accounts->leftOnAccount($piggyBank->account); $date = Session::get('end', Carbon::now()->endOfMonth());
$leftOnAccount = $accounts->leftOnAccount($piggyBank->account, $date);
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount; $savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$leftToSave = $piggyBank->targetamount - $savedSoFar; $leftToSave = $piggyBank->targetamount - $savedSoFar;
$maxAmount = round(min($leftOnAccount, $leftToSave), 2); $maxAmount = round(min($leftOnAccount, $leftToSave), 2);

View File

@@ -1,5 +1,6 @@
<?php namespace FireflyIII\Http\Controllers; <?php namespace FireflyIII\Http\Controllers;
use Config;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input; use Input;
use Preferences; use Preferences;
@@ -21,7 +22,7 @@ class PreferencesController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Preferences'); View::share('title', trans('firefly.preferences'));
View::share('mainTitleIcon', 'fa-gear'); View::share('mainTitleIcon', 'fa-gear');
} }
@@ -37,9 +38,11 @@ class PreferencesController extends Controller
$viewRange = $viewRangePref->data; $viewRange = $viewRangePref->data;
$frontPageAccounts = Preferences::get('frontPageAccounts', []); $frontPageAccounts = Preferences::get('frontPageAccounts', []);
$budgetMax = Preferences::get('budgetMaximum', 1000); $budgetMax = Preferences::get('budgetMaximum', 1000);
$languagePref = Preferences::get('language', 'en');
$language = $languagePref->data;
$budgetMaximum = $budgetMax->data; $budgetMaximum = $budgetMax->data;
return view('preferences.index', compact('budgetMaximum', 'accounts', 'frontPageAccounts', 'viewRange')); return view('preferences.index', compact('budgetMaximum', 'language', 'accounts', 'frontPageAccounts', 'viewRange'));
} }
/** /**
@@ -49,10 +52,12 @@ class PreferencesController extends Controller
{ {
// front page accounts // front page accounts
$frontPageAccounts = []; $frontPageAccounts = [];
if (is_array(Input::get('frontPageAccounts'))) {
foreach (Input::get('frontPageAccounts') as $id) { foreach (Input::get('frontPageAccounts') as $id) {
$frontPageAccounts[] = intval($id); $frontPageAccounts[] = intval($id);
} }
Preferences::set('frontPageAccounts', $frontPageAccounts); Preferences::set('frontPageAccounts', $frontPageAccounts);
}
// view range: // view range:
Preferences::set('viewRange', Input::get('viewRange')); Preferences::set('viewRange', Input::get('viewRange'));
@@ -65,6 +70,12 @@ class PreferencesController extends Controller
$budgetMaximum = intval(Input::get('budgetMaximum')); $budgetMaximum = intval(Input::get('budgetMaximum'));
Preferences::set('budgetMaximum', $budgetMaximum); Preferences::set('budgetMaximum', $budgetMaximum);
// language:
$lang = Input::get('language');
if (in_array($lang, array_keys(Config::get('firefly.lang')))) {
Preferences::set('language', $lang);
}
Session::flash('success', 'Preferences saved!'); Session::flash('success', 'Preferences saved!');

View File

@@ -2,13 +2,13 @@
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Preferences; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Session; use Session;
use Steam;
use View; use View;
/** /**
@@ -21,311 +21,132 @@ class ReportController extends Controller
/** @var ReportHelperInterface */ /** @var ReportHelperInterface */
protected $helper; protected $helper;
/** @var ReportQueryInterface */
protected $query;
/** /**
* @param ReportHelperInterface $helper * @param ReportHelperInterface $helper
* @param ReportQueryInterface $query
*/ */
public function __construct(ReportHelperInterface $helper, ReportQueryInterface $query) public function __construct(ReportHelperInterface $helper)
{ {
$this->query = $query; parent::__construct();
$this->helper = $helper; $this->helper = $helper;
View::share('title', 'Reports'); View::share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart'); View::share('mainTitleIcon', 'fa-line-chart');
} }
/** /**
* @param string $year * @param AccountRepositoryInterface $repository
* @param string $month
* *
* @return \Illuminate\View\View
*/
public function budget($year = '2014', $month = '1')
{
$date = new Carbon($year . '-' . $month . '-01');
$subTitle = 'Budget report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
// should show shared reports?
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
$accountAmounts = []; // array with sums of spent amounts on each account.
$accounts = $this->query->getAllAccounts($start, $end, $showSharedReports); // all accounts and some data.
foreach ($accounts as $account) {
$budgets = $this->query->getBudgetSummary($account, $start, $end);// get budget summary for this account:
$balancedAmount = $this->query->balancedTransactionsSum($account, $start, $end);
$accountAmounts[$account->id] = $balancedAmount;
// balance out the transactions (see transaction groups & tags) ^^
// array with budget information for each account:
$array = [];
// should always hide account
$hide = true;
// loop all budgets
/** @var \FireflyIII\Models\Budget $budget */
foreach ($budgets as $budget) {
$id = intval($budget->id);
$data = $budget->toArray();
$array[$id] = $data;
// no longer hide account if any budget has money in it.
if (floatval($data['queryAmount']) != 0) {
$hide = false;
}
$accountAmounts[$account->id] += $data['queryAmount'];
}
$account->hide = $hide;
$account->budgetInformation = $array;
$account->balancedAmount = $balancedAmount;
}
/**
* Start getBudgetsForMonth DONE
*/
$budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
/**
* End getBudgetsForMonth DONE
*/
return view('reports.budget', compact('subTitle', 'accountAmounts', 'year', 'month', 'subTitleIcon', 'date', 'accounts', 'budgets'));
}
/**
* @return View * @return View
* @internal param ReportHelperInterface $helper * @internal param ReportHelperInterface $helper
*
*/ */
public function index() public function index(AccountRepositoryInterface $repository)
{ {
$start = Session::get('first'); $start = Session::get('first');
$months = $this->helper->listOfMonths($start); $months = $this->helper->listOfMonths($start);
$years = $this->helper->listOfYears($start);
$title = 'Reports';
$mainTitleIcon = 'fa-line-chart';
return view('reports.index', compact('years', 'months', 'title', 'mainTitleIcon')); // does the user have shared accounts?
} $accounts = $repository->getAccounts(['Default account', 'Asset account']);
$hasShared = false;
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function modalBalancedTransfers(Account $account, $year = '2014', $month = '1')
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$journals = $this->query->balancedTransactionsList($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return View
* @internal param ReportQueryInterface $query
*
*/
public function modalLeftUnbalanced(Account $account, $year = '2014', $month = '1')
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$set = $this->query->getTransactionsWithoutBudget($account, $start, $end);
$journals = $set->filter(
function (TransactionJournal $journal) {
$count = $journal->transactiongroups()->where('relation', 'balance')->count();
if ($count == 0) {
return $journal;
}
return null;
}
);
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param Account $account
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function modalNoBudget(Account $account, $year = '2014', $month = '1')
{
$start = new Carbon($year . '-' . $month . '-01');
$end = clone $start;
$end->endOfMonth();
$journals = $this->query->getTransactionsWithoutBudget($account, $start, $end);
return view('reports.modal-journal-list', compact('journals'));
}
/**
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function month($year = '2014', $month = '1')
{
$date = new Carbon($year . '-' . $month . '-01');
$subTitle = 'Report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report.
/** @var Preference $pref */
$pref = Preferences::get('showSharedReports', false);
$showSharedReports = $pref->data;
/**
*
* get income for month (date)
*
*/
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
/**
* Start getIncomeForMonth DONE
*/
$income = $this->query->incomeByPeriod($start, $end, $showSharedReports);
/**
* End getIncomeForMonth DONE
*/
/**
* Start getExpenseGroupedForMonth DONE
*/
$set = $this->query->journalsByExpenseAccount($start, $end, $showSharedReports);
$expenses = Steam::makeArray($set);
$expenses = Steam::sortArray($expenses);
$expenses = Steam::limitArray($expenses, 10);
/**
* End getExpenseGroupedForMonth DONE
*/
/**
* Start getBudgetsForMonth DONE
*/
$budgets = $this->helper->getBudgetsForMonth($date, $showSharedReports);
/**
* End getBudgetsForMonth DONE
*/
/**
* Start getCategoriesForMonth DONE
*/
// all categories.
$result = $this->query->journalsByCategory($start, $end);
$categories = Steam::makeArray($result);
// all transfers
if ($showSharedReports === false) {
$result = $this->query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers);
} else {
$merged = $categories;
}
// sort.
$sorted = Steam::sortNegativeArray($merged);
// limit to $limit:
$categories = Steam::limitArray($sorted, 10);
/**
* End getCategoriesForMonth DONE
*/
/**
* Start getAccountsForMonth
*/
$list = $this->query->accountList($showSharedReports);
$accounts = [];
/** @var Account $account */ /** @var Account $account */
foreach ($list as $account) { foreach ($accounts as $account) {
$id = intval($account->id); if ($account->getMeta('accountRole') == 'sharedAsset') {
/** @noinspection PhpParamsInspection */ $hasShared = true;
$accounts[$id] = [ }
'name' => $account->name, }
'startBalance' => Steam::balance($account, $start),
'endBalance' => Steam::balance($account, $end)
];
$accounts[$id]['difference'] = $accounts[$id]['endBalance'] - $accounts[$id]['startBalance'];
return view('reports.index', compact('months', 'hasShared'));
} }
/** /**
* End getAccountsForMonth * @param string $year
* @param string $month
*
* @param bool $shared
*
* @return \Illuminate\View\View
*/ */
public function month($year = '2014', $month = '1', $shared = false)
{
$start = new Carbon($year . '-' . $month . '-01');
$subTitle = trans('firefly.reportForMonth', ['date' => $start->formatLocalized($this->monthFormat)]);
$subTitleIcon = 'fa-calendar';
$end = clone $start;
$incomeTopLength = 8;
$expenseTopLength = 8;
if ($shared == 'shared') {
$shared = true;
$subTitle = trans('firefly.reportForMonthShared', ['date' => $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, $shared);
return view( return view(
'reports.month', 'reports.month',
compact( compact(
'income', 'expenses', 'budgets', 'accounts', 'categories', 'start', 'shared',
'date', 'subTitle', 'displaySum', 'subTitleIcon' 'subTitle', 'subTitleIcon',
'accounts',
'incomes', 'incomeTopLength',
'expenses', 'expenseTopLength',
'budgets', 'balance',
'categories',
'bills'
) )
); );
} }
/** /**
* @param $year * @param $year
* *
* @param bool $shared
*
* @return $this * @return $this
*/ */
public function year($year) public function year($year, $shared = false)
{ {
/** @var Preference $pref */ $start = new Carbon('01-01-' . $year);
$pref = Preferences::get('showSharedReports', false); $end = clone $start;
$showSharedReports = $pref->data; $subTitle = trans('firefly.reportForYear', ['year' => $year]);
$date = new Carbon('01-01-' . $year);
$end = clone $date;
$end->endOfYear();
$title = 'Reports';
$subTitle = $year;
$subTitleIcon = 'fa-bar-chart'; $subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart'; $incomeTopLength = 8;
$balances = $this->helper->yearBalanceReport($date, $showSharedReports); $expenseTopLength = 8;
$groupedIncomes = $this->query->journalsByRevenueAccount($date, $end, $showSharedReports);
$groupedExpenses = $this->query->journalsByExpenseAccount($date, $end, $showSharedReports); 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);
return view( return view(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon') 'reports.year',
compact(
'start', // the date for this report.
'shared', // is a shared report?
'accounts', // all accounts
'incomes', 'expenses', // expenses and incomes.
'subTitle', 'subTitleIcon', // subtitle and subtitle icon.
'incomeTopLength', // length of income top X
'expenseTopLength' // length of expense top X.
)
); );
} }

View File

@@ -133,7 +133,7 @@ class TagController extends Controller
* changes to an advancePayment. * changes to an advancePayment.
*/ */
if ($tag->tagMode == 'balancingAct') { if ($tag->tagMode == 'balancingAct' || $tag->tagMode == 'nothing') {
foreach ($tag->transactionjournals as $journal) { foreach ($tag->transactionjournals as $journal) {
if ($journal->transactionType->type == 'Transfer') { if ($journal->transactionType->type == 'Transfer') {
$allowToAdvancePayment = false; $allowToAdvancePayment = false;
@@ -289,9 +289,9 @@ class TagController extends Controller
public function update(TagFormRequest $request, TagRepositoryInterface $repository, Tag $tag) public function update(TagFormRequest $request, TagRepositoryInterface $repository, Tag $tag)
{ {
if (Input::get('setTag') == 'true') { if (Input::get('setTag') == 'true') {
$latitude = strlen($request->get('latitude')) > 0 ? $request->get('latitude') : null; $latitude = $request->get('latitude');
$longitude = strlen($request->get('longitude')) > 0 ? $request->get('longitude') : null; $longitude = $request->get('longitude');
$zoomLevel = strlen($request->get('zoomLevel')) > 0 ? $request->get('zoomLevel') : null; $zoomLevel = $request->get('zoomLevel');
} else { } else {
$latitude = null; $latitude = null;
$longitude = null; $longitude = null;

View File

@@ -30,7 +30,7 @@ class TransactionController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Transactions'); View::share('title', trans('firefly.transactions'));
View::share('mainTitleIcon', 'fa-repeat'); View::share('mainTitleIcon', 'fa-repeat');
} }
@@ -44,18 +44,16 @@ class TransactionController extends Controller
{ {
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)'; $budgets[0] = trans('form.noBudget');
$piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get()); $piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get());
$piggies[0] = '(no piggy bank)'; $piggies[0] = trans('form.noPiggybank');
$preFilled = Session::has('preFilled') ? Session::get('preFilled') : []; $preFilled = Session::has('preFilled') ? Session::get('preFilled') : [];
$respondTo = ['account_id', 'account_from_id']; $respondTo = ['account_id', 'account_from_id'];
$subTitle = 'Add a new ' . e($what); $subTitle = trans('form.add_new_' . $what);
foreach ($respondTo as $r) { foreach ($respondTo as $r) {
if (!is_null(Input::get($r))) {
$preFilled[$r] = Input::get($r); $preFilled[$r] = Input::get($r);
} }
}
Session::put('preFilled', $preFilled); Session::put('preFilled', $preFilled);
// put previous url in session if not redirect from store (not "create another"). // put previous url in session if not redirect from store (not "create another").
@@ -119,10 +117,11 @@ class TransactionController extends Controller
$what = strtolower($journal->transactiontype->type); $what = strtolower($journal->transactiontype->type);
$accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account']));
$budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get());
$budgets[0] = '(no budget)'; $budgets[0] = trans('form.noBudget');
$transactions = $journal->transactions()->orderBy('amount', 'DESC')->get(); $transactions = $journal->transactions()->orderBy('amount', 'DESC')->get();
$piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get()); $piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get());
$piggies[0] = '(no piggy bank)'; $piggies[0] = trans('form.noPiggybank');
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
$preFilled = [ $preFilled = [
'date' => $journal->date->format('Y-m-d'), 'date' => $journal->date->format('Y-m-d'),
'category' => '', 'category' => '',
@@ -150,7 +149,7 @@ class TransactionController extends Controller
$preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->orderBy('date', 'DESC')->first()->piggy_bank_id; $preFilled['piggy_bank_id'] = $journal->piggyBankEvents()->orderBy('date', 'DESC')->first()->piggy_bank_id;
} }
$preFilled['amount'] = $journal->amount; $preFilled['amount'] = $journal->actualAmount;
$preFilled['account_id'] = $journal->assetAccount->id; $preFilled['account_id'] = $journal->assetAccount->id;
$preFilled['expense_account'] = $transactions[0]->account->name; $preFilled['expense_account'] = $transactions[0]->account->name;
$preFilled['revenue_account'] = $transactions[1]->account->name; $preFilled['revenue_account'] = $transactions[1]->account->name;
@@ -182,19 +181,19 @@ class TransactionController extends Controller
case 'expenses': case 'expenses':
case 'withdrawal': case 'withdrawal':
$subTitleIcon = 'fa-long-arrow-left'; $subTitleIcon = 'fa-long-arrow-left';
$subTitle = 'Expenses'; $subTitle = trans('firefly.expenses');
$types = ['Withdrawal']; $types = ['Withdrawal'];
break; break;
case 'revenue': case 'revenue':
case 'deposit': case 'deposit':
$subTitleIcon = 'fa-long-arrow-right'; $subTitleIcon = 'fa-long-arrow-right';
$subTitle = 'Revenue, income and deposits'; $subTitle = trans('firefly.income');
$types = ['Deposit']; $types = ['Deposit'];
break; break;
case 'transfer': case 'transfer':
case 'transfers': case 'transfers':
$subTitleIcon = 'fa-exchange'; $subTitleIcon = 'fa-exchange';
$subTitle = 'Transfers'; $subTitle = trans('firefly.transfers');
$types = ['Transfer']; $types = ['Transfer'];
break; break;
} }
@@ -249,7 +248,7 @@ class TransactionController extends Controller
$t->after = $t->before + $t->amount; $t->after = $t->before + $t->amount;
} }
); );
$subTitle = e($journal->transactiontype->type) . ' "' . e($journal->description) . '"'; $subTitle = trans('firefly.' . $journal->transactiontype->type) . ' "' . e($journal->description) . '"';
return view('transactions.show', compact('journal', 'subTitle')); return view('transactions.show', compact('journal', 'subTitle'));
} }
@@ -269,7 +268,9 @@ class TransactionController extends Controller
// rescan journal, UpdateJournalConnection // rescan journal, UpdateJournalConnection
event(new JournalSaved($journal)); event(new JournalSaved($journal));
// ConnectJournalToPiggyBank // ConnectJournalToPiggyBank
if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) {
event(new JournalCreated($journal, intval($request->get('piggy_bank_id')))); event(new JournalCreated($journal, intval($request->get('piggy_bank_id'))));
}
$repository->deactivateReminder($request->get('reminder_id')); $repository->deactivateReminder($request->get('reminder_id'));

View File

@@ -1,8 +1,12 @@
<?php namespace FireflyIII\Http\Middleware; <?php namespace FireflyIII\Http\Middleware;
use App;
use Closure; use Closure;
use Config;
use FireflyIII\Models\Preference;
use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Preferences;
/** /**
* Class Authenticate * Class Authenticate
@@ -48,6 +52,11 @@ class Authenticate
return redirect()->guest('auth/login'); return redirect()->guest('auth/login');
} }
} }
// if logged in, set user language:
$pref = Preferences::get('language', 'en');
App::setLocale($pref->data);
setlocale(LC_TIME, Config::get('firefly.locales.' . $pref->data));
return $next($request); return $next($request);
} }

View File

@@ -3,7 +3,6 @@
namespace FireflyIII\Http\Middleware; namespace FireflyIII\Http\Middleware;
use App;
use Closure; use Closure;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\PiggyBankRepetition;
@@ -46,24 +45,23 @@ class PiggyBanks
*/ */
public function handle(Request $request, Closure $next) public function handle(Request $request, Closure $next)
{ {
if ($this->auth->check() && !$request->isXmlHttpRequest() && App::environment() != 'testing') { if ($this->auth->check() && !$request->isXmlHttpRequest()) {
// get piggy banks without a repetition: // get piggy banks without a repetition:
/** @var Collection $set */ /** @var Collection $set */
$set = $this->auth->user()->piggybanks() $set = $this->auth->user()->piggybanks()
->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id') ->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id')
->whereNull('piggy_bank_repetitions.id') ->whereNull('piggy_bank_repetitions.id')
->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']); ->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']);
if ($set->count() > 0) {
/** @var PiggyBank $partialPiggy */ /** @var PiggyBank $partialPiggy */
foreach ($set as $partialPiggy) { foreach ($set as $partialPiggy) {
$repetition = new PiggyBankRepetition; $repetition = new PiggyBankRepetition;
$repetition->piggyBank()->associate($partialPiggy); $repetition->piggyBank()->associate($partialPiggy);
$repetition->startdate = is_null($partialPiggy->startdate) ? null : $partialPiggy->startdate; $repetition->startdate = $partialPiggy->startdate;
$repetition->targetdate = is_null($partialPiggy->targetdate) ? null : $partialPiggy->targetdate; $repetition->targetdate = $partialPiggy->targetdate;
$repetition->currentamount = 0; $repetition->currentamount = 0;
$repetition->save(); $repetition->save();
} }
}
unset($partialPiggy, $set, $repetition); unset($partialPiggy, $set, $repetition);
} }

View File

@@ -43,29 +43,27 @@ class Range
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @param \Closure $theNext * @param \Closure $theNext
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* *
* @return mixed * @return mixed
*/ */
public function handle(Request $request, Closure $theNext) public function handle(Request $request, Closure $theNext)
{ {
if ($this->auth->check() && App::environment() != 'testing') { if ($this->auth->check()) {
// ignore preference. set the range to be the current month: // ignore preference. set the range to be the current month:
if (!Session::has('start') && !Session::has('end')) { if (!Session::has('start') && !Session::has('end')) {
/** @var \FireflyIII\Models\Preference $viewRange */ /** @var \FireflyIII\Models\Preference $viewRange */
$viewRange = Preferences::get('viewRange', '1M'); $viewRange = Preferences::get('viewRange', '1M')->data;
$start = new Carbon; $start = new Carbon;
$start = Navigation::updateStartDate($viewRange->data, $start); $start = Navigation::updateStartDate($viewRange, $start);
$end = Navigation::updateEndDate($viewRange->data, $start); $end = Navigation::updateEndDate($viewRange, $start);
Session::put('start', $start); Session::put('start', $start);
Session::put('end', $end); Session::put('end', $end);
} }
if (!Session::has('first')) { if (!Session::has('first')) {
/**
* Get helper thing.
*/
/** @var \FireflyIII\Repositories\Journal\JournalRepositoryInterface $repository */ /** @var \FireflyIII\Repositories\Journal\JournalRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Journal\JournalRepositoryInterface'); $repository = App::make('FireflyIII\Repositories\Journal\JournalRepositoryInterface');
$journal = $repository->first(); $journal = $repository->first();
@@ -75,16 +73,12 @@ class Range
Session::put('first', Carbon::now()->startOfYear()); Session::put('first', Carbon::now()->startOfYear());
} }
} }
$current = Carbon::now()->formatLocalized('%B %Y');
// set current / next / prev month. $next = Carbon::now()->endOfMonth()->addDay()->formatLocalized('%B %Y');
$current = Carbon::now()->format('F Y'); $prev = Carbon::now()->startOfMonth()->subDay()->formatLocalized('%B %Y');
$next = Carbon::now()->endOfMonth()->addDay()->format('F Y');
$prev = Carbon::now()->startOfMonth()->subDay()->format('F Y');
View::share('currentMonthName', $current); View::share('currentMonthName', $current);
View::share('previousMonthName', $prev); View::share('previousMonthName', $prev);
View::share('nextMonthName', $next); View::share('nextMonthName', $next);
} }
return $theNext($request); return $theNext($request);

View File

@@ -46,36 +46,25 @@ class Reminders
*/ */
public function handle(Request $request, Closure $next) public function handle(Request $request, Closure $next)
{ {
if ($this->auth->check() && !$request->isXmlHttpRequest() && App::environment() != 'testing') { if ($this->auth->check() && !$request->isXmlHttpRequest()) {
// do reminders stuff. // do reminders stuff.
$piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get(); $piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get();
$today = new Carbon;
/** @var \FireflyIII\Helpers\Reminders\ReminderHelperInterface $helper */ /** @var \FireflyIII\Helpers\Reminders\ReminderHelperInterface $helper */
$helper = App::make('FireflyIII\Helpers\Reminders\ReminderHelperInterface'); $helper = App::make('FireflyIII\Helpers\Reminders\ReminderHelperInterface');
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) { foreach ($piggyBanks as $piggyBank) {
$ranges = $helper->getReminderRanges($piggyBank); $helper->createReminders($piggyBank, new Carbon);
foreach ($ranges as $range) {
if ($today < $range['end'] && $today > $range['start']) {
// create a reminder here!
$helper->createReminder($piggyBank, $range['start'], $range['end']);
// stop looping, we're done.
break;
}
}
} }
// delete invalid reminders // delete invalid reminders
$set = $this->auth->user()->reminders()->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')->whereNull('piggy_banks.id')->get( // this is a construction SQLITE cannot handle :(
['reminders.id'] if (env('DB_CONNECTION') != 'sqlite') {
); Reminder::whereUserId($this->auth->user()->id)
foreach ($set as $reminder) { ->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')
$reminder->delete(); ->whereNull('piggy_banks.id')
->delete();
} }
// get and list active reminders: // get and list active reminders:
$reminders = $this->auth->user()->reminders()->today()->get(); $reminders = $this->auth->user()->reminders()->today()->get();
$reminders->each( $reminders->each(

View File

@@ -50,12 +50,11 @@ class JournalFormRequest extends Request
/** /**
* @return array * @return array
* @throws Exception * @throws Exception
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
public function rules() public function rules()
{ {
// can we switch on the "what"?
$what = Input::get('what'); $what = Input::get('what');
$rules = [ $rules = [
'description' => 'required|min:1,max:255', 'description' => 'required|min:1,max:255',
'what' => 'required|in:withdrawal,deposit,transfer', 'what' => 'required|in:withdrawal,deposit,transfer',
@@ -74,8 +73,6 @@ class JournalFormRequest extends Request
if (intval(Input::get('budget_id')) != 0) { if (intval(Input::get('budget_id')) != 0) {
$rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets'; $rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets';
} }
break; break;
case 'deposit': case 'deposit':
$rules['category'] = 'between:1,255'; $rules['category'] = 'between:1,255';
@@ -93,7 +90,5 @@ class JournalFormRequest extends Request
} }
return $rules; return $rules;
} }
} }

View File

@@ -48,6 +48,7 @@ class TagFormRequest extends Request
'date' => 'date', 'date' => 'date',
'latitude' => 'numeric|min:-90|max:90', 'latitude' => 'numeric|min:-90|max:90',
'longitude' => 'numeric|min:-90|max:90', 'longitude' => 'numeric|min:-90|max:90',
'zoomLevel' => 'numeric|min:0|max:80',
'tagMode' => 'required|in:nothing,balancingAct,advancePayment' 'tagMode' => 'required|in:nothing,balancingAct,advancePayment'
]; ];
} }

View File

@@ -20,7 +20,7 @@ Breadcrumbs::register(
'home', 'home',
function (Generator $breadcrumbs) { function (Generator $breadcrumbs) {
$breadcrumbs->push('Home', route('index')); $breadcrumbs->push(trans('breadcrumbs.home'), route('index'));
} }
); );
@@ -28,38 +28,32 @@ Breadcrumbs::register(
'index', 'index',
function (Generator $breadcrumbs) { function (Generator $breadcrumbs) {
$breadcrumbs->push('Home', route('index')); $breadcrumbs->push(trans('breadcrumbs.home'), route('index'));
} }
); );
//trans('breadcrumbs.')
// accounts // accounts
Breadcrumbs::register( Breadcrumbs::register(
'accounts.index', function (Generator $breadcrumbs, $what) { 'accounts.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(ucfirst(e($what)) . ' accounts', route('accounts.index', $what)); $breadcrumbs->push(trans('breadcrumbs.' . strtolower(e($what)) . '_accounts'), route('accounts.index', $what));
} }
); );
Breadcrumbs::register(
'accounts.create', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push(trans('breadcrumbs.new_' . strtolower(e($what)) . '_account'), route('accounts.create', $what));
}
);
Breadcrumbs::register( Breadcrumbs::register(
'accounts.show', function (Generator $breadcrumbs, Account $account) { 'accounts.show', function (Generator $breadcrumbs, Account $account) {
switch ($account->accountType->type) {
default: $what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
throw new FireflyException('Cannot handle account type "' . e($account->accountType->type) . '"');
break;
case 'Default account':
case 'Asset account':
$what = 'asset';
break;
case 'Cash account':
$what = 'cash';
break;
case 'Expense account':
case 'Beneficiary account':
$what = 'expense';
break;
case 'Revenue account':
$what = 'revenue';
break;
}
$breadcrumbs->parent('accounts.index', $what); $breadcrumbs->parent('accounts.index', $what);
$breadcrumbs->push(e($account->name), route('accounts.show', $account->id)); $breadcrumbs->push(e($account->name), route('accounts.show', $account->id));
} }
@@ -67,14 +61,17 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'accounts.delete', function (Generator $breadcrumbs, Account $account) { 'accounts.delete', function (Generator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account); $breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Delete ' . e($account->name), route('accounts.delete', $account->id)); $breadcrumbs->push(trans('breadcrumbs.delete_account', ['name' => e($account->name)]), route('accounts.delete', $account->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'accounts.edit', function (Generator $breadcrumbs, Account $account) { 'accounts.edit', function (Generator $breadcrumbs, Account $account) {
$breadcrumbs->parent('accounts.show', $account); $breadcrumbs->parent('accounts.show', $account);
$breadcrumbs->push('Edit ' . e($account->name), route('accounts.edit', $account->id)); $what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$breadcrumbs->push(trans('breadcrumbs.edit_'.$what.'_account', ['name' => e($account->name)]), route('accounts.edit', $account->id));
} }
); );
@@ -82,26 +79,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'budgets.index', function (Generator $breadcrumbs) { 'budgets.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Budgets', route('budgets.index')); $breadcrumbs->push(trans('breadcrumbs.budgets'), route('budgets.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'budgets.create', function (Generator $breadcrumbs) { 'budgets.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('budgets.index'); $breadcrumbs->parent('budgets.index');
$breadcrumbs->push('Create new budget', route('budgets.create')); $breadcrumbs->push(trans('breadcrumbs.newBudget'), route('budgets.create'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'budgets.edit', function (Generator $breadcrumbs, Budget $budget) { 'budgets.edit', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget); $breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Edit ' . e($budget->name), route('budgets.edit', $budget->id)); $breadcrumbs->push(trans('breadcrumbs.edit_budget', ['name' => e($budget->name)]), route('budgets.edit', $budget->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'budgets.delete', function (Generator $breadcrumbs, Budget $budget) { 'budgets.delete', function (Generator $breadcrumbs, Budget $budget) {
$breadcrumbs->parent('budgets.show', $budget); $breadcrumbs->parent('budgets.show', $budget);
$breadcrumbs->push('Delete ' . e($budget->name), route('budgets.delete', $budget->id)); $breadcrumbs->push(trans('breadcrumbs.delete_budget', ['name' => e($budget->name)]), route('budgets.delete', $budget->id));
} }
); );
@@ -128,26 +125,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'categories.index', function (Generator $breadcrumbs) { 'categories.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Categories', route('categories.index')); $breadcrumbs->push(trans('breadcrumbs.categories'), route('categories.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'categories.create', function (Generator $breadcrumbs) { 'categories.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('categories.index'); $breadcrumbs->parent('categories.index');
$breadcrumbs->push('Create new category', route('categories.create')); $breadcrumbs->push(trans('breadcrumbs.newCategory'), route('categories.create'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'categories.edit', function (Generator $breadcrumbs, Category $category) { 'categories.edit', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category); $breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Edit ' . e($category->name), route('categories.edit', $category->id)); $breadcrumbs->push(trans('breadcrumbs.edit_category', ['name' => e($category->name)]), route('categories.edit', $category->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'categories.delete', function (Generator $breadcrumbs, Category $category) { 'categories.delete', function (Generator $breadcrumbs, Category $category) {
$breadcrumbs->parent('categories.show', $category); $breadcrumbs->parent('categories.show', $category);
$breadcrumbs->push('Delete ' . e($category->name), route('categories.delete', $category->id)); $breadcrumbs->push(trans('breadcrumbs.delete_category', ['name' => e($category->name)]), route('categories.delete', $category->id));
} }
); );
@@ -170,20 +167,20 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'currency.index', function (Generator $breadcrumbs) { 'currency.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Currencies', route('currency.index')); $breadcrumbs->push(trans('breadcrumbs.currencies'), route('currency.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'currency.edit', function (Generator $breadcrumbs, TransactionCurrency $currency) { 'currency.edit', function (Generator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index'); $breadcrumbs->parent('currency.index');
$breadcrumbs->push('Edit ' . $currency->name, route('currency.edit', $currency->id)); $breadcrumbs->push(trans('breadcrumbs.edit_currency', ['name' => e($currency->name)]), route('currency.edit', $currency->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'currency.delete', function (Generator $breadcrumbs, TransactionCurrency $currency) { 'currency.delete', function (Generator $breadcrumbs, TransactionCurrency $currency) {
$breadcrumbs->parent('currency.index'); $breadcrumbs->parent('currency.index');
$breadcrumbs->push('Delete ' . $currency->name, route('currency.delete', $currency->id)); $breadcrumbs->push(trans('breadcrumbs.delete_currency', ['name' => e($currency->name)]), route('currency.delete', $currency->id));
} }
); );
@@ -192,26 +189,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'piggy-banks.index', function (Generator $breadcrumbs) { 'piggy-banks.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Piggy banks', route('piggy-banks.index')); $breadcrumbs->push(trans('breadcrumbs.piggyBanks'), route('piggy-banks.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'piggy-banks.create', function (Generator $breadcrumbs) { 'piggy-banks.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('piggy-banks.index'); $breadcrumbs->parent('piggy-banks.index');
$breadcrumbs->push('Create new piggy bank', route('piggy-banks.create')); $breadcrumbs->push(trans('breadcrumbs.newPiggyBank'), route('piggy-banks.create'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'piggy-banks.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) { 'piggy-banks.edit', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank); $breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push('Edit ' . e($piggyBank->name), route('piggy-banks.edit', $piggyBank->id)); $breadcrumbs->push(trans('breadcrumbs.edit_piggyBank', ['name' => e($piggyBank->name)]), route('piggy-banks.edit', $piggyBank->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'piggy-banks.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) { 'piggy-banks.delete', function (Generator $breadcrumbs, PiggyBank $piggyBank) {
$breadcrumbs->parent('piggy-banks.show', $piggyBank); $breadcrumbs->parent('piggy-banks.show', $piggyBank);
$breadcrumbs->push('Delete ' . e($piggyBank->name), route('piggy-banks.delete', $piggyBank->id)); $breadcrumbs->push(trans('breadcrumbs.delete_piggyBank', ['name' => e($piggyBank->name)]), route('piggy-banks.delete', $piggyBank->id));
} }
); );
@@ -227,7 +224,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'preferences', function (Generator $breadcrumbs) { 'preferences', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Preferences', route('preferences')); $breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences'));
} }
); );
@@ -236,14 +233,14 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'profile', function (Generator $breadcrumbs) { 'profile', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Profile', route('profile')); $breadcrumbs->push(trans('breadcrumbs.profile'), route('profile'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'change-password', function (Generator $breadcrumbs) { 'change-password', function (Generator $breadcrumbs) {
$breadcrumbs->parent('profile'); $breadcrumbs->parent('profile');
$breadcrumbs->push('Change your password', route('change-password')); $breadcrumbs->push(trans('breadcrumbs.changePassword'), route('change-password'));
} }
); );
@@ -252,26 +249,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'bills.index', function (Generator $breadcrumbs) { 'bills.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Bills', route('bills.index')); $breadcrumbs->push(trans('breadcrumbs.bills'), route('bills.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'bills.create', function (Generator $breadcrumbs) { 'bills.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('bills.index'); $breadcrumbs->parent('bills.index');
$breadcrumbs->push('Create new bill', route('bills.create')); $breadcrumbs->push(trans('breadcrumbs.newBill'), route('bills.create'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'bills.edit', function (Generator $breadcrumbs, Bill $bill) { 'bills.edit', function (Generator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill); $breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push('Edit ' . e($bill->name), route('bills.edit', $bill->id)); $breadcrumbs->push(trans('breadcrumbs.edit_bill', ['name' => e($bill->name)]), route('bills.edit', $bill->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'bills.delete', function (Generator $breadcrumbs, Bill $bill) { 'bills.delete', function (Generator $breadcrumbs, Bill $bill) {
$breadcrumbs->parent('bills.show', $bill); $breadcrumbs->parent('bills.show', $bill);
$breadcrumbs->push('Delete ' . e($bill->name), route('bills.delete', $bill->id)); $breadcrumbs->push(trans('breadcrumbs.delete_bill', ['name' => e($bill->name)]), route('bills.delete', $bill->id));
} }
); );
@@ -287,7 +284,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'reminders.index', function (Generator $breadcrumbs) { 'reminders.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Reminders', route('reminders.index')); $breadcrumbs->push(trans('breadcrumbs.reminders'), route('reminders.index'));
} }
); );
@@ -296,7 +293,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) { 'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) {
$breadcrumbs->parent('reminders.index'); $breadcrumbs->parent('reminders.index');
$breadcrumbs->push('Reminder #' . $reminder->id, route('reminders.show', $reminder->id)); $breadcrumbs->push(trans('breadcrumbs.reminder', ['id' => e($reminder->id)]), route('reminders.show', $reminder->id));
} }
); );
@@ -306,28 +303,33 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'reports.index', function (Generator $breadcrumbs) { 'reports.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Reports', route('reports.index')); $breadcrumbs->push(trans('breadcrumbs.reports'), route('reports.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'reports.year', function (Generator $breadcrumbs, Carbon $date) { 'reports.year', function (Generator $breadcrumbs, Carbon $date, $shared) {
$breadcrumbs->parent('reports.index'); $breadcrumbs->parent('reports.index');
$breadcrumbs->push($date->year, route('reports.year', $date->year)); 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( Breadcrumbs::register(
'reports.month', function (Generator $breadcrumbs, Carbon $date) { 'reports.month', function (Generator $breadcrumbs, Carbon $date, $shared) {
$breadcrumbs->parent('reports.year', $date); $breadcrumbs->parent('reports.year', $date, $shared);
$breadcrumbs->push('Monthly report for ' . $date->format('F Y'), route('reports.month', [$date->year, $date->month]));
}
);
Breadcrumbs::register( if ($shared) {
'reports.budget', function (Generator $breadcrumbs, Carbon $date) { $title = trans('breadcrumbs.monthly_report_shared', ['date' => $date->year]);
$breadcrumbs->parent('reports.index'); } else {
$breadcrumbs->push('Budget report for ' . $date->format('F Y'), route('reports.budget', [$date->year, $date->month])); $title = trans('breadcrumbs.monthly_report', ['date' => $date->year]);
}
$breadcrumbs->push($title, route('reports.month', [$date->year, $date->month]));
} }
); );
@@ -335,7 +337,7 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'search', function (Generator $breadcrumbs, $query) { 'search', function (Generator $breadcrumbs, $query) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Search for "' . e($query) . '"', route('search')); $breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search'));
} }
); );
@@ -343,47 +345,26 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'transactions.index', function (Generator $breadcrumbs, $what) { 'transactions.index', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', $what));
switch ($what) {
case 'expenses':
case 'withdrawal':
$subTitle = 'Expenses';
break;
case 'revenue':
case 'deposit':
$subTitle = 'Revenue, income and deposits';
break;
case 'transfer':
case 'transfers':
$subTitle = 'Transfers';
break;
case 'opening balance':
$subTitle = 'Opening balances';
break;
default:
throw new FireflyException('Cannot handle $what "' . e($what) . '" in bread crumbs');
}
$breadcrumbs->push($subTitle, route('transactions.index', $what));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'transactions.create', function (Generator $breadcrumbs, $what) { 'transactions.create', function (Generator $breadcrumbs, $what) {
$breadcrumbs->parent('transactions.index', $what); $breadcrumbs->parent('transactions.index', $what);
$breadcrumbs->push('Create new ' . e($what), route('transactions.create', $what)); $breadcrumbs->push(trans('breadcrumbs.create_' . e($what)), route('transactions.create', $what));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $journal) { 'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal); $breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Edit ' . e($journal->description), route('transactions.edit', $journal->id)); $breadcrumbs->push(trans('breadcrumbs.edit_journal', ['description' => $journal->description]), route('transactions.edit', $journal->id));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $journal) { 'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.show', $journal); $breadcrumbs->parent('transactions.show', $journal);
$breadcrumbs->push('Delete ' . e($journal->description), route('transactions.delete', $journal->id)); $breadcrumbs->push(trans('breadcrumbs.delete_journal', ['description' => e($journal->description)]), route('transactions.delete', $journal->id));
} }
); );
@@ -391,7 +372,7 @@ Breadcrumbs::register(
'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) { 'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) {
$breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type)); $breadcrumbs->parent('transactions.index', strtolower($journal->transactionType->type));
$breadcrumbs->push(e($journal->description), route('transactions.show', $journal->id)); $breadcrumbs->push($journal->description, route('transactions.show', $journal->id));
} }
); );
@@ -400,16 +381,32 @@ Breadcrumbs::register(
Breadcrumbs::register( Breadcrumbs::register(
'tags.index', function (Generator $breadcrumbs) { 'tags.index', function (Generator $breadcrumbs) {
$breadcrumbs->parent('home'); $breadcrumbs->parent('home');
$breadcrumbs->push('Tags', route('tags.index')); $breadcrumbs->push(trans('breadcrumbs.tags'), route('tags.index'));
} }
); );
Breadcrumbs::register( Breadcrumbs::register(
'tags.create', function (Generator $breadcrumbs) { 'tags.create', function (Generator $breadcrumbs) {
$breadcrumbs->parent('tags.index'); $breadcrumbs->parent('tags.index');
$breadcrumbs->push('Create tag', route('tags.create')); $breadcrumbs->push(trans('breadcrumbs.createTag'), route('tags.create'));
} }
); );
Breadcrumbs::register(
'tags.edit', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag);
$breadcrumbs->push(trans('breadcrumbs.edit_tag', ['tag' => e($tag->tag)]), route('tags.edit', $tag->id));
}
);
Breadcrumbs::register(
'tags.delete', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.show', $tag);
$breadcrumbs->push(trans('breadcrumbs.delete_tag', ['tag' => e($tag->tag)]), route('tags.delete', $tag->id));
}
);
Breadcrumbs::register( Breadcrumbs::register(
'tags.show', function (Generator $breadcrumbs, Tag $tag) { 'tags.show', function (Generator $breadcrumbs, Tag $tag) {
$breadcrumbs->parent('tags.index'); $breadcrumbs->parent('tags.index');

View File

@@ -223,7 +223,6 @@ Route::group(
Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching. Route::get('/bills/rescan/{bill}', ['uses' => 'BillController@rescan', 'as' => 'bills.rescan']); # rescan for matching.
Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']); Route::get('/bills/create', ['uses' => 'BillController@create', 'as' => 'bills.create']);
Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']); Route::get('/bills/edit/{bill}', ['uses' => 'BillController@edit', 'as' => 'bills.edit']);
Route::get('/bills/add/{bill}', ['uses' => 'BillController@add', 'as' => 'bills.add']);
Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']); Route::get('/bills/delete/{bill}', ['uses' => 'BillController@delete', 'as' => 'bills.delete']);
Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']); Route::get('/bills/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']);
Route::post('/bills/store', ['uses' => 'BillController@store', 'as' => 'bills.store']); Route::post('/bills/store', ['uses' => 'BillController@store', 'as' => 'bills.store']);
@@ -273,22 +272,41 @@ Route::group(
/** /**
* Google Chart Controller * ALL CHART Controllers
*/ */
Route::get('/chart/home/account', ['uses' => 'GoogleChartController@allAccountsBalanceChart']); // accounts:
Route::get('/chart/home/budgets', ['uses' => 'GoogleChartController@allBudgetsHomeChart']); Route::get('/chart/account/frontpage', ['uses' => 'Chart\AccountController@frontpage']);
Route::get('/chart/home/categories', ['uses' => 'GoogleChartController@allCategoriesHomeChart']); Route::get('/chart/account/month/{year}/{month}/{shared?}', ['uses' => 'Chart\AccountController@all'])->where(
Route::get('/chart/home/bills', ['uses' => 'GoogleChartController@billsOverview']); ['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']
Route::get('/chart/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']); );
Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']); Route::get('/chart/account/{account}', ['uses' => 'Chart\AccountController@single']);
Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending'])->where(['year' => '[0-9]+']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']);
Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']); // bills:
Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']); Route::get('/chart/bill/frontpage', ['uses' => 'Chart\BillController@frontpage']);
Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']); Route::get('/chart/bill/{bill}', ['uses' => 'Chart\BillController@single']);
Route::get('/chart/piggy-history/{piggyBank}', ['uses' => 'GoogleChartController@piggyBankHistory']);
Route::get('/chart/category/{category}/period', ['uses' => 'GoogleChartController@categoryPeriodChart']); // budgets:
Route::get('/chart/category/{category}/overview', ['uses' => 'GoogleChartController@categoryOverviewChart']); 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']);
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/year/{year}/{shared?}', ['uses' => 'Chart\CategoryController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/category/{category}/month', ['uses' => 'Chart\CategoryController@month']); // should be period.
Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']);
// piggy banks:
Route::get('/chart/piggyBank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']);
// reports:
Route::get('/chart/report/in-out/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOut'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/chart/report/in-out-sum/{year}/{shared?}', ['uses' => 'Chart\ReportController@yearInOutSummarized'])->where(
['year' => '[0-9]{4}', 'shared' => 'shared']
);
/** /**
* Help Controller * Help Controller
@@ -306,10 +324,7 @@ Route::group(
Route::get('/json/box/out', ['uses' => 'JsonController@boxOut', 'as' => 'json.box.out']); Route::get('/json/box/out', ['uses' => 'JsonController@boxOut', 'as' => 'json.box.out']);
Route::get('/json/box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'json.box.paid']); Route::get('/json/box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'json.box.paid']);
Route::get('/json/box/bills-paid', ['uses' => 'JsonController@boxBillsPaid', 'as' => 'json.box.unpaid']); Route::get('/json/box/bills-paid', ['uses' => 'JsonController@boxBillsPaid', 'as' => 'json.box.unpaid']);
Route::get('/json/show-shared-reports', 'JsonController@showSharedReports');
Route::get('/json/transaction-journals/{what}', 'JsonController@transactionJournals'); Route::get('/json/transaction-journals/{what}', 'JsonController@transactionJournals');
Route::get('/json/show-shared-reports/set', 'JsonController@setSharedReports');
/** /**
* Piggy Bank Controller * Piggy Bank Controller
@@ -355,19 +370,13 @@ Route::group(
* Report Controller * Report Controller
*/ */
Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']); Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year']); //Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}']);
Route::get('/reports/{year}/{month}', ['uses' => 'ReportController@month', 'as' => 'reports.month']); Route::get('/reports/{year}/{shared?}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']);
Route::get('/reports/budget/{year}/{month}', ['uses' => 'ReportController@budget', 'as' => 'reports.budget']); 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: // pop ups for budget report:
Route::get('/reports/modal/{account}/{year}/{month}/no-budget', ['uses' => 'ReportController@modalNoBudget', 'as' => 'reports.no-budget']);
Route::get(
'/reports/modal/{account}/{year}/{month}/balanced-transfers',
['uses' => 'ReportController@modalBalancedTransfers', 'as' => 'reports.balanced-transfers']
);
Route::get(
'/reports/modal/{account}/{year}/{month}/left-unbalanced', ['uses' => 'ReportController@modalLeftUnbalanced', 'as' => 'reports.left-unbalanced']
);
/** /**
* Search Controller * Search Controller

View File

@@ -28,7 +28,7 @@ class Account extends Model
/** /**
* @param array $fields * @param array $fields
* * @SuppressWarnings(PHPMD.CyclomaticComplexity)
* *
* @return Account|null * @return Account|null
*/ */
@@ -133,6 +133,7 @@ class Account extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string
@@ -160,6 +161,7 @@ class Account extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param array $types * @param array $types
*/ */
@@ -174,6 +176,7 @@ class Account extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param string $name * @param string $name
* @param string $value * @param string $value
@@ -191,6 +194,7 @@ class Account extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setNameAttribute($value) public function setNameAttribute($value)

View File

@@ -37,6 +37,7 @@ class Category extends Model
/** /**
* @param array $fields * @param array $fields
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* *
* @return Account|null * @return Account|null
*/ */
@@ -79,6 +80,7 @@ class Category extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setNameAttribute($value) public function setNameAttribute($value)
@@ -89,6 +91,7 @@ class Category extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string

View File

@@ -64,6 +64,7 @@ class PiggyBank extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return int * @return int
@@ -93,6 +94,7 @@ class PiggyBank extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setNameAttribute($value) public function setNameAttribute($value)
@@ -103,6 +105,7 @@ class PiggyBank extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string

View File

@@ -18,6 +18,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return int * @return int
@@ -38,6 +39,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return mixed * @return mixed
@@ -53,6 +55,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return bool * @return bool
@@ -73,6 +76,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
@@ -86,6 +90,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* *
* @return $this * @return $this
@@ -100,6 +105,7 @@ class Reminder extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setMetadataAttribute($value) public function setMetadataAttribute($value)

View File

@@ -29,6 +29,8 @@ class Tag extends Model
/** /**
* @param array $fields * @param array $fields
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* *
* @return Tag|null * @return Tag|null
*/ */
@@ -77,6 +79,7 @@ class Tag extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string
@@ -88,6 +91,7 @@ class Tag extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string
@@ -99,6 +103,7 @@ class Tag extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setDescriptionAttribute($value) public function setDescriptionAttribute($value)
@@ -108,6 +113,7 @@ class Tag extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setTagAttribute($value) public function setTagAttribute($value)

View File

@@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Query\JoinClause;
use Watson\Validating\ValidatingTrait; use Watson\Validating\ValidatingTrait;
/** /**
@@ -61,20 +62,90 @@ class TransactionJournal extends Model
/** /**
* @return float * @return float
*/ */
public function getAmountAttribute() public function getActualAmountAttribute()
{ {
$amount = 0;
/** @var Transaction $t */ /** @var Transaction $t */
foreach ($this->transactions as $t) { foreach ($this->transactions as $t) {
if ($t->amount > 0) { if ($t->amount > 0) {
return floatval($t->amount); $amount = floatval($t->amount);
} }
} }
return 0; return $amount;
} }
/** /**
* @return Account|mixed * @return float
*/
public function getAmountAttribute()
{
$amount = 0;
/** @var Transaction $t */
foreach ($this->transactions as $t) {
if ($t->amount > 0) {
$amount = floatval($t->amount);
}
}
/*
* If the journal has tags, it gets complicated.
*/
if ($this->tags->count() == 0) {
return $amount;
}
// if journal is part of advancePayment AND journal is a withdrawal,
// then journal is being repaid by other journals, so the actual amount will lower:
/** @var Tag $advancePayment */
$advancePayment = $this->tags()->where('tagMode', 'advancePayment')->first();
if ($advancePayment && $this->transactionType->type == 'Withdrawal') {
// loop other deposits, remove from our amount.
$others = $advancePayment->transactionJournals()->transactionTypes(['Deposit'])->get();
foreach ($others as $other) {
$amount -= $other->actualAmount;
}
return $amount;
}
// if this journal is part of an advancePayment AND the journal is a deposit,
// then the journal amount is correcting a withdrawal, and the amount is zero:
if ($advancePayment && $this->transactionType->type == 'Deposit') {
return 0;
}
// is balancing act?
$balancingAct = $this->tags()->where('tagMode', 'balancingAct')->first();
if ($balancingAct) {
// this is the transfer
// this is the expense:
if ($this->transactionType->type == 'Withdrawal') {
$transfer = $balancingAct->transactionJournals()->transactionTypes(['Transfer'])->first();
if ($transfer) {
$amount -= $transfer->actualAmount;
return $amount;
}
}
}
return $amount;
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
return $this->belongsToMany('FireflyIII\Models\Tag');
}
/**
* @return Account
*/ */
public function getAssetAccountAttribute() public function getAssetAccountAttribute()
{ {
@@ -93,7 +164,7 @@ class TransactionJournal extends Model
} }
return $this->transactions()->first(); return $this->transactions()->first()->account;
} }
/** /**
@@ -105,6 +176,28 @@ class TransactionJournal extends Model
return $this->hasMany('FireflyIII\Models\Transaction'); return $this->hasMany('FireflyIII\Models\Transaction');
} }
/**
* @return float
*/
public function getCorrectedActualAmountAttribute()
{
$amount = 0;
$type = $this->transactionType->type;
/** @var Transaction $t */
foreach ($this->transactions as $t) {
if ($t->amount > 0 && $type != 'Withdrawal') {
$amount = floatval($t->amount);
break;
}
if ($t->amount < 0 && $type == 'Withdrawal') {
$amount = floatval($t->amount);
break;
}
}
return $amount;
}
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
* @return array * @return array
@@ -116,6 +209,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
* *
* @return string * @return string
@@ -140,6 +234,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Account $account * @param Account $account
*/ */
@@ -154,6 +249,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Carbon $date * @param Carbon $date
* *
@@ -166,6 +262,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Carbon $date * @param Carbon $date
* *
@@ -178,6 +275,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param $amount * @param $amount
*/ */
@@ -195,6 +293,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param Carbon $date * @param Carbon $date
* *
@@ -206,7 +305,26 @@ class TransactionJournal extends Model
} }
/** /**
* Returns the account to which the money was moved.
*
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param EloquentBuilder $query
* @param Account $account
*/
public function scopeToAccountIs(EloquentBuilder $query, Account $account)
{
$query->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '>', 0);
}
);
$query->where('transactions.account_id', $account->id);
}
/**
* @codeCoverageIgnore
*
* @param EloquentBuilder $query * @param EloquentBuilder $query
* @param array $types * @param array $types
*/ */
@@ -239,6 +357,7 @@ class TransactionJournal extends Model
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
*
* @param $value * @param $value
*/ */
public function setDescriptionAttribute($value) public function setDescriptionAttribute($value)
@@ -247,15 +366,6 @@ class TransactionJournal extends Model
$this->attributes['encrypted'] = true; $this->attributes['encrypted'] = true;
} }
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
return $this->belongsToMany('FireflyIII\Models\Tag');
}
/** /**
* @codeCoverageIgnore * @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo * @return \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@@ -17,6 +17,8 @@ class ConfigServiceProvider extends ServiceProvider
* to overwrite any "vendor" or package configuration that you may want to * to overwrite any "vendor" or package configuration that you may want to
* modify before the application handles the incoming request / command. * modify before the application handles the incoming request / command.
* *
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @return void * @return void
*/ */
public function register() public function register()
@@ -163,6 +165,7 @@ class ConfigServiceProvider extends ServiceProvider
'Session', 'Session',
'Route', 'Route',
'Auth', 'Auth',
'Lang',
'URL', 'URL',
'Config', 'Config',
'ExpandedForm' => [ 'ExpandedForm' => [

View File

@@ -49,7 +49,47 @@ class EventServiceProvider extends ServiceProvider
public function boot(DispatcherContract $events) public function boot(DispatcherContract $events)
{ {
parent::boot($events); parent::boot($events);
$this->registerDeleteEvents();
$this->registerCreateEvents();
BudgetLimit::saved(
function (BudgetLimit $budgetLimit) {
$end = Navigation::addPeriod(clone $budgetLimit->startdate, $budgetLimit->repeat_freq, 0);
$end->subDay();
$set = $budgetLimit->limitrepetitions()->where('startdate', $budgetLimit->startdate->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))
->get();
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $end;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);
try {
$repetition->save();
} catch (QueryException $e) {
Log::error('Trying to save new LimitRepetition failed!');
Log::error($e->getMessage());
}
} else {
if ($set->count() == 1) {
$repetition = $set->first();
$repetition->amount = $budgetLimit->amount;
$repetition->save();
}
}
}
);
}
/**
*
*/
protected function registerDeleteEvents()
{
TransactionJournal::deleted( TransactionJournal::deleted(
function (TransactionJournal $journal) { function (TransactionJournal $journal) {
@@ -59,8 +99,6 @@ class EventServiceProvider extends ServiceProvider
} }
} }
); );
PiggyBank::deleting( PiggyBank::deleting(
function (PiggyBank $piggyBank) { function (PiggyBank $piggyBank) {
$reminders = $piggyBank->reminders()->get(); $reminders = $piggyBank->reminders()->get();
@@ -82,6 +120,14 @@ class EventServiceProvider extends ServiceProvider
} }
); );
}
/**
*
*/
protected function registerCreateEvents()
{
// move this routine to a filter // move this routine to a filter
// in case of repeated piggy banks and/or other problems. // in case of repeated piggy banks and/or other problems.
PiggyBank::created( PiggyBank::created(
@@ -94,47 +140,6 @@ class EventServiceProvider extends ServiceProvider
$repetition->save(); $repetition->save();
} }
); );
BudgetLimit::saved(
function (BudgetLimit $budgetLimit) {
$end = Navigation::addPeriod(clone $budgetLimit->startdate, $budgetLimit->repeat_freq, 0);
$end->subDay();
$set = $budgetLimit->limitrepetitions()->where('startdate', $budgetLimit->startdate->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))
->get();
/*
* Create new LimitRepetition:
*/
if ($set->count() == 0) {
$repetition = new LimitRepetition;
$repetition->startdate = $budgetLimit->startdate;
$repetition->enddate = $end;
$repetition->amount = $budgetLimit->amount;
$repetition->budgetLimit()->associate($budgetLimit);
try {
$repetition->save();
} catch (QueryException $e) {
Log::error('Trying to save new LimitRepetition failed!');
Log::error($e->getMessage());
}
} else {
if ($set->count() == 1) {
/*
* Update existing one.
*/
$repetition = $set->first();
$repetition->amount = $budgetLimit->amount;
$repetition->save();
}
}
}
);
} }
} }

View File

@@ -10,9 +10,9 @@ use FireflyIII\Support\Preferences;
use FireflyIII\Support\Steam; use FireflyIII\Support\Steam;
use FireflyIII\Support\Twig\Budget; use FireflyIII\Support\Twig\Budget;
use FireflyIII\Support\Twig\General; use FireflyIII\Support\Twig\General;
use FireflyIII\Support\Twig\Translation;
use FireflyIII\Support\Twig\Journal; use FireflyIII\Support\Twig\Journal;
use FireflyIII\Support\Twig\PiggyBank; use FireflyIII\Support\Twig\PiggyBank;
use FireflyIII\Support\Twig\Translation;
use FireflyIII\Validation\FireflyValidator; use FireflyIII\Validation\FireflyValidator;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Twig; use Twig;
@@ -46,6 +46,9 @@ class FireflyServiceProvider extends ServiceProvider
Twig::addExtension(new Translation); Twig::addExtension(new Translation);
} }
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function register() public function register()
{ {

View File

@@ -162,8 +162,6 @@ class AccountRepository implements AccountRepositoryInterface
->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC'); ->orderBy('transaction_journals.id', 'DESC');
$query->before(Session::get('end', Carbon::now()->endOfMonth()));
$query->after(Session::get('start', Carbon::now()->startOfMonth()));
$count = $query->count(); $count = $query->count();
$set = $query->take(50)->offset($offset)->get(['transaction_journals.*']); $set = $query->take(50)->offset($offset)->get(['transaction_journals.*']);
$paginator = new LengthAwarePaginator($set, $count, 50, $page); $paginator = new LengthAwarePaginator($set, $count, 50, $page);
@@ -308,12 +306,14 @@ class AccountRepository implements AccountRepositoryInterface
/** /**
* @param Account $account * @param Account $account
* @param Carbon $date
* *
* @return float * @return float
*/ */
public function leftOnAccount(Account $account) public function leftOnAccount(Account $account, Carbon $date)
{ {
$balance = Steam::balance($account, null, true);
$balance = Steam::balance($account, $date, true);
/** @var PiggyBank $p */ /** @var PiggyBank $p */
foreach ($account->piggybanks()->get() as $p) { foreach ($account->piggybanks()->get() as $p) {
$balance -= $p->currentRelevantRep()->currentamount; $balance -= $p->currentRelevantRep()->currentamount;

View File

@@ -115,10 +115,11 @@ interface AccountRepositoryInterface
/** /**
* @param Account $account * @param Account $account
* @param Carbon $date
* *
* @return float * @return float
*/ */
public function leftOnAccount(Account $account); public function leftOnAccount(Account $account, Carbon $date);
/** /**
* @param Account $account * @param Account $account

View File

@@ -22,6 +22,27 @@ use Navigation;
*/ */
class BillRepository implements BillRepositoryInterface class BillRepository implements BillRepositoryInterface
{ {
/**
* Returns the sum of all payments connected to this bill between the dates.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end)
{
$amount = 0;
$journals = $bill->transactionjournals()->before($end)->after($start)->get();
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$amount += $journal->amount;
}
return $amount;
}
/** /**
* Create a fake bill to help the chart controller. * Create a fake bill to help the chart controller.
* *
@@ -251,13 +272,10 @@ class BillRepository implements BillRepositoryInterface
*/ */
public function scan(Bill $bill, TransactionJournal $journal) public function scan(Bill $bill, TransactionJournal $journal)
{ {
/* $amountMatch = false;
* Match words.
*/
$wordMatch = false; $wordMatch = false;
$matches = explode(',', $bill->match); $matches = explode(',', $bill->match);
$description = strtolower($journal->description); $description = strtolower($journal->description);
Log::debug('Now scanning ' . $description);
/* /*
* Attach expense account to description for more narrow matching. * Attach expense account to description for more narrow matching.
@@ -295,7 +313,6 @@ class BillRepository implements BillRepositoryInterface
* Match amount. * Match amount.
*/ */
$amountMatch = false;
if (count($transactions) > 1) { if (count($transactions) > 1) {
$amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount)); $amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount));

View File

@@ -15,6 +15,17 @@ use Illuminate\Support\Collection;
interface BillRepositoryInterface interface BillRepositoryInterface
{ {
/**
* Returns the sum of all payments connected to this bill between the dates.
*
* @param Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function billPaymentsInRange(Bill $bill, Carbon $start, Carbon $end);
/** /**
* Create a fake bill to help the chart controller. * Create a fake bill to help the chart controller.
* *

View File

@@ -8,6 +8,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Input; use Input;
@@ -48,9 +49,11 @@ class BudgetRepository implements BudgetRepositoryInterface
* *
* @return float * @return float
*/ */
public function expensesOnDay(Budget $budget, Carbon $date) public function expensesOnDayCorrected(Budget $budget, Carbon $date)
{ {
return floatval($budget->transactionjournals()->lessThan(0)->transactionTypes(['Withdrawal'])->onDate($date)->sum('amount')); $sum = floatval($budget->transactionjournals()->transactionTypes(['Withdrawal'])->onDate($date)->get(['transaction_journals.*'])->sum('amount'));
return $sum * -1;
} }
/** /**
@@ -98,7 +101,6 @@ class BudgetRepository implements BudgetRepositoryInterface
public function getBudgets() public function getBudgets()
{ {
$budgets = Auth::user()->budgets()->get(); $budgets = Auth::user()->budgets()->get();
$budgets->sortBy('name');
return $budgets; return $budgets;
} }
@@ -246,27 +248,45 @@ class BudgetRepository implements BudgetRepositoryInterface
->whereNotNull('budget_transaction_journal.budget_id'); ->whereNotNull('budget_transaction_journal.budget_id');
} }
) )
->before($end)
->after($start) ->after($start)
->before($end)
->lessThan(0) ->lessThan(0)
->transactionTypes(['Withdrawal']) ->transactionTypes(['Withdrawal'])
->get(); ->sum('transactions.amount');
return floatval($noBudgetSet->sum('amount')) * -1; return floatval($noBudgetSet) * -1;
} }
/** /**
* @param Budget $budget * @param Budget $budget
* @param Carbon $date * @param Carbon $start
* @param Carbon $end
* @param bool $shared
* *
* @return float * @return float
*/ */
public function spentInMonth(Budget $budget, Carbon $date) public function spentInPeriodCorrected(Budget $budget, Carbon $start, Carbon $end, $shared = true)
{ {
$end = clone $date; if ($shared === true) {
$date->startOfMonth(); // get everything:
$end->endOfMonth(); $sum = floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->get(['transaction_journals.*'])->sum('amount'));
$sum = floatval($budget->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1; } else {
// get all journals in this month where the asset account is NOT shared.
$sum = $budget->transactionjournals()
->before($end)
->after($start)
->lessThan(0)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->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 = floatval($sum);
}
return $sum; return $sum;
} }
@@ -289,17 +309,7 @@ class BudgetRepository implements BudgetRepositoryInterface
return $newBudget; return $newBudget;
} }
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function sumBudgetExpensesInPeriod(Budget $budget, $start, $end)
{
return floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
}
/** /**
* @param Budget $budget * @param Budget $budget

View File

@@ -27,12 +27,14 @@ interface BudgetRepositoryInterface
public function destroy(Budget $budget); public function destroy(Budget $budget);
/** /**
* Takes tags into account.
*
* @param Budget $budget * @param Budget $budget
* @param Carbon $date * @param Carbon $date
* *
* @return float * @return float
*/ */
public function expensesOnDay(Budget $budget, Carbon $date); public function expensesOnDayCorrected(Budget $budget, Carbon $date);
/** /**
* @return Collection * @return Collection
@@ -123,12 +125,17 @@ interface BudgetRepositoryInterface
public function getWithoutBudgetSum(Carbon $start, Carbon $end); public function getWithoutBudgetSum(Carbon $start, Carbon $end);
/** /**
*
* Same as ::spentInPeriod but corrects journals for their amount (tags).
*
* @param Budget $budget * @param Budget $budget
* @param Carbon $date * @param Carbon $start
* @param Carbon $end
* @param boolean $shared
* *
* @return float * @return float
*/ */
public function spentInMonth(Budget $budget, Carbon $date); public function spentInPeriodCorrected(Budget $budget, Carbon $start, Carbon $end, $shared = true);
/** /**
* @param array $data * @param array $data
@@ -137,15 +144,6 @@ interface BudgetRepositoryInterface
*/ */
public function store(array $data); public function store(array $data);
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function sumBudgetExpensesInPeriod(Budget $budget, $start, $end);
/** /**
* @param Budget $budget * @param Budget $budget
* @param array $data * @param array $data

View File

@@ -4,6 +4,7 @@ namespace FireflyIII\Repositories\Category;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use Crypt;
use DB; use DB;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -58,33 +59,44 @@ class CategoryRepository implements CategoryRepositoryInterface
} }
/** /**
*
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @return Collection * @return Collection
*/ */
public function getCategoriesAndExpenses($start, $end) public function getCategoriesAndExpensesCorrected($start, $end)
{ {
return TransactionJournal:: $set = Auth::user()->transactionjournals()
where('transaction_journals.user_id', Auth::user()->id)
->leftJoin(
'transactions',
function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
}
)
->leftJoin( ->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id' 'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
) )
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id') ->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->before($end) ->before($end)
->where('categories.user_id', Auth::user()->id) ->where('categories.user_id', Auth::user()->id)
->after($start) ->after($start)
->where('transaction_types.type', 'Withdrawal') ->transactionTypes(['Withdrawal'])
->groupBy('categories.id') ->groupBy('categories.id')
->orderBy('sum', 'DESC') ->get(['categories.id as category_id', 'categories.encrypted as category_encrypted', 'categories.name', 'transaction_journals.*']);
->get(['categories.id', 'categories.encrypted', 'categories.name', DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
$result = [];
foreach ($set as $entry) {
$categoryId = intval($entry->category_id);
if (isset($result[$categoryId])) {
$result[$categoryId]['sum'] += floatval($entry->amount);
} else {
$isEncrypted = intval($entry->category_encrypted) == 1 ? true : false;
$name = strlen($entry->name) == 0 ? trans('firefly.noCategory') : $entry->name;
$name = $isEncrypted ? Crypt::decrypt($name) : $name;
$result[$categoryId] = [
'name' => $name,
'sum' => floatval($entry->amount),
];
}
}
return $result;
} }
/** /**
@@ -168,22 +180,54 @@ class CategoryRepository implements CategoryRepositoryInterface
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @param bool $shared
*
* @return float * @return float
*/ */
public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end) public function spentInPeriodCorrected(Category $category, Carbon $start, Carbon $end, $shared = false)
{ {
return floatval($category->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1; if ($shared === true) {
// shared is true.
// always ignore transfers between accounts!
$sum = floatval(
$category->transactionjournals()
->transactionTypes(['Withdrawal'])
->before($end)->after($start)->get(['transaction_journals.*'])->sum('amount')
);
} else {
// do something else, SEE budgets.
// get all journals in this month where the asset account is NOT shared.
$sum = $category->transactionjournals()
->before($end)
->after($start)
->transactionTypes(['Withdrawal'])
->lessThan(0)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->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 = floatval($sum);
}
return $sum;
} }
/** /**
* Corrected for tags
*
* @param Category $category * @param Category $category
* @param Carbon $date * @param Carbon $date
* *
* @return float * @return float
*/ */
public function spentOnDaySum(Category $category, Carbon $date) public function spentOnDaySumCorrected(Category $category, Carbon $date)
{ {
return floatval($category->transactionjournals()->onDate($date)->lessThan(0)->sum('amount')) * -1; return floatval($category->transactionjournals()->onDate($date)->get(['transaction_journals.*'])->sum('amount'));
} }
/** /**

View File

@@ -33,12 +33,14 @@ interface CategoryRepositoryInterface
public function getCategories(); public function getCategories();
/** /**
* Corrected for tags.
*
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @return Collection * @return Collection
*/ */
public function getCategoriesAndExpenses($start, $end); public function getCategoriesAndExpensesCorrected($start, $end);
/** /**
* @param Category $category * @param Category $category
@@ -71,21 +73,28 @@ interface CategoryRepositoryInterface
public function getWithoutCategory(Carbon $start, Carbon $end); public function getWithoutCategory(Carbon $start, Carbon $end);
/** /**
* Corrected for tags.
*
* @param Category $category * @param Category $category
* @param \Carbon\Carbon $start * @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end * @param \Carbon\Carbon $end
* *
* @param bool $shared
*
* @return float * @return float
*/ */
public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end); public function spentInPeriodCorrected(Category $category, Carbon $start, Carbon $end, $shared = false);
/** /**
*
* Corrected for tags.
*
* @param Category $category * @param Category $category
* @param Carbon $date * @param Carbon $date
* *
* @return float * @return float
*/ */
public function spentOnDaySum(Category $category, Carbon $date); public function spentOnDaySumCorrected(Category $category, Carbon $date);
/** /**
* @param array $data * @param array $data

View File

@@ -338,61 +338,86 @@ class JournalRepository implements JournalRepositoryInterface
*/ */
protected function storeAccounts(TransactionType $type, array $data) protected function storeAccounts(TransactionType $type, array $data)
{ {
$from = null; $fromAccount = null;
$to = null; $toAccount = null;
switch ($type->type) { switch ($type->type) {
case 'Withdrawal': case 'Withdrawal':
list($fromAccount, $toAccount) = $this->storeWithdrawalAccounts($data);
$from = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$to = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
break; break;
case 'Deposit': case 'Deposit':
$to = Account::find($data['account_id']); list($fromAccount, $toAccount) = $this->storeDepositAccounts($data);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$from = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
break; break;
case 'Transfer': case 'Transfer':
$from = Account::find($data['account_from_id']); $fromAccount = Account::find($data['account_from_id']);
$to = Account::find($data['account_to_id']); $toAccount = Account::find($data['account_to_id']);
break; break;
} }
if (is_null($to) || (!is_null($to) && is_null($to->id))) {
if (is_null($toAccount)) {
Log::error('"to"-account is null, so we cannot continue!'); Log::error('"to"-account is null, so we cannot continue!');
App::abort(500, '"to"-account is null, so we cannot continue!'); App::abort(500, '"to"-account is null, so we cannot continue!');
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
if (is_null($from) || (!is_null($from) && is_null($from->id))) { if (is_null($fromAccount)) {
Log::error('"from"-account is null, so we cannot continue!'); Log::error('"from"-account is null, so we cannot continue!');
App::abort(500, '"from"-account is null, so we cannot continue!'); App::abort(500, '"from"-account is null, so we cannot continue!');
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
return [$from, $to]; return [$fromAccount, $toAccount];
}
/**
* @param array $data
*
* @return array
*/
protected function storeWithdrawalAccounts(array $data)
{
$fromAccount = Account::find($data['account_id']);
if (strlen($data['expense_account']) > 0) {
$toType = AccountType::where('type', 'Expense account')->first();
$toAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => $data['expense_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$toAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
return [$fromAccount, $toAccount];
}
/**
* @param array $data
*
* @return array
*/
protected function storeDepositAccounts(array $data)
{
$toAccount = Account::find($data['account_id']);
if (strlen($data['revenue_account']) > 0) {
$fromType = AccountType::where('type', 'Revenue account')->first();
$fromAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['revenue_account'], 'active' => 1]
);
} else {
$toType = AccountType::where('type', 'Cash account')->first();
$fromAccount = Account::firstOrCreateEncrypted(
['user_id' => $data['user'], 'account_type_id' => $toType->id, 'name' => 'Cash account', 'active' => 1]
);
}
return [$fromAccount, $toAccount];
} }
} }

View File

@@ -9,7 +9,6 @@ use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\PiggyBankRepetition;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation;
/** /**
* Class PiggyBankRepository * Class PiggyBankRepository
@@ -19,57 +18,6 @@ use Navigation;
class PiggyBankRepository implements PiggyBankRepositoryInterface class PiggyBankRepository implements PiggyBankRepositoryInterface
{ {
/**
*
* Based on the piggy bank, the reminder-setting and
* other variables this method tries to divide the piggy bank into equal parts. Each is
* accommodated by a reminder (if everything goes to plan).
*
* @param PiggyBankRepetition $repetition
*
* @return Collection
*/
public function calculateParts(PiggyBankRepetition $repetition)
{
/** @var PiggyBank $piggyBank */
$piggyBank = $repetition->piggyBank()->first();
$bars = new Collection;
$currentStart = clone $repetition->startdate;
if (is_null($piggyBank->reminder)) {
$entry = ['repetition' => $repetition, 'amountPerBar' => floatval($piggyBank->targetamount),
'currentAmount' => floatval($repetition->currentamount), 'cumulativeAmount' => floatval($piggyBank->targetamount),
'startDate' => clone $repetition->startdate, 'targetDate' => clone $repetition->targetdate];
$bars->push($this->createPiggyBankPart($entry));
return $bars;
}
while ($currentStart < $repetition->targetdate) {
$currentTarget = Navigation::endOfX($currentStart, $piggyBank->reminder, $repetition->targetdate);
$entry = ['repetition' => $repetition, 'amountPerBar' => null, 'currentAmount' => floatval($repetition->currentamount),
'cumulativeAmount' => null, 'startDate' => $currentStart, 'targetDate' => $currentTarget];
$bars->push($this->createPiggyBankPart($entry));
$currentStart = clone $currentTarget;
$currentStart->addDay();
}
$amountPerBar = floatval($piggyBank->targetamount) / $bars->count();
$cumulative = $amountPerBar;
/** @var PiggyBankPart $bar */
foreach ($bars as $index => $bar) {
$bar->setAmountPerBar($amountPerBar);
$bar->setCumulativeAmount($cumulative);
if ($bars->count() - 1 == $index) {
$bar->setCumulativeAmount($piggyBank->targetamount);
}
$cumulative += $amountPerBar;
}
return $bars;
}
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* @param $amount * @param $amount
@@ -83,24 +31,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return true; return true;
} }
/**
* @param array $data
*
* @return PiggyBankPart
*/
public function createPiggyBankPart(array $data)
{
$part = new PiggyBankPart;
$part->setRepetition($data['repetition']);
$part->setAmountPerBar($data['amountPerBar']);
$part->setCurrentamount($data['currentAmount']);
$part->setCumulativeAmount($data['cumulativeAmount']);
$part->setStartdate($data['startDate']);
$part->setTargetdate($data['targetDate']);
return $part;
}
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* *
@@ -198,7 +128,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
* @param array $data * @param array $data
* *
* @return PiggyBank * @return PiggyBank
* @internal param PiggyBank $account
*/ */
public function update(PiggyBank $piggyBank, array $data) public function update(PiggyBank $piggyBank, array $data)
{ {

View File

@@ -14,18 +14,6 @@ use Illuminate\Support\Collection;
interface PiggyBankRepositoryInterface interface PiggyBankRepositoryInterface
{ {
/**
*
* Based on the piggy bank, the reminder-setting and
* other variables this method tries to divide the piggy bank into equal parts. Each is
* accommodated by a reminder (if everything goes to plan).
*
* @param PiggyBankRepetition $repetition
*
* @return Collection
*/
public function calculateParts(PiggyBankRepetition $repetition);
/** /**
* @return Collection * @return Collection
*/ */
@@ -38,13 +26,6 @@ interface PiggyBankRepositoryInterface
*/ */
public function getEvents(PiggyBank $piggyBank); public function getEvents(PiggyBank $piggyBank);
/**
* @param array $data
*
* @return PiggyBankPart
*/
public function createPiggyBankPart(array $data);
/** /**
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* @param $amount * @param $amount

View File

@@ -4,6 +4,8 @@ namespace FireflyIII\Repositories\Tag;
use Auth; use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
@@ -17,7 +19,10 @@ use Illuminate\Support\Collection;
class TagRepository implements TagRepositoryInterface class TagRepository implements TagRepositoryInterface
{ {
/** /**
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five.
*
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param Tag $tag * @param Tag $tag
* *
@@ -33,89 +38,55 @@ class TagRepository implements TagRepositoryInterface
return false; return false;
} }
if ($tag->tagMode == 'nothing') { switch ($tag->tagMode) {
// save it, no problem: case 'nothing':
$journal->tags()->save($tag); $journal->tags()->save($tag);
return true; return true;
break;
case 'balancingAct':
return $this->connectBalancingAct($journal, $tag);
break;
case 'advancePayment':
return $this->connectAdvancePayment($journal, $tag);
break;
} }
/* return false;
* get some withdrawal types: }
/**
* 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.
*
* This method effectively tells you the amount of money that has been balanced out
* correctly in the given period for the given account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/ */
/** @var TransactionType $withdrawal */ public function coveredByBalancingActs(Account $account, Carbon $start, Carbon $end)
$withdrawal = TransactionType::whereType('Withdrawal')->first(); {
/** @var TransactionType $deposit */ // the quickest way to do this is by scanning all balancingAct tags
$deposit = TransactionType::whereType('Deposit')->first(); // because there will be less of them any way.
/** @var TransactionType $transfer */ $tags = Auth::user()->tags()->where('tagMode', 'balancingAct')->get();
$transfer = TransactionType::whereType('Transfer')->first(); $amount = 0;
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count(); /** @var Tag $tag */
$transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count(); foreach ($tags as $tag) {
$deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count(); $transfer = $tag->transactionjournals()->after($start)->before($end)->toAccountIs($account)->transactionTypes(['Transfer'])->first();
if ($transfer) {
if ($tag->tagMode == 'balancingAct') { $amount += $transfer->amount;
// only if this is the only withdrawal.
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) {
$journal->tags()->save($tag);
return true;
}
// and only if this is the only transfer
if ($journal->transaction_type_id == $transfer->id && $transfers < 1) {
$journal->tags()->save($tag);
return true;
}
// ignore expense
return false;
}
if ($tag->tagMode == 'advancePayment') {
// advance payments cannot accept transfers:
if ($journal->transaction_type_id == $transfer->id) {
return false;
}
// the first transaction to be attached to this
// tag is attached just like that:
if ($withdrawals < 1 && $deposits < 1) {
$journal->tags()->save($tag);
return true;
}
// if withdrawal and already has a withdrawal, return false:
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals == 1) {
return false;
}
// if already has transaction journals, must match ALL asset account id's:
if ($deposits > 0 || $withdrawals == 1) {
$match = true;
/** @var TransactionJournal $check */
foreach ($tag->transactionjournals as $check) {
if ($check->assetAccount->id != $journal->assetAccount->id) {
$match = false;
} }
} }
if ($match) {
$journal->tags()->save($tag);
return true; return $amount;
} }
}
return false;
}
// @codeCoverageIgnoreStart
return false;
}
// @codeCoverageIgnoreEnd
/** /**
* @param Tag $tag * @param Tag $tag
* *
@@ -127,6 +98,7 @@ class TagRepository implements TagRepositoryInterface
return true; return true;
} }
// @codeCoverageIgnoreEnd
/** /**
* @return Collection * @return Collection
@@ -186,4 +158,107 @@ class TagRepository implements TagRepositoryInterface
return $tag; return $tag;
} }
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return boolean
*/
protected function connectBalancingAct(TransactionJournal $journal, Tag $tag)
{
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
$transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count();
// only if this is the only withdrawal.
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) {
$journal->tags()->save($tag);
return true;
}
// and only if this is the only transfer
if ($journal->transaction_type_id == $transfer->id && $transfers < 1) {
$journal->tags()->save($tag);
return true;
}
// ignore expense
return false;
}
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return boolean
*/
protected function connectAdvancePayment(TransactionJournal $journal, Tag $tag)
{
/** @var TransactionType $transfer */
$transfer = TransactionType::whereType('Transfer')->first();
/** @var TransactionType $withdrawal */
$withdrawal = TransactionType::whereType('Withdrawal')->first();
/** @var TransactionType $deposit */
$deposit = TransactionType::whereType('Deposit')->first();
$withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count();
$deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count();
// advance payments cannot accept transfers:
if ($journal->transaction_type_id == $transfer->id) {
return false;
}
// the first transaction to be attached to this
// tag is attached just like that:
if ($withdrawals < 1 && $deposits < 1) {
$journal->tags()->save($tag);
return true;
}
// if withdrawal and already has a withdrawal, return false:
if ($journal->transaction_type_id == $withdrawal->id && $withdrawals == 1) {
return false;
}
// if already has transaction journals, must match ALL asset account id's:
if ($deposits > 0 || $withdrawals == 1) {
return $this->matchAll($journal, $tag);
}
return false;
}
/**
* @param TransactionJournal $journal
* @param Tag $tag
*
* @return bool
*/
protected function matchAll(TransactionJournal $journal, Tag $tag)
{
$match = true;
/** @var TransactionJournal $check */
foreach ($tag->transactionjournals as $check) {
if ($check->assetAccount->id != $journal->assetAccount->id) {
$match = false;
}
}
if ($match) {
$journal->tags()->save($tag);
return true;
}
return false;
}
} }

View File

@@ -2,6 +2,8 @@
namespace FireflyIII\Repositories\Tag; namespace FireflyIII\Repositories\Tag;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -15,6 +17,23 @@ use Illuminate\Support\Collection;
interface TagRepositoryInterface interface TagRepositoryInterface
{ {
/**
* 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.
*
* This method effectively tells you the amount of money that has been balanced out
* correctly in the given period for the given account.
*
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function coveredByBalancingActs(Account $account, Carbon $start, Carbon $end);
/** /**
* @param array $data * @param array $data
* *

View File

@@ -84,32 +84,20 @@ class Amount
*/ */
public function formatJournal(TransactionJournal $journal, $coloured = true) public function formatJournal(TransactionJournal $journal, $coloured = true)
{ {
$showPositive = true;
if (is_null($journal->symbol)) { if (is_null($journal->symbol)) {
$symbol = $journal->transactionCurrency->symbol; $symbol = $journal->transactionCurrency->symbol;
} else { } else {
$symbol = $journal->symbol; $symbol = $journal->symbol;
} }
$amount = 0; $amount = $journal->amount;
if ($journal->transactionType->type == 'Withdrawal') {
if (is_null($journal->type)) { $amount = $amount * -1;
$type = $journal->transactionType->type;
} else {
$type = $journal->type;
} }
if ($journal->transactionType->type == 'Transfer' && $coloured) {
if ($type == 'Withdrawal') { return '<span class="text-info">' . $this->formatWithSymbol($symbol, $amount, false) . '</span>';
$showPositive = false;
}
foreach ($journal->transactions as $t) {
if (floatval($t->amount) > 0 && $showPositive === true) {
$amount = floatval($t->amount);
break;
}
if (floatval($t->amount) < 0 && $showPositive === false) {
$amount = floatval($t->amount);
} }
if ($journal->transactionType->type == 'Transfer' && !$coloured) {
return $this->formatWithSymbol($symbol, $amount, false);
} }
return $this->formatWithSymbol($symbol, $amount, $coloured); return $this->formatWithSymbol($symbol, $amount, $coloured);

View File

@@ -51,29 +51,8 @@ class ExpandedForm
if (isset($options['label'])) { if (isset($options['label'])) {
return $options['label']; return $options['label'];
} }
$labels = [
'amount_min' => 'Amount (min)',
'amount_max' => 'Amount (max)',
'match' => 'Matches on',
'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from',
'account_to_id' => 'Account to',
'account_id' => 'Asset account',
'budget_id' => 'Budget',
'openingBalance' => 'Opening balance',
'tagMode' => 'Tag mode',
'tagPosition' => 'Tag location',
'virtualBalance' => 'Virtual balance',
'longitude_latitude' => 'Location',
'targetamount' => 'Target amount',
'accountRole' => 'Account role',
'openingBalanceDate' => 'Opening balance date',
'ccType' => 'Credit card payment plan',
'ccMonthlyPaymentDate' => 'Credit card monthly payment date',
'piggy_bank_id' => 'Piggy bank'];
return trans('form.' . $name);
return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name));
} }

View File

@@ -28,21 +28,11 @@ class Navigation
$add = ($skip + 1); $add = ($skip + 1);
$functionMap = [ $functionMap = [
'1D' => 'addDays', '1D' => 'addDays', 'daily' => 'addDays',
'daily' => 'addDays', '1W' => 'addWeeks', 'weekly' => 'addWeeks', 'week' => 'addWeeks',
'1W' => 'addWeeks', '1M' => 'addMonths', 'month' => 'addMonths', 'monthly' => 'addMonths', '3M' => 'addMonths',
'weekly' => 'addWeeks', 'quarter' => 'addMonths', 'quarterly' => 'addMonths', '6M' => 'addMonths', 'half-year' => 'addMonths',
'week' => 'addWeeks', 'year' => 'addYears', 'yearly' => 'addYears',
'1M' => 'addMonths',
'month' => 'addMonths',
'monthly' => 'addMonths',
'3M' => 'addMonths',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'6M' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYears',
'yearly' => 'addYears',
]; ];
$modifierMap = [ $modifierMap = [
'quarter' => 3, 'quarter' => 3,
@@ -75,21 +65,11 @@ class Navigation
$currentEnd = clone $theCurrentEnd; $currentEnd = clone $theCurrentEnd;
$functionMap = [ $functionMap = [
'1D' => 'addDay', '1D' => 'addDay', 'daily' => 'addDay',
'daily' => 'addDay', '1W' => 'addWeek', 'week' => 'addWeek', 'weekly' => 'addWeek',
'1W' => 'addWeek', '1M' => 'addMonth', 'month' => 'addMonth', 'monthly' => 'addMonth',
'week' => 'addWeek', '3M' => 'addMonths', 'quarter' => 'addMonths', 'quarterly' => 'addMonths', '6M' => 'addMonths', 'half-year' => 'addMonths',
'weekly' => 'addWeek', 'year' => 'addYear', 'yearly' => 'addYear',
'1M' => 'addMonth',
'month' => 'addMonth',
'monthly' => 'addMonth',
'3M' => 'addMonths',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'6M' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYear',
'yearly' => 'addYear',
]; ];
$modifierMap = [ $modifierMap = [
'quarter' => 3, 'quarter' => 3,

View File

@@ -23,12 +23,9 @@ class Steam
* *
* @return float * @return float
*/ */
public function balance(Account $account, Carbon $date = null, $ignoreVirtualBalance = false) public function balance(Account $account, Carbon $date, $ignoreVirtualBalance = false)
{ {
$date = is_null($date) ? Carbon::now() : $date;
// find the first known transaction on this account: // find the first known transaction on this account:
//
$firstDateObject = $account $firstDateObject = $account
->transactions() ->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
@@ -37,12 +34,6 @@ class Steam
$firstDate = is_null($firstDateObject) ? clone $date : new Carbon($firstDateObject->date); $firstDate = is_null($firstDateObject) ? clone $date : new Carbon($firstDateObject->date);
$date = $date < $firstDate ? $firstDate : $date; $date = $date < $firstDate ? $firstDate : $date;
/**
*select * from transactions
* left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
* order by date ASC
*/
$balance = floatval( $balance = floatval(
$account->transactions()->leftJoin( $account->transactions()->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'

View File

@@ -39,6 +39,22 @@ class Budget extends Twig_Extension
} }
); );
$functions[] = new Twig_SimpleFunction(
'spentInRepetitionCorrected', function (LimitRepetition $repetition) {
$sum
= Auth::user()->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->before($repetition->enddate)
->after($repetition->startdate)
->where('limit_repetitions.id', '=', $repetition->id)
->get(['transaction_journals.*'])->sum('amount');
return floatval($sum);
}
);
return $functions; return $functions;
} }

View File

@@ -3,10 +3,12 @@
namespace FireflyIII\Support\Twig; namespace FireflyIII\Support\Twig;
use App; use App;
use Carbon\Carbon;
use Config; use Config;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use Route; use Route;
use Session;
use Twig_Extension; use Twig_Extension;
use Twig_SimpleFilter; use Twig_SimpleFilter;
use Twig_SimpleFunction; use Twig_SimpleFunction;
@@ -21,6 +23,7 @@ class General extends Twig_Extension
/** /**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @return array * @return array
*/ */
public function getFilters() public function getFilters()
@@ -56,8 +59,9 @@ class General extends Twig_Extension
if (is_null($account)) { if (is_null($account)) {
return 'NULL'; return 'NULL';
} }
$date = Session::get('end', Carbon::now()->endOfMonth());
return App::make('steam')->balance($account); return App::make('steam')->balance($account, $date);
} }
); );

View File

@@ -18,6 +18,7 @@ class Journal extends Twig_Extension
{ {
/** /**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @return array * @return array
*/ */
public function getFilters() public function getFilters()
@@ -27,20 +28,25 @@ class Journal extends Twig_Extension
$filters[] = new Twig_SimpleFilter( $filters[] = new Twig_SimpleFilter(
'typeIcon', function (TransactionJournal $journal) { 'typeIcon', function (TransactionJournal $journal) {
$type = $journal->transactionType->type; $type = $journal->transactionType->type;
if ($type == 'Withdrawal') {
return '<span class="glyphicon glyphicon-arrow-left" title="Withdrawal"></span>'; switch ($type) {
} case 'Withdrawal':
if ($type == 'Deposit') { return '<span class="glyphicon glyphicon-arrow-left" title="' . trans('firefly.withdrawal') . '"></span>';
return '<span class="glyphicon glyphicon-arrow-right" title="Deposit"></span>'; break;
} case 'Deposit':
if ($type == 'Transfer') { return '<span class="glyphicon glyphicon-arrow-right" title="' . trans('firefly.deposit') . '"></span>';
return '<i class="fa fa-fw fa-exchange" title="Transfer"></i>'; break;
} case 'Transfer':
if ($type == 'Opening balance') { return '<i class="fa fa-fw fa-exchange" title="' . trans('firefly.transfer') . '"></i>';
return '<span class="glyphicon glyphicon-ban-circle" title="Opening balance"></span>'; break;
case 'Opening balance':
return '<span class="glyphicon glyphicon-ban-circle" title="' . trans('firefly.openingBalance') . '"></span>';
break;
default:
return '';
break;
} }
return '';
}, ['is_safe' => ['html']] }, ['is_safe' => ['html']]
); );
@@ -49,6 +55,8 @@ class Journal extends Twig_Extension
} }
/** /**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @return array * @return array
*/ */
public function getFunctions() public function getFunctions()
@@ -70,13 +78,41 @@ class Journal extends Twig_Extension
if ($journal->tags->count() == 0) { if ($journal->tags->count() == 0) {
return App::make('amount')->formatJournal($journal); return App::make('amount')->formatJournal($journal);
} }
foreach ($journal->tags as $tag) { foreach ($journal->tags as $tag) {
if ($tag->tagMode == 'balancingAct') { if ($tag->tagMode == 'balancingAct') {
// return tag formatted for a "balancing act". // return tag formatted for a "balancing act", even if other
// tags are present.
$amount = App::make('amount')->formatJournal($journal, false); $amount = App::make('amount')->formatJournal($journal, false);
return '<a href="' . route('tags.show', $tag->id) . '" class="label label-success" title="' . $amount return '<a href="' . route('tags.show', $tag->id) . '" class="label label-success" title="' . $amount
. '"><i class="fa fa-fw fa-refresh"></i> ' . $tag->tag . '</span>'; . '"><i class="fa fa-fw fa-refresh"></i> ' . $tag->tag . '</a>';
}
/*
* AdvancePayment with a deposit will show the tag instead of the amount:
*/
if ($tag->tagMode == 'advancePayment' && $journal->transactionType->type == 'Deposit') {
$amount = App::make('amount')->formatJournal($journal, false);
return '<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>';
}
/*
* AdvancePayment with a withdrawal will show the amount with a link to
* the tag. The TransactionJournal should properly calculate the amount.
*/
if ($tag->tagMode == 'advancePayment' && $journal->transactionType->type == 'Withdrawal') {
$amount = App::make('amount')->formatJournal($journal);
return '<a href="' . route('tags.show', $tag->id) . '">' . $amount . '</a>';
}
if ($tag->tagMode == 'nothing') {
// return the amount:
return App::make('amount')->formatJournal($journal);
} }
} }

View File

@@ -24,7 +24,7 @@ class Translation extends Twig_Extension
$filters[] = new Twig_SimpleFilter( $filters[] = new Twig_SimpleFilter(
'_', function ($name) { '_', function ($name) {
return trans('firefly.'.$name); return trans('firefly.' . $name);
}, ['is_safe' => ['html']] }, ['is_safe' => ['html']]
); );

View File

@@ -29,10 +29,11 @@ class FireflyValidator extends Validator
* @param array $rules * @param array $rules
* @param array $messages * @param array $messages
* @param array $customAttributes * @param array $customAttributes
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/ */
public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = []) public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
{ {
parent::__construct($translator, $data, $rules, $messages); parent::__construct($translator, $data, $rules, $messages, $customAttributes);
} }
/** /**

View File

@@ -56,6 +56,7 @@ return [
'asset' => 'Asset accounts', 'asset' => 'Asset accounts',
'expense' => 'Expense accounts', 'expense' => 'Expense accounts',
'revenue' => 'Revenue accounts', 'revenue' => 'Revenue accounts',
'cash' => 'Cash accounts',
], ],
'subIconsByIdentifier' => 'subIconsByIdentifier' =>
[ [
@@ -89,6 +90,22 @@ return [
'Beneficiary account' => 'expense', 'Beneficiary account' => 'expense',
'Revenue account' => 'revenue', 'Revenue account' => 'revenue',
'Cash account' => 'cash', 'Cash account' => 'cash',
] ],
'lang' => [
'en' => 'English',
'nl' => 'Nederlands'
],
'locales' => [
'en' => ['en', 'English', 'en_US', 'en_US.utf8'],
'nl' => ['nl', 'Dutch', 'nl_NL', 'nl_NL.utf8'],
],
'month' => [
'en' => '%B %Y',
'nl' => '%B %Y',
],
'monthAndDay' => [
'en' => '%B %e, %Y',
'nl' => '%e %B %Y',
],
]; ];

2
database/.gitignore vendored
View File

@@ -1 +1 @@
*.sqlite

2
pu.sh
View File

@@ -11,7 +11,7 @@ fi
if [ ! -z "$1" ] if [ ! -z "$1" ]
then then
phpunit --verbose tests/repositories/$1.php phpunit --verbose tests/helpers/$1.php
fi fi
# restore .env file # restore .env file

View File

@@ -1,7 +1,7 @@
$(document).ready(function () { $(document).ready(function () {
if (typeof(googleComboChart) === 'function' && typeof(billID) !== 'undefined') { if (typeof(googleComboChart) === 'function' && typeof(billID) !== 'undefined') {
googleComboChart('chart/bills/' + billID, 'bill-overview'); googleComboChart('chart/bill/' + billID, 'bill-overview');
} }
} }
); );

View File

@@ -8,7 +8,7 @@ $(function () {
if (typeof budgetID !== 'undefined' && typeof repetitionID === 'undefined') { if (typeof budgetID !== 'undefined' && typeof repetitionID === 'undefined') {
googleColumnChart('chart/budget/' + budgetID + '/spending', 'budgetOverview'); googleColumnChart('chart/budget/' + budgetID, 'budgetOverview');
} }
if (typeof budgetID !== 'undefined' && typeof repetitionID !== 'undefined') { if (typeof budgetID !== 'undefined' && typeof repetitionID !== 'undefined') {
googleLineChart('chart/budget/' + budgetID + '/' + repetitionID, 'budgetOverview'); googleLineChart('chart/budget/' + budgetID + '/' + repetitionID, 'budgetOverview');
@@ -95,7 +95,9 @@ function updateTotal() {
} }
function updateIncome(e) { function updateIncome(e) {
$('#monthlyBudgetModal').empty().load('budgets/income').modal('show'); $('#monthlyBudgetModal').empty().load('budgets/income', function () {
$('#monthlyBudgetModal').modal('show');
});
return false; return false;
} }
@@ -112,7 +114,7 @@ function updateRanges() {
var value = parseInt(input.val()); var value = parseInt(input.val());
// calculate sum: // calculate sum:
sum += value sum += value;
// update small display: // update small display:
$('#budget-range-display-' + id).text('\u20AC ' + value.toFixed(2)); $('#budget-range-display-' + id).text('\u20AC ' + value.toFixed(2));

View File

@@ -1,8 +1,8 @@
$(function () { $(function () {
if (typeof categoryID !== 'undefined') { if (typeof categoryID !== 'undefined') {
googleColumnChart('chart/category/' + categoryID + '/overview', 'componentOverview'); googleColumnChart('chart/category/' + categoryID + '/all', 'all');
googleColumnChart('chart/category/' + categoryID + '/period', 'periodOverview'); googleColumnChart('chart/category/' + categoryID + '/month', 'month');
} }

View File

@@ -6,7 +6,7 @@ $(function () {
ranges[currentMonthName] = [moment().startOf('month'), moment().endOf('month')]; ranges[currentMonthName] = [moment().startOf('month'), moment().endOf('month')];
ranges[previousMonthName] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]; ranges[previousMonthName] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')];
ranges[nextMonthName] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')]; ranges[nextMonthName] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')];
ranges['Everything'] = [firstDate, moment()]; ranges[everything] = [firstDate, moment()];
$('#daterange').daterangepicker( $('#daterange').daterangepicker(
{ {
@@ -15,10 +15,19 @@ $(function () {
//View::share('nextMonthName', $next); //View::share('nextMonthName', $next);
ranges: ranges ranges: ranges,
,
opens: 'left', opens: 'left',
locale: {
applyLabel: applyLabel,
cancelLabel: cancelLabel,
fromLabel: fromLabel,
toLabel: toLabel,
weekLabel: 'W',
customRangeLabel: customRangeLabel,
daysOfWeek: moment.weekdaysMin(),
monthNames: moment.monthsShort(),
firstDay: moment.localeData()._week.dow
},
format: 'DD-MM-YYYY', format: 'DD-MM-YYYY',
startDate: start, startDate: start,
endDate: end endDate: end

View File

@@ -1,5 +1,5 @@
var google = google || {}; var google = google || {};
google.load('visualization', '1.1', {'packages': ['corechart', 'bar', 'line', 'sankey', 'table']}); google.load('visualization', '1.1', {'packages': ['corechart', 'bar', 'line'],'language': language });
function googleChart(chartType, URL, container, options) { function googleChart(chartType, URL, container, options) {
if ($('#' + container).length === 1) { if ($('#' + container).length === 1) {

View File

@@ -2,11 +2,12 @@ google.setOnLoadCallback(drawChart);
function drawChart() { function drawChart() {
googleLineChart('chart/home/account', 'accounts-chart'); googleLineChart('chart/account/frontpage', 'accounts-chart');
//googleColumnChart('chart/home/budgets', 'budgets-chart'); googlePieChart('chart/bill/frontpage', 'bills-chart');
googleStackedColumnChart('chart/home/budgets', 'budgets-chart'); googleStackedColumnChart('chart/budget/frontpage', 'budgets-chart');
googleColumnChart('chart/home/categories', 'categories-chart'); googleColumnChart('chart/category/frontpage', 'categories-chart');
googlePieChart('chart/home/bills', 'bills-chart');
getBoxAmounts(); getBoxAmounts();
} }

View File

@@ -3,7 +3,7 @@ $(function () {
$('.removeMoney').on('click', removeMoney); $('.removeMoney').on('click', removeMoney);
if (typeof(googleLineChart) === 'function' && typeof(piggyBankID) !== 'undefined') { if (typeof(googleLineChart) === 'function' && typeof(piggyBankID) !== 'undefined') {
googleLineChart('chart/piggy-history/' + piggyBankID, 'piggy-bank-history'); googleLineChart('chart/piggyBank/' + piggyBankID, 'piggy-bank-history');
} }
$('#sortable tbody').sortable( $('#sortable tbody').sortable(

View File

@@ -1,20 +1,26 @@
if (typeof(google) != 'undefined') { if (typeof(google) != 'undefined') {
google.setOnLoadCallback(drawChart); google.setOnLoadCallback(drawChart);
} }
function drawChart() { function drawChart() {
googleColumnChart('chart/reports/income-expenses/' + year, 'income-expenses-chart'); googleColumnChart('chart/report/in-out/' + year + shared, 'income-expenses-chart');
googleColumnChart('chart/reports/income-expenses-sum/' + year, 'income-expenses-sum-chart') googleColumnChart('chart/report/in-out-sum/' + year + shared, 'income-expenses-sum-chart');
googleStackedColumnChart('chart/budgets/spending/' + year, 'budgets'); googleStackedColumnChart('chart/budget/year/' + year + shared, 'budgets');
googleStackedColumnChart('chart/category/year/' + year + shared, 'categories');
googleLineChart('/chart/account/month/' + year + '/' + month + shared, 'account-balances-chart');
} }
$(function () { $(function () {
$('.openModal').on('click', openModal); $('.openModal').on('click', openModal);
includeSharedToggle();
$('#includeShared').click(includeSharedSet);
// click open the top X income list:
$('#showIncomes').click(showIncomes);
// click open the top X expense list:
$('#showExpenses').click(showExpenses);
}); });
function openModal(e) { function openModal(e) {
@@ -32,31 +38,44 @@ function openModal(e) {
return false; return false;
} }
function includeSharedToggle() {
// get setting from JSON.
$.getJSON('json/show-shared-reports').success(function (data) {
console.log('GO');
if (data.value == true) {
// show shared data, update button:
//<i class="state-icon glyphicon glyphicon-check"></i>
$('#includeShared').empty().addClass('btn-info').append($('<i>').addClass('state-icon glyphicon glyphicon-check')).append(' Include shared asset accounts').show();
console.log('true');
} else {
$('#includeShared').empty().removeClass('btn-info').append($('<i>').addClass('state-icon glyphicon glyphicon-unchecked')).append(' Include shared asset accounts').show();
console.log('false');
}
}).fail(function () {
console.log('fail');
});
}
function includeSharedSet() { function showIncomes() {
// get setting from JSON. "use strict";
$.getJSON('json/show-shared-reports/set').success(function (data) { if (incomeRestShow) {
console.log('Value is now: ' + data.value); // hide everything, make button say "show"
includeSharedToggle(); $('#showIncomes').text(showTheRest);
}).fail(function () { $('.incomesCollapsed').removeClass('in').addClass('out');
console.log('fail');
}); // 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() {
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; return false;
} }

View File

@@ -0,0 +1,89 @@
<?php
return [
'home' => 'Home',
// accounts
'asset_accounts' => 'Asset accounts',
'expense_accounts' => 'Expense accounts',
'revenue_accounts' => 'Revenue accounts',
'cash_accounts' => 'Cash accounts',
'new_asset_account' => 'New asset accounts',
'new_expense_account' => 'New expense account',
'new_revenue_account' => 'New revenue account',
'delete_account' => 'Delete account ":name"',
'edit_account' => 'Edit account ":name"',
'edit_asset_account' => 'Edit asset account ":name"',
'edit_expense_account' => 'Edit expense account ":name"',
'edit_revenue_account' => 'Edit revenue account ":name"',
// budgets
'budgets' => 'Budgets',
'newBudget' => 'Create a new budget',
'delete_budget' => 'Delete budget ":name"',
'edit_budget' => 'Edit budget ":name"',
// categories
'categories' => 'Categories',
'newCategory' => 'Create a new categori',
'delete_category' => 'Delete category ":name"',
'edit_category' => 'Edit category ":name"',
// currencies
'currencies' => 'Currencies',
'edit_currency' => 'Edit currencies ":name"',
'delete_currency' => 'Delete currencies ":name"',
// piggy banks
'piggyBanks' => 'Piggy banks',
'newPiggyBank' => 'Create a new piggy bank',
'edit_piggyBank' => 'Edit piggy bank ":name"',
'delete_piggyBank' => 'Delete piggy bank ":name"',
// top menu
'preferences' => 'Preferences',
'profile' => 'Profile',
'changePassword' => 'Change your password',
// bills
'bills' => 'Bills',
'newBill' => 'New bill',
'edit_bill' => 'Edit bill ":name"',
'delete_bill' => 'Delete bill ":name"',
// reminders
'reminders' => 'Reminders',
'reminder' => 'Reminder #:id',
// reports
'reports' => 'Reports',
'monthly_report' => 'Montly report for :date',
'monthly_report_shared' => 'Montly 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',
// search
'searchResult' => 'Search for ":query"',
// transaction lists.
'withdrawal_list' => 'Expenses',
'deposit_list' => 'Revenue, income and deposits',
'transfer_list' => 'Transfers',
'transfers_list' => 'Transfers',
// create transactions
'create_withdrawal' => 'Create new withdrawal',
'create_deposit' => 'Create new deposit',
'create_transfer' => 'Create new transfer',
// edit transactions
'edit_journal' => 'Edit transaction ":description"',
'delete_journal' => 'Delete transaction ":description"',
// tags
'tags' => 'Tags',
'createTag' => 'Create new tag',
'edit_tag' => 'Edit tag ":tag"',
'delete_tag' => 'Delete tag ":tag"',
];

View File

@@ -1,12 +1,201 @@
<?php <?php
return [ return [
'welcome' => 'Welcome to Firefly!', 'test' => 'You have selected English.',
'mainTitle' => 'What\'s playing?', 'close' => 'Close',
'close' => 'Clone',
'pleaseHold' => 'Please hold...', 'pleaseHold' => 'Please hold...',
'mandatoryFields' => 'Mandatory fields', 'mandatoryFields' => 'Mandatory fields',
'optionalFields' => 'Optional fields', 'optionalFields' => 'Optional fields',
'options' => 'Options', 'options' => 'Options',
'something' => 'Something!' 'something' => 'Something!',
'actions' => 'Actions',
'edit' => 'Edit',
'delete' => 'Delete',
'welcomeBack' => 'What\'s playing?',
'everything' => 'Everything',
'customRange' => 'Custom range',
'apply' => 'Apply',
'cancel' => 'Cancel',
'from' => 'From',
'to' => 'To',
'showEverything' => 'Show everything',
'create_new_budget' => 'Create a new budget',
'store_new_budget' => ' Store new budget',
'availableIn' => 'Available in :date',
'transactionsWithoutBudget' => 'Expenses without budget',
'transactionsWithoutBudgetDate' => 'Expenses without budget in :date',
'createBudget' => 'New budget',
'inactiveBudgets' => 'Inactive budgets',
'newCategory' => 'New category',
'withoutCategory' => 'Without a category',
'details_for_asset' => 'Details for asset account ":name"',
'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"',
'update_asset_account' => 'Update asset account',
'update_expense_account' => 'Update expense account',
'update_revenue_account' => 'Update revenue account',
'make_new_asset_account' => 'New asset account',
'make_new_expense_account' => 'New expense account',
'make_new_revenue_account' => 'New revenue account',
// new user:
'welcome' => 'Welcome to Firefly!',
'createNewAsset' => 'Create a new asset account to get started. This will allow you to create transactions and start your financial management',
'createNewAssetButton' => 'Create new asset account',
// home page:
'yourAccounts' => 'Your accounts',
'budgetsAndSpending' => 'Budgets and spending',
'savings' => 'Savings',
'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel',
'createPiggyToContinue' => 'Create piggy banks to fill this panel.',
'newWithdrawal' => 'New expense',
'newDeposit' => 'New deposit',
'newTransfer' => 'New transfer',
'moneyIn' => 'Money in',
'moneyOut' => 'Money out',
'billsToPay' => 'Bills to pay',
'billsPaid' => 'Bills paid',
'viewDetails' => 'View details',
'divided' => 'divided',
'toDivide' => 'left to divide',
// menu and titles, should be recycled as often as possible:
'toggleNavigation' => 'Toggle navigation',
'seeAllReminders' => 'See all reminders',
'reminders' => 'Reminders',
'currency' => 'Currency',
'preferences' => 'Preferences',
'logout' => 'Logout',
'searchPlaceholder' => 'Search...',
'dashboard' => 'Dashboard',
'currencies' => 'Currencies',
'accounts' => 'Accounts',
'assetAccounts' => 'Asset accounts',
'expenseAccounts' => 'Expense accounts',
'revenueAccounts' => 'Revenue accounts',
'Asset account' => 'Asset account',
'Default account' => 'Asset account',
'Expense account' => 'Expense account',
'Revenue account' => 'Revenue account',
'budgets' => 'Budgets',
'categories' => 'Categories',
'tags' => 'Tags',
'reports' => 'Reports',
'transactions' => 'Transactions',
'expenses' => 'Expenses',
'income' => 'Revenue / income',
'transfers' => 'Transfer',
'moneyManagement' => 'Money management',
'piggyBanks' => 'Piggy banks',
'bills' => 'Bills',
'createNew' => 'Create new',
'withdrawal' => 'Withdrawal',
'deposit' => 'Deposit',
'transfer' => 'Transfer',
'Withdrawal' => 'Withdrawal',
'Deposit' => 'Deposit',
'Transfer' => 'Transfer',
'bill' => 'Rekening',
'yes' => 'Yes',
'no' => 'No',
'amount' => 'Amount',
'newBalance' => 'New balance',
'overview' => 'Overview',
'saveOnAccount' => 'Save on account',
'unknown' => 'Unknown',
'daily' => 'Daily',
'weekly' => 'Weekly',
'monthly' => 'Monthly',
'quarterly' => 'Quarterly',
'half-year' => 'Every six months',
'yearly' => 'Yearly',
'reportForYear' => 'Yearly report for :year',
'reportForYearShared' => 'Yearly report for :year (including shared accounts)',
'reportForMonth' => 'Montly report for :year',
'reportForMonthShared' => 'Montly report for :year (including shared accounts)',
'incomeVsExpenses' => 'Income vs. expenses',
'accountBalances' => 'Account balances',
'balanceStartOfYear' => 'Balance at start of year',
'balanceEndOfYear' => 'Balance at end of year',
'balanceStartOfMonth' => 'Balance at end of month',
'balanceEndOfMonth' => 'Balance at end of month',
'balanceStart' => 'Balance at end of period',
'balanceEnd' => 'Balance at end of period',
'reportsOwnAccounts' => 'Reports for your own accounts',
'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts',
'account' => 'Account',
'splitByAccount' => 'Split by account',
'balancedByTransfersAndTags' => 'Balanced by transfers and tags',
'coveredWithTags' => 'Covered with tags',
'leftUnbalanced' => 'Left unbalanced',
'expectedBalance' => 'Expected balance',
'outsideOfBudgets' => 'Outside of budgets',
'leftInBudget' => 'Left in budget',
'sumOfSums' => 'Sum of sums',
'notCharged' => 'Not charged (yet)',
'inactive' => 'Inactive',
'difference' => 'Difference',
'in' => 'In',
'out' => 'Out',
'topX' => 'top :number',
'showTheRest' => 'Show everything',
'hideTheRest' => 'Show only the top :number',
// charts:
'dayOfMonth' => 'Day of the month',
'month' => 'Month',
'budget' => 'Budget',
'spent' => 'Spent',
'overspent' => 'Overspent',
'left' => 'Left',
'noCategory' => '(no category)',
'noBudget' => '(no budget)',
'category' => 'Category',
'maxAmount' => 'Maximum amount',
'minAmount' => 'Minumum amount',
'billEntry' => 'Current bill entry',
'name' => 'Name',
'date' => 'Date',
'paid' => 'Paid',
'unpaid' => 'Unpaid',
'day' => 'Day',
'budgeted' => 'Budgeted',
'period' => 'Period',
'balance' => 'Balance',
'summary' => 'Summary',
'sum' => 'Sum',
'average' => 'Average',
'balanceFor' => 'Balance for :name',
'asset_accounts' => 'Asset accounts',
'expense_accounts' => 'Expense accounts',
'revenue_accounts' => 'Revenue accounts',
// some extra help:
'accountExtraHelp_asset' => '',
'accountExtraHelp_expense' => '',
'accountExtraHelp_revenue' => '',
]; ];

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