diff --git a/.env.example b/.env.example index f8a2c2d6e0..1784ebbb83 100644 --- a/.env.example +++ b/.env.example @@ -27,7 +27,4 @@ ANALYTICS_ID= RUNCLEANUP=true SITE_OWNER=mail@example.com -SENDGRID_USERNAME= -SENDGRID_PASSWORD= - BLOCKED_DOMAINS= \ No newline at end of file diff --git a/README.md b/README.md index f96bf5bca4..9c903b7412 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,6 @@ "Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money. -_Firefly is a system you'll have install yourself on webhosting of your choosing._ - Personal financial management is pretty difficult, and everybody has their own approach to it. Some people make budgets, other people limit their cashflow by throwing away their credit cards, others try to increase their current cashflow. There are tons of ways to save and earn money. @@ -26,6 +24,11 @@ To get to know Firefly, and to see if it fits you, check out these resources: - The [full description](https://github.com/JC5/firefly-iii/wiki/full-description), which will tell you how Firefly works, and the philosophy behind it. +#### A quick technical overview + +Firefly is a system you'll have install yourself on webhosting of your choosing. It needs PHP and MySQL. The current version of Firefly III requires PHP 5.6.4 or +higher. Soon, this will be PHP 7.0.0 or higher. + #### About the name (should you care) @@ -92,7 +95,7 @@ is open source software under active development, and it is in no way guaranteed ## Translations -Firefly III is currently available in Dutch and English. Support for other languages is being worked on. I can use +Firefly III is currently available in Dutch and English. Support for other languages is being worked on. I could use your help. Checkout [Crowdin](https://crowdin.com/project/firefly-iii) for more information. ## Credits diff --git a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php index 52f3b540e7..879a1f8d84 100644 --- a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php +++ b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php @@ -49,19 +49,12 @@ class ChartJsBillChartGenerator implements BillChartGenerator */ public function single(Bill $bill, Collection $entries) { - // language: - $format = trans('config.month'); - - $data = [ + $format = trans('config.month'); + $data = [ 'count' => 3, 'labels' => [], 'datasets' => [], ]; - - // dataset: max amount - // dataset: min amount - // dataset: actual amount - $minAmount = []; $maxAmount = []; $actualAmount = []; diff --git a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php index 6af66916ff..77678b93e9 100644 --- a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php +++ b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php @@ -68,24 +68,24 @@ class ChartJsBudgetChartGenerator implements BudgetChartGenerator */ public function frontpage(Collection $entries) { - $data = [ + $data = [ 'count' => 0, 'labels' => [], 'datasets' => [], ]; - // dataset: left - // dataset: spent - // dataset: overspent $left = []; $spent = []; $overspent = []; - foreach ($entries as $entry) { - if ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0) { - $data['labels'][] = $entry[0]; - $left[] = round($entry[1], 2); - $spent[] = round($entry[2] * -1, 2); // spent is coming in negative, must be positive - $overspent[] = round($entry[3] * -1, 2); // same + $filtered = $entries->filter( + function ($entry) { + return ($entry[1] != 0 || $entry[2] != 0 || $entry[3] != 0); } + ); + foreach ($filtered as $entry) { + $data['labels'][] = $entry[0]; + $left[] = round($entry[1], 2); + $spent[] = round($entry[2] * -1, 2); // spent is coming in negative, must be positive + $overspent[] = round($entry[3] * -1, 2); // same } $data['datasets'][] = [ diff --git a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php index 8a2ed404c0..8bb0402d25 100644 --- a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php +++ b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php @@ -100,9 +100,9 @@ class ChartJsCategoryChartGenerator implements CategoryChartGenerator ], ]; foreach ($entries as $entry) { - if ($entry['sum'] != 0) { - $data['labels'][] = $entry['name']; - $data['datasets'][0]['data'][] = round(($entry['sum'] * -1), 2); + if ($entry->spent != 0) { + $data['labels'][] = $entry->name; + $data['datasets'][0]['data'][] = round(($entry->spent * -1), 2); } } diff --git a/app/Handlers/Events/ConnectJournalToPiggyBank.php b/app/Handlers/Events/ConnectJournalToPiggyBank.php index d4c9cef764..4b599a9dec 100644 --- a/app/Handlers/Events/ConnectJournalToPiggyBank.php +++ b/app/Handlers/Events/ConnectJournalToPiggyBank.php @@ -28,8 +28,6 @@ class ConnectJournalToPiggyBank /** * Handle the event when journal is saved. * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * * @param JournalCreated $event * * @return boolean diff --git a/app/Helpers/Collection/BalanceLine.php b/app/Helpers/Collection/BalanceLine.php index a5e48efae1..0800010c42 100644 --- a/app/Helpers/Collection/BalanceLine.php +++ b/app/Helpers/Collection/BalanceLine.php @@ -3,7 +3,6 @@ namespace FireflyIII\Helpers\Collection; use FireflyIII\Models\Budget as BudgetModel; -use FireflyIII\Models\LimitRepetition; use Illuminate\Support\Collection; /** @@ -26,9 +25,6 @@ class BalanceLine /** @var BudgetModel */ protected $budget; - /** @var LimitRepetition */ - protected $repetition; - protected $role = self::ROLE_DEFAULTROLE; /** @@ -48,24 +44,19 @@ class BalanceLine } /** - * @return string + * @return Collection */ - public function getTitle() + public function getBalanceEntries() { - 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 $this->balanceEntries; + } - return ''; + /** + * @param Collection $balanceEntries + */ + public function setBalanceEntries($balanceEntries) + { + $this->balanceEntries = $balanceEntries; } /** @@ -100,6 +91,27 @@ class BalanceLine $this->role = $role; } + /** + * @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 ''; + } + /** * 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 @@ -110,7 +122,7 @@ class BalanceLine */ public function leftOfRepetition() { - $start = $this->getRepetition() ? $this->getRepetition()->amount : 0; + $start = isset($this->budget->amount) ? $this->budget->amount : 0; /** @var BalanceEntry $balanceEntry */ foreach ($this->getBalanceEntries() as $balanceEntry) { $start += $balanceEntry->getSpent(); @@ -118,36 +130,4 @@ class BalanceLine 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; - } } diff --git a/app/Helpers/Collection/Expense.php b/app/Helpers/Collection/Expense.php index a703ee7a15..ce489c5118 100644 --- a/app/Helpers/Collection/Expense.php +++ b/app/Helpers/Collection/Expense.php @@ -2,6 +2,7 @@ namespace FireflyIII\Helpers\Collection; +use Crypt; use FireflyIII\Models\TransactionJournal; use Illuminate\Support\Collection; use stdClass; @@ -36,7 +37,7 @@ class Expense bcscale(2); $accountId = $entry->account_id; - $amount = strval(round($entry->amount, 2)); + $amount = strval(round($entry->journalAmount, 2)); if (bccomp('0', $amount) === -1) { $amount = bcmul($amount, '-1'); } @@ -44,7 +45,7 @@ class Expense if (!$this->expenses->has($accountId)) { $newObject = new stdClass; $newObject->amount = $amount; - $newObject->name = $entry->name; + $newObject->name = Crypt::decrypt($entry->account_name); $newObject->count = 1; $newObject->id = $accountId; $this->expenses->put($accountId, $newObject); diff --git a/app/Helpers/Collection/Income.php b/app/Helpers/Collection/Income.php index 9d1389dfcb..6a66de8fef 100644 --- a/app/Helpers/Collection/Income.php +++ b/app/Helpers/Collection/Income.php @@ -2,6 +2,7 @@ namespace FireflyIII\Helpers\Collection; +use Crypt; use FireflyIII\Models\TransactionJournal; use Illuminate\Support\Collection; use stdClass; @@ -38,15 +39,15 @@ class Income $accountId = $entry->account_id; if (!$this->incomes->has($accountId)) { $newObject = new stdClass; - $newObject->amount = strval(round($entry->amount_positive, 2)); - $newObject->name = $entry->name; + $newObject->amount = strval(round($entry->journalAmount, 2)); + $newObject->name = Crypt::decrypt($entry->account_name); $newObject->count = 1; $newObject->id = $accountId; $this->incomes->put($accountId, $newObject); } else { bcscale(2); $existing = $this->incomes->get($accountId); - $existing->amount = bcadd($existing->amount, $entry->amount_positive); + $existing->amount = bcadd($existing->amount, $entry->journalAmount); $existing->count++; $this->incomes->put($accountId, $existing); } diff --git a/app/Helpers/Csv/Specifix/RabobankDescription.php b/app/Helpers/Csv/Specifix/RabobankDescription.php index 19304f16db..426688d758 100644 --- a/app/Helpers/Csv/Specifix/RabobankDescription.php +++ b/app/Helpers/Csv/Specifix/RabobankDescription.php @@ -29,6 +29,22 @@ class RabobankDescription } + /** + * @param array $data + */ + public function setData($data) + { + $this->data = $data; + } + + /** + * @param array $row + */ + public function setRow($row) + { + $this->row = $row; + } + /** * Fixes Rabobank specific thing. */ @@ -46,21 +62,5 @@ class RabobankDescription } - /** - * @param array $data - */ - public function setData($data) - { - $this->data = $data; - } - - /** - * @param array $row - */ - public function setRow($row) - { - $this->row = $row; - } - } diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 43349c78a1..b96ef459af 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -3,6 +3,7 @@ namespace FireflyIII\Helpers\Report; use Carbon\Carbon; +use DB; use FireflyIII\Helpers\Collection\Account as AccountCollection; use FireflyIII\Helpers\Collection\Balance; use FireflyIII\Helpers\Collection\BalanceEntry; @@ -19,8 +20,9 @@ use FireflyIII\Models\Account; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget as BudgetModel; use FireflyIII\Models\LimitRepetition; +use FireflyIII\Models\Tag; +use FireflyIII\Models\TransactionJournal; use Illuminate\Support\Collection; -use Steam; /** * Class ReportHelper @@ -61,10 +63,9 @@ class ReportHelper implements ReportHelperInterface */ /** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */ $repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); - $set = $repository->getCategories(); + + $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); foreach ($set as $category) { - $spent = $repository->balanceInPeriod($category, $start, $end, $accounts); - $category->spent = $spent; $object->addCategory($category); } @@ -123,21 +124,59 @@ class ReportHelper implements ReportHelperInterface $startAmount = '0'; $endAmount = '0'; $diff = '0'; + $ids = $accounts->pluck('id')->toArray(); + + $yesterday = clone $start; + $yesterday->subDay(); + bcscale(2); + // get balances for start. + $startSet = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->whereIn('accounts.id', $ids) + ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->where('transaction_journals.date', '<=', $yesterday->format('Y-m-d')) + ->groupBy('accounts.id') + ->get(['accounts.id', DB::Raw('SUM(`transactions`.`amount`) as `balance`')]); + + // and for end: + $endSet = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->whereIn('accounts.id', $ids) + ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->groupBy('accounts.id') + ->get(['accounts.id', DB::Raw('SUM(`transactions`.`amount`) as `balance`')]); + + $accounts->each( - function (Account $account) use ($start, $end) { + function (Account $account) use ($startSet, $endSet) { /** * The balance for today always incorporates transactions * made on today. So to get todays "start" balance, we sub one * day. */ - $yesterday = clone $start; - $yesterday->subDay(); + // + $currentStart = $startSet->filter( + function (Account $entry) use ($account) { + return $account->id == $entry->id; + } + ); + if ($currentStart->first()) { + $account->startBalance = $currentStart->first()->balance; + } - /** @noinspection PhpParamsInspection */ - $account->startBalance = Steam::balance($account, $yesterday); - $account->endBalance = Steam::balance($account, $end); + $currentEnd = $endSet->filter( + function (Account $entry) use ($account) { + return $account->id == $entry->id; + } + ); + if ($currentEnd->first()) { + $account->endBalance = $currentEnd->first()->balance; + } } ); @@ -170,9 +209,10 @@ class ReportHelper implements ReportHelperInterface public function getIncomeReport($start, $end, Collection $accounts) { $object = new Income; - $set = $this->query->incomeInPeriod($start, $end, $accounts); + $set = $this->query->income($accounts, $start, $end); + foreach ($set as $entry) { - $object->addToTotal($entry->amount_positive); + $object->addToTotal($entry->journalAmount); $object->addOrCreateIncome($entry); } @@ -191,9 +231,10 @@ class ReportHelper implements ReportHelperInterface public function getExpenseReport($start, $end, Collection $accounts) { $object = new Expense; - $set = $this->query->expenseInPeriod($start, $end, $accounts); + $set = $this->query->expense($accounts, $start, $end); + foreach ($set as $entry) { - $object->addToTotal($entry->amount); // can be positive, if it's a transfer + $object->addToTotal($entry->journalAmount); // can be positive, if it's a transfer $object->addOrCreateExpense($entry); } @@ -211,18 +252,24 @@ class ReportHelper implements ReportHelperInterface { $object = new BudgetCollection; /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ - $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); - $set = $repository->getBudgets(); - + $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + $set = $repository->getBudgets(); + $allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end); + $allTotalSpent = $repository->spentAllPerDayForAccounts($accounts, $start, $end); bcscale(2); foreach ($set as $budget) { - $repetitions = $repository->getBudgetLimitRepetitions($budget, $start, $end); + $repetitions = $allRepetitions->filter( + function (LimitRepetition $rep) use ($budget) { + return $rep->budget_id == $budget->id; + } + ); + $totalSpent = isset($allTotalSpent[$budget->id]) ? $allTotalSpent[$budget->id] : []; // no repetition(s) for this budget: if ($repetitions->count() == 0) { - $spent = $repository->balanceInPeriod($budget, $start, $end, $accounts); + $spent = array_sum($totalSpent); $budgetLine = new BudgetLine; $budgetLine->setBudget($budget); $budgetLine->setOverspent($spent); @@ -237,7 +284,7 @@ class ReportHelper implements ReportHelperInterface $budgetLine = new BudgetLine; $budgetLine->setBudget($budget); $budgetLine->setRepetition($repetition); - $expenses = $repository->balanceInPeriod($budget, $start, $end, $accounts); + $expenses = $this->getSumOfRange($start, $end, $totalSpent); // 200 en -100 is 100, vergeleken met 0 === 1 // 200 en -200 is 0, vergeleken met 0 === 0 @@ -282,13 +329,18 @@ class ReportHelper implements ReportHelperInterface */ public function getBalanceReport(Carbon $start, Carbon $end, Collection $accounts) { - $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + /** @var \FireflyIII\Repositories\Budget\BudgetRepositoryInterface $repository */ + $repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface'); + + /** @var \FireflyIII\Repositories\Tag\TagRepositoryInterface $tagRepository */ $tagRepository = app('FireflyIII\Repositories\Tag\TagRepositoryInterface'); - $balance = new Balance; + + $balance = new Balance; // build a balance header: - $header = new BalanceHeader; - $budgets = $repository->getBudgets(); + $header = new BalanceHeader; + $budgets = $repository->getBudgetsAndLimitsInRange($start, $end); + $spentData = $repository->spentPerBudgetPerAccount($budgets, $accounts, $start, $end); foreach ($accounts as $account) { $header->addAccount($account); } @@ -298,19 +350,21 @@ class ReportHelper implements ReportHelperInterface $line = new BalanceLine; $line->setBudget($budget); - // get budget amount for current period: - $rep = $repository->getCurrentRepetition($budget, $start, $end); - // could be null? - $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. - + $entry = $spentData->filter( + function (TransactionJournal $model) use ($budget, $account) { + return $model->account_id == $account->id && $model->budget_id == $budget->id; + } + ); + $spent = 0; + if (!is_null($entry->first())) { + $spent = $entry->first()->spent; + } $balanceEntry->setSpent($spent); $line->addBalanceEntry($balanceEntry); } @@ -324,13 +378,30 @@ class ReportHelper implements ReportHelperInterface $empty = new BalanceLine; $tags = new BalanceLine; $diffLine = new BalanceLine; + $tagsLeft = $tagRepository->allCoveredByBalancingActs($accounts, $start, $end); $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); + $entry = $spentData->filter( + function (TransactionJournal $model) use ($budget, $account) { + return $model->account_id == $account->id && is_null($model->budget_id); + } + ); + $spent = 0; + if (!is_null($entry->first())) { + $spent = $entry->first()->spent; + } + $leftEntry = $tagsLeft->filter( + function (Tag $tag) use ($account) { + return $tag->account_id == $account->id; + } + ); + $left = 0; + if (!is_null($leftEntry->first())) { + $left = $leftEntry->first()->sum; + } bcscale(2); $diff = bcadd($spent, $left); @@ -380,6 +451,7 @@ class ReportHelper implements ReportHelperInterface /** @var \FireflyIII\Repositories\Bill\BillRepositoryInterface $repository */ $repository = app('FireflyIII\Repositories\Bill\BillRepositoryInterface'); $bills = $repository->getBillsForAccounts($accounts); + $journals = $repository->getAllJournalsInRange($bills, $start, $end); $collection = new BillCollection; /** @var Bill $bill */ @@ -392,16 +464,17 @@ class ReportHelper implements ReportHelperInterface // is hit in period? bcscale(2); - $set = $repository->getJournalsInRange($bill, $start, $end); - if ($set->count() == 0) { - $billLine->setHit(false); - } else { - $billLine->setHit(true); - $amount = '0'; - foreach ($set as $entry) { - $amount = bcadd($amount, $entry->amount); + + $entry = $journals->filter( + function (TransactionJournal $journal) use ($bill) { + return $journal->bill_id == $bill->id; } - $billLine->setAmount($amount); + ); + if (!is_null($entry->first())) { + $billLine->setAmount($entry->first()->journalAmount); + $billLine->setHit(true); + } else { + $billLine->setHit(false); } $collection->addBill($billLine); @@ -410,4 +483,32 @@ class ReportHelper implements ReportHelperInterface return $collection; } -} + + /** + * Take the array as returned by SingleCategoryRepositoryInterface::spentPerDay and SingleCategoryRepositoryInterface::earnedByDay + * and sum up everything in the array in the given range. + * + * @param Carbon $start + * @param Carbon $end + * @param array $array + * + * @return string + */ + protected function getSumOfRange(Carbon $start, Carbon $end, array $array) + { + bcscale(2); + $sum = '0'; + $currentStart = clone $start; // to not mess with the original one + $currentEnd = clone $end; // to not mess with the original one + + while ($currentStart <= $currentEnd) { + $date = $currentStart->format('Y-m-d'); + if (isset($array[$date])) { + $sum = bcadd($sum, $array[$date]); + } + $currentStart->addDay(); + } + + return $sum; + } +} \ No newline at end of file diff --git a/app/Helpers/Report/ReportQuery.php b/app/Helpers/Report/ReportQuery.php index ae484d8095..24fa89dc7d 100644 --- a/app/Helpers/Report/ReportQuery.php +++ b/app/Helpers/Report/ReportQuery.php @@ -4,12 +4,8 @@ namespace FireflyIII\Helpers\Report; use Auth; use Carbon\Carbon; -use Crypt; -use FireflyIII\Models\Account; -use FireflyIII\Models\Budget; -use FireflyIII\Models\TransactionJournal; +use DB; use FireflyIII\Models\TransactionType; -use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; @@ -20,215 +16,163 @@ use Illuminate\Support\Collection; */ class ReportQuery implements ReportQueryInterface { - /** - * Covers tags - * - * @param Account $account - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return float - */ - public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end) - { - return Auth::user()->transactionjournals() - ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->transactionTypes([TransactionType::WITHDRAWAL]) - ->where('transactions.account_id', $account->id) + /** + * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) + * grouped by month like so: "2015-01" => '123.45' + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function spentPerMonth(Collection $accounts, Carbon $start, Carbon $end) + { + $ids = $accounts->pluck('id')->toArray(); + $query = Auth::user()->transactionjournals() + ->leftJoin( + 'transactions AS t_from', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions AS t_to', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0); + } + ) + ->whereIn('t_from.account_id', $ids) + ->whereNotIn('t_to.account_id', $ids) + ->after($start) + ->before($end) + ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) + ->groupBy('dateFormatted') + ->get( + [ + DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'), + DB::Raw('SUM(`t_from`.`amount`) AS `sum`') + ] + ); + $array = []; + foreach ($query as $result) { + $array[$result->dateFormatted] = $result->sum; + } + + return $array; + + } + + /** + * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) + * grouped by month like so: "2015-01" => '123.45' + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function earnedPerMonth(Collection $accounts, Carbon $start, Carbon $end) + { + $ids = $accounts->pluck('id')->toArray(); + $query = Auth::user()->transactionjournals() + ->leftJoin( + 'transactions AS t_from', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions AS t_to', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0); + } + ) + ->whereIn('t_to.account_id', $ids) + ->whereNotIn('t_from.account_id', $ids) + ->after($start) + ->before($end) + ->transactionTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) + ->groupBy('dateFormatted') + ->get( + [ + DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") AS `dateFormatted`'), + DB::Raw('SUM(`t_to`.`amount`) AS `sum`') + ] + ); + $array = []; + foreach ($query as $result) { + $array[$result->dateFormatted] = $result->sum; + } + + return $array; + } + + /** + * This method returns all the "in" transaction journals for the given account and given period. The amount + * is stored in "journalAmount". + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function income(Collection $accounts, Carbon $start, Carbon $end) + { + $ids = $accounts->pluck('id')->toArray(); + $set = Auth::user()->transactionjournals() + ->leftJoin( + 'transactions as t_from', function (JoinClause $join) { + $join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions as t_to', function (JoinClause $join) { + $join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0); + } + ) + ->leftJoin('accounts', 't_from.account_id', '=', 'accounts.id') + ->transactionTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) ->before($end) ->after($start) - ->where('budget_transaction_journal.budget_id', $budget->id) - ->get(['transaction_journals.*'])->sum('amount'); + ->whereIn('t_to.account_id', $ids) + ->whereNotIn('t_from.account_id', $ids) + ->get(['transaction_journals.*', 't_to.amount as journalAmount', 'accounts.id as account_id', 'accounts.name as account_name']); + + return $set; } /** - * @param Account $account - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentNoBudget(Account $account, Carbon $start, Carbon $end) - { - return - Auth::user()->transactionjournals() - ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->transactionTypes([TransactionType::WITHDRAWAL]) - ->where('transactions.account_id', $account->id) - ->before($end) - ->after($start) - ->whereNull('budget_transaction_journal.budget_id')->get(['transaction_journals.*'])->sum('amount'); - } - - /** - * @param Carbon $start - * @param Carbon $end - * - * @return Builder - */ - protected function queryJournalsWithTransactions(Carbon $start, Carbon $end) - { - $query = TransactionJournal:: - leftJoin( - 'transactions as t_from', function (JoinClause $join) { - $join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0); - } - ) - ->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id') - ->leftJoin( - 'account_meta as acm_from', function (JoinClause $join) { - $join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole'); - } - ) - ->leftJoin( - 'transactions as t_to', function (JoinClause $join) { - $join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0); - } - ) - ->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id') - ->leftJoin( - 'account_meta as acm_to', function (JoinClause $join) { - $join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole'); - } - ) - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); - $query->before($end)->after($start)->where('transaction_journals.user_id', Auth::user()->id); - - return $query; - } - - /** - * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results - * will simply list the transaction journals only. This should allow any follow up counting to be accurate with - * regards to tags. It will only get the incomes to the specified accounts. + * This method returns all the "out" transaction journals for the given account and given period. The amount + * is stored in "journalAmount". * + * @param Collection $accounts * @param Carbon $start * @param Carbon $end - * @param Collection $accounts * * @return Collection */ - public function incomeInPeriod(Carbon $start, Carbon $end, Collection $accounts) + public function expense(Collection $accounts, Carbon $start, Carbon $end) { - $query = $this->queryJournalsWithTransactions($start, $end); + $ids = $accounts->pluck('id')->toArray(); + $set = Auth::user()->transactionjournals() + ->leftJoin( + 'transactions as t_from', function (JoinClause $join) { + $join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions as t_to', function (JoinClause $join) { + $join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0); + } + ) + ->leftJoin('accounts', 't_to.account_id', '=', 'accounts.id') + ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]) + ->before($end) + ->after($start) + ->whereIn('t_from.account_id', $ids) + ->whereNotIn('t_to.account_id', $ids) + ->get(['transaction_journals.*', 't_from.amount as journalAmount', 'accounts.id as account_id', 'accounts.name as account_name']); - $ids = []; - /** @var Account $account */ - foreach ($accounts as $account) { - $ids[] = $account->id; - } - - // OR is a deposit - // OR any transfer TO the accounts in $accounts, not FROM any of the accounts in $accounts. - $query->where( - function (Builder $query) use ($ids) { - $query->where( - function (Builder $q) { - $q->where('transaction_types.type', TransactionType::DEPOSIT); - } - ); - $query->orWhere( - function (Builder $q) use ($ids) { - $q->where('transaction_types.type', TransactionType::TRANSFER); - $q->whereNotIn('ac_from.id', $ids); - $q->whereIn('ac_to.id', $ids); - } - ); - } - ); - - // only include selected accounts. - $query->whereIn('ac_to.id', $ids); - $query->orderBy('transaction_journals.date'); - - // get everything - $data = $query->get( - ['transaction_journals.*', - 'transaction_types.type', 'ac_from.name as name', - 't_from.amount as from_amount', - 't_to.amount as to_amount', - 'ac_from.id as account_id', 'ac_from.encrypted as account_encrypted'] - ); - - $data->each( - function (TransactionJournal $journal) { - if (intval($journal->account_encrypted) == 1) { - $journal->name = Crypt::decrypt($journal->name); - } - } - ); - - return $data; - } - - /** - * See ReportQueryInterface::incomeInPeriod - * - * This method returns all "expense" journals in a certain period, which are both transfers to a shared account - * and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does - * not group and returns different fields. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return Collection - * - */ - public function expenseInPeriod(Carbon $start, Carbon $end, Collection $accounts) - { - $ids = []; - - /** @var Account $account */ - foreach ($accounts as $account) { - $ids[] = $account->id; - } - - $query = $this->queryJournalsWithTransactions($start, $end); - - // withdrawals from any account are an expense. - // transfers away, from an account in the list, to an account not in the list, are an expense. - - $query->where( - function (Builder $query) use ($ids) { - $query->where( - function (Builder $q) { - $q->where('transaction_types.type', TransactionType::WITHDRAWAL); - } - ); - $query->orWhere( - function (Builder $q) use ($ids) { - $q->where('transaction_types.type', TransactionType::TRANSFER); - $q->whereIn('ac_from.id', $ids); - $q->whereNotIn('ac_to.id', $ids); - } - ); - } - ); - - // expense goes from the selected accounts: - $query->whereIn('ac_from.id', $ids); - - $query->orderBy('transaction_journals.date'); - $data = $query->get( // get everything - ['transaction_journals.*', 'transaction_types.type', - 't_from.amount as from_amount', - 't_to.amount as to_amount', - 'ac_to.name as name', 'ac_to.id as account_id', 'ac_to.encrypted as account_encrypted'] - ); - - $data->each( - function (TransactionJournal $journal) { - if (intval($journal->account_encrypted) == 1) { - $journal->name = Crypt::decrypt($journal->name); - } - } - ); - - return $data; + return $set; } } diff --git a/app/Helpers/Report/ReportQueryInterface.php b/app/Helpers/Report/ReportQueryInterface.php index d7fa9cf085..0656ec2115 100644 --- a/app/Helpers/Report/ReportQueryInterface.php +++ b/app/Helpers/Report/ReportQueryInterface.php @@ -3,8 +3,6 @@ namespace FireflyIII\Helpers\Report; use Carbon\Carbon; -use FireflyIII\Models\Account; -use FireflyIII\Models\Budget; use Illuminate\Support\Collection; /** @@ -16,54 +14,52 @@ interface ReportQueryInterface { /** - * See ReportQueryInterface::incomeInPeriod - * - * This method returns all "expense" journals in a certain period, which are both transfers to a shared account - * and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does - * not group and returns different fields. + * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) + * grouped by month like so: "2015-01" => '123.45' * + * @param Collection $accounts * @param Carbon $start * @param Carbon $end - * @param Collection $accounts - * - * @return Collection * + * @return array */ - public function expenseInPeriod(Carbon $start, Carbon $end, Collection $accounts); + public function earnedPerMonth(Collection $accounts, Carbon $start, Carbon $end); /** - * This method works the same way as ReportQueryInterface::incomeInPeriod does, but instead of returning results - * will simply list the transaction journals only. This should allow any follow up counting to be accurate with - * regards to tags. It will only get the incomes to the specified accounts. + * This method returns all the "out" transaction journals for the given account and given period. The amount + * is stored in "journalAmount". * + * @param Collection $accounts * @param Carbon $start * @param Carbon $end - * @param Collection $accounts * * @return Collection */ - public function incomeInPeriod(Carbon $start, Carbon $end, Collection $accounts); + public function expense(Collection $accounts, Carbon $start, Carbon $end); /** - * Covers tags as well. + * This method returns all the "in" transaction journals for the given account and given period. The amount + * is stored in "journalAmount". * - * @param Account $account - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end * - * @return float + * @return Collection */ - public function spentInBudget(Account $account, Budget $budget, Carbon $start, Carbon $end); + public function income(Collection $accounts, Carbon $start, Carbon $end); /** - * @param Account $account - * @param Carbon $start - * @param Carbon $end + * Returns an array of the amount of money spent in the given accounts (on withdrawals, opening balances and transfers) + * grouped by month like so: "2015-01" => '123.45' * - * @return string + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array */ - public function spentNoBudget(Account $account, Carbon $start, Carbon $end); + public function spentPerMonth(Collection $accounts, Carbon $start, Carbon $end); } diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index b7a6c4c006..e018002bfc 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -6,7 +6,7 @@ use Config; use ExpandedForm; use FireflyIII\Http\Requests\AccountFormRequest; use FireflyIII\Models\Account; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use Input; use Preferences; use Session; @@ -56,12 +56,12 @@ class AccountController extends Controller } /** - * @param AccountRepositoryInterface $repository - * @param Account $account + * @param ARI $repository + * @param Account $account * * @return \Illuminate\View\View */ - public function delete(AccountRepositoryInterface $repository, Account $account) + public function delete(ARI $repository, Account $account) { $typeName = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type); $subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]); @@ -77,12 +77,12 @@ class AccountController extends Controller } /** - * @param AccountRepositoryInterface $repository - * @param Account $account + * @param ARI $repository + * @param Account $account * * @return \Illuminate\Http\RedirectResponse */ - public function destroy(AccountRepositoryInterface $repository, Account $account) + public function destroy(ARI $repository, Account $account) { $type = $account->accountType->type; $typeName = Config::get('firefly.shortNamesByFullName.' . $type); @@ -98,12 +98,12 @@ class AccountController extends Controller } /** - * @param AccountRepositoryInterface $repository - * @param Account $account + * @param ARI $repository + * @param Account $account * * @return \Illuminate\View\View */ - public function edit(AccountRepositoryInterface $repository, Account $account) + public function edit(ARI $repository, Account $account) { $what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type]; @@ -143,40 +143,31 @@ class AccountController extends Controller } /** - * @param AccountRepositoryInterface $repository + * @param ARI $repository * @param $what * * @return \Illuminate\View\View */ - public function index(AccountRepositoryInterface $repository, $what) + public function index(ARI $repository, $what) { $subTitle = trans('firefly.' . $what . '_accounts'); $subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what); $types = Config::get('firefly.accountTypesByIdentifier.' . $what); $accounts = $repository->getAccounts($types); - // last activity: - /** - * HERE WE ARE - */ - $start = clone Session::get('start', Carbon::now()->startOfMonth()); - $end = clone Session::get('end', Carbon::now()->endOfMonth()); + $start = clone Session::get('start', Carbon::now()->startOfMonth()); + $end = clone Session::get('end', Carbon::now()->endOfMonth()); $start->subDay(); - // start balances: - $ids = []; - foreach ($accounts as $account) { - $ids[] = $account->id; - } - + $ids = $accounts->pluck('id')->toArray(); $startBalances = Steam::balancesById($ids, $start); $endBalances = Steam::balancesById($ids, $end); $activities = Steam::getLastActivities($ids); $accounts->each( function (Account $account) use ($activities, $startBalances, $endBalances) { - $account->lastActivityDate = isset($activities[$account->id]) ? $activities[$account->id] : null; - $account->startBalance = isset($startBalances[$account->id]) ? $startBalances[$account->id] : null; - $account->endBalance = isset($endBalances[$account->id]) ? $endBalances[$account->id] : null; + $account->lastActivityDate = $this->isInArray($activities, $account->id); + $account->startBalance = $this->isInArray($startBalances, $account->id); + $account->endBalance = $this->isInArray($endBalances, $account->id); } ); @@ -184,12 +175,12 @@ class AccountController extends Controller } /** - * @param AccountRepositoryInterface $repository - * @param Account $account + * @param ARI $repository + * @param Account $account * * @return \Illuminate\View\View */ - public function show(AccountRepositoryInterface $repository, Account $account) + public function show(ARI $repository, Account $account) { $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); $subTitleIcon = Config::get('firefly.subTitlesByIdentifier.' . $account->accountType->type); @@ -203,12 +194,12 @@ class AccountController extends Controller } /** - * @param AccountFormRequest $request - * @param AccountRepositoryInterface $repository + * @param AccountFormRequest $request + * @param ARI $repository * * @return \Illuminate\Http\RedirectResponse */ - public function store(AccountFormRequest $request, AccountRepositoryInterface $repository) + public function store(AccountFormRequest $request, ARI $repository) { $accountData = [ 'name' => $request->input('name'), @@ -242,13 +233,13 @@ class AccountController extends Controller } /** - * @param AccountFormRequest $request - * @param AccountRepositoryInterface $repository - * @param Account $account + * @param AccountFormRequest $request + * @param ARI $repository + * @param Account $account * * @return \Illuminate\Http\RedirectResponse */ - public function update(AccountFormRequest $request, AccountRepositoryInterface $repository, Account $account) + public function update(AccountFormRequest $request, ARI $repository, Account $account) { $accountData = [ @@ -283,4 +274,20 @@ class AccountController extends Controller } + + /** + * @param array $array + * @param $entryId + * + * @return null|mixed + */ + protected function isInArray(array $array, $entryId) + { + if (isset($array[$entryId])) { + return $array[$entryId]; + } + + return null; + } + } diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index ab6e06a35e..c4f5ba4f9b 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -58,15 +58,7 @@ class AuthController extends Controller */ public function postLogin(Request $request) { - $this->validate( - $request, [ - $this->loginUsername() => 'required', 'password' => 'required', - ] - ); - - // If the class is using the ThrottlesLogins trait, we can automatically throttle - // the login attempts for this application. We'll key this by the username and - // the IP address of the client making these requests into this application. + $this->validate($request, [$this->loginUsername() => 'required', 'password' => 'required',]); $throttles = $this->isUsingThrottlesLoginsTrait(); if ($throttles && $this->hasTooManyLoginAttempts($request)) { @@ -80,10 +72,7 @@ class AuthController extends Controller return $this->handleUserWasAuthenticated($request, $throttles); } - // default error message: $message = $this->getFailedLoginMessage(); - - // try to find a blocked user with this email address. /** @var User $foundUser */ $foundUser = User::where('email', $credentials['email'])->where('blocked', 1)->first(); if (!is_null($foundUser)) { @@ -95,22 +84,13 @@ class AuthController extends Controller $message = trans('firefly.' . $code . '_error', ['email' => $credentials['email']]); } - // try - - // If the login attempt was unsuccessful we will increment the number of attempts - // to login and redirect the user back to the login form. Of course, when this - // user surpasses their maximum number of attempts they will get locked out. if ($throttles) { $this->incrementLoginAttempts($request); } return redirect($this->loginPath()) ->withInput($request->only($this->loginUsername(), 'remember')) - ->withErrors( - [ - $this->loginUsername() => $message, - ] - ); + ->withErrors([$this->loginUsername() => $message,]); } diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php index 93eda589b0..72944e31f1 100644 --- a/app/Http/Controllers/Auth/PasswordController.php +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -78,6 +78,7 @@ class PasswordController extends Controller } abort(404); + return ''; } diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 6e92369b22..144cd44693 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -6,8 +6,9 @@ use Carbon\Carbon; use FireflyIII\Http\Requests\BudgetFormRequest; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use Illuminate\Support\Collection; use Input; use Navigation; use Preferences; @@ -20,7 +21,6 @@ use View; * Class BudgetController * * @package FireflyIII\Http\Controllers - * @SuppressWarnings(PHPMD.TooManyMethods) */ class BudgetController extends Controller { @@ -132,13 +132,13 @@ class BudgetController extends Controller } /** - * @param BudgetRepositoryInterface $repository + * @param BudgetRepositoryInterface $repository * - * @param AccountRepositoryInterface $accountRepository + * @param ARI $accountRepository * * @return \Illuminate\View\View */ - public function index(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository) + public function index(BudgetRepositoryInterface $repository, ARI $accountRepository) { $budgets = $repository->getActiveBudgets(); $inactive = $repository->getInactiveBudgets(); @@ -232,13 +232,26 @@ class BudgetController extends Controller $journals = $repository->getJournals($budget, $repetition); if (is_null($repetition->id)) { - $limits = $repository->getBudgetLimits($budget); + $start = $repository->firstActivity($budget); + $end = new Carbon; + $set = $budget->limitrepetitions()->orderBy('startdate', 'DESC')->get(); $subTitle = e($budget->name); } else { - $limits = [$repetition->budgetLimit]; + $start = $repetition->startdate; + $end = $repetition->enddate; + $set = new Collection([$repetition]); $subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]); } + $spentArray = $repository->spentPerDay($budget, $start, $end); + $limits = new Collection(); + + /** @var LimitRepetition $entry */ + foreach ($set as $entry) { + $entry->spent = $this->getSumOfRange($entry->startdate, $entry->enddate, $spentArray); + $limits->push($entry); + } + $journals->setPath('/budgets/show/' . $budget->id); return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle')); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 04ef878492..d564789c21 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -4,7 +4,8 @@ use Auth; use Carbon\Carbon; use FireflyIII\Http\Requests\CategoryFormRequest; use FireflyIII\Models\Category; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; +use FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface as SCRI; use FireflyIII\Support\CacheProperties; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -68,12 +69,12 @@ class CategoryController extends Controller } /** - * @param CategoryRepositoryInterface $repository - * @param Category $category + * @param SCRI $repository + * @param Category $category * * @return \Illuminate\Http\RedirectResponse */ - public function destroy(CategoryRepositoryInterface $repository, Category $category) + public function destroy(SCRI $repository, Category $category) { $name = $category->name; @@ -107,17 +108,18 @@ class CategoryController extends Controller } /** - * @param CategoryRepositoryInterface $repository + * @param CRI $repository + * @param SCRI $singleRepository * * @return \Illuminate\View\View */ - public function index(CategoryRepositoryInterface $repository) + public function index(CRI $repository, SCRI $singleRepository) { - $categories = $repository->getCategories(); + $categories = $repository->listCategories(); $categories->each( - function (Category $category) use ($repository) { - $category->lastActivity = $repository->getLatestActivity($category); + function (Category $category) use ($singleRepository) { + $category->lastActivity = $singleRepository->getLatestActivity($category); } ); @@ -125,15 +127,15 @@ class CategoryController extends Controller } /** - * @param CategoryRepositoryInterface $repository + * @param CRI $repository * * @return \Illuminate\View\View */ - public function noCategory(CategoryRepositoryInterface $repository) + public function noCategory(CRI $repository) { $start = Session::get('start', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->startOfMonth()); - $list = $repository->getWithoutCategory($start, $end); + $list = $repository->listNoCategory($start, $end); $subTitle = trans( 'firefly.without_category_between', ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] @@ -143,14 +145,14 @@ class CategoryController extends Controller } /** - * @param CategoryRepositoryInterface $repository - * @param Category $category + * @param SCRI $repository + * @param Category $category * - * @param $date + * @param $date * * @return \Illuminate\View\View */ - public function showWithDate(CategoryRepositoryInterface $repository, Category $category, $date) + public function showWithDate(SCRI $repository, Category $category, $date) { $carbon = new Carbon($date); $range = Preferences::get('viewRange', '1M')->data; @@ -170,12 +172,12 @@ class CategoryController extends Controller } /** - * @param CategoryRepositoryInterface $repository - * @param Category $category + * @param SCRI $repository + * @param Category $category * * @return \Illuminate\View\View */ - public function show(CategoryRepositoryInterface $repository, Category $category) + public function show(SCRI $repository, Category $category) { $hideCategory = true; // used in list. $page = intval(Input::get('page')); @@ -200,6 +202,12 @@ class CategoryController extends Controller $cache->addProperty($end); $cache->addProperty('category-show'); $cache->addProperty($category->id); + + // get all spent and earned data: + // get amount earned in period, grouped by day. + $spentArray = $repository->spentPerDay($category, $start, $end); + $earnedArray = $repository->earnedPerDay($category, $start, $end); + if ($cache->has()) { $entries = $cache->get(); } else { @@ -208,9 +216,9 @@ class CategoryController extends Controller $end = Navigation::startOfPeriod($end, $range); $currentEnd = Navigation::endOfPeriod($end, $range); - // here do something. - $spent = $repository->spentInPeriod($category, $end, $currentEnd); - $earned = $repository->earnedInPeriod($category, $end, $currentEnd); + // get data from spentArray: + $spent = $this->getSumOfRange($end, $currentEnd, $spentArray); + $earned = $this->getSumOfRange($end, $currentEnd, $earnedArray); $dateStr = $end->format('Y-m-d'); $dateName = Navigation::periodShow($end, $range); $entries->push([$dateStr, $dateName, $spent, $earned]); @@ -225,12 +233,12 @@ class CategoryController extends Controller } /** - * @param CategoryFormRequest $request - * @param CategoryRepositoryInterface $repository + * @param CategoryFormRequest $request + * @param SCRI $repository * * @return \Illuminate\Http\RedirectResponse */ - public function store(CategoryFormRequest $request, CategoryRepositoryInterface $repository) + public function store(CategoryFormRequest $request, SCRI $repository) { $categoryData = [ 'name' => $request->input('name'), @@ -252,13 +260,13 @@ class CategoryController extends Controller /** - * @param CategoryFormRequest $request - * @param CategoryRepositoryInterface $repository - * @param Category $category + * @param CategoryFormRequest $request + * @param SCRI $repository + * @param Category $category * * @return \Illuminate\Http\RedirectResponse */ - public function update(CategoryFormRequest $request, CategoryRepositoryInterface $repository, Category $category) + public function update(CategoryFormRequest $request, SCRI $repository, Category $category) { $categoryData = [ 'name' => $request->input('name'), diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 5048553403..dc6fe0cf13 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -5,7 +5,7 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; use Preferences; @@ -37,14 +37,14 @@ class AccountController extends Controller /** * Shows the balances for a given set of dates and accounts. * - * @param $report_type + * @param $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse */ - public function report($report_type, Carbon $start, Carbon $end, Collection $accounts) + public function report($reportType, Carbon $start, Carbon $end, Collection $accounts) { // chart properties for cache: $cache = new CacheProperties(); @@ -53,6 +53,7 @@ class AccountController extends Controller $cache->addProperty('all'); $cache->addProperty('accounts'); $cache->addProperty('default'); + $cache->addProperty($reportType); $cache->addProperty($accounts); if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore @@ -68,11 +69,11 @@ class AccountController extends Controller /** * Shows the balances for all the user's expense accounts. * - * @param AccountRepositoryInterface $repository + * @param ARI $repository * * @return \Symfony\Component\HttpFoundation\Response */ - public function expenseAccounts(AccountRepositoryInterface $repository) + public function expenseAccounts(ARI $repository) { $start = clone Session::get('start', Carbon::now()->startOfMonth()); $end = clone Session::get('end', Carbon::now()->endOfMonth()); @@ -98,11 +99,11 @@ class AccountController extends Controller /** * Shows the balances for all the user's frontpage accounts. * - * @param AccountRepositoryInterface $repository + * @param ARI $repository * * @return \Symfony\Component\HttpFoundation\Response */ - public function frontpage(AccountRepositoryInterface $repository) + public function frontpage(ARI $repository) { $frontPage = Preferences::get('frontPageAccounts', []); $start = clone Session::get('start', Carbon::now()->startOfMonth()); diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 8d854c474b..39d3c4dffa 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -6,7 +6,7 @@ use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; @@ -37,8 +37,10 @@ class BudgetController extends Controller } /** + * TODO expand with no budget chart. + * * @param BudgetRepositoryInterface $repository - * @param $report_type + * @param $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts @@ -46,11 +48,11 @@ class BudgetController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function multiYear(BudgetRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets) + public function multiYear(BudgetRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets) { // chart properties for cache: $cache = new CacheProperties(); - $cache->addProperty($report_type); + $cache->addProperty($reportType); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($accounts); @@ -61,41 +63,43 @@ class BudgetController extends Controller return Response::json($cache->get()); // @codeCoverageIgnore } - /** - * budget - * year: - * spent: x - * budgeted: x - * year - * spent: x - * budgeted: x + /* + * Get the budgeted amounts for each budgets in each year. */ + $budgetedSet = $repository->getBudgetedPerYear($budgets, $start, $end); + $budgetedArray = []; + /** @var Budget $entry */ + foreach ($budgetedSet as $entry) { + $budgetedArray[$entry->id][$entry->dateFormatted] = $entry->budgeted; + } + + $set = $repository->getBudgetsAndExpensesPerYear($budgets, $accounts, $start, $end); $entries = new Collection; // go by budget, not by year. + /** @var Budget $budget */ foreach ($budgets as $budget) { - $entry = ['name' => '', 'spent' => [], 'budgeted' => []]; - + $entry = ['name' => '', 'spent' => [], 'budgeted' => []]; + $id = $budget->id; $currentStart = clone $start; while ($currentStart < $end) { // fix the date: $currentEnd = clone $currentStart; $currentEnd->endOfYear(); - // get data: - if (is_null($budget->id)) { - $name = trans('firefly.noBudget'); - $sum = $repository->getWithoutBudgetSum($currentStart, $currentEnd); - $budgeted = 0; - } else { - $name = $budget->name; - $sum = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts); - $budgeted = $repository->getBudgetLimitRepetitions($budget, $currentStart, $currentEnd)->sum('amount'); + // save to array: + $year = $currentStart->year; + $entry['name'] = $budget->name; + $spent = 0; + $budgeted = 0; + if (isset($set[$id]['entries'][$year])) { + $spent = $set[$id]['entries'][$year] * -1; } - // save to array: - $year = $currentStart->year; - $entry['name'] = $name; - $entry['spent'][$year] = ($sum * -1); + if (isset($budgetedArray[$id][$year])) { + $budgeted = round($budgetedArray[$id][$year], 2); + } + + $entry['spent'][$year] = $spent; $entry['budgeted'][$year] = $budgeted; // jump to next year. @@ -106,30 +110,25 @@ class BudgetController extends Controller } // generate chart with data: $data = $this->generator->multiYear($entries); + $cache->store($data); return Response::json($data); } /** - * @param BudgetRepositoryInterface $repository - * @param AccountRepositoryInterface $accountRepository - * @param Budget $budget + * @param BudgetRepositoryInterface $repository + * @param Budget $budget * * @return \Symfony\Component\HttpFoundation\Response */ - public function budget(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget) + public function budget(BudgetRepositoryInterface $repository, Budget $budget) { // dates and times $first = $repository->getFirstBudgetLimitDate($budget); $range = Preferences::get('viewRange', '1M')->data; $last = Session::get('end', new Carbon); - $final = clone $last; - $final->addYears(2); - $last = Navigation::endOfX($last, $range, $final); - $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); - // chart properties for cache: $cache = new CacheProperties(); @@ -137,18 +136,29 @@ class BudgetController extends Controller $cache->addProperty($last); $cache->addProperty('budget'); if ($cache->has()) { + return Response::json($cache->get()); // @codeCoverageIgnore } + $final = clone $last; + $final->addYears(2); + $last = Navigation::endOfX($last, $range, $final); $entries = new Collection; + // get all expenses: + $set = $repository->getExpensesPerMonth($budget, $first, $last); // TODO while ($first < $last) { - $end = Navigation::addPeriod($first, $range, 0); - $end->subDay(); - $chartDate = clone $end; - $chartDate->startOfMonth(); - $spent = $repository->balanceInPeriod($budget, $first, $end, $accounts) * -1; - $entries->push([$chartDate, $spent]); + $monthFormatted = $first->format('Y-m'); + + $filtered = $set->filter( + function (Budget $obj) use ($monthFormatted) { + return $obj->dateFormatted == $monthFormatted; + } + ); + $spent = is_null($filtered->first()) ? '0' : $filtered->first()->monthlyAmount; + + $entries->push([$first, round(($spent * -1), 2)]); + $first = Navigation::addPeriod($first, $range, 0); } @@ -185,15 +195,24 @@ class BudgetController extends Controller return Response::json($cache->get()); // @codeCoverageIgnore } + $set = $repository->getExpensesPerDay($budget, $start, $end); $entries = new Collection; $amount = $repetition->amount; + // get sum (har har)! while ($start <= $end) { + $formatted = $start->format('Y-m-d'); + $filtered = $set->filter( + function (Budget $obj) use ($formatted) { + return $obj->date == $formatted; + } + ); + $sum = is_null($filtered->first()) ? '0' : $filtered->first()->dailyAmount; + /* * Sum of expenses on this day: */ - $sum = $repository->expensesOnDay($budget, $start); - $amount = bcadd($amount, $sum); + $amount = round(bcadd($amount, $sum), 2); $entries->push([clone $start, $amount]); $start->addDay(); } @@ -208,13 +227,13 @@ class BudgetController extends Controller /** * Shows a budget list with spent/left/overspent. * - * @param BudgetRepositoryInterface $repository + * @param BudgetRepositoryInterface $repository * - * @param AccountRepositoryInterface $accountRepository + * @param ARI $accountRepository * * @return \Symfony\Component\HttpFoundation\Response */ - public function frontpage(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository) + public function frontpage(BudgetRepositoryInterface $repository, ARI $accountRepository) { $start = Session::get('start', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth()); @@ -273,24 +292,23 @@ class BudgetController extends Controller } /** + * TODO expand with no budget chart. + * * @param BudgetRepositoryInterface $repository - * @param $report_type + * @param $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse */ - public function year(BudgetRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts) + public function year(BudgetRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) { - $allBudgets = $repository->getBudgets(); - $budgets = new Collection; - // chart properties for cache: $cache = new CacheProperties(); $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty($report_type); + $cache->addProperty($reportType); $cache->addProperty($accounts); $cache->addProperty('budget'); $cache->addProperty('year'); @@ -298,26 +316,30 @@ class BudgetController extends Controller return Response::json($cache->get()); // @codeCoverageIgnore } - // filter empty budgets: - foreach ($allBudgets as $budget) { - $spent = $repository->balanceInPeriod($budget, $start, $end, $accounts); - if ($spent != 0) { - $budgets->push($budget); - } - } + $budgetInformation = $repository->getBudgetsAndExpensesPerMonth($accounts, $start, $end); + $budgets = new Collection; + $entries = new Collection; - $entries = new Collection; + /** @var array $row */ + foreach ($budgetInformation as $row) { + $budgets->push($row['budget']); + } while ($start < $end) { // month is the current end of the period: $month = clone $start; $month->endOfMonth(); - $row = [clone $start]; + $row = [clone $start]; + $dateFormatted = $start->format('Y-m'); - // each budget, fill the row: - foreach ($budgets as $budget) { - $spent = $repository->balanceInPeriod($budget, $start, $month, $accounts); - $row[] = $spent * -1; + // each budget, check if there is an entry for this month: + /** @var array $row */ + foreach ($budgetInformation as $budgetRow) { + $spent = 0; // nothing spent. + if (isset($budgetRow['entries'][$dateFormatted])) { + $spent = $budgetRow['entries'][$dateFormatted] * -1; // to fit array + } + $row[] = $spent; } $entries->push($row); $start->endOfMonth()->addDay(); diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 6511dd92ac..2314d61c74 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -6,13 +6,15 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; +use FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface as SCRI; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; use Navigation; use Preferences; use Response; use Session; +use stdClass; /** * Class CategoryController @@ -38,12 +40,12 @@ class CategoryController extends Controller /** * Show an overview for a category for all time, per month/week/year. * - * @param CategoryRepositoryInterface $repository - * @param Category $category + * @param SCRI $repository + * @param Category $category * * @return \Symfony\Component\HttpFoundation\Response */ - public function all(CategoryRepositoryInterface $repository, Category $category) + public function all(SCRI $repository, Category $category) { // oldest transaction in category: $start = $repository->getFirstActivityDate($category); @@ -62,12 +64,18 @@ class CategoryController extends Controller if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore } + $spentArray = $repository->spentPerDay($category, $start, $end); + $earnedArray = $repository->earnedPerDay($category, $start, $end); + while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range); - $spent = $repository->spentInPeriod($category, $start, $currentEnd); - $earned = $repository->earnedInPeriod($category, $start, $currentEnd); - $date = Navigation::periodShow($start, $range); + + // get the sum from $spentArray and $earnedArray: + $spent = $this->getSumOfRange($start, $currentEnd, $spentArray); + $earned = $this->getSumOfRange($start, $currentEnd, $earnedArray); + + $date = Navigation::periodShow($start, $range); $entries->push([clone $start, $date, $spent, $earned]); $start = Navigation::addPeriod($start, $range, 0); } @@ -84,14 +92,15 @@ class CategoryController extends Controller } + /** * Show this month's category overview. * - * @param CategoryRepositoryInterface $repository + * @param CRI $repository * * @return \Symfony\Component\HttpFoundation\Response */ - public function frontpage(CategoryRepositoryInterface $repository) + public function frontpage(CRI $repository) { $start = Session::get('start', Carbon::now()->startOfMonth()); @@ -107,30 +116,26 @@ class CategoryController extends Controller return Response::json($cache->get()); // @codeCoverageIgnore } - $array = $repository->getCategoriesAndExpenses($start, $end); - // sort by callback: - uasort( - $array, - function ($left, $right) { - if ($left['sum'] == $right['sum']) { - return 0; - } + // get data for categories (and "no category"): + $set = $repository->spentForAccountsPerMonth(new Collection, $start, $end); + $outside = $repository->sumSpentNoCategory(new Collection, $start, $end); - return ($left['sum'] < $right['sum']) ? -1 : 1; - } - ); - $set = new Collection($array); + // this is a "fake" entry for the "no category" entry. + $entry = new stdClass(); + $entry->name = trans('firefly.no_category'); + $entry->spent = $outside; + $set->push($entry); + + $set = $set->sortBy('spent'); $data = $this->generator->frontpage($set); $cache->store($data); - return Response::json($data); } /** - * @param CategoryRepositoryInterface $repository - * @param $report_type + * @param $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts @@ -138,11 +143,14 @@ class CategoryController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function multiYear(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts, Collection $categories) + public function multiYear($reportType, Carbon $start, Carbon $end, Collection $accounts, Collection $categories) { + /** @var CRI $repository */ + $repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); + // chart properties for cache: $cache = new CacheProperties(); - $cache->addProperty($report_type); + $cache->addProperty($reportType); $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($accounts); @@ -163,7 +171,14 @@ class CategoryController extends Controller * earned: x */ $entries = new Collection; - // go by budget, not by year. + // go by category, not by year. + + // given a set of categories and accounts, it should not be difficult to get + // the exact array of data we need. + + // then get the data for "no category". + $set = $repository->listMultiYear($categories, $accounts, $start, $end); + /** @var Category $category */ foreach ($categories as $category) { $entry = ['name' => '', 'spent' => [], 'earned' => []]; @@ -171,22 +186,35 @@ class CategoryController extends Controller $currentStart = clone $start; while ($currentStart < $end) { // fix the date: + $year = $currentStart->year; $currentEnd = clone $currentStart; $currentEnd->endOfYear(); + // get data: if (is_null($category->id)) { $name = trans('firefly.noCategory'); - $spent = $repository->spentNoCategoryForAccounts($accounts, $currentStart, $currentEnd); - $earned = $repository->earnedNoCategoryForAccounts($accounts, $currentStart, $currentEnd); + $spent = $repository->sumSpentNoCategory($accounts, $currentStart, $currentEnd); + $earned = $repository->sumEarnedNoCategory($accounts, $currentStart, $currentEnd); } else { + // get from set: + $entrySpent = $set->filter( + function (Category $cat) use ($year, $category) { + return ($cat->type == 'Withdrawal' && $cat->dateFormatted == $year && $cat->id == $category->id); + } + )->first(); + $entryEarned = $set->filter( + function (Category $cat) use ($year, $category) { + return ($cat->type == 'Deposit' && $cat->dateFormatted == $year && $cat->id == $category->id); + } + )->first(); + $name = $category->name; - $spent = $repository->spentInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd); - $earned = $repository->earnedInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd); + $spent = !is_null($entrySpent) ? $entrySpent->sum : 0; + $earned = !is_null($entryEarned) ? $entryEarned->sum : 0; } // save to array: - $year = $currentStart->year; $entry['name'] = $name; $entry['spent'][$year] = ($spent * -1); $entry['earned'][$year] = $earned; @@ -201,18 +229,17 @@ class CategoryController extends Controller $data = $this->generator->multiYear($entries); $cache->store($data); - return Response::json($data); } /** - * @param CategoryRepositoryInterface $repository - * @param Category $category + * @param SCRI $repository + * @param Category $category * * @return \Symfony\Component\HttpFoundation\Response */ - public function currentPeriod(CategoryRepositoryInterface $repository, Category $category) + public function currentPeriod(SCRI $repository, Category $category) { $start = clone Session::get('start', Carbon::now()->startOfMonth()); $end = Session::get('end', Carbon::now()->endOfMonth()); @@ -223,16 +250,21 @@ class CategoryController extends Controller $cache->addProperty($end); $cache->addProperty($category->id); $cache->addProperty('category'); - $cache->addProperty('currentPeriod'); + $cache->addProperty('current-period'); if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore } $entries = new Collection; + // get amount earned in period, grouped by day. + // get amount spent in period, grouped by day. + $spentArray = $repository->spentPerDay($category, $start, $end); + $earnedArray = $repository->earnedPerDay($category, $start, $end); while ($start <= $end) { - $spent = $repository->spentOnDaySum($category, $start); - $earned = $repository->earnedOnDaySum($category, $start); + $str = $start->format('Y-m-d'); + $spent = isset($spentArray[$str]) ? $spentArray[$str] : 0; + $earned = isset($earnedArray[$str]) ? $earnedArray[$str] : 0; $date = Navigation::periodShow($start, '1D'); $entries->push([clone $start, $date, $spent, $earned]); $start->addDay(); @@ -242,19 +274,17 @@ class CategoryController extends Controller $cache->store($data); return Response::json($data); - - } /** - * @param CategoryRepositoryInterface $repository + * @param SCRI $repository * @param Category $category * * @param $date * * @return \Symfony\Component\HttpFoundation\Response */ - public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, $date) + public function specificPeriod(SCRI $repository, Category $category, $date) { $carbon = new Carbon($date); $range = Preferences::get('viewRange', '1M')->data; @@ -274,12 +304,17 @@ class CategoryController extends Controller } $entries = new Collection; + // get amount earned in period, grouped by day. + $spentArray = $repository->spentPerDay($category, $start, $end); + $earnedArray = $repository->earnedPerDay($category, $start, $end); + // get amount spent in period, grouped by day. while ($start <= $end) { - $spent = $repository->spentOnDaySum($category, $start); - $earned = $repository->earnedOnDaySum($category, $start); - $theDate = Navigation::periodShow($start, '1D'); - $entries->push([clone $start, $theDate, $spent, $earned]); + $str = $start->format('Y-m-d'); + $spent = isset($spentArray[$str]) ? $spentArray[$str] : 0; + $earned = isset($earnedArray[$str]) ? $earnedArray[$str] : 0; + $date = Navigation::periodShow($start, '1D'); + $entries->push([clone $start, $date, $spent, $earned]); $start->addDay(); } @@ -295,99 +330,62 @@ class CategoryController extends Controller * Returns a chart of what has been earned in this period in each category * grouped by month. * - * @param CategoryRepositoryInterface $repository - * @param $report_type + * @param CRI $repository + * @param $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse */ - public function earnedInPeriod(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts) + public function earnedInPeriod(CRI $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) { - $original = clone $start; - $cache = new CacheProperties; // chart properties for cache: + $cache = new CacheProperties; // chart properties for cache: $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty($report_type); + $cache->addProperty($reportType); $cache->addProperty($accounts); $cache->addProperty('category'); $cache->addProperty('earned-in-period'); if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore } - $categories = new Collection; - $sets = new Collection; - $entries = new Collection; - // run a very special query each month: - $start = clone $original; - while ($start < $end) { - $currentEnd = clone $start; - $currentStart = clone $start; - $currentStart->startOfMonth(); - $currentEnd->endOfMonth(); - // get a list of categories, and what the user has earned for that category - // (if the user has earned anything) - $set = $repository->earnedForAccounts($accounts, $currentStart, $currentEnd); - $categories = $categories->merge($set); - // save the set combined with the data that is in it: - // for example: - // december 2015, salary:1000, bonus:200 - $sets->push([$currentStart, $set]); - $start->addMonth(); - } - // filter categories into a single bunch. Useful later on. - // $categories contains all the categories the user has earned money - // in in this period. - $categories = $categories->unique('id'); - $categories = $categories->sortBy( + $set = $repository->earnedForAccountsPerMonth($accounts, $start, $end); + $categories = $set->unique('id')->sortBy( function (Category $category) { return $category->name; } ); + $entries = new Collection; - // start looping the time again, this time processing the - // data for each month. - $start = clone $original; - while ($start < $end) { - $currentEnd = clone $start; - $currentStart = clone $start; - $currentStart->startOfMonth(); - $currentEnd->endOfMonth(); - - // in $sets we have saved all the sets of data for each month - // so now we need to retrieve the corrent one. - // match is on date of course. - $currentSet = $sets->first( - function ($key, $value) use ($currentStart) { - // set for this date. - return ($value[0] == $currentStart); + while ($start < $end) { // filter the set: + $row = [clone $start]; + // get possibly relevant entries from the big $set + $currentSet = $set->filter( + function (Category $category) use ($start) { + return $category->dateFormatted == $start->format("Y-m"); } ); - // create a row used later on. - $row = [clone $currentStart]; - - // loop all categories: + // check for each category if its in the current set. /** @var Category $category */ foreach ($categories as $category) { - // if entry is not null, we've earned money in this period for this category. - $entry = $currentSet[1]->first( - function ($key, $value) use ($category) { - return $value->id == $category->id; + // if its in there, use the value. + $entry = $currentSet->filter( + function (Category $cat) use ($category) { + return ($cat->id == $category->id); } - ); - // save amount + )->first(); if (!is_null($entry)) { - $row[] = $entry->earned; + $row[] = round($entry->earned, 2); } else { $row[] = 0; } } + $entries->push($row); $start->addMonth(); } - $data = $this->generator->earnedInPeriod($categories, $entries); $cache->store($data); @@ -399,87 +397,67 @@ class CategoryController extends Controller * Returns a chart of what has been spent in this period in each category * grouped by month. * - * @param CategoryRepositoryInterface $repository - * @param $report_type + * @param CRI $repository + * @param $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse */ - public function spentInPeriod(CategoryRepositoryInterface $repository, $report_type, Carbon $start, Carbon $end, Collection $accounts) + public function spentInPeriod(CRI $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) { - $original = clone $start; - $cache = new CacheProperties; // chart properties for cache: + $cache = new CacheProperties; // chart properties for cache: $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty($report_type); + $cache->addProperty($reportType); $cache->addProperty($accounts); $cache->addProperty('category'); $cache->addProperty('spent-in-period'); if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore } - $categories = new Collection; - $sets = new Collection; - $entries = new Collection; - // run a very special query each month: - $start = clone $original; - while ($start < $end) { - $currentEnd = clone $start; - $currentStart = clone $start; - $currentStart->startOfMonth(); - $currentEnd->endOfMonth(); - $set = $repository->spentForAccounts($accounts, $currentStart, $currentEnd); - $categories = $categories->merge($set); - $sets->push([$currentStart, $set]); - $start->addMonth(); - } - $categories = $categories->unique('id'); - $categories = $categories->sortBy( + $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); + $categories = $set->unique('id')->sortBy( function (Category $category) { return $category->name; } ); + $entries = new Collection; - $start = clone $original; - while ($start < $end) { - $currentEnd = clone $start; - $currentStart = clone $start; - $currentStart->startOfMonth(); - $currentEnd->endOfMonth(); - $currentSet = $sets->first( - function ($key, $value) use ($currentStart) { - // set for this date. - return ($value[0] == $currentStart); + while ($start < $end) { // filter the set: + $row = [clone $start]; + // get possibly relevant entries from the big $set + $currentSet = $set->filter( + function (Category $category) use ($start) { + return $category->dateFormatted == $start->format("Y-m"); } ); - $row = [clone $currentStart]; - + // check for each category if its in the current set. /** @var Category $category */ foreach ($categories as $category) { - /** @var Category $entry */ - $entry = $currentSet[1]->first( - function ($key, $value) use ($category) { - return $value->id == $category->id; + // if its in there, use the value. + $entry = $currentSet->filter( + function (Category $cat) use ($category) { + return ($cat->id == $category->id); } - ); + )->first(); if (!is_null($entry)) { - $row[] = $entry->spent; + $row[] = round(($entry->spent * -1), 2); } else { $row[] = 0; } } + $entries->push($row); $start->addMonth(); } - $data = $this->generator->spentInPeriod($categories, $entries); $cache->store($data); return $data; - } + } diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 9311589093..545fe46d7a 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -36,66 +36,40 @@ class ReportController extends Controller * Summarizes all income and expenses, per month, for a given year. * * @param ReportQueryInterface $query - * @param $report_type + * @param $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse */ - public function yearInOut(ReportQueryInterface $query, $report_type, Carbon $start, Carbon $end, Collection $accounts) + public function yearInOut(ReportQueryInterface $query, $reportType, Carbon $start, Carbon $end, Collection $accounts) { // chart properties for cache: $cache = new CacheProperties; $cache->addProperty('yearInOut'); $cache->addProperty($start); + $cache->addProperty($reportType); $cache->addProperty($accounts); $cache->addProperty($end); if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore } + // spent per month, and earned per month. For a specific set of accounts + // grouped by month + $spentArray = $query->spentPerMonth($accounts, $start, $end); + $earnedArray = $query->earnedPerMonth($accounts, $start, $end); - // per year? if ($start->diffInMonths($end) > 12) { - - $entries = new Collection; - while ($start < $end) { - $startOfYear = clone $start; - $startOfYear->startOfYear(); - $endOfYear = clone $startOfYear; - $endOfYear->endOfYear(); - - // total income and total expenses: - $incomeSum = $query->incomeInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive'); - $expenseSum = $query->expenseInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive'); - - $entries->push([clone $start, $incomeSum, $expenseSum]); - $start->addYear(); - } - - $data = $this->generator->multiYearInOut($entries); - $cache->store($data); + // data = method X + $data = $this->multiYearInOut($earnedArray, $spentArray, $start, $end); } else { - // per month: - - $entries = new Collection; - while ($start < $end) { - $month = clone $start; - $month->endOfMonth(); - // total income and total expenses: - $incomeSum = $query->incomeInPeriod($start, $month, $accounts)->sum('amount_positive'); - $expenseSum = $query->expenseInPeriod($start, $month, $accounts)->sum('amount_positive'); - - - $entries->push([clone $start, $incomeSum, $expenseSum]); - $start->addMonth(); - } - - $data = $this->generator->yearInOut($entries); - $cache->store($data); + // data = method Y + $data = $this->singleYearInOut($earnedArray, $spentArray, $start, $end); } + $cache->store($data); return Response::json($data); @@ -105,14 +79,14 @@ class ReportController extends Controller * Summarizes all income and expenses for a given year. Gives a total and an average. * * @param ReportQueryInterface $query - * @param $report_type + * @param $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse */ - public function yearInOutSummarized(ReportQueryInterface $query, $report_type, Carbon $start, Carbon $end, Collection $accounts) + public function yearInOutSummarized(ReportQueryInterface $query, $reportType, Carbon $start, Carbon $end, Collection $accounts) { // chart properties for cache: @@ -120,58 +94,155 @@ class ReportController extends Controller $cache->addProperty('yearInOutSummarized'); $cache->addProperty($start); $cache->addProperty($end); + $cache->addProperty($reportType); $cache->addProperty($accounts); if ($cache->has()) { return Response::json($cache->get()); // @codeCoverageIgnore } + // spent per month, and earned per month. For a specific set of accounts + // grouped by month + $spentArray = $query->spentPerMonth($accounts, $start, $end); + $earnedArray = $query->earnedPerMonth($accounts, $start, $end); + if ($start->diffInMonths($end) > 12) { + // per year + $data = $this->multiYearInOutSummarized($earnedArray, $spentArray, $start, $end); + } else { + // per month! + $data = $this->singleYearInOutSummarized($earnedArray, $spentArray, $start, $end); + } + $cache->store($data); + return Response::json($data); + } + + /** + * @param array $earned + * @param array $spent + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + protected function singleYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end) + { $income = '0'; $expense = '0'; $count = 0; + while ($start < $end) { + $date = $start->format('Y-m'); + $currentIncome = isset($earned[$date]) ? $earned[$date] : 0; + $currentExpense = isset($spent[$date]) ? ($spent[$date] * -1) : 0; + $income = bcadd($income, $currentIncome); + $expense = bcadd($expense, $currentExpense); - bcscale(2); - - if ($start->diffInMonths($end) > 12) { - // per year - while ($start < $end) { - $startOfYear = clone $start; - $startOfYear->startOfYear(); - $endOfYear = clone $startOfYear; - $endOfYear->endOfYear(); - - // total income and total expenses: - $currentIncome = $query->incomeInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive'); - $currentExpense = $query->expenseInPeriod($startOfYear, $endOfYear, $accounts)->sum('amount_positive'); - $income = bcadd($income, $currentIncome); - $expense = bcadd($expense, $currentExpense); - - $count++; - $start->addYear(); - } - - $data = $this->generator->multiYearInOutSummarized($income, $expense, $count); - $cache->store($data); - } else { - // per month! - while ($start < $end) { - $month = clone $start; - $month->endOfMonth(); - // total income and total expenses: - $currentIncome = $query->incomeInPeriod($start, $month, $accounts)->sum('amount_positive'); - $currentExpense = $query->expenseInPeriod($start, $month, $accounts)->sum('amount_positive'); - $income = bcadd($income, $currentIncome); - $expense = bcadd($expense, $currentExpense); - - $count++; - $start->addMonth(); - } - - $data = $this->generator->yearInOutSummarized($income, $expense, $count); - $cache->store($data); + $count++; + $start->addMonth(); } + $data = $this->generator->yearInOutSummarized($income, $expense, $count); - return Response::json($data); + return $data; + } + + /** + * @param array $earned + * @param array $spent + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + protected function multiYearInOutSummarized(array $earned, array $spent, Carbon $start, Carbon $end) + { + $income = '0'; + $expense = '0'; + $count = 0; + while ($start < $end) { + + $currentIncome = $this->pluckFromArray($start->year, $earned); + $currentExpense = $this->pluckFromArray($start->year, $spent) * -1; + $income = bcadd($income, $currentIncome); + $expense = bcadd($expense, $currentExpense); + + $count++; + $start->addYear(); + } + + $data = $this->generator->multiYearInOutSummarized($income, $expense, $count); + + return $data; + } + + /** + * @param array $earned + * @param array $spent + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + protected function multiYearInOut(array $earned, array $spent, Carbon $start, Carbon $end) + { + $entries = new Collection; + while ($start < $end) { + + $incomeSum = $this->pluckFromArray($start->year, $earned); + $expenseSum = $this->pluckFromArray($start->year, $spent) * -1; + + $entries->push([clone $start, $incomeSum, $expenseSum]); + $start->addYear(); + } + + $data = $this->generator->multiYearInOut($entries); + + return $data; + } + + /** + * @param array $earned + * @param array $spent + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + protected function singleYearInOut(array $earned, array $spent, Carbon $start, Carbon $end) + { + // per month? simply use each month. + + $entries = new Collection; + while ($start < $end) { + // total income and total expenses: + $date = $start->format('Y-m'); + $incomeSum = isset($earned[$date]) ? $earned[$date] : 0; + $expenseSum = isset($spent[$date]) ? ($spent[$date] * -1) : 0; + + $entries->push([clone $start, $incomeSum, $expenseSum]); + $start->addMonth(); + } + + $data = $this->generator->yearInOut($entries); + + return $data; + } + + /** + * @param int $year + * @param array $set + * + * @return string + */ + protected function pluckFromArray($year, array $set) + { + bcscale(2); + $sum = '0'; + foreach ($set as $date => $amount) { + if (substr($date, 0, 4) == $year) { + $sum = bcadd($sum, $amount); + } + } + + return $sum; } } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 465c38c441..55d0bc1765 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -1,6 +1,7 @@ format('Y-m-d'); + if (isset($array[$date])) { + $sum = bcadd($sum, $array[$date]); + } + $currentStart->addDay(); + } + + return $sum; + } } diff --git a/app/Http/Controllers/CronController.php b/app/Http/Controllers/CronController.php deleted file mode 100644 index a10531c1ae..0000000000 --- a/app/Http/Controllers/CronController.php +++ /dev/null @@ -1,74 +0,0 @@ - 0 && strlen(env('SENDGRID_PASSWORD')) > 0) { - - $set = [ - 'blocks' => 'https://api.sendgrid.com/api/blocks.get.json', - 'bounces' => 'https://api.sendgrid.com/api/bounces.get.json', - 'invalids' => 'https://api.sendgrid.com/api/invalidemails.get.json', - - ]; - echo '
';
-            foreach ($set as $name => $URL) {
-
-
-                $parameters = [
-                    'api_user' => env('SENDGRID_USERNAME'),
-                    'api_key'  => env('SENDGRID_PASSWORD'),
-                    'date'     => 1,
-                    'days'     => 7
-                ];
-                $fullURL    = $URL . '?' . http_build_query($parameters);
-                $data       = json_decode(file_get_contents($fullURL));
-
-                /*
-                 * Loop the result, if any.
-                 */
-                if (is_array($data)) {
-                    echo 'Found ' . count($data) . ' entries in the SendGrid ' . $name . ' list.' . "\n";
-                    foreach ($data as $entry) {
-                        $address = $entry->email;
-                        $user    = User::where('email', $address)->where('blocked', 0)->first();
-                        if (!is_null($user)) {
-                            echo 'Found a user: ' . $address . ', who is now blocked.' . "\n";
-                            $user->blocked      = 1;
-                            $user->blocked_code = 'bounced';
-                            $user->password     = 'bounced';
-                            $user->save();
-                        } else {
-                            echo 'Found no user: ' . $address . ', did nothing.' . "\n";
-                        }
-                    }
-                }
-            }
-            echo 'Done!' . "\n";
-        } else {
-            echo 'Please fill in SendGrid details.';
-        }
-
-    }
-
-}
diff --git a/app/Http/Controllers/CsvController.php b/app/Http/Controllers/CsvController.php
index e3c864ce85..bde7d5b7e0 100644
--- a/app/Http/Controllers/CsvController.php
+++ b/app/Http/Controllers/CsvController.php
@@ -8,7 +8,7 @@ use FireflyIII\Exceptions\FireflyException;
 use FireflyIII\Helpers\Csv\Data;
 use FireflyIII\Helpers\Csv\Importer;
 use FireflyIII\Helpers\Csv\WizardInterface;
