diff --git a/README.md b/README.md index 419627813e..976c4f4772 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Firefly III -#### v3.4.0.6 +#### v3.4.0.7 [![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) @@ -34,20 +34,11 @@ and the philosophy behind it. 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 - [A double-entry bookkeeping system](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system); - You can store, edit and remove [withdrawals, deposits and transfers](https://en.wikipedia.org/wiki/Financial_transaction). This allows you full financial management; -- You can manage different types of accounts +- You can manage different types of accounts; - [Asset](https://en.wikipedia.org/wiki/Asset) accounts - Shared [asset accounts](https://en.wikipedia.org/wiki/Asset) ([household accounts](https://en.wikipedia.org/wiki/Household)) - Saving accounts @@ -65,8 +56,8 @@ Everything is organised: - Clear views that should show you how you're doing; - Easy navigation through your records; - Browse back and forth to see previous months or even years; -- Lots of charts because we all love them. -- Financial reporting showing you how well you are doing; +- Lots of charts because we all love them; +- Financial reporting showing you how well you are doing. ## Screenshots @@ -86,6 +77,16 @@ _Please note that everything in these screenshots is fictional and may not be re ![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 Firefly III is pretty much all grown up. Full test coverage (nerd alert!) is coming. One of the things on the todo-list diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 07e2bb7ece..6f8232dbc0 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -26,6 +26,8 @@ class Kernel extends ConsoleKernel * * @param \Illuminate\Console\Scheduling\Schedule $schedule * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * * @return void */ protected function schedule(Schedule $schedule) diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 5bf9d44d91..4a9d49b8e1 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -27,6 +27,7 @@ class Handler extends ExceptionHandler * * @param \Illuminate\Http\Request $request * @param \Exception $e + * @SuppressWarnings(PHPMD.ShortVariable) * * @return \Illuminate\Http\Response */ @@ -43,6 +44,7 @@ class Handler extends ExceptionHandler * Report or log an exception. * * This is a great spot to send exceptions to Sentry, Bugsnag, etc. + * @SuppressWarnings(PHPMD.ShortVariable) * * @param \Exception $e * diff --git a/app/Handlers/Events/ConnectJournalToPiggyBank.php b/app/Handlers/Events/ConnectJournalToPiggyBank.php index 1f3d2a2e70..d520d7c177 100644 --- a/app/Handlers/Events/ConnectJournalToPiggyBank.php +++ b/app/Handlers/Events/ConnectJournalToPiggyBank.php @@ -6,7 +6,6 @@ use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; -use Log; /** * Class ConnectJournalToPiggyBank @@ -28,6 +27,8 @@ class ConnectJournalToPiggyBank /** * Handle the event when journal is saved. * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * * @param JournalCreated $event * * @return boolean @@ -37,45 +38,25 @@ class ConnectJournalToPiggyBank /** @var TransactionJournal $journal */ $journal = $event->journal; $piggyBankId = $event->piggyBankId; - if (intval($piggyBankId) < 1) { - return false; - } - - Log::debug('JournalCreated event: ' . $journal->id . ', ' . $piggyBankId); /** @var PiggyBank $piggyBank */ $piggyBank = Auth::user()->piggybanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); - if (is_null($piggyBank) || $journal->transactionType->type != 'Transfer') { - return false; - } - Log::debug('Found a piggy bank'); - $amount = $journal->amount; - Log::debug('Amount: ' . $amount); - if ($amount == 0) { + if (is_null($piggyBank)) { return false; } // update piggy bank rep for date of transaction journal. $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); if (is_null($repetition)) { - Log::debug('Found no repetition for piggy bank for date ' . $journal->date->format('Y M d')); - return false; } - Log::debug('Found rep! ' . $repetition->id); - - /* - * Add amount when - */ + $amount = $journal->amount; /** @var Transaction $transaction */ foreach ($journal->transactions()->get() as $transaction) { if ($transaction->account_id == $piggyBank->account_id) { if ($transaction->amount < 0) { - $amount = $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); + $amount = $transaction->amount * -1; } } } @@ -83,14 +64,8 @@ class ConnectJournalToPiggyBank $repetition->currentamount += $amount; $repetition->save(); - PiggyBankEvent::create( - [ - 'piggy_bank_id' => $piggyBank->id, - 'transaction_journal_id' => $journal->id, - 'date' => $journal->date, - 'amount' => $amount - ] - ); + PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]); + return true; } diff --git a/app/Handlers/Events/JournalDeletedHandler.php b/app/Handlers/Events/JournalDeletedHandler.php deleted file mode 100644 index 757738ba62..0000000000 --- a/app/Handlers/Events/JournalDeletedHandler.php +++ /dev/null @@ -1,36 +0,0 @@ -piggyBank()->first(); - $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); + $repetition = null; + if ($piggyBank) { + /** @var PiggyBankRepetition $repetition */ + $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); + } if (is_null($repetition)) { return; diff --git a/app/Helpers/Collection/Account.php b/app/Helpers/Collection/Account.php new file mode 100644 index 0000000000..ad227fdea2 --- /dev/null +++ b/app/Helpers/Collection/Account.php @@ -0,0 +1,90 @@ +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; + } + + +} \ No newline at end of file diff --git a/app/Helpers/Collection/Balance.php b/app/Helpers/Collection/Balance.php new file mode 100644 index 0000000000..cbafe987ce --- /dev/null +++ b/app/Helpers/Collection/Balance.php @@ -0,0 +1,65 @@ +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; + } + + + + +} \ No newline at end of file diff --git a/app/Helpers/Collection/BalanceEntry.php b/app/Helpers/Collection/BalanceEntry.php new file mode 100644 index 0000000000..8ba9a734a0 --- /dev/null +++ b/app/Helpers/Collection/BalanceEntry.php @@ -0,0 +1,79 @@ +account; + } + + /** + * @param AccountModel $account + */ + public function setAccount($account) + { + $this->account = $account; + } + + /** + * @return float + */ + public function getSpent() + { + return $this->spent; + } + + /** + * @param float $spent + */ + public function setSpent($spent) + { + $this->spent = $spent; + } + + /** + * @return float + */ + public function getLeft() + { + return $this->left; + } + + /** + * @param float $left + */ + public function setLeft($left) + { + $this->left = $left; + } + + + + +} \ No newline at end of file diff --git a/app/Helpers/Collection/BalanceHeader.php b/app/Helpers/Collection/BalanceHeader.php new file mode 100644 index 0000000000..3563420408 --- /dev/null +++ b/app/Helpers/Collection/BalanceHeader.php @@ -0,0 +1,47 @@ +accounts = new Collection; + } + + /** + * @param AccountModel $account + */ + public function addAccount(AccountModel $account) + { + $this->accounts->push($account); + } + + /** + * @return Collection + */ + public function getAccounts() + { + return $this->accounts; + } + + + +} \ No newline at end of file diff --git a/app/Helpers/Collection/BalanceLine.php b/app/Helpers/Collection/BalanceLine.php new file mode 100644 index 0000000000..4e045eca8a --- /dev/null +++ b/app/Helpers/Collection/BalanceLine.php @@ -0,0 +1,170 @@ +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; + } + + +} \ No newline at end of file diff --git a/app/Helpers/Collection/Bill.php b/app/Helpers/Collection/Bill.php new file mode 100644 index 0000000000..973b886482 --- /dev/null +++ b/app/Helpers/Collection/Bill.php @@ -0,0 +1,55 @@ +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; + } + +} \ No newline at end of file diff --git a/app/Helpers/Collection/BillLine.php b/app/Helpers/Collection/BillLine.php new file mode 100644 index 0000000000..613121073b --- /dev/null +++ b/app/Helpers/Collection/BillLine.php @@ -0,0 +1,125 @@ +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; + } + + +} \ No newline at end of file diff --git a/app/Helpers/Collection/Budget.php b/app/Helpers/Collection/Budget.php new file mode 100644 index 0000000000..7cd5e1f3fa --- /dev/null +++ b/app/Helpers/Collection/Budget.php @@ -0,0 +1,149 @@ +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 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 \Illuminate\Support\Collection + */ + public function getBudgetLines() + { + return $this->budgetLines; + } + + + + +} \ No newline at end of file diff --git a/app/Helpers/Collection/BudgetLine.php b/app/Helpers/Collection/BudgetLine.php new file mode 100644 index 0000000000..a950ed088f --- /dev/null +++ b/app/Helpers/Collection/BudgetLine.php @@ -0,0 +1,134 @@ +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; + } + + + + + + +} \ No newline at end of file diff --git a/app/Helpers/Collection/Category.php b/app/Helpers/Collection/Category.php new file mode 100644 index 0000000000..96f1c63363 --- /dev/null +++ b/app/Helpers/Collection/Category.php @@ -0,0 +1,80 @@ +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; + } + + +} \ No newline at end of file diff --git a/app/Helpers/Collection/Expense.php b/app/Helpers/Collection/Expense.php new file mode 100644 index 0000000000..3c0d908ab6 --- /dev/null +++ b/app/Helpers/Collection/Expense.php @@ -0,0 +1,82 @@ +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->queryAmount); + $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->queryAmount); + $existing->count++; + $this->expenses->put($accountId, $existing); + } + } + + /** + * @param $add + */ + public function addToTotal($add) + { + $this->total += floatval($add); + } + + /** + * @return Collection + */ + public function getExpenses() + { + $this->expenses->sortBy( + function (stdClass $object) { + return $object->amount; + } + ); + + return $this->expenses; + } + + /** + * @return float + */ + public function getTotal() + { + return $this->total; + } +} \ No newline at end of file diff --git a/app/Helpers/Collection/Income.php b/app/Helpers/Collection/Income.php new file mode 100644 index 0000000000..c3e60e0cf7 --- /dev/null +++ b/app/Helpers/Collection/Income.php @@ -0,0 +1,85 @@ +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->queryAmount); + $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->queryAmount); + $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; + } + + +} \ No newline at end of file diff --git a/app/Helpers/Help/Help.php b/app/Helpers/Help/Help.php index 19bab089e9..39628ea108 100644 --- a/app/Helpers/Help/Help.php +++ b/app/Helpers/Help/Help.php @@ -18,6 +18,7 @@ class Help implements HelpInterface /** * @codeCoverageIgnore + * * @param $key * * @return string @@ -29,6 +30,7 @@ class Help implements HelpInterface /** * @codeCoverageIgnore + * * @param $route * * @return array @@ -57,6 +59,7 @@ class Help implements HelpInterface /** * @codeCoverageIgnore + * * @param $route * * @return bool @@ -68,6 +71,7 @@ class Help implements HelpInterface /** * @codeCoverageIgnore + * * @param $route * @param array $content * @@ -81,6 +85,7 @@ class Help implements HelpInterface /** * @codeCoverageIgnore + * * @param $route * * @return bool diff --git a/app/Helpers/Reminders/ReminderHelper.php b/app/Helpers/Reminders/ReminderHelper.php index 53484378ea..e17c81281d 100644 --- a/app/Helpers/Reminders/ReminderHelper.php +++ b/app/Helpers/Reminders/ReminderHelper.php @@ -63,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 * and end date for each reminder that this piggy bank will have, if the piggy bank has diff --git a/app/Helpers/Reminders/ReminderHelperInterface.php b/app/Helpers/Reminders/ReminderHelperInterface.php index c3db2dd1d1..46ea7fd510 100644 --- a/app/Helpers/Reminders/ReminderHelperInterface.php +++ b/app/Helpers/Reminders/ReminderHelperInterface.php @@ -4,6 +4,7 @@ namespace FireflyIII\Helpers\Reminders; use Carbon\Carbon; use FireflyIII\Models\PiggyBank; +use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\Reminder; /** @@ -49,4 +50,15 @@ interface ReminderHelperInterface * @return Reminder */ public function createReminder(PiggyBank $piggyBank, Carbon $start, Carbon $end); + + /** + * Create all reminders for a piggy bank for a given date. + * + * @param PiggyBank $piggyBank + * + * @param Carbon $date + * + * @return mixed + */ + public function createReminders(PiggyBank $piggyBank, Carbon $date); } diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index b06c6237e9..c1f3863e0e 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -3,12 +3,23 @@ namespace FireflyIII\Helpers\Report; use App; -use Auth; 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 Illuminate\Database\Query\JoinClause; -use Illuminate\Support\Collection; -use Steam; +use FireflyIII\Models\Bill; +use FireflyIII\Models\Budget as BudgetModel; +use FireflyIII\Models\LimitRepetition; /** * Class ReportHelper @@ -18,47 +29,337 @@ use Steam; class ReportHelper implements ReportHelperInterface { + /** @var ReportQueryInterface */ + protected $query; + /** - * This method gets some kind of list for a monthly overview. + * @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 bool $showSharedReports + * @param Carbon $end + * @param $shared * - * @return Collection + * @return Account */ - public function getBudgetsForMonth(Carbon $date, $showSharedReports = false) + public function getAccountReport(Carbon $date, Carbon $end, $shared) { - /** @var \FireflyIII\Helpers\Report\ReportQueryInterface $query */ - $query = App::make('FireflyIII\Helpers\Report\ReportQueryInterface'); - $start = clone $date; - $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); - $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'; + $accounts = $this->query->getAllAccounts($date, $end, $shared); + $start = 0; + $end = 0; + $diff = 0; - // find transactions to shared asset accounts, which are without a budget by default: - // which is only relevant when shared asset accounts are hidden. - if ($showSharedReports === false) { - $transfers = $query->sharedExpenses($start, $end)->sum('queryAmount'); - $budgets[0]['spent'] += floatval($transfers) * -1; + // summarize: + foreach ($accounts as $account) { + $start += $account->startBalance; + $end += $account->endBalance; + $diff += ($account->endBalance - $account->startBalance); } - return $budgets; + $object = new AccountCollection; + $object->setStart($start); + $object->setEnd($end); + $object->setDifference($diff); + $object->setAccounts($accounts); + + return $object; + } + + /** + * + * The balance report contains a Balance object which in turn contains: + * + * A BalanceHeader object which contains all relevant user asset accounts for the report. + * + * A number of BalanceLine objects, which hold: + * - A budget + * - A number of BalanceEntry objects. + * + * The BalanceEntry object holds: + * - The same budget (again) + * - A user asset account as mentioned in the BalanceHeader + * - The amount of money spent on the budget by the user asset account + * + * @param Carbon $start + * @param Carbon $end + * @param boolean $shared + * + * @return Balance + */ + public function getBalanceReport(Carbon $start, Carbon $end, $shared) + { + $repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + $tagRepository = App::make('FireflyIII\Repositories\Tag\TagRepositoryInterface'); + $balance = new Balance; + + // build a balance header: + $header = new BalanceHeader; + + $accounts = $this->query->getAllAccounts($start, $end, $shared); + $budgets = $repository->getBudgets(); + foreach ($accounts as $account) { + $header->addAccount($account); + } + + /** @var BudgetModel $budget */ + foreach ($budgets as $budget) { + $line = new BalanceLine; + $line->setBudget($budget); + + // get budget amount for current period: + $rep = $repository->getCurrentRepetition($budget, $start); + $line->setRepetition($rep); + + // loop accounts: + foreach ($accounts as $account) { + $balanceEntry = new BalanceEntry; + $balanceEntry->setAccount($account); + + // get spent: + $spent = $this->query->spentInBudget($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; + + } + + /** + * @param Carbon $start + * @param Carbon $end + * @param boolean $shared + * + * @return BudgetCollection + */ + public function getBudgetReport(Carbon $start, Carbon $end, $shared) + { + $object = new BudgetCollection; + /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ + $repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + $set = $repository->getBudgets(); + + foreach ($set as $budget) { + + $repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end); + + // no repetition(s) for this budget: + if ($repetitions->count() == 0) { + $spent = $repository->spentInPeriod($budget, $start, $end, $shared); + $budgetLine = new BudgetLine; + $budgetLine->setBudget($budget); + $budgetLine->setOverspent($spent); + $object->addOverspent($spent); + $object->addBudgetLine($budgetLine); + continue; + } + + // one or more repetitions for budget: + /** @var LimitRepetition $repetition */ + foreach ($repetitions as $repetition) { + $budgetLine = new BudgetLine; + $budgetLine->setBudget($budget); + $budgetLine->setRepetition($repetition); + $expenses = $repository->spentInPeriod($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->spentInPeriod($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->expenseInPeriod($start, $end, $shared); + foreach ($set as $entry) { + $object->addToTotal($entry->queryAmount); + $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->incomeInPeriod($start, $end, $shared); + foreach ($set as $entry) { + $object->addToTotal($entry->queryAmount); + $object->addOrCreateIncome($entry); + } + + return $object; } /** @@ -68,13 +369,14 @@ class ReportHelper implements ReportHelperInterface */ public function listOfMonths(Carbon $date) { + $start = clone $date; $end = Carbon::now(); $months = []; while ($start <= $end) { $year = $start->year; $months[$year][] = [ - 'formatted' => $start->format('F Y'), + 'formatted' => $start->formatLocalized('%B %Y'), 'month' => $start->month, 'year' => $year, ]; @@ -83,77 +385,4 @@ class ReportHelper implements ReportHelperInterface 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; - } } diff --git a/app/Helpers/Report/ReportHelperInterface.php b/app/Helpers/Report/ReportHelperInterface.php index 8b66f48f15..13bef6fb67 100644 --- a/app/Helpers/Report/ReportHelperInterface.php +++ b/app/Helpers/Report/ReportHelperInterface.php @@ -3,7 +3,12 @@ namespace FireflyIII\Helpers\Report; 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 @@ -14,13 +19,77 @@ interface ReportHelperInterface { /** - * This method gets some kind of list for a monthly overview. + * This method generates a full report for the given period on all + * the users asset and cash accounts. * - * @param Carbon $date + * @param Carbon $date + * @param Carbon $end + * @param boolean $shared * - * @return Collection + * @return Account */ - public function getBudgetsForMonth(Carbon $date); + public function getAccountReport(Carbon $date, Carbon $end, $shared); + + /** + * This method generates a full report for the given period on all + * the users bills and their payments. + * + * @param Carbon $start + * @param Carbon $end + * @param boolean $shared + * + * @return Account + */ + public function getBillReport(Carbon $start, Carbon $end, $shared); + + /** + * @param Carbon $start + * @param Carbon $end + * @param boolean $shared + * + * @return Balance + */ + public function getBalanceReport(Carbon $start, Carbon $end, $shared); + + /** + * @param Carbon $start + * @param Carbon $end + * @param boolean $shared + * + * @return BudgetCollection + */ + public function getBudgetReport(Carbon $start, Carbon $end, $shared); + + /** + * @param Carbon $start + * @param Carbon $end + * @param boolean $shared + * + * @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 @@ -29,18 +98,4 @@ interface ReportHelperInterface */ 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); } diff --git a/app/Helpers/Report/ReportQuery.php b/app/Helpers/Report/ReportQuery.php index e481387cae..38fce48ebb 100644 --- a/app/Helpers/Report/ReportQuery.php +++ b/app/Helpers/Report/ReportQuery.php @@ -7,6 +7,7 @@ use Carbon\Carbon; use Crypt; use DB; use FireflyIII\Models\Account; +use FireflyIII\Models\Budget; use FireflyIII\Models\TransactionJournal; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; @@ -22,100 +23,57 @@ use Steam; class ReportQuery implements ReportQueryInterface { + /** - * This query retrieves a list of accounts that are active and not shared. + * This method returns all "expense" journals in a certain period, which are both transfers to a shared account + * and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does + * not group and returns different fields. * - * @param bool $showSharedReports + * @param Carbon $start + * @param Carbon $end + * @param bool $includeShared * * @return Collection + * */ - public function accountList($showSharedReports = false) + public function expenseInPeriod(Carbon $start, Carbon $end, $includeShared = false) { - $query = Auth::user()->accounts(); - if ($showSharedReports === false) { - - $query->leftJoin( - 'account_meta', function (JoinClause $join) { - $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', "accountRole"); - } - )->where( + $query = $this->queryJournalsWithTransactions($start, $end); + if ($includeShared === false) { + $query->where( function (Builder $query) { - $query->where('account_meta.data', '!=', '"sharedAsset"'); - $query->orWhereNull('account_meta.data'); + $query->where( + function (Builder $q) { // only get withdrawals not from a shared account + $q->where('transaction_types.type', 'Withdrawal'); + $q->where('acm_from.data', '!=', '"sharedAsset"'); + } + ); + $query->orWhere( + function (Builder $q) { // and transfers from a shared account. + $q->where('transaction_types.type', 'Transfer'); + $q->where('acm_to.data', '=', '"sharedAsset"'); + } + ); } ); - + } else { + $query->where('transaction_types.type', 'Withdrawal'); // any withdrawal is fine. } - $query->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'); + $query->groupBy('transaction_journals.id')->orderBy('transaction_journals.date'); - return $query->get(['accounts.*']); - } + // get everything, decrypt and return + $data = $query->get(['transaction_journals.id', 'transaction_journals.description', 'transaction_journals.encrypted', 'transaction_types.type', + DB::Raw('SUM(`t_from`.`amount`) as `queryAmount`'), + 'transaction_journals.date', 't_to.account_id as account_id', 'ac_to.name as name', 'ac_to.encrypted as account_encrypted']); - /** - * 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) - { + $data->each( + function (Model $object) { + $object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name; + } + ); + $data->sortByDesc('queryAmount'); - $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')); + return $data; } /** @@ -123,15 +81,15 @@ class ReportQuery implements ReportQueryInterface * * @param Carbon $start * @param Carbon $end - * @param bool $showSharedReports + * @param bool $includeShared * * @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') ->accountTypeIn(['Default account', 'Asset account', 'Cash account']); - if ($showSharedReports === false) { + if ($includeShared === false) { $query->leftJoin( 'account_meta', function (JoinClause $join) { $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole'); @@ -167,39 +125,6 @@ class ReportQuery implements ReportQueryInterface 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 - * 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) - { - $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 @@ -208,14 +133,14 @@ class ReportQuery implements ReportQueryInterface * * @param Carbon $start * @param Carbon $end - * @param bool $showSharedReports + * @param bool $includeShared * * @return Collection */ - public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false) + public function incomeInPeriod(Carbon $start, Carbon $end, $includeShared = false) { $query = $this->queryJournalsWithTransactions($start, $end); - if ($showSharedReports === false) { + if ($includeShared === false) { // only get deposits not to a shared account // and transfers to a shared account. $query->where( @@ -259,318 +184,60 @@ class ReportQuery implements ReportQueryInterface $object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name; } ); + $data->sortByDesc('queryAmount'); return $data; } - /** - * 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) - { - $query = Auth::user()->transactionjournals() - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.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('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) - ->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) - ->before($end) - ->where('transaction_types.type', 'Transfer') - ->where('transaction_journals.user_id', Auth::user()->id) - ->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 Budget $budget * @param Carbon $start * @param Carbon $end * - * @return Builder + * @return float */ - protected function queryJournalsNoBudget(Account $account, Carbon $start, Carbon $end) + public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end) { - return TransactionJournal:: - leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id') - ->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') - ->before($end) - ->after($start) - ->where('accounts.id', $account->id) - ->where('transaction_journals.user_id', Auth::user()->id) - ->where('transaction_types.type', 'Withdrawal') - ->groupBy('budgets.id') - ->orderBy('budgets.name', 'ASC'); + + return floatval( + Auth::user()->transactionjournals() + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->transactionTypes(['Withdrawal']) + ->where('transactions.amount', '<', 0) + ->where('transactions.account_id', $account->id) + ->before($end) + ->after($start) + ->where('budget_transaction_journal.budget_id', $budget->id) + ->sum('transactions.amount') + ); + } + + /** + * @param Account $account + * @param Carbon $start + * @param Carbon $end + * @param bool $shared + * + * @return float + */ + public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = false) + { + 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') + ->where('transactions.amount', '<', 0) + ->transactionTypes(['Withdrawal']) + ->where('transactions.account_id', $account->id) + ->before($end) + ->after($start) + ->whereNull('budget_transaction_journal.budget_id') + ->sum('transactions.amount') + ); } /** @@ -609,5 +276,4 @@ class ReportQuery implements ReportQueryInterface return $query; } - } diff --git a/app/Helpers/Report/ReportQueryInterface.php b/app/Helpers/Report/ReportQueryInterface.php index 47a7857707..bcf36e45da 100644 --- a/app/Helpers/Report/ReportQueryInterface.php +++ b/app/Helpers/Report/ReportQueryInterface.php @@ -4,6 +4,7 @@ namespace FireflyIII\Helpers\Report; use Carbon\Carbon; use FireflyIII\Models\Account; +use FireflyIII\Models\Budget; use Illuminate\Support\Collection; /** @@ -14,72 +15,17 @@ use Illuminate\Support\Collection; interface ReportQueryInterface { - /** - * This query retrieves a list of accounts that are active and not shared. - * - * @param bool $showSharedReports - * - * @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 $end - * - * @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); - /** * Get a users accounts combined with various meta-data related to the start and end date. * * @param Carbon $start * @param Carbon $end - * @param bool $showSharedReports + * @param bool $includeShared * * @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. - * - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * - * @return mixed - */ - public function getBudgetSummary(Account $account, Carbon $start, Carbon $end); - - /** - * 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 @@ -88,79 +34,47 @@ interface ReportQueryInterface * * @param Carbon $start * @param Carbon $end - * @param bool $showSharedReports + * @param bool $includeShared * * @return Collection + * */ - public function incomeByPeriod(Carbon $start, Carbon $end, $showSharedReports = false); + public function incomeInPeriod(Carbon $start, Carbon $end, $includeShared = false); /** - * Gets a list of expenses grouped by the budget they were filed under. + * This method returns all "expense" journals in a certain period, which are both transfers to a shared account + * and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does + * not group and returns different fields. * * @param Carbon $start * @param Carbon $end - * @param bool $showSharedReports + * @param bool $includeShared * * @return Collection + * */ - public function journalsByBudget(Carbon $start, Carbon $end, $showSharedReports = false); + public function expenseInPeriod(Carbon $start, Carbon $end, $includeShared = 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 Account $account + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end * - * @param Carbon $start - * @param Carbon $end - * @param bool $showSharedReports - * - * @return Collection + * @return float */ - public function journalsByCategory(Carbon $start, Carbon $end, $showSharedReports = false); + public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end); /** - * 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. + * @param Account $account + * @param Carbon $start + * @param Carbon $end + * @param bool $shared * - * So now it will include them! - * - * @param Carbon $start - * @param Carbon $end - * @param bool $showSharedReports - * - * @return Collection + * @return float */ - public function journalsByExpenseAccount(Carbon $start, Carbon $end, $showSharedReports = false); + public function spentNoBudget(Account $account, Carbon $start, Carbon $end, $shared = 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); } diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 74a9966476..1e55f4b445 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -28,7 +28,7 @@ class AccountController extends Controller { parent::__construct(); View::share('mainTitleIcon', 'fa-credit-card'); - View::share('title', 'Accounts'); + View::share('title', trans('firefly.accounts')); } /** @@ -137,7 +137,7 @@ class AccountController extends Controller */ public function index(AccountRepositoryInterface $repository, $what) { - $subTitle = Config::get('firefly.subTitlesByIdentifier.' . $what); + $subTitle = trans('firefly.' . $what . '_accounts'); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $types = Config::get('firefly.accountTypesByIdentifier.' . $what); $accounts = $repository->getAccounts($types); diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 27c50adb65..80301480e1 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -38,6 +38,7 @@ class AuthController extends Controller * * @param \Illuminate\Contracts\Auth\Guard $auth * @param \Illuminate\Contracts\Auth\Registrar $registrar + * * @codeCoverageIgnore * */ diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php index 907b514894..9648ce287d 100644 --- a/app/Http/Controllers/Auth/PasswordController.php +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -7,6 +7,7 @@ use Illuminate\Foundation\Auth\ResetsPasswords; /** * Class PasswordController + * * @codeCoverageIgnore * @package FireflyIII\Http\Controllers\Auth */ diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index d525a1094f..a2877aa940 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -29,54 +29,10 @@ class BillController extends Controller public function __construct() { parent::__construct(); - View::share('title', 'Bills'); + View::share('title', trans('firefly.bills')); 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 */ diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 27205a295b..c62d06a799 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -29,7 +29,7 @@ class BudgetController extends Controller public function __construct() { parent::__construct(); - View::share('title', 'Budgets'); + View::share('title', trans('firefly.budgets')); View::share('mainTitleIcon', 'fa-tasks'); View::share('hideBudgets', true); } @@ -137,7 +137,8 @@ class BudgetController extends Controller $budgets->each( function (Budget $budget) use ($repository) { $date = Session::get('start', Carbon::now()->startOfMonth()); - $budget->spent = $repository->spentInMonth($budget, $date); + $end = Session::get('end', Carbon::now()->endOfMonth()); + $budget->spent = $repository->spentInPeriod($budget, $date, $end); $budget->currentRep = $repository->getCurrentRepetition($budget, $date); } ); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 3b54d2fe1d..26681b3d7a 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -26,7 +26,7 @@ class CategoryController extends Controller public function __construct() { parent::__construct(); - View::share('title', 'Categories'); + View::share('title', trans('firefly.categories')); View::share('mainTitleIcon', 'fa-bar-chart'); } diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php new file mode 100644 index 0000000000..530e032851 --- /dev/null +++ b/app/Http/Controllers/Chart/AccountController.php @@ -0,0 +1,150 @@ +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()); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Chart/BillController.php b/app/Http/Controllers/Chart/BillController.php new file mode 100644 index 0000000000..f32611d055 --- /dev/null +++ b/app/Http/Controllers/Chart/BillController.php @@ -0,0 +1,141 @@ +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()); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php new file mode 100644 index 0000000000..11031f865a --- /dev/null +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -0,0 +1,160 @@ +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->expensesOnDay($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->spentInPeriod($budget, $start, $end, true); + $allEntries->push([$budget->name, 0, 0, $expenses]); + continue; + } + /** @var LimitRepetition $repetition */ + foreach ($repetitions as $repetition) { + $expenses = $repository->spentInPeriod($budget, $repetition->startdate, $repetition->enddate, true); + $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->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->spentInPeriod($budget, $start, $month, $shared); + $row[] = $spent; + } + $chart->addRowArray($row); + + $start->addMonth(); + } + + $chart->generate(); + + return Response::json($chart->getData()); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php new file mode 100644 index 0000000000..b51120ebc1 --- /dev/null +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -0,0 +1,168 @@ +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->spentInPeriod($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->getCategoriesAndExpenses($start, $end); + + foreach ($set as $entry) { + $isEncrypted = intval($entry->encrypted) == 1 ? true : false; + $name = strlen($entry->name) == 0 ? trans('firefly.noCategory') : $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 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->spentOnDaySum($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->spentInPeriod($category, $start, $month, $shared); + $row[] = $spent; + } + $chart->addRowArray($row); + + $start->addMonth(); + } + + $chart->generate(); + + return Response::json($chart->getData()); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Chart/PiggyBankController.php b/app/Http/Controllers/Chart/PiggyBankController.php new file mode 100644 index 0000000000..d240afa583 --- /dev/null +++ b/app/Http/Controllers/Chart/PiggyBankController.php @@ -0,0 +1,58 @@ +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()); + + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php new file mode 100644 index 0000000000..0980cf9525 --- /dev/null +++ b/app/Http/Controllers/Chart/ReportController.php @@ -0,0 +1,101 @@ +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->incomeInPeriod($start, $month, $shared)->sum('queryAmount')); + $expenseSum = floatval($query->expenseInPeriod($start, $month, $shared)->sum('queryAmount')) * -1; + + $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->incomeInPeriod($start, $month, $shared)->sum('queryAmount')); + $expense += floatval($query->expenseInPeriod($start, $month, $shared)->sum('queryAmount')) * -1; + $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()); + + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 1af8d4b562..2c66882c87 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -1,8 +1,11 @@ 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); + } } } diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index 7c0e33404e..2518d5ba7b 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -28,7 +28,7 @@ class CurrencyController extends Controller public function __construct() { parent::__construct(); - View::share('title', 'Currencies'); + View::share('title', trans('firefly.currencies')); View::share('mainTitleIcon', 'fa-usd'); } diff --git a/app/Http/Controllers/GoogleChartController.php b/app/Http/Controllers/GoogleChartController.php deleted file mode 100644 index 96fd291304..0000000000 --- a/app/Http/Controllers/GoogleChartController.php +++ /dev/null @@ -1,590 +0,0 @@ -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()); - - } - - -} diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index bdaf16b314..f043c0c568 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -53,7 +53,7 @@ class HomeController extends Controller $types = Config::get('firefly.accountTypesByIdentifier.asset'); $count = $repository->countAccounts($types); $title = 'Firefly'; - $subTitle = 'What\'s playing?'; + $subTitle = trans('firefly.welcomeBack'); $mainTitleIcon = 'fa-fire'; $transactions = []; $frontPage = Preferences::get('frontPageAccounts', []); @@ -63,13 +63,11 @@ class HomeController extends Controller $savings = $repository->getSavingsAccounts(); $piggyBankAccounts = $repository->getPiggyBankAccounts(); - $savingsTotal = 0; foreach ($savings as $savingAccount) { $savingsTotal += Steam::balance($savingAccount, $end); } - // check if all books are correct. $sum = $repository->sumOfEverything(); if ($sum != 0) { Session::flash( diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 43863523c9..8c4f64e97a 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -12,7 +12,6 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; -use Preferences; use Response; use Session; use Steam; @@ -39,19 +38,14 @@ class JsonController extends Controller $end = Session::get('end', Carbon::now()->endOfMonth()); $amount = 0; - // these two functions are the same as the chart TODO + // these two functions are the same as the chart $bills = $repository->getActiveBills(); /** @var Bill $bill */ foreach ($bills as $bill) { - $ranges = $repository->getRanges($bill, $start, $end); - - foreach ($ranges as $range) { - // paid a bill in this range? - $amount += $repository->getJournalsInRange($bill, $range['start'], $range['end'])->sum('amount'); - } + $amount += $repository->billPaymentsInRange($bill, $start, $end); } - unset($ranges, $bill, $range, $bills); + unset($bill, $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. /** @var Account $creditCard */ foreach ($creditCards as $creditCard) { - $balance = Steam::balance($creditCard, null, true); + $balance = Steam::balance($creditCard, $end, true); if ($balance == 0) { // find a transfer TO the credit card which should account for // anything paid. If not, the CC is not yet used. @@ -100,7 +94,7 @@ class JsonController extends Controller $creditCards = $accountRepository->getCreditCards(); foreach ($creditCards as $creditCard) { - $balance = Steam::balance($creditCard, null, true); + $balance = Steam::balance($creditCard, $end, true); $date = new Carbon($creditCard->getMeta('ccMonthlyPaymentDate')); if ($balance < 0) { // unpaid! create a fake bill that matches the amount. @@ -128,7 +122,7 @@ class JsonController extends Controller { $start = Session::get('start', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth()); - $amount = $reportQuery->incomeByPeriod($start, $end, true)->sum('queryAmount'); + $amount = $reportQuery->incomeInPeriod($start, $end, true)->sum('queryAmount'); return Response::json(['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]); } @@ -142,7 +136,7 @@ class JsonController extends Controller { $start = Session::get('start', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth()); - $amount = $reportQuery->journalsByExpenseAccount($start, $end, true)->sum('queryAmount'); + $amount = $reportQuery->expenseInPeriod($start, $end, true)->sum('queryAmount') * -1; return Response::json(['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount]); } @@ -202,30 +196,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. * diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 8ffa215491..de177cfb76 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -31,7 +31,7 @@ class PiggyBankController extends Controller public function __construct() { parent::__construct(); - View::share('title', 'Piggy banks'); + View::share('title', trans('firefly.piggyBanks')); View::share('mainTitleIcon', 'fa-sort-amount-asc'); } @@ -45,7 +45,8 @@ class PiggyBankController extends Controller */ 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; $leftToSave = $piggyBank->targetamount - $savedSoFar; $maxAmount = min($leftOnAccount, $leftToSave); @@ -157,6 +158,7 @@ class PiggyBankController extends Controller { /** @var Collection $piggyBanks */ $piggyBanks = $piggyRepository->getPiggyBanks(); + $end = Session::get('end', Carbon::now()->endOfMonth()); $accounts = []; /** @var PiggyBank $piggyBank */ @@ -172,8 +174,8 @@ class PiggyBankController extends Controller if (!isset($accounts[$account->id])) { $accounts[$account->id] = [ 'name' => $account->name, - 'balance' => Steam::balance($account, null, true), - 'leftForPiggyBanks' => $repository->leftOnAccount($account), + 'balance' => Steam::balance($account, $end, true), + 'leftForPiggyBanks' => $repository->leftOnAccount($account, $end), 'sumOfSaved' => $piggyBank->savedSoFar, 'sumOfTargets' => floatval($piggyBank->targetamount), 'leftToSave' => $piggyBank->leftToSave @@ -215,7 +217,8 @@ class PiggyBankController extends Controller public function postAdd(PiggyBankRepositoryInterface $repository, AccountRepositoryInterface $accounts, PiggyBank $piggyBank) { $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; $leftToSave = $piggyBank->targetamount - $savedSoFar; $maxAmount = round(min($leftOnAccount, $leftToSave), 2); diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index 88aba3e124..ab2425ebde 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -1,5 +1,6 @@ data; $frontPageAccounts = Preferences::get('frontPageAccounts', []); $budgetMax = Preferences::get('budgetMaximum', 1000); + $languagePref = Preferences::get('language', 'en'); + $language = $languagePref->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 $frontPageAccounts = []; - foreach (Input::get('frontPageAccounts') as $id) { - $frontPageAccounts[] = intval($id); + if (is_array(Input::get('frontPageAccounts'))) { + foreach (Input::get('frontPageAccounts') as $id) { + $frontPageAccounts[] = intval($id); + } + Preferences::set('frontPageAccounts', $frontPageAccounts); } - Preferences::set('frontPageAccounts', $frontPageAccounts); // view range: Preferences::set('viewRange', Input::get('viewRange')); @@ -65,6 +70,12 @@ class PreferencesController extends Controller $budgetMaximum = intval(Input::get('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!'); diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 22090e77e5..6ba200646b 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -2,13 +2,13 @@ use Carbon\Carbon; use FireflyIII\Helpers\Report\ReportHelperInterface; -use FireflyIII\Helpers\Report\ReportQueryInterface; use FireflyIII\Models\Account; +use FireflyIII\Models\Budget; +use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\Preference; use FireflyIII\Models\TransactionJournal; -use Preferences; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Session; -use Steam; use View; /** @@ -21,311 +21,132 @@ class ReportController extends Controller /** @var ReportHelperInterface */ protected $helper; - /** @var ReportQueryInterface */ - protected $query; /** * @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; - View::share('title', 'Reports'); + View::share('title', trans('firefly.reports')); View::share('mainTitleIcon', 'fa-line-chart'); } /** - * @param string $year - * @param string $month + * @param AccountRepositoryInterface $repository * - * @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 * @internal param ReportHelperInterface $helper - * */ - public function index() + public function index(AccountRepositoryInterface $repository) { - $start = Session::get('first'); - $months = $this->helper->listOfMonths($start); - $years = $this->helper->listOfYears($start); - $title = 'Reports'; - $mainTitleIcon = 'fa-line-chart'; + $start = Session::get('first'); + $months = $this->helper->listOfMonths($start); - 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; + /** @var Account $account */ + foreach ($accounts as $account) { + if ($account->getMeta('accountRole') == 'sharedAsset') { + $hasShared = true; } - ); + } - 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')); + return view('reports.index', compact('months', 'hasShared')); } /** * @param string $year * @param string $month * + * @param bool $shared + * * @return \Illuminate\View\View */ - public function month($year = '2014', $month = '1') + public function month($year = '2014', $month = '1', $shared = false) { - $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; + $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)]); + } - - /** - * - * 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 */ - foreach ($list as $account) { - $id = intval($account->id); - /** @noinspection PhpParamsInspection */ - $accounts[$id] = [ - 'name' => $account->name, - 'startBalance' => Steam::balance($account, $start), - 'endBalance' => Steam::balance($account, $end) - ]; - - $accounts[$id]['difference'] = $accounts[$id]['endBalance'] - $accounts[$id]['startBalance']; - } - - /** - * End getAccountsForMonth - */ + $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( 'reports.month', compact( - 'income', 'expenses', 'budgets', 'accounts', 'categories', - 'date', 'subTitle', 'displaySum', 'subTitleIcon' + 'start', 'shared', + 'subTitle', 'subTitleIcon', + 'accounts', + 'incomes', 'incomeTopLength', + 'expenses', 'expenseTopLength', + 'budgets', 'balance', + 'categories', + 'bills' ) ); + } /** - * @param $year + * @param $year + * + * @param bool $shared * * @return $this */ - public function year($year) + public function year($year, $shared = false) { - /** @var Preference $pref */ - $pref = Preferences::get('showSharedReports', false); - $showSharedReports = $pref->data; - $date = new Carbon('01-01-' . $year); - $end = clone $date; + $start = new Carbon('01-01-' . $year); + $end = clone $start; + $subTitle = trans('firefly.reportForYear', ['year' => $year]); + $subTitleIcon = 'fa-bar-chart'; + $incomeTopLength = 8; + $expenseTopLength = 8; + + if ($shared == 'shared') { + $shared = true; + $subTitle = trans('firefly.reportForYearShared', ['year' => $year]); + } $end->endOfYear(); - $title = 'Reports'; - $subTitle = $year; - $subTitleIcon = 'fa-bar-chart'; - $mainTitleIcon = 'fa-line-chart'; - $balances = $this->helper->yearBalanceReport($date, $showSharedReports); - $groupedIncomes = $this->query->journalsByRevenueAccount($date, $end, $showSharedReports); - $groupedExpenses = $this->query->journalsByExpenseAccount($date, $end, $showSharedReports); + + $accounts = $this->helper->getAccountReport($start, $end, $shared); + $incomes = $this->helper->getIncomeReport($start, $end, $shared); + $expenses = $this->helper->getExpenseReport($start, $end, $shared); + 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. + ) ); } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 77a5daf1f8..90cd841829 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -289,9 +289,9 @@ class TagController extends Controller public function update(TagFormRequest $request, TagRepositoryInterface $repository, Tag $tag) { if (Input::get('setTag') == 'true') { - $latitude = strlen($request->get('latitude')) > 0 ? $request->get('latitude') : null; - $longitude = strlen($request->get('longitude')) > 0 ? $request->get('longitude') : null; - $zoomLevel = strlen($request->get('zoomLevel')) > 0 ? $request->get('zoomLevel') : null; + $latitude = $request->get('latitude'); + $longitude = $request->get('longitude'); + $zoomLevel = $request->get('zoomLevel'); } else { $latitude = null; $longitude = null; diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 5ee2a7a690..e8a0c23a81 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -30,7 +30,7 @@ class TransactionController extends Controller public function __construct() { parent::__construct(); - View::share('title', 'Transactions'); + View::share('title', trans('firefly.transactions')); View::share('mainTitleIcon', 'fa-repeat'); } @@ -44,17 +44,15 @@ class TransactionController extends Controller { $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); - $budgets[0] = '(no budget)'; + $budgets[0] = trans('form.noBudget'); $piggies = ExpandedForm::makeSelectList(Auth::user()->piggyBanks()->get()); - $piggies[0] = '(no piggy bank)'; + $piggies[0] = trans('form.noPiggybank'); $preFilled = Session::has('preFilled') ? Session::get('preFilled') : []; $respondTo = ['account_id', 'account_from_id']; - $subTitle = 'Add a new ' . e($what); + $subTitle = trans('form.add_new_' . $what); foreach ($respondTo as $r) { - if (!is_null(Input::get($r))) { - $preFilled[$r] = Input::get($r); - } + $preFilled[$r] = Input::get($r); } Session::put('preFilled', $preFilled); @@ -119,10 +117,11 @@ class TransactionController extends Controller $what = strtolower($journal->transactiontype->type); $accounts = ExpandedForm::makeSelectList($repository->getAccounts(['Default account', 'Asset account'])); $budgets = ExpandedForm::makeSelectList(Auth::user()->budgets()->get()); - $budgets[0] = '(no budget)'; + $budgets[0] = trans('form.noBudget'); $transactions = $journal->transactions()->orderBy('amount', 'DESC')->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 = [ 'date' => $journal->date->format('Y-m-d'), 'category' => '', @@ -182,19 +181,19 @@ class TransactionController extends Controller case 'expenses': case 'withdrawal': $subTitleIcon = 'fa-long-arrow-left'; - $subTitle = 'Expenses'; + $subTitle = trans('firefly.expenses'); $types = ['Withdrawal']; break; case 'revenue': case 'deposit': $subTitleIcon = 'fa-long-arrow-right'; - $subTitle = 'Revenue, income and deposits'; + $subTitle = trans('firefly.income'); $types = ['Deposit']; break; case 'transfer': case 'transfers': $subTitleIcon = 'fa-exchange'; - $subTitle = 'Transfers'; + $subTitle = trans('firefly.transfers'); $types = ['Transfer']; break; } @@ -249,7 +248,7 @@ class TransactionController extends Controller $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')); } @@ -269,7 +268,9 @@ class TransactionController extends Controller // rescan journal, UpdateJournalConnection event(new JournalSaved($journal)); // ConnectJournalToPiggyBank - event(new JournalCreated($journal, intval($request->get('piggy_bank_id')))); + if ($journal->transactionType->type == 'Transfer' && intval($request->get('piggy_bank_id')) > 0) { + event(new JournalCreated($journal, intval($request->get('piggy_bank_id')))); + } $repository->deactivateReminder($request->get('reminder_id')); diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index f5cfcdd144..d4a32536b8 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -1,8 +1,12 @@ 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); } diff --git a/app/Http/Middleware/PiggyBanks.php b/app/Http/Middleware/PiggyBanks.php index 810f396fce..eff3e9aea7 100644 --- a/app/Http/Middleware/PiggyBanks.php +++ b/app/Http/Middleware/PiggyBanks.php @@ -3,7 +3,6 @@ namespace FireflyIII\Http\Middleware; -use App; use Closure; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankRepetition; @@ -53,16 +52,15 @@ class PiggyBanks ->leftJoin('piggy_bank_repetitions', 'piggy_banks.id', '=', 'piggy_bank_repetitions.piggy_bank_id') ->whereNull('piggy_bank_repetitions.id') ->get(['piggy_banks.id', 'piggy_banks.startdate', 'piggy_banks.targetdate']); - if ($set->count() > 0) { - /** @var PiggyBank $partialPiggy */ - foreach ($set as $partialPiggy) { - $repetition = new PiggyBankRepetition; - $repetition->piggyBank()->associate($partialPiggy); - $repetition->startdate = is_null($partialPiggy->startdate) ? null : $partialPiggy->startdate; - $repetition->targetdate = is_null($partialPiggy->targetdate) ? null : $partialPiggy->targetdate; - $repetition->currentamount = 0; - $repetition->save(); - } + + /** @var PiggyBank $partialPiggy */ + foreach ($set as $partialPiggy) { + $repetition = new PiggyBankRepetition; + $repetition->piggyBank()->associate($partialPiggy); + $repetition->startdate = $partialPiggy->startdate; + $repetition->targetdate = $partialPiggy->targetdate; + $repetition->currentamount = 0; + $repetition->save(); } unset($partialPiggy, $set, $repetition); } diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index aa7fd7a98b..9faf2b4e00 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -43,6 +43,7 @@ class Range * * @param \Illuminate\Http\Request $request * @param \Closure $theNext + * @SuppressWarnings(PHPMD.CyclomaticComplexity) * * @return mixed */ @@ -63,9 +64,6 @@ class Range Session::put('end', $end); } if (!Session::has('first')) { - /** - * Get helper thing. - */ /** @var \FireflyIII\Repositories\Journal\JournalRepositoryInterface $repository */ $repository = App::make('FireflyIII\Repositories\Journal\JournalRepositoryInterface'); $journal = $repository->first(); @@ -75,16 +73,12 @@ class Range Session::put('first', Carbon::now()->startOfYear()); } } - - // set current / next / prev month. $current = Carbon::now()->format('F Y'); $next = Carbon::now()->endOfMonth()->addDay()->format('F Y'); $prev = Carbon::now()->startOfMonth()->subDay()->format('F Y'); View::share('currentMonthName', $current); View::share('previousMonthName', $prev); View::share('nextMonthName', $next); - - } return $theNext($request); diff --git a/app/Http/Middleware/Reminders.php b/app/Http/Middleware/Reminders.php index 55c555aec0..c64c2ad1fa 100644 --- a/app/Http/Middleware/Reminders.php +++ b/app/Http/Middleware/Reminders.php @@ -49,33 +49,22 @@ class Reminders if ($this->auth->check() && !$request->isXmlHttpRequest()) { // do reminders stuff. $piggyBanks = $this->auth->user()->piggyBanks()->where('remind_me', 1)->get(); - $today = new Carbon; /** @var \FireflyIII\Helpers\Reminders\ReminderHelperInterface $helper */ $helper = App::make('FireflyIII\Helpers\Reminders\ReminderHelperInterface'); /** @var PiggyBank $piggyBank */ foreach ($piggyBanks as $piggyBank) { - $ranges = $helper->getReminderRanges($piggyBank); - - 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; - } - - } + $helper->createReminders($piggyBank, new Carbon); } // delete invalid reminders - $set = $this->auth->user()->reminders()->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id')->whereNull('piggy_banks.id')->get( - ['reminders.id'] - ); - foreach ($set as $reminder) { - $reminder->delete(); + // this is a construction SQLITE cannot handle :( + if (env('DB_CONNECTION') != 'sqlite') { + Reminder::whereUserId($this->auth->user()->id) + ->leftJoin('piggy_banks', 'piggy_banks.id', '=', 'remindersable_id') + ->whereNull('piggy_banks.id') + ->delete(); } - // get and list active reminders: $reminders = $this->auth->user()->reminders()->today()->get(); $reminders->each( diff --git a/app/Http/Requests/JournalFormRequest.php b/app/Http/Requests/JournalFormRequest.php index 2ae445e1e9..1e3d4e0d53 100644 --- a/app/Http/Requests/JournalFormRequest.php +++ b/app/Http/Requests/JournalFormRequest.php @@ -50,12 +50,11 @@ class JournalFormRequest extends Request /** * @return array * @throws Exception + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function rules() { - // can we switch on the "what"? - $what = Input::get('what'); - + $what = Input::get('what'); $rules = [ 'description' => 'required|min:1,max:255', 'what' => 'required|in:withdrawal,deposit,transfer', @@ -74,8 +73,6 @@ class JournalFormRequest extends Request if (intval(Input::get('budget_id')) != 0) { $rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets'; } - - break; case 'deposit': $rules['category'] = 'between:1,255'; @@ -93,7 +90,5 @@ class JournalFormRequest extends Request } return $rules; - - } } diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index 777288d5a8..2d0aefc35a 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -48,6 +48,7 @@ class TagFormRequest extends Request 'date' => 'date', 'latitude' => 'numeric|min:-90|max:90', 'longitude' => 'numeric|min:-90|max:90', + 'zoomLevel' => 'numeric|min:0|max:80', 'tagMode' => 'required|in:nothing,balancingAct,advancePayment' ]; } diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index a038774e44..5f3c45adfb 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -20,7 +20,7 @@ Breadcrumbs::register( 'home', function (Generator $breadcrumbs) { - $breadcrumbs->push('Home', route('index')); + $breadcrumbs->push(trans('breadcrumbs.home'), route('index')); } ); @@ -28,17 +28,26 @@ Breadcrumbs::register( 'index', function (Generator $breadcrumbs) { - $breadcrumbs->push('Home', route('index')); + $breadcrumbs->push(trans('breadcrumbs.home'), route('index')); } ); +//trans('breadcrumbs.') // accounts Breadcrumbs::register( 'accounts.index', function (Generator $breadcrumbs, $what) { $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( 'accounts.show', function (Generator $breadcrumbs, Account $account) { switch ($account->accountType->type) { @@ -67,14 +76,15 @@ Breadcrumbs::register( Breadcrumbs::register( 'accounts.delete', function (Generator $breadcrumbs, Account $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( 'accounts.edit', function (Generator $breadcrumbs, Account $account) { $breadcrumbs->parent('accounts.show', $account); - $breadcrumbs->push('Edit ' . e($account->name), route('accounts.edit', $account->id)); + $breadcrumbs->push(trans('breadcrumbs.edit_account', ['name' => e($account->name)]), route('accounts.edit', $account->id)); } ); @@ -82,26 +92,26 @@ Breadcrumbs::register( Breadcrumbs::register( 'budgets.index', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Budgets', route('budgets.index')); + $breadcrumbs->push(trans('breadcrumbs.budgets'), route('budgets.index')); } ); Breadcrumbs::register( 'budgets.create', function (Generator $breadcrumbs) { $breadcrumbs->parent('budgets.index'); - $breadcrumbs->push('Create new budget', route('budgets.create')); + $breadcrumbs->push(trans('breadcrumbs.newBudget'), route('budgets.create')); } ); Breadcrumbs::register( 'budgets.edit', function (Generator $breadcrumbs, Budget $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( 'budgets.delete', function (Generator $breadcrumbs, Budget $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 +138,26 @@ Breadcrumbs::register( Breadcrumbs::register( 'categories.index', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Categories', route('categories.index')); + $breadcrumbs->push(trans('breadcrumbs.categories'), route('categories.index')); } ); Breadcrumbs::register( 'categories.create', function (Generator $breadcrumbs) { $breadcrumbs->parent('categories.index'); - $breadcrumbs->push('Create new category', route('categories.create')); + $breadcrumbs->push(trans('breadcrumbs.newCategory'), route('categories.create')); } ); Breadcrumbs::register( 'categories.edit', function (Generator $breadcrumbs, Category $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( 'categories.delete', function (Generator $breadcrumbs, Category $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 +180,20 @@ Breadcrumbs::register( Breadcrumbs::register( 'currency.index', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Currencies', route('currency.index')); + $breadcrumbs->push(trans('breadcrumbs.currencies'), route('currency.index')); } ); Breadcrumbs::register( 'currency.edit', function (Generator $breadcrumbs, TransactionCurrency $currency) { $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( 'currency.delete', function (Generator $breadcrumbs, TransactionCurrency $currency) { $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 +202,26 @@ Breadcrumbs::register( Breadcrumbs::register( 'piggy-banks.index', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Piggy banks', route('piggy-banks.index')); + $breadcrumbs->push(trans('breadcrumbs.piggyBanks'), route('piggy-banks.index')); } ); Breadcrumbs::register( 'piggy-banks.create', function (Generator $breadcrumbs) { $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( 'piggy-banks.edit', function (Generator $breadcrumbs, PiggyBank $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( 'piggy-banks.delete', function (Generator $breadcrumbs, PiggyBank $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 +237,7 @@ Breadcrumbs::register( Breadcrumbs::register( 'preferences', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Preferences', route('preferences')); + $breadcrumbs->push(trans('breadcrumbs.preferences'), route('preferences')); } ); @@ -236,14 +246,14 @@ Breadcrumbs::register( Breadcrumbs::register( 'profile', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Profile', route('profile')); + $breadcrumbs->push(trans('breadcrumbs.profile'), route('profile')); } ); Breadcrumbs::register( 'change-password', function (Generator $breadcrumbs) { $breadcrumbs->parent('profile'); - $breadcrumbs->push('Change your password', route('change-password')); + $breadcrumbs->push(trans('breadcrumbs.changePassword'), route('change-password')); } ); @@ -252,26 +262,26 @@ Breadcrumbs::register( Breadcrumbs::register( 'bills.index', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Bills', route('bills.index')); + $breadcrumbs->push(trans('breadcrumbs.bills'), route('bills.index')); } ); Breadcrumbs::register( 'bills.create', function (Generator $breadcrumbs) { $breadcrumbs->parent('bills.index'); - $breadcrumbs->push('Create new bill', route('bills.create')); + $breadcrumbs->push(trans('breadcrumbs.newBill'), route('bills.create')); } ); Breadcrumbs::register( 'bills.edit', function (Generator $breadcrumbs, Bill $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( 'bills.delete', function (Generator $breadcrumbs, Bill $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 +297,7 @@ Breadcrumbs::register( Breadcrumbs::register( 'reminders.index', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Reminders', route('reminders.index')); + $breadcrumbs->push(trans('breadcrumbs.reminders'), route('reminders.index')); } ); @@ -296,7 +306,7 @@ Breadcrumbs::register( Breadcrumbs::register( 'reminders.show', function (Generator $breadcrumbs, Reminder $reminder) { $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 +316,33 @@ Breadcrumbs::register( Breadcrumbs::register( 'reports.index', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Reports', route('reports.index')); + $breadcrumbs->push(trans('breadcrumbs.reports'), route('reports.index')); } ); Breadcrumbs::register( - 'reports.year', function (Generator $breadcrumbs, Carbon $date) { + 'reports.year', function (Generator $breadcrumbs, Carbon $date, $shared) { $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( - 'reports.month', function (Generator $breadcrumbs, Carbon $date) { - $breadcrumbs->parent('reports.year', $date); - $breadcrumbs->push('Monthly report for ' . $date->format('F Y'), route('reports.month', [$date->year, $date->month])); -} -); + 'reports.month', function (Generator $breadcrumbs, Carbon $date, $shared) { + $breadcrumbs->parent('reports.year', $date, $shared); -Breadcrumbs::register( - 'reports.budget', function (Generator $breadcrumbs, Carbon $date) { - $breadcrumbs->parent('reports.index'); - $breadcrumbs->push('Budget report for ' . $date->format('F Y'), route('reports.budget', [$date->year, $date->month])); + if ($shared) { + $title = trans('breadcrumbs.monthly_report_shared', ['date' => $date->year]); + } else { + $title = trans('breadcrumbs.monthly_report', ['date' => $date->year]); + } + + $breadcrumbs->push($title, route('reports.month', [$date->year, $date->month])); } ); @@ -335,7 +350,7 @@ Breadcrumbs::register( Breadcrumbs::register( 'search', function (Generator $breadcrumbs, $query) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Search for "' . e($query) . '"', route('search')); + $breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search')); } ); @@ -343,47 +358,26 @@ Breadcrumbs::register( Breadcrumbs::register( 'transactions.index', function (Generator $breadcrumbs, $what) { $breadcrumbs->parent('home'); - - 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->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', $what)); } ); Breadcrumbs::register( 'transactions.create', function (Generator $breadcrumbs, $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( 'transactions.edit', function (Generator $breadcrumbs, TransactionJournal $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( 'transactions.delete', function (Generator $breadcrumbs, TransactionJournal $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 +385,7 @@ Breadcrumbs::register( 'transactions.show', function (Generator $breadcrumbs, TransactionJournal $journal) { $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 +394,32 @@ Breadcrumbs::register( Breadcrumbs::register( 'tags.index', function (Generator $breadcrumbs) { $breadcrumbs->parent('home'); - $breadcrumbs->push('Tags', route('tags.index')); + $breadcrumbs->push(trans('breadcrumbs.tags'), route('tags.index')); } ); Breadcrumbs::register( 'tags.create', function (Generator $breadcrumbs) { $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( 'tags.show', function (Generator $breadcrumbs, Tag $tag) { $breadcrumbs->parent('tags.index'); diff --git a/app/Http/routes.php b/app/Http/routes.php index 957d55829a..c5b4ed80af 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -188,7 +188,7 @@ Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'r Route::controllers( [ - 'auth' => 'Auth\AuthController', + 'auth' => 'Auth\AuthController', 'password' => 'Auth\PasswordController', ] ); @@ -223,7 +223,6 @@ Route::group( 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/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/show/{bill}', ['uses' => 'BillController@show', 'as' => 'bills.show']); Route::post('/bills/store', ['uses' => 'BillController@store', 'as' => 'bills.store']); @@ -273,22 +272,38 @@ Route::group( /** - * Google Chart Controller + * ALL CHART Controllers */ - Route::get('/chart/home/account', ['uses' => 'GoogleChartController@allAccountsBalanceChart']); - Route::get('/chart/home/budgets', ['uses' => 'GoogleChartController@allBudgetsHomeChart']); - Route::get('/chart/home/categories', ['uses' => 'GoogleChartController@allCategoriesHomeChart']); - Route::get('/chart/home/bills', ['uses' => 'GoogleChartController@billsOverview']); - Route::get('/chart/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']); - Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']); - 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']); - Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']); - Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']); - Route::get('/chart/piggy-history/{piggyBank}', ['uses' => 'GoogleChartController@piggyBankHistory']); - Route::get('/chart/category/{category}/period', ['uses' => 'GoogleChartController@categoryPeriodChart']); - Route::get('/chart/category/{category}/overview', ['uses' => 'GoogleChartController@categoryOverviewChart']); + // accounts: + Route::get('/chart/account/frontpage', ['uses' => 'Chart\AccountController@frontpage']); + Route::get('/chart/account/month/{year}/{month}/{shared?}', ['uses' => 'Chart\AccountController@all'])->where(['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared']); + Route::get('/chart/account/{account}', ['uses' => 'Chart\AccountController@single']); + + + // bills: + Route::get('/chart/bill/frontpage', ['uses' => 'Chart\BillController@frontpage']); + Route::get('/chart/bill/{bill}', ['uses' => 'Chart\BillController@single']); + + // budgets: + Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']); + Route::get('/chart/budget/year/{year}/{shared?}', ['uses' => 'Chart\BudgetController@year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']); + Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']); + + // 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 @@ -306,10 +321,7 @@ Route::group( 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-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/show-shared-reports/set', 'JsonController@setSharedReports'); - /** * Piggy Bank Controller @@ -355,19 +367,13 @@ Route::group( * Report Controller */ Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']); - Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year']); - Route::get('/reports/{year}/{month}', ['uses' => 'ReportController@month', 'as' => 'reports.month']); - Route::get('/reports/budget/{year}/{month}', ['uses' => 'ReportController@budget', 'as' => 'reports.budget']); + //Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}']); + Route::get('/reports/{year}/{shared?}', ['uses' => 'ReportController@year', 'as' => 'reports.year'])->where(['year' => '[0-9]{4}', 'shared' => 'shared']); + Route::get('/reports/{year}/{month}/{shared?}', ['uses' => 'ReportController@month', 'as' => 'reports.month'])->where( + ['year' => '[0-9]{4}', 'month' => '[0-9]{1,2}', 'shared' => 'shared'] + ); // pop ups for budget report: - Route::get('/reports/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 diff --git a/app/Models/Account.php b/app/Models/Account.php index e9e39346f5..d9733d77fd 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -28,7 +28,7 @@ class Account extends Model /** * @param array $fields - * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) * * @return Account|null */ @@ -133,6 +133,7 @@ class Account extends Model /** * @codeCoverageIgnore + * * @param $value * * @return string @@ -160,6 +161,7 @@ class Account extends Model /** * @codeCoverageIgnore + * * @param EloquentBuilder $query * @param array $types */ @@ -174,6 +176,7 @@ class Account extends Model /** * @codeCoverageIgnore + * * @param EloquentBuilder $query * @param string $name * @param string $value @@ -191,6 +194,7 @@ class Account extends Model /** * @codeCoverageIgnore + * * @param $value */ public function setNameAttribute($value) diff --git a/app/Models/Category.php b/app/Models/Category.php index 363f773d2a..a37dae2fff 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -37,6 +37,7 @@ class Category extends Model /** * @param array $fields + * @SuppressWarnings(PHPMD.CyclomaticComplexity) * * @return Account|null */ @@ -79,6 +80,7 @@ class Category extends Model /** * @codeCoverageIgnore + * * @param $value */ public function setNameAttribute($value) @@ -89,6 +91,7 @@ class Category extends Model /** * @codeCoverageIgnore + * * @param $value * * @return string diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 9879b3f1a8..f4486b99c3 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -64,6 +64,7 @@ class PiggyBank extends Model /** * @codeCoverageIgnore + * * @param $value * * @return int @@ -93,6 +94,7 @@ class PiggyBank extends Model /** * @codeCoverageIgnore + * * @param $value */ public function setNameAttribute($value) @@ -103,6 +105,7 @@ class PiggyBank extends Model /** * @codeCoverageIgnore + * * @param $value * * @return string diff --git a/app/Models/Reminder.php b/app/Models/Reminder.php index 0160f1a9f1..38da24deed 100644 --- a/app/Models/Reminder.php +++ b/app/Models/Reminder.php @@ -18,6 +18,7 @@ class Reminder extends Model /** * @codeCoverageIgnore + * * @param $value * * @return int @@ -38,6 +39,7 @@ class Reminder extends Model /** * @codeCoverageIgnore + * * @param $value * * @return mixed @@ -53,6 +55,7 @@ class Reminder extends Model /** * @codeCoverageIgnore + * * @param $value * * @return bool @@ -73,6 +76,7 @@ class Reminder extends Model /** * @codeCoverageIgnore + * * @param EloquentBuilder $query * @param Carbon $start * @param Carbon $end @@ -86,6 +90,7 @@ class Reminder extends Model /** * @codeCoverageIgnore + * * @param EloquentBuilder $query * * @return $this @@ -100,6 +105,7 @@ class Reminder extends Model /** * @codeCoverageIgnore + * * @param $value */ public function setMetadataAttribute($value) diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 286a481fc0..68955aa201 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -29,6 +29,8 @@ class Tag extends Model /** * @param array $fields + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) * * @return Tag|null */ @@ -77,6 +79,7 @@ class Tag extends Model /** * @codeCoverageIgnore + * * @param $value * * @return string @@ -88,6 +91,7 @@ class Tag extends Model /** * @codeCoverageIgnore + * * @param $value * * @return string @@ -99,6 +103,7 @@ class Tag extends Model /** * @codeCoverageIgnore + * * @param $value */ public function setDescriptionAttribute($value) @@ -108,6 +113,7 @@ class Tag extends Model /** * @codeCoverageIgnore + * * @param $value */ public function setTagAttribute($value) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 7fd1e4db4c..9bbe977e46 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Database\Query\JoinClause; use Watson\Validating\ValidatingTrait; /** @@ -61,20 +62,66 @@ class TransactionJournal extends Model /** * @return float */ - public function getAmountAttribute() + public function getActualAmountAttribute() { + $amount = 0; /** @var Transaction $t */ foreach ($this->transactions as $t) { 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 $tag */ + $tag = $this->tags()->where('tagMode', 'advancePayment')->first(); + if ($tag && $this->transactionType->type == 'Withdrawal') { + // loop other deposits, remove from our amount. + $others = $tag->transactionJournals()->transactionTypes(['Deposit'])->get(); + foreach ($others as $other) { + $amount -= $other->amount; + } + + 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() { @@ -93,7 +140,7 @@ class TransactionJournal extends Model } - return $this->transactions()->first(); + return $this->transactions()->first()->account; } /** @@ -105,6 +152,28 @@ class TransactionJournal extends Model 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 * @return array @@ -116,6 +185,7 @@ class TransactionJournal extends Model /** * @codeCoverageIgnore + * * @param $value * * @return string @@ -140,6 +210,7 @@ class TransactionJournal extends Model /** * @codeCoverageIgnore + * * @param EloquentBuilder $query * @param Account $account */ @@ -154,6 +225,7 @@ class TransactionJournal extends Model /** * @codeCoverageIgnore + * * @param EloquentBuilder $query * @param Carbon $date * @@ -166,6 +238,7 @@ class TransactionJournal extends Model /** * @codeCoverageIgnore + * * @param EloquentBuilder $query * @param Carbon $date * @@ -178,6 +251,7 @@ class TransactionJournal extends Model /** * @codeCoverageIgnore + * * @param EloquentBuilder $query * @param $amount */ @@ -195,6 +269,7 @@ class TransactionJournal extends Model /** * @codeCoverageIgnore + * * @param EloquentBuilder $query * @param Carbon $date * @@ -206,7 +281,26 @@ class TransactionJournal extends Model } /** + * Returns the account to which the money was moved. + * * @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 array $types */ @@ -239,6 +333,7 @@ class TransactionJournal extends Model /** * @codeCoverageIgnore + * * @param $value */ public function setDescriptionAttribute($value) @@ -247,15 +342,6 @@ class TransactionJournal extends Model $this->attributes['encrypted'] = true; } - /** - * @codeCoverageIgnore - * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany - */ - public function tags() - { - return $this->belongsToMany('FireflyIII\Models\Tag'); - } - /** * @codeCoverageIgnore * @return \Illuminate\Database\Eloquent\Relations\BelongsTo diff --git a/app/Providers/ConfigServiceProvider.php b/app/Providers/ConfigServiceProvider.php index b758d58acd..f5b1b6f7ab 100644 --- a/app/Providers/ConfigServiceProvider.php +++ b/app/Providers/ConfigServiceProvider.php @@ -17,6 +17,8 @@ class ConfigServiceProvider extends ServiceProvider * to overwrite any "vendor" or package configuration that you may want to * modify before the application handles the incoming request / command. * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * * @return void */ public function register() @@ -163,6 +165,7 @@ class ConfigServiceProvider extends ServiceProvider 'Session', 'Route', 'Auth', + 'Lang', 'URL', 'Config', 'ExpandedForm' => [ diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index fd52a224f7..9299f12fcb 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -49,7 +49,47 @@ class EventServiceProvider extends ServiceProvider public function boot(DispatcherContract $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( function (TransactionJournal $journal) { @@ -59,8 +99,6 @@ class EventServiceProvider extends ServiceProvider } } ); - - PiggyBank::deleting( function (PiggyBank $piggyBank) { $reminders = $piggyBank->reminders()->get(); @@ -82,6 +120,14 @@ class EventServiceProvider extends ServiceProvider } ); + } + + /** + * + */ + protected function registerCreateEvents() + { + // move this routine to a filter // in case of repeated piggy banks and/or other problems. PiggyBank::created( @@ -94,47 +140,6 @@ class EventServiceProvider extends ServiceProvider $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(); - - } - } - } - ); - - } } diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 066ee64665..e3e1a90fb9 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -10,9 +10,9 @@ use FireflyIII\Support\Preferences; use FireflyIII\Support\Steam; use FireflyIII\Support\Twig\Budget; use FireflyIII\Support\Twig\General; -use FireflyIII\Support\Twig\Translation; use FireflyIII\Support\Twig\Journal; use FireflyIII\Support\Twig\PiggyBank; +use FireflyIII\Support\Twig\Translation; use FireflyIII\Validation\FireflyValidator; use Illuminate\Support\ServiceProvider; use Twig; @@ -46,6 +46,9 @@ class FireflyServiceProvider extends ServiceProvider Twig::addExtension(new Translation); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function register() { diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 7c3f3e94ff..b5e7cfcd31 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -162,8 +162,6 @@ class AccountRepository implements AccountRepositoryInterface ->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.id', 'DESC'); - $query->before(Session::get('end', Carbon::now()->endOfMonth())); - $query->after(Session::get('start', Carbon::now()->startOfMonth())); $count = $query->count(); $set = $query->take(50)->offset($offset)->get(['transaction_journals.*']); $paginator = new LengthAwarePaginator($set, $count, 50, $page); @@ -308,12 +306,14 @@ class AccountRepository implements AccountRepositoryInterface /** * @param Account $account + * @param Carbon $date * * @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 */ foreach ($account->piggybanks()->get() as $p) { $balance -= $p->currentRelevantRep()->currentamount; diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index cc80a6c403..1a25f120d5 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -115,10 +115,11 @@ interface AccountRepositoryInterface /** * @param Account $account + * @param Carbon $date * * @return float */ - public function leftOnAccount(Account $account); + public function leftOnAccount(Account $account, Carbon $date); /** * @param Account $account diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 0c3419f7b9..d384b8da8a 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -22,6 +22,27 @@ use Navigation; */ 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. * @@ -251,13 +272,10 @@ class BillRepository implements BillRepositoryInterface */ public function scan(Bill $bill, TransactionJournal $journal) { - /* - * Match words. - */ + $amountMatch = false; $wordMatch = false; $matches = explode(',', $bill->match); $description = strtolower($journal->description); - Log::debug('Now scanning ' . $description); /* * Attach expense account to description for more narrow matching. @@ -295,7 +313,6 @@ class BillRepository implements BillRepositoryInterface * Match amount. */ - $amountMatch = false; if (count($transactions) > 1) { $amount = max(floatval($transactions[0]->amount), floatval($transactions[1]->amount)); diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index a6d5c5ea1e..4f2b509786 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -15,6 +15,17 @@ use Illuminate\Support\Collection; 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. * diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index b0e3ac5559..96a5899e13 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -8,6 +8,7 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\LimitRepetition; use Illuminate\Database\Query\Builder as QueryBuilder; +use Illuminate\Database\Query\JoinClause; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Input; @@ -98,8 +99,6 @@ class BudgetRepository implements BudgetRepositoryInterface public function getBudgets() { $budgets = Auth::user()->budgets()->get(); - $budgets->sortBy('name'); - return $budgets; } @@ -250,22 +249,40 @@ class BudgetRepository implements BudgetRepositoryInterface ->before($end) ->lessThan(0) ->transactionTypes(['Withdrawal']) - ->sum('transactions.amount'); + ->sum('transactions.amount'); + return floatval($noBudgetSet) * -1; } /** * @param Budget $budget - * @param Carbon $date + * @param Carbon $start + * @param Carbon $end + * @param bool $shared * * @return float */ - public function spentInMonth(Budget $budget, Carbon $date) + public function spentInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true) { - $end = clone $date; - $date->startOfMonth(); - $end->endOfMonth(); - $sum = floatval($budget->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1; + if ($shared === true) { + // get everything: + $sum = floatval($budget->transactionjournals()->before($end)->after($start)->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"') + ->sum('amount'); + $sum = floatval($sum) * -1; + } return $sum; } diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index d2c0cd0bed..fc65660310 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -124,11 +124,13 @@ interface BudgetRepositoryInterface /** * @param Budget $budget - * @param Carbon $date + * @param Carbon $start + * @param Carbon $end + * @param boolean $shared * * @return float */ - public function spentInMonth(Budget $budget, Carbon $date); + public function spentInPeriod(Budget $budget, Carbon $start, Carbon $end, $shared = true); /** * @param array $data diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 9fe14b1bbb..e67a9db2a8 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -168,11 +168,41 @@ class CategoryRepository implements CategoryRepositoryInterface * @param Carbon $start * @param Carbon $end * + * @param bool $shared + * * @return float */ - public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end) + public function spentInPeriod(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)->lessThan(0)->sum('amount') + ) * -1; + + } 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"') + ->sum('amount'); + $sum = floatval($sum) * -1; + } + + return $sum; } /** diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 30456995a1..2c1355c057 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -75,9 +75,11 @@ interface CategoryRepositoryInterface * @param \Carbon\Carbon $start * @param \Carbon\Carbon $end * + * @param bool $shared + * * @return float */ - public function spentInPeriodSum(Category $category, Carbon $start, Carbon $end); + public function spentInPeriod(Category $category, Carbon $start, Carbon $end, $shared = false); /** * @param Category $category diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 012a144bdb..c13c79adb7 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -338,61 +338,86 @@ class JournalRepository implements JournalRepositoryInterface */ protected function storeAccounts(TransactionType $type, array $data) { - $from = null; - $to = null; + $fromAccount = null; + $toAccount = null; switch ($type->type) { case 'Withdrawal': - - $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] - ); - } + list($fromAccount, $toAccount) = $this->storeWithdrawalAccounts($data); break; case 'Deposit': - $to = Account::find($data['account_id']); - - 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] - ); - } + list($fromAccount, $toAccount) = $this->storeDepositAccounts($data); break; case 'Transfer': - $from = Account::find($data['account_from_id']); - $to = Account::find($data['account_to_id']); + $fromAccount = Account::find($data['account_from_id']); + $toAccount = Account::find($data['account_to_id']); 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!'); App::abort(500, '"to"-account is null, so we cannot continue!'); // @codeCoverageIgnoreStart } // @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!'); App::abort(500, '"from"-account is null, so we cannot continue!'); // @codeCoverageIgnoreStart } + // @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]; } } diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 6fe50edd23..ab4ad796b3 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -19,57 +19,6 @@ use Navigation; 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 $amount @@ -83,24 +32,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface 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 * @@ -151,8 +82,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface { // split query to make it work in sqlite: $set = PiggyBank:: - leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.id') - ->where('accounts.user_id', Auth::user()->id)->get(['piggy_banks.*']); + leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.id') + ->where('accounts.user_id', Auth::user()->id)->get(['piggy_banks.*']); foreach ($set as $e) { $e->order = 0; $e->save(); @@ -198,7 +129,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface * @param array $data * * @return PiggyBank - * @internal param PiggyBank $account */ public function update(PiggyBank $piggyBank, array $data) { diff --git a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php index ce67a6b6b0..7724a294a8 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php +++ b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php @@ -14,18 +14,6 @@ use Illuminate\Support\Collection; 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 */ @@ -38,13 +26,6 @@ interface PiggyBankRepositoryInterface */ public function getEvents(PiggyBank $piggyBank); - /** - * @param array $data - * - * @return PiggyBankPart - */ - public function createPiggyBankPart(array $data); - /** * @param PiggyBank $piggyBank * @param $amount diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 4f27c7f9d9..753d35f23e 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -4,6 +4,8 @@ namespace FireflyIII\Repositories\Tag; use Auth; +use Carbon\Carbon; +use FireflyIII\Models\Account; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; @@ -17,7 +19,10 @@ use Illuminate\Support\Collection; class TagRepository implements TagRepositoryInterface { + /** + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five. + * * @param TransactionJournal $journal * @param Tag $tag * @@ -33,88 +38,54 @@ class TagRepository implements TagRepositoryInterface return false; } - if ($tag->tagMode == 'nothing') { - // save it, no problem: - $journal->tags()->save($tag); - - return true; - } - - /* - * get some withdrawal types: - */ - /** @var TransactionType $withdrawal */ - $withdrawal = TransactionType::whereType('Withdrawal')->first(); - /** @var TransactionType $deposit */ - $deposit = TransactionType::whereType('Deposit')->first(); - /** @var TransactionType $transfer */ - $transfer = TransactionType::whereType('Transfer')->first(); - - $withdrawals = $tag->transactionjournals()->where('transaction_type_id', $withdrawal->id)->count(); - $transfers = $tag->transactionjournals()->where('transaction_type_id', $transfer->id)->count(); - $deposits = $tag->transactionjournals()->where('transaction_type_id', $deposit->id)->count(); - - if ($tag->tagMode == 'balancingAct') { - - // only if this is the only withdrawal. - if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) { + switch ($tag->tagMode) { + case 'nothing': $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; + break; + case 'balancingAct': + return $this->connectBalancingAct($journal, $tag); + break; + case 'advancePayment': + return $this->connectAdvancePayment($journal, $tag); + break; } - 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 false; - } - // @codeCoverageIgnoreStart return false; } - // @codeCoverageIgnoreEnd + + /** + * 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) + { + // the quickest way to do this is by scanning all balancingAct tags + // because there will be less of them any way. + $tags = Auth::user()->tags()->where('tagMode', 'balancingAct')->get(); + $amount = 0; + + /** @var Tag $tag */ + foreach ($tags as $tag) { + $transfer = $tag->transactionjournals()->after($start)->before($end)->toAccountIs($account)->transactionTypes(['Transfer'])->first(); + if ($transfer) { + $amount += $transfer->amount; + } + } + + return $amount; + } /** * @param Tag $tag @@ -127,6 +98,7 @@ class TagRepository implements TagRepositoryInterface return true; } + // @codeCoverageIgnoreEnd /** * @return Collection @@ -186,4 +158,107 @@ class TagRepository implements TagRepositoryInterface 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; + } } diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index 94d5e44cb2..95cbe558e5 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -2,6 +2,8 @@ namespace FireflyIII\Repositories\Tag; +use Carbon\Carbon; +use FireflyIII\Models\Account; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; use Illuminate\Support\Collection; @@ -15,6 +17,23 @@ use Illuminate\Support\Collection; 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 * diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 583b7e73a9..8a1916e9ca 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -84,32 +84,20 @@ class Amount */ public function formatJournal(TransactionJournal $journal, $coloured = true) { - $showPositive = true; if (is_null($journal->symbol)) { $symbol = $journal->transactionCurrency->symbol; } else { $symbol = $journal->symbol; } - $amount = 0; - - if (is_null($journal->type)) { - $type = $journal->transactionType->type; - } else { - $type = $journal->type; + $amount = $journal->amount; + if ($journal->transactionType->type == 'Withdrawal') { + $amount = $amount * -1; } - - if ($type == 'Withdrawal') { - $showPositive = false; + if ($journal->transactionType->type == 'Transfer' && $coloured) { + return '' . $this->formatWithSymbol($symbol, $amount, 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); diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 35eb8855b9..750a0edd84 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -51,29 +51,7 @@ class ExpandedForm if (isset($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 isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name)); + return trans('form.'.$name); } diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 470089f369..b9dd7a652a 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -28,21 +28,11 @@ class Navigation $add = ($skip + 1); $functionMap = [ - '1D' => 'addDays', - 'daily' => 'addDays', - '1W' => 'addWeeks', - 'weekly' => 'addWeeks', - 'week' => 'addWeeks', - '1M' => 'addMonths', - 'month' => 'addMonths', - 'monthly' => 'addMonths', - '3M' => 'addMonths', - 'quarter' => 'addMonths', - 'quarterly' => 'addMonths', - '6M' => 'addMonths', - 'half-year' => 'addMonths', - 'year' => 'addYears', - 'yearly' => 'addYears', + '1D' => 'addDays', 'daily' => 'addDays', + '1W' => 'addWeeks', 'weekly' => 'addWeeks', 'week' => 'addWeeks', + '1M' => 'addMonths', 'month' => 'addMonths', 'monthly' => 'addMonths', '3M' => 'addMonths', + 'quarter' => 'addMonths', 'quarterly' => 'addMonths', '6M' => 'addMonths', 'half-year' => 'addMonths', + 'year' => 'addYears', 'yearly' => 'addYears', ]; $modifierMap = [ 'quarter' => 3, @@ -75,21 +65,11 @@ class Navigation $currentEnd = clone $theCurrentEnd; $functionMap = [ - '1D' => 'addDay', - 'daily' => 'addDay', - '1W' => 'addWeek', - 'week' => 'addWeek', - 'weekly' => 'addWeek', - '1M' => 'addMonth', - 'month' => 'addMonth', - 'monthly' => 'addMonth', - '3M' => 'addMonths', - 'quarter' => 'addMonths', - 'quarterly' => 'addMonths', - '6M' => 'addMonths', - 'half-year' => 'addMonths', - 'year' => 'addYear', - 'yearly' => 'addYear', + '1D' => 'addDay', 'daily' => 'addDay', + '1W' => 'addWeek', 'week' => 'addWeek', 'weekly' => 'addWeek', + '1M' => 'addMonth', 'month' => 'addMonth', 'monthly' => 'addMonth', + '3M' => 'addMonths', 'quarter' => 'addMonths', 'quarterly' => 'addMonths', '6M' => 'addMonths', 'half-year' => 'addMonths', + 'year' => 'addYear', 'yearly' => 'addYear', ]; $modifierMap = [ 'quarter' => 3, diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 22fa07e211..512e7bbe93 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -23,12 +23,9 @@ class Steam * * @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: - // $firstDateObject = $account ->transactions() ->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); $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( $account->transactions()->leftJoin( 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index 1d36857366..c1bcf09e16 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -3,10 +3,12 @@ namespace FireflyIII\Support\Twig; use App; +use Carbon\Carbon; use Config; use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; use Route; +use Session; use Twig_Extension; use Twig_SimpleFilter; use Twig_SimpleFunction; @@ -21,6 +23,7 @@ class General extends Twig_Extension /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @return array */ public function getFilters() @@ -56,8 +59,9 @@ class General extends Twig_Extension if (is_null($account)) { return 'NULL'; } + $date = Session::get('end', Carbon::now()->endOfMonth()); - return App::make('steam')->balance($account); + return App::make('steam')->balance($account, $date); } ); diff --git a/app/Support/Twig/Journal.php b/app/Support/Twig/Journal.php index bb63d656c4..299cdaf6d6 100644 --- a/app/Support/Twig/Journal.php +++ b/app/Support/Twig/Journal.php @@ -18,6 +18,7 @@ class Journal extends Twig_Extension { /** + * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @return array */ public function getFilters() @@ -27,20 +28,25 @@ class Journal extends Twig_Extension $filters[] = new Twig_SimpleFilter( 'typeIcon', function (TransactionJournal $journal) { $type = $journal->transactionType->type; - if ($type == 'Withdrawal') { - return ''; - } - if ($type == 'Deposit') { - return ''; - } - if ($type == 'Transfer') { - return ''; - } - if ($type == 'Opening balance') { - return ''; + + switch ($type) { + case 'Withdrawal': + return ''; + break; + case 'Deposit': + return ''; + break; + case 'Transfer': + return ''; + break; + case 'Opening balance': + return ''; + break; + default: + return ''; + break; } - return ''; }, ['is_safe' => ['html']] ); @@ -49,6 +55,8 @@ class Journal extends Twig_Extension } /** + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * * @return array */ public function getFunctions() @@ -70,13 +78,38 @@ class Journal extends Twig_Extension if ($journal->tags->count() == 0) { return App::make('amount')->formatJournal($journal); } + + foreach ($journal->tags as $tag) { 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); - return ' ' . $tag->tag . ''; + . '"> ' . $tag->tag . ''; + } + + /* + * 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 ' ' . $tag->tag . ''; + } + /* + * 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 '' . $amount . ''; + } + + + if ($tag->tagMode == 'nothing') { + // return the amount: + return App::make('amount')->formatJournal($journal); } } diff --git a/app/Support/Twig/Translation.php b/app/Support/Twig/Translation.php index 3bfd795420..3513d4ddb1 100644 --- a/app/Support/Twig/Translation.php +++ b/app/Support/Twig/Translation.php @@ -24,7 +24,7 @@ class Translation extends Twig_Extension $filters[] = new Twig_SimpleFilter( '_', function ($name) { - return trans('firefly.'.$name); + return trans('firefly.' . $name); }, ['is_safe' => ['html']] ); diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index c28c7ea5b8..9263fd13d4 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -29,10 +29,11 @@ class FireflyValidator extends Validator * @param array $rules * @param array $messages * @param array $customAttributes + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ 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); } /** diff --git a/config/firefly.php b/config/firefly.php index 4996101cde..aa8bb76867 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -21,7 +21,7 @@ return [ 'defaultAsset' => 'Default asset account', 'sharedAsset' => 'Shared asset account', 'savingAsset' => 'Savings account', - 'ccAsset' => 'Credit card', + 'ccAsset' => 'Credit card', ], 'range_to_text' => [ @@ -32,7 +32,7 @@ return [ '6M' => 'half year', 'custom' => '(custom)' ], - 'ccTypes' => [ + 'ccTypes' => [ 'monthlyFull' => 'Full payment every month' ], 'range_to_name' => [ @@ -77,9 +77,9 @@ return [ ], 'accountTypeByIdentifier' => [ - 'asset' => 'Asset account', - 'expense' => 'Expense account', - 'revenue' => 'Revenue account' + 'asset' => 'Asset account', + 'expense' => 'Expense account', + 'revenue' => 'Revenue account' ], 'shortNamesByFullName' => [ @@ -89,6 +89,22 @@ return [ 'Beneficiary account' => 'expense', 'Revenue account' => 'revenue', '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', + ], ]; diff --git a/database/.gitignore b/database/.gitignore index 9b1dffd90f..8b13789179 100644 --- a/database/.gitignore +++ b/database/.gitignore @@ -1 +1 @@ -*.sqlite + diff --git a/pu.sh b/pu.sh index 714a38b315..cccdb0f534 100755 --- a/pu.sh +++ b/pu.sh @@ -11,7 +11,7 @@ fi if [ ! -z "$1" ] then - phpunit --verbose tests/repositories/$1.php + phpunit --verbose tests/helpers/$1.php fi # restore .env file diff --git a/public/js/bills.js b/public/js/bills.js index 0bddb7f3ef..5f45f2a90b 100644 --- a/public/js/bills.js +++ b/public/js/bills.js @@ -1,7 +1,7 @@ $(document).ready(function () { if (typeof(googleComboChart) === 'function' && typeof(billID) !== 'undefined') { - googleComboChart('chart/bills/' + billID, 'bill-overview'); + googleComboChart('chart/bill/' + billID, 'bill-overview'); } } ); \ No newline at end of file diff --git a/public/js/categories.js b/public/js/categories.js index 01c72143d9..d4fb5c692f 100644 --- a/public/js/categories.js +++ b/public/js/categories.js @@ -1,8 +1,8 @@ $(function () { if (typeof categoryID !== 'undefined') { - googleColumnChart('chart/category/' + categoryID + '/overview', 'componentOverview'); - googleColumnChart('chart/category/' + categoryID + '/period', 'periodOverview'); + googleColumnChart('chart/category/' + categoryID + '/all', 'all'); + googleColumnChart('chart/category/' + categoryID + '/month', 'month'); } diff --git a/public/js/gcharts.js b/public/js/gcharts.js index e1d057551b..065c4975ad 100644 --- a/public/js/gcharts.js +++ b/public/js/gcharts.js @@ -1,5 +1,5 @@ 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) { if ($('#' + container).length === 1) { diff --git a/public/js/index.js b/public/js/index.js index 1af5c9199e..ec5579ee23 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -2,11 +2,12 @@ google.setOnLoadCallback(drawChart); function drawChart() { - googleLineChart('chart/home/account', 'accounts-chart'); - //googleColumnChart('chart/home/budgets', 'budgets-chart'); - googleStackedColumnChart('chart/home/budgets', 'budgets-chart'); - googleColumnChart('chart/home/categories', 'categories-chart'); - googlePieChart('chart/home/bills', 'bills-chart'); + googleLineChart('chart/account/frontpage', 'accounts-chart'); + googlePieChart('chart/bill/frontpage', 'bills-chart'); + googleStackedColumnChart('chart/budget/frontpage', 'budgets-chart'); + googleColumnChart('chart/category/frontpage', 'categories-chart'); + + getBoxAmounts(); } diff --git a/public/js/piggy-banks.js b/public/js/piggy-banks.js index 39c4658b24..cefb43d0f6 100644 --- a/public/js/piggy-banks.js +++ b/public/js/piggy-banks.js @@ -3,7 +3,7 @@ $(function () { $('.removeMoney').on('click', removeMoney); 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( diff --git a/public/js/reports.js b/public/js/reports.js index 0b6dcacad4..0517d4951d 100644 --- a/public/js/reports.js +++ b/public/js/reports.js @@ -1,20 +1,26 @@ if (typeof(google) != 'undefined') { google.setOnLoadCallback(drawChart); - } function drawChart() { - googleColumnChart('chart/reports/income-expenses/' + year, 'income-expenses-chart'); - googleColumnChart('chart/reports/income-expenses-sum/' + year, 'income-expenses-sum-chart') + googleColumnChart('chart/report/in-out/' + year + shared, 'income-expenses-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 () { $('.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) { @@ -32,31 +38,44 @@ function openModal(e) { 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: - // - $('#includeShared').empty().addClass('btn-info').append($('').addClass('state-icon glyphicon glyphicon-check')).append(' Include shared asset accounts').show(); - console.log('true'); - } else { - $('#includeShared').empty().removeClass('btn-info').append($('').addClass('state-icon glyphicon glyphicon-unchecked')).append(' Include shared asset accounts').show(); - console.log('false'); - } - }).fail(function () { - console.log('fail'); - }); + +function showIncomes() { + "use strict"; + if (incomeRestShow) { + // hide everything, make button say "show" + $('#showIncomes').text(showTheRest); + $('.incomesCollapsed').removeClass('in').addClass('out'); + + // toggle: + incomeRestShow = false; + } else { + // show everything, make button say "hide". + $('#showIncomes').text(hideTheRest); + $('.incomesCollapsed').removeClass('out').addClass('in'); + + // toggle: + incomeRestShow = true; + } + + return false; } -function includeSharedSet() { - // get setting from JSON. - $.getJSON('json/show-shared-reports/set').success(function (data) { - console.log('Value is now: ' + data.value); - includeSharedToggle(); - }).fail(function () { - console.log('fail'); - }); +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; } \ No newline at end of file diff --git a/resources/lang/en/breadcrumbs.php b/resources/lang/en/breadcrumbs.php new file mode 100644 index 0000000000..49962c8cf8 --- /dev/null +++ b/resources/lang/en/breadcrumbs.php @@ -0,0 +1,74 @@ + 'Home', + + 'asset_accounts' => 'Asset accounts', + 'expense_accounts' => 'Expense accounts', + 'revenue_accounts' => 'Revenue 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"', + + 'budgets' => 'Budgets', + 'newBudget' => 'Create a new budget', + 'delete_budget' => 'Delete budget ":name"', + 'edit_budget' => 'Edit budget ":name"', + + 'categories' => 'Categories', + 'newCategory' => 'Create a new categori', + 'delete_category' => 'Delete category ":name"', + 'edit_category' => 'Edit category ":name"', + + 'currencies' => 'Currencies', + 'edit_currency' => 'Edit currencies ":name"', + 'delete_currency' => 'Delete currencies ":name"', + + 'piggyBanks' => 'Piggy banks', + 'newPiggyBank' => 'Create a new piggy bank', + 'edit_piggyBank' => 'Edit piggy bank ":name"', + 'delete_piggyBank' => 'Delete piggy bank ":name"', + + 'preferences' => 'Preferences', + 'profile' => 'Profile', + 'changePassword' => 'Change your password', + + 'bills' => 'Bills', + 'newBill' => 'New bill', + 'edit_bill' => 'Edit bill ":name"', + 'delete_bill' => 'Delete bill ":name"', + + 'reminders' => 'Reminders', + 'reminder' => 'Reminder #:id', + + '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', + + 'searchResult' => 'Search for ":query"', + + 'withdrawal_list' => 'Expenses', + 'deposit_list' => 'Revenue, income and deposits', + 'transfer_list' => 'Transfers', + 'transfers_list' => 'Transfers', + + 'create_withdrawal' => 'Create new withdrawal', + 'create_deposit' => 'Create new deposit', + 'create_transfer' => 'Create new transfer', + + 'edit_journal' => 'Edit transaction ":description"', + 'delete_journal' => 'Delete transaction ":description"', + + 'tags' => 'Tags', + 'createTag' => 'Create new tag', + 'edit_tag' => 'Edit tag ":tag"', + 'delete_tag' => 'Delete tag ":tag"', + +]; \ No newline at end of file diff --git a/resources/lang/en/firefly.php b/resources/lang/en/firefly.php index 7053e27f94..f4150d4695 100644 --- a/resources/lang/en/firefly.php +++ b/resources/lang/en/firefly.php @@ -1,12 +1,161 @@ 'Welcome to Firefly!', - 'mainTitle' => 'What\'s playing?', - 'close' => 'Clone', - 'pleaseHold' => 'Please hold...', - 'mandatoryFields' => 'Mandatory fields', - 'optionalFields' => 'Optional fields', - 'options' => 'Options', - 'something' => 'Something!' +// general fields and things. +return [ + 'test' => 'You have selected English.', + 'close' => 'Close', + 'pleaseHold' => 'Please hold...', + 'mandatoryFields' => 'Mandatory fields', + 'optionalFields' => 'Optional fields', + 'options' => 'Options', + 'something' => 'Something!', + 'actions' => 'Actions', + 'edit' => 'Edit', + 'delete' => 'Delete', + 'welcomeBack' => 'What\'s playing?', + + // 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', + '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' => '', ]; diff --git a/resources/lang/en/form.php b/resources/lang/en/form.php new file mode 100644 index 0000000000..9d4e273217 --- /dev/null +++ b/resources/lang/en/form.php @@ -0,0 +1,78 @@ + 'Name', + 'active' => 'Active', + 'amount_min' => 'Minimum amount', + 'amount_max' => 'Maximum amount', + 'match' => 'Matches on', + 'repeat_freq' => 'Repeats', + 'account_from_id' => 'From account', + 'account_to_id' => 'To account', + 'account_id' => 'Asset account', + 'budget_id' => 'Budget', + 'openingBalance' => 'Opening balance', + 'tagMode' => 'Tag mode', + 'tagPosition' => 'Tag location', + 'virtualBalance' => 'Vitual 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', + 'returnHere' => 'Return here', + 'returnHereExplanation' => 'After storing, return here to create another one.', + 'returnHereUpdateExplanation' => 'After updating, return here.', + 'description' => 'Description', + 'expense_account' => 'Expense account', + 'revenue_account' => 'Revenue account', + 'amount' => 'Amount', + 'date' => 'Date', + 'category' => 'Category', + 'tags' => 'Tags', + 'deletePermanently' => 'Delete permanently', + 'cancel' => 'Cancel', + 'targetdate' => 'Target date', + 'remind_me' => 'Remind me', + 'tag' => 'Tag', + 'reminder' => 'Remind me every', + 'under' => 'Under', + + 'store_new_withdrawal' => 'Store new withdrawal', + 'store_new_deposit' => 'Store new deposit', + 'store_new_transfer' => 'Store new transfer', + 'add_new_withdrawal' => 'Add a new withdrawal', + 'add_new_deposit' => 'Add a new deposit', + 'add_new_transfer' => 'Add a new transfer', + 'noPiggybank' => '(no piggy bank)', + 'noBudget' => '(no budget)', + + 'delete_account' => 'Delete account ":name"', + 'delete_bill' => 'Delete bill ":name"', + 'delete_budget' => 'Delete budget ":name"', + 'delete_category' => 'Delete category ":name"', + 'delete_currency' => 'Delete currency ":name"', + 'delete_piggyBank' => 'Delete piggy banl ":name"', + 'delete_journal' => 'Delete transaction with description ":description"', + + 'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?', + 'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?', + 'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?', + 'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?', + 'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?', + 'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?', + 'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?', + + 'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.', + 'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.' . + '|All :count transactions connected to this account will be deleted as well.', + 'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.' . + '|All :count piggy bank connected to this account will be deleted as well.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.' . + '|All :count transactions connected to this bill will spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.' . + '|All :count transactions connected to this budget will spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.' . + '|All :count transactions connected to this category will spared deletion.', +]; \ No newline at end of file diff --git a/resources/lang/en/list.php b/resources/lang/en/list.php new file mode 100644 index 0000000000..22a32b39cb --- /dev/null +++ b/resources/lang/en/list.php @@ -0,0 +1,32 @@ + 'Name', + 'role' => 'Role', + 'currentBalance' => 'Current balance', + 'active' => 'Is active?', + 'lastActivity' => 'Last activity', + 'balanceDiff' => 'Balance difference between :start and :end', + 'matchedOn' => 'Matched on', + 'matchesOn' => 'Matched on', + 'matchingAmount' => 'Amount', + 'lastMatch' => 'Last match', + 'expectedMatch' => 'Expected match', + 'automatch' => 'Automatch?', + 'repeat_freq' => 'Repeats', + 'description' => 'Description', + 'amount' => 'Amount', + 'date' => 'Date', + 'from' => 'From', + 'to' => 'To', + 'budget' => 'Budget', + 'category' => 'Category', + 'bill' => 'Bill', + 'withdrawal' => 'Withdrawal', + 'deposit' => 'Deposit', + 'transfer' => 'Transfer', + 'type' => 'Type', + 'completed' => 'Completed', +]; \ No newline at end of file diff --git a/resources/lang/en/pagination.php b/resources/lang/en/pagination.php index 13b4dcb3c0..fcab34b253 100644 --- a/resources/lang/en/pagination.php +++ b/resources/lang/en/pagination.php @@ -2,18 +2,18 @@ return [ - /* - |-------------------------------------------------------------------------- - | Pagination Language Lines - |-------------------------------------------------------------------------- - | - | The following language lines are used by the paginator library to build - | the simple pagination links. You are free to change them to anything - | you want to customize your views to better match your application. - | - */ + /* + |-------------------------------------------------------------------------- + | Pagination Language Lines + |-------------------------------------------------------------------------- + | + | The following language lines are used by the paginator library to build + | the simple pagination links. You are free to change them to anything + | you want to customize your views to better match your application. + | + */ - 'previous' => '« Previous', - 'next' => 'Next »', + 'previous' => '« Previous', + 'next' => 'Next »', ]; diff --git a/resources/lang/en/passwords.php b/resources/lang/en/passwords.php index 1fc0e1ef17..50dfd5d15b 100644 --- a/resources/lang/en/passwords.php +++ b/resources/lang/en/passwords.php @@ -2,21 +2,21 @@ return [ - /* - |-------------------------------------------------------------------------- - | Password Reminder Language Lines - |-------------------------------------------------------------------------- - | - | The following language lines are the default lines which match reasons - | that are given by the password broker for a password update attempt - | has failed, such as for an invalid token or invalid new password. - | - */ + /* + |-------------------------------------------------------------------------- + | Password Reminder Language Lines + |-------------------------------------------------------------------------- + | + | The following language lines are the default lines which match reasons + | that are given by the password broker for a password update attempt + | has failed, such as for an invalid token or invalid new password. + | + */ - "password" => "Passwords must be at least six characters and match the confirmation.", - "user" => "We can't find a user with that e-mail address.", - "token" => "This password reset token is invalid.", - "sent" => "We have e-mailed your password reset link!", - "reset" => "Your password has been reset!", + "password" => "Passwords must be at least six characters and match the confirmation.", + "user" => "We can't find a user with that e-mail address.", + "token" => "This password reset token is invalid.", + "sent" => "We have e-mailed your password reset link!", + "reset" => "Your password has been reset!", ]; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index e0e00f3efb..6a25eebfa4 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -13,68 +13,69 @@ return [ | */ - "accepted" => "The :attribute must be accepted.", - "active_url" => "The :attribute is not a valid URL.", - "after" => "The :attribute must be a date after :date.", - "alpha" => "The :attribute may only contain letters.", - "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", - "alpha_num" => "The :attribute may only contain letters and numbers.", - "array" => "The :attribute must be an array.", - "unique_for_user" => "There already is an entry with this :attribute.", - 'piggy_bank_reminder' => 'The target date is too close to today to allow reminders.', - "before" => "The :attribute must be a date before :date.", - "between" => [ + "accepted" => "The :attribute must be accepted.", + "active_url" => "The :attribute is not a valid URL.", + "after" => "The :attribute must be a date after :date.", + "alpha" => "The :attribute may only contain letters.", + "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", + "alpha_num" => "The :attribute may only contain letters and numbers.", + "array" => "The :attribute must be an array.", + "unique_for_user" => "There already is an entry with this :attribute.", + 'piggy_bank_reminder' => 'The target date is too close to today to allow reminders.', + "before" => "The :attribute must be a date before :date.", + 'unique_object_for_user' => 'This name is already in use', + "between" => [ "numeric" => "The :attribute must be between :min and :max.", "file" => "The :attribute must be between :min and :max kilobytes.", "string" => "The :attribute must be between :min and :max characters.", "array" => "The :attribute must have between :min and :max items.", ], - "boolean" => "The :attribute field must be true or false.", - "confirmed" => "The :attribute confirmation does not match.", - "date" => "The :attribute is not a valid date.", - "date_format" => "The :attribute does not match the format :format.", - "different" => "The :attribute and :other must be different.", - "digits" => "The :attribute must be :digits digits.", - "digits_between" => "The :attribute must be between :min and :max digits.", - "email" => "The :attribute must be a valid email address.", - "filled" => "The :attribute field is required.", - "exists" => "The selected :attribute is invalid.", - "image" => "The :attribute must be an image.", - "in" => "The selected :attribute is invalid.", - "integer" => "The :attribute must be an integer.", - "ip" => "The :attribute must be a valid IP address.", - "max" => [ + "boolean" => "The :attribute field must be true or false.", + "confirmed" => "The :attribute confirmation does not match.", + "date" => "The :attribute is not a valid date.", + "date_format" => "The :attribute does not match the format :format.", + "different" => "The :attribute and :other must be different.", + "digits" => "The :attribute must be :digits digits.", + "digits_between" => "The :attribute must be between :min and :max digits.", + "email" => "The :attribute must be a valid email address.", + "filled" => "The :attribute field is required.", + "exists" => "The selected :attribute is invalid.", + "image" => "The :attribute must be an image.", + "in" => "The selected :attribute is invalid.", + "integer" => "The :attribute must be an integer.", + "ip" => "The :attribute must be a valid IP address.", + "max" => [ "numeric" => "The :attribute may not be greater than :max.", "file" => "The :attribute may not be greater than :max kilobytes.", "string" => "The :attribute may not be greater than :max characters.", "array" => "The :attribute may not have more than :max items.", ], - "mimes" => "The :attribute must be a file of type: :values.", - "min" => [ + "mimes" => "The :attribute must be a file of type: :values.", + "min" => [ "numeric" => "The :attribute must be at least :min.", "file" => "The :attribute must be at least :min kilobytes.", "string" => "The :attribute must be at least :min characters.", "array" => "The :attribute must have at least :min items.", ], - "not_in" => "The selected :attribute is invalid.", - "numeric" => "The :attribute must be a number.", - "regex" => "The :attribute format is invalid.", - "required" => "The :attribute field is required.", - "required_if" => "The :attribute field is required when :other is :value.", - "required_with" => "The :attribute field is required when :values is present.", - "required_with_all" => "The :attribute field is required when :values is present.", - "required_without" => "The :attribute field is required when :values is not present.", - "required_without_all" => "The :attribute field is required when none of :values are present.", - "same" => "The :attribute and :other must match.", - "size" => [ + "not_in" => "The selected :attribute is invalid.", + "numeric" => "The :attribute must be a number.", + "regex" => "The :attribute format is invalid.", + "required" => "The :attribute field is required.", + "required_if" => "The :attribute field is required when :other is :value.", + "required_with" => "The :attribute field is required when :values is present.", + "required_with_all" => "The :attribute field is required when :values is present.", + "required_without" => "The :attribute field is required when :values is not present.", + "required_without_all" => "The :attribute field is required when none of :values are present.", + "same" => "The :attribute and :other must match.", + "size" => [ "numeric" => "The :attribute must be :size.", "file" => "The :attribute must be :size kilobytes.", "string" => "The :attribute must be :size characters.", "array" => "The :attribute must contain :size items.", ], - "unique" => "The :attribute has already been taken.", - "url" => "The :attribute format is invalid.", - "timezone" => "The :attribute must be a valid zone.", + "unique" => "The :attribute has already been taken.", + "url" => "The :attribute format is invalid.", + "timezone" => "The :attribute must be a valid zone.", /* |-------------------------------------------------------------------------- @@ -87,7 +88,7 @@ return [ | */ - 'custom' => [ + 'custom' => [ 'attribute-name' => [ 'rule-name' => 'custom-message', ], @@ -104,6 +105,6 @@ return [ | */ - 'attributes' => [], + 'attributes' => [], ]; diff --git a/resources/lang/nl/breadcrumbs.php b/resources/lang/nl/breadcrumbs.php new file mode 100644 index 0000000000..3994dceb70 --- /dev/null +++ b/resources/lang/nl/breadcrumbs.php @@ -0,0 +1,73 @@ + 'Home', + + 'asset_accounts' => 'Betaalrekeningen', + 'expense_accounts' => 'Crediteuren', + 'revenue_accounts' => 'Debiteuren', + + 'new_asset_account' => 'Nieuwe betaalrekening', + 'new_expense_account' => 'Nieuwe crediteur', + 'new_revenue_account' => 'Nieuwe debiteur', + + 'delete_account' => 'Verwijder rekening ":name"', + 'edit_account' => 'Wijzig rekening ":name"', + + 'budgets' => 'Budgetten', + 'newBudget' => 'Maak een nieuw budget', + 'delete_budget' => 'Verwijder budget ":name"', + 'edit_budget' => 'Wijzig budget ":name"', + + 'categories' => 'Categorieën', + 'newCategory' => 'Maak een nieuw categorie', + 'delete_category' => 'Verwijder categorie ":name"', + 'edit_category' => 'Wijzig categorie ":name"', + + 'currencies' => 'Munteenheden', + 'edit_currency' => 'Wijzig munteenheid ":name"', + 'delete_currency' => 'Verwijder munteenheid ":name"', + + 'piggyBanks' => 'Spaarpotjes', + 'newPiggyBank' => 'Nieuw spaarpotje', + 'edit_piggyBank' => 'Wijzig spaarpotje ":name"', + 'delete_piggyBank' => 'Verwijder spaarportje ":name"', + + 'preferences' => 'Voorkeuren', + 'profile' => 'Profiel', + 'changePassword' => 'Verander je wachtwoord', + + 'bills' => 'Rekeningen', + 'newBill' => 'Nieuwe rekening', + 'edit_bill' => 'Wijzig rekening ":name"', + 'delete_bill' => 'Verwijder rekening ":name"', + + 'reminders' => 'Herinneringen', + 'reminder' => 'Herinnering #:id', + + 'reports' => 'Overzichten', + 'monthly_report' => 'Maandoverzicht :date', + 'monthly_report_shared' => 'Maandoverzicht :date (inclusief gedeelde rekeningen)', + 'yearly_report' => 'Jaaroverzicht :date', + 'yearly_report_shared' => 'Jaaroverzicht :date (inclusief gedeelde rekeningen)', + 'budget_report' => 'Budgetoverzicht :date', + + 'searchResult' => 'Zoeken naar ":query"', + + 'withdrawal_list' => 'Uitgaven', + 'deposit_list' => 'Inkomsten', + 'transfer_list' => 'Overschrijvingen', + 'transfers_list' => 'Overschrijvingen', + + 'create_withdrawal' => 'Sla nieuwe uitgave op', + 'create_deposit' => 'Sla nieuwe inkomsten op', + 'create_transfer' => 'Sla nieuwe overschrijving op', + + 'edit_journal' => 'Wijzig transactie ":description"', + 'delete_journal' => 'Verwijder transactie ":description"', + + 'tags' => 'Tags', + 'createTag' => 'Maak nieuwe tag', + 'edit_tag' => 'Wijzig tag ":tag"', + 'delete_tag' => 'Verwijder tag ":tag"', + +]; \ No newline at end of file diff --git a/resources/lang/nl/firefly.php b/resources/lang/nl/firefly.php new file mode 100644 index 0000000000..74dfbe3699 --- /dev/null +++ b/resources/lang/nl/firefly.php @@ -0,0 +1,169 @@ + 'Nederlands geselecteerd!', + 'close' => 'Sluiten', + 'pleaseHold' => 'Momentje...', + 'mandatoryFields' => 'Verplichte velden', + 'optionalFields' => 'Optionele velden', + 'options' => 'Opties', + 'something' => 'Iets!', + 'actions' => 'Acties', + 'edit' => 'Wijzig', + 'delete' => 'Verwijder', + 'welcomeBack' => 'Hoe staat het er voor?', + + // new user: + 'welcome' => 'Welkom bij Firefly!', + 'createNewAsset' => 'Maak om te beginnen een nieuwe betaalrekening. Dit is je start van je financiële beheer.', + 'createNewAssetButton' => 'Maak een nieuwe betaalrekening', + + // home page: + 'yourAccounts' => 'Je betaalrekeningen', + 'budgetsAndSpending' => 'Budgetten en uitgaven', + 'savings' => 'Sparen', + 'markAsSavingsToContinue' => 'Om hier wat te zien stel je je betaalrekeningen in als "spaarrekening".', + 'createPiggyToContinue' => 'Maak spaarpotjes om hier iets te zien.', + 'newWithdrawal' => 'Nieuwe uitgave', + 'newDeposit' => 'Nieuwe inkomsten', + 'newTransfer' => 'Nieuwe overschrijving', + 'moneyIn' => 'Inkomsten', + 'moneyOut' => 'Uitgaven', + 'billsToPay' => 'Openstaande rekeningen', + 'billsPaid' => 'Betaalde rekeningen', + 'viewDetails' => 'Meer info', + 'divided' => 'verdeeld', + 'toDivide' => 'te verdelen', + + // menu and titles, should be recycled as often as possible: + 'toggleNavigation' => 'Navigatie aan of uit', + 'seeAllReminders' => 'Bekijk alle herinneringen', + 'reminders' => 'Herinneringen', + 'currency' => 'Munteenheden', + 'preferences' => 'Voorkeuren', + 'logout' => 'Uitloggen', + 'searchPlaceholder' => 'Zoeken...', + 'dashboard' => 'Dashboard', + 'currencies' => 'Munteenheden', + 'accounts' => 'Rekeningen', + 'assetAccounts' => 'Betaalrekeningen', + 'expenseAccounts' => 'Crediteuren', + 'revenueAccounts' => 'Debiteuren', + 'Asset account' => 'Betaalrekening', + 'Default account' => 'Betaalrekening', + 'Expense account' => 'Crediteur', + 'Revenue account' => 'Debiteur', + 'budgets' => 'Budgetten', + 'categories' => 'Categorieën', + 'tags' => 'Tags', + 'reports' => 'Overzichten', + 'transactions' => 'Transacties', + 'expenses' => 'Uitgaven', + 'income' => 'Inkomsten', + 'transfers' => 'Overschrijvingen', + 'moneyManagement' => 'Geldbeheer', + 'piggyBanks' => 'Spaarpotjes', + 'bills' => 'Rekeningen', + 'createNew' => 'Nieuw', + 'withdrawal' => 'Uitgave', + 'deposit' => 'Inkomsten', + 'transfer' => 'Overschrijving', + 'Withdrawal' => 'Uitgave', + 'Deposit' => 'Inkomsten', + 'Transfer' => 'Overschrijving', + 'bill' => 'Rekening', + 'yes' => 'Ja', + 'no' => 'Nee', + 'amount' => 'Bedrag', + 'newBalance' => 'Nieuw saldo', + 'overview' => 'Overzicht', + 'saveOnAccount' => 'Sparen op rekening', + 'unknown' => 'Onbekend', + 'daily' => 'Dagelijks', + 'weekly' => 'Wekelijks', + 'monthly' => 'Maandelijks', + 'quarterly' => 'Elk kwartaal', + 'half-year' => 'Elk half jaar', + 'yearly' => 'Jaarlijks', + + 'reportForYear' => 'Jaaroverzicht :year', + 'reportForYearShared' => 'Jaaroverzicht :year (inclusief gedeelde rekeningen)', + 'reportForMonth' => 'Maandoverzicht van :date', + 'reportForMonthShared' => 'Maandoverzicht van :date (inclusief gedeelde rekeningen)', + 'incomeVsExpenses' => 'Inkomsten tegenover uitgaven', + 'accountBalances' => 'Rekeningsaldi', + 'balanceStartOfYear' => 'Saldo aan het begin van het jaar', + 'balanceEndOfYear' => 'Saldo aan het einde van het jaar', + 'balanceStartOfMonth' => 'Saldo aan het einde van de maand', + 'balanceEndOfMonth' => 'Saldo aan het einde van de maand', + + 'balanceStart' => 'Saldo aan het einde van de periode', + 'balanceEnd' => 'Saldo aan het einde van de periode', + + 'reportsOwnAccounts' => 'Overzichten voor je eigen betaalrekeningen', + 'reportsOwnAccountsAndShared' => 'Overzichten voor je eigen betaalrekeningen en gedeelde rekeningen', + + 'account' => 'Rekening', + + 'splitByAccount' => 'Per betaalrekening', + 'balancedByTransfersAndTags' => 'Gecorrigeerd met overschrijvingen en tags', + 'coveredWithTags' => 'Gecorrigeerd met tags', + 'leftUnbalanced' => 'Ongecorrigeerd', + 'expectedBalance' => 'Verwacht saldo', + 'outsideOfBudgets' => 'Buiten budgetten', + 'leftInBudget' => 'Over van budget', + + 'sumOfSums' => 'Alles bij elkaar', + 'notCharged' => '(Nog) niet betaald', + 'inactive' => 'Niet actief', + + 'difference' => 'Verschil', + 'in' => 'In', + 'out' => 'Uit', + 'topX' => 'top :number', + 'showTheRest' => 'Laat alles zien', + 'hideTheRest' => 'Laat alleen de top :number zien', + + // charts: + 'dayOfMonth' => 'Dag vd maand', + 'month' => 'Maand', + 'budget' => 'Budget', + 'spent' => 'Uitgegeven', + 'overspent' => 'Teveel uitgegeven', + 'left' => 'Over', + 'noCategory' => '(geen categorie)', + 'noBudget' => '(geen budget)', + 'category' => 'Categorie', + 'maxAmount' => 'Maximaal bedrag', + 'minAmount' => 'Minimaal bedrag', + 'billEntry' => 'Bedrag voor deze rekening', + 'name' => 'Naam', + 'date' => 'Datum', + 'paid' => 'Betaald', + 'unpaid' => 'Niet betaald', + 'day' => 'Dag', + 'budgeted' => 'Gebudgetteerd', + 'period' => 'Periode', + 'balance' => 'Saldo', + 'summary' => 'Samenvatting', + 'sum' => 'Som', + 'average' => 'Gemiddeld', + 'balanceFor' => 'Saldo op :name', + + 'asset_accounts' => 'Betaalrekeningen', + 'expense_accounts' => 'Crediteuren', + 'revenue_accounts' => 'Debiteuren', + + // some extra help: + 'accountExtraHelp_asset' => '', + 'accountExtraHelp_expense' => 'Een crediteur is een persoon of een bedrijf waar je geld aan moet betalen. Je staat bij ze in het krijt. Een verwarrende' . + ' term misschien, maar zo werkt het nou eenmaal. De supermarkt, je huurbaas of de bank zijn crediteuren. Jouw ' . + 'geld (krediet) gaat naar hen toe. De term komt uit de wereld van de boekhouding. De uitgaves die je hier ziet zijn ' . + 'positief, want je kijkt uit hun perspectief. Zodra jij afrekent in een winkel, komt het geld er bij hen bij (positief).', + 'accountExtraHelp_revenue' => 'Als je geld krijgt van een bedrijf of een persoon is dat een debiteur. ' . + 'Dat kan salaris zijn, of een andere betaling. ' . + ' Ze hebben een schuld (debet) aan jou. De term komt uit de wereld van de boekhouding.' . + ' De inkomsten die je hier ziet zijn negatief, want je kijkt uit hun perspectief. Zodra een debiteur geld naar jou ' . + 'overmaakt gaat het er bij hen af (negatief).', +]; diff --git a/resources/lang/nl/form.php b/resources/lang/nl/form.php new file mode 100644 index 0000000000..062b7a8db1 --- /dev/null +++ b/resources/lang/nl/form.php @@ -0,0 +1,78 @@ + 'Naam', + 'active' => 'Actief', + 'amount_min' => 'Minimumbedrag', + 'amount_max' => 'Maximumbedrag', + 'match' => 'Reageert op', + 'repeat_freq' => 'Herhaling', + 'account_from_id' => 'Van account', + 'account_to_id' => 'Naar account', + 'account_id' => 'Betaalrekening', + 'budget_id' => 'Budget', + 'openingBalance' => 'Startsaldo', + 'tagMode' => 'Tag modus', + 'tagPosition' => 'Tag locatie', + 'virtualBalance' => 'Virtuele saldo', + 'longitude_latitude' => 'Locatie', + 'targetamount' => 'Doelbedrag', + 'accountRole' => 'Rol van rekening', + 'openingBalanceDate' => 'Startsaldodatum', + 'ccType' => 'Betaalplan', + 'ccMonthlyPaymentDate' => 'Betaaldatum', + 'piggy_bank_id' => 'Spaarpotje', + 'returnHere' => 'Keer terug', + 'returnHereExplanation' => 'Terug naar deze pagina na het opslaan.', + 'returnHereUpdateExplanation' => 'Terug naar deze pagina na het wijzigen.', + 'description' => 'Omschrijving', + 'expense_account' => 'Crediteur', + 'revenue_account' => 'Debiteur', + 'amount' => 'Bedrag', + 'date' => 'Datum', + 'category' => 'Categorie', + 'tags' => 'Tags', + 'deletePermanently' => 'Verwijderen', + 'cancel' => 'Annuleren', + 'targetdate' => 'Doeldatum', + 'remind_me' => 'Help me herinneren', + 'tag' => 'Tag', + 'reminder' => 'Herinner me elke', + 'under' => 'Onder', + + 'store_new_withdrawal' => 'Nieuwe uitgave opslaan', + 'store_new_deposit' => 'Nieuwe inkomsten opslaan', + 'store_new_transfer' => 'Nieuwe overschrijving opslaan', + 'add_new_withdrawal' => 'Maak nieuwe uitgave', + 'add_new_deposit' => 'Maak nieuwe inkomsten', + 'add_new_transfer' => 'Maak nieuwe overschrijving', + 'noPiggybank' => '(geen spaarpotje)', + 'noBudget' => '(geen budget)', + + 'delete_account' => 'Verwijder rekening ":name"', + 'delete_bill' => 'Verwijder rekening ":name"', + 'delete_budget' => 'Verwijder budget ":name"', + 'delete_category' => 'Verwijder categorie ":name"', + 'delete_currency' => 'Verwijder munteenheid ":name"', + 'delete_piggyBank' => 'Verwijder spaarpotje ":name"', + 'delete_journal' => 'Verwijder transactie met omschrijving ":description"', + + 'account_areYouSure' => 'Weet je zeker dat je de rekening met naam ":name" wilt verwijderen?', + 'bill_areYouSure' => 'Weet je zeker dat je de rekening met naam ":name" wilt verwijderen?', + 'budget_areYouSure' => 'Weet je zeker dat je het budget met naam ":name" wilt verwijderen?', + 'category_areYouSure' => 'Weet je zeker dat je het category met naam ":name" wilt verwijderen?', + 'currency_areYouSure' => 'Weet je zeker dat je de munteenheid met naam ":name" wilt verwijderen?', + 'piggyBank_areYouSure' => 'Weet je zeker dat je het spaarpotje met naam ":name" wilt verwijderen?', + 'journal_areYouSure' => 'Weet je zeker dat je de transactie met naam ":description" wilt verwijderen?', + + 'permDeleteWarning' => 'Dingen verwijderen uit Firefly is permanent en kan niet ongedaan gemaakt worden.', + 'also_delete_transactions' => 'Ook de enige transactie verbonden aan deze rekening wordt verwijderd.' . + '|Ook alle :count transacties verbonden aan deze rekening worden verwijderd.', + 'also_delete_piggyBanks' => 'Ook het spaarpotje verbonden aan deze rekening wordt verwijderd.' . + '|Ook alle :count spaarpotjes verbonden aan deze rekening worden verwijderd.', + 'bill_keep_transactions' => 'De transactie verbonden aan deze rekening blijft bewaard.' . + '|De :count transacties verbonden aan deze rekening blijven bewaard.', + 'budget_keep_transactions' => 'De transactie verbonden aan dit budget blijft bewaard.' . + '|De :count transacties verbonden aan dit budget blijven bewaard.', + 'category_keep_transactions' => 'De transactie verbonden aan deze categorie blijft bewaard.' . + '|De :count transacties verbonden aan deze categorie blijven bewaard.', +]; \ No newline at end of file diff --git a/resources/lang/nl/list.php b/resources/lang/nl/list.php new file mode 100644 index 0000000000..42d4b8c538 --- /dev/null +++ b/resources/lang/nl/list.php @@ -0,0 +1,33 @@ + 'Naam', + 'role' => 'Rol', + 'currentBalance' => 'Huidig saldo', + 'active' => 'Actief?', + 'lastActivity' => 'Laatste activiteit', + 'balanceDiff' => 'Saldoverschil tussen :start en :end', + 'matchedOn' => 'Wordt herkend', + 'matchesOn' => 'Wordt herkend', + 'matchingAmount' => 'Bedrag', + 'lastMatch' => 'Laatste keer gezien', + 'expectedMatch' => 'Wordt verwacht', + 'automatch' => 'Automatisch herkennen?', + 'repeat_freq' => 'Herhaling', + 'description' => 'Omschrijving', + 'amount' => 'Bedrag', + 'date' => 'Datum', + 'from' => 'Van', + 'to' => 'Naar', + 'budget' => 'Budget', + 'category' => 'Categorie', + 'bill' => 'Rekening', + 'withdrawal' => 'Uitgave', + 'deposit' => 'Inkomsten', + 'transfer' => 'Overschrijving', + 'type' => 'Type', + 'completed' => 'Opgeslagen' + +]; \ No newline at end of file diff --git a/resources/lang/nl/pagination.php b/resources/lang/nl/pagination.php new file mode 100644 index 0000000000..9a2a9677a4 --- /dev/null +++ b/resources/lang/nl/pagination.php @@ -0,0 +1,19 @@ + '« Vorige', + 'next' => 'Volgende »', + +]; diff --git a/resources/lang/nl/passwords.php b/resources/lang/nl/passwords.php new file mode 100644 index 0000000000..82c8be3b28 --- /dev/null +++ b/resources/lang/nl/passwords.php @@ -0,0 +1,22 @@ + "Wachtwoorden moeten zes karakters lang zijn, en natuurlijk 2x hetzelfde invoeren.", + "user" => "Geen gebruiker met dat e-mailadres.", + "token" => "Ongeldig token! Sorry", + "sent" => "Je krijgt een mailtje met een linkje om je wachtwoord te herstellen!", + "reset" => "Je wachtwoord is hersteld!", + +]; diff --git a/resources/lang/nl/validation.php b/resources/lang/nl/validation.php new file mode 100644 index 0000000000..a34fdeee08 --- /dev/null +++ b/resources/lang/nl/validation.php @@ -0,0 +1,110 @@ + "The :attribute must be accepted.", + "active_url" => "The :attribute is not a valid URL.", + "after" => "The :attribute must be a date after :date.", + "alpha" => "The :attribute may only contain letters.", + "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", + "alpha_num" => "The :attribute may only contain letters and numbers.", + "array" => "The :attribute must be an array.", + "unique_for_user" => "There already is an entry with this :attribute.", + 'piggy_bank_reminder' => 'The target date is too close to today to allow reminders.', + "before" => "The :attribute must be a date before :date.", + 'unique_object_for_user' => 'Deze naam is al in gebruik', + "between" => [ + "numeric" => "The :attribute must be between :min and :max.", + "file" => "The :attribute must be between :min and :max kilobytes.", + "string" => "The :attribute must be between :min and :max characters.", + "array" => "The :attribute must have between :min and :max items.", + ], + "boolean" => "The :attribute field must be true or false.", + "confirmed" => "The :attribute confirmation does not match.", + "date" => "The :attribute is not a valid date.", + "date_format" => "The :attribute does not match the format :format.", + "different" => "The :attribute and :other must be different.", + "digits" => "The :attribute must be :digits digits.", + "digits_between" => "The :attribute must be between :min and :max digits.", + "email" => "The :attribute must be a valid email address.", + "filled" => "The :attribute field is required.", + "exists" => "The selected :attribute is invalid.", + "image" => "The :attribute must be an image.", + "in" => "The selected :attribute is invalid.", + "integer" => "The :attribute must be an integer.", + "ip" => "The :attribute must be a valid IP address.", + "max" => [ + "numeric" => "The :attribute may not be greater than :max.", + "file" => "The :attribute may not be greater than :max kilobytes.", + "string" => "The :attribute may not be greater than :max characters.", + "array" => "The :attribute may not have more than :max items.", + ], + "mimes" => "The :attribute must be a file of type: :values.", + "min" => [ + "numeric" => "The :attribute must be at least :min.", + "file" => "The :attribute must be at least :min kilobytes.", + "string" => "The :attribute must be at least :min characters.", + "array" => "The :attribute must have at least :min items.", + ], + "not_in" => "The selected :attribute is invalid.", + "numeric" => "The :attribute must be a number.", + "regex" => "The :attribute format is invalid.", + "required" => "The :attribute field is required.", + "required_if" => "The :attribute field is required when :other is :value.", + "required_with" => "The :attribute field is required when :values is present.", + "required_with_all" => "The :attribute field is required when :values is present.", + "required_without" => "The :attribute field is required when :values is not present.", + "required_without_all" => "The :attribute field is required when none of :values are present.", + "same" => "The :attribute and :other must match.", + "size" => [ + "numeric" => "The :attribute must be :size.", + "file" => "The :attribute must be :size kilobytes.", + "string" => "The :attribute must be :size characters.", + "array" => "The :attribute must contain :size items.", + ], + "unique" => "The :attribute has already been taken.", + "url" => "The :attribute format is invalid.", + "timezone" => "The :attribute must be a valid zone.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => [], + +]; diff --git a/resources/twig/accounts/delete.twig b/resources/twig/accounts/delete.twig index 4608db3fdc..2b0c987487 100644 --- a/resources/twig/accounts/delete.twig +++ b/resources/twig/accounts/delete.twig @@ -3,29 +3,30 @@ {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), account) }} {{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('accounts.destroy',account.id)}) }}
-
+
- Delete account "{{ account.name }}" + + {{ trans('form.delete_account', {'name': account.name}) }}
-

