From 1b4edae4d9de8bd91170dec71266d8c776fee47a Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Dec 2017 18:22:29 +0100 Subject: [PATCH] Final code for #384 --- .../Chart/ExpenseReportController.php | 269 ++++++++++++++++++ .../Controllers/Report/ExpenseController.php | 81 +++++- config/firefly.php | 2 +- public/js/ff/reports/account/all.js | 57 ---- public/js/ff/reports/account/month.js | 3 +- public/js/ff/reports/all.js | 136 +++++++++ public/js/ff/reports/audit/all.js | 32 --- public/js/ff/reports/budget/all.js | 20 -- public/js/ff/reports/category/all.js | 20 -- public/js/ff/reports/default/all.js | 105 ------- public/js/ff/reports/index.js | 30 -- public/js/ff/reports/tag/all.js | 20 -- resources/views/reports/account/report.twig | 2 +- resources/views/reports/audit/report.twig | 1 + resources/views/reports/budget/month.twig | 2 +- resources/views/reports/category/month.twig | 2 +- resources/views/reports/default/month.twig | 1 + .../views/reports/default/multi-year.twig | 1 + resources/views/reports/default/year.twig | 2 +- resources/views/reports/index.twig | 1 + .../views/reports/partials/exp-budgets.twig | 4 +- .../reports/partials/exp-categories.twig | 6 +- .../reports/partials/exp-not-grouped.twig | 4 +- .../reports/partials/top-transactions.twig | 24 ++ resources/views/reports/tag/month.twig | 3 +- routes/web.php | 4 +- 26 files changed, 526 insertions(+), 306 deletions(-) create mode 100644 app/Http/Controllers/Chart/ExpenseReportController.php delete mode 100644 public/js/ff/reports/account/all.js create mode 100644 public/js/ff/reports/all.js delete mode 100644 public/js/ff/reports/budget/all.js delete mode 100644 public/js/ff/reports/category/all.js delete mode 100644 public/js/ff/reports/tag/all.js create mode 100644 resources/views/reports/partials/top-transactions.twig diff --git a/app/Http/Controllers/Chart/ExpenseReportController.php b/app/Http/Controllers/Chart/ExpenseReportController.php new file mode 100644 index 0000000000..acc7485d1c --- /dev/null +++ b/app/Http/Controllers/Chart/ExpenseReportController.php @@ -0,0 +1,269 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Chart; + +use Carbon\Carbon; +use FireflyIII\Generator\Chart\Basic\GeneratorInterface; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Support\CacheProperties; +use Illuminate\Support\Collection; +use Navigation; +use Response; + +/** + * Separate controller because many helper functions are shared. + * + * Class ExpenseReportController + */ +class ExpenseReportController extends Controller +{ + /** @var AccountRepositoryInterface */ + protected $accountRepository; + /** @var GeneratorInterface */ + protected $generator; + + /** + * + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->generator = app(GeneratorInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); + + return $next($request); + } + ); + } + + + /** + * @param Collection $accounts + * @param Collection $expense + * @param Carbon $start + * @param Carbon $end + * + * @return \Illuminate\Http\JsonResponse + */ + public function mainChart(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + + $cache = new CacheProperties; + $cache->addProperty('chart.expense.report.main'); + $cache->addProperty($accounts); + $cache->addProperty($expense); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + // return Response::json($cache->get()); // @codeCoverageIgnore + } + + $format = Navigation::preferredCarbonLocalizedFormat($start, $end); + $function = Navigation::preferredEndOfPeriod($start, $end); + $chartData = []; + $currentStart = clone $start; + $combined = $this->combineAccounts($expense); + + // make "all" set: + $all = new Collection; + foreach ($combined as $name => $combi) { + $all = $all->merge($combi); + } + + // prep chart data: + foreach ($combined as $name => $combi) { + // first is always expense account: + /** @var Account $exp */ + $exp = $combi->first(); + $chartData[$exp->id . '-in'] = [ + 'label' => $name . ' (' . strtolower(strval(trans('firefly.income'))) . ')', + 'type' => 'bar', + 'yAxisID' => 'y-axis-0', + 'entries' => [], + ]; + $chartData[$exp->id . '-out'] = [ + 'label' => $name . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')', + 'type' => 'bar', + 'yAxisID' => 'y-axis-0', + 'entries' => [], + ]; + // total in, total out: + $chartData[$exp->id . '-total-in'] = [ + 'label' => $name . ' (' . strtolower(strval(trans('firefly.sum_of_income'))) . ')', + 'type' => 'line', + 'fill' => false, + 'yAxisID' => 'y-axis-1', + 'entries' => [], + ]; + $chartData[$exp->id . '-total-out'] = [ + 'label' => $name . ' (' . strtolower(strval(trans('firefly.sum_of_expenses'))) . ')', + 'type' => 'line', + 'fill' => false, + 'yAxisID' => 'y-axis-1', + 'entries' => [], + ]; + } + + $sumOfIncome = []; + $sumOfExpense = []; + + while ($currentStart < $end) { + $currentEnd = clone $currentStart; + $currentEnd = $currentEnd->$function(); + + // get expenses grouped by opposing name: + $expenses = $this->groupByName($this->getExpenses($accounts, $all, $currentStart, $currentEnd)); + $income = $this->groupByName($this->getIncome($accounts, $all, $currentStart, $currentEnd)); + $label = $currentStart->formatLocalized($format); + + foreach ($combined as $name => $combi) { + // first is always expense account: + /** @var Account $exp */ + $exp = $combi->first(); + $labelIn = $exp->id . '-in'; + $labelOut = $exp->id . '-out'; + $labelSumIn = $exp->id . '-total-in'; + $labelSumOut = $exp->id . '-total-out'; + $currentIncome = $income[$name] ?? '0'; + $currentExpense = $expenses[$name] ?? '0'; + + // add to sum: + $sumOfIncome[$exp->id] = $sumOfIncome[$exp->id] ?? '0'; + $sumOfExpense[$exp->id] = $sumOfExpense[$exp->id] ?? '0'; + $sumOfIncome[$exp->id] = bcadd($sumOfIncome[$exp->id], $currentIncome); + $sumOfExpense[$exp->id] = bcadd($sumOfExpense[$exp->id], $currentExpense); + + // add to chart: + $chartData[$labelIn]['entries'][$label] = $currentIncome; + $chartData[$labelOut]['entries'][$label] = $currentExpense; + $chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$exp->id]; + $chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$exp->id]; + } + $currentStart = clone $currentEnd; + $currentStart->addDay(); + } + // remove all empty entries to prevent cluttering: + $newSet = []; + foreach ($chartData as $key => $entry) { + if (0 === !array_sum($entry['entries'])) { + $newSet[$key] = $chartData[$key]; + } + } + if (0 === count($newSet)) { + $newSet = $chartData; // @codeCoverageIgnore + } + $data = $this->generator->multiSet($newSet); + $cache->store($data); + + return Response::json($data); + } + + /** + * @param Collection $accounts + * + * @return array + */ + protected function combineAccounts(Collection $accounts): array + { + $combined = []; + /** @var Account $expenseAccount */ + foreach ($accounts as $expenseAccount) { + $collection = new Collection; + $collection->push($expenseAccount); + + $revenue = $this->accountRepository->findByName($expenseAccount->name, [AccountType::REVENUE]); + if (!is_null($revenue->id)) { + $collection->push($revenue); + } + $combined[$expenseAccount->name] = $collection; + } + + return $combined; + } + + /** + * @param Collection $accounts + * @param Collection $opposing + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function getExpenses(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): Collection + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setOpposingAccounts($opposing); + + $transactions = $collector->getJournals(); + + return $transactions; + } + + /** + * @param Collection $accounts + * @param Collection $opposing + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function getIncome(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): Collection + { + /** @var JournalCollectorInterface $collector */ + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setOpposingAccounts($opposing); + + $transactions = $collector->getJournals(); + + return $transactions; + } + + /** + * @param Collection $set + * + * @return array + */ + private function groupByName(Collection $set): array + { + // group by opposing account name. + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $name = $transaction->opposing_account_name; + $grouped[$name] = $grouped[$name] ?? '0'; + $grouped[$name] = bcadd($transaction->transaction_amount, $grouped[$name]); + } + + return $grouped; + } +} diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index b23fbf8cf8..26566c82ac 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -28,6 +28,7 @@ use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\CacheProperties; @@ -88,13 +89,13 @@ class ExpenseController extends Controller $all = $all->merge($combi); } // now find spent / earned: - $spent = $this->spentByBudget($accounts, $all, $start, $end); + $spent = $this->spentByBudget($accounts, $all, $start, $end); // join arrays somehow: $together = []; foreach ($spent as $categoryId => $spentInfo) { if (!isset($together[$categoryId])) { $together[$categoryId]['spent'] = $spentInfo; - $together[$categoryId]['budget'] = $spentInfo['name']; + $together[$categoryId]['budget'] = $spentInfo['name']; $together[$categoryId]['grand_total'] = '0'; } $together[$categoryId]['grand_total'] = bcadd($spentInfo['grand_total'], $together[$categoryId]['grand_total']); @@ -150,7 +151,7 @@ class ExpenseController extends Controller unset($spentInfo); foreach ($earned as $categoryId => $earnedInfo) { if (!isset($together[$categoryId])) { - $together[$categoryId]['earned'] = $earnedInfo; + $together[$categoryId]['earned'] = $earnedInfo; $together[$categoryId]['category'] = $earnedInfo['name']; $together[$categoryId]['grand_total'] = '0'; } @@ -210,6 +211,76 @@ class ExpenseController extends Controller } + public function topExpense(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + // Properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expense-budget'); + $cache->addProperty($accounts->pluck('id')->toArray()); + $cache->addProperty($expense->pluck('id')->toArray()); + if ($cache->has()) { + //return $cache->get(); // @codeCoverageIgnore + } + $combined = $this->combineAccounts($expense); + $all = new Collection; + foreach ($combined as $name => $combi) { + $all = $all->merge($combi); + } + // get all expenses in period: + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts); + $collector->setOpposingAccounts($all); + $set = $collector->getJournals(); + $sorted = $set->sortBy( + function (Transaction $transaction) { + return floatval($transaction->transaction_amount); + } + ); + $result = view('reports.partials.top-transactions', compact('sorted'))->render(); + $cache->store($result); + + return $result; + } + + + + public function topIncome(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + // Properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expense-budget'); + $cache->addProperty($accounts->pluck('id')->toArray()); + $cache->addProperty($expense->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + $combined = $this->combineAccounts($expense); + $all = new Collection; + foreach ($combined as $name => $combi) { + $all = $all->merge($combi); + } + // get all expenses in period: + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($accounts); + $collector->setOpposingAccounts($all); + $set = $collector->getJournals(); + $sorted = $set->sortByDesc( + function (Transaction $transaction) { + return floatval($transaction->transaction_amount); + } + ); + $result = view('reports.partials.top-transactions', compact('sorted'))->render(); + $cache->store($result); + + return $result; + } + /** * @param Collection $accounts * @@ -348,7 +419,7 @@ class ExpenseController extends Controller $sum = []; // loop to support multi currency foreach ($set as $transaction) { - $currencyId = $transaction->transaction_currency_id; + $currencyId = $transaction->transaction_currency_id; $budgetName = $transaction->transaction_budget_name; $budgetId = intval($transaction->transaction_budget_id); // if null, grab from journal: @@ -369,7 +440,7 @@ class ExpenseController extends Controller 'per_currency' => [ $currencyId => [ 'sum' => '0', - 'budget' => [ + 'budget' => [ 'id' => $budgetId, 'name' => $budgetName, ], diff --git a/config/firefly.php b/config/firefly.php index 0ecf8112a6..05c58b89d6 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -54,7 +54,7 @@ return [ 'import_pre' => [ 'bunq' => 'FireflyIII\Support\Import\Prerequisites\BunqPrerequisites', 'spectre' => 'FireflyIII\Support\Import\Prerequisites\SpectrePrerequisites', - 'plaid' => 'FireflyIII\Support\Import\Prerequisites\PlairPrerequisites', + 'plaid' => 'FireflyIII\Support\Import\Prerequisites\PlaidPrerequisites', ], 'import_info' => [ 'bunq' => 'FireflyIII\Support\Import\Information\BunqInformation', diff --git a/public/js/ff/reports/account/all.js b/public/js/ff/reports/account/all.js deleted file mode 100644 index 539b599256..0000000000 --- a/public/js/ff/reports/account/all.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * all.js - * Copyright (c) 2017 thegrumpydictator@gmail.com - * - * This file is part of Firefly III. - * - * Firefly III is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Firefly III is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Firefly III. If not, see . - */ - -function loadAjaxPartial(holder, uri) { - "use strict"; - $.get(uri).done(function (data) { - displayAjaxPartial(data, holder); - }).fail(function () { - failAjaxPartial(uri, holder); - }); -} - -function failAjaxPartial(uri, holder) { - "use strict"; - var holderObject = $('#' + holder); - holderObject.parent().find('.overlay').remove(); - holderObject.addClass('general-chart-error'); - -} - -function displayAjaxPartial(data, holder) { - "use strict"; - var obj = $('#' + holder); - obj.html(data); - obj.parent().find('.overlay').remove(); - - // call some often needed recalculations and what-not: - - // find a sortable table and make it sortable: - if (typeof $.bootstrapSortable === "function") { - $.bootstrapSortable(true); - } - - // find the info click things and respond to them: - triggerInfoClick(); - - // trigger list thing - listLengthInitial(); - -} \ No newline at end of file diff --git a/public/js/ff/reports/account/month.js b/public/js/ff/reports/account/month.js index 099cf9a906..a3068ce4ab 100644 --- a/public/js/ff/reports/account/month.js +++ b/public/js/ff/reports/account/month.js @@ -36,5 +36,6 @@ function drawChart() { // month view: // draw account chart - lineChart(mainUri, 'in-out-chart'); + // month view: + doubleYChart(mainUri, 'in-out-chart'); } \ No newline at end of file diff --git a/public/js/ff/reports/all.js b/public/js/ff/reports/all.js new file mode 100644 index 0000000000..493a82fe4d --- /dev/null +++ b/public/js/ff/reports/all.js @@ -0,0 +1,136 @@ +function loadAjaxPartial(holder, uri) { + "use strict"; + $.get(uri).done(function (data) { + displayAjaxPartial(data, holder); + }).fail(function () { + failAjaxPartial(uri, holder); + }); +} + +function failAjaxPartial(uri, holder) { + "use strict"; + var holderObject = $('#' + holder); + holderObject.parent().find('.overlay').remove(); + holderObject.addClass('general-chart-error'); + +} + + +function createCookie(name, value, days) { + "use strict"; + var expires; + + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toGMTString(); + } else { + expires = ""; + } + document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; +} + +function readCookie(name) { + "use strict"; + var nameEQ = encodeURIComponent(name) + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) === ' ') { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) === 0) { + return decodeURIComponent(c.substring(nameEQ.length, c.length)); + } + } + return null; +} + + +function triggerInfoClick() { + "use strict"; + // find the little info buttons and respond to them. + $('.firefly-info-button').unbind('click').click(clickInfoButton); +} + +function clickInfoButton(e) { + "use strict"; + // find all data tags, regardless of what they are: + var element = $(e.target); + var attributes = element.data(); + + // set wait cursor + $('body').addClass('waiting'); + + // add some more elements: + attributes.startDate = startDate; + attributes.endDate = endDate; + attributes.accounts = accountIds; + + $.getJSON('popup/general', {attributes: attributes}).done(respondInfoButton).fail(errorInfoButton); +} + +function errorInfoButton() { + "use strict"; + // remove wait cursor + $('body').removeClass('waiting'); + alert('Apologies. The requested data is not (yet) available.'); +} + +function respondInfoButton(data) { + "use strict"; + // remove wait cursor + $('body').removeClass('waiting'); + $('#defaultModal').empty().html(data.html).modal('show'); + +} + +function displayAjaxPartial(data, holder) { + "use strict"; + var obj = $('#' + holder); + obj.html(data); + obj.parent().find('.overlay').remove(); + + // call some often needed recalculations and what-not: + + // find a sortable table and make it sortable: + if (typeof $.bootstrapSortable === "function") { + $.bootstrapSortable(true); + } + + // find the info click things and respond to them: + triggerInfoClick(); + + // trigger list thing + listLengthInitial(); + + // budget thing in year and multi year report: + $('.budget-chart-activate').unbind('click').on('click', clickBudgetChart); + + // category thing in year and multi year report: + $('.category-chart-activate').unbind('click').on('click', clickCategoryChart); +} + +function clickCategoryChart(e) { + "use strict"; + var link = $(e.target); + var categoryId = link.data('category'); + $('#category_help').remove(); + + var URL = 'chart/category/report-period/' + categoryId + '/' + accountIds + '/' + startDate + '/' + endDate; + var container = 'category_chart'; + columnChart(URL, container); + return false; +} + +function clickBudgetChart(e) { + "use strict"; + var link = $(e.target); + var budgetId = link.data('budget'); + $('#budget_help').remove(); + + var URL = 'chart/budget/period/' + budgetId + '/' + accountIds + '/' + startDate + '/' + endDate; + var container = 'budget_chart'; + columnChart(URL, container); + return false; +} \ No newline at end of file diff --git a/public/js/ff/reports/audit/all.js b/public/js/ff/reports/audit/all.js index 52f5dc9320..cfc4913105 100644 --- a/public/js/ff/reports/audit/all.js +++ b/public/js/ff/reports/audit/all.js @@ -86,35 +86,3 @@ function showOnlyColumns(checkboxes) { } } } - - -function createCookie(name, value, days) { - "use strict"; - var expires; - - if (days) { - var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toGMTString(); - } else { - expires = ""; - } - document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; -} - -function readCookie(name) { - "use strict"; - var nameEQ = encodeURIComponent(name) + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1, c.length); - } - if (c.indexOf(nameEQ) === 0) { - return decodeURIComponent(c.substring(nameEQ.length, c.length)); - } - } - return null; -} - diff --git a/public/js/ff/reports/budget/all.js b/public/js/ff/reports/budget/all.js deleted file mode 100644 index 5a99dc19ab..0000000000 --- a/public/js/ff/reports/budget/all.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * all.js - * Copyright (c) 2017 thegrumpydictator@gmail.com - * - * This file is part of Firefly III. - * - * Firefly III is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Firefly III is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Firefly III. If not, see . - */ - diff --git a/public/js/ff/reports/category/all.js b/public/js/ff/reports/category/all.js deleted file mode 100644 index 5a99dc19ab..0000000000 --- a/public/js/ff/reports/category/all.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * all.js - * Copyright (c) 2017 thegrumpydictator@gmail.com - * - * This file is part of Firefly III. - * - * Firefly III is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Firefly III is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Firefly III. If not, see . - */ - diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index dd49e9ec04..fab470e48e 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -33,108 +33,3 @@ $(function () { loadAjaxPartial('incomeVsExpenseReport', incExpReportUri); }); - -function triggerInfoClick() { - "use strict"; - // find the little info buttons and respond to them. - $('.firefly-info-button').unbind('click').click(clickInfoButton); -} - -function clickInfoButton(e) { - "use strict"; - // find all data tags, regardless of what they are: - var element = $(e.target); - var attributes = element.data(); - - // set wait cursor - $('body').addClass('waiting'); - - // add some more elements: - attributes.startDate = startDate; - attributes.endDate = endDate; - attributes.accounts = accountIds; - - $.getJSON('popup/general', {attributes: attributes}).done(respondInfoButton).fail(errorInfoButton); -} - -function errorInfoButton() { - "use strict"; - // remove wait cursor - $('body').removeClass('waiting'); - alert('Apologies. The requested data is not (yet) available.'); -} - -function respondInfoButton(data) { - "use strict"; - // remove wait cursor - $('body').removeClass('waiting'); - $('#defaultModal').empty().html(data.html).modal('show'); - -} - -function loadAjaxPartial(holder, uri) { - "use strict"; - $.get(uri).done(function (data) { - displayAjaxPartial(data, holder); - }).fail(function () { - failAjaxPartial(uri, holder); - }); -} - -function displayAjaxPartial(data, holder) { - "use strict"; - var obj = $('#' + holder); - obj.html(data); - obj.parent().find('.overlay').remove(); - - // call some often needed recalculations and what-not: - - // find a sortable table and make it sortable: - if (typeof $.bootstrapSortable === "function") { - $.bootstrapSortable(true); - } - - // find the info click things and respond to them: - triggerInfoClick(); - - // trigger list thing - listLengthInitial(); - - // budget thing in year and multi year report: - $('.budget-chart-activate').unbind('click').on('click', clickBudgetChart); - - // category thing in year and multi year report: - $('.category-chart-activate').unbind('click').on('click', clickCategoryChart); -} - -function failAjaxPartial(uri, holder) { - "use strict"; - var holderObject = $('#' + holder); - holderObject.parent().find('.overlay').remove(); - holderObject.addClass('general-chart-error'); - -} - -function clickCategoryChart(e) { - "use strict"; - var link = $(e.target); - var categoryId = link.data('category'); - $('#category_help').remove(); - - var URL = 'chart/category/report-period/' + categoryId + '/' + accountIds + '/' + startDate + '/' + endDate; - var container = 'category_chart'; - columnChart(URL, container); - return false; -} - -function clickBudgetChart(e) { - "use strict"; - var link = $(e.target); - var budgetId = link.data('budget'); - $('#budget_help').remove(); - - var URL = 'chart/budget/period/' + budgetId + '/' + accountIds + '/' + startDate + '/' + endDate; - var container = 'budget_chart'; - columnChart(URL, container); - return false; -} \ No newline at end of file diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index d15de74232..ff041cbef9 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -179,33 +179,3 @@ function preSelectDate(e) { } -function createCookie(name, value, days) { - "use strict"; - var expires; - - if (days) { - var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toGMTString(); - } else { - expires = ""; - } - document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; -} - -function readCookie(name) { - "use strict"; - var nameEQ = encodeURIComponent(name) + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1, c.length); - } - if (c.indexOf(nameEQ) === 0) { - return decodeURIComponent(c.substring(nameEQ.length, c.length)); - } - } - return null; -} - diff --git a/public/js/ff/reports/tag/all.js b/public/js/ff/reports/tag/all.js deleted file mode 100644 index 5a99dc19ab..0000000000 --- a/public/js/ff/reports/tag/all.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * all.js - * Copyright (c) 2017 thegrumpydictator@gmail.com - * - * This file is part of Firefly III. - * - * Firefly III is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Firefly III is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Firefly III. If not, see . - */ - diff --git a/resources/views/reports/account/report.twig b/resources/views/reports/account/report.twig index 4dbaf2b5f1..b03f054604 100644 --- a/resources/views/reports/account/report.twig +++ b/resources/views/reports/account/report.twig @@ -127,7 +127,7 @@ - + {% endblock %} diff --git a/resources/views/reports/audit/report.twig b/resources/views/reports/audit/report.twig index 5716f4dc08..740b28913a 100644 --- a/resources/views/reports/audit/report.twig +++ b/resources/views/reports/audit/report.twig @@ -86,5 +86,6 @@ + {% endblock %} diff --git a/resources/views/reports/budget/month.twig b/resources/views/reports/budget/month.twig index e79e09b4af..a52cd09552 100644 --- a/resources/views/reports/budget/month.twig +++ b/resources/views/reports/budget/month.twig @@ -260,7 +260,7 @@ - + {% endblock %} diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index bb4506523e..55ba6f5f3d 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -397,7 +397,7 @@ - + {% endblock %} diff --git a/resources/views/reports/default/month.twig b/resources/views/reports/default/month.twig index b6b0aa5b50..9b0bddad9e 100644 --- a/resources/views/reports/default/month.twig +++ b/resources/views/reports/default/month.twig @@ -171,6 +171,7 @@ var accountChartUri = '{{ route('chart.account.report', [accountIds, start.format('Ymd'), end.format('Ymd')]) }}'; + {% endblock %} diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 6b94ad6ebe..96acae16a0 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -222,6 +222,7 @@ var categoryIncomeUri = '{{ route('report-data.category.income', [accountIds, start.format('Ymd'), end.format('Ymd')]) }}'; + {% endblock %} diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index 802152bdc2..821fb90e8c 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -217,7 +217,7 @@ - + diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index fd1bcd0b0c..aa2b563864 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -179,5 +179,6 @@ var filterPlaceholder = "{{ trans('firefly.multi_select_filter_placeholder')|escape('js') }}"; + {% endblock %} diff --git a/resources/views/reports/partials/exp-budgets.twig b/resources/views/reports/partials/exp-budgets.twig index de59cc258e..992570d1dc 100644 --- a/resources/views/reports/partials/exp-budgets.twig +++ b/resources/views/reports/partials/exp-budgets.twig @@ -1,8 +1,8 @@ - - + + diff --git a/resources/views/reports/partials/exp-categories.twig b/resources/views/reports/partials/exp-categories.twig index aff8d62089..e700cf4268 100644 --- a/resources/views/reports/partials/exp-categories.twig +++ b/resources/views/reports/partials/exp-categories.twig @@ -1,9 +1,9 @@
{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'category'|_ }}{{ 'spent'|_ }}
- - - + + + diff --git a/resources/views/reports/partials/exp-not-grouped.twig b/resources/views/reports/partials/exp-not-grouped.twig index bc454b7d44..85a5a667c8 100644 --- a/resources/views/reports/partials/exp-not-grouped.twig +++ b/resources/views/reports/partials/exp-not-grouped.twig @@ -3,8 +3,8 @@ - - + + diff --git a/resources/views/reports/partials/top-transactions.twig b/resources/views/reports/partials/top-transactions.twig new file mode 100644 index 0000000000..e068286fd1 --- /dev/null +++ b/resources/views/reports/partials/top-transactions.twig @@ -0,0 +1,24 @@ +
{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'earned'|_ }}{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'earned'|_ }}
{{ 'name'|_ }}
+ + + + + + + + + + {% for transaction in sorted %} + + + + + + + {% endfor %} + +
{{ 'account'|_ }}{{ 'description'|_ }}{{ 'date'|_ }}
+ {{ transaction.opposing_account_name }} + {{ transaction.description }} + {{ transaction.date.formatLocalized(monthAndDayFormat) }} + {{ transaction|transactionAmount }}
diff --git a/resources/views/reports/tag/month.twig b/resources/views/reports/tag/month.twig index 54153b91ba..e4139f5dbb 100644 --- a/resources/views/reports/tag/month.twig +++ b/resources/views/reports/tag/month.twig @@ -422,8 +422,7 @@ var mainUri = '{{ route('chart.tag.main', [accountIds, tagTags, start.format('Ymd'), end.format('Ymd')]) }}'; - - + {% endblock %} diff --git a/routes/web.php b/routes/web.php index 3e46cf739e..5cb2d597d6 100755 --- a/routes/web.php +++ b/routes/web.php @@ -623,8 +623,8 @@ Route::group( Route::get('budget/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@budget', 'as' => 'budget']); //expense earned top X - Route::get('expenses/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topX', 'as' => 'expenses']); - Route::get('income/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topXPeriod', 'as' => 'income']); + Route::get('expenses/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topExpense', 'as' => 'expenses']); + Route::get('income/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topIncome', 'as' => 'income']); } );