-use FireflyIII\Repositories\Account\AccountRepositoryInterface;
+use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
 use Illuminate\Http\Request;
 use Input;
 use Log;
@@ -146,11 +146,11 @@ class CsvController extends Controller
      *
      * STEP ONE
      *
-     * @param AccountRepositoryInterface $repository
+     * @param ARI $repository
      *
      * @return \Illuminate\View\View
      */
-    public function index(AccountRepositoryInterface $repository)
+    public function index(ARI $repository)
     {
         $subTitle = trans('firefly.csv_import');
 
diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php
index 6239791a60..a838ba8736 100644
--- a/app/Http/Controllers/HomeController.php
+++ b/app/Http/Controllers/HomeController.php
@@ -4,7 +4,7 @@ use Artisan;
 use Carbon\Carbon;
 use Config;
 use FireflyIII\Models\Tag;
-use FireflyIII\Repositories\Account\AccountRepositoryInterface;
+use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
 use Input;
 use Preferences;
 use Session;
@@ -62,11 +62,11 @@ class HomeController extends Controller
     }
 
     /**
-     * @param AccountRepositoryInterface $repository
+     * @param ARI $repository
      *
      * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
      */
-    public function index(AccountRepositoryInterface $repository)
+    public function index(ARI $repository)
     {
         $types = Config::get('firefly.accountTypesByIdentifier.asset');
         $count = $repository->countAccounts($types);
diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php
index a7729bf8e1..bc1b25539b 100644
--- a/app/Http/Controllers/JsonController.php
+++ b/app/Http/Controllers/JsonController.php
@@ -3,9 +3,9 @@
 use Amount;
 use Carbon\Carbon;
 use FireflyIII\Helpers\Report\ReportQueryInterface;
-use FireflyIII\Repositories\Account\AccountRepositoryInterface;
+use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
 use FireflyIII\Repositories\Bill\BillRepositoryInterface;
-use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
+use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
 use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
 use FireflyIII\Repositories\Tag\TagRepositoryInterface;
 use FireflyIII\Support\CacheProperties;
@@ -60,7 +60,7 @@ class JsonController extends Controller
     }
 
     /**
-     * @param BillRepositoryInterface    $repository
+     * @param BillRepositoryInterface $repository
      *
      * @return \Symfony\Component\HttpFoundation\Response
      */