- Are you sure that you want to delete the {{account.accountType.type|lower}} "{{account.name}}"? +

+ {{ trans('form.permDeleteWarning') }}

- - {% if account.transactions|length > 0 %} -

- {{account.accountType.type|capitalize}} "{{ account.name }}" still has {{ account.transactions|length }} transaction(s) associated to it. These will be deleted as well. -

- {% endif %} - {% if account.piggyBanks|length > 0 %} -

- {{account.accountType.type|capitalize}} "{{ account.name }}" still has {{ account.piggyBanks|length }} piggy bank(s) associated to it. These will be deleted as well. -

- {% endif %}

- - Cancel + {{ trans('form.account_areYouSure', {'name': account.name}) }} +

+

+ + {{ trans('form.cancel') }} +

+

+ {% if account.transactions|length > 0 %} + {{ Lang.choice('form.also_delete_transactions', account.transactions|length, {count: account.transactions|length}) }} + {% endif %}
+ {% if account.piggyBanks|length > 0 %} + {{ Lang.choice('form.also_delete_piggyBanks', account.piggyBanks|length, {count: account.piggyBanks|length}) }} + {% endif %}

diff --git a/resources/twig/accounts/index.twig b/resources/twig/accounts/index.twig index a596dd3111..f331622602 100644 --- a/resources/twig/accounts/index.twig +++ b/resources/twig/accounts/index.twig @@ -1,6 +1,13 @@ {% extends "./layout/default.twig" %} {% block content %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} +
+
+

+ {{ ('accountExtraHelp_'~what)|_ }} +

+
+
@@ -11,7 +18,7 @@