diff --git a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php b/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php index e17022d9b6..e0444c1cba 100644 --- a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php +++ b/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php @@ -23,6 +23,13 @@ use Illuminate\Support\Collection; interface CategoryChartGeneratorInterface { + /** + * @param array $data + * + * @return array + */ + public function pieChart(array $data): array; + /** * @param Collection $entries * diff --git a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php index 560b4f95bb..9e6221f24b 100644 --- a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php +++ b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace FireflyIII\Generator\Chart\Category; +use FireflyIII\Support\ChartColour; use Illuminate\Support\Collection; @@ -117,6 +118,30 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface return $data; } + /** + * @param array $entries + * + * @return array + */ + public function pieChart(array $entries): array + { + $data = [ + 'datasets' => [ + 0 => [], + ], + 'labels' => [], + ]; + $index = 0; + foreach ($entries as $entry) { + $data['datasets'][0]['data'][] = round($entry['amount'], 2); + $data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index); + $data['labels'][] = $entry['name']; + $index++; + } + + return $data; + } + /** * * @param Collection $entries diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index d2bd5ec3c2..623d65fef1 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -16,7 +16,11 @@ namespace FireflyIII\Generator\Report\Category; use Carbon\Carbon; use FireflyIII\Generator\Report\ReportGeneratorInterface; +use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionType; use Illuminate\Support\Collection; +use Log; /** * Class MonthReportGenerator @@ -39,13 +43,17 @@ class MonthReportGenerator implements ReportGeneratorInterface */ public function generate(): string { - $accountIds = join(',', $this->accounts->pluck('id')->toArray()); - $reportType = 'category'; + $accountIds = join(',', $this->accounts->pluck('id')->toArray()); + $categoryIds = join(',', $this->categories->pluck('id')->toArray()); + $reportType = 'category'; + $accountSummary = $this->getAccountSummary(); + $categorySummary = $this->getCategorySummary(); // render! - return view('reports.category.month', compact('accountIds', 'reportType')) + return view('reports.category.month', compact('accountIds', 'categoryIds', 'reportType', 'accountSummary', 'categorySummary')) ->with('start', $this->start)->with('end', $this->end) ->with('categories', $this->categories) + ->with('accounts', $this->accounts) ->render(); } @@ -96,4 +104,226 @@ class MonthReportGenerator implements ReportGeneratorInterface return $this; } + + /** + * @return array + */ + private function getAccountSummary(): array + { + $spent = $this->getSpentAccountSummary(); + $earned = $this->getEarnedAccountSummary(); + $return = []; + + /** + * @var int $accountId + * @var string $entry + */ + foreach ($spent as $accountId => $entry) { + if (!isset($return[$accountId])) { + $return[$accountId] = ['spent' => 0, 'earned' => 0]; + } + + $return[$accountId]['spent'] = $entry; + } + unset($entry); + + /** + * @var int $accountId + * @var string $entry + */ + foreach ($earned as $accountId => $entry) { + if (!isset($return[$accountId])) { + $return[$accountId] = ['spent' => 0, 'earned' => 0]; + } + + $return[$accountId]['earned'] = $entry; + } + + + return $return; + + } + + /** + * @return array + */ + private function getCategorySummary(): array + { + $spent = $this->getSpentCategorySummary(); + $earned = $this->getEarnedCategorySummary(); + $return = []; + + /** + * @var int $categoryId + * @var string $entry + */ + foreach ($spent as $categoryId => $entry) { + if (!isset($return[$categoryId])) { + $return[$categoryId] = ['spent' => 0, 'earned' => 0]; + } + + $return[$categoryId]['spent'] = $entry; + } + unset($entry); + + /** + * @var int $categoryId + * @var string $entry + */ + foreach ($earned as $categoryId => $entry) { + if (!isset($return[$categoryId])) { + $return[$categoryId] = ['spent' => 0, 'earned' => 0]; + } + + $return[$categoryId]['earned'] = $entry; + } + + return $return; + } + + /** + * @return array + */ + private function getEarnedAccountSummary(): array + { + $transactions = $this->getIncomes(); + $result = []; + /** @var Transaction $transaction */ + foreach ($transactions as $transaction) { + $accountId = $transaction->account_id; + $result[$accountId] = $result[$accountId] ?? '0'; + $result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]); + } + + return $result; + } + + /** + * @return array + */ + private function getEarnedCategorySummary(): array + { + $transactions = $this->getIncomes(); + $result = []; + /** @var Transaction $transaction */ + foreach ($transactions as $transaction) { + $jrnlCatId = intval($transaction->transaction_journal_category_id); + $transCatId = intval($transaction->transaction_category_id); + $categoryId = max($jrnlCatId, $transCatId); + + $result[$categoryId] = $result[$categoryId] ?? '0'; + $result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]); + } + + return $result; + } + + /** + * @return Collection + */ + private function getExpenses(): Collection + { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) + ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->setCategories($this->categories)->getOpposingAccount()->disableFilter(); + + $accountIds = $this->accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + + $transactions = $transactions->filter( + function (Transaction $transaction) use ($accountIds) { + $opposing = $transaction->opposing_account_id; + // remove internal transfer + if (in_array($opposing, $accountIds)) { + Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); + + return null; + } + // remove positive amount + if (bccomp($transaction->transaction_amount, '0') === 1) { + Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); + + return null; + } + + return $transaction; + } + ); + + return $transactions; + } + + /** + * @return Collection + */ + private function getIncomes(): Collection + { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) + ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ->setCategories($this->categories)->getOpposingAccount(); + $accountIds = $this->accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $transactions = $transactions->filter( + function (Transaction $transaction) use ($accountIds) { + $opposing = $transaction->opposing_account_id; + // remove internal transfer + if (in_array($opposing, $accountIds)) { + Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); + + return null; + } + // remove positive amount + if (bccomp($transaction->transaction_amount, '0') === -1) { + Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); + + return null; + } + + return $transaction; + } + ); + + return $transactions; + } + + /** + * @return array + */ + private function getSpentAccountSummary(): array + { + $transactions = $this->getExpenses(); + $result = []; + /** @var Transaction $transaction */ + foreach ($transactions as $transaction) { + $accountId = $transaction->account_id; + $result[$accountId] = $result[$accountId] ?? '0'; + $result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]); + } + + return $result; + } + + /** + * @return array + */ + private function getSpentCategorySummary(): array + { + $transactions = $this->getExpenses(); + $result = []; + /** @var Transaction $transaction */ + foreach ($transactions as $transaction) { + $jrnlCatId = intval($transaction->transaction_journal_category_id); + $transCatId = intval($transaction->transaction_category_id); + $categoryId = max($jrnlCatId, $transCatId); + + $result[$categoryId] = $result[$categoryId] ?? '0'; + $result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]); + } + + return $result; + + + } } \ No newline at end of file diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 11406b025b..349b1d2b99 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -6,6 +6,7 @@ namespace FireflyIII\Helpers\Collector; use Carbon\Carbon; use Crypt; +use DB; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\AccountType; use FireflyIII\Models\Budget; @@ -16,6 +17,7 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; +use Illuminate\Database\Query\JoinClause; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Log; @@ -30,9 +32,10 @@ use Log; class JournalCollector implements JournalCollectorInterface { + /** @var array */ + private $accountIds = []; /** @var int */ private $count = 0; - /** @var array */ private $fields = [ @@ -63,6 +66,8 @@ class JournalCollector implements JournalCollectorInterface /** @var bool */ private $joinedCategory = false; /** @var bool */ + private $joinedOpposing = false; + /** @var bool */ private $joinedTag = false; /** @var int */ private $limit; @@ -112,6 +117,16 @@ class JournalCollector implements JournalCollectorInterface return $this->count; } + /** + * @return JournalCollectorInterface + */ + public function disableFilter(): JournalCollectorInterface + { + $this->filterTransfers = false; + + return $this; + } + /** * @return Collection */ @@ -133,6 +148,42 @@ class JournalCollector implements JournalCollectorInterface return $set; } + /** + * @return JournalCollectorInterface + */ + public function getOpposingAccount(): JournalCollectorInterface + { + $this->joinOpposingTables(); + + $accountIds = $this->accountIds; + $this->query->where( + function (EloquentBuilder $q1) use ($accountIds) { + // set 1 + $q1->where( + function (EloquentBuilder $q2) use ($accountIds) { + // transactions.account_id in set + $q2->whereIn('transactions.account_id', $accountIds); + // opposing.account_id not in set + $q2->whereNotIn('opposing.account_id', $accountIds); + + } + ); + // or set 2 + $q1->orWhere( + function (EloquentBuilder $q3) use ($accountIds) { + // transactions.account_id not in set + $q3->whereNotIn('transactions.account_id', $accountIds); + // B in set + // opposing.account_id not in set + $q3->whereIn('opposing.account_id', $accountIds); + } + ); + } + ); + + return $this; + } + /** * @return LengthAwarePaginator * @throws FireflyException @@ -159,12 +210,15 @@ class JournalCollector implements JournalCollectorInterface if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->whereIn('transactions.account_id', $accountIds); + Log::debug(sprintf('setAccounts: %s', join(', ', $accountIds))); + $this->accountIds = $accountIds; } if ($accounts->count() > 1) { $this->filterTransfers = true; } + return $this; } @@ -179,6 +233,7 @@ class JournalCollector implements JournalCollectorInterface if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->whereIn('transactions.account_id', $accountIds); + $this->accountIds = $accountIds; } if ($accounts->count() > 1) { @@ -223,6 +278,29 @@ class JournalCollector implements JournalCollectorInterface return $this; } + /** + * @param Collection $categories + * + * @return JournalCollectorInterface + */ + public function setCategories(Collection $categories): JournalCollectorInterface + { + $categoryIds = $categories->pluck('id')->toArray(); + if (count($categoryIds) === 0) { + return $this; + } + $this->joinCategoryTables(); + + $this->query->where( + function (EloquentBuilder $q) use ($categoryIds) { + $q->whereIn('category_transaction.category_id', $categoryIds); + $q->orWhereIn('category_transaction_journal.category_id', $categoryIds); + } + ); + + return $this; + } + /** * @param Category $category * @@ -389,6 +467,7 @@ class JournalCollector implements JournalCollectorInterface $set = $set->filter( function (Transaction $transaction) { if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) { + Log::debug( sprintf( 'Included journal #%d (transaction #%d) because its a %s with amount %f', @@ -443,6 +522,24 @@ class JournalCollector implements JournalCollectorInterface $this->joinedCategory = true; $this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); $this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id'); + $this->fields[] = 'category_transaction_journal.category_id as transaction_journal_category_id'; + $this->fields[] = 'category_transaction.category_id as transaction_category_id'; + } + } + + private function joinOpposingTables() + { + if (!$this->joinedOpposing) { + // join opposing transaction (hard): + $this->query->leftJoin( + 'transactions as opposing', function (JoinClause $join) { + $join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id') + ->where('opposing.identifier', '=', 'transactions.identifier') + ->where('opposing.amount', '=', DB::raw('transactions.amount * -1')); + } + ); + + $this->fields[] = 'opposing.account_id as opposing_account_id'; } } @@ -481,5 +578,4 @@ class JournalCollector implements JournalCollectorInterface return $query; } - } \ No newline at end of file diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index 26ad19b30a..b451a111ae 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace FireflyIII\Helpers\Collector; + use Carbon\Carbon; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; @@ -32,11 +33,21 @@ interface JournalCollectorInterface */ public function count(): int; + /** + * @return JournalCollectorInterface + */ + public function disableFilter(): JournalCollectorInterface; + /** * @return Collection */ public function getJournals(): Collection; + /** + * @return JournalCollectorInterface + */ + public function getOpposingAccount(): JournalCollectorInterface; + /** * @return LengthAwarePaginator */ @@ -68,6 +79,13 @@ interface JournalCollectorInterface */ public function setBudget(Budget $budget): JournalCollectorInterface; + /** + * @param Collection $categories + * + * @return JournalCollectorInterface + */ + public function setCategories(Collection $categories): JournalCollectorInterface; + /** * @param Category $category * diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index f8d9882e28..d38be5f4db 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -16,10 +16,13 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\AccountType; use FireflyIII\Models\Category; +use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; @@ -48,7 +51,6 @@ class CategoryController extends Controller $this->generator = app(CategoryChartGeneratorInterface::class); } - /** * Show an overview for a category for all time, per month/week/year. * @@ -109,6 +111,51 @@ class CategoryController extends Controller return Response::json($data); } + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * @param string $others + * + * @return \Illuminate\Http\JsonResponse + */ + public function expensePieChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) + { + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + $others = intval($others) === 1; + $names = []; + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end) + ->setTypes([TransactionType::WITHDRAWAL]) + ->setCategories($categories); + $set = $collector->getSumPerCategory(); + $result = []; + $total = '0'; + foreach ($set as $categoryId => $amount) { + if (!isset($names[$categoryId])) { + $category = $repository->find(intval($categoryId)); + $names[$categoryId] = $category->name; + } + $amount = bcmul($amount, '-1'); + $total = bcadd($total, $amount); + $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; + } + + if ($others) { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); + $sum = bcmul($collector->getSum(), '-1'); + $sum = bcsub($sum, $total); + $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + } + + $data = $this->generator->pieChart($result); + + return Response::json($data); + } + /** * @param CRI $repository * @param AccountRepositoryInterface $accountRepository @@ -153,6 +200,49 @@ class CategoryController extends Controller } + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * @param string $others + * + * @return \Illuminate\Http\JsonResponse + */ + public function incomePieChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) + { + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + /** @var bool $others */ + $others = intval($others) === 1; + $names = []; + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setCategories($categories); + $set = $collector->getSumPerCategory(); + $result = []; + $total = '0'; + foreach ($set as $categoryId => $amount) { + if (!isset($names[$categoryId])) { + $category = $repository->find(intval($categoryId)); + $names[$categoryId] = $category->name; + } + $total = bcadd($total, $amount); + $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; + } + + if ($others) { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); + $sum = $collector->getSum(); + $sum = bcsub($sum, $total); + $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + } + + $data = $this->generator->pieChart($result); + + return Response::json($data); + } + /** * @param CRI $repository * @param Category $category diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index 7201e1f0cb..07ea8ca68c 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -8,6 +8,8 @@ /* globals $, Chart, currencySymbol,mon_decimal_point ,accounting, mon_thousands_sep, frac_digits */ +var allCharts = {}; + /* Make some colours: */ @@ -29,7 +31,6 @@ var colourSet = [ [240, 98, 146], [0, 121, 107], [194, 24, 91] - ]; var fillColors = []; @@ -234,6 +235,11 @@ function lineChart(URL, container, options) { function areaChart(URL, container, options) { "use strict"; + if ($('#' + container).length === 0) { + console.log('No container called ' + container + ' was found.'); + return; + } + $.getJSON(URL).done(function (data) { var ctx = document.getElementById(container).getContext("2d"); var newData = {}; @@ -358,14 +364,30 @@ function stackedColumnChart(URL, container, options) { function pieChart(URL, container, options) { "use strict"; + if ($('#' + container).length === 0) { + console.log('No container called ' + container + ' was found.'); + return; + } + $.getJSON(URL).done(function (data) { - var ctx = document.getElementById(container).getContext("2d"); - new Chart(ctx, { - type: 'pie', - data: data, - options: defaultPieOptions - }); + if (allCharts.hasOwnProperty(container)) { + console.log('Will draw updated pie chart'); + + allCharts[container].data.datasets = data.datasets; + allCharts[container].data.labels = data.labels; + allCharts[container].update(); + } else { + // new chart! + console.log('Will draw new pie chart'); + var ctx = document.getElementById(container).getContext("2d"); + allCharts[container] = new Chart(ctx, { + type: 'pie', + data: data, + options: defaultPieOptions + }); + } + }).fail(function () { $('#' + container).addClass('general-chart-error'); diff --git a/public/js/ff/index.js b/public/js/ff/index.js index c714f83ac7..a6a7e5d795 100644 --- a/public/js/ff/index.js +++ b/public/js/ff/index.js @@ -1,4 +1,4 @@ -/* globals $, columnChart,showTour, Tour, google, lineChart, pieChart, stackedColumnChart, areaChart */ + /* globals $, columnChart,showTour, Tour, google, lineChart, pieChart, stackedColumnChart, areaChart */ $(function () { "use strict"; diff --git a/public/js/ff/reports/category/all.js b/public/js/ff/reports/category/all.js new file mode 100644 index 0000000000..25a412d1c5 --- /dev/null +++ b/public/js/ff/reports/category/all.js @@ -0,0 +1,10 @@ +/* + * all.js + * Copyright (C) 2016 thegrumpydictator@gmail.com + * + * This software may be modified and distributed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + diff --git a/public/js/ff/reports/category/month.js b/public/js/ff/reports/category/month.js new file mode 100644 index 0000000000..250ca75b53 --- /dev/null +++ b/public/js/ff/reports/category/month.js @@ -0,0 +1,57 @@ +/* + * month.js + * Copyright (C) 2016 thegrumpydictator@gmail.com + * + * This software may be modified and distributed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +$(function () { + "use strict"; + drawChart(); + $('#categories-in-pie-chart-checked').on('change', redrawCatInPie); + $('#categories-out-pie-chart-checked').on('change', redrawCatOutPie); +}); + + +function drawChart() { + "use strict"; + + // month view: + + // draw pie chart of income, depending on "show other transactions too": + redrawCatInPie(); + redrawCatOutPie(); +} + +function redrawCatOutPie() { + "use strict"; + var checkbox = $('#categories-out-pie-chart-checked'); + var container = 'categories-out-pie-chart'; + + // + var others = '0'; + // check if box is checked: + if (checkbox.prop('checked')) { + others = '1'; + } + + pieChart('chart/category/' + accountIds + '/' + categoryIds + '/' + startDate + '/' + endDate + '/' + others + '/expense', container); +} + +function redrawCatInPie() { + "use strict"; + var checkbox = $('#categories-in-pie-chart-checked'); + var container = 'categories-in-pie-chart'; + + // + var others = '0'; + // check if box is checked: + if (checkbox.prop('checked')) { + others = '1'; + } + + pieChart('chart/category/' + accountIds + '/' + categoryIds + '/' + startDate + '/' + endDate + '/' + others + '/income', container); +} \ No newline at end of file diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index 4864dc3ca6..71ac4b5921 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -8,14 +8,135 @@
- Summary here. Accounts and categories involved. Summary of in/out +
+
+

{{ 'accounts'|_ }}

+
+
+ + + + + + + + + + {% for account in accounts %} + + + + + + {% endfor %} + +
{{ 'name'|_ }}{{ 'earned'|_ }}{{ 'spent'|_ }}
+ {{ account.name }} + + {% if accountSummary[account.id] %} + {{ accountSummary[account.id].earned|formatAmount }} + {% else %} + {{ 0|formatAmount }} + {% endif %} + + {% if accountSummary[account.id] %} + {{ accountSummary[account.id].spent|formatAmount }} + {% else %} + {{ 0|formatAmount }} + {% endif %} +
+
+
+ +
+
+

{{ 'categories'|_ }}

+
+
+ + + + + + + + + + {% for category in categories %} + + + + + + {% endfor %} + +
{{ 'name'|_ }}{{ 'earned'|_ }}{{ 'spent'|_ }}
+ {{ category.name }} + + {% if categorySummary[category.id] %} + {{ categorySummary[category.id].earned|formatAmount }} + {% else %} + {{ 0|formatAmount }} + {% endif %} + + {% if categorySummary[category.id] %} + {{ categorySummary[category.id].spent|formatAmount }} + {% else %} + {{ 0|formatAmount }} + {% endif %} +
+
+
-
- Pie chart with spending (aka all withdrawals in category). Optional checkbox to include all other transactions. + {% if categories.count > 1 %} +
+
+
+

{{ 'income_per_category'|_ }}

+
+
+ + +
+
-
- Pie chart with income (aka all deposits in category). Optional checkbox to include all other transactions (for comparison). +
+
+
+

{{ 'expense_per_category'|_ }}

+
+
+ + +
+
+ {% endif %} + {% if accounts.count > 1 %} +
+
+
+

{{ 'income_per_account'|_ }}

+
+
+
+
+
+
+
+
+

{{ 'expense_per_account'|_ }}

+
+
+
+
+
+ {% endif %} + {#Pie chart with income (aka all deposits in category). Optional checkbox to include all other transactions (for comparison).#}
@@ -59,6 +180,23 @@ {% endblock %} {% block scripts %} + + + + + + + + + {% endblock %} {% block styles %} diff --git a/routes/web.php b/routes/web.php index 464999178f..6be7a49bc5 100755 --- a/routes/web.php +++ b/routes/web.php @@ -204,11 +204,14 @@ Route::group( // categories: Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']); - Route::get('/chart/category/{category}/period', ['uses' => 'Chart\CategoryController@currentPeriod']); Route::get('/chart/category/{category}/period/{date}', ['uses' => 'Chart\CategoryController@specificPeriod']); Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']); + // these charts are used in reports: + Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/income', ['uses' => 'Chart\CategoryController@incomePieChart']); + Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/expense', ['uses' => 'Chart\CategoryController@expensePieChart']); + // piggy banks: Route::get('/chart/piggy-bank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']);