@@ -111,13 +111,13 @@ class JsonController extends Controller
     }
 
     /**
-     * @param ReportQueryInterface       $reportQuery
+     * @param ReportQueryInterface $reportQuery
      *
-     * @param AccountRepositoryInterface $accountRepository
+     * @param ARI                  $accountRepository
      *
      * @return \Symfony\Component\HttpFoundation\Response
      */
-    public function boxIn(ReportQueryInterface $reportQuery, AccountRepositoryInterface $accountRepository)
+    public function boxIn(ReportQueryInterface $reportQuery, ARI $accountRepository)
     {
         $start = Session::get('start', Carbon::now()->startOfMonth());
         $end   = Session::get('end', Carbon::now()->endOfMonth());
@@ -131,7 +131,7 @@ class JsonController extends Controller
             return Response::json($cache->get()); // @codeCoverageIgnore
         }
         $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
-        $amount   = $reportQuery->incomeInPeriod($start, $end, $accounts)->sum('to_amount');
+        $amount   = $reportQuery->income($accounts, $start, $end)->sum('journalAmount');
 
         $data = ['box' => 'in', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
         $cache->store($data);
@@ -140,13 +140,13 @@ class JsonController extends Controller
     }
 
     /**
-     * @param ReportQueryInterface       $reportQuery
+     * @param ReportQueryInterface $reportQuery
      *
-     * @param AccountRepositoryInterface $accountRepository
+     * @param ARI                  $accountRepository
      *
      * @return \Symfony\Component\HttpFoundation\Response
      */
-    public function boxOut(ReportQueryInterface $reportQuery, AccountRepositoryInterface $accountRepository)
+    public function boxOut(ReportQueryInterface $reportQuery, ARI $accountRepository)
     {
         $start = Session::get('start', Carbon::now()->startOfMonth());
         $end   = Session::get('end', Carbon::now()->endOfMonth());
@@ -162,7 +162,7 @@ class JsonController extends Controller
             return Response::json($cache->get()); // @codeCoverageIgnore
         }
 
-        $amount = $reportQuery->expenseInPeriod($start, $end, $accounts)->sum('to_amount');
+        $amount = $reportQuery->expense($accounts, $start, $end)->sum('journalAmount');
 
         $data = ['box' => 'out', 'amount' => Amount::format($amount, false), 'amount_raw' => $amount];
         $cache->store($data);
@@ -173,13 +173,13 @@ class JsonController extends Controller
     /**
      * Returns a list of categories.
      *
-     * @param CategoryRepositoryInterface $repository
+     * @param CRI $repository
      *
      * @return \Illuminate\Http\JsonResponse
      */
-    public function categories(CategoryRepositoryInterface $repository)
+    public function categories(CRI $repository)
     {
-        $list   = $repository->getCategories();
+        $list   = $repository->listCategories();
         $return = [];
         foreach ($list as $entry) {
             $return[] = $entry->name;
@@ -191,11 +191,11 @@ class JsonController extends Controller
     /**
      * Returns a JSON list of all beneficiaries.
      *
-     * @param AccountRepositoryInterface $accountRepository
+     * @param ARI $accountRepository
      *
      * @return \Illuminate\Http\JsonResponse
      */
-    public function expenseAccounts(AccountRepositoryInterface $accountRepository)
+    public function expenseAccounts(ARI $accountRepository)
     {
         $list   = $accountRepository->getAccounts(['Expense account', 'Beneficiary account']);
         $return = [];
@@ -208,11 +208,11 @@ class JsonController extends Controller
     }
 
     /**
-     * @param AccountRepositoryInterface $accountRepository
+     * @param ARI $accountRepository
      *
      * @return \Illuminate\Http\JsonResponse
      */
-    public function revenueAccounts(AccountRepositoryInterface $accountRepository)
+    public function revenueAccounts(ARI $accountRepository)
     {
         $list   = $accountRepository->getAccounts(['Revenue account']);
         $return = [];
diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php
index a500010852..440c5c74d0 100644
--- a/app/Http/Controllers/NewUserController.php
+++ b/app/Http/Controllers/NewUserController.php
@@ -5,7 +5,7 @@ use Carbon\Carbon;
 use Config;
 use FireflyIII\Http\Requests\NewUserFormRequest;
 use FireflyIII\Models\AccountMeta;
-use FireflyIII\Repositories\Account\AccountRepositoryInterface;
+use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
 use Preferences;
 use Session;
 use View;
@@ -20,11 +20,11 @@ class NewUserController extends Controller
 
 
     /**
-     * @param AccountRepositoryInterface $repository
+     * @param ARI $repository
      *
      * @@return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
      */
-    public function index(AccountRepositoryInterface $repository)
+    public function index(ARI $repository)
     {
         View::share('title', 'Welcome to Firefly!');
         View::share('mainTitleIcon', 'fa-fire');
@@ -42,12 +42,12 @@ class NewUserController extends Controller
     }
 
     /**
-     * @param NewUserFormRequest         $request
-     * @param AccountRepositoryInterface $repository
+     * @param NewUserFormRequest $request
+     * @param ARI                $repository
      *
      * @return \Illuminate\Http\RedirectResponse
      */
-    public function submit(NewUserFormRequest $request, AccountRepositoryInterface $repository)
+    public function submit(NewUserFormRequest $request, ARI $repository)
     {
         // create normal asset account:
         $assetAccount = [
diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php
index c0bf03eabd..92ceb4cb88 100644
--- a/app/Http/Controllers/PiggyBankController.php
+++ b/app/Http/Controllers/PiggyBankController.php
@@ -6,7 +6,7 @@ use Config;
 use ExpandedForm;
 use FireflyIII\Http\Requests\PiggyBankFormRequest;
 use FireflyIII\Models\PiggyBank;
-use FireflyIII\Repositories\Account\AccountRepositoryInterface;
+use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
 use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
 use Illuminate\Support\Collection;
 use Input;
@@ -18,7 +18,6 @@ use View;
 
 /**
  *
- * @SuppressWarnings(PHPMD.TooManyMethods)
  *
  * Class PiggyBankController
  *
@@ -40,12 +39,12 @@ class PiggyBankController extends Controller
     /**
      * Add money to piggy bank
      *
-     * @param AccountRepositoryInterface $repository
-     * @param PiggyBank                  $piggyBank
+     * @param ARI       $repository
+     * @param PiggyBank $piggyBank
      *
      * @return $this
      */
-    public function add(AccountRepositoryInterface $repository, PiggyBank $piggyBank)
+    public function add(ARI $repository, PiggyBank $piggyBank)
     {
         bcscale(2);
         $date          = Session::get('end', Carbon::now()->endOfMonth());
@@ -58,11 +57,11 @@ class PiggyBankController extends Controller
     }
 
     /**
-     * @param AccountRepositoryInterface $repository
+     * @param ARI $repository
      *
      * @return mixed
      */
-    public function create(AccountRepositoryInterface $repository)
+    public function create(ARI $repository)
     {
 
         $periods      = Config::get('firefly.piggy_bank_periods');
@@ -116,12 +115,12 @@ class PiggyBankController extends Controller
     }
 
     /**
-     * @param AccountRepositoryInterface $repository
-     * @param PiggyBank                  $piggyBank
+     * @param ARI       $repository
+     * @param PiggyBank $piggyBank
      *
      * @return View
      */
-    public function edit(AccountRepositoryInterface $repository, PiggyBank $piggyBank)
+    public function edit(ARI $repository, PiggyBank $piggyBank)
     {
 
         $periods      = Config::get('firefly.piggy_bank_periods');
@@ -157,12 +156,12 @@ class PiggyBankController extends Controller
     }
 
     /**
-     * @param AccountRepositoryInterface   $repository
+     * @param ARI   $repository
      * @param PiggyBankRepositoryInterface $piggyRepository
      *
      * @return View
      */
-    public function index(AccountRepositoryInterface $repository, PiggyBankRepositoryInterface $piggyRepository)
+    public function index(ARI $repository, PiggyBankRepositoryInterface $piggyRepository)
     {
         /** @var Collection $piggyBanks */
         $piggyBanks = $piggyRepository->getPiggyBanks();
@@ -219,12 +218,12 @@ class PiggyBankController extends Controller
 
     /**
      * @param PiggyBankRepositoryInterface $repository
-     * @param AccountRepositoryInterface   $accounts
+     * @param ARI                          $accounts
      * @param PiggyBank                    $piggyBank
      *
      * @return \Illuminate\Http\RedirectResponse
      */
-    public function postAdd(PiggyBankRepositoryInterface $repository, AccountRepositoryInterface $accounts, PiggyBank $piggyBank)
+    public function postAdd(PiggyBankRepositoryInterface $repository, ARI $accounts, PiggyBank $piggyBank)
     {
         bcscale(2);
         $amount        = round(Input::get('amount'), 2);
diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php
index c782b418e4..9cc7d3e861 100644
--- a/app/Http/Controllers/PreferencesController.php
+++ b/app/Http/Controllers/PreferencesController.php
@@ -1,7 +1,7 @@
 getAccounts(['Default account', 'Asset account']);
         $viewRangePref     = Preferences::get('viewRange', '1M');
diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php
index ccf47d7e4a..59f4f112b4 100644
--- a/app/Http/Controllers/ReportController.php
+++ b/app/Http/Controllers/ReportController.php
@@ -3,7 +3,7 @@
 use Carbon\Carbon;
 use FireflyIII\Helpers\Report\ReportHelperInterface;
 use FireflyIII\Models\Account;
-use FireflyIII\Repositories\Account\AccountRepositoryInterface;
+use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
 use Illuminate\Support\Collection;
 use Session;
 use View;
@@ -35,12 +35,12 @@ class ReportController extends Controller
     }
 
     /**
-     * @param AccountRepositoryInterface $repository
+     * @param ARI $repository
      *
      * @return View
      * @internal param ReportHelperInterface $helper
      */
-    public function index(AccountRepositoryInterface $repository)
+    public function index(ARI $repository)
     {
         $start        = Session::get('first');
         $months       = $this->helper->listOfMonths($start);
@@ -66,21 +66,21 @@ class ReportController extends Controller
 
         return view(
             'reports.index', compact(
-            'months', 'accounts', 'start', 'accountList',
-            'startOfMonth', 'endOfMonth', 'startOfYear', 'endOfYear'
-        )
+                               'months', 'accounts', 'start', 'accountList',
+                               'startOfMonth', 'endOfMonth', 'startOfYear', 'endOfYear'
+                           )
         );
     }
 
     /**
-     * @param            $report_type
+     * @param            $reportType
      * @param Carbon     $start
      * @param Carbon     $end
      * @param Collection $accounts
      *
      * @return View
      */
-    public function defaultYear($report_type, Carbon $start, Carbon $end, Collection $accounts)
+    public function defaultYear($reportType, Carbon $start, Carbon $end, Collection $accounts)
     {
         $incomeTopLength  = 8;
         $expenseTopLength = 8;
@@ -104,7 +104,7 @@ class ReportController extends Controller
         return view(
             'reports.default.year',
             compact(
-                'start', 'accountReport', 'incomes', 'report_type', 'accountIds', 'end',
+                'start', 'accountReport', 'incomes', 'reportType', 'accountIds', 'end',
                 'expenses', 'incomeTopLength', 'expenseTopLength'
             )
         );
@@ -112,66 +112,65 @@ class ReportController extends Controller
 
 
     /**
-     * @param            $report_type
+     * @param            $reportType
      * @param Carbon     $start
      * @param Carbon     $end
      * @param Collection $accounts
      *
      * @return View
      */
-    public function defaultMonth($report_type, Carbon $start, Carbon $end, Collection $accounts)
+    public function defaultMonth($reportType, Carbon $start, Carbon $end, Collection $accounts)
     {
         $incomeTopLength  = 8;
         $expenseTopLength = 8;
 
         // get report stuff!
-        $accountReport = $this->helper->getAccountReport($start, $end, $accounts);
-        $incomes       = $this->helper->getIncomeReport($start, $end, $accounts);
-        $expenses      = $this->helper->getExpenseReport($start, $end, $accounts);
-        $budgets       = $this->helper->getBudgetReport($start, $end, $accounts);
-        $categories    = $this->helper->getCategoryReport($start, $end, $accounts);
-        $balance       = $this->helper->getBalanceReport($start, $end, $accounts);
+        $accountReport = $this->helper->getAccountReport($start, $end, $accounts); // done (+2)
+        $incomes       = $this->helper->getIncomeReport($start, $end, $accounts); // done (+3)
+        $expenses      = $this->helper->getExpenseReport($start, $end, $accounts); // done (+1)
+        $budgets       = $this->helper->getBudgetReport($start, $end, $accounts); // done (+5)
+        $categories    = $this->helper->getCategoryReport($start, $end, $accounts); // done (+1) (20)
+        $balance       = $this->helper->getBalanceReport($start, $end, $accounts); // +566
         $bills         = $this->helper->getBillReport($start, $end, $accounts);
 
         // and some id's, joined:
-        $accountIds = [];
-        /** @var Account $account */
-        foreach ($accounts as $account) {
-            $accountIds[] = $account->id;
-        }
-        $accountIds = join(',', $accountIds);
+        $accountIds = join(',', $accounts->pluck('id')->toArray());
 
         // continue!
         return view(
             'reports.default.month',
             compact(
-                'start', 'end', 'report_type',
+                'start', 'end', 'reportType',
                 'accountReport',
                 'incomes', 'incomeTopLength',
                 'expenses', 'expenseTopLength',
                 'budgets', 'balance',
                 'categories',
                 'bills',
-                'accountIds', 'report_type'
+                'accountIds', 'reportType'
             )
         );
     }
 
     /**
-     * @param $report_type
+     * @param $reportType
      * @param $start
      * @param $end
      * @param $accounts
      *
      * @return View
      */
-    public function defaultMultiYear($report_type, $start, $end, $accounts)
+    public function defaultMultiYear($reportType, $start, $end, $accounts)
     {
 
-
+        $incomeTopLength  = 8;
+        $expenseTopLength = 8;
         // list of users stuff:
-        $budgets    = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface')->getActiveBudgets();
-        $categories = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface')->getCategories();
+        $budgets       = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface')->getActiveBudgets();
+        $categories    = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface')->listCategories();
+        $accountReport = $this->helper->getAccountReport($start, $end, $accounts); // done (+2)
+        $incomes       = $this->helper->getIncomeReport($start, $end, $accounts); // done (+3)
+        $expenses      = $this->helper->getExpenseReport($start, $end, $accounts); // done (+1)
 
         // and some id's, joined:
         $accountIds = [];
@@ -182,19 +181,23 @@ class ReportController extends Controller
         $accountIds = join(',', $accountIds);
 
         return view(
-            'reports.default.multi-year', compact('budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'report_type')
+            'reports.default.multi-year',
+            compact(
+                'budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'reportType', 'accountReport', 'incomes', 'expenses',
+                'incomeTopLength', 'expenseTopLength'
+            )
         );
     }
 
     /**
-     * @param            $report_type
+     * @param            $reportType
      * @param Carbon     $start
      * @param Carbon     $end
      * @param Collection $accounts
      *
      * @return View
      */
-    public function report($report_type, Carbon $start, Carbon $end, Collection $accounts)
+    public function report($reportType, Carbon $start, Carbon $end, Collection $accounts)
     {
         // throw an error if necessary.
         if ($end < $start) {
@@ -206,7 +209,7 @@ class ReportController extends Controller
             $start = Session::get('first');
         }
 
-        switch ($report_type) {
+        switch ($reportType) {
             default:
             case 'default':
 
@@ -223,14 +226,14 @@ class ReportController extends Controller
 
                 // more than one year date difference means year report.
                 if ($start->diffInMonths($end) > 12) {
-                    return $this->defaultMultiYear($report_type, $start, $end, $accounts);
+                    return $this->defaultMultiYear($reportType, $start, $end, $accounts);
                 }
                 // more than two months date difference means year report.
                 if ($start->diffInMonths($end) > 1) {
-                    return $this->defaultYear($report_type, $start, $end, $accounts);
+                    return $this->defaultYear($reportType, $start, $end, $accounts);
                 }
 
-                return $this->defaultMonth($report_type, $start, $end, $accounts);
+                return $this->defaultMonth($reportType, $start, $end, $accounts);
         }
 
 
diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php
index 8db37205bd..54e9b5fc2b 100644
--- a/app/Http/Controllers/TransactionController.php
+++ b/app/Http/Controllers/TransactionController.php
@@ -14,7 +14,7 @@ use FireflyIII\Models\PiggyBankEvent;
 use FireflyIII\Models\Transaction;
 use FireflyIII\Models\TransactionJournal;
 use FireflyIII\Models\TransactionType;
-use FireflyIII\Repositories\Account\AccountRepositoryInterface;
+use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI;
 use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
 use Illuminate\Support\Collection;
 use Input;
@@ -43,12 +43,12 @@ class TransactionController extends Controller
     }
 
     /**
-     * @param AccountRepositoryInterface $repository
-     * @param string                     $what
+     * @param ARI    $repository
+     * @param string $what
      *
      * @return \Illuminate\View\View
      */
-    public function create(AccountRepositoryInterface $repository, $what = TransactionType::DEPOSIT)
+    public function create(ARI $repository, $what = TransactionType::DEPOSIT)
     {
         $what        = strtolower($what);
         $maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
@@ -133,12 +133,12 @@ class TransactionController extends Controller
     /**
      * Shows the view to edit a transaction.
      *
-     * @param AccountRepositoryInterface $repository
-     * @param TransactionJournal         $journal
+     * @param ARI                $repository
+     * @param TransactionJournal $journal
      *
      * @return $this
      */
-    public function edit(AccountRepositoryInterface $repository, TransactionJournal $journal)
+    public function edit(ARI $repository, TransactionJournal $journal)
     {
         // cannot edit opening balance
         if ($journal->isOpeningBalance()) {
@@ -163,11 +163,7 @@ class TransactionController extends Controller
             'piggy_bank_id' => 0
         ];
         // get tags:
-        $tags = [];
-        foreach ($journal->tags as $tag) {
-            $tags[] = $tag->tag;
-        }
-        $preFilled['tags'] = join(',', $tags);
+        $preFilled['tags'] = join(',', $journal->tags->pluck('tag')->toArray());
 
         $category = $journal->categories()->first();
         if (!is_null($category)) {
@@ -285,7 +281,7 @@ class TransactionController extends Controller
         $what     = strtolower($journal->getTransactionType());
         $subTitle = trans('firefly.' . $journal->getTransactionType()) . ' "' . e($journal->description) . '"';
 
-        return view('transactions.show', compact('journal','events', 'subTitle', 'what'));
+        return view('transactions.show', compact('journal', 'events', 'subTitle', 'what'));
     }
 
     /**
@@ -354,7 +350,6 @@ class TransactionController extends Controller
     public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, AttachmentHelperInterface $att, TransactionJournal $journal)
     {
 
-        // cannot edit opening balance
         if ($journal->isOpeningBalance()) {
             return view('error')->with('message', 'Cannot edit this transaction. Edit the account instead!');
         }
diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php
index a11d1d9e99..5cc1d665de 100644
--- a/app/Http/Middleware/Authenticate.php
+++ b/app/Http/Middleware/Authenticate.php
@@ -54,8 +54,9 @@ class Authenticate
                 return redirect()->guest('auth/login');
             }
         }
-
-        if ($this->auth->user() instanceof User && intval($this->auth->user()->blocked) == 1) {
+        /** @var User $user */
+        $user = $this->auth->user();
+        if ($user instanceof User && intval($user->blocked) == 1) {
             Auth::logout();
 
             return redirect()->route('index');
diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php
index ac2017b182..27ef9622d5 100644
--- a/app/Http/Middleware/Range.php
+++ b/app/Http/Middleware/Range.php
@@ -42,7 +42,6 @@ class Range
      *
      * @param  \Illuminate\Http\Request $request
      * @param  \Closure                 $theNext
-     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      *
      * @return mixed
      */
diff --git a/app/Http/Requests/JournalFormRequest.php b/app/Http/Requests/JournalFormRequest.php
index 141bb78f3e..b3377b4a47 100644
--- a/app/Http/Requests/JournalFormRequest.php
+++ b/app/Http/Requests/JournalFormRequest.php
@@ -51,7 +51,6 @@ class JournalFormRequest extends Request
     /**
      * @return array
      * @throws Exception
-     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
     public function rules()
     {
diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php
index e429509482..cf3de22751 100644
--- a/app/Http/breadcrumbs.php
+++ b/app/Http/breadcrumbs.php
@@ -355,9 +355,9 @@ Breadcrumbs::register(
     $breadcrumbs->parent('reports.index');
 
     $monthFormat = trans('config.month_and_day');
-    $title       = trans('firefly.report_default', ['start' => $start->formatLocalized($monthFormat), 'end' => $end->formatLocalized($monthFormat)]);
+    $title       = trans('firefly.report_' . $reportType, ['start' => $start->formatLocalized($monthFormat), 'end' => $end->formatLocalized($monthFormat)]);
 
-    $breadcrumbs->push($title, route('reports.report', ['url' => 'abcde']));
+    $breadcrumbs->push($title, route('reports.report', [$reportType, $start->format('Ymd'), $end->format('Ymd'), $accountIds]));
 }
 );
 
diff --git a/app/Http/routes.php b/app/Http/routes.php
index ac043af6f3..891e36424e 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -264,7 +264,6 @@ Route::bind(
  * Auth\AuthController
  */
 Route::get('/register', ['uses' => 'Auth\AuthController@getRegister', 'as' => 'register']);
-Route::get('/cron/sendgrid', ['uses' => 'CronController@sendgrid']);
 
 Route::controllers(
     [
@@ -389,7 +388,7 @@ Route::group(
     // accounts:
     Route::get('/chart/account/frontpage', ['uses' => 'Chart\AccountController@frontpage']);
     Route::get('/chart/account/expense', ['uses' => 'Chart\AccountController@expenseAccounts']);
-    Route::get('/chart/account/report/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\AccountController@report']);
+    Route::get('/chart/account/report/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\AccountController@report']);
     Route::get('/chart/account/{account}', ['uses' => 'Chart\AccountController@single']);
 
 
@@ -401,8 +400,8 @@ Route::group(
     Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']);
 
     // this chart is used in reports:
-    Route::get('/chart/budget/year/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@year']);
-    Route::get('/chart/budget/multi-year/{report_type}/{start_date}/{end_date}/{accountList}/{budgetList}', ['uses' => 'Chart\BudgetController@multiYear']);
+    Route::get('/chart/budget/year/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@year']);
+    Route::get('/chart/budget/multi-year/{reportType}/{start_date}/{end_date}/{accountList}/{budgetList}', ['uses' => 'Chart\BudgetController@multiYear']);
 
     Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']);
     Route::get('/chart/budget/{budget}', ['uses' => 'Chart\BudgetController@budget']);
@@ -411,10 +410,10 @@ Route::group(
     Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']);
 
     // these three charts are for reports:
-    Route::get('/chart/category/earned-in-period/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@earnedInPeriod']);
-    Route::get('/chart/category/spent-in-period/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@spentInPeriod']);
+    Route::get('/chart/category/earned-in-period/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@earnedInPeriod']);
+    Route::get('/chart/category/spent-in-period/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\CategoryController@spentInPeriod']);
     Route::get(
-        '/chart/category/multi-year/{report_type}/{start_date}/{end_date}/{accountList}/{categoryList}', ['uses' => 'Chart\CategoryController@multiYear']
+        '/chart/category/multi-year/{reportType}/{start_date}/{end_date}/{accountList}/{categoryList}', ['uses' => 'Chart\CategoryController@multiYear']
     );
 
     Route::get('/chart/category/{category}/period', ['uses' => 'Chart\CategoryController@currentPeriod']);
@@ -422,11 +421,11 @@ Route::group(
     Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']);
 
     // piggy banks:
-    Route::get('/chart/piggyBank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']);
+    Route::get('/chart/piggy-bank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']);
 
     // reports:
-    Route::get('/chart/report/in-out/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOut']);
-    Route::get('/chart/report/in-out-sum/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized']);
+    Route::get('/chart/report/in-out/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOut']);
+    Route::get('/chart/report/in-out-sum/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized']);
 
 
     /**
@@ -492,7 +491,7 @@ Route::group(
      * Report Controller
      */
     Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
-    Route::get('/reports/report/{report_type}/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@report', 'as' => 'reports.report']);
+    Route::get('/reports/report/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@report', 'as' => 'reports.report']);
 
     /**
      * Search Controller
diff --git a/app/Models/Account.php b/app/Models/Account.php
index b3967b1da4..91f75cb286 100644
--- a/app/Models/Account.php
+++ b/app/Models/Account.php
@@ -1,53 +1,38 @@
 app->bind('FireflyIII\Repositories\Account\AccountRepositoryInterface', 'FireflyIII\Repositories\Account\AccountRepository');
         $this->app->bind('FireflyIII\Repositories\Budget\BudgetRepositoryInterface', 'FireflyIII\Repositories\Budget\BudgetRepository');
         $this->app->bind('FireflyIII\Repositories\Category\CategoryRepositoryInterface', 'FireflyIII\Repositories\Category\CategoryRepository');
+        $this->app->bind('FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface', 'FireflyIII\Repositories\Category\SingleCategoryRepository');
         $this->app->bind('FireflyIII\Repositories\Journal\JournalRepositoryInterface', 'FireflyIII\Repositories\Journal\JournalRepository');
         $this->app->bind('FireflyIII\Repositories\Bill\BillRepositoryInterface', 'FireflyIII\Repositories\Bill\BillRepository');
         $this->app->bind('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', 'FireflyIII\Repositories\PiggyBank\PiggyBankRepository');
diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php
index 4e40f5bec8..f946ad464e 100644
--- a/app/Providers/RouteServiceProvider.php
+++ b/app/Providers/RouteServiceProvider.php
@@ -39,6 +39,8 @@ class RouteServiceProvider extends ServiceProvider
      *
      * @param  \Illuminate\Routing\Router $router
      *
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
      * @return void
      */
     public function map(Router $router)
diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php
index dcd9a867f1..a034e4534f 100644
--- a/app/Repositories/Account/AccountRepository.php
+++ b/app/Repositories/Account/AccountRepository.php
@@ -14,7 +14,6 @@ use FireflyIII\Models\Preference;
 use FireflyIII\Models\Transaction;
 use FireflyIII\Models\TransactionJournal;
 use FireflyIII\Models\TransactionType;
-use FireflyIII\Support\CacheProperties;
 use Illuminate\Database\Eloquent\Relations\HasMany;
 use Illuminate\Pagination\LengthAwarePaginator;
 use Illuminate\Support\Collection;
@@ -24,7 +23,6 @@ use Steam;
 
 
 /**
- * @SuppressWarnings(PHPMD.TooManyMethods)
  *
  * Class AccountRepository
  *
@@ -40,14 +38,7 @@ class AccountRepository implements AccountRepositoryInterface
      */
     public function countAccounts(array $types)
     {
-        $cache = new CacheProperties;
-        $cache->addProperty('user-count-accounts');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
         $count = Auth::user()->accounts()->accountTypeIn($types)->count();
-        $cache->store($count);
 
         return $count;
     }
@@ -77,14 +68,6 @@ class AccountRepository implements AccountRepositoryInterface
      */
     public function getAccounts(array $types)
     {
-        $cache = new CacheProperties();
-        $cache->addProperty('get-accounts');
-        $cache->addProperty($types);
-
-        if ($cache->has()) {
-            return $cache->get();
-        }
-
         /** @var Collection $result */
         $result = Auth::user()->accounts()->with(
             ['accountmeta' => function (HasMany $query) {
@@ -97,9 +80,6 @@ class AccountRepository implements AccountRepositoryInterface
                 return strtolower($account->name);
             }
         );
-
-        $cache->store($result);
-
         return $result;
     }
 
@@ -117,12 +97,6 @@ class AccountRepository implements AccountRepositoryInterface
      */
     public function getCreditCards(Carbon $date)
     {
-        $cache = new CacheProperties();
-        $cache->addProperty('user-credit-cards');
-        if ($cache->has()) {
-            return $cache->get();
-        }
-
         $set = Auth::user()->accounts()
                    ->hasMetaValue('accountRole', 'ccAsset')
                    ->hasMetaValue('ccType', 'monthlyFull')
@@ -139,8 +113,6 @@ class AccountRepository implements AccountRepositoryInterface
                            DB::Raw('SUM(`transactions`.`amount`) AS `balance`')
                        ]
                    );
-        $cache->store($set);
-
         return $set;
     }
 
@@ -152,16 +124,7 @@ class AccountRepository implements AccountRepositoryInterface
      */
     public function getFirstTransaction(TransactionJournal $journal, Account $account)
     {
-        $cache = new CacheProperties();
-        $cache->addProperty('first-transaction');
-        $cache->addProperty($journal->id);
-        $cache->addProperty($account->id);
-
-        if ($cache->has()) {
-            return $cache->get();
-        }
         $transaction = $journal->transactions()->where('account_id', $account->id)->first();
-        $cache->store($transaction);
 
         return $transaction;
     }
@@ -173,11 +136,6 @@ class AccountRepository implements AccountRepositoryInterface
      */
     public function getFrontpageAccounts(Preference $preference)
     {
-        $cache = new CacheProperties();
-        $cache->addProperty('user-frontpage-accounts');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
         $query = Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account']);
 
         if (count($preference->data) > 0) {
@@ -185,9 +143,6 @@ class AccountRepository implements AccountRepositoryInterface
         }
 
         $result = $query->get(['accounts.*']);
-
-        $cache->store($result);
-
         return $result;
     }
 
@@ -204,15 +159,6 @@ class AccountRepository implements AccountRepositoryInterface
      */
     public function getFrontpageTransactions(Account $account, Carbon $start, Carbon $end)
     {
-        $cache = new CacheProperties();
-        $cache->addProperty($account->id);
-        $cache->addProperty($start);
-        $cache->addProperty($end);
-        $cache->addProperty('frontpage-transactions');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
         $set = Auth::user()
                    ->transactionjournals()
                    ->with(['transactions'])
@@ -227,8 +173,6 @@ class AccountRepository implements AccountRepositoryInterface
                    ->orderBy('transaction_journals.id', 'DESC')
                    ->take(10)
                    ->get(['transaction_journals.*', 'transaction_currencies.symbol', 'transaction_types.type']);
-        $cache->store($set);
-
         return $set;
     }
 
@@ -266,24 +210,12 @@ class AccountRepository implements AccountRepositoryInterface
      */
     public function getPiggyBankAccounts()
     {
-        $ids        = [];
         $start      = clone Session::get('start', new Carbon);
         $end        = clone Session::get('end', new Carbon);
-        $accountIds = DB::table('piggy_banks')->distinct()->get(['piggy_banks.account_id']);
+        $collection = new Collection(DB::table('piggy_banks')->distinct()->get(['piggy_banks.account_id']));
+        $ids        = $collection->pluck('account_id')->toArray();
         $accounts   = new Collection;
 
-        /** @var PiggyBank $id */
-        foreach ($accountIds as $id) {
-            $ids[] = intval($id->account_id);
-        }
-
-        $cache = new CacheProperties;
-        $cache->addProperty($ids);
-        $cache->addProperty('user-piggy-bank-accounts');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
         $ids = array_unique($ids);
         if (count($ids) > 0) {
             $accounts = Auth::user()->accounts()->whereIn('id', $ids)->where('accounts.active', 1)->get();
@@ -309,8 +241,6 @@ class AccountRepository implements AccountRepositoryInterface
             }
         );
 
-        $cache->store($accounts);
-
         return $accounts;
 
     }
@@ -387,21 +317,12 @@ class AccountRepository implements AccountRepositoryInterface
      */
     public function openingBalanceTransaction(Account $account)
     {
-        $cache = new CacheProperties;
-        $cache->addProperty($account->id);
-        $cache->addProperty('opening-balance-journal');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
-
         $journal = TransactionJournal
             ::orderBy('transaction_journals.date', 'ASC')
             ->accountIs($account)
             ->transactionTypes([TransactionType::OPENING_BALANCE])
             ->orderBy('created_at', 'ASC')
             ->first(['transaction_journals.*']);
-        $cache->store($journal);
 
         return $journal;
     }
@@ -614,7 +535,6 @@ class AccountRepository implements AccountRepositoryInterface
      * @param Account $account
      * @param array   $data
      *
-     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
     protected function updateMetadata(Account $account, array $data)
     {
diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php
index d03cf070e6..b18ffb2807 100644
--- a/app/Repositories/Account/AccountRepositoryInterface.php
+++ b/app/Repositories/Account/AccountRepositoryInterface.php
@@ -44,7 +44,7 @@ interface AccountRepositoryInterface
     /**
      * @param array $types
      *
-     * @return mixed
+     * @return Collection
      */
     public function getAccounts(array $types);
 
diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php
index 1e08433502..b07a37f45a 100644
--- a/app/Repositories/Bill/BillRepository.php
+++ b/app/Repositories/Bill/BillRepository.php
@@ -7,11 +7,9 @@ use Carbon\Carbon;
 use DB;
 use FireflyIII\Models\Account;
 use FireflyIII\Models\Bill;
-use FireflyIII\Models\Transaction;
 use FireflyIII\Models\TransactionJournal;
 use FireflyIII\Models\TransactionType;
 use FireflyIII\Repositories\Account\AccountRepositoryInterface;
-use FireflyIII\Support\CacheProperties;
 use Illuminate\Database\Query\Builder;
 use Illuminate\Database\Query\JoinClause;
 use Illuminate\Support\Collection;
@@ -36,6 +34,40 @@ class BillRepository implements BillRepositoryInterface
         return $bill->delete();
     }
 
+    /**
+     * Returns all journals connected to these bills in the given range. Amount paid
+     * is stored in "journalAmount" as a negative number.
+     *
+     * @param Collection $bills
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return Collection
+     */
+    public function getAllJournalsInRange(Collection $bills, Carbon $start, Carbon $end)
+    {
+        $ids = $bills->pluck('id')->toArray();
+
+        $set = Auth::user()->transactionjournals()
+                   ->leftJoin(
+                       'transactions', function (JoinClause $join) {
+                       $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
+                   }
+                   )
+                   ->whereIn('bill_id', $ids)
+                   ->before($end)
+                   ->after($start)
+                   ->groupBy('transaction_journals.bill_id')
+                   ->get(
+                       [
+                           'transaction_journals.bill_id',
+                           DB::Raw('SUM(`transactions`.`amount`) as `journalAmount`')
+                       ]
+                   );
+
+        return $set;
+    }
+
 
     /**
      * @return Collection
@@ -64,26 +96,22 @@ class BillRepository implements BillRepositoryInterface
      */
     public function getBillsForAccounts(Collection $accounts)
     {
-        /** @var Collection $set */
-        $set = Auth::user()->bills()->orderBy('name', 'ASC')->get();
-
-        $ids = [];
-        /** @var Account $account */
-        foreach ($accounts as $account) {
-            $ids[] = $account->id;
-        }
-
-        $set = $set->filter(
-            function (Bill $bill) use ($ids) {
-                // get transaction journals from or to any of the mentioned accounts.
-                // when zero, return null.
-                $journals = $bill->transactionjournals()->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
-                                 ->whereIn('transactions.account_id', $ids)->count();
-
-                return ($journals > 0);
-
-            }
-        );
+        $ids = $accounts->pluck('id')->toArray();
+        $set = Auth::user()->bills()
+                   ->leftJoin(
+                       'transaction_journals', function (JoinClause $join) {
+                       $join->on('transaction_journals.bill_id', '=', 'bills.id')->whereNull('transaction_journals.deleted_at');
+                   }
+                   )
+                   ->leftJoin(
+                       'transactions', function (JoinClause $join) {
+                       $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
+                   }
+                   )
+                   ->whereIn('transactions.account_id', $ids)
+                   ->whereNull('transaction_journals.deleted_at')
+                   ->groupBy('bills.id')
+                   ->get(['bills.*']);
 
         $set = $set->sortBy(
             function (Bill $bill) {
@@ -107,13 +135,6 @@ class BillRepository implements BillRepositoryInterface
      */
     public function getJournals(Bill $bill)
     {
-        $cache = new CacheProperties;
-        $cache->addProperty($bill->id);
-        $cache->addProperty('journals-for-bill');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
         $set = $bill->transactionjournals()
                     ->leftJoin(
                         'transactions', function (JoinClause $join) {
@@ -125,8 +146,6 @@ class BillRepository implements BillRepositoryInterface
                     ->orderBy('transaction_journals.order', 'ASC')
                     ->orderBy('transaction_journals.id', 'DESC')
                     ->get(['transaction_journals.*', 'transactions.amount as journalAmount']);
-        $cache->store($set);
-
         return $set;
     }
 
@@ -156,12 +175,8 @@ class BillRepository implements BillRepositoryInterface
         $set = DB::table('transactions')->where('amount', '>', 0)->where('amount', '>=', $bill->amount_min)->where('amount', '<=', $bill->amount_max)->get(
             ['transaction_journal_id']
         );
-        $ids = [];
+        $ids = $set->pluck('transaction_journal_id')->toArray();
 
-        /** @var Transaction $entry */
-        foreach ($set as $entry) {
-            $ids[] = intval($entry->transaction_journal_id);
-        }
         $journals = new Collection;
         if (count($ids) > 0) {
             $journals = Auth::user()->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->whereIn('transaction_journals.id', $ids)->get(
@@ -425,13 +440,6 @@ class BillRepository implements BillRepositoryInterface
      */
     public function getBillsPaidInRange(Carbon $start, Carbon $end)
     {
-        $cache = new CacheProperties;
-        $cache->addProperty($start);
-        $cache->addProperty($end);
-        $cache->addProperty('bills-paid-in-range');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
         $amount = '0';
         $bills  = $this->getActiveBills();
 
@@ -452,8 +460,6 @@ class BillRepository implements BillRepositoryInterface
                 $amount = bcadd($amount, $paid->sum_amount);
             }
         }
-        $cache->store($amount);
-
         return $amount;
     }
 
@@ -486,13 +492,6 @@ class BillRepository implements BillRepositoryInterface
      */
     public function getBillsUnpaidInRange(Carbon $start, Carbon $end)
     {
-        $cache = new CacheProperties;
-        $cache->addProperty($start);
-        $cache->addProperty($end);
-        $cache->addProperty('bills-unpaid-in-range');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
         $amount = '0';
         $bills  = $this->getActiveBills();
 
@@ -516,8 +515,6 @@ class BillRepository implements BillRepositoryInterface
                 $amount = bcadd($amount, $bill->expectedAmount);
             }
         }
-        $cache->store($amount);
-
         return $amount;
     }
 
@@ -533,13 +530,6 @@ class BillRepository implements BillRepositoryInterface
     public function getCreditCardBill(Carbon $start, Carbon $end)
     {
 
-        $cache = new CacheProperties;
-        $cache->addProperty($start);
-        $cache->addProperty($end);
-        $cache->addProperty('credit-card-bill');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
         /** @var AccountRepositoryInterface $accountRepository */
         $accountRepository = app('FireflyIII\Repositories\Account\AccountRepositoryInterface');
         $amount            = '0';
diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php
index d171aedcdb..0cf52754bf 100644
--- a/app/Repositories/Bill/BillRepositoryInterface.php
+++ b/app/Repositories/Bill/BillRepositoryInterface.php
@@ -59,6 +59,19 @@ interface BillRepositoryInterface
      */
     public function destroy(Bill $bill);
 
+    /**
+     * Returns all journals connected to these bills in the given range. Amount paid
+     * is stored in "journalAmount" as a negative number.
+     *
+     * @param Collection $bills
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return Collection
+     */
+    public function getAllJournalsInRange(Collection $bills, Carbon $start, Carbon $end);
+
+
     /**
      * @return Collection
      */
diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php
index a38cd42261..2750dcfe6a 100644
--- a/app/Repositories/Budget/BudgetRepository.php
+++ b/app/Repositories/Budget/BudgetRepository.php
@@ -10,7 +10,6 @@ use FireflyIII\Models\BudgetLimit;
 use FireflyIII\Models\LimitRepetition;
 use FireflyIII\Models\TransactionType;
 use FireflyIII\Repositories\Shared\ComponentRepository;
-use FireflyIII\Support\CacheProperties;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Query\Builder as QueryBuilder;
 use Illuminate\Database\Query\JoinClause;
@@ -36,6 +35,67 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
 
     }
 
+    /**
+     * Returns the expenses for this budget grouped per day, with the date
+     * in "date" (a string, not a Carbon) and the amount in "dailyAmount".
+     *
+     * @param Budget $budget
+     * @param Carbon $start
+     * @param Carbon $end
+     *
+     * @return Collection
+     */
+    public function getExpensesPerDay(Budget $budget, Carbon $start, Carbon $end)
+    {
+        $set = Auth::user()->budgets()
+                   ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.budget_id', '=', 'budgets.id')
+                   ->leftJoin('transaction_journals', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
+                   ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
+                   ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
+                   ->whereNull('transaction_journals.deleted_at')
+                   ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
+                   ->where('budgets.id', $budget->id)
+                   ->where('transactions.amount', '<', 0)
+                   ->groupBy('transaction_journals.date')
+                   ->orderBy('transaction_journals.date')
+                   ->get(['transaction_journals.date', DB::Raw('SUM(`transactions`.`amount`) as `dailyAmount`')]);
+
+        return $set;
+    }
+
+    /**
+     * Returns the expenses for this budget grouped per month, with the date
+     * in "dateFormatted" (a string, not a Carbon) and the amount in "dailyAmount".
+     *
+     * @param Budget $budget
+     * @param Carbon $start
+     * @param Carbon $end
+     *
+     * @return Collection
+     */
+    public function getExpensesPerMonth(Budget $budget, Carbon $start, Carbon $end)
+    {
+        $set = Auth::user()->budgets()
+                   ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.budget_id', '=', 'budgets.id')
+                   ->leftJoin('transaction_journals', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
+                   ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
+                   ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
+                   ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
+                   ->whereNull('transaction_journals.deleted_at')
+                   ->where('budgets.id', $budget->id)
+                   ->where('transactions.amount', '<', 0)
+                   ->groupBy('dateFormatted')
+                   ->orderBy('transaction_journals.date')
+                   ->get(
+                       [
+                           DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y-%m") AS `dateFormatted`'),
+                           DB::Raw('SUM(`transactions`.`amount`) as `monthlyAmount`')
+                       ]
+                   );
+
+        return $set;
+    }
+
     /**
      * @param Budget $budget
      *
@@ -48,20 +108,6 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
         return true;
     }
 
-    /**
-     * @param Budget $budget
-     * @param Carbon $date
-     *
-     * @return float
-     */
-    public function expensesOnDay(Budget $budget, Carbon $date)
-    {
-        bcscale(2);
-        $sum = $budget->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
-
-        return $sum;
-    }
-
     /**
      * @return Collection
      */
@@ -125,30 +171,68 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
 
     /**
      * @param Budget $budget
+     *
+     * @return Carbon
+     */
+    public function firstActivity(Budget $budget)
+    {
+        $first = $budget->transactionjournals()->orderBy('date', 'ASC')->first();
+        if ($first) {
+            return $first->date;
+        }
+
+        return new Carbon;
+    }
+
+    /**
      * @param Carbon $start
      * @param Carbon $end
      *
      * @return Collection
      */
-    public function getBudgetLimitRepetitions(Budget $budget, Carbon $start, Carbon $end)
+    public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end)
     {
         /** @var Collection $repetitions */
         return LimitRepetition::
         leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
+                              ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
                               ->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00'))
                               ->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d 00:00:00'))
-                              ->where('budget_limits.budget_id', $budget->id)
-                              ->get(['limit_repetitions.*']);
+                              ->where('budgets.user_id', Auth::user()->id)
+                              ->get(['limit_repetitions.*', 'budget_limits.budget_id']);
     }
 
     /**
-     * @param Budget $budget
+     * Returns an array with the following key:value pairs:
      *
-     * @return Collection
+     * yyyy-mm-dd:
+     *
+     * Where yyyy-mm-dd is the date and  is the money spent using DEPOSITS in the $budget
+     * from all the users accounts.
+     *
+     * @param Budget $budget
+     * @param Carbon $start
+     * @param Carbon $end
+     *
+     * @return array
      */
-    public function getBudgetLimits(Budget $budget)
+    public function spentPerDay(Budget $budget, Carbon $start, Carbon $end)
     {
-        return $budget->budgetLimits()->orderBy('startdate', 'DESC')->get();
+        /** @var Collection $query */
+        $query = $budget->transactionJournals()
+                        ->transactionTypes([TransactionType::WITHDRAWAL])
+                        ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
+                        ->where('transactions.amount', '<', 0)
+                        ->before($end)
+                        ->after($start)
+                        ->groupBy('dateFormatted')->get(['transaction_journals.date as dateFormatted', DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
+
+        $return = [];
+        foreach ($query->toArray() as $entry) {
+            $return[$entry['dateFormatted']] = $entry['sum'];
+        }
+
+        return $return;
     }
 
     /**
@@ -177,21 +261,10 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
      */
     public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end)
     {
-        $cache = new CacheProperties;
-        $cache->addProperty($budget->id);
-        $cache->addProperty($start);
-        $cache->addProperty($end);
-
-        $cache->addProperty('getCurrentRepetition');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
         $data = $budget->limitrepetitions()
                        ->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
                        ->where('limit_repetitions.enddate', $end->format('Y-m-d 00:00:00'))
                        ->first(['limit_repetitions.*']);
-        $cache->store($data);
-
         return $data;
     }
 
@@ -210,6 +283,64 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
         return Carbon::now()->startOfYear();
     }
 
+    /**
+     * Returns an array with every budget in it and the expenses for each budget
+     * per month.
+     *
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return array
+     */
+    public function getBudgetsAndExpensesPerMonth(Collection $accounts, Carbon $start, Carbon $end)
+    {
+        $ids = $accounts->pluck('id')->toArray();
+
+        /** @var Collection $set */
+        $set = Auth::user()->budgets()
+                   ->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
+                   ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
+                   ->leftJoin(
+                       'transactions', function (JoinClause $join) {
+                       $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
+                   }
+                   )
+                   ->groupBy('budgets.id')
+                   ->groupBy('dateFormatted')
+                   ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
+                   ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
+                   ->whereIn('transactions.account_id', $ids)
+                   ->get(
+                       [
+                           'budgets.*',
+                           DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y-%m") AS `dateFormatted`'),
+                           DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`')
+                       ]
+                   );
+
+        $set = $set->sortBy(
+            function (Budget $budget) {
+                return strtolower($budget->name);
+            }
+        );
+
+        $return = [];
+        foreach ($set as $budget) {
+            $id = $budget->id;
+            if (!isset($return[$id])) {
+                $return[$id] = [
+                    'budget'  => $budget,
+                    'entries' => [],
+                ];
+            }
+            // store each entry:
+            $return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount;
+        }
+
+        return $return;
+    }
+
     /**
      * @return Collection
      */
@@ -238,17 +369,6 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
      */
     public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50)
     {
-        $cache = new CacheProperties;
-        $cache->addProperty($budget->id);
-        if ($repetition) {
-            $cache->addProperty($repetition->id);
-        }
-        $cache->addProperty($take);
-        $cache->addProperty('getJournals');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
         $offset     = intval(Input::get('page')) > 0 ? intval(Input::get('page')) * $take : 0;
         $setQuery   = $budget->transactionJournals()->withRelevantData()->take($take)->offset($offset)
                              ->orderBy('transaction_journals.date', 'DESC')
@@ -268,50 +388,10 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
 
 
         $paginator = new LengthAwarePaginator($set, $count, $take, $offset);
-        $cache->store($paginator);
 
         return $paginator;
     }
 
-    /**
-     * @deprecated
-     *
-     * @param Budget $budget
-     *
-     * @return Carbon
-     */
-    public function getLastBudgetLimitDate(Budget $budget)
-    {
-        $limit = $budget->budgetlimits()->orderBy('startdate', 'DESC')->first();
-        if ($limit) {
-            return $limit->startdate;
-        }
-
-        return Carbon::now()->startOfYear();
-    }
-
-    /**
-     * @deprecated
-     *
-     * @param Budget $budget
-     * @param Carbon $date
-     *
-     * @return float|null
-     */
-    public function getLimitAmountOnDate(Budget $budget, Carbon $date)
-    {
-        $repetition = LimitRepetition::leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
-                                     ->where('limit_repetitions.startdate', $date->format('Y-m-d 00:00:00'))
-                                     ->where('budget_limits.budget_id', $budget->id)
-                                     ->first(['limit_repetitions.*']);
-
-        if ($repetition) {
-            return $repetition->amount;
-        }
-
-        return null;
-    }
-
     /**
      * @param Carbon $start
      * @param Carbon $end
@@ -450,7 +530,197 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
         }
 
         return $limit;
+    }
 
+    /**
+     * Get the budgeted amounts for each budgets in each year.
+     *
+     * @param Collection $budgets
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return Collection
+     */
+    public function getBudgetedPerYear(Collection $budgets, Carbon $start, Carbon $end)
+    {
+        $budgetIds = $budgets->pluck('id')->toArray();
+
+        $set = Auth::user()->budgets()
+                   ->leftJoin('budget_limits', 'budgets.id', '=', 'budget_limits.budget_id')
+                   ->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
+                   ->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d'))
+                   ->where('limit_repetitions.enddate', '<=', $end->format('Y-m-d'))
+                   ->groupBy('budgets.id')
+                   ->groupBy('dateFormatted')
+                   ->whereIn('budgets.id', $budgetIds)
+                   ->get(
+                       [
+                           'budgets.*',
+                           DB::Raw('DATE_FORMAT(`limit_repetitions`.`startdate`,"%Y") as `dateFormatted`'),
+                           DB::Raw('SUM(`limit_repetitions`.`amount`) as `budgeted`')
+                       ]
+                   );
+
+        return $set;
+    }
+
+    /**
+     * Returns an array with every budget in it and the expenses for each budget
+     * per year for.
+     *
+     * @param Collection $budgets
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return array
+     */
+    public function getBudgetsAndExpensesPerYear(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end)
+    {
+        $ids       = $accounts->pluck('id')->toArray();
+        $budgetIds = $budgets->pluck('id')->toArray();
+
+        /** @var Collection $set */
+        $set = Auth::user()->budgets()
+                   ->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
+                   ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
+                   ->leftJoin(
+                       'transactions', function (JoinClause $join) {
+                       $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
+                   }
+                   )
+                   ->groupBy('budgets.id')
+                   ->groupBy('dateFormatted')
+                   ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
+                   ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
+                   ->whereIn('transactions.account_id', $ids)
+                   ->whereIn('budgets.id', $budgetIds)
+                   ->get(
+                       [
+                           'budgets.*',
+                           DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y") AS `dateFormatted`'),
+                           DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`')
+                       ]
+                   );
+
+        $set = $set->sortBy(
+            function (Budget $budget) {
+                return strtolower($budget->name);
+            }
+        );
+
+        $return = [];
+        foreach ($set as $budget) {
+            $id = $budget->id;
+            if (!isset($return[$id])) {
+                $return[$id] = [
+                    'budget'  => $budget,
+                    'entries' => [],
+                ];
+            }
+            // store each entry:
+            $return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount;
+        }
+
+        return $return;
+    }
+
+    /**
+     * Returns an array with the following key:value pairs:
+     *
+     * yyyy-mm-dd:
+     *
+     * That array contains:
+     *
+     * budgetid:
+     *
+     * Where yyyy-mm-dd is the date and  is the money spent using WITHDRAWALS in the $budget
+     * from the given users accounts..
+     *
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return array
+     */
+    public function spentAllPerDayForAccounts(Collection $accounts, Carbon $start, Carbon $end)
+    {
+        $ids = $accounts->pluck('id')->toArray();
+        /** @var Collection $query */
+        $query = Auth::user()->transactionJournals()
+                     ->transactionTypes([TransactionType::WITHDRAWAL])
+                     ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
+                     ->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
+                     ->whereIn('transactions.account_id', $ids)
+                     ->where('transactions.amount', '<', 0)
+                     ->before($end)
+                     ->after($start)
+                     ->groupBy('budget_id')
+                     ->groupBy('dateFormatted')
+                     ->get(
+                         ['transaction_journals.date as dateFormatted', 'budget_transaction_journal.budget_id',
+                          DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]
+                     );
+
+        $return = [];
+        foreach ($query->toArray() as $entry) {
+            $budgetId = $entry['budget_id'];
+            if (!isset($return[$budgetId])) {
+                $return[$budgetId] = [];
+            }
+            $return[$budgetId][$entry['dateFormatted']] = $entry['sum'];
+        }
+
+        return $return;
+    }
+
+    /**
+     * Returns a list of expenses (in the field "spent", grouped per budget per account.
+     *
+     * @param Collection $budgets
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return Collection
+     */
+    public function spentPerBudgetPerAccount(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end)
+    {
+        $accountIds = $accounts->pluck('id')->toArray();
+        $budgetIds  = $budgets->pluck('id')->toArray();
+        $set        = Auth::user()->transactionjournals()
+                          ->leftJoin(
+                              'transactions AS t_from', function (JoinClause $join) {
+                              $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0);
+                          }
+                          )
+                          ->leftJoin(
+                              'transactions AS t_to', function (JoinClause $join) {
+                              $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0);
+                          }
+                          )
+                          ->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
+                          ->whereIn('t_from.account_id', $accountIds)
+                          ->whereNotIn('t_to.account_id', $accountIds)
+                          ->where(
+                              function (Builder $q) use ($budgetIds) {
+                                  $q->whereIn('budget_transaction_journal.budget_id', $budgetIds);
+                                  $q->orWhereNull('budget_transaction_journal.budget_id');
+                              }
+                          )
+                          ->after($start)
+                          ->before($end)
+                          ->groupBy('t_from.account_id')
+                          ->groupBy('budget_transaction_journal.budget_id')
+                          ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE])
+                          ->get(
+                              [
+                                  't_from.account_id', 'budget_transaction_journal.budget_id',
+                                  DB::Raw('SUM(`t_from`.`amount`) AS `spent`')
+                              ]
+                          );
+
+        return $set;
 
     }
 }
diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php
index 9ac42a85be..c9c9d8bc40 100644
--- a/app/Repositories/Budget/BudgetRepositoryInterface.php
+++ b/app/Repositories/Budget/BudgetRepositoryInterface.php
@@ -15,11 +15,93 @@ use Illuminate\Support\Collection;
  */
 interface BudgetRepositoryInterface
 {
+
+
     /**
      * @return void
      */
     public function cleanupBudgets();
 
+    /**
+     * Returns the expenses for this budget grouped per day, with the date
+     * in "date" (a string, not a Carbon) and the amount in "dailyAmount".
+     *
+     * @param Budget $budget
+     * @param Carbon $start
+     * @param Carbon $end
+     *
+     * @return Collection
+     */
+    public function getExpensesPerDay(Budget $budget, Carbon $start, Carbon $end);
+
+    /**
+     * @param Budget $budget
+     *
+     * @return Carbon
+     */
+    public function firstActivity(Budget $budget);
+
+    /**
+     * Returns the expenses for this budget grouped per month, with the date
+     * in "date" (a string, not a Carbon) and the amount in "dailyAmount".
+     *
+     * @param Budget $budget
+     * @param Carbon $start
+     * @param Carbon $end
+     *
+     * @return Collection
+     */
+    public function getExpensesPerMonth(Budget $budget, Carbon $start, Carbon $end);
+
+
+    /**
+     * Returns a list of expenses (in the field "spent", grouped per budget per account.
+     *
+     * @param Collection $budgets
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return Collection
+     */
+    public function spentPerBudgetPerAccount(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end);
+
+    /**
+     * Returns an array with the following key:value pairs:
+     *
+     * yyyy-mm-dd:
+     *
+     * Where yyyy-mm-dd is the date and  is the money spent using WITHDRAWALS in the $budget
+     * from all the users accounts.
+     *
+     * @param Budget $budget
+     * @param Carbon $start
+     * @param Carbon $end
+     *
+     * @return array
+     */
+    public function spentPerDay(Budget $budget, Carbon $start, Carbon $end);
+
+    /**
+     * Returns an array with the following key:value pairs:
+     *
+     * yyyy-mm-dd:
+     *
+     * That array contains:
+     *
+     * budgetid:
+     *
+     * Where yyyy-mm-dd is the date and  is the money spent using WITHDRAWALS in the $budget
+     * from the given users accounts..
+     *
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return array
+     */
+    public function spentAllPerDayForAccounts(Collection $accounts, Carbon $start, Carbon $end);
+
     /**
      * @param Budget $budget
      *
@@ -28,14 +110,29 @@ interface BudgetRepositoryInterface
     public function destroy(Budget $budget);
 
     /**
-     * Takes tags into account.
+     * Returns an array with every budget in it and the expenses for each budget
+     * per month.
      *
-     * @param Budget $budget
-     * @param Carbon $date
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
      *
-     * @return float
+     * @return array
      */
-    public function expensesOnDay(Budget $budget, Carbon $date);
+    public function getBudgetsAndExpensesPerMonth(Collection $accounts, Carbon $start, Carbon $end);
+
+    /**
+     * Returns an array with every budget in it and the expenses for each budget
+     * per year for.
+     *
+     * @param Collection $budgets
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return array
+     */
+    public function getBudgetsAndExpensesPerYear(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end);
 
     /**
      * @return Collection
@@ -43,20 +140,23 @@ interface BudgetRepositoryInterface
     public function getActiveBudgets();
 
     /**
-     * @param Budget $budget
+     * Get the budgeted amounts for each budgets in each year.
+     *
+     * @param Collection $budgets
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return Collection
+     */
+    public function getBudgetedPerYear(Collection $budgets, Carbon $start, Carbon $end);
+
+    /**
      * @param Carbon $start
      * @param Carbon $end
      *
      * @return Collection
      */
-    public function getBudgetLimitRepetitions(Budget $budget, Carbon $start, Carbon $end);
-
-    /**
-     * @param Budget $budget
-     *
-     * @return Collection
-     */
-    public function getBudgetLimits(Budget $budget);
+    public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end);
 
     /**
      * @return Collection
@@ -106,25 +206,6 @@ interface BudgetRepositoryInterface
      */
     public function getJournals(Budget $budget, LimitRepetition $repetition = null, $take = 50);
 
-    /**
-     * @deprecated
-     *
-     * @param Budget $budget
-     *
-     * @return Carbon
-     */
-    public function getLastBudgetLimitDate(Budget $budget);
-
-    /**
-     * @deprecated
-     *
-     * @param Budget $budget
-     * @param Carbon $date
-     *
-     * @return float
-     */
-    public function getLimitAmountOnDate(Budget $budget, Carbon $date);
-
     /**
      * @param Carbon $start
      * @param Carbon $end
diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php
index bf8eb047af..534188bace 100644
--- a/app/Repositories/Category/CategoryRepository.php
+++ b/app/Repositories/Category/CategoryRepository.php
@@ -4,13 +4,10 @@ namespace FireflyIII\Repositories\Category;
 
 use Auth;
 use Carbon\Carbon;
-use Crypt;
 use DB;
 use FireflyIII\Models\Category;
-use FireflyIII\Models\TransactionJournal;
 use FireflyIII\Models\TransactionType;
-use FireflyIII\Repositories\Shared\ComponentRepository;
-use FireflyIII\Support\CacheProperties;
+use FireflyIII\Sql\Query;
 use Illuminate\Database\Query\JoinClause;
 use Illuminate\Support\Collection;
 
@@ -19,44 +16,16 @@ use Illuminate\Support\Collection;
  *
  * @package FireflyIII\Repositories\Category
  */
-class CategoryRepository extends ComponentRepository implements CategoryRepositoryInterface
+class CategoryRepository implements CategoryRepositoryInterface
 {
 
     /**
-     * @param Category $category
+     * Returns a list of all the categories belonging to a user.
      *
-     * @return int
-     */
-    public function countJournals(Category $category)
-    {
-        return $category->transactionJournals()->count();
-
-    }
-
-    /**
-     * @param Category $category
-     *
-     * @return boolean
-     */
-    public function destroy(Category $category)
-    {
-        $category->delete();
-
-        return true;
-    }
-
-    /**
      * @return Collection
      */
-    public function getCategories()
+    public function listCategories()
     {
-        $cache = new CacheProperties;
-        $cache->addProperty('category-list');
-
-        if ($cache->has()) {
-            return $cache->get();
-        }
-
         /** @var Collection $set */
         $set = Auth::user()->categories()->orderBy('name', 'ASC')->get();
         $set = $set->sortBy(
@@ -65,181 +34,19 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
             }
         );
 
-        $cache->store($set);
-
         return $set;
     }
 
     /**
-     * Returns the amount earned without category by accounts in period.
+     * Returns a list of transaction journals in the range (all types, all accounts) that have no category
+     * associated to them.
      *
-     * @param Collection $accounts
-     * @param Carbon     $start
-     * @param Carbon     $end
-     *
-     * @return string
-     */
-    public function earnedNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end)
-    {
-
-        $accountIds = [];
-        foreach ($accounts as $account) {
-            $accountIds[] = $account->id;
-        }
-
-        // is deposit AND account_from is in the list of $accounts
-        // not from any of the accounts in the list?
-
-        return Auth::user()
-                   ->transactionjournals()
-                   ->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
-                   ->whereNull('category_transaction_journal.id')
-                   ->before($end)
-                   ->after($start)
-                   ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
-                   ->whereIn('transactions.account_id', $accountIds)
-                   ->transactionTypes([TransactionType::DEPOSIT])
-                   ->get(['transaction_journals.*'])->sum('amount');
-    }
-
-
-    /**
-     * Returns the amount spent without category by accounts in period.
-     *
-     * @param Collection $accounts
-     * @param Carbon     $start
-     * @param Carbon     $end
-     *
-     * @return string
-     */
-    public function spentNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end)
-    {
-
-        $accountIds = [];
-        foreach ($accounts as $account) {
-            $accountIds[] = $account->id;
-        }
-
-        // is withdrawal or transfer AND account_from is in the list of $accounts
-
-
-        return Auth::user()
-                   ->transactionjournals()
-                   ->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
-                   ->whereNull('category_transaction_journal.id')
-                   ->before($end)
-                   ->after($start)
-                   ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
-                   ->whereIn('transactions.account_id', $accountIds)
-                   ->transactionTypes([TransactionType::WITHDRAWAL])
-                   ->get(['transaction_journals.*'])->sum('amount');
-    }
-
-
-    /**
-     *
-     * @param Carbon $start
-     * @param Carbon $end
-     *
-     * @return array
-     */
-    public function getCategoriesAndExpenses(Carbon $start, Carbon $end)
-    {
-        $set = Auth::user()->transactionjournals()
-                   ->leftJoin(
-                       'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
-                   )
-                   ->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
-                   ->before($end)
-                   ->where('categories.user_id', Auth::user()->id)
-                   ->after($start)
-                   ->transactionTypes([TransactionType::WITHDRAWAL])
-                   ->get(['categories.id as category_id', 'categories.encrypted as category_encrypted', 'categories.name', 'transaction_journals.*']);
-
-        bcscale(2);
-        $result = [];
-        foreach ($set as $entry) {
-            $categoryId = intval($entry->category_id);
-            if (isset($result[$categoryId])) {
-                $result[$categoryId]['sum'] = bcadd($result[$categoryId]['sum'], $entry->amount);
-            } else {
-                $isEncrypted         = intval($entry->category_encrypted) === 1 ? true : false;
-                $name                = strlen($entry->name) === 0 ? trans('firefly.no_category') : $entry->name;
-                $name                = $isEncrypted ? Crypt::decrypt($name) : $name;
-                $result[$categoryId] = [
-                    'name' => $name,
-                    'sum'  => $entry->amount,
-                ];
-
-            }
-        }
-
-        return $result;
-    }
-
-    /**
-     * @param Category $category
-     *
-     * @return Carbon
-     */
-    public function getFirstActivityDate(Category $category)
-    {
-        /** @var TransactionJournal $first */
-        $first = $category->transactionjournals()->orderBy('date', 'ASC')->first();
-        if ($first) {
-            return $first->date;
-        }
-
-        return new Carbon;
-
-    }
-
-    /**
-     * @param Category $category
-     * @param int      $page
-     *
-     * @return Collection
-     */
-    public function getJournals(Category $category, $page)
-    {
-        $offset = $page > 0 ? $page * 50 : 0;
-
-        return $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)
-                        ->orderBy('transaction_journals.date', 'DESC')
-                        ->orderBy('transaction_journals.order', 'ASC')
-                        ->orderBy('transaction_journals.id', 'DESC')
-                        ->get(
-                            ['transaction_journals.*']
-                        );
-
-    }
-
-    /**
-     * @param Category $category
-     *
-     * @return Carbon|null
-     */
-    public function getLatestActivity(Category $category)
-    {
-        $latest = $category->transactionjournals()
-                           ->orderBy('transaction_journals.date', 'DESC')
-                           ->orderBy('transaction_journals.order', 'ASC')
-                           ->orderBy('transaction_journals.id', 'DESC')
-                           ->first();
-        if ($latest) {
-            return $latest->date;
-        }
-
-        return null;
-    }
-
-    /**
      * @param Carbon $start
      * @param Carbon $end
      *
      * @return Collection
      */
-    public function getWithoutCategory(Carbon $start, Carbon $end)
+    public function listNoCategory(Carbon $start, Carbon $end)
     {
         return Auth::user()
                    ->transactionjournals()
@@ -254,269 +61,65 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
     }
 
     /**
-     * @param Category   $category
-     * @param Carbon     $start
-     * @param Carbon     $end
-     * @param Collection $accounts
+     * This method returns a very special collection for each category:
      *
-     * @return string
-     */
-    public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, Collection $accounts)
-    {
-        return $this->commonBalanceInPeriod($category, $start, $end, $accounts);
-    }
-
-    /**
-     * Corrected for tags
+     * category, year, expense/earned, amount
      *
-     * @param Category $category
-     * @param Carbon   $date
+     * categories can be duplicated.
      *
-     * @return string
-     */
-    public function spentOnDaySum(Category $category, Carbon $date)
-    {
-        return $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
-    }
-
-    /**
-     * @param array $data
-     *
-     * @return Category
-     */
-    public function store(array $data)
-    {
-        $newCategory = new Category(
-            [
-                'user_id' => $data['user'],
-                'name'    => $data['name'],
-            ]
-        );
-        $newCategory->save();
-
-        return $newCategory;
-    }
-
-    /**
-     * @param Category $category
-     * @param array    $data
-     *
-     * @return Category
-     */
-    public function update(Category $category, array $data)
-    {
-        // update the account:
-        $category->name = $data['name'];
-        $category->save();
-
-        return $category;
-    }
-
-    /**
-     * @deprecated
-     * This method returns the sum of the journals in the category, optionally
-     * limited by a start or end date.
-     *
-     * @param Category $category
-     * @param Carbon   $start
-     * @param Carbon   $end
-     *
-     * @return string
-     */
-    public function journalsSum(Category $category, Carbon $start = null, Carbon $end = null)
-    {
-        $query = $category->transactionJournals()
-                          ->orderBy('transaction_journals.date', 'DESC')
-                          ->orderBy('transaction_journals.order', 'ASC')
-                          ->orderBy('transaction_journals.id', 'DESC');
-        if (!is_null($start)) {
-            $query->after($start);
-        }
-
-        if (!is_null($end)) {
-            $query->before($end);
-        }
-
-        return $query->get(['transaction_journals.*'])->sum('amount');
-
-    }
-
-    /**
-     * @param Category       $category
-     * @param \Carbon\Carbon $start
-     * @param \Carbon\Carbon $end
-     *
-     * @return string
-     */
-    public function spentInPeriod(Category $category, Carbon $start, Carbon $end)
-    {
-        $cache = new CacheProperties; // we must cache this.
-        $cache->addProperty($category->id);
-        $cache->addProperty($start);
-        $cache->addProperty($end);
-        $cache->addProperty('spentInPeriod');
-
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
-        $sum = $category->transactionjournals()->transactionTypes([TransactionType::WITHDRAWAL])->before($end)->after($start)->get(['transaction_journals.*'])
-                        ->sum(
-                            'amount'
-                        );
-
-        $cache->store($sum);
-
-        return $sum;
-    }
-
-    /**
-     * @param Category       $category
-     * @param \Carbon\Carbon $start
-     * @param \Carbon\Carbon $end
-     *
-     * @return string
-     */
-    public function earnedInPeriod(Category $category, Carbon $start, Carbon $end)
-    {
-        $cache = new CacheProperties; // we must cache this.
-        $cache->addProperty($category->id);
-        $cache->addProperty($start);
-        $cache->addProperty($end);
-        $cache->addProperty('earnedInPeriod');
-
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
-        $sum = $category->transactionjournals()->transactionTypes([TransactionType::DEPOSIT])->before($end)->after($start)->get(['transaction_journals.*'])
-                        ->sum(
-                            'amount'
-                        );
-
-        $cache->store($sum);
-
-        return $sum;
-    }
-
-
-    /**
-     * @param Category $category
-     * @param int      $page
-     * @param Carbon   $start
-     * @param Carbon   $end
-     *
-     * @return mixed
-     */
-    public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end)
-    {
-        $offset = $page > 0 ? $page * 50 : 0;
-
-        return $category->transactionJournals()
-                        ->after($start)
-                        ->before($end)
-                        ->withRelevantData()->take(50)->offset($offset)
-                        ->orderBy('transaction_journals.date', 'DESC')
-                        ->orderBy('transaction_journals.order', 'ASC')
-                        ->orderBy('transaction_journals.id', 'DESC')
-                        ->get(
-                            ['transaction_journals.*']
-                        );
-    }
-
-    /**
-     * @param Category $category
-     *
-     * @param Carbon   $start
-     * @param Carbon   $end
-     *
-     * @return int
-     */
-    public function countJournalsInRange(Category $category, Carbon $start, Carbon $end)
-    {
-        return $category->transactionJournals()->before($end)->after($start)->count();
-    }
-
-    /**
-     *
-     * Corrected for tags.
-     *
-     * @param Category $category
-     * @param Carbon   $date
-     *
-     * @return float
-     */
-    public function earnedOnDaySum(Category $category, Carbon $date)
-    {
-        return $category->transactionjournals()->transactionTypes([TransactionType::DEPOSIT])->onDate($date)->get(['transaction_journals.*'])->sum('amount');
-    }
-
-    /**
-     * Calculates how much is spent in this period.
-     *
-     * @param Category   $category
+     * @param Collection $categories
      * @param Collection $accounts
      * @param Carbon     $start
      * @param Carbon     $end
      *
-     * @return string
+     * @return Collection
      */
-    public function spentInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end)
+    public function listMultiYear(Collection $categories, Collection $accounts, Carbon $start, Carbon $end)
     {
-        $accountIds = [];
-        foreach ($accounts as $account) {
-            $accountIds[] = $account->id;
-        }
+        /*
+         * select categories.id, DATE_FORMAT(transaction_journals.date,"%Y") as dateFormatted, transaction_types.type, SUM(amount) as sum from categories
 
-        $sum
-            = $category
-            ->transactionjournals()
-            ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
-            ->after($start)
-            ->before($end)
-            ->whereIn('transactions.account_id', $accountIds)
-            ->transactionTypes([TransactionType::WITHDRAWAL])
-            ->get(['transaction_journals.*'])
-            ->sum('amount');
+left join category_transaction_journal ON category_transaction_journal.category_id = categories.id
+left join transaction_journals ON transaction_journals.id = category_transaction_journal.transaction_journal_id
+left join transaction_types ON transaction_types.id = transaction_journals.transaction_type_id
+left join transactions ON transactions.transaction_journal_id = transaction_journals.id
 
-        return $sum;
+
+where
+categories.user_id =1
+and transaction_types.type in ("Withdrawal","Deposit")
+and transactions.account_id IN (2,4,6,10,11,610,725,879,1248)
+
+group by categories.id, transaction_types.type, dateFormatted
+         */
+        $set = Auth::user()->categories()
+                   ->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id')
+                   ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'category_transaction_journal.transaction_journal_id')
+                   ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
+                   ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
+                   ->whereIn('transaction_types.type', [TransactionType::DEPOSIT, TransactionType::WITHDRAWAL])
+                   ->whereIn('transactions.account_id', $accounts->pluck('id')->toArray())
+                   ->whereIn('categories.id', $categories->pluck('id')->toArray())
+                   ->groupBy('categories.id')
+                   ->groupBy('transaction_types.type')
+                   ->groupBy('dateFormatted')
+                   ->get(
+                       [
+                           'categories.*',
+                           DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y") as `dateFormatted`'),
+                           'transaction_types.type',
+                           DB::Raw('SUM(`amount`) as `sum`')
+                       ]
+                   );
+
+        return $set;
 
     }
 
-    /**
-     * Calculate how much is earned in this period.
-     *
-     * @param Category   $category
-     * @param Collection $accounts
-     * @param Carbon     $start
-     * @param Carbon     $end
-     *
-     * @return string
-     */
-    public function earnedInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end)
-    {
-        $accountIds = [];
-        foreach ($accounts as $account) {
-            $accountIds[] = $account->id;
-        }
-        $sum
-            = $category
-            ->transactionjournals()
-            ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
-            ->before($end)
-            ->whereIn('transactions.account_id', $accountIds)
-            ->transactionTypes([TransactionType::DEPOSIT])
-            ->after($start)
-            ->get(['transaction_journals.*'])
-            ->sum('amount');
-
-        return $sum;
-
-    }
 
     /**
      * Returns a collection of Categories appended with the amount of money that has been earned
-     * in these categories, based on the $accounts involved, in period X.
+     * in these categories, based on the $accounts involved, in period X, grouped per month.
      * The amount earned in category X in period X is saved in field "earned".
      *
      * @param $accounts
@@ -525,13 +128,8 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
      *
      * @return Collection
      */
-    public function earnedForAccounts(Collection $accounts, Carbon $start, Carbon $end)
+    public function earnedForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end)
     {
-        $accountIds = [];
-        foreach ($accounts as $account) {
-            $accountIds[] = $account->id;
-        }
-
 
         $collection = Auth::user()->categories()
                           ->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id')
@@ -547,15 +145,22 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
                               $join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0);
                           }
                           )
-                          ->whereIn('t_dest.account_id', $accountIds)// to these accounts (earned)
-                          ->whereNotIn('t_src.account_id', $accountIds)//-- but not from these accounts
+                          ->whereIn('t_dest.account_id', $accounts->pluck('id')->toArray())// to these accounts (earned)
+                          ->whereNotIn('t_src.account_id', $accounts->pluck('id')->toArray())//-- but not from these accounts
                           ->whereIn(
                 'transaction_types.type', [TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
-            )// earned from these things.
+            )
                           ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
                           ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
                           ->groupBy('categories.id')
-                          ->get(['categories.*', DB::Raw('SUM(`t_dest`.`amount`) AS `earned`')]);
+                          ->groupBy('dateFormatted')
+                          ->get(
+                              [
+                                  'categories.*',
+                                  DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") as `dateFormatted`'),
+                                  DB::Raw('SUM(`t_dest`.`amount`) AS `earned`')
+                              ]
+                          );
 
         return $collection;
 
@@ -564,8 +169,8 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
 
     /**
      * Returns a collection of Categories appended with the amount of money that has been spent
-     * in these categories, based on the $accounts involved, in period X.
-     * The amount earned in category X in period X is saved in field "spent".
+     * in these categories, based on the $accounts involved, in period X, grouped per month.
+     * The amount spent in category X in period X is saved in field "spent".
      *
      * @param $accounts
      * @param $start
@@ -573,15 +178,10 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
      *
      * @return Collection
      */
-    public function spentForAccounts(Collection $accounts, Carbon $start, Carbon $end)
+    public function spentForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end)
     {
-        $accountIds = [];
-        foreach ($accounts as $account) {
-            $accountIds[] = $account->id;
-        }
-
-
-        $collection = Auth::user()->categories()
+        $accountIds = $accounts->pluck('id')->toArray();
+        $query      = Auth::user()->categories()
                           ->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id')
                           ->leftJoin('transaction_journals', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
                           ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
@@ -595,16 +195,105 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
                               $join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0);
                           }
                           )
-                          ->whereIn('t_src.account_id', $accountIds)// from these accounts (spent)
-                          ->whereNotIn('t_dest.account_id', $accountIds)//-- but not from these accounts (spent internally)
                           ->whereIn(
-                'transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
-            )// spent on these things.
+                              'transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
+                          )// spent on these things.
                           ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
                           ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
                           ->groupBy('categories.id')
-                          ->get(['categories.*', DB::Raw('SUM(`t_dest`.`amount`) AS `spent`')]);
+                          ->groupBy('dateFormatted');
+
+        if (count($accountIds) > 0) {
+            $query->whereIn('t_src.account_id', $accountIds)// from these accounts (spent)
+                  ->whereNotIn('t_dest.account_id', $accountIds);//-- but not from these accounts (spent internally)
+        }
+
+        $collection = $query->get(
+            [
+                'categories.*',
+                DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") as `dateFormatted`'),
+                DB::Raw('SUM(`t_src`.`amount`) AS `spent`')
+            ]
+        );
 
         return $collection;
     }
+
+
+    /**
+     * Returns the total amount of money related to transactions without any category connected to
+     * it. Returns either the spent amount.
+     *
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return string
+     */
+    public function sumSpentNoCategory(Collection $accounts, Carbon $start, Carbon $end)
+    {
+        return $this->sumNoCategory($accounts, $start, $end, Query::SPENT);
+    }
+
+    /**
+     * Returns the total amount of money related to transactions without any category connected to
+     * it. Returns either the earned amount.
+     *
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return string
+     */
+    public function sumEarnedNoCategory(Collection $accounts, Carbon $start, Carbon $end)
+    {
+        return $this->sumNoCategory($accounts, $start, $end, Query::EARNED);
+    }
+
+    /**
+     * Returns the total amount of money related to transactions without any category connected to
+     * it. Returns either the earned or the spent amount.
+     *
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     * @param int        $group
+     *
+     * @return string
+     */
+    protected function sumNoCategory(Collection $accounts, Carbon $start, Carbon $end, $group = Query::EARNED)
+    {
+        $accountIds = $accounts->pluck('id')->toArray();
+        if ($group == Query::EARNED) {
+            $types = [TransactionType::DEPOSIT];
+        } else {
+            $types = [TransactionType::WITHDRAWAL];
+        }
+
+        // is withdrawal or transfer AND account_from is in the list of $accounts
+        $query = Auth::user()
+                     ->transactionjournals()
+                     ->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
+                     ->whereNull('category_transaction_journal.id')
+                     ->before($end)
+                     ->after($start)
+                     ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
+                     ->transactionTypes($types);
+        if (count($accountIds) > 0) {
+            $query->whereIn('transactions.account_id', $accountIds);
+        }
+
+
+        $single = $query->first(
+            [
+                DB::Raw('SUM(`transactions`.`amount`) as `sum`')
+            ]
+        );
+        if (!is_null($single)) {
+            return $single->sum;
+        }
+
+        return '0';
+
+    }
 }
diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php
index 34553e7ea3..7de84fe660 100644
--- a/app/Repositories/Category/CategoryRepositoryInterface.php
+++ b/app/Repositories/Category/CategoryRepositoryInterface.php
@@ -3,7 +3,6 @@
 namespace FireflyIII\Repositories\Category;
 
 use Carbon\Carbon;
-use FireflyIII\Models\Category;
 use Illuminate\Support\Collection;
 
 /**
@@ -13,33 +12,11 @@ use Illuminate\Support\Collection;
  */
 interface CategoryRepositoryInterface
 {
-    /**
-     * @param Category $category
-     *
-     * @return int
-     */
-    public function countJournals(Category $category);
 
-    /**
-     * @param Category $category
-     *
-     * @param Carbon   $start
-     * @param Carbon   $end
-     *
-     * @return int
-     */
-    public function countJournalsInRange(Category $category, Carbon $start, Carbon $end);
-
-    /**
-     * @param Category $category
-     *
-     * @return boolean
-     */
-    public function destroy(Category $category);
 
     /**
      * Returns a collection of Categories appended with the amount of money that has been earned
-     * in these categories, based on the $accounts involved, in period X.
+     * in these categories, based on the $accounts involved, in period X, grouped per month.
      * The amount earned in category X in period X is saved in field "earned".
      *
      * @param $accounts
@@ -48,11 +25,45 @@ interface CategoryRepositoryInterface
      *
      * @return Collection
      */
-    public function earnedForAccounts(Collection $accounts, Carbon $start, Carbon $end);
+    public function earnedForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end);
+
+    /**
+     * Returns a list of all the categories belonging to a user.
+     *
+     * @return Collection
+     */
+    public function listCategories();
+
+    /**
+     * This method returns a very special collection for each category:
+     *
+     * category, year, expense/earned, amount
+     *
+     * categories can be duplicated.
+     *
+     * @param Collection $categories
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return Collection
+     */
+    public function listMultiYear(Collection $categories, Collection $accounts, Carbon $start, Carbon $end);
+
+    /**
+     * Returns a list of transaction journals in the range (all types, all accounts) that have no category
+     * associated to them.
+     *
+     * @param Carbon $start
+     * @param Carbon $end
+     *
+     * @return Collection
+     */
+    public function listNoCategory(Carbon $start, Carbon $end);
 
     /**
      * Returns a collection of Categories appended with the amount of money that has been spent
-     * in these categories, based on the $accounts involved, in period X.
+     * in these categories, based on the $accounts involved, in period X, grouped per month.
      * The amount earned in category X in period X is saved in field "spent".
      *
      * @param $accounts
@@ -61,40 +72,11 @@ interface CategoryRepositoryInterface
      *
      * @return Collection
      */
-    public function spentForAccounts(Collection $accounts, Carbon $start, Carbon $end);
+    public function spentForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end);
 
     /**
-     * @return Collection
-     */
-    public function getCategories();
-
-
-    /**
-     * Calculates how much is spent in this period.
-     *
-     * @param Category   $category
-     * @param Collection $accounts
-     * @param Carbon     $start
-     * @param Carbon     $end
-     *
-     * @return string
-     */
-    public function spentInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end);
-
-    /**
-     * Calculate how much is earned in this period.
-     *
-     * @param Category   $category
-     * @param Collection $accounts
-     * @param Carbon     $start
-     * @param Carbon     $end
-     *
-     * @return string
-     */
-    public function earnedInPeriodForAccounts(Category $category, Collection $accounts, Carbon $start, Carbon $end);
-
-    /**
-     * Returns the amount spent without category by accounts in period.
+     * Returns the total amount of money related to transactions without any category connected to
+     * it. Returns either the earned amount.
      *
      * @param Collection $accounts
      * @param Carbon     $start
@@ -102,10 +84,11 @@ interface CategoryRepositoryInterface
      *
      * @return string
      */
-    public function spentNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end);
+    public function sumEarnedNoCategory(Collection $accounts, Carbon $start, Carbon $end);
 
     /**
-     * Returns the amount earned without category by accounts in period.
+     * Returns the total amount of money related to transactions without any category connected to
+     * it. Returns either the spent amount.
      *
      * @param Collection $accounts
      * @param Carbon     $start
@@ -113,137 +96,6 @@ interface CategoryRepositoryInterface
      *
      * @return string
      */
-    public function earnedNoCategoryForAccounts(Collection $accounts, Carbon $start, Carbon $end);
-
-    /**
-     * Corrected for tags.
-     *
-     * @param Carbon $start
-     * @param Carbon $end
-     *
-     * @return array
-     */
-    public function getCategoriesAndExpenses(Carbon $start, Carbon $end);
-
-    /**
-     * @param Category $category
-     *
-     * @return Carbon
-     */
-    public function getFirstActivityDate(Category $category);
-
-    /**
-     * @param Category $category
-     * @param int      $page
-     *
-     * @return Collection
-     */
-    public function getJournals(Category $category, $page);
-
-    /**
-     * @param Category $category
-     * @param int      $page
-     *
-     * @param Carbon   $start
-     * @param Carbon   $end
-     *
-     * @return Collection
-     */
-    public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end);
-
-    /**
-     * @deprecated
-     * This method returns the sum of the journals in the category, optionally
-     * limited by a start or end date.
-     *
-     * @param Category $category
-     * @param Carbon   $start
-     * @param Carbon   $end
-     *
-     * @return string
-     */
-    public function journalsSum(Category $category, Carbon $start = null, Carbon $end = null);
-
-    /**
-     * @param Category $category
-     *
-     * @return Carbon|null
-     */
-    public function getLatestActivity(Category $category);
-
-    /**
-     * @param Carbon $start
-     * @param Carbon $end
-     *
-     * @return Collection
-     */
-    public function getWithoutCategory(Carbon $start, Carbon $end);
-
-    /**
-     * Corrected for tags and list of accounts.
-     *
-     * @param Category       $category
-     * @param \Carbon\Carbon $start
-     * @param \Carbon\Carbon $end
-     * @param Collection     $accounts
-     *
-     * @return string
-     */
-    public function balanceInPeriod(Category $category, Carbon $start, Carbon $end, Collection $accounts);
-
-    /**
-     * @param Category       $category
-     * @param \Carbon\Carbon $start
-     * @param \Carbon\Carbon $end
-     *
-     * @return string
-     */
-    public function spentInPeriod(Category $category, Carbon $start, Carbon $end);
-
-    /**
-     * @param Category       $category
-     * @param \Carbon\Carbon $start
-     * @param \Carbon\Carbon $end
-     *
-     * @return string
-     */
-    public function earnedInPeriod(Category $category, Carbon $start, Carbon $end);
-
-    /**
-     *
-     * Corrected for tags.
-     *
-     * @param Category $category
-     * @param Carbon   $date
-     *
-     * @return float
-     */
-    public function spentOnDaySum(Category $category, Carbon $date);
-
-    /**
-     *
-     * Corrected for tags.
-     *
-     * @param Category $category
-     * @param Carbon   $date
-     *
-     * @return float
-     */
-    public function earnedOnDaySum(Category $category, Carbon $date);
-
-    /**
-     * @param array $data
-     *
-     * @return Category
-     */
-    public function store(array $data);
-
-    /**
-     * @param Category $category
-     * @param array    $data
-     *
-     * @return Category
-     */
-    public function update(Category $category, array $data);
+    public function sumSpentNoCategory(Collection $accounts, Carbon $start, Carbon $end);
 
 }
diff --git a/app/Repositories/Category/SingleCategoryRepository.php b/app/Repositories/Category/SingleCategoryRepository.php
new file mode 100644
index 0000000000..320c78e7f0
--- /dev/null
+++ b/app/Repositories/Category/SingleCategoryRepository.php
@@ -0,0 +1,238 @@
+transactionJournals()->count();
+
+    }
+
+    /**
+     * @param Category $category
+     *
+     * @param Carbon   $start
+     * @param Carbon   $end
+     *
+     * @return int
+     */
+    public function countJournalsInRange(Category $category, Carbon $start, Carbon $end)
+    {
+        return $category->transactionJournals()->before($end)->after($start)->count();
+    }
+
+    /**
+     * @param Category $category
+     *
+     * @return boolean
+     */
+    public function destroy(Category $category)
+    {
+        $category->delete();
+
+        return true;
+    }
+
+    /**
+     * Returns an array with the following key:value pairs:
+     *
+     * yyyy-mm-dd:
+     *
+     * Where yyyy-mm-dd is the date and  is the money earned using DEPOSITS in the $category
+     * from all the users $accounts.
+     *
+     * @param Category $category
+     * @param Carbon   $start
+     * @param Carbon   $end
+     *
+     * @return array
+     */
+    public function earnedPerDay(Category $category, Carbon $start, Carbon $end)
+    {
+        /** @var Collection $query */
+        $query = $category->transactionJournals()
+                          ->transactionTypes([TransactionType::DEPOSIT])
+                          ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
+                          ->where('transactions.amount', '>', 0)
+                          ->before($end)
+                          ->after($start)
+                          ->groupBy('date')->get(['transaction_journals.date as dateFormatted', DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
+
+        $return = [];
+        foreach ($query->toArray() as $entry) {
+            $return[$entry['dateFormatted']] = $entry['sum'];
+        }
+
+        return $return;
+    }
+
+    /**
+     * @param Category $category
+     *
+     * @return Carbon
+     */
+    public function getFirstActivityDate(Category $category)
+    {
+        /** @var TransactionJournal $first */
+        $first = $category->transactionjournals()->orderBy('date', 'ASC')->first();
+        if ($first) {
+            return $first->date;
+        }
+
+        return new Carbon;
+
+    }
+
+    /**
+     * @param Category $category
+     * @param int      $page
+     *
+     * @return Collection
+     */
+    public function getJournals(Category $category, $page)
+    {
+        $offset = $page > 0 ? $page * 50 : 0;
+
+        return $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)
+                        ->orderBy('transaction_journals.date', 'DESC')
+                        ->orderBy('transaction_journals.order', 'ASC')
+                        ->orderBy('transaction_journals.id', 'DESC')
+                        ->get(
+                            ['transaction_journals.*']
+                        );
+
+    }
+
+    /**
+     * @param Category $category
+     * @param int      $page
+     * @param Carbon   $start
+     * @param Carbon   $end
+     *
+     * @return mixed
+     */
+    public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end)
+    {
+        $offset = $page > 0 ? $page * 50 : 0;
+
+        return $category->transactionJournals()
+                        ->after($start)
+                        ->before($end)
+                        ->withRelevantData()->take(50)->offset($offset)
+                        ->orderBy('transaction_journals.date', 'DESC')
+                        ->orderBy('transaction_journals.order', 'ASC')
+                        ->orderBy('transaction_journals.id', 'DESC')
+                        ->get(
+                            ['transaction_journals.*']
+                        );
+    }
+
+
+    /**
+     * @param Category $category
+     *
+     * @return Carbon|null
+     */
+    public function getLatestActivity(Category $category)
+    {
+        $latest = $category->transactionjournals()
+                           ->orderBy('transaction_journals.date', 'DESC')
+                           ->orderBy('transaction_journals.order', 'ASC')
+                           ->orderBy('transaction_journals.id', 'DESC')
+                           ->first();
+        if ($latest) {
+            return $latest->date;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns an array with the following key:value pairs:
+     *
+     * yyyy-mm-dd:
+     *
+     * Where yyyy-mm-dd is the date and  is the money spent using DEPOSITS in the $category
+     * from all the users accounts.
+     *
+     * @param Category $category
+     * @param Carbon   $start
+     * @param Carbon   $end
+     *
+     * @return array
+     */
+    public function spentPerDay(Category $category, Carbon $start, Carbon $end)
+    {
+        /** @var Collection $query */
+        $query = $category->transactionJournals()
+                          ->transactionTypes([TransactionType::WITHDRAWAL])
+                          ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
+                          ->where('transactions.amount', '<', 0)
+                          ->before($end)
+                          ->after($start)
+                          ->groupBy('date')->get(['transaction_journals.date as dateFormatted', DB::Raw('SUM(`transactions`.`amount`) AS `sum`')]);
+
+        $return = [];
+        foreach ($query->toArray() as $entry) {
+            $return[$entry['dateFormatted']] = $entry['sum'];
+        }
+
+        return $return;
+    }
+
+    /**
+     * @param array $data
+     *
+     * @return Category
+     */
+    public function store(array $data)
+    {
+        $newCategory = new Category(
+            [
+                'user_id' => $data['user'],
+                'name'    => $data['name'],
+            ]
+        );
+        $newCategory->save();
+
+        return $newCategory;
+    }
+
+    /**
+     * @param Category $category
+     * @param array    $data
+     *
+     * @return Category
+     */
+    public function update(Category $category, array $data)
+    {
+        // update the account:
+        $category->name = $data['name'];
+        $category->save();
+
+        return $category;
+    }
+
+
+}
\ No newline at end of file
diff --git a/app/Repositories/Category/SingleCategoryRepositoryInterface.php b/app/Repositories/Category/SingleCategoryRepositoryInterface.php
new file mode 100644
index 0000000000..6e6b4b76c2
--- /dev/null
+++ b/app/Repositories/Category/SingleCategoryRepositoryInterface.php
@@ -0,0 +1,122 @@
+
+     *
+     * Where yyyy-mm-dd is the date and  is the money earned using DEPOSITS in the $category
+     * from all the users accounts.
+     *
+     * @param Category $category
+     * @param Carbon   $start
+     * @param Carbon   $end
+     *
+     * @return array
+     */
+    public function earnedPerDay(Category $category, Carbon $start, Carbon $end);
+
+
+    /**
+     * @param Category $category
+     *
+     * @return Carbon
+     */
+    public function getFirstActivityDate(Category $category);
+
+    /**
+     * @param Category $category
+     * @param int      $page
+     *
+     * @return Collection
+     */
+    public function getJournals(Category $category, $page);
+
+
+    /**
+     * @param Category $category
+     * @param int      $page
+     *
+     * @param Carbon   $start
+     * @param Carbon   $end
+     *
+     * @return Collection
+     */
+    public function getJournalsInRange(Category $category, $page, Carbon $start, Carbon $end);
+
+    /**
+     * @param Category $category
+     *
+     * @return Carbon|null
+     */
+    public function getLatestActivity(Category $category);
+
+    /**
+     * Returns an array with the following key:value pairs:
+     *
+     * yyyy-mm-dd:
+     *
+     * Where yyyy-mm-dd is the date and  is the money spent using WITHDRAWALS in the $category
+     * from all the users accounts.
+     *
+     * @param Category $category
+     * @param Carbon   $start
+     * @param Carbon   $end
+     *
+     * @return array
+     */
+    public function spentPerDay(Category $category, Carbon $start, Carbon $end);
+
+
+    /**
+     * @param array $data
+     *
+     * @return Category
+     */
+    public function store(array $data);
+
+    /**
+     * @param Category $category
+     * @param array    $data
+     *
+     * @return Category
+     */
+    public function update(Category $category, array $data);
+}
\ No newline at end of file
diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php
index 061d9c39a3..c3d4d08c5e 100644
--- a/app/Repositories/Journal/JournalRepository.php
+++ b/app/Repositories/Journal/JournalRepository.php
@@ -13,7 +13,6 @@ use FireflyIII\Models\Tag;
 use FireflyIII\Models\Transaction;
 use FireflyIII\Models\TransactionJournal;
 use FireflyIII\Models\TransactionType;
-use FireflyIII\Support\CacheProperties;
 use Illuminate\Pagination\LengthAwarePaginator;
 use Illuminate\Support\Collection;
 use Log;
@@ -45,14 +44,7 @@ class JournalRepository implements JournalRepositoryInterface
      */
     public function first()
     {
-        $cache = new CacheProperties;
-        $cache->addProperty('user-first-journal');
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
         $entry = Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first(['transaction_journals.*']);
-        $cache->store($entry);
 
         return $entry;
     }
diff --git a/app/Repositories/Shared/ComponentRepository.php b/app/Repositories/Shared/ComponentRepository.php
index f9584ef72d..aa604b2859 100644
--- a/app/Repositories/Shared/ComponentRepository.php
+++ b/app/Repositories/Shared/ComponentRepository.php
@@ -4,9 +4,7 @@ namespace FireflyIII\Repositories\Shared;
 
 use Carbon\Carbon;
 use DB;
-use FireflyIII\Models\Account;
 use FireflyIII\Models\TransactionType;
-use FireflyIII\Support\CacheProperties;
 use Illuminate\Support\Collection;
 
 /**
@@ -27,23 +25,7 @@ class ComponentRepository
      */
     protected function commonBalanceInPeriod($object, Carbon $start, Carbon $end, Collection $accounts)
     {
-        $cache = new CacheProperties; // we must cache this.
-        $cache->addProperty($object->id);
-        $cache->addProperty(get_class($object));
-        $cache->addProperty($start);
-        $cache->addProperty($end);
-        $cache->addProperty($accounts);
-        $cache->addProperty('balanceInPeriodList');
-
-        if ($cache->has()) {
-            return $cache->get(); // @codeCoverageIgnore
-        }
-
-        $ids = [];
-        /** @var Account $account */
-        foreach ($accounts as $account) {
-            $ids[] = $account->id;
-        }
+        $ids = $accounts->pluck('id')->toArray();
 
 
         $entry  = $object->transactionjournals()
@@ -56,8 +38,6 @@ class ComponentRepository
                          ->first([DB::Raw('SUM(`transactions`.`amount`) as `journalAmount`')]);
         $amount = $entry->journalAmount;
 
-        $cache->store($amount);
-
         return $amount;
     }
 }
diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php
index 446aa85755..ccdb2d9dae 100644
--- a/app/Repositories/Tag/TagRepository.php
+++ b/app/Repositories/Tag/TagRepository.php
@@ -5,11 +5,12 @@ namespace FireflyIII\Repositories\Tag;
 
 use Auth;
 use Carbon\Carbon;
+use DB;
 use FireflyIII\Models\Account;
 use FireflyIII\Models\Tag;
 use FireflyIII\Models\TransactionJournal;
 use FireflyIII\Models\TransactionType;
-use FireflyIII\Support\CacheProperties;
+use Illuminate\Database\Query\JoinClause;
 use Illuminate\Support\Collection;
 
 /**
@@ -22,7 +23,6 @@ class TagRepository implements TagRepositoryInterface
 
 
     /**
-     * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five.
      *
      * @param TransactionJournal $journal
      * @param Tag                $tag
@@ -31,7 +31,6 @@ class TagRepository implements TagRepositoryInterface
      */
     public function connect(TransactionJournal $journal, Tag $tag)
     {
-
         /*
          * Already connected:
          */
@@ -94,6 +93,49 @@ class TagRepository implements TagRepositoryInterface
         return $amount;
     }
 
+
+    /**
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return Collection
+     */
+    public function allCoveredByBalancingActs(Collection $accounts, Carbon $start, Carbon $end)
+    {
+        $ids = $accounts->pluck('id')->toArray();
+        $set = Auth::user()->tags()
+                   ->leftJoin('tag_transaction_journal', 'tag_transaction_journal.tag_id', '=', 'tags.id')
+                   ->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
+                   ->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
+                   ->leftJoin(
+                       'transactions AS t_from', function (JoinClause $join) {
+                       $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0);
+                   }
+                   )
+                   ->leftJoin(
+                       'transactions AS t_to', function (JoinClause $join) {
+                       $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0);
+                   }
+                   )
+                   ->where('tags.tagMode', 'balancingAct')
+                   ->where('transaction_types.type', TransactionType::TRANSFER)
+                   ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
+                   ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
+                   ->whereNull('transaction_journals.deleted_at')
+                   ->whereIn('t_from.account_id', $ids)
+                   ->whereIn('t_to.account_id', $ids)
+                   ->groupBy('t_to.account_id')
+                   ->get(
+                       [
+                           't_to.account_id',
+                           DB::Raw('SUM(`t_to`.`amount`) as `sum`')
+                       ]
+                   );
+
+        return $set;
+    }
+
     /**
      * @param Tag $tag
      *
@@ -112,13 +154,6 @@ class TagRepository implements TagRepositoryInterface
      */
     public function get()
     {
-        $cache = new CacheProperties;
-        $cache->addProperty('tags-list');
-
-        if ($cache->has()) {
-            return $cache->get();
-        }
-
         /** @var Collection $tags */
         $tags = Auth::user()->tags()->get();
         $tags = $tags->sortBy(
@@ -127,8 +162,6 @@ class TagRepository implements TagRepositoryInterface
             }
         );
 
-        $cache->store($tags);
-
         return $tags;
     }
 
diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php
index 75c6a4df96..35f5c0f83c 100644
--- a/app/Repositories/Tag/TagRepositoryInterface.php
+++ b/app/Repositories/Tag/TagRepositoryInterface.php
@@ -16,7 +16,14 @@ use Illuminate\Support\Collection;
  */
 interface TagRepositoryInterface
 {
-
+    /**
+     * @param Collection $accounts
+     * @param Carbon     $start
+     * @param Carbon     $end
+     *
+     * @return Collection
+     */
+    public function allCoveredByBalancingActs(Collection $accounts, Carbon $start, Carbon $end);
 
     /**
      * @param TransactionJournal $journal
diff --git a/app/Sql/Query.php b/app/Sql/Query.php
new file mode 100644
index 0000000000..b0989a82d7
--- /dev/null
+++ b/app/Sql/Query.php
@@ -0,0 +1,17 @@
+ 123,2
-     * @param Account $account
-     * @param Carbon  $start
-     * @param Carbon  $end
+     *
+     * @param \FireflyIII\Models\Account $account
+     * @param \Carbon\Carbon             $start
+     * @param \Carbon\Carbon             $end
      *
      * @return array
      */
@@ -126,7 +127,7 @@ class Steam
      * @param array          $ids
      * @param \Carbon\Carbon $date
      *
-     * @return float
+     * @return array
      */
     public function balancesById(array $ids, Carbon $date)
     {
diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php
index bf2a1c725a..4534c3f86b 100644
--- a/app/Support/Twig/General.php
+++ b/app/Support/Twig/General.php
@@ -24,7 +24,6 @@ class General extends Twig_Extension
 
 
     /**
-     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      * @return array
      */
     public function getFilters()
diff --git a/app/User.php b/app/User.php
index 2fd0ae8fdf..d1924bbccc 100644
--- a/app/User.php
+++ b/app/User.php
@@ -1,43 +1,55 @@
 =5.6.4",
         "davejamesmiller/laravel-breadcrumbs": "~3.0",
         "watson/validating": "~1.0",
         "doctrine/dbal": "~2.5",
diff --git a/composer.lock b/composer.lock
index 262eefd6f6..943a47391f 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "4fc3975430321d76dd859916a9dcbfb0",
-    "content-hash": "ad9d33604e0a88fabea148474769942d",
+    "hash": "35ee47a928ca01f96176d94b1bdbd800",
+    "content-hash": "1d6d8db5b01d70aed4926680b5236331",
     "packages": [
         {
             "name": "classpreloader/classpreloader",
@@ -269,33 +269,33 @@
         },
         {
             "name": "doctrine/cache",
-            "version": "v1.5.4",
+            "version": "v1.6.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/cache.git",
-                "reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136"
+                "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/cache/zipball/47cdc76ceb95cc591d9c79a36dc3794975b5d136",
-                "reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136",
+                "url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6",
+                "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.2"
+                "php": "~5.5|~7.0"
             },
             "conflict": {
                 "doctrine/common": ">2.2,<2.4"
             },
             "require-dev": {
-                "phpunit/phpunit": ">=3.7",
+                "phpunit/phpunit": "~4.8|~5.0",
                 "predis/predis": "~1.0",
                 "satooshi/php-coveralls": "~0.6"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.5.x-dev"
+                    "dev-master": "1.6.x-dev"
                 }
             },
             "autoload": {
@@ -335,7 +335,7 @@
                 "cache",
                 "caching"
             ],
-            "time": "2015-12-19 05:03:47"
+            "time": "2015-12-31 16:37:02"
         },
         {
             "name": "doctrine/collections",
@@ -478,16 +478,16 @@
         },
         {
             "name": "doctrine/dbal",
-            "version": "v2.5.3",
+            "version": "v2.5.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/dbal.git",
-                "reference": "2fbcea96eae34a53183377cdbb0b9bec33974648"
+                "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/2fbcea96eae34a53183377cdbb0b9bec33974648",
-                "reference": "2fbcea96eae34a53183377cdbb0b9bec33974648",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/abbdfd1cff43a7b99d027af3be709bc8fc7d4769",
+                "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769",
                 "shasum": ""
             },
             "require": {
@@ -545,7 +545,7 @@
                 "persistence",
                 "queryobject"
             ],
-            "time": "2015-12-25 16:28:24"
+            "time": "2016-01-05 22:11:12"
         },
         {
             "name": "doctrine/inflector",
@@ -668,42 +668,6 @@
             ],
             "time": "2014-09-09 13:34:57"
         },
-        {
-            "name": "grumpydictator/gchart",
-            "version": "1.1.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/JC5/gchart.git",
-                "reference": "0b1de3e045aaf19bb17658d3e81db706d6945e5d"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/JC5/gchart/zipball/0b1de3e045aaf19bb17658d3e81db706d6945e5d",
-                "reference": "0b1de3e045aaf19bb17658d3e81db706d6945e5d",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.3.0"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-0": {
-                    "Grumpydictator\\Gchart": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "James Cole",
-                    "email": "TheGrumpyDictator@gmail.com"
-                }
-            ],
-            "description": "GChart is a small package that allows you to easily generate data for the Google Charts API.",
-            "time": "2015-12-15 21:07:33"
-        },
         {
             "name": "illuminate/html",
             "version": "v5.0.0",
@@ -897,16 +861,16 @@
         },
         {
             "name": "laravel/framework",
-            "version": "v5.1.27",
+            "version": "v5.1.28",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "b16f80878fd3603022d3c84593397cedd9af0bcf"
+                "reference": "3f0fd27939dfdafb1e50058423cd24e640894ba2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/b16f80878fd3603022d3c84593397cedd9af0bcf",
-                "reference": "b16f80878fd3603022d3c84593397cedd9af0bcf",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/3f0fd27939dfdafb1e50058423cd24e640894ba2",
+                "reference": "3f0fd27939dfdafb1e50058423cd24e640894ba2",
                 "shasum": ""
             },
             "require": {
@@ -1021,7 +985,7 @@
                 "framework",
                 "laravel"
             ],
-            "time": "2015-12-17 20:35:38"
+            "time": "2015-12-31 17:41:30"
         },
         {
             "name": "league/commonmark",
@@ -3065,6 +3029,8 @@
     },
     "prefer-stable": false,
     "prefer-lowest": false,
-    "platform": [],
+    "platform": {
+        "php": ">=5.6.4"
+    },
     "platform-dev": []
 }
diff --git a/config/firefly.php b/config/firefly.php
index a7696da971..a6f6f68ba7 100644
--- a/config/firefly.php
+++ b/config/firefly.php
@@ -2,7 +2,7 @@
 
 return [
     'chart'              => 'chartjs',
-    'version'            => '3.5.6.1',
+    'version'            => '3.6.0',
     'index_periods'      => ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom'],
     'budget_periods'     => ['daily', 'weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
     'csv_import_enabled' => true,
diff --git a/config/twigbridge.php b/config/twigbridge.php
index 04826b5504..c4e6d2c4ba 100644
--- a/config/twigbridge.php
+++ b/config/twigbridge.php
@@ -94,7 +94,7 @@ return [
             'TwigBridge\Extension\Laravel\Dump',
             'TwigBridge\Extension\Laravel\Input',
             'TwigBridge\Extension\Laravel\Session',
-            'TwigBridge\Extension\Laravel\String',
+            'TwigBridge\Extension\Laravel\Str',
             'TwigBridge\Extension\Laravel\Translator',
             'TwigBridge\Extension\Laravel\Url',
 
diff --git a/public/js/piggy-banks.js b/public/js/piggy-banks.js
index 4b8aa3ab6b..72789f59b2 100644
--- a/public/js/piggy-banks.js
+++ b/public/js/piggy-banks.js
@@ -15,7 +15,7 @@ $(function () {
     $('.removeMoney').on('click', removeMoney);
 
     if (typeof(lineChart) === 'function' && typeof(piggyBankID) !== 'undefined') {
-        lineChart('chart/piggyBank/' + piggyBankID, 'piggy-bank-history');
+        lineChart('chart/piggy-bank/' + piggyBankID, 'piggy-bank-history');
     }
 
     $('#sortable tbody').sortable(
diff --git a/public/js/reports/default/multi-year.js b/public/js/reports/default/multi-year.js
index d43475f6c1..61931c1d78 100644
--- a/public/js/reports/default/multi-year.js
+++ b/public/js/reports/default/multi-year.js
@@ -5,6 +5,11 @@ $(function () {
     "use strict";
     drawChart();
 
+    // click open the top X income list:
+    $('#showIncomes').click(showIncomes);
+    // click open the top X expense list:
+    $('#showExpenses').click(showExpenses);
+
 });
 
 
@@ -152,4 +157,48 @@ function readCookie(name) {
 
 function eraseCookie(name) {
     createCookie(name, "", -1);
+}
+
+
+
+function showIncomes() {
+    "use strict";
+    if (incomeRestShow) {
+        // hide everything, make button say "show"
+        $('#showIncomes').text(showTheRest);
+        $('.incomesCollapsed').removeClass('in').addClass('out');
+
+        // toggle:
+        incomeRestShow = false;
+    } else {
+        // show everything, make button say "hide".
+        $('#showIncomes').text(hideTheRest);
+        $('.incomesCollapsed').removeClass('out').addClass('in');
+
+        // toggle:
+        incomeRestShow = true;
+    }
+
+    return false;
+}
+
+function showExpenses() {
+    "use strict";
+    if (expenseRestShow) {
+        // hide everything, make button say "show"
+        $('#showExpenses').text(showTheRestExpense);
+        $('.expenseCollapsed').removeClass('in').addClass('out');
+
+        // toggle:
+        expenseRestShow = false;
+    } else {
+        // show everything, make button say "hide".
+        $('#showExpenses').text(hideTheRestExpense);
+        $('.expenseCollapsed').removeClass('out').addClass('in');
+
+        // toggle:
+        expenseRestShow = true;
+    }
+
+    return false;
 }
\ No newline at end of file
diff --git a/resources/twig/budgets/show.twig b/resources/twig/budgets/show.twig
index 173b9a1d4e..666b874300 100644
--- a/resources/twig/budgets/show.twig
+++ b/resources/twig/budgets/show.twig
@@ -44,47 +44,43 @@
             {% endif %}
 
             {% for limit in limits %}
-                {% for rep in limit.limitRepetitions %}
-                    {% set spentInRep = spentInRepetition(rep) %}
-                    
- -
-
-
- {{ 'amount'|_ }}: {{ rep.amount|formatAmount }} -
-
- {{ 'spent'|_ }}: {{ spentInRep|formatAmount }} -
+
+ +
+
+
+ {{ 'amount'|_ }}: {{ limit.amount|formatAmount }}
-
-
- {% set overspent = rep.amount + spentInRep < 0 %} +
+ {{ 'spent'|_ }}: {{ limit.spent|formatAmount }} +
+
+
+
+ {% set overspent = limit.amount + limit.spent < 0 %} - {% if overspent %} - {% set pct = (spentInRep != 0 ? (rep.amount / (spentInRep*-1))*100 : 0) %} -
-
-
-
- {% else %} - {% set amount = rep.amount %} - {% set pct = (amount != 0 ? (((spentInRep*-1) / amount)*100) : 0) %} -
-
-
- {% endif %} -
+ {% if overspent %} + {% set pct = (limit.spent != 0 ? (limit.amount / (limit.spent*-1))*100 : 0) %} +
+
+
+
+ {% else %} + {% set pct = (limit.amount != 0 ? (((limit.spent*-1) / limit.amount)*100) : 0) %} +
+
+
+ {% endif %}
- {% endfor %} +
{% endfor %} {% if limits|length == 1 %} diff --git a/resources/twig/emails/registered-html.twig b/resources/twig/emails/registered-html.twig index d05f6901d0..532322e75a 100644 --- a/resources/twig/emails/registered-html.twig +++ b/resources/twig/emails/registered-html.twig @@ -61,6 +61,7 @@ + diff --git a/resources/twig/list/journals-tiny.twig b/resources/twig/list/journals-tiny.twig index 4265f62298..ca488f91fa 100644 --- a/resources/twig/list/journals-tiny.twig +++ b/resources/twig/list/journals-tiny.twig @@ -1,7 +1,7 @@
+ +
+
+ {% include 'reports/partials/accounts.twig' %} + {% include 'reports/partials/income-vs-expenses.twig' %} +
+
+ + {% include 'reports/partials/income.twig' %} +
+
+ + {% include 'reports/partials/expenses.twig' %} +
+
+ {% for account in accounts